diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,7 +1,15 @@
 Revision history for haskell-igraph
 ===================================
 
-v0.7.1 -- 2018-XX-XX
+v0.8.0 -- XXXX-XX-XX
+--------------------
+
+* Ship igraph C sources v0.8.0
+
+* Add random number generator.
+
+v0.7.1 -- 2018-11-26
+--------------------
 
 * Add a few more functions.
 
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2016 Kai Zhang
+Copyright (c) 2016-2020 Kai Zhang
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -0,0 +1,2 @@
+Haskell bindings to igraph C library
+====================================
diff --git a/cbits/haskell_igraph.c b/cbits/haskell_igraph.c
--- a/cbits/haskell_igraph.c
+++ b/cbits/haskell_igraph.c
@@ -1,4 +1,3 @@
-#include <igraph/igraph.h>
 #include "haskell_attributes.h"
 
 const igraph_attribute_table_t igraph_haskell_attribute_table={
diff --git a/haskell-igraph.cabal b/haskell-igraph.cabal
--- a/haskell-igraph.cabal
+++ b/haskell-igraph.cabal
@@ -1,6 +1,7 @@
+cabal-version:       2.2
 name:                haskell-igraph
-version:             0.7.1
-synopsis:            Haskell interface of the igraph library.
+version:             0.8.0
+synopsis:            Bindings to the igraph C library (v0.8.0).
 description:         igraph<"http://igraph.org/c/"> is a library for creating
                      and manipulating large graphs. This package provides the Haskell
                      interface of igraph.
@@ -8,21 +9,25 @@
 license-file:        LICENSE
 author:              Kai Zhang
 maintainer:          kai@kzhang.org
-copyright:           (c) 2016-2018 Kai Zhang
+copyright:           (c) 2016-2020 Kai Zhang
 category:            Math
 build-type:          Simple
-cabal-version:       >=1.24
 extra-source-files:
-  include/haskell_igraph.h
-  include/bytestring.h
-  include/haskell_attributes.h
+  include/*.h
+  igraph/include/*.h
+  igraph/include/*.pmt
+  igraph/include/f2c/*.h
+  igraph/include/prpack/*.h
+  igraph/include/cs/*.h
+  igraph/include/cliquer/*.h
+  igraph/include/bliss/*.hh
+  igraph/include/plfit/*.h
+  igraph/AUTHORS
+  igraph/COPYING
+  stack.yaml
   README.md
   ChangeLog.md
 
-Flag graphics
-  Description: Enable graphics output
-  Default:     False
-
 library
   exposed-modules:
     IGraph.Internal.Initialization
@@ -30,13 +35,13 @@
     IGraph.Internal
     IGraph
     IGraph.Mutable
+    IGraph.Random
     IGraph.Types
-    IGraph.Exporter.GEXF
     IGraph.Algorithms
     IGraph.Algorithms.Structure
     IGraph.Algorithms.Community
     IGraph.Algorithms.Clique
-    IGraph.Algorithms.Layout
+    --IGraph.Algorithms.Layout
     IGraph.Algorithms.Motif
     IGraph.Algorithms.Generators
     IGraph.Algorithms.Isomorphism
@@ -45,36 +50,579 @@
   other-modules:
     IGraph.Internal.C2HS
 
-  if flag(graphics)
-    exposed-modules: IGraph.Exporter.Graphics
-
-  if flag(graphics)
-    build-depends: diagrams-lib, diagrams-cairo
-
   build-depends:
       base >= 4.0 && < 5.0
     , bytestring >= 0.9
     , cereal
-    , colour
     , conduit >= 1.3.0
     , containers
     , data-ordlist
     , primitive
-    , hxt
-    , split
     , singletons
 
-  extra-libraries:     igraph
+  extra-libraries:     stdc++
   hs-source-dirs:      src
   default-language:    Haskell2010
   ghc-options:         -Wall
-  build-tools:         c2hs >=0.25.0
+  build-tool-depends: c2hs:c2hs >=0.25.0
   c-sources:
     cbits/haskell_igraph.c
     cbits/haskell_attributes.c
     cbits/bytestring.c
-  include-dirs:        include
 
+    -- igraph
+    igraph/src/abort_.c
+    igraph/src/adjlist.c
+    igraph/src/arithchk.c
+    igraph/src/arpack.c
+    igraph/src/array.c
+    igraph/src/atlas.c
+    igraph/src/attributes.c
+    igraph/src/backspac.c
+    igraph/src/basic_query.c
+    igraph/src/bfgs.c
+    igraph/src/bigint.c
+    igraph/src/bignum.c
+    igraph/src/bipartite.c
+    igraph/src/blas.c
+    igraph/src/c_abs.c
+    igraph/src/cabs.c
+    igraph/src/cattributes.c
+    igraph/src/c_cos.c
+    igraph/src/c_div.c
+    igraph/src/centrality.c
+    igraph/src/c_exp.c
+    igraph/src/cliquer.c
+    igraph/src/cliquer_graph.c
+    igraph/src/cliques.c
+    igraph/src/c_log.c
+    igraph/src/close.c
+    igraph/src/cocitation.c
+    igraph/src/cohesive_blocks.c
+    igraph/src/coloring.c
+    igraph/src/community.c
+    igraph/src/community_leiden.c
+    igraph/src/complex.c
+    igraph/src/components.c
+    igraph/src/conversion.c
+    igraph/src/cores.c
+    igraph/src/cs_add.c
+    igraph/src/cs_amd.c
+    igraph/src/cs_chol.c
+    igraph/src/cs_cholsol.c
+    igraph/src/cs_compress.c
+    igraph/src/cs_counts.c
+    igraph/src/cs_cumsum.c
+    igraph/src/cs_dfs.c
+    igraph/src/cs_dmperm.c
+    igraph/src/cs_droptol.c
+    igraph/src/cs_dropzeros.c
+    igraph/src/cs_dupl.c
+    igraph/src/cs_entry.c
+    igraph/src/cs_ereach.c
+    igraph/src/cs_etree.c
+    igraph/src/cs_fkeep.c
+    igraph/src/cs_gaxpy.c
+    igraph/src/cs_happly.c
+    igraph/src/cs_house.c
+    igraph/src/c_sin.c
+    igraph/src/cs_ipvec.c
+    igraph/src/cs_leaf.c
+    igraph/src/cs_load.c
+    igraph/src/cs_lsolve.c
+    igraph/src/cs_ltsolve.c
+    igraph/src/cs_lu.c
+    igraph/src/cs_lusol.c
+    igraph/src/cs_malloc.c
+    igraph/src/cs_maxtrans.c
+    igraph/src/cs_multiply.c
+    igraph/src/cs_norm.c
+    igraph/src/cs_permute.c
+    igraph/src/cs_pinv.c
+    igraph/src/cs_post.c
+    igraph/src/cs_print.c
+    igraph/src/cs_pvec.c
+    igraph/src/cs_qr.c
+    igraph/src/cs_qrsol.c
+    igraph/src/c_sqrt.c
+    igraph/src/cs_randperm.c
+    igraph/src/cs_reach.c
+    igraph/src/cs_scatter.c
+    igraph/src/cs_scc.c
+    igraph/src/cs_schol.c
+    igraph/src/cs_spsolve.c
+    igraph/src/cs_sqr.c
+    igraph/src/cs_symperm.c
+    igraph/src/cs_tdfs.c
+    igraph/src/cs_transpose.c
+    igraph/src/cs_updown.c
+    igraph/src/cs_usolve.c
+    igraph/src/cs_util.c
+    igraph/src/cs_utsolve.c
+    igraph/src/ctype.c
+    igraph/src/d_abs.c
+    igraph/src/d_acos.c
+    igraph/src/d_asin.c
+    igraph/src/dasum.c
+    igraph/src/d_atan.c
+    igraph/src/d_atn2.c
+    igraph/src/daxpy.c
+    igraph/src/d_cnjg.c
+    igraph/src/dcopy.c
+    igraph/src/d_cos.c
+    igraph/src/d_cosh.c
+    igraph/src/d_dim.c
+    igraph/src/ddot.c
+    igraph/src/decomposition.c
+    igraph/src/derf_.c
+    igraph/src/derfc_.c
+    igraph/src/d_exp.c
+    igraph/src/dfe.c
+    igraph/src/dgebak.c
+    igraph/src/dgebal.c
+    igraph/src/dgeev.c
+    igraph/src/dgeevx.c
+    igraph/src/dgehd2.c
+    igraph/src/dgehrd.c
+    igraph/src/dgemm.c
+    igraph/src/dgemv.c
+    igraph/src/dgeqr2.c
+    igraph/src/dger.c
+    igraph/src/dgesv.c
+    igraph/src/dgetf2.c
+    igraph/src/dgetrf.c
+    igraph/src/dgetrs.c
+    igraph/src/dgetv0.c
+    igraph/src/dhseqr.c
+    igraph/src/d_imag.c
+    igraph/src/d_int.c
+    igraph/src/disnan.c
+    igraph/src/distances.c
+    igraph/src/dlabad.c
+    igraph/src/dlacn2.c
+    igraph/src/dlacpy.c
+    igraph/src/dladiv.c
+    igraph/src/dlae2.c
+    igraph/src/dlaebz.c
+    igraph/src/dlaev2.c
+    igraph/src/dlaexc.c
+    igraph/src/dlagtf.c
+    igraph/src/dlagts.c
+    igraph/src/dlahqr.c
+    igraph/src/dlahr2.c
+    igraph/src/dlaisnan.c
+    igraph/src/dlaln2.c
+    igraph/src/dlamch.c
+    igraph/src/dlaneg.c
+    igraph/src/dlange.c
+    igraph/src/dlanhs.c
+    igraph/src/dlanst.c
+    igraph/src/dlansy.c
+    igraph/src/dlanv2.c
+    igraph/src/dlapy2.c
+    igraph/src/dlaqr0.c
+    igraph/src/dlaqr1.c
+    igraph/src/dlaqr2.c
+    igraph/src/dlaqr3.c
+    igraph/src/dlaqr4.c
+    igraph/src/dlaqr5.c
+    igraph/src/dlaqrb.c
+    igraph/src/dlaqtr.c
+    igraph/src/dlar1v.c
+    igraph/src/dlarfb.c
+    igraph/src/dlarf.c
+    igraph/src/dlarfg.c
+    igraph/src/dlarft.c
+    igraph/src/dlarfx.c
+    igraph/src/dlarnv.c
+    igraph/src/dlarra.c
+    igraph/src/dlarrb.c
+    igraph/src/dlarrc.c
+    igraph/src/dlarrd.c
+    igraph/src/dlarre.c
+    igraph/src/dlarrf.c
+    igraph/src/dlarrj.c
+    igraph/src/dlarrk.c
+    igraph/src/dlarrr.c
+    igraph/src/dlarrv.c
+    igraph/src/dlartg.c
+    igraph/src/dlaruv.c
+    igraph/src/dlascl.c
+    igraph/src/dlaset.c
+    igraph/src/dlasq2.c
+    igraph/src/dlasq3.c
+    igraph/src/dlasq4.c
+    igraph/src/dlasq5.c
+    igraph/src/dlasq6.c
+    igraph/src/dlasr.c
+    igraph/src/dlasrt.c
+    igraph/src/dlassq.c
+    igraph/src/dlaswp.c
+    igraph/src/dlasy2.c
+    igraph/src/dlatrd.c
+    igraph/src/d_lg10.c
+    igraph/src/d_log.c
+    igraph/src/d_mod.c
+    igraph/src/dmout.c
+    igraph/src/dnaitr.c
+    igraph/src/dnapps.c
+    igraph/src/dnaup2.c
+    igraph/src/dnaupd.c
+    igraph/src/dnconv.c
+    igraph/src/dneigh.c
+    igraph/src/dneupd.c
+    igraph/src/dngets.c
+    igraph/src/d_nint.c
+    igraph/src/dnrm2.c
+    igraph/src/dolio.c
+    igraph/src/dorg2r.c
+    igraph/src/dorghr.c
+    igraph/src/dorgqr.c
+    igraph/src/dorm2l.c
+    igraph/src/dorm2r.c
+    igraph/src/dormhr.c
+    igraph/src/dormql.c
+    igraph/src/dormqr.c
+    igraph/src/dormtr.c
+    igraph/src/dotproduct.c
+    igraph/src/dpotf2.c
+    igraph/src/dpotrf.c
+    igraph/src/d_prod.c
+    igraph/src/dqueue.c
+    igraph/src/drot.c
+    igraph/src/dsaitr.c
+    igraph/src/dsapps.c
+    igraph/src/dsaup2.c
+    igraph/src/dsaupd.c
+    igraph/src/dscal.c
+    igraph/src/dsconv.c
+    igraph/src/dseigt.c
+    igraph/src/dsesrt.c
+    igraph/src/dseupd.c
+    igraph/src/dsgets.c
+    igraph/src/d_sign.c
+    igraph/src/d_sin.c
+    igraph/src/d_sinh.c
+    igraph/src/dsortc.c
+    igraph/src/dsortr.c
+    igraph/src/d_sqrt.c
+    igraph/src/dstatn.c
+    igraph/src/dstats.c
+    igraph/src/dstebz.c
+    igraph/src/dstein.c
+    igraph/src/dstemr.c
+    igraph/src/dsteqr.c
+    igraph/src/dsterf.c
+    igraph/src/dstqrb.c
+    igraph/src/dswap.c
+    igraph/src/dsyevr.c
+    igraph/src/dsymv.c
+    igraph/src/dsyr2.c
+    igraph/src/dsyr2k.c
+    igraph/src/dsyrk.c
+    igraph/src/dsytd2.c
+    igraph/src/dsytrd.c
+    igraph/src/d_tan.c
+    igraph/src/d_tanh.c
+    igraph/src/dtime_.c
+    igraph/src/dtrevc.c
+    igraph/src/dtrexc.c
+    igraph/src/dtrmm.c
+    igraph/src/dtrmv.c
+    igraph/src/dtrsen.c
+    igraph/src/dtrsm.c
+    igraph/src/dtrsna.c
+    igraph/src/dtrsv.c
+    igraph/src/dtrsyl.c
+    igraph/src/due.c
+    igraph/src/dummy.c
+    igraph/src/dvout.c
+    igraph/src/ef1asc_.c
+    igraph/src/ef1cmc_.c
+    igraph/src/eigen.c
+    igraph/src/embedding.c
+    igraph/src/endfile.c
+    igraph/src/erf_.c
+    igraph/src/erfc_.c
+    igraph/src/err.c
+    igraph/src/error.c
+    igraph/src/etime_.c
+    igraph/src/exit_.c
+    igraph/src/f77_aloc.c
+    igraph/src/f77vers.c
+    igraph/src/fast_community.c
+    igraph/src/feedback_arc_set.c
+    igraph/src/flow.c
+    igraph/src/fmt.c
+    igraph/src/fmtlib.c
+    igraph/src/foreign.c
+    igraph/src/foreign-dl-lexer.c
+    igraph/src/foreign-dl-parser.c
+    igraph/src/foreign-gml-lexer.c
+    igraph/src/foreign-gml-parser.c
+    igraph/src/foreign-graphml.c
+    igraph/src/foreign-lgl-lexer.c
+    igraph/src/foreign-lgl-parser.c
+    igraph/src/foreign-ncol-lexer.c
+    igraph/src/foreign-ncol-parser.c
+    igraph/src/foreign-pajek-lexer.c
+    igraph/src/foreign-pajek-parser.c
+    igraph/src/forestfire.c
+    igraph/src/fortran_intrinsics.c
+    igraph/src/ftell_.c
+    igraph/src/games.c
+    igraph/src/getenv_.c
+    igraph/src/glet.c
+    igraph/src/glpk_support.c
+    igraph/src/gml_tree.c
+    igraph/src/gss.c
+    igraph/src/h_abs.c
+    igraph/src/hacks.c
+    igraph/src/h_dim.c
+    igraph/src/h_dnnt.c
+    igraph/src/heap.c
+    igraph/src/h_indx.c
+    igraph/src/h_len.c
+    igraph/src/hl_ge.c
+    igraph/src/hl_gt.c
+    igraph/src/hl_le.c
+    igraph/src/hl_lt.c
+    igraph/src/h_mod.c
+    igraph/src/h_nint.c
+    igraph/src/h_sign.c
+    igraph/src/i77vers.c
+    igraph/src/i_abs.c
+    igraph/src/idamax.c
+    igraph/src/i_dim.c
+    igraph/src/i_dnnt.c
+    igraph/src/ieeeck.c
+    igraph/src/igraph_buckets.c
+    igraph/src/igraph_cliquer.c
+    igraph/src/igraph_error.c
+    igraph/src/igraph_estack.c
+    igraph/src/igraph_fixed_vectorlist.c
+    igraph/src/igraph_grid.c
+    igraph/src/igraph_hashtable.c
+    igraph/src/igraph_heap.c
+    igraph/src/igraph_marked_queue.c
+    igraph/src/igraph_psumtree.c
+    igraph/src/igraph_set.c
+    igraph/src/igraph_stack.c
+    igraph/src/igraph_strvector.c
+    igraph/src/igraph_trie.c
+    igraph/src/i_indx.c
+    igraph/src/iio.c
+    igraph/src/iladlc.c
+    igraph/src/iladlr.c
+    igraph/src/ilaenv.c
+    igraph/src/i_len.c
+    igraph/src/ilnw.c
+    igraph/src/i_mod.c
+    igraph/src/i_nint.c
+    igraph/src/inquire.c
+    igraph/src/interrupt.c
+    igraph/src/iparmq.c
+    igraph/src/i_sign.c
+    igraph/src/iterators.c
+    igraph/src/ivout.c
+    igraph/src/kolmogorov.c
+    igraph/src/lad.c
+    igraph/src/lapack.c
+    igraph/src/layout.c
+    igraph/src/layout_dh.c
+    igraph/src/layout_fr.c
+    igraph/src/layout_gem.c
+    igraph/src/layout_kk.c
+    igraph/src/lbfgs.c
+    igraph/src/lbitbits.c
+    igraph/src/lbitshft.c
+    igraph/src/len_trim.c
+    igraph/src/l_ge.c
+    igraph/src/l_gt.c
+    igraph/src/l_le.c
+    igraph/src/l_lt.c
+    igraph/src/lread.c
+    igraph/src/lsame.c
+    igraph/src/lsap.c
+    igraph/src/lwrite.c
+    igraph/src/matching.c
+    igraph/src/math.c
+    igraph/src/matrix.c
+    igraph/src/maximal_cliques.c
+    igraph/src/memory.c
+    igraph/src/microscopic_update.c
+    igraph/src/mixing.c
+    igraph/src/motifs.c
+    igraph/src/open.c
+    igraph/src/operators.c
+    igraph/src/optimal_modularity.c
+    igraph/src/options.c
+    igraph/src/other.c
+    igraph/src/paths.c
+    igraph/src/plfit.c
+    igraph/src/pow_ci.c
+    igraph/src/pow_dd.c
+    igraph/src/pow_di.c
+    igraph/src/pow_hh.c
+    igraph/src/pow_ii.c
+    igraph/src/pow_ri.c
+    igraph/src/pow_zi.c
+    igraph/src/pow_zz.c
+    igraph/src/progress.c
+    igraph/src/qsort.c
+    igraph/src/qsort_r.c
+    igraph/src/r_abs.c
+    igraph/src/r_acos.c
+    igraph/src/random.c
+    igraph/src/random_walk.c
+    igraph/src/r_asin.c
+    igraph/src/r_atan.c
+    igraph/src/r_atn2.c
+    igraph/src/r_cnjg.c
+    igraph/src/r_cos.c
+    igraph/src/r_cosh.c
+    igraph/src/rdfmt.c
+    igraph/src/r_dim.c
+    igraph/src/reorder.c
+    igraph/src/rewind.c
+    igraph/src/r_exp.c
+    igraph/src/r_imag.c
+    igraph/src/r_int.c
+    igraph/src/r_lg10.c
+    igraph/src/r_log.c
+    igraph/src/r_mod.c
+    igraph/src/r_nint.c
+    igraph/src/rsfe.c
+    igraph/src/r_sign.c
+    igraph/src/r_sin.c
+    igraph/src/r_sinh.c
+    igraph/src/rsli.c
+    igraph/src/rsne.c
+    igraph/src/r_sqrt.c
+    igraph/src/r_tan.c
+    igraph/src/r_tanh.c
+    igraph/src/sbm.c
+    igraph/src/scan.c
+    igraph/src/s_cat.c
+    igraph/src/scg_approximate_methods.c
+    igraph/src/scg.c
+    igraph/src/scg_exact_scg.c
+    igraph/src/scg_kmeans.c
+    igraph/src/scg_optimal_method.c
+    igraph/src/scg_utils.c
+    igraph/src/s_cmp.c
+    igraph/src/s_copy.c
+    igraph/src/second.c
+    igraph/src/separators.c
+    igraph/src/sfe.c
+    igraph/src/sig_die.c
+    igraph/src/signal_.c
+    igraph/src/signbit.c
+    igraph/src/sir.c
+    igraph/src/spanning_trees.c
+    igraph/src/sparsemat.c
+    igraph/src/s_paus.c
+    igraph/src/spectral_properties.c
+    igraph/src/spmatrix.c
+    igraph/src/s_rnge.c
+    igraph/src/s_stop.c
+    igraph/src/statusbar.c
+    igraph/src/st-cuts.c
+    igraph/src/structural_properties.c
+    igraph/src/structure_generators.c
+    igraph/src/sue.c
+    igraph/src/sugiyama.c
+    igraph/src/system_.c
+    igraph/src/topology.c
+    igraph/src/triangles.c
+    igraph/src/type_indexededgelist.c
+    igraph/src/types.c
+    igraph/src/typesize.c
+    igraph/src/uio.c
+    igraph/src/uninit.c
+    igraph/src/util.c
+    igraph/src/vector.c
+    igraph/src/vector_ptr.c
+    igraph/src/version.c
+    igraph/src/visitors.c
+    igraph/src/wref.c
+    igraph/src/wrtfmt.c
+    igraph/src/wsfe.c
+    igraph/src/wsle.c
+    igraph/src/wsne.c
+    igraph/src/xerbla.c
+    igraph/src/xwsne.c
+    igraph/src/z_abs.c
+    igraph/src/z_cos.c
+    igraph/src/z_div.c
+    igraph/src/zeroin.c
+    igraph/src/zeta.c
+    igraph/src/z_exp.c
+    igraph/src/z_log.c
+    igraph/src/z_sin.c
+    igraph/src/z_sqrt.c
+
+  cxx-sources:
+    igraph/src/clustertool.cpp
+    igraph/src/degree_sequence.cpp
+    igraph/src/DensityGrid_3d.cpp
+    igraph/src/DensityGrid.cpp
+    igraph/src/drl_graph_3d.cpp
+    igraph/src/drl_graph.cpp
+    igraph/src/drl_layout_3d.cpp
+    igraph/src/drl_layout.cpp
+    igraph/src/drl_parse.cpp
+    igraph/src/gengraph_box_list.cpp
+    igraph/src/gengraph_degree_sequence.cpp
+    igraph/src/gengraph_graph_molloy_hash.cpp
+    igraph/src/gengraph_graph_molloy_optimized.cpp
+    igraph/src/gengraph_mr-connected.cpp
+    igraph/src/gengraph_powerlaw.cpp
+    igraph/src/gengraph_random.cpp
+    igraph/src/NetDataTypes.cpp
+    igraph/src/NetRoutines.cpp
+    igraph/src/pottsmodel_2.cpp
+    igraph/src/prpack_base_graph.cpp
+    igraph/src/prpack.cpp
+    igraph/src/prpack_igraph_graph.cpp
+    igraph/src/prpack_preprocessed_ge_graph.cpp
+    igraph/src/prpack_preprocessed_gs_graph.cpp
+    igraph/src/prpack_preprocessed_scc_graph.cpp
+    igraph/src/prpack_preprocessed_schur_graph.cpp
+    igraph/src/prpack_result.cpp
+    igraph/src/prpack_solver.cpp
+    igraph/src/prpack_utils.cpp
+    igraph/src/walktrap_communities.cpp
+    igraph/src/walktrap.cpp
+    igraph/src/walktrap_graph.cpp
+    igraph/src/walktrap_heap.cpp
+    igraph/src/bliss.cc
+    igraph/src/bliss_heap.cc
+    igraph/src/defs.cc
+    igraph/src/graph.cc
+    igraph/src/igraph_hrg.cc
+    igraph/src/igraph_hrg_types.cc
+    igraph/src/infomap.cc
+    igraph/src/infomap_FlowGraph.cc
+    igraph/src/infomap_Greedy.cc
+    igraph/src/infomap_Node.cc
+    igraph/src/orbit.cc
+    igraph/src/partition.cc
+    igraph/src/uintseqhash.cc
+    igraph/src/utils.cc
+
+  include-dirs:
+    include
+    igraph/include
+    igraph/include/f2c
+    igraph/include/prpack
+    igraph/include/cs
+    igraph/include/cliquer
+    igraph/include/bliss
+    igraph/include/plfit
+  
+  cxx-options: -DPRPACK_IGRAPH_SUPPORT
+
 test-suite tests
   type: exitcode-stdio-1.0
   hs-source-dirs: tests
@@ -102,3 +650,4 @@
 source-repository  head
   type: git
   location: https://github.com/kaizhang/haskell-igraph.git
+
diff --git a/igraph/AUTHORS b/igraph/AUTHORS
new file mode 100644
--- /dev/null
+++ b/igraph/AUTHORS
@@ -0,0 +1,4 @@
+Gabor Csardi <csardi.gabor@gmail.com>
+Tamas Nepusz <ntamas@gmail.com>
+Szabolcs Horvat <szhorvat@gmail.com>
+Vincent Traag <v.a.traag@cwts.leidenuniv.nl>
diff --git a/igraph/COPYING b/igraph/COPYING
new file mode 100644
--- /dev/null
+++ b/igraph/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/igraph/include/DensityGrid.h b/igraph/include/DensityGrid.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/DensityGrid.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __DENSITY_GRID_H__
+#define __DENSITY_GRID_H__
+
+
+// Compile time adjustable parameters
+
+
+#include <deque>
+
+using namespace std;
+
+#include "drl_layout.h"
+#include "drl_Node.h"
+#ifdef MUSE_MPI
+    #include <mpi.h>
+#endif
+
+namespace drl {
+
+class DensityGrid {
+
+public:
+
+    // Methods
+    void Init();
+    void Subtract(Node &n, bool first_add, bool fine_first_add, bool fineDensity);
+    void Add(Node &n, bool fineDensity );
+    float GetDensity(float Nx, float Ny, bool fineDensity);
+
+    // Contructor/Destructor
+    DensityGrid() {};
+    ~DensityGrid();
+
+private:
+
+    // Private Members
+    void Subtract( Node &N );
+    void Add( Node &N );
+    void fineSubtract( Node &N );
+    void fineAdd( Node &N );
+
+    // new dynamic variables -- SBM
+    float (*fall_off)[RADIUS * 2 + 1];
+    float (*Density)[GRID_SIZE];
+    deque<Node>* Bins;
+
+    // old static variables
+    //float fall_off[RADIUS*2+1][RADIUS*2+1];
+    //float Density[GRID_SIZE][GRID_SIZE];
+    //deque<Node *> Bins[GRID_SIZE][GRID_SIZE];
+};
+
+} // namespace drl
+
+#endif // __DENSITY_GRID_H__
+
diff --git a/igraph/include/DensityGrid_3d.h b/igraph/include/DensityGrid_3d.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/DensityGrid_3d.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __DENSITY_GRID_H__
+#define __DENSITY_GRID_H__
+
+
+// Compile time adjustable parameters
+
+
+#include <deque>
+
+using namespace std;
+
+#include "drl_layout_3d.h"
+#include "drl_Node_3d.h"
+#ifdef MUSE_MPI
+    #include <mpi.h>
+#endif
+
+namespace drl3d {
+
+class DensityGrid {
+
+public:
+
+    // Methods
+    void Init();
+    void Subtract(Node &n, bool first_add, bool fine_first_add, bool fineDensity);
+    void Add(Node &n, bool fineDensity );
+    float GetDensity(float Nx, float Ny, float Nz, bool fineDensity);
+
+    // Contructor/Destructor
+    DensityGrid() {};
+    ~DensityGrid();
+
+private:
+
+    // Private Members
+    void Subtract( Node &N );
+    void Add( Node &N );
+    void fineSubtract( Node &N );
+    void fineAdd( Node &N );
+
+    // new dynamic variables -- SBM
+    float (*fall_off)[RADIUS * 2 + 1][RADIUS * 2 + 1];
+    float (*Density)[GRID_SIZE][GRID_SIZE];
+    deque<Node>* Bins;
+
+    // old static variables
+    //float fall_off[RADIUS*2+1][RADIUS*2+1];
+    //float Density[GRID_SIZE][GRID_SIZE];
+    //deque<Node *> Bins[GRID_SIZE][GRID_SIZE];
+};
+
+} // namespace drl3d
+
+#endif // __DENSITY_GRID_H__
+
diff --git a/igraph/include/NetDataTypes.h b/igraph/include/NetDataTypes.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/NetDataTypes.h
@@ -0,0 +1,926 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Jörg Reichardt
+   The original copyright notice follows here */
+
+/***************************************************************************
+                          NetDataTypes.h  -  description
+                             -------------------
+    begin                : Mon Oct 6 2003
+    copyright            : (C) 2003 by Joerg Reichardt
+    email                : reichardt@mitte
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+#ifndef NETDATATYPES_H
+#define NETDATATYPES_H
+
+#include <string.h>
+
+//###########################################################################################
+
+struct HUGE_INDEX {
+    unsigned int field_index;
+    unsigned long in_field_index;
+};
+
+template <class DATA> class HugeArray {
+private:
+    unsigned long int size;
+    unsigned int highest_field_index;
+    unsigned long max_bit_left;
+    unsigned long max_index;
+    DATA *data;
+    DATA *fields[32];
+public:
+    HUGE_INDEX get_huge_index(unsigned long);
+    DATA &Set(unsigned long);
+    DATA Get(unsigned long);
+    HugeArray(void);
+    ~HugeArray(void);
+    DATA &operator[](unsigned long);
+    unsigned long Size(void) {
+        return max_index;
+    }
+} ;
+//###############################################################################################
+template <class L_DATA > class DLList;
+template <class L_DATA > class DL_Indexed_List;
+template <class L_DATA > class ClusterList;
+template <class L_DATA > class DLList_Iter;
+
+template <class L_DATA>
+class DLItem {
+    friend class DLList<L_DATA> ;
+    friend class DL_Indexed_List<L_DATA>;
+    friend class DLList_Iter<L_DATA>;
+private:
+    L_DATA  item;
+    unsigned long index;
+    DLItem *previous;
+    DLItem *next;
+    DLItem(L_DATA i, unsigned long ind);
+    DLItem(L_DATA i, unsigned long ind, DLItem<L_DATA> *p, DLItem<L_DATA> *n);
+    ~DLItem();
+public:
+    void del() {
+        delete item;
+    }
+};
+
+template <class L_DATA >
+class DLList {
+    friend class DLList_Iter<L_DATA>;
+protected:
+    DLItem<L_DATA>  *head;
+    DLItem<L_DATA>  *tail;
+    unsigned long number_of_items;
+    DLItem<L_DATA> *pInsert(L_DATA, DLItem<L_DATA>*);
+    L_DATA pDelete(DLItem<L_DATA>*);
+public:
+    DLList(void);
+    ~DLList();
+    unsigned long Size(void) {
+        return number_of_items;
+    }
+    int Insert(L_DATA, unsigned long);
+    int Delete(unsigned long);
+    int fDelete(L_DATA);
+    L_DATA Push(L_DATA);
+    L_DATA Pop(void);
+    L_DATA Get(unsigned long);
+    int Enqueue(L_DATA);
+    L_DATA Dequeue(void);
+    unsigned long Is_In_List(L_DATA);
+    void delete_items();
+};
+
+template <class L_DATA>
+class DL_Indexed_List : virtual public DLList<L_DATA> {
+    friend class DLList_Iter<L_DATA>;
+private:
+    DLItem<L_DATA> *pInsert(L_DATA, DLItem<L_DATA>*);
+    L_DATA pDelete(DLItem<L_DATA>*);
+    HugeArray<DLItem<L_DATA>*> array;
+    unsigned long last_index;
+public:
+    DL_Indexed_List(void);
+    ~DL_Indexed_List();
+    L_DATA Push(L_DATA);
+    L_DATA Pop(void);
+    L_DATA Get(unsigned long);
+};
+
+//#####################################################################################################
+
+template <class L_DATA> class DLList_Iter {
+private:
+    DLList<L_DATA>  *list;
+    DLItem<L_DATA> *current;
+    bool end_reached;
+public:
+    DLList_Iter(void);
+    ~DLList_Iter() {
+        end_reached = true;
+    };
+    L_DATA Next(void);
+    L_DATA Previous(void);
+    L_DATA First(DLList<L_DATA> *l);
+    L_DATA Last(DLList<L_DATA> *l);
+    bool End(void) {
+        return end_reached;
+    }
+    DLItem<L_DATA> *Get_Current(void) {
+        return current;
+    }
+    L_DATA Get_Current_Item(void) {
+        return current->item;
+    }
+    void Set_Current(DLItem<L_DATA> *c) {
+        current = c;
+    }
+    void Set_Status(bool s) {
+        end_reached = s;
+    }
+    bool Swap(DLList_Iter<L_DATA>);  //swapt die beiden Elemente, wenn sie in der gleichen Liste stehen!!
+
+};
+
+//#####################################################################################################
+struct RGBcolor {
+    unsigned int red;
+    unsigned int green;
+    unsigned int blue;
+    char pajek_c[20];
+};
+//-------------------------------------------------------------------------------
+
+class NLink;
+
+class NNode {
+    friend class NLink;
+private :
+    unsigned long index;
+    unsigned long cluster_index;
+    unsigned long marker, affiliations;
+    unsigned long *state_history;
+    unsigned int max_states;
+    long distance;
+    double clustering;
+    double weight;
+    double affinity;
+//    double old_weight;
+
+    DLList<NNode*> *neighbours;    //list with pointers to neighbours
+    DLList<NLink*> *n_links;
+    DLList<NLink*> *global_link_list;
+    char name[255];
+    RGBcolor color;
+public :
+    NNode(unsigned long, unsigned long, DLList<NLink*>*, char*, int);
+    ~NNode();
+    unsigned long Get_Index(void)  {
+        return (index);
+    }
+    unsigned long Get_ClusterIndex(void) {
+        return (cluster_index);
+    }
+    unsigned long Get_Marker(void) {
+        return marker;
+    }
+    void Set_Marker(unsigned long m) {
+        marker = m;
+    }
+    unsigned long Get_Affiliations(void) {
+        return affiliations;
+    }
+    void Set_Affiliations(unsigned long m) {
+        affiliations = m;
+    }
+    void Set_ClusterIndex(unsigned long ci) {
+        cluster_index = ci;
+        return;
+    }
+    void Set_Index(unsigned long i) {
+        index = i;
+        return;
+    }
+    unsigned long Get_Degree(void) {
+        return (neighbours->Size());
+    }
+    char *Get_Name(void) {
+        return name;
+    }
+    void Set_Name(char* n) {
+        strcpy(name, n);
+    }
+    double Get_Links_Among_Neigbours(void);
+    double Get_Clustering(void);
+    double Get_Weight(void) {
+        return weight;
+    }
+    double Get_Affinity(void) {
+        return affinity;
+    }
+    unsigned long *Get_StateHistory(void) {
+        return state_history;
+    }
+    void Add_StateHistory(unsigned int q);
+    //  double Get_OldWeight(void) {return old_weight;}
+    void Set_Weight(double w) {
+        weight = w;
+    }
+    void Set_Affinity(double w) {
+        affinity = w;
+    }
+
+    //  void Set_OldWeight(double w) {old_weight=w;}
+    long Get_Distance(void) {
+        return distance;
+    }
+    void Set_Distance(long d) {
+        distance = d;
+    }
+    int  Connect_To(NNode*, double);
+    DLList<NNode*> *Get_Neighbours(void) {
+        return neighbours;
+    }
+    DLList<NLink*> *Get_Links(void) {
+        return n_links;
+    }
+    int  Disconnect_From(NNode*);
+    int  Disconnect_From_All(void);
+    bool Is_Linked_To(NNode*);
+    RGBcolor Get_Color(void) {
+        return color;
+    }
+    void Set_Color(RGBcolor c);
+    NLink *Get_LinkToNeighbour(NNode *neighbour);
+};
+
+//#####################################################################################################
+
+class NLink {
+    friend class NNode;
+private :
+    NNode *start;
+    NNode *end;
+    double weight;
+    double old_weight;
+    unsigned long index;
+    unsigned long marker;
+public :
+    NLink( NNode*, NNode*, double);
+    ~NLink();
+    unsigned long Get_Start_Index(void)  {
+        return (start->Get_Index());
+    }
+    unsigned long Get_End_Index(void)    {
+        return (end->Get_Index());
+    }
+    NNode *Get_Start(void) {
+        return (start);
+    }
+    NNode *Get_End(void) {
+        return (end);
+    }
+    double Get_Weight(void) {
+        return weight;
+    }
+    void Set_Weight(double w) {
+        weight = w;
+    }
+    double Get_OldWeight(void) {
+        return old_weight;
+    }
+    void Set_OldWeight(double w) {
+        old_weight = w;
+    }
+    unsigned long Get_Marker(void) {
+        return marker;
+    }
+    void Set_Marker(unsigned long m) {
+        marker = m;
+    }
+    unsigned long Get_Index() {
+        return index;
+    }
+    void Set_Index(unsigned long i) {
+        index = i;
+    }
+};
+
+//#####################################################################################################
+
+template <class L_DATA>  class ClusterList : public DLList<L_DATA> {
+    friend class DLList_Iter<L_DATA>;
+private:
+    long links_out_of_cluster;
+    unsigned long links_inside_cluster;
+    unsigned long frequency;
+    double cluster_energy;
+    DLList<L_DATA> *candidates;
+    long marker;
+public:
+    ClusterList(void);
+    ~ClusterList();
+    long Get_Links_OOC(void) {
+        return (links_out_of_cluster);
+    }
+    void Set_Links_OOC(long looc) {
+        links_out_of_cluster = looc;
+    }
+    unsigned long Get_Links_IC(void) {
+        return (links_inside_cluster);
+    }
+    unsigned long Get_Frequency(void) {
+        return (frequency);
+    }
+    void IncreaseFrequency(void) {
+        frequency++;
+    }
+    void Set_Links_IC(unsigned long lic) {
+        links_inside_cluster = lic;
+    }
+    double Get_Energy(void) {
+        return (cluster_energy);
+    }
+    void Set_Energy(double e) {
+        cluster_energy = e;
+    }
+    DLList<L_DATA> *Get_Candidates(void) {
+        return candidates;
+    }
+    bool operator<(ClusterList<L_DATA> &b);
+    bool operator==(ClusterList <L_DATA> &b);
+    long Get_Marker(void) {
+        return marker;
+    }
+    void Set_Marker(long m) {
+        marker = m;
+    }
+};
+//#####################################################################################################
+template <class L_DATA>
+class DL_Node_List : virtual public DL_Indexed_List<NNode*> {
+    friend class DLList_Iter<L_DATA>;
+private:
+    DLItem<L_DATA> *pInsert(NNode*, DLItem<NNode*>*);
+    NNode* pDelete(DLItem<NNode*>*);
+    HugeArray<DLItem<NNode*>*> array;
+    unsigned long last_index;
+public:
+    DL_Node_List(void);
+    ~DL_Node_List();
+    NNode* Push(NNode*);
+    NNode* Pop(void);
+    NNode* Get(unsigned long);
+    int Delete(unsigned long);
+
+};
+//#####################################################################################################
+
+
+
+struct cluster_join_move {
+    ClusterList<NNode*> *c1;
+    ClusterList<NNode*> *c2;
+    double joint_energy;
+    long joint_looc;
+    unsigned long joint_lic;
+} ;
+
+struct network {
+    DL_Indexed_List<NNode*> *node_list;
+    DL_Indexed_List<NLink*> *link_list;
+    DL_Indexed_List<ClusterList<NNode*>*> *cluster_list;
+    DL_Indexed_List<cluster_join_move*> *moveset;
+    unsigned long max_k;
+    unsigned long min_k;
+    unsigned long diameter;
+    double av_weight;
+    double max_weight;
+    double min_weight;
+    double sum_weights;
+    double av_k;
+    double av_bids;
+    unsigned long max_bids;
+    unsigned long min_bids;
+    unsigned long sum_bids;
+} ;
+
+/*
+struct network
+{
+  DLList<NNode*> *node_list;
+  DLList<NLink*> *link_list;
+  DLList<ClusterList<NNode*>*> *cluster_list;
+  DLList<cluster_join_move*> *moveset;
+} ;
+*/
+
+template <class DATA>
+HugeArray<DATA>::HugeArray(void) {
+    max_bit_left = 1 << 31; //wir setzen das 31. Bit auf 1
+    size = 2;
+    max_index = 0;
+    highest_field_index = 0;
+    data = new DATA[2]; //ein extra Platz fuer das Nullelement
+    data[0] = 0;
+    data[1] = 0;
+    for (int i = 0; i < 32; i++) {
+        fields[i] = NULL;
+    }
+    fields[highest_field_index] = data;
+}
+
+template <class DATA> HugeArray<DATA>::~HugeArray(void) {
+    for (unsigned int i = 0; i <= highest_field_index; i++) {
+        data = fields[i];
+        delete [] data;
+    }
+}
+
+template <class DATA>
+HUGE_INDEX HugeArray<DATA>::get_huge_index(unsigned long index) {
+    HUGE_INDEX h_index;
+    unsigned int shift_index = 0;
+    unsigned long help_index;
+    help_index = index;
+    if (index < 2) {
+        h_index.field_index = 0;
+        h_index.in_field_index = index;
+        return h_index;
+    }
+    // wie oft muessen wir help_index nach links shiften, damit das 31. Bit gesetzt ist??
+    while (!(max_bit_left & help_index)) {
+        help_index <<= 1;
+        shift_index++;
+    }
+    h_index.field_index = 31 - shift_index;   // das hoechste  besetzte Bit im Index
+    help_index = 1 << h_index.field_index;  // in help_index wird das hoechste besetzte Bit von Index gesetzt
+    h_index.in_field_index = (index ^ help_index); // index XOR help_index, womit alle bits unter dem hoechsten erhalten bleiben
+    return h_index;
+}
+
+template <class DATA>
+DATA &HugeArray<DATA>::Set(unsigned long int index) {
+    HUGE_INDEX h_index;
+    unsigned long data_size;
+    while (size < index + 1) {
+        highest_field_index++;
+        data_size = 1 << highest_field_index;
+        data = new DATA[data_size];
+        for (unsigned long i = 0; i < data_size; i++) {
+            data[i] = 0;
+        }
+        size = size + data_size; //overflow noch abfangen
+        //printf("Vergroesserung auf: %u bei index %u\n",size,index);
+        fields[highest_field_index] = data;
+    }
+    h_index = get_huge_index(index);
+//printf("index %lu = %lu . %lu\n",index,h_index.field_index,h_index.in_field_index);
+    data = fields[h_index.field_index];
+    if (max_index < index) {
+        max_index = index;
+    }
+    return (data[h_index.in_field_index]);
+}
+
+template <class DATA>
+DATA HugeArray<DATA>::Get(unsigned long index) {
+    return (Set(index));
+}
+
+
+template <class DATA>
+DATA &HugeArray<DATA>::operator[](unsigned long index) {
+    return (Set(index));
+}
+
+
+//###############################################################################
+template <class L_DATA>
+DLItem<L_DATA>::DLItem(L_DATA i, unsigned long ind) : item(i), index(ind), previous(0), next(0) {
+}
+
+template <class L_DATA>
+DLItem<L_DATA>::DLItem(L_DATA i, unsigned long ind, DLItem<L_DATA> *p, DLItem<L_DATA> *n) : item(i), index(ind), previous(p), next(n) {
+}
+
+template <class L_DATA>
+DLItem<L_DATA>::~DLItem() {
+//delete item;      //eigentlich muessten wir pruefen, ob item ueberhaupt ein Pointer ist...
+//previous=NULL;
+//next=NULL;
+}
+
+
+//######################################################################################################################
+template <class L_DATA>
+DLList<L_DATA>::DLList(void) {
+    head = tail = NULL;
+    number_of_items = 0;
+    head = new DLItem<L_DATA>(NULL, 0); //fuer head und Tail gibt es das gleiche Array-Element!! Vorsicht!!
+    tail = new DLItem<L_DATA>(NULL, 0);
+    if ( !head || !tail ) {
+        if (head) {
+            delete (head);
+        }
+        if (tail) {
+            delete (tail);
+        }
+        return;
+    }  else {
+        head->next = tail;
+        tail->previous = head;
+    }
+}
+
+template <class L_DATA>
+DLList<L_DATA>::~DLList() {
+    DLItem<L_DATA> *cur = head, *next;
+    while (cur) {
+        next = cur->next;
+        delete (cur);
+        cur = next;
+    }
+    number_of_items = 0;
+    //  printf("Liste Zerstoert!\n");
+}
+
+template <class L_DATA>
+void DLList<L_DATA>::delete_items() {
+    DLItem<L_DATA> *cur, *next;
+    cur = this->head;
+    while (cur) {
+        next = cur->next;
+        cur->del();
+        cur = next;
+    }
+    this->number_of_items = 0;
+}
+
+//privates Insert
+template <class L_DATA>
+DLItem<L_DATA> *DLList<L_DATA>::pInsert(L_DATA data, DLItem<L_DATA> *pos) {
+    DLItem<L_DATA> *i = new DLItem<L_DATA>(data, number_of_items + 1, pos->previous, pos);
+    if (i) {
+        pos->previous->next = i;
+        pos->previous = i;
+        number_of_items++;
+        return (i);
+    } else {
+        return (0);
+    }
+}
+//privates delete
+template <class L_DATA>
+L_DATA DLList<L_DATA>::pDelete(DLItem<L_DATA> *i) {
+    L_DATA data = i->item;
+    i->previous->next = i->next;
+    i->next->previous = i->previous;
+//  array[i->index]=0;
+    delete (i);
+    number_of_items--;
+    return (data);
+}
+//oeffentliches Insert
+template <class L_DATA>
+int DLList<L_DATA>::Insert(L_DATA data, unsigned long pos) {
+    if ((pos < 0) || (pos > (number_of_items))) {
+        return (0);
+    }
+    DLItem<L_DATA> *cur = head;
+    while (pos--) {
+        cur = cur->next;
+    }
+    return (pInsert(data, cur) != 0);
+}
+//oeffentliche Delete
+template <class L_DATA>
+int DLList<L_DATA>::Delete(unsigned long pos) {
+    if ((pos < 0) || (pos > (number_of_items))) {
+        return (0);
+    }
+    DLItem<L_DATA> *cur = head;
+    while (pos--) {
+        cur = cur->next;
+    }
+    return (pDelete(cur) != 0);
+}
+
+//oeffentliche Delete
+template <class L_DATA>
+int DLList<L_DATA>::fDelete(L_DATA data) {
+    if ((number_of_items == 0) || (!data)) {
+        return (0);
+    }
+    DLItem<L_DATA> *cur;
+    cur = head->next;
+    while ((cur != tail) && (cur->item != data)) {
+        cur = cur->next;
+    }
+    if (cur != tail) {
+        return (pDelete(cur) != 0);
+    }
+    return (0);
+}
+
+template <class L_DATA>
+L_DATA DLList<L_DATA>::Push(L_DATA data) {
+    DLItem<L_DATA> *tmp;
+    tmp = pInsert(data, tail);
+    if (tmp) {
+        return (tmp->item);
+    }
+    return (0);
+}
+
+template <class L_DATA>
+L_DATA DLList<L_DATA>::Pop(void) {
+    return (pDelete(tail->previous));
+}
+
+
+template <class L_DATA>
+L_DATA DLList<L_DATA>::Get(unsigned long pos) {
+    if ((pos < 1) || (pos > (number_of_items + 1))) {
+        return (0);
+    }
+//  return(array[pos]->item);
+    DLItem<L_DATA> *cur = head;
+    while (pos--) {
+        cur = cur->next;
+    }
+    return (cur->item);
+}
+
+
+template <class L_DATA>
+int DLList<L_DATA>::Enqueue(L_DATA data) {
+    return (pInsert(data, tail) != 0);
+}
+
+template <class L_DATA>
+L_DATA DLList<L_DATA>::Dequeue(void) {
+    return (pDelete(head->next));
+}
+
+//gibt Index des gesuchte Listenelement zurueck, besser waere eigentlich zeiger
+template <class L_DATA>
+unsigned long DLList<L_DATA>::Is_In_List(L_DATA data) {
+    DLItem<L_DATA> *cur = head, *next;
+    unsigned long pos = 0;
+    while (cur) {
+        next = cur->next;
+        if (cur->item == data) {
+            return (pos) ;
+        }
+        cur = next;
+        pos++;
+    }
+    return (0);
+}
+
+//######################################################################################################################
+template <class L_DATA>
+DL_Indexed_List<L_DATA>::DL_Indexed_List(void) : DLList<L_DATA>() {
+    last_index = 0;
+}
+
+template <class L_DATA>
+DL_Indexed_List<L_DATA>::~DL_Indexed_List() {
+    /* This is already done by the DLList destructor */
+    /*   DLItem<L_DATA> *cur, *next; */
+    /*   cur=this->head; */
+    /*   while (cur) */
+    /*     { */
+    /*       next=cur->next; */
+    /*       delete(cur); */
+    /*       cur=next; */
+    /*     } */
+    /*     this->number_of_items=0; */
+    //  printf("Liste Zerstoert!\n");
+}
+
+//privates Insert
+template <class L_DATA>
+DLItem<L_DATA> *DL_Indexed_List<L_DATA>::pInsert(L_DATA data, DLItem<L_DATA> *pos) {
+    DLItem<L_DATA> *i = new DLItem<L_DATA>(data, last_index, pos->previous, pos);
+    if (i) {
+        pos->previous->next = i;
+        pos->previous = i;
+        this->number_of_items++;
+        array[last_index] = i;
+        last_index++;
+        return (i);
+    } else {
+        return (0);
+    }
+}
+//privates delete
+template <class L_DATA>
+L_DATA DL_Indexed_List<L_DATA>::pDelete(DLItem<L_DATA> *i) {
+    L_DATA data = i->item;
+    i->previous->next = i->next;
+    i->next->previous = i->previous;
+    array[i->index] = 0;
+    last_index = i->index;
+    delete (i);
+    this->number_of_items--;
+    return (data);
+}
+template <class L_DATA>
+L_DATA DL_Indexed_List<L_DATA>::Push(L_DATA data) {
+    DLItem<L_DATA> *tmp;
+    tmp = pInsert(data, this->tail);
+    if (tmp) {
+        return (tmp->item);
+    }
+    return (0);
+}
+
+template <class L_DATA>
+L_DATA DL_Indexed_List<L_DATA>::Pop(void) {
+    return (pDelete(this->tail->previous));
+}
+
+template <class L_DATA>
+L_DATA DL_Indexed_List<L_DATA>::Get(unsigned long pos) {
+    if (pos > this->number_of_items - 1) {
+        return (0);
+    }
+    return (array[pos]->item);
+}
+
+//#######################################################################################
+
+//************************************************************************************************************
+template <class L_DATA>
+ClusterList<L_DATA>::ClusterList(void) : DLList<L_DATA>() {
+    links_out_of_cluster = 0;
+    links_inside_cluster = 0;
+    frequency = 1;
+    cluster_energy = 1e30;
+    candidates = new DLList<L_DATA>();
+    marker = 0;
+}
+
+template <class L_DATA>
+ClusterList<L_DATA>::~ClusterList() {
+    while (candidates->Size()) {
+        candidates->Pop();
+    }
+    delete candidates;
+}
+
+
+template <class L_DATA>
+bool ClusterList<L_DATA>::operator==(ClusterList<L_DATA> &b) {
+    bool found = false;
+    L_DATA n_cur, n_cur_b;
+    DLList_Iter<L_DATA> a_iter, b_iter;
+
+    if (this->Size() != b.Size()) {
+        return false;
+    }
+
+    n_cur = a_iter.First(this);
+    while (!(a_iter.End())) {
+        found = false;
+        n_cur_b = b_iter.First(&b);
+        while (!(b_iter.End()) && !found) {
+            if (n_cur == n_cur_b) {
+                found = true;
+            }
+            n_cur_b = b_iter.Next();
+        }
+        if (!found) {
+            return false;
+        }
+        n_cur = a_iter.Next();
+    }
+    return (found);
+}
+//A<B ist Wahr, wenn A echte Teilmenge von B ist
+template <class L_DATA>
+bool ClusterList<L_DATA>::operator<(ClusterList<L_DATA> &b) {
+    bool found = false;
+    L_DATA n_cur, n_cur_b;
+    DLList_Iter<L_DATA> a_iter, b_iter;
+
+    if (this->Size() >= b.Size()) {
+        return false;
+    }
+    n_cur = a_iter.First(this);
+    while (!(a_iter.End())) {
+        found = false;
+        n_cur_b = b_iter.First(&b);
+        while (!(b_iter.End()) && !found) {
+            if (n_cur == n_cur_b) {
+                found = true;
+            }
+            n_cur_b = b_iter.Next();
+        }
+        if (!found) {
+            return false;
+        }
+        n_cur = a_iter.Next();
+    }
+    return (found);
+}
+
+//#####################################################################################
+template <class L_DATA>
+DLList_Iter<L_DATA>::DLList_Iter() {
+    list = NULL;
+    current = NULL;
+    end_reached = true;
+}
+
+template <class L_DATA>
+L_DATA DLList_Iter<L_DATA>::Next(void) {
+    current = current->next;
+    if (current == (list->tail)) {
+        end_reached = true;
+    }
+    return (current->item);
+}
+
+template <class L_DATA>
+L_DATA DLList_Iter<L_DATA>::Previous(void) {
+    current = current->previous;
+    if (current == (list->head)) {
+        end_reached = true;
+    }
+    return (current->item);
+}
+
+template <class L_DATA>
+L_DATA DLList_Iter<L_DATA>::First(DLList<L_DATA> *l) {
+    list = l;
+    current = list->head->next;
+    if (current == (list->tail)) {
+        end_reached = true;
+    } else {
+        end_reached = false;
+    }
+    return (current->item);
+}
+
+template <class L_DATA>
+L_DATA DLList_Iter<L_DATA>::Last(DLList<L_DATA> *l) {
+    list = l;
+    current = list->tail->previous;
+    if (current == (list->head)) {
+        end_reached = true;    // falls die List leer ist
+    } else {
+        end_reached = false;
+    }
+    return (current->item);
+}
+
+template <class L_DATA>
+bool DLList_Iter<L_DATA>::Swap(DLList_Iter<L_DATA> b) {
+    L_DATA h;
+    if (list != b.list) {
+        return false;    //elemeten muessen aus der gleichen List stammen
+    }
+    if (end_reached || b.end_reached) {
+        return false;
+    }
+    h = current->item; current->item = b.current->item; b.current->item = h;
+    return true;
+}
+
+#endif
+
diff --git a/igraph/include/NetRoutines.h b/igraph/include/NetRoutines.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/NetRoutines.h
@@ -0,0 +1,61 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Jörg Reichardt
+   The original copyright notice follows here */
+
+/***************************************************************************
+                          NetRoutines.h  -  description
+                             -------------------
+    begin                : Tue Oct 28 2003
+    copyright            : (C) 2003 by Joerg Reichardt
+    email                : reichardt@mitte
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef NETROUTINES_H
+#define NETROUTINES_H
+
+#include "NetDataTypes.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+
+int igraph_i_read_network(const igraph_t *graph,
+                          const igraph_vector_t *weights,
+                          network *net, igraph_bool_t use_weights,
+                          unsigned int states);
+
+void reduce_cliques(DLList<ClusterList<NNode*>*>*, FILE *file);
+void reduce_cliques2(network*, bool,  long );
+void clear_all_markers(network *net);
+
+#endif
+
diff --git a/igraph/include/arith.h b/igraph/include/arith.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/arith.h
@@ -0,0 +1,9 @@
+#define IEEE_8087
+#define Arith_Kind_ASL 1
+#define Long int
+#define Intcast (int)(long)
+#define Double_Align
+#define X64_bit_pointers
+#define NANCHECK
+#define QNaN0 0x0
+#define QNaN1 0xfff80000
diff --git a/igraph/include/array.pmt b/igraph/include/array.pmt
new file mode 100644
--- /dev/null
+++ b/igraph/include/array.pmt
@@ -0,0 +1,90 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+
+int FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, long int n1, long int n2,
+                                  long int n3) {
+    int ret;
+    ret = FUNCTION(igraph_vector, init)(&a->data, n1 * n2 * n3);
+    a->n1 = n1;
+    a->n2 = n2;
+    a->n3 = n3;
+    a->n1n2 = n1 * n2;
+
+    return ret;
+}
+
+void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a) {
+    FUNCTION(igraph_vector, destroy)(&a->data);
+}
+
+long int FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a) {
+    return (a->n1n2) * (a->n3);
+}
+
+long int FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, long int idx) {
+    switch (idx) {
+    case 1: return a->n1;
+        break;
+    case 2: return a->n2;
+        break;
+    case 3: return a->n3;
+        break;
+    }
+    return 0;
+}
+
+int FUNCTION(igraph_array3, resize)(TYPE(igraph_array3) *a, long int n1, long int n2,
+                                    long int n3) {
+    int ret = FUNCTION(igraph_vector, resize)(&a->data, n1 * n2 * n3);
+    a->n1 = n1;
+    a->n2 = n2;
+    a->n3 = n3;
+    a->n1n2 = n1 * n2;
+
+    return ret;
+}
+
+void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a) {
+    FUNCTION(igraph_vector, null)(&a->data);
+}
+
+BASE FUNCTION(igraph_array3, sum)(const TYPE(igraph_array3) *a) {
+    return FUNCTION(igraph_vector, sum)(&a->data);
+}
+
+void FUNCTION(igraph_array3, scale)(TYPE(igraph_array3) *a, BASE by) {
+    FUNCTION(igraph_vector, scale)(&a->data, by);
+}
+
+void FUNCTION(igraph_array3, fill)(TYPE(igraph_array3) *a, BASE e) {
+    FUNCTION(igraph_vector, fill)(&a->data, e);
+}
+
+int FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to,
+                                    const TYPE(igraph_array3) *from) {
+    IGRAPH_CHECK(FUNCTION(igraph_array3, resize)(to, from->n1, from->n2, from->n3));
+    FUNCTION(igraph_vector, update)(&to->data, &from->data);
+    return 0;
+}
diff --git a/igraph/include/atlas-edges.h b/igraph/include/atlas-edges.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/atlas-edges.h
@@ -0,0 +1,1296 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+__BEGIN_DECLS
+
+#include "igraph_types.h"
+
+const igraph_real_t igraph_i_atlas_edges[] = {
+    0, 0,
+    1, 0,
+    2, 0,
+    2, 1, 0, 1,
+    3, 0,
+    3, 1, 1, 2,
+    3, 2, 0, 1, 0, 2,
+    3, 3, 0, 1, 0, 2, 1, 2,
+    4, 0,
+    4, 1, 3, 2,
+    4, 2, 3, 2, 3, 1,
+    4, 2, 0, 1, 3, 2,
+    4, 3, 3, 2, 1, 2, 3, 1,
+    4, 3, 3, 0, 3, 1, 3, 2,
+    4, 3, 0, 1, 1, 2, 0, 3,
+    4, 4, 3, 2, 1, 2, 3, 1, 3, 0,
+    4, 4, 0, 1, 1, 2, 2, 3, 0, 3,
+    4, 5, 0, 1, 0, 2, 0, 3, 1, 2, 2, 3,
+    4, 6, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2,
+    5, 0,
+    5, 1, 4, 3,
+    5, 2, 1, 2, 0, 1,
+    5, 2, 0, 2, 4, 3,
+    5, 3, 1, 2, 0, 1, 2, 0,
+    5, 3, 4, 3, 3, 2, 3, 1,
+    5, 3, 3, 2, 4, 3, 0, 4,
+    5, 3, 1, 2, 0, 1, 4, 3,
+    5, 4, 4, 3, 1, 2, 3, 1, 3, 2,
+    5, 4, 0, 3, 1, 0, 2, 1, 3, 2,
+    5, 4, 4, 3, 4, 0, 4, 1, 4, 2,
+    5, 4, 4, 0, 3, 1, 4, 3, 3, 2,
+    5, 4, 2, 3, 1, 2, 0, 1, 4, 0,
+    5, 4, 1, 2, 0, 1, 2, 0, 4, 3,
+    5, 5, 0, 3, 2, 0, 3, 2, 1, 0, 2, 1,
+    5, 5, 4, 2, 4, 3, 2, 3, 4, 1, 4, 0,
+    5, 5, 0, 1, 1, 2, 2, 3, 0, 4, 0, 2,
+    5, 5, 4, 0, 1, 2, 4, 3, 3, 2, 3, 1,
+    5, 5, 1, 0, 4, 1, 2, 4, 3, 2, 1, 3,
+    5, 5, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4,
+    5, 6, 1, 0, 4, 1, 4, 0, 0, 3, 1, 3, 3, 4,
+    5, 6, 1, 0, 4, 1, 2, 4, 3, 2, 1, 3, 2, 1,
+    5, 6, 1, 0, 4, 1, 2, 4, 3, 2, 1, 3, 3, 4,
+    5, 6, 0, 1, 4, 3, 2, 3, 4, 2, 4, 0, 4, 1,
+    5, 6, 0, 4, 3, 0, 4, 3, 2, 3, 1, 2, 0, 1,
+    5, 6, 2, 1, 0, 2, 3, 0, 1, 3, 4, 1, 0, 4,
+    5, 7, 4, 0, 1, 2, 4, 3, 3, 2, 3, 1, 4, 1, 2, 4,
+    5, 7, 4, 1, 2, 4, 3, 2, 1, 3, 3, 4, 0, 3, 4, 0,
+    5, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1,
+    5, 7, 2, 1, 0, 2, 3, 0, 1, 3, 4, 1, 0, 4, 2, 4,
+    5, 8, 1, 0, 4, 1, 2, 4, 3, 2, 1, 3, 4, 0, 3, 4, 0, 3,
+    5, 8, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3,
+    5, 9, 0, 1, 3, 4, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4,
+    5, 10, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4,
+    6, 0,
+    6, 1, 5, 4,
+    6, 2, 0, 3, 5, 4,
+    6, 2, 1, 3, 1, 2,
+    6, 3, 1, 3, 2, 1, 3, 2,
+    6, 3, 0, 3, 5, 0, 4, 0,
+    6, 3, 4, 3, 5, 4, 0, 5,
+    6, 3, 4, 3, 5, 1, 5, 2,
+    6, 3, 1, 2, 3, 0, 5, 4,
+    6, 4, 0, 3, 4, 0, 5, 4, 0, 5,
+    6, 4, 3, 0, 5, 3, 4, 5, 0, 4,
+    6, 4, 5, 1, 5, 3, 5, 2, 0, 5,
+    6, 4, 4, 3, 3, 1, 4, 0, 3, 2,
+    6, 4, 0, 2, 1, 3, 2, 1, 5, 3,
+    6, 4, 1, 3, 2, 1, 3, 2, 0, 5,
+    6, 4, 1, 2, 0, 3, 5, 0, 4, 0,
+    6, 4, 4, 5, 1, 2, 0, 5, 3, 4,
+    6, 4, 0, 2, 4, 0, 3, 1, 5, 3,
+    6, 5, 3, 0, 5, 3, 4, 5, 0, 4, 5, 0,
+    6, 5, 5, 3, 3, 1, 3, 2, 4, 3, 4, 5,
+    6, 5, 5, 3, 5, 4, 2, 3, 3, 4, 0, 4,
+    6, 5, 4, 3, 1, 2, 4, 0, 3, 2, 3, 1,
+    6, 5, 1, 4, 3, 4, 4, 0, 2, 1, 3, 2,
+    6, 5, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4,
+    6, 5, 5, 3, 5, 4, 5, 0, 5, 1, 5, 2,
+    6, 5, 1, 4, 5, 1, 1, 0, 2, 1, 2, 3,
+    6, 5, 0, 1, 3, 4, 0, 2, 3, 0, 5, 3,
+    6, 5, 1, 0, 2, 1, 2, 4, 1, 3, 5, 3,
+    6, 5, 4, 3, 0, 5, 4, 0, 3, 2, 3, 1,
+    6, 5, 1, 2, 0, 1, 4, 5, 1, 3, 2, 3,
+    6, 5, 0, 1, 0, 5, 2, 3, 3, 4, 4, 5,
+    6, 5, 4, 3, 5, 1, 5, 2, 0, 3, 4, 0,
+    6, 5, 1, 2, 3, 0, 5, 3, 4, 5, 0, 4,
+    6, 6, 0, 3, 5, 0, 4, 5, 3, 4, 5, 3, 4, 0,
+    6, 6, 1, 4, 2, 4, 4, 0, 2, 3, 3, 1, 3, 4,
+    6, 6, 1, 4, 2, 4, 4, 0, 2, 1, 3, 1, 2, 3,
+    6, 6, 2, 0, 5, 4, 4, 3, 5, 3, 4, 0, 2, 4,
+    6, 6, 3, 2, 4, 3, 0, 4, 1, 0, 2, 1, 0, 3,
+    6, 6, 4, 1, 3, 1, 4, 2, 3, 2, 2, 0, 1, 0,
+    6, 6, 5, 2, 5, 3, 5, 4, 3, 4, 5, 1, 5, 0,
+    6, 6, 4, 3, 4, 2, 4, 0, 1, 4, 3, 0, 5, 3,
+    6, 6, 4, 3, 3, 5, 5, 4, 5, 1, 3, 2, 4, 0,
+    6, 6, 4, 2, 1, 2, 4, 3, 4, 1, 4, 0, 0, 5,
+    6, 6, 1, 2, 3, 1, 0, 3, 2, 0, 4, 0, 5, 0,
+    6, 6, 2, 0, 4, 2, 1, 4, 2, 1, 3, 1, 5, 3,
+    6, 6, 1, 2, 3, 1, 0, 3, 2, 0, 4, 0, 5, 3,
+    6, 6, 5, 3, 2, 5, 2, 0, 4, 2, 4, 3, 3, 1,
+    6, 6, 0, 2, 3, 4, 1, 0, 5, 3, 4, 5, 3, 0,
+    6, 6, 1, 2, 3, 0, 5, 3, 4, 5, 0, 4, 5, 0,
+    6, 6, 4, 3, 1, 2, 4, 0, 3, 2, 3, 1, 5, 0,
+    6, 6, 1, 4, 2, 4, 4, 0, 0, 5, 3, 1, 2, 3,
+    6, 6, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5,
+    6, 6, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5,
+    6, 6, 1, 3, 2, 1, 3, 2, 0, 4, 5, 0, 4, 5,
+    6, 7, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 0, 5,
+    6, 7, 1, 4, 2, 4, 2, 1, 3, 1, 2, 3, 2, 0, 0, 1,
+    6, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1,
+    6, 7, 0, 1, 3, 2, 0, 2, 3, 0, 3, 1, 5, 1, 5, 2,
+    6, 7, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 3, 4,
+    6, 7, 1, 0, 4, 1, 2, 4, 3, 2, 5, 1, 2, 5, 1, 2,
+    6, 7, 0, 4, 2, 0, 1, 2, 3, 1, 5, 3, 3, 0, 2, 3,
+    6, 7, 1, 4, 2, 4, 2, 3, 2, 1, 3, 1, 4, 5, 0, 4,
+    6, 7, 1, 0, 4, 1, 2, 4, 3, 2, 5, 1, 2, 5, 4, 5,
+    6, 7, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4,
+    6, 7, 0, 5, 4, 0, 5, 4, 0, 2, 3, 0, 3, 2, 0, 1,
+    6, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 4, 1,
+    6, 7, 0, 1, 4, 0, 1, 4, 0, 2, 3, 0, 3, 2, 3, 5,
+    6, 7, 1, 4, 2, 4, 4, 0, 0, 5, 3, 1, 2, 3, 3, 4,
+    6, 7, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1,
+    6, 7, 1, 5, 0, 1, 4, 0, 3, 4, 2, 3, 1, 2, 0, 3,
+    6, 7, 1, 4, 2, 4, 4, 0, 0, 5, 3, 1, 2, 3, 2, 1,
+    6, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 5, 1,
+    6, 7, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3,
+    6, 7, 5, 0, 3, 5, 2, 3, 0, 2, 1, 3, 4, 1, 3, 4,
+    6, 7, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 2, 3,
+    6, 7, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3,
+    6, 7, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3,
+    6, 7, 1, 2, 0, 1, 2, 0, 3, 0, 4, 3, 5, 4, 3, 5,
+    6, 8, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1,
+    6, 8, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3,
+    6, 8, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 0, 0, 4,
+    6, 8, 1, 2, 3, 1, 0, 3, 1, 0, 2, 0, 3, 2, 5, 3, 4, 0,
+    6, 8, 0, 1, 2, 4, 0, 2, 5, 2, 3, 1, 3, 2, 2, 1, 4, 1,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 1, 5,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4,
+    6, 8, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 2, 1, 5, 1,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0,
+    6, 8, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 3, 0, 5, 1,
+    6, 8, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 5, 3,
+    6, 8, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 0, 5, 5, 4,
+    6, 8, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 5, 1, 5, 3,
+    6, 8, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 2, 0, 2,
+    6, 8, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2,
+    6, 8, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 1, 4, 0, 1,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0,
+    6, 8, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 1,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 5, 2, 5, 0,
+    6, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 1, 5, 2,
+    6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3,
+    6, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 2,
+    6, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4,
+    6, 9, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 4, 5,
+    6, 9, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3,
+    6, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 5,
+    6, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5,
+    6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0,
+    6, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 0, 4, 1, 0, 4, 1,
+    6, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1,
+    6, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0,
+    6, 9, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5,
+    6, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0,
+    6, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4,
+    6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1,
+    6, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0,
+    6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3,
+    6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1,
+    6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2,
+    6, 9, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2,
+    6, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 5, 2, 4, 1,
+    6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2,
+    6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 4, 5,
+    6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 5,
+    6, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 0,
+    6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5,
+    6, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 4,
+    6, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2,
+    6, 10, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2,
+    6, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2,
+    6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1,
+    6, 10, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3,
+    6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4,
+    6, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1,
+    6, 10, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 3, 2, 0, 3,
+    6, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0,
+    6, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 1, 5,
+    6, 11, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3,
+    6, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5,
+    6, 11, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2,
+    6, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, 5, 1,
+    6, 11, 1, 3, 4, 1, 3, 4, 2, 3, 0, 2, 4, 0, 5, 4, 2, 5, 4, 2, 0, 5, 1, 5,
+    6, 11, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1,
+    6, 11, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2,
+    6, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 0, 3,
+    6, 12, 0, 1, 1, 2, 0, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 4, 3,
+    6, 12, 3, 2, 1, 3, 2, 1, 0, 2, 5, 0, 2, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 0, 4,
+    6, 12, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 4, 5,
+    6, 12, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 2, 3,
+    6, 12, 0, 1, 1, 2, 0, 2, 3, 2, 3, 1, 4, 0, 2, 4, 5, 1, 0, 5, 4, 5, 3, 4, 5, 3,
+    6, 13, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 2, 3, 0, 4,
+    6, 13, 0, 1, 1, 2, 0, 2, 3, 2, 3, 1, 4, 0, 2, 4, 5, 1, 0, 5, 4, 5, 3, 4, 5, 3, 3, 0,
+    6, 14, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 1, 3, 2, 0, 4, 0, 5, 3,
+    6, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5,
+    7, 0,
+    7, 1, 6, 5,
+    7, 2, 2, 3, 1, 2,
+    7, 2, 5, 4, 6, 0,
+    7, 3, 0, 4, 4, 2, 2, 0,
+    7, 3, 0, 1, 0, 6, 0, 5,
+    7, 3, 5, 4, 6, 0, 5, 6,
+    7, 3, 3, 2, 1, 2, 5, 6,
+    7, 3, 3, 1, 5, 6, 0, 4,
+    7, 4, 2, 5, 6, 2, 5, 6, 1, 2,
+    7, 4, 1, 2, 4, 1, 5, 4, 2, 5,
+    7, 4, 1, 0, 5, 1, 1, 2, 4, 1,
+    7, 4, 1, 0, 2, 1, 5, 2, 6, 2,
+    7, 4, 3, 4, 2, 3, 1, 2, 0, 1,
+    7, 4, 4, 2, 0, 4, 2, 0, 5, 6,
+    7, 4, 0, 1, 6, 0, 0, 5, 4, 2,
+    7, 4, 3, 1, 5, 4, 6, 5, 0, 6,
+    7, 4, 0, 4, 3, 0, 2, 5, 6, 2,
+    7, 4, 2, 3, 1, 2, 6, 0, 5, 4,
+    7, 5, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0,
+    7, 5, 2, 5, 6, 2, 5, 6, 4, 2, 3, 2,
+    7, 5, 4, 2, 4, 0, 2, 0, 5, 4, 6, 0,
+    7, 5, 2, 5, 6, 2, 5, 6, 1, 2, 0, 1,
+    7, 5, 4, 1, 0, 4, 3, 0, 1, 3, 2, 1,
+    7, 5, 1, 2, 0, 1, 4, 0, 3, 4, 2, 3,
+    7, 5, 5, 1, 5, 0, 2, 5, 3, 5, 4, 5,
+    7, 5, 1, 5, 6, 1, 1, 0, 2, 1, 3, 2,
+    7, 5, 1, 5, 4, 1, 2, 3, 6, 2, 2, 1,
+    7, 5, 1, 5, 6, 1, 1, 2, 2, 3, 4, 3,
+    7, 5, 2, 1, 3, 2, 4, 3, 5, 4, 3, 6,
+    7, 5, 6, 5, 2, 6, 1, 2, 5, 2, 3, 4,
+    7, 5, 4, 3, 5, 4, 6, 5, 0, 6, 1, 0,
+    7, 5, 0, 4, 3, 0, 2, 5, 6, 2, 5, 6,
+    7, 5, 4, 1, 5, 2, 6, 5, 3, 6, 2, 3,
+    7, 5, 1, 4, 3, 1, 1, 0, 2, 1, 6, 5,
+    7, 5, 0, 4, 3, 0, 1, 0, 2, 1, 6, 5,
+    7, 5, 0, 4, 3, 0, 2, 1, 5, 2, 6, 2,
+    7, 5, 6, 5, 3, 4, 2, 3, 1, 2, 0, 1,
+    7, 5, 2, 3, 1, 2, 6, 0, 5, 6, 5, 4,
+    7, 5, 0, 1, 4, 6, 5, 4, 3, 2, 6, 5,
+    7, 6, 1, 5, 6, 1, 5, 6, 2, 5, 1, 2, 6, 2,
+    7, 6, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1,
+    7, 6, 0, 4, 3, 0, 1, 3, 2, 1, 1, 4, 3, 4,
+    7, 6, 5, 2, 4, 5, 2, 4, 3, 2, 6, 3, 2, 6,
+    7, 6, 1, 2, 4, 1, 5, 4, 2, 5, 0, 1, 4, 0,
+    7, 6, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0,
+    7, 6, 2, 5, 6, 2, 5, 6, 2, 4, 1, 2, 3, 2,
+    7, 6, 1, 4, 3, 1, 2, 3, 1, 2, 2, 5, 6, 2,
+    7, 6, 5, 4, 6, 5, 1, 6, 5, 1, 3, 6, 0, 1,
+    7, 6, 6, 5, 1, 6, 5, 1, 3, 1, 0, 3, 1, 4,
+    7, 6, 0, 4, 3, 0, 2, 3, 4, 2, 2, 5, 6, 2,
+    7, 6, 1, 4, 3, 1, 2, 3, 1, 2, 2, 5, 6, 5,
+    7, 6, 2, 3, 1, 2, 3, 6, 5, 4, 6, 5, 5, 2,
+    7, 6, 2, 5, 6, 2, 5, 6, 1, 4, 3, 1, 2, 1,
+    7, 6, 4, 5, 0, 4, 3, 0, 2, 3, 4, 2, 6, 3,
+    7, 6, 0, 4, 3, 0, 1, 3, 6, 5, 1, 4, 1, 0,
+    7, 6, 1, 4, 3, 1, 2, 3, 5, 2, 6, 5, 2, 6,
+    7, 6, 6, 3, 5, 6, 4, 5, 1, 4, 2, 1, 5, 2,
+    7, 6, 1, 0, 3, 1, 6, 3, 5, 6, 4, 5, 1, 4,
+    7, 6, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5,
+    7, 6, 0, 4, 3, 0, 4, 3, 2, 5, 6, 2, 5, 6,
+    7, 6, 6, 3, 0, 6, 6, 2, 5, 6, 6, 1, 4, 6,
+    7, 6, 2, 4, 5, 2, 2, 3, 6, 2, 1, 2, 1, 0,
+    7, 6, 1, 0, 2, 1, 5, 2, 1, 4, 3, 1, 6, 2,
+    7, 6, 1, 0, 2, 1, 3, 6, 1, 3, 4, 1, 5, 4,
+    7, 6, 1, 0, 2, 1, 5, 2, 6, 5, 1, 4, 3, 1,
+    7, 6, 1, 0, 2, 4, 5, 2, 6, 5, 2, 6, 3, 2,
+    7, 6, 4, 0, 1, 4, 3, 1, 2, 1, 5, 2, 6, 2,
+    7, 6, 6, 5, 1, 2, 0, 1, 2, 0, 3, 2, 0, 4,
+    7, 6, 0, 4, 3, 0, 1, 0, 2, 1, 5, 2, 6, 2,
+    7, 6, 1, 0, 3, 1, 6, 3, 2, 6, 4, 1, 5, 4,
+    7, 6, 2, 5, 6, 2, 4, 2, 1, 4, 3, 1, 0, 3,
+    7, 6, 0, 4, 3, 0, 2, 3, 4, 2, 1, 2, 6, 5,
+    7, 6, 0, 4, 3, 0, 2, 1, 5, 2, 6, 5, 2, 6,
+    7, 6, 3, 4, 1, 0, 2, 1, 5, 2, 6, 5, 2, 6,
+    7, 6, 4, 5, 0, 4, 3, 0, 6, 3, 1, 0, 2, 1,
+    7, 6, 2, 5, 6, 2, 5, 6, 1, 4, 3, 1, 1, 0,
+    7, 6, 4, 5, 3, 4, 2, 3, 1, 2, 0, 1, 6, 0,
+    7, 6, 6, 4, 5, 6, 4, 5, 2, 3, 1, 2, 0, 1,
+    7, 6, 0, 1, 4, 0, 2, 3, 5, 2, 6, 5, 3, 6,
+    7, 6, 1, 2, 0, 1, 4, 0, 3, 4, 2, 3, 6, 5,
+    7, 7, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 3, 4,
+    7, 7, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, 5, 2,
+    7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1,
+    7, 7, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, 1, 0,
+    7, 7, 0, 4, 3, 0, 2, 3, 4, 2, 2, 5, 6, 2, 2, 0,
+    7, 7, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 2, 6,
+    7, 7, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 3, 4, 6, 3,
+    7, 7, 0, 4, 3, 0, 2, 3, 4, 2, 2, 5, 6, 2, 3, 4,
+    7, 7, 0, 4, 3, 0, 1, 3, 3, 6, 1, 4, 1, 0, 5, 4,
+    7, 7, 0, 4, 3, 0, 1, 3, 6, 5, 1, 4, 1, 0, 3, 4,
+    7, 7, 5, 2, 4, 5, 2, 4, 3, 2, 6, 3, 2, 6, 2, 1,
+    7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 2, 5,
+    7, 7, 5, 2, 4, 5, 2, 4, 3, 2, 6, 3, 2, 6, 3, 1,
+    7, 7, 1, 4, 3, 1, 2, 3, 4, 2, 2, 0, 2, 1, 6, 0,
+    7, 7, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, 3, 5,
+    7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 3, 5,
+    7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 1, 5,
+    7, 7, 3, 2, 4, 3, 3, 5, 2, 4, 5, 2, 6, 1, 6, 4,
+    7, 7, 1, 2, 5, 1, 4, 5, 2, 4, 0, 2, 5, 0, 0, 3,
+    7, 7, 3, 4, 1, 3, 2, 1, 6, 2, 5, 6, 1, 5, 4, 1,
+    7, 7, 0, 1, 4, 0, 1, 4, 2, 1, 3, 2, 5, 3, 4, 5,
+    7, 7, 6, 3, 5, 6, 1, 5, 2, 1, 3, 2, 4, 2, 5, 4,
+    7, 7, 1, 2, 4, 1, 5, 4, 6, 5, 3, 6, 2, 3, 5, 2,
+    7, 7, 4, 1, 3, 4, 1, 3, 2, 1, 6, 2, 5, 6, 2, 5,
+    7, 7, 3, 0, 6, 3, 0, 6, 1, 0, 0, 2, 5, 0, 0, 4,
+    7, 7, 1, 5, 6, 1, 1, 2, 3, 1, 4, 3, 1, 4, 4, 0,
+    7, 7, 5, 0, 6, 5, 0, 6, 5, 2, 1, 5, 6, 3, 4, 6,
+    7, 7, 4, 1, 0, 4, 1, 0, 2, 1, 0, 3, 6, 0, 4, 5,
+    7, 7, 5, 2, 6, 5, 2, 6, 2, 4, 3, 2, 1, 0, 2, 1,
+    7, 7, 4, 1, 0, 4, 3, 0, 1, 3, 2, 1, 1, 5, 6, 1,
+    7, 7, 1, 0, 4, 1, 0, 4, 5, 4, 2, 1, 3, 2, 6, 1,
+    7, 7, 0, 1, 4, 0, 1, 4, 2, 1, 3, 2, 5, 4, 6, 4,
+    7, 7, 2, 3, 5, 2, 6, 5, 3, 6, 1, 2, 4, 5, 0, 5,
+    7, 7, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 2, 1, 6, 5,
+    7, 7, 2, 5, 6, 2, 5, 6, 4, 2, 1, 2, 0, 1, 3, 1,
+    7, 7, 2, 5, 6, 2, 4, 2, 1, 4, 3, 1, 2, 3, 0, 1,
+    7, 7, 6, 2, 5, 6, 2, 5, 1, 2, 0, 1, 4, 1, 3, 1,
+    7, 7, 0, 4, 3, 0, 1, 3, 4, 1, 5, 4, 2, 1, 6, 3,
+    7, 7, 2, 5, 6, 2, 5, 6, 4, 5, 3, 6, 1, 2, 0, 1,
+    7, 7, 2, 5, 6, 2, 1, 4, 1, 2, 0, 1, 4, 0, 0, 3,
+    7, 7, 6, 5, 1, 2, 4, 1, 0, 4, 3, 0, 1, 3, 3, 4,
+    7, 7, 4, 1, 0, 4, 1, 0, 3, 6, 2, 3, 0, 2, 5, 0,
+    7, 7, 4, 1, 0, 4, 3, 0, 1, 3, 2, 1, 5, 2, 6, 1,
+    7, 7, 4, 1, 0, 4, 1, 0, 2, 3, 0, 2, 5, 0, 6, 5,
+    7, 7, 0, 1, 5, 0, 6, 5, 3, 6, 2, 3, 0, 2, 4, 0,
+    7, 7, 1, 0, 4, 1, 2, 4, 3, 2, 4, 3, 0, 4, 6, 5,
+    7, 7, 3, 6, 2, 3, 1, 2, 0, 1, 4, 0, 1, 4, 5, 4,
+    7, 7, 1, 0, 5, 1, 6, 5, 2, 6, 1, 2, 3, 2, 4, 3,
+    7, 7, 2, 3, 1, 2, 0, 1, 4, 0, 5, 4, 6, 5, 4, 1,
+    7, 7, 5, 2, 6, 5, 2, 6, 1, 2, 4, 1, 0, 4, 3, 1,
+    7, 7, 2, 3, 1, 2, 0, 1, 4, 0, 5, 4, 6, 5, 5, 2,
+    7, 7, 1, 4, 0, 1, 2, 0, 3, 2, 5, 3, 0, 5, 6, 3,
+    7, 7, 2, 1, 3, 2, 6, 3, 5, 6, 0, 5, 2, 0, 5, 4,
+    7, 7, 5, 2, 6, 5, 2, 6, 1, 2, 0, 1, 4, 0, 3, 0,
+    7, 7, 4, 1, 0, 4, 3, 0, 1, 3, 2, 1, 5, 2, 6, 2,
+    7, 7, 1, 0, 2, 1, 5, 2, 4, 5, 0, 4, 4, 1, 6, 3,
+    7, 7, 2, 5, 6, 2, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0,
+    7, 7, 6, 5, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 3, 2,
+    7, 7, 2, 1, 5, 2, 4, 5, 0, 4, 3, 0, 6, 3, 2, 6,
+    7, 7, 4, 0, 3, 4, 1, 3, 2, 1, 5, 2, 6, 5, 2, 6,
+    7, 7, 6, 5, 2, 6, 1, 2, 4, 1, 0, 4, 3, 0, 1, 3,
+    7, 7, 4, 1, 0, 4, 2, 0, 3, 2, 6, 3, 5, 6, 0, 5,
+    7, 7, 0, 4, 3, 0, 4, 3, 2, 1, 5, 2, 6, 5, 2, 6,
+    7, 7, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0, 6,
+    7, 7, 1, 0, 4, 1, 0, 4, 5, 2, 6, 5, 3, 6, 2, 3,
+    7, 8, 0, 1, 4, 0, 5, 4, 2, 5, 1, 2, 5, 1, 4, 1, 2, 4,
+    7, 8, 4, 1, 5, 4, 2, 5, 1, 2, 0, 1, 5, 0, 0, 4, 2, 0,
+    7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 3, 4, 5, 1, 6, 1,
+    7, 8, 4, 1, 5, 4, 2, 5, 1, 2, 5, 1, 6, 5, 2, 4, 3, 2,
+    7, 8, 1, 3, 0, 1, 4, 0, 2, 4, 1, 2, 4, 1, 5, 4, 1, 5,
+    7, 8, 2, 0, 3, 2, 6, 3, 5, 6, 0, 5, 3, 0, 0, 6, 4, 0,
+    7, 8, 1, 0, 2, 1, 5, 2, 4, 5, 0, 4, 2, 0, 5, 0, 6, 5,
+    7, 8, 1, 0, 2, 1, 3, 2, 1, 3, 4, 3, 2, 4, 5, 2, 3, 5,
+    7, 8, 2, 0, 3, 2, 6, 3, 5, 6, 0, 5, 3, 0, 6, 0, 4, 5,
+    7, 8, 1, 0, 2, 1, 4, 3, 1, 5, 4, 1, 2, 4, 5, 2, 3, 5,
+    7, 8, 3, 5, 2, 1, 4, 3, 1, 5, 4, 1, 2, 4, 5, 2, 4, 6,
+    7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 3, 4, 2, 1, 5, 2,
+    7, 8, 3, 5, 2, 1, 4, 3, 1, 5, 4, 1, 2, 4, 5, 2, 0, 3,
+    7, 8, 4, 0, 2, 4, 0, 2, 3, 0, 2, 3, 5, 2, 6, 5, 2, 6,
+    7, 8, 3, 2, 6, 3, 5, 6, 2, 5, 0, 2, 5, 0, 4, 5, 2, 4,
+    7, 8, 0, 5, 4, 0, 2, 4, 5, 2, 1, 5, 4, 1, 3, 4, 5, 3,
+    7, 8, 2, 3, 1, 2, 4, 1, 5, 4, 1, 5, 5, 2, 6, 5, 3, 6,
+    7, 8, 5, 2, 4, 5, 0, 4, 3, 0, 6, 3, 2, 6, 4, 2, 3, 2,
+    7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 3, 2, 5, 4, 2, 5,
+    7, 8, 5, 6, 2, 5, 6, 2, 3, 2, 4, 3, 0, 4, 3, 0, 2, 4,
+    7, 8, 1, 0, 5, 0, 3, 2, 1, 3, 5, 2, 6, 1, 6, 2, 6, 5,
+    7, 8, 5, 4, 6, 5, 3, 6, 0, 3, 4, 0, 2, 4, 3, 2, 0, 2,
+    7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 2, 1, 5,
+    7, 8, 5, 0, 6, 2, 0, 6, 1, 0, 2, 1, 5, 2, 4, 5, 4, 6,
+    7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 2, 1, 1, 5, 6, 1,
+    7, 8, 0, 2, 4, 0, 1, 4, 0, 1, 3, 0, 1, 3, 5, 1, 6, 1,
+    7, 8, 4, 2, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 1, 5, 6, 1,
+    7, 8, 0, 4, 3, 0, 4, 3, 1, 4, 3, 1, 1, 5, 2, 1, 6, 1,
+    7, 8, 2, 1, 0, 2, 3, 0, 5, 3, 2, 5, 3, 2, 4, 3, 6, 5,
+    7, 8, 4, 2, 0, 4, 3, 0, 1, 3, 4, 1, 3, 4, 1, 5, 6, 1,
+    7, 8, 2, 1, 0, 2, 3, 0, 5, 3, 2, 5, 6, 5, 4, 3, 5, 0,
+    7, 8, 1, 0, 2, 1, 3, 2, 1, 3, 4, 2, 3, 4, 4, 5, 6, 4,
+    7, 8, 6, 5, 1, 2, 4, 1, 0, 4, 3, 0, 1, 3, 0, 1, 3, 4,
+    7, 8, 0, 1, 6, 5, 2, 3, 6, 4, 6, 3, 6, 2, 6, 0, 6, 1,
+    7, 8, 6, 4, 1, 2, 2, 3, 6, 5, 4, 5, 6, 2, 6, 0, 6, 1,
+    7, 8, 0, 1, 1, 2, 2, 3, 6, 5, 6, 4, 6, 3, 6, 0, 6, 2,
+    7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 6, 1, 5, 1, 2, 5,
+    7, 8, 3, 0, 2, 3, 4, 2, 0, 4, 1, 0, 2, 1, 5, 2, 6, 2,
+    7, 8, 2, 1, 3, 2, 6, 3, 5, 6, 0, 5, 2, 0, 5, 2, 4, 5,
+    7, 8, 1, 0, 2, 1, 3, 2, 4, 3, 5, 2, 1, 5, 6, 1, 2, 6,
+    7, 8, 2, 5, 4, 2, 1, 4, 3, 1, 0, 3, 1, 0, 2, 1, 6, 2,
+    7, 8, 4, 5, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 6, 3,
+    7, 8, 0, 1, 4, 0, 1, 4, 2, 1, 4, 2, 5, 4, 1, 5, 6, 3,
+    7, 8, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 1, 6, 5, 0,
+    7, 8, 4, 5, 0, 4, 1, 0, 4, 1, 3, 0, 1, 3, 6, 1, 2, 6,
+    7, 8, 2, 5, 4, 2, 0, 4, 1, 0, 4, 1, 3, 0, 1, 3, 6, 1,
+    7, 8, 1, 6, 2, 1, 0, 2, 1, 0, 4, 1, 3, 4, 2, 3, 4, 5,
+    7, 8, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 1, 6, 5, 3,
+    7, 8, 0, 4, 3, 0, 4, 3, 1, 4, 3, 1, 5, 1, 6, 2, 1, 6,
+    7, 8, 2, 3, 1, 2, 0, 1, 5, 0, 4, 5, 0, 4, 2, 0, 6, 5,
+    7, 8, 4, 5, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 3, 2, 6, 2,
+    7, 8, 2, 3, 1, 2, 0, 1, 4, 0, 5, 4, 4, 1, 2, 6, 5, 2,
+    7, 8, 0, 1, 1, 2, 2, 3, 6, 3, 4, 5, 6, 2, 6, 0, 6, 1,
+    7, 8, 4, 1, 0, 4, 3, 0, 1, 3, 0, 1, 2, 1, 5, 2, 6, 2,
+    7, 8, 0, 1, 1, 2, 2, 3, 6, 5, 4, 5, 6, 2, 6, 4, 6, 1,
+    7, 8, 0, 1, 4, 0, 0, 2, 5, 0, 6, 5, 3, 6, 2, 3, 5, 2,
+    7, 8, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 2, 5, 6, 2,
+    7, 8, 4, 5, 3, 4, 1, 3, 2, 1, 6, 2, 4, 6, 3, 2, 0, 1,
+    7, 8, 1, 0, 2, 6, 3, 2, 4, 3, 5, 2, 1, 5, 6, 1, 6, 5,
+    7, 8, 2, 3, 1, 2, 0, 1, 4, 0, 5, 4, 6, 5, 5, 2, 4, 1,
+    7, 8, 4, 1, 0, 4, 3, 0, 1, 3, 3, 4, 2, 1, 2, 5, 6, 2,
+    7, 8, 0, 6, 4, 0, 1, 4, 3, 1, 0, 3, 2, 4, 3, 2, 5, 2,
+    7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 3, 2, 1, 0, 6, 5,
+    7, 8, 0, 1, 4, 0, 3, 2, 6, 3, 5, 6, 2, 5, 6, 2, 3, 5,
+    7, 8, 5, 2, 6, 5, 2, 6, 4, 2, 0, 4, 3, 0, 2, 3, 1, 2,
+    7, 8, 2, 0, 1, 2, 0, 1, 5, 0, 4, 5, 0, 4, 6, 0, 3, 6,
+    7, 8, 0, 1, 2, 0, 3, 2, 2, 1, 1, 4, 5, 4, 5, 3, 1, 6,
+    7, 8, 1, 6, 2, 1, 0, 2, 1, 0, 4, 1, 3, 4, 2, 3, 5, 6,
+    7, 8, 6, 1, 0, 6, 1, 0, 5, 1, 0, 5, 2, 1, 3, 2, 4, 3,
+    7, 8, 6, 5, 2, 6, 1, 2, 4, 1, 3, 4, 0, 3, 4, 0, 2, 4,
+    7, 8, 1, 6, 0, 1, 5, 0, 1, 5, 3, 0, 4, 3, 2, 4, 0, 2,
+    7, 8, 2, 6, 4, 2, 0, 4, 1, 0, 4, 1, 3, 4, 5, 3, 2, 5,
+    7, 8, 1, 0, 2, 1, 6, 2, 5, 6, 1, 5, 4, 1, 3, 4, 2, 3,
+    7, 8, 6, 1, 4, 3, 1, 0, 5, 1, 3, 2, 2, 1, 4, 6, 5, 4,
+    7, 8, 4, 2, 0, 4, 1, 0, 4, 1, 3, 4, 6, 3, 5, 6, 3, 5,
+    7, 8, 4, 1, 2, 4, 0, 2, 6, 0, 3, 6, 0, 3, 5, 0, 4, 5,
+    7, 8, 5, 6, 4, 5, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1,
+    7, 8, 6, 3, 5, 6, 4, 5, 0, 4, 1, 0, 2, 1, 5, 2, 4, 1,
+    7, 8, 0, 1, 2, 0, 3, 2, 2, 1, 1, 4, 5, 4, 5, 3, 4, 6,
+    7, 8, 4, 0, 3, 4, 2, 3, 6, 2, 5, 6, 1, 5, 4, 1, 2, 1,
+    7, 8, 6, 1, 0, 6, 4, 3, 5, 1, 0, 5, 2, 1, 3, 2, 5, 6,
+    7, 8, 6, 2, 5, 6, 3, 5, 6, 3, 4, 3, 0, 4, 1, 0, 4, 1,
+    7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 6, 5, 1,
+    7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 6, 4, 2,
+    7, 8, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 6, 0, 5,
+    7, 8, 4, 0, 2, 4, 3, 2, 6, 3, 5, 6, 4, 5, 1, 2, 5, 1,
+    7, 8, 5, 1, 2, 4, 3, 2, 0, 3, 5, 0, 4, 5, 1, 2, 0, 6,
+    7, 8, 5, 6, 2, 5, 4, 2, 0, 4, 3, 0, 2, 3, 1, 4, 3, 1,
+    7, 8, 0, 4, 1, 0, 4, 1, 3, 4, 5, 3, 6, 5, 2, 6, 4, 2,
+    7, 8, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 2, 6, 1,
+    7, 8, 1, 2, 0, 1, 4, 0, 5, 4, 2, 5, 3, 2, 6, 3, 5, 6,
+    7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 6, 1, 6,
+    7, 8, 6, 2, 5, 6, 2, 5, 1, 2, 4, 1, 0, 4, 3, 0, 1, 3,
+    7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 5, 6, 1,
+    7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 3,
+    7, 8, 0, 4, 1, 0, 3, 2, 1, 4, 2, 5, 5, 3, 6, 4, 6, 3,
+    7, 8, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 6, 2, 5, 6, 2, 5,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3,
+    7, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 2,
+    7, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 4, 5,
+    7, 9, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3,
+    7, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 5,
+    7, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0,
+    7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 0, 4, 1, 0, 4, 1,
+    7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1,
+    7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0,
+    7, 9, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5,
+    7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0,
+    7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1,
+    7, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2,
+    7, 9, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 5, 2, 4, 1,
+    7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 0, 0, 4, 0, 6,
+    7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 0, 0, 4, 2, 6,
+    7, 9, 1, 2, 3, 1, 0, 3, 1, 0, 2, 0, 3, 2, 5, 3, 4, 0, 1, 6,
+    7, 9, 0, 1, 2, 4, 0, 2, 3, 1, 3, 2, 2, 1, 4, 1, 5, 2, 2, 6,
+    7, 9, 0, 1, 2, 4, 0, 2, 5, 2, 3, 1, 3, 2, 2, 1, 4, 1, 1, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 1, 5, 1, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 1, 5, 4, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 2, 1, 5, 1, 1, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 1, 6,
+    7, 9, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 5, 3, 2, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 3, 0, 5, 1, 0, 6,
+    7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 0, 0, 4, 5, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 2, 6,
+    7, 9, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 5, 3, 5, 6,
+    7, 9, 1, 2, 3, 1, 0, 3, 1, 0, 2, 0, 3, 2, 4, 0, 6, 5, 6, 3,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 0, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 6, 5, 4, 0, 3, 2, 4,
+    7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 2, 1, 5, 1, 5, 6,
+    7, 9, 2, 0, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 5, 3, 4, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 2, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 3, 0, 5, 1, 5, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 5, 4, 3, 1, 3, 2, 3, 0, 5, 1, 2, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 4, 0, 3, 1, 3, 2, 5, 1, 5, 3, 0, 6,
+    7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 0, 5, 5, 4, 5, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 6,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 2, 0, 2, 0, 6,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, 3, 6,
+    7, 9, 0, 1, 2, 4, 0, 2, 5, 2, 3, 1, 3, 2, 2, 1, 4, 1, 5, 6,
+    7, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 2, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 6, 5, 6, 1,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, 2, 6,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 3, 4, 0, 6,
+    7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 3, 6, 1, 6, 0, 1, 1, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 0, 6,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 5, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 5, 6,
+    7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 3, 6, 1, 6, 0, 1, 0, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 2, 0, 2, 1, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 2, 0, 2, 4, 6,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, 2, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 3, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 2, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 5, 1, 3, 1, 3, 2, 2, 1, 6, 0, 6, 4,
+    7, 9, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 1, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 0, 5, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 0, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 3, 6,
+    7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 3, 6, 1, 6, 0, 1, 2, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, 3, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 1, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 1, 4, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 5, 1, 3, 1, 3, 2, 3, 0, 6, 4, 6, 0,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 5, 2, 5, 0, 1, 6,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, 6, 0,
+    7, 9, 5, 3, 3, 2, 4, 3, 5, 4, 2, 5, 1, 2, 4, 1, 6, 0, 6, 2,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 5, 2, 5, 0, 0, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, 5, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 4, 6,
+    7, 9, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 3, 6, 1, 6, 0, 1, 5, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 1, 5, 2, 5, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, 1, 6,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 2, 1, 3, 6,
+    7, 9, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 0, 5, 5, 4, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 5, 5, 3, 1, 5, 0, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 5, 5, 2, 5, 0, 4, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 1, 0, 6,
+    7, 9, 0, 1, 2, 5, 0, 2, 5, 3, 3, 1, 3, 2, 5, 1, 6, 4, 6, 0,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 1, 5, 2, 0, 6,
+    7, 9, 6, 3, 1, 2, 6, 5, 3, 4, 6, 4, 0, 5, 6, 0, 6, 1, 6, 2,
+    7, 9, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 6, 2, 5, 6, 2, 5,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 3, 4, 6, 5, 6, 0,
+    7, 9, 1, 4, 2, 4, 2, 3, 0, 4, 3, 1, 4, 5, 0, 5, 6, 4, 6, 3,
+    7, 9, 4, 1, 5, 4, 6, 5, 3, 6, 2, 3, 1, 2, 5, 2, 0, 5, 2, 0,
+    7, 9, 4, 1, 3, 1, 2, 3, 4, 0, 5, 0, 5, 2, 5, 4, 6, 4, 5, 6,
+    7, 9, 1, 0, 2, 1, 6, 2, 3, 6, 5, 3, 4, 5, 3, 4, 2, 3, 0, 2,
+    7, 9, 0, 2, 5, 0, 1, 5, 2, 1, 4, 2, 5, 4, 6, 5, 3, 6, 2, 3,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0, 6, 1, 3, 4, 1,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 4, 5, 1, 6, 1, 0, 6,
+    7, 9, 0, 4, 1, 0, 4, 1, 3, 4, 2, 3, 6, 2, 5, 6, 1, 5, 2, 1,
+    7, 9, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 3, 6, 5, 3, 6,
+    7, 9, 6, 5, 3, 6, 2, 3, 0, 4, 0, 5, 1, 0, 2, 0, 1, 2, 5, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 1, 6, 1, 0, 6,
+    7, 9, 5, 2, 6, 5, 3, 6, 2, 3, 0, 2, 4, 0, 5, 4, 1, 5, 0, 1,
+    7, 9, 2, 4, 1, 2, 4, 1, 5, 4, 0, 5, 1, 0, 6, 4, 3, 6, 2, 3,
+    7, 9, 6, 2, 5, 6, 2, 5, 1, 2, 0, 1, 4, 0, 1, 4, 3, 1, 0, 3,
+    7, 9, 0, 5, 6, 0, 1, 6, 4, 1, 2, 4, 3, 2, 1, 3, 5, 1, 6, 5,
+    7, 9, 6, 5, 3, 6, 2, 3, 5, 2, 0, 5, 1, 0, 4, 1, 0, 4, 2, 0,
+    7, 9, 0, 4, 3, 0, 1, 3, 4, 1, 2, 4, 6, 2, 5, 6, 2, 5, 3, 2,
+    7, 9, 1, 0, 4, 1, 5, 4, 0, 5, 6, 0, 3, 6, 2, 3, 0, 2, 3, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 3,
+    7, 9, 0, 1, 1, 2, 2, 3, 0, 3, 4, 1, 5, 4, 5, 3, 6, 0, 6, 4,
+    7, 9, 0, 1, 4, 0, 1, 4, 2, 1, 5, 2, 4, 5, 6, 5, 3, 6, 2, 3,
+    7, 9, 1, 0, 6, 3, 0, 4, 5, 0, 3, 5, 5, 6, 1, 2, 1, 4, 6, 2,
+    7, 9, 6, 2, 5, 6, 2, 5, 1, 2, 0, 3, 4, 0, 1, 4, 3, 1, 3, 4,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 1, 5, 6, 6, 0,
+    7, 9, 0, 4, 1, 0, 2, 1, 3, 2, 0, 3, 2, 4, 5, 4, 6, 5, 3, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 2, 6, 1, 5, 6,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 4, 6, 1, 6, 5,
+    7, 9, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 4, 6, 2,
+    7, 9, 0, 4, 3, 0, 4, 3, 6, 1, 5, 6, 1, 5, 2, 1, 5, 2, 6, 2,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 4,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0,
+    7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 0,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 4,
+    7, 10, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1,
+    7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2,
+    7, 10, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1,
+    7, 10, 0, 1, 1, 2, 3, 4, 0, 2, 3, 0, 2, 4, 5, 2, 1, 5, 4, 1, 3, 5,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 2, 2, 6,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 2, 1, 6,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, 1, 6,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, 0, 6,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, 3, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 4, 5, 4, 6,
+    7, 10, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3, 3, 6,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 5, 2, 6,
+    7, 10, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3, 2, 6,
+    7, 10, 2, 3, 1, 2, 4, 1, 5, 4, 2, 5, 0, 2, 4, 0, 0, 1, 5, 0, 6, 5,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 0, 4, 5, 6,
+    7, 10, 2, 0, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 4, 3, 4, 6,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 4, 5, 6, 5,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 6,
+    7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 6, 5,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 0, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 2, 6,
+    7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 3, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 0, 4, 1, 0, 4, 1, 0, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, 1, 6,
+    7, 10, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0, 0, 6,
+    7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 3, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 5, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 3, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, 0, 6,
+    7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 2, 0, 5, 3, 2, 3, 6, 2,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 6, 4, 6, 2,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 2, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, 5, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 3, 0, 4, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 3, 6,
+    7, 10, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0, 2, 6,
+    7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0, 5, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 6,
+    7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 2, 0, 5, 3, 2, 3, 0, 6,
+    7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 1, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 4, 6,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 6, 4, 6, 0,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 5, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 0, 4, 1, 0, 4, 1, 5, 6,
+    7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0, 0, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 1, 0, 5, 1, 2, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, 2, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 5, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 0, 6,
+    7, 10, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 5, 4, 4, 0, 5, 0, 5, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 6, 5, 6, 4,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 1, 6,
+    7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 4, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 4, 6,
+    7, 10, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 2, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 0, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, 5, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 1, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 3, 6,
+    7, 10, 4, 3, 4, 1, 1, 2, 5, 4, 2, 5, 3, 1, 5, 3, 3, 2, 6, 0, 6, 2,
+    7, 10, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 1, 5, 6, 1, 4, 6, 2, 6, 5, 2,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, 0, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 3, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 2, 6,
+    7, 10, 0, 1, 2, 5, 0, 2, 3, 0, 3, 1, 3, 2, 2, 1, 5, 1, 6, 5, 6, 4,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 1, 6,
+    7, 10, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 6,
+    7, 10, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 2, 0, 1, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 1, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 4, 2, 5, 2, 1, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 0, 5, 2, 5, 0, 5, 1, 4, 6,
+    7, 10, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, 4, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 5, 2, 4, 1, 0, 6,
+    7, 10, 4, 0, 1, 4, 3, 1, 2, 3, 1, 2, 6, 1, 0, 6, 5, 0, 1, 5, 0, 1,
+    7, 10, 3, 2, 6, 3, 5, 6, 0, 5, 2, 0, 5, 2, 1, 5, 2, 1, 4, 2, 5, 4,
+    7, 10, 2, 0, 1, 2, 3, 1, 0, 3, 6, 0, 1, 6, 5, 1, 0, 5, 4, 0, 1, 4,
+    7, 10, 6, 4, 1, 2, 6, 5, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3,
+    7, 10, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3,
+    7, 10, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 0, 5, 5, 2, 6, 1, 2, 6,
+    7, 10, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 0, 5, 2, 6, 2, 0, 6,
+    7, 10, 6, 4, 1, 2, 6, 5, 3, 4, 4, 5, 0, 5, 6, 3, 6, 1, 6, 2, 0, 4,
+    7, 10, 1, 0, 2, 1, 0, 2, 3, 2, 4, 3, 2, 4, 5, 2, 4, 5, 6, 4, 1, 6,
+    7, 10, 0, 1, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 5, 2, 6,
+    7, 10, 0, 2, 5, 0, 4, 5, 2, 4, 1, 2, 5, 1, 6, 5, 3, 6, 2, 3, 2, 6,
+    7, 10, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 1, 0, 5, 6, 0, 2, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 2, 6, 4, 0, 2, 4, 0,
+    7, 10, 0, 4, 3, 0, 2, 3, 5, 2, 6, 5, 2, 6, 4, 2, 1, 4, 3, 1, 4, 3,
+    7, 10, 1, 6, 2, 1, 0, 2, 1, 0, 4, 1, 3, 4, 2, 3, 5, 6, 4, 5, 3, 1,
+    7, 10, 6, 5, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 4,
+    7, 10, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 0, 6, 1, 6, 2, 5, 3,
+    7, 10, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 0, 5, 3, 5, 2, 6, 5, 6, 1,
+    7, 10, 0, 3, 2, 0, 1, 2, 3, 1, 4, 3, 2, 4, 0, 4, 6, 0, 5, 6, 0, 5,
+    7, 10, 0, 3, 2, 0, 1, 2, 3, 1, 4, 3, 0, 5, 0, 4, 6, 0, 5, 6, 1, 4,
+    7, 10, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 0, 6, 1, 6, 2, 4, 2,
+    7, 10, 1, 2, 5, 1, 6, 5, 2, 6, 1, 6, 5, 2, 4, 1, 0, 4, 3, 0, 1, 3,
+    7, 10, 4, 2, 6, 2, 5, 3, 4, 1, 2, 0, 6, 3, 5, 2, 0, 1, 0, 4, 6, 0,
+    7, 10, 4, 2, 3, 6, 5, 3, 5, 1, 2, 0, 6, 0, 5, 2, 1, 4, 0, 4, 5, 4,
+    7, 10, 4, 0, 5, 4, 4, 1, 2, 1, 3, 2, 0, 3, 3, 4, 5, 3, 6, 1, 6, 5,
+    7, 10, 0, 4, 1, 0, 2, 1, 4, 2, 3, 4, 5, 3, 4, 5, 5, 2, 6, 3, 2, 6,
+    7, 10, 1, 6, 2, 1, 0, 2, 1, 0, 4, 1, 3, 4, 2, 3, 5, 6, 4, 5, 4, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 3, 6, 1, 6, 5, 5, 1,
+    7, 10, 1, 0, 4, 1, 0, 4, 2, 0, 3, 2, 6, 3, 5, 6, 0, 5, 5, 2, 6, 2,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 1, 5, 4, 6, 1, 5, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 4, 1, 6, 1, 6, 5,
+    7, 10, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 3, 2, 5, 6, 1, 4, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 2, 5, 4, 6, 5, 6, 0, 0, 2,
+    7, 10, 2, 0, 5, 2, 1, 5, 0, 1, 3, 0, 5, 3, 6, 5, 4, 6, 0, 4, 4, 3,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 2, 1, 5, 6, 2, 6, 5,
+    7, 10, 5, 0, 6, 5, 2, 6, 3, 2, 0, 3, 4, 0, 2, 4, 4, 3, 1, 4, 3, 1,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 3, 5, 6, 3, 5, 4, 5, 4, 6,
+    7, 10, 5, 2, 2, 1, 3, 2, 4, 3, 1, 4, 5, 0, 6, 1, 6, 0, 1, 5, 2, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 2, 6, 4, 4, 2, 5, 1,
+    7, 10, 4, 2, 2, 3, 4, 1, 0, 1, 3, 0, 6, 4, 0, 6, 5, 0, 4, 5, 1, 5,
+    7, 10, 2, 1, 5, 2, 3, 5, 0, 3, 4, 0, 6, 4, 3, 6, 1, 3, 4, 1, 5, 4,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 6, 5, 6, 3,
+    7, 10, 0, 1, 4, 0, 1, 4, 2, 1, 5, 2, 4, 5, 6, 5, 3, 6, 2, 3, 5, 3,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 5, 6, 1, 6, 2, 4, 2,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 3, 5, 2, 6, 5, 6, 4,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 3, 6, 2, 4, 0,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 4, 6, 5, 6, 3,
+    7, 10, 0, 5, 6, 0, 1, 6, 0, 1, 1, 5, 2, 1, 3, 2, 4, 3, 6, 4, 4, 5,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 5, 6, 4, 2, 0,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 5, 6, 3,
+    7, 10, 2, 1, 2, 0, 3, 2, 4, 3, 1, 4, 5, 1, 5, 0, 6, 0, 1, 6, 6, 5,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 1, 6, 1, 6, 4, 6, 5,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 2, 4, 1, 6, 0, 5, 6,
+    7, 10, 3, 1, 0, 3, 5, 0, 1, 5, 2, 1, 6, 2, 0, 6, 0, 4, 4, 2, 4, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 4, 6, 1, 6, 2, 6, 5,
+    7, 10, 0, 3, 2, 0, 1, 2, 3, 1, 4, 3, 2, 4, 5, 4, 5, 0, 6, 4, 6, 1,
+    7, 10, 0, 1, 6, 5, 2, 3, 3, 4, 6, 4, 0, 5, 6, 2, 6, 1, 4, 2, 5, 1,
+    7, 10, 5, 2, 6, 5, 2, 6, 4, 2, 0, 4, 3, 0, 2, 3, 1, 0, 1, 3, 4, 1,
+    7, 10, 3, 4, 1, 3, 4, 1, 0, 4, 3, 0, 1, 0, 2, 1, 5, 2, 6, 5, 2, 6,
+    7, 10, 5, 6, 2, 5, 6, 2, 3, 6, 0, 3, 4, 0, 5, 4, 1, 4, 3, 1, 2, 1,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 5, 4, 2,
+    7, 10, 1, 0, 2, 1, 3, 2, 0, 3, 5, 0, 1, 5, 4, 5, 6, 4, 3, 6, 2, 6,
+    7, 10, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 1, 2, 5, 6, 0, 3, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 1, 5,
+    7, 11, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5,
+    7, 11, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 5, 1,
+    7, 11, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 11, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1,
+    7, 11, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 0, 3,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 4, 6, 4,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 6, 4,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 2, 4, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 2, 5, 6, 2,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 2, 0, 6,
+    7, 11, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 6, 5,
+    7, 11, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 0, 4, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, 1, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, 6, 4,
+    7, 11, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 4, 5, 1, 0, 1, 6,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 4, 2, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, 5, 6,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 2, 6,
+    7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 6, 1,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 3, 6,
+    7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 6, 4,
+    7, 11, 0, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 11, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, 0, 6,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 0, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 1, 6,
+    7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 5, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 5, 4, 3, 5, 1, 5, 0, 6,
+    7, 11, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 2, 6,
+    7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 3, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 2, 6,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 5, 6,
+    7, 11, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, 5, 6,
+    7, 11, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 5, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 4, 5, 6,
+    7, 11, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, 1, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 4, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 5, 6,
+    7, 11, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 4, 6,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 4, 0, 2, 4, 1, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 6, 5, 6, 2,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 5, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1, 1, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 1, 6,
+    7, 11, 1, 0, 4, 1, 0, 4, 5, 0, 4, 5, 3, 4, 1, 3, 5, 1, 2, 3, 1, 2, 2, 6,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 3, 2, 0, 3, 2, 4, 5, 2, 1, 6,
+    7, 11, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 5, 6,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, 1, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1, 5, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 0, 2, 1, 3, 5, 1, 6, 3,
+    7, 11, 4, 3, 0, 4, 1, 0, 2, 1, 3, 2, 0, 5, 5, 3, 0, 3, 1, 5, 5, 2, 4, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 5, 0, 5, 2, 0, 5, 1, 4, 1, 6, 3,
+    7, 11, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 3, 2, 0, 3, 1, 6,
+    7, 11, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, 6, 2,
+    7, 11, 0, 1, 2, 4, 0, 2, 4, 5, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 6,
+    7, 11, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5,
+    7, 11, 6, 5, 0, 6, 5, 0, 1, 5, 6, 1, 2, 6, 5, 2, 3, 5, 6, 3, 4, 6, 5, 4,
+    7, 11, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 1, 2, 5, 6, 2, 1, 6, 3, 1,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 1, 3, 5, 6, 1, 4, 6, 1, 4, 3, 1,
+    7, 11, 1, 4, 2, 3, 4, 2, 0, 6, 4, 5, 6, 5, 3, 1, 6, 4, 3, 0, 3, 6, 4, 3,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 2, 6, 4, 2, 6, 5, 2, 0, 2,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 4, 0, 3, 0, 2, 0, 6, 0, 3, 6,
+    7, 11, 0, 1, 2, 0, 5, 2, 6, 5, 2, 6, 1, 2, 4, 1, 2, 4, 3, 2, 4, 3, 3, 1,
+    7, 11, 4, 5, 1, 4, 2, 1, 3, 2, 6, 3, 5, 6, 2, 5, 4, 2, 3, 5, 0, 5, 2, 0,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 2, 5, 2, 5, 0, 6, 2, 4, 6, 5, 4,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 1, 2, 6, 0, 2, 6, 0, 5, 2, 0, 5,
+    7, 11, 0, 5, 6, 0, 1, 6, 5, 1, 2, 5, 6, 2, 4, 3, 3, 2, 4, 5, 6, 4, 6, 5,
+    7, 11, 0, 5, 6, 0, 1, 6, 5, 1, 2, 5, 6, 2, 3, 6, 5, 3, 4, 5, 6, 4, 4, 3,
+    7, 11, 0, 5, 0, 6, 1, 2, 1, 6, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 0, 2, 2, 4, 5, 2, 4, 5, 6, 5, 6, 0,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0, 6, 4, 2, 0, 4, 2, 0, 6, 4,
+    7, 11, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 5, 1, 5, 2, 6, 1, 2, 6, 4, 2, 5, 4,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 2, 5, 4, 2, 6, 2, 4, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 4, 6, 2, 0, 2, 4, 0,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 3, 1, 5, 3, 4, 5, 1, 4, 6, 5, 6, 1,
+    7, 11, 0, 4, 3, 0, 4, 3, 2, 4, 3, 2, 1, 3, 2, 1, 4, 1, 5, 2, 6, 5, 2, 6,
+    7, 11, 0, 5, 0, 6, 1, 4, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 11, 0, 1, 4, 0, 5, 4, 6, 5, 3, 6, 2, 3, 1, 2, 4, 1, 2, 4, 5, 2, 1, 5,
+    7, 11, 0, 4, 3, 0, 4, 3, 2, 4, 6, 2, 1, 6, 5, 1, 2, 5, 3, 2, 1, 3, 4, 1,
+    7, 11, 0, 1, 6, 5, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3, 6, 4,
+    7, 11, 4, 1, 0, 4, 1, 0, 3, 1, 0, 3, 5, 1, 6, 5, 1, 6, 2, 1, 5, 2, 6, 2,
+    7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 4, 6, 5, 4, 6,
+    7, 11, 1, 0, 2, 1, 3, 2, 4, 3, 0, 4, 2, 0, 5, 2, 6, 5, 3, 6, 6, 0, 0, 5,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 2, 4, 0, 6, 4, 0, 6, 3, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 2, 0, 5, 2, 6, 5, 4, 6, 0, 5, 6, 0,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 2, 6, 3, 6, 0, 3, 4, 0,
+    7, 11, 4, 6, 5, 4, 6, 5, 3, 6, 5, 3, 2, 5, 3, 2, 5, 0, 6, 0, 1, 0, 2, 1,
+    7, 11, 2, 0, 4, 2, 5, 4, 3, 5, 1, 3, 0, 1, 2, 1, 3, 2, 6, 3, 5, 6, 4, 3,
+    7, 11, 4, 3, 4, 2, 1, 4, 3, 1, 0, 3, 1, 0, 3, 2, 3, 5, 2, 5, 6, 0, 4, 6,
+    7, 11, 0, 1, 0, 2, 2, 3, 5, 1, 1, 3, 5, 2, 6, 3, 6, 0, 5, 3, 4, 5, 3, 4,
+    7, 11, 4, 0, 1, 4, 6, 1, 0, 6, 3, 0, 1, 3, 5, 1, 0, 5, 6, 5, 2, 3, 0, 2,
+    7, 11, 0, 1, 5, 0, 4, 5, 1, 4, 2, 1, 3, 2, 4, 3, 4, 2, 6, 4, 2, 6, 3, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 3, 5, 6, 3, 4, 6, 5, 6,
+    7, 11, 6, 3, 5, 6, 2, 5, 3, 2, 5, 3, 4, 5, 2, 4, 1, 2, 5, 1, 0, 1, 4, 0,
+    7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 6, 5, 4, 6,
+    7, 11, 0, 4, 3, 0, 2, 3, 5, 2, 6, 5, 2, 6, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1,
+    7, 11, 5, 0, 0, 1, 3, 0, 5, 3, 2, 5, 6, 2, 4, 6, 5, 4, 1, 5, 6, 1, 3, 6,
+    7, 11, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 2, 1, 5, 1, 2, 5, 6, 4, 6, 2, 3, 6,
+    7, 11, 3, 2, 6, 3, 5, 6, 0, 5, 2, 0, 1, 2, 0, 1, 5, 1, 2, 5, 4, 2, 6, 4,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 1, 5, 3, 5, 1, 6, 4, 6, 5, 3, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 3, 6, 0, 6, 2, 6, 3, 5, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 6, 5, 4, 6, 3, 6, 1, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 6, 5, 1, 6, 2, 6, 4, 2,
+    7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 3, 4, 2, 6, 1, 6, 4, 5, 2, 3, 5,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 6, 1, 4, 6, 6, 5, 2, 6,
+    7, 11, 0, 3, 0, 6, 1, 2, 1, 5, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 11, 5, 1, 6, 5, 4, 6, 3, 4, 2, 3, 0, 2, 1, 0, 5, 0, 6, 0, 2, 6, 4, 2,
+    7, 11, 1, 0, 2, 1, 3, 2, 4, 3, 0, 4, 5, 2, 3, 5, 6, 0, 6, 5, 6, 2, 3, 6,
+    7, 11, 0, 3, 4, 0, 2, 4, 3, 2, 1, 3, 4, 1, 5, 1, 6, 0, 6, 1, 5, 3, 4, 5,
+    7, 11, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 5, 2, 6, 3, 4, 4, 5, 4, 6, 5, 6,
+    7, 11, 0, 2, 1, 0, 2, 1, 0, 3, 3, 1, 5, 4, 5, 3, 6, 4, 6, 2, 6, 0, 1, 6,
+    7, 11, 4, 1, 5, 4, 2, 5, 1, 2, 0, 1, 5, 0, 6, 5, 3, 6, 2, 3, 0, 2, 4, 0,
+    7, 11, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 6, 1, 6, 2, 5, 1, 3, 5, 5, 2, 4, 5,
+    7, 11, 0, 5, 0, 6, 1, 4, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 3, 6, 4, 6, 2, 2, 0, 4, 0,
+    7, 11, 0, 2, 1, 0, 2, 1, 0, 3, 3, 1, 5, 4, 5, 3, 6, 4, 6, 2, 4, 0, 1, 4,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 6, 2, 0, 6, 1, 6,
+    7, 11, 0, 3, 4, 0, 1, 4, 3, 1, 4, 3, 0, 1, 2, 4, 6, 2, 5, 6, 2, 5, 1, 2,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 6, 1, 6, 5,
+    7, 11, 4, 1, 5, 4, 2, 5, 1, 2, 0, 1, 4, 0, 5, 0, 6, 5, 3, 6, 2, 3, 5, 3,
+    7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 3, 4, 2, 5, 1, 5, 4, 6, 5, 4, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 6, 3, 6, 4,
+    7, 11, 0, 4, 3, 0, 1, 3, 4, 1, 3, 4, 5, 1, 6, 5, 1, 6, 2, 1, 5, 2, 6, 2,
+    7, 11, 4, 1, 0, 4, 3, 0, 2, 5, 5, 4, 6, 5, 2, 6, 1, 2, 3, 1, 6, 3, 2, 3,
+    7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 2, 5, 4, 5, 3, 6, 0, 6, 5, 3, 6,
+    7, 11, 5, 2, 2, 4, 5, 3, 4, 1, 5, 4, 0, 1, 3, 0, 0, 2, 6, 2, 6, 3, 0, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 4, 5, 2, 5, 3, 6, 1, 0, 6,
+    7, 11, 0, 3, 0, 4, 1, 2, 1, 5, 1, 6, 2, 4, 2, 6, 3, 5, 3, 6, 4, 5, 5, 6,
+    7, 11, 4, 0, 3, 4, 5, 3, 0, 5, 1, 0, 2, 1, 3, 2, 4, 1, 5, 2, 6, 4, 5, 6,
+    7, 11, 2, 3, 4, 2, 0, 4, 5, 0, 1, 5, 4, 1, 3, 4, 5, 3, 1, 0, 6, 5, 6, 2,
+    7, 11, 4, 1, 0, 4, 3, 0, 4, 3, 5, 4, 6, 5, 2, 6, 1, 2, 3, 1, 6, 3, 2, 5,
+    7, 11, 0, 3, 4, 0, 2, 4, 3, 2, 1, 3, 0, 1, 6, 0, 5, 6, 2, 5, 1, 5, 4, 1,
+    7, 11, 0, 3, 0, 4, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 6, 4, 5, 5, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 3, 4, 2, 5, 1, 5, 4, 6, 1, 5, 6,
+    7, 11, 4, 1, 5, 4, 6, 5, 3, 6, 2, 3, 1, 2, 0, 1, 5, 0, 4, 0, 5, 2, 6, 2,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 4, 2, 3, 5, 4, 5, 3, 0, 6, 5, 6, 1,
+    7, 11, 0, 4, 1, 0, 4, 1, 3, 4, 2, 3, 1, 2, 6, 1, 5, 6, 3, 5, 5, 4, 2, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 5, 1, 4, 2, 6, 2, 3, 6, 4, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 2, 1, 6, 5, 2, 4, 1, 0, 3,
+    7, 11, 0, 3, 0, 4, 1, 2, 1, 5, 1, 6, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 11, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 3, 5, 1, 6, 3, 6, 4, 4, 2, 0, 3,
+    7, 11, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 6, 3, 6, 4, 5,
+    7, 11, 4, 3, 2, 4, 3, 2, 0, 3, 2, 1, 5, 4, 5, 0, 6, 4, 6, 1, 1, 5, 0, 6,
+    7, 11, 6, 4, 3, 6, 1, 3, 4, 1, 0, 4, 2, 0, 3, 2, 0, 1, 5, 0, 6, 5, 5, 2,
+    7, 11, 6, 1, 2, 6, 1, 2, 0, 1, 3, 0, 4, 3, 5, 4, 3, 5, 2, 0, 4, 0, 5, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 4, 3, 0, 2,
+    7, 12, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, 0, 6,
+    7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 5, 1, 2, 4,
+    7, 12, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 2, 3,
+    7, 12, 0, 1, 1, 2, 0, 2, 3, 2, 3, 1, 4, 0, 2, 4, 5, 1, 0, 5, 4, 5, 3, 4, 5, 3,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 1, 5, 6, 1,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 4, 6, 5, 3,
+    7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 1, 6,
+    7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 3, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 4, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 6, 1,
+    7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 0, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 0, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 2, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2, 1, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2, 4, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 0, 2, 6, 1, 6, 5,
+    7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 1, 5, 3, 2, 5, 1, 0, 4, 1, 1, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 3, 5, 0, 3, 5, 6,
+    7, 12, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, 1, 4,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 3, 5, 0, 3, 3, 6,
+    7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 4, 6,
+    7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 1, 5, 3, 2, 5, 1, 0, 4, 1, 2, 6,
+    7, 12, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, 0, 4,
+    7, 12, 1, 3, 4, 1, 3, 4, 2, 3, 0, 2, 4, 0, 5, 4, 2, 5, 4, 2, 0, 5, 1, 5, 3, 6,
+    7, 12, 1, 3, 4, 1, 3, 4, 2, 3, 0, 2, 4, 0, 5, 4, 2, 5, 4, 2, 0, 5, 1, 5, 0, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 0, 4, 5, 5, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 0, 3, 5, 6,
+    7, 12, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 4, 6,
+    7, 12, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 0, 6,
+    7, 12, 3, 6, 1, 3, 2, 1, 0, 2, 5, 0, 6, 5, 2, 6, 5, 1, 0, 3, 1, 6, 0, 1, 4, 5,
+    7, 12, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 3, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 0, 3, 0, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 0, 2, 5, 6,
+    7, 12, 0, 3, 6, 0, 5, 6, 3, 5, 1, 3, 6, 1, 4, 6, 3, 4, 2, 3, 6, 2, 3, 6, 5, 4,
+    7, 12, 0, 1, 4, 0, 5, 4, 1, 5, 4, 1, 2, 4, 1, 2, 6, 1, 4, 6, 2, 6, 3, 2, 4, 3,
+    7, 12, 4, 1, 3, 2, 3, 0, 4, 2, 5, 1, 5, 0, 3, 5, 4, 3, 5, 4, 6, 5, 3, 6, 4, 6,
+    7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 4, 2, 3, 4, 5, 3, 0, 5, 6, 3, 1, 6,
+    7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 6, 3, 0, 6, 5, 0, 1, 5, 4, 1, 2, 4,
+    7, 12, 6, 2, 5, 6, 3, 5, 2, 3, 1, 2, 4, 1, 5, 4, 2, 5, 4, 2, 0, 4, 1, 0, 5, 1,
+    7, 12, 5, 4, 6, 5, 3, 6, 4, 3, 0, 4, 3, 0, 1, 3, 0, 1, 4, 1, 3, 5, 2, 3, 4, 2,
+    7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 0, 1, 5, 6, 1, 0, 6,
+    7, 12, 1, 2, 0, 1, 2, 0, 3, 2, 0, 3, 1, 3, 4, 2, 0, 4, 5, 4, 2, 5, 6, 2, 1, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 2, 4, 5, 6, 4, 3, 6,
+    7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 0, 1, 5, 6, 1, 2, 6,
+    7, 12, 1, 2, 0, 1, 2, 0, 3, 2, 0, 3, 1, 3, 4, 2, 0, 4, 6, 4, 2, 6, 5, 2, 4, 5,
+    7, 12, 1, 0, 2, 1, 0, 2, 3, 0, 4, 3, 0, 4, 5, 0, 3, 5, 4, 5, 6, 4, 3, 6, 6, 0,
+    7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 4, 1, 0, 4, 5, 0, 2, 5, 6, 5, 2, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 6, 4, 0, 6, 5, 0, 3, 5,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 1, 5, 6, 1, 0, 6, 5, 0, 4, 5,
+    7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 6, 1, 0, 6, 5, 0, 2, 5,
+    7, 12, 5, 4, 3, 5, 4, 3, 6, 4, 3, 6, 2, 3, 4, 2, 0, 4, 3, 0, 1, 0, 2, 1, 6, 2,
+    7, 12, 4, 1, 3, 2, 3, 0, 4, 2, 5, 1, 5, 0, 3, 5, 4, 3, 5, 4, 6, 5, 0, 6, 3, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 6, 1, 0, 6, 5, 0, 1, 5,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 3, 1, 5, 1, 6, 5, 4, 6,
+    7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 4, 3, 5, 6, 3, 4, 6,
+    7, 12, 1, 0, 2, 1, 3, 2, 0, 3, 4, 3, 1, 4, 4, 0, 5, 4, 0, 5, 3, 5, 6, 0, 1, 6,
+    7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 6, 2, 4, 6, 5, 0, 1, 5,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 5, 4, 2, 5, 1, 5, 6, 1, 5, 6,
+    7, 12, 0, 2, 1, 0, 2, 1, 0, 3, 3, 1, 4, 2, 5, 3, 6, 1, 6, 4, 4, 0, 3, 4, 1, 5,
+    7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 2, 4, 3, 4, 1, 0, 4, 5, 2, 5, 4, 6, 0, 3, 6,
+    7, 12, 5, 0, 2, 5, 6, 2, 3, 6, 2, 3, 1, 2, 0, 1, 4, 0, 1, 4, 5, 1, 6, 5, 0, 2,
+    7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 6, 4, 2, 6, 5, 2, 3, 5,
+    7, 12, 0, 2, 1, 0, 5, 1, 3, 5, 6, 3, 2, 6, 4, 2, 3, 4, 5, 4, 2, 5, 1, 2, 4, 1,
+    7, 12, 0, 2, 1, 0, 2, 1, 0, 3, 3, 1, 4, 0, 1, 4, 4, 2, 3, 4, 5, 4, 6, 5, 3, 6,
+    7, 12, 0, 1, 1, 2, 0, 2, 3, 0, 3, 1, 3, 2, 6, 1, 2, 6, 4, 6, 3, 4, 5, 3, 6, 5,
+    7, 12, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 0, 6, 5, 0, 6, 3, 4,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 2, 5, 6, 0, 3, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 3, 5, 1, 5, 4, 6, 3, 4, 6, 1, 6, 6, 5,
+    7, 12, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 2, 6, 5, 0, 6, 6, 2, 0, 5, 1, 5, 6, 1,
+    7, 12, 0, 1, 6, 5, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3, 6, 4, 5, 1,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 6, 1, 5, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 4, 5, 6, 4, 5, 6,
+    7, 12, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 1, 5, 3, 2, 5, 1, 0, 4, 1, 6, 3, 6, 1,
+    7, 12, 0, 4, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 12, 0, 1, 2, 4, 0, 2, 2, 1, 4, 6, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 6, 1, 2, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 3, 5, 1, 6, 3, 6, 4, 4, 2, 0, 3, 4, 3,
+    7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 1, 6, 5,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 6, 5, 0, 6, 1, 6,
+    7, 12, 0, 3, 4, 0, 2, 4, 3, 2, 1, 3, 4, 1, 1, 0, 5, 1, 5, 2, 6, 1, 2, 6, 4, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 1, 6, 5, 3, 1, 5, 6, 2,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 1, 5, 5, 3, 6, 1, 4, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 1, 5, 5, 3, 6, 1, 0, 6,
+    7, 12, 4, 3, 1, 2, 0, 1, 0, 3, 4, 0, 6, 4, 4, 2, 5, 4, 6, 2, 6, 3, 3, 2, 5, 1,
+    7, 12, 2, 3, 4, 2, 0, 4, 6, 0, 6, 5, 4, 5, 1, 3, 5, 1, 0, 5, 6, 1, 3, 6, 5, 3,
+    7, 12, 0, 3, 0, 5, 1, 2, 1, 5, 1, 6, 2, 4, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 3, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 4, 3, 5, 3, 6, 1, 3, 6, 6, 2, 0, 6, 4, 6,
+    7, 12, 0, 1, 2, 4, 0, 2, 6, 1, 3, 1, 3, 2, 4, 1, 5, 1, 5, 2, 5, 3, 0, 3, 4, 6,
+    7, 12, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 3, 1, 6,
+    7, 12, 4, 3, 1, 2, 5, 0, 0, 3, 4, 0, 4, 1, 4, 2, 5, 1, 6, 2, 6, 3, 3, 2, 6, 4,
+    7, 12, 2, 3, 4, 2, 5, 4, 0, 5, 6, 0, 1, 6, 3, 1, 6, 3, 5, 3, 1, 5, 4, 0, 3, 0,
+    7, 12, 0, 3, 0, 5, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 6, 5, 6,
+    7, 12, 3, 2, 1, 2, 5, 0, 0, 3, 4, 0, 4, 1, 6, 4, 5, 1, 6, 2, 6, 3, 6, 1, 0, 6,
+    7, 12, 0, 5, 0, 6, 1, 3, 1, 4, 1, 6, 2, 3, 2, 4, 2, 6, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 3, 0, 5, 1, 2, 1, 4, 1, 6, 2, 4, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 3, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 6, 5, 6,
+    7, 12, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 3, 4, 5, 3, 2, 6, 6, 1,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 5, 4, 4, 1, 3, 4, 5, 3, 6, 1, 6, 5,
+    7, 12, 0, 2, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 12, 0, 5, 0, 6, 1, 3, 1, 4, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 1, 2, 4, 5, 2, 0, 5, 4, 3, 6, 5, 6, 4, 3, 5,
+    7, 12, 0, 2, 0, 6, 1, 2, 1, 4, 1, 5, 2, 3, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 2, 0, 6, 1, 3, 1, 4, 1, 5, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6,
+    7, 12, 0, 2, 0, 6, 1, 3, 1, 4, 1, 5, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 12, 0, 5, 0, 6, 1, 3, 1, 4, 1, 6, 2, 3, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 5, 6,
+    7, 12, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 12, 3, 0, 2, 3, 4, 2, 0, 4, 5, 1, 5, 2, 6, 1, 6, 0, 3, 6, 5, 3, 4, 5, 6, 4,
+    7, 12, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 1, 0, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 12, 3, 0, 2, 3, 4, 2, 0, 4, 5, 1, 5, 2, 6, 1, 6, 0, 3, 6, 5, 3, 6, 4, 1, 3,
+    7, 12, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 6, 4, 6, 5, 6,
+    7, 12, 2, 3, 4, 2, 5, 2, 4, 1, 6, 0, 3, 0, 3, 1, 6, 3, 5, 6, 1, 5, 4, 0, 3, 4,
+    7, 12, 2, 3, 4, 2, 4, 1, 2, 5, 6, 0, 6, 4, 3, 1, 6, 3, 0, 3, 1, 5, 4, 0, 5, 3,
+    7, 12, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 3, 2, 6, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 12, 3, 0, 2, 3, 4, 2, 0, 4, 5, 1, 5, 2, 6, 1, 6, 0, 3, 6, 1, 3, 6, 4, 5, 4,
+    7, 12, 6, 3, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 0, 6, 4, 1, 3, 4, 6, 5, 5, 1, 0, 4,
+    7, 12, 0, 3, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6,
+    7, 12, 0, 3, 0, 5, 0, 6, 1, 2, 1, 4, 1, 6, 2, 3, 2, 5, 3, 4, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 3, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 4, 5, 4, 6, 5, 6,
+    7, 12, 0, 4, 3, 0, 1, 3, 4, 1, 1, 0, 4, 5, 2, 4, 6, 2, 5, 6, 2, 5, 3, 2, 6, 3,
+    7, 12, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 5, 2, 5, 4, 6, 4, 6, 3, 6, 2, 5, 3,
+    7, 12, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 4, 5, 4, 6,
+    7, 12, 3, 0, 4, 2, 3, 1, 4, 0, 5, 2, 5, 1, 4, 3, 5, 4, 3, 5, 6, 1, 0, 6, 2, 6,
+    7, 12, 1, 0, 4, 1, 0, 4, 5, 0, 6, 5, 1, 6, 3, 4, 5, 3, 2, 3, 5, 2, 6, 3, 2, 6,
+    7, 12, 0, 1, 2, 0, 2, 3, 3, 4, 0, 4, 0, 5, 6, 1, 4, 6, 6, 5, 2, 6, 3, 1, 5, 3,
+    7, 12, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 6, 0, 6, 1, 6, 2, 6, 3, 6, 4, 6, 5,
+    7, 12, 3, 6, 1, 2, 0, 6, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 2, 5, 4, 5, 6, 4,
+    7, 13, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 0, 6, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 4, 3, 0, 2, 6, 4,
+    7, 13, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 0, 4, 1, 6,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 6, 4, 5, 5, 6,
+    7, 13, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 0, 3, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 13, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6,
+    7, 13, 0, 6, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 4, 5, 1, 6,
+    7, 13, 0, 1, 1, 2, 2, 3, 4, 5, 0, 4, 1, 3, 4, 1, 2, 4, 0, 3, 5, 3, 4, 3, 0, 2, 5, 6,
+    7, 13, 0, 5, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 6, 5, 6,
+    7, 13, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 13, 5, 6, 0, 5, 6, 0, 4, 6, 5, 4, 1, 5, 6, 1, 3, 6, 5, 3, 2, 5, 6, 2, 1, 0, 2, 1,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 6,
+    7, 13, 3, 4, 0, 3, 4, 0, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 6, 0, 3, 6, 5, 3, 4, 5,
+    7, 13, 3, 4, 0, 3, 4, 0, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 6, 0, 3, 6, 5, 3, 0, 5,
+    7, 13, 3, 4, 0, 3, 4, 0, 1, 4, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 6, 4, 0, 6, 5, 0, 3, 5,
+    7, 13, 0, 5, 0, 6, 1, 3, 1, 4, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 5, 4, 6,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 1, 6, 5, 1, 6,
+    7, 13, 0, 4, 0, 6, 1, 3, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 0, 5, 0, 6, 1, 4, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6,
+    7, 13, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 3, 6, 5, 4, 6,
+    7, 13, 0, 5, 0, 6, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 1, 6, 5, 5, 1,
+    7, 13, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 4, 0, 5, 6, 0, 3, 6, 6, 4,
+    7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 1, 5, 1, 2, 6,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 5, 6,
+    7, 13, 1, 0, 2, 1, 4, 2, 1, 4, 3, 1, 0, 3, 2, 3, 5, 2, 1, 5, 0, 5, 6, 0, 1, 6, 2, 6,
+    7, 13, 2, 5, 6, 2, 5, 6, 4, 5, 3, 4, 0, 3, 4, 0, 1, 4, 3, 1, 6, 3, 2, 1, 4, 2, 3, 2,
+    7, 13, 0, 3, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 13, 2, 4, 3, 2, 1, 3, 4, 1, 0, 4, 3, 0, 6, 3, 1, 6, 5, 1, 4, 5, 6, 4, 3, 5, 1, 2,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 4, 2, 5, 3, 5, 3, 6,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 5, 5, 6,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 5, 3, 6, 4, 5,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 4, 2, 5, 3, 4, 3, 5,
+    7, 13, 0, 2, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 2, 5, 1, 2, 5, 1, 4, 5, 3, 4, 0, 3, 4, 0, 3, 2, 4, 2, 1, 3, 6, 3, 2, 6, 1, 6,
+    7, 13, 0, 4, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6,
+    7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 0, 4, 6, 1, 4, 6,
+    7, 13, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, 5, 0, 6, 5, 1, 6, 4, 0,
+    7, 13, 0, 1, 1, 2, 0, 2, 3, 0, 1, 3, 3, 2, 4, 0, 2, 5, 3, 4, 5, 3, 0, 5, 6, 4, 6, 1,
+    7, 13, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, 6, 2, 5, 6, 0, 5, 1, 2,
+    7, 13, 5, 4, 6, 2, 6, 4, 4, 3, 5, 0, 3, 1, 3, 2, 6, 3, 5, 6, 4, 0, 1, 4, 5, 1, 0, 3,
+    7, 13, 0, 2, 0, 6, 1, 3, 1, 4, 1, 5, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 1, 5, 4, 1, 0, 4, 5, 0, 2, 5, 4, 2, 3, 4, 5, 3, 0, 1, 2, 0, 3, 2, 6, 5, 6, 4,
+    7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 0, 4, 0, 6, 4, 6,
+    7, 13, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 5, 0, 4, 5, 1, 5, 6, 1, 0, 6, 3, 6,
+    7, 13, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 4, 1, 5, 3, 2, 5, 1, 0, 6, 3, 4, 6, 5, 1,
+    7, 13, 5, 2, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 6, 2, 6, 3, 1, 2, 3, 1, 4, 0,
+    7, 13, 1, 0, 2, 1, 0, 2, 0, 3, 3, 2, 6, 2, 1, 6, 5, 1, 6, 5, 4, 6, 5, 4, 0, 5, 4, 0,
+    7, 13, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6,
+    7, 13, 0, 5, 0, 6, 1, 3, 1, 4, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 13, 0, 1, 1, 2, 4, 1, 3, 1, 0, 4, 2, 3, 0, 3, 2, 0, 5, 0, 6, 5, 4, 6, 3, 5, 4, 2,
+    7, 13, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6,
+    7, 13, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 0, 4, 2, 3, 6, 5, 0, 6,
+    7, 13, 5, 2, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 6, 2, 6, 3, 2, 3, 5, 0, 4, 0,
+    7, 13, 0, 1, 1, 2, 2, 3, 5, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 6, 3, 6, 4, 4, 2, 0, 3,
+    7, 13, 0, 1, 0, 6, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6,
+    7, 13, 0, 1, 0, 6, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 13, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 2, 0, 6, 5, 0, 2, 5, 5, 3, 4, 5, 6, 4, 3, 6,
+    7, 13, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 5, 0, 6, 0, 2, 2, 5, 5, 3, 4, 5, 6, 4, 3, 6,
+    7, 13, 3, 4, 0, 3, 4, 0, 3, 6, 3, 1, 2, 3, 4, 2, 1, 0, 2, 1, 5, 2, 3, 5, 6, 2, 5, 6,
+    7, 13, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 6, 5, 3, 6, 4, 6,
+    7, 13, 1, 0, 2, 1, 6, 0, 1, 4, 3, 1, 0, 3, 2, 3, 1, 6, 1, 5, 2, 6, 4, 3, 5, 4, 6, 5,
+    7, 13, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 5, 2, 6, 3, 5, 4, 6,
+    7, 13, 0, 6, 1, 2, 2, 3, 0, 3, 4, 2, 5, 0, 6, 3, 4, 1, 3, 4, 6, 5, 1, 5, 3, 5, 1, 3,
+    7, 13, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 6, 4, 4, 3, 5, 4, 5, 2, 6, 0, 3, 6, 3, 5,
+    7, 13, 0, 1, 1, 2, 2, 3, 3, 6, 0, 4, 6, 5, 0, 6, 6, 4, 2, 5, 5, 3, 4, 5, 1, 5, 6, 1,
+    7, 13, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 13, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 4, 5, 4, 6, 5, 6,
+    7, 13, 2, 3, 5, 2, 6, 5, 3, 6, 4, 3, 5, 4, 0, 5, 2, 0, 1, 2, 0, 1, 5, 1, 4, 2, 6, 4,
+    7, 13, 2, 1, 0, 5, 6, 0, 4, 6, 5, 4, 1, 5, 6, 1, 3, 6, 5, 3, 2, 5, 6, 2, 1, 0, 3, 4,
+    7, 13, 0, 1, 2, 0, 2, 3, 3, 4, 0, 4, 0, 5, 6, 1, 4, 6, 6, 5, 2, 6, 3, 1, 5, 3, 6, 0,
+    7, 13, 0, 4, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 13, 2, 3, 0, 2, 3, 0, 4, 3, 4, 6, 5, 1, 4, 5, 3, 1, 5, 2, 6, 0, 6, 1, 2, 1, 4, 1,
+    7, 13, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 3, 5, 4, 5, 2, 5, 1, 6, 5, 1, 6, 2, 6,
+    7, 13, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6,
+    7, 13, 3, 0, 2, 3, 4, 2, 0, 4, 5, 1, 5, 2, 6, 1, 6, 0, 3, 6, 1, 3, 6, 4, 5, 4, 5, 3,
+    7, 13, 0, 4, 0, 5, 0, 6, 1, 2, 1, 4, 1, 5, 2, 3, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6,
+    7, 13, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 6, 4, 6, 5, 6,
+    7, 13, 0, 2, 0, 5, 0, 6, 1, 2, 1, 4, 1, 6, 2, 3, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 3, 4, 5, 0, 3, 5, 0, 6, 6, 4, 2, 3, 4, 2, 1, 0, 2, 1, 1, 6, 5, 1, 2, 5, 6, 2,
+    7, 13, 0, 2, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 13, 3, 0, 2, 3, 4, 2, 0, 4, 5, 3, 5, 2, 5, 4, 1, 3, 1, 0, 4, 1, 6, 0, 3, 6, 1, 6,
+    7, 13, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 6, 0, 1, 6, 6, 3, 4, 6, 1, 2, 5, 0,
+    7, 13, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6,
+    7, 13, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5,
+    7, 13, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 5, 3, 5, 4, 6, 4, 6, 2, 6, 5, 5, 2, 3, 6,
+    7, 13, 0, 1, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 13, 0, 1, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 13, 0, 1, 2, 0, 2, 3, 3, 4, 0, 4, 0, 5, 6, 1, 4, 6, 6, 5, 2, 6, 3, 1, 5, 3, 2, 1,
+    7, 14, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 1, 3, 2, 0, 4, 0, 5, 3,
+    7, 14, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 2, 3, 0, 4, 1, 6,
+    7, 14, 0, 6, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6,
+    7, 14, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 3, 1, 2, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 3, 6, 5, 3, 4, 5, 6, 4,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 6, 2, 1, 6, 5, 1, 0, 5,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 2, 3, 5, 6, 4, 0, 6,
+    7, 14, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 2, 4, 5, 1, 0, 3, 1, 4, 0, 1, 0, 4, 6, 4, 0, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 3, 4, 3, 5, 4, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 6, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 3, 4, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 0, 1, 5, 1, 0, 4, 1, 4, 5, 3, 2, 5, 6, 4, 0, 6,
+    7, 14, 1, 3, 2, 1, 0, 2, 5, 0, 4, 5, 3, 4, 5, 3, 2, 5, 1, 0, 4, 1, 6, 1, 5, 1, 2, 6, 0, 4,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 6, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 6, 0, 3, 4, 3, 1, 5, 4, 5, 0, 0, 3, 1, 5, 5, 3, 6, 4, 6, 1,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 3, 6, 5, 4, 6, 5, 4,
+    7, 14, 3, 1, 1, 4, 2, 3, 3, 4, 0, 4, 1, 5, 0, 1, 0, 2, 2, 5, 5, 3, 4, 5, 1, 2, 6, 2, 5, 6,
+    7, 14, 0, 3, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 3, 0, 6, 1, 2, 1, 4, 1, 5, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 14, 0, 1, 0, 6, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 4, 6, 5, 2, 1, 5, 0, 5, 6, 3,
+    7, 14, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 4, 2, 3, 0, 6, 1, 5, 6,
+    7, 14, 0, 4, 0, 6, 1, 2, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 4, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 5, 6,
+    7, 14, 0, 5, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 14, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 1, 2, 5, 2, 4, 0, 3, 1, 5, 0, 6, 5, 6, 4, 0, 1,
+    7, 14, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 5, 2, 6, 0, 6, 1, 5, 0, 1, 2, 3, 1, 4, 0,
+    7, 14, 5, 6, 0, 5, 6, 0, 4, 6, 5, 4, 1, 5, 6, 1, 3, 6, 5, 3, 2, 5, 6, 2, 1, 0, 2, 1, 3, 4,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 14, 0, 1, 2, 0, 2, 3, 3, 4, 0, 4, 0, 5, 6, 1, 4, 6, 6, 5, 2, 6, 3, 1, 5, 3, 6, 0, 3, 6,
+    7, 14, 3, 1, 4, 2, 4, 5, 4, 0, 1, 4, 0, 3, 5, 0, 5, 2, 6, 1, 3, 6, 6, 0, 5, 6, 6, 2, 4, 6,
+    7, 14, 0, 4, 3, 0, 2, 3, 4, 2, 1, 4, 3, 1, 1, 0, 2, 1, 5, 4, 1, 5, 6, 1, 3, 6, 0, 5, 6, 0,
+    7, 14, 3, 4, 4, 2, 1, 5, 4, 0, 1, 4, 5, 3, 3, 0, 5, 2, 6, 4, 0, 6, 3, 6, 2, 6, 5, 6, 1, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 2, 3, 2, 6, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 4, 5, 3, 4, 3, 1, 5, 2, 1, 6, 5, 6, 6, 0, 5, 3, 6, 4, 0, 1,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 1, 4, 6, 0, 3, 1, 5, 2, 4, 5, 5, 6, 1, 5, 5, 3, 6, 4, 3, 0,
+    7, 14, 3, 1, 4, 2, 0, 3, 4, 0, 1, 4, 5, 3, 5, 0, 5, 2, 6, 4, 1, 6, 6, 3, 0, 6, 6, 5, 2, 6,
+    7, 14, 0, 1, 4, 2, 3, 0, 4, 0, 4, 5, 5, 3, 1, 3, 5, 2, 6, 4, 2, 6, 6, 5, 3, 6, 6, 1, 0, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 6, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 14, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 3, 2, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 1, 4, 6, 1, 3, 1, 5, 2, 5, 0, 5, 6, 4, 5, 5, 3, 6, 0, 3, 0,
+    7, 14, 2, 3, 4, 2, 3, 0, 4, 0, 4, 5, 3, 4, 3, 1, 5, 2, 0, 1, 5, 6, 6, 0, 5, 3, 6, 4, 1, 6,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 14, 0, 3, 0, 4, 0, 6, 1, 2, 1, 4, 1, 5, 2, 3, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6,
+    7, 14, 0, 1, 0, 5, 0, 6, 1, 4, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 5, 6,
+    7, 14, 0, 1, 0, 4, 0, 6, 1, 3, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 1, 4, 4, 5, 3, 1, 5, 2, 5, 0, 6, 1, 0, 6, 5, 3, 6, 4, 3, 0,
+    7, 14, 0, 1, 0, 5, 0, 6, 1, 3, 1, 4, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 2, 3, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 5, 6,
+    7, 14, 2, 3, 4, 2, 6, 3, 4, 0, 4, 5, 3, 5, 3, 1, 5, 2, 3, 0, 1, 4, 6, 0, 1, 6, 6, 4, 0, 1,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 6,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6,
+    7, 14, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 6, 3, 4, 3, 5, 4, 6, 5, 6,
+    7, 14, 0, 3, 0, 4, 0, 5, 1, 2, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 6, 5, 6,
+    7, 14, 0, 3, 0, 4, 0, 5, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 6, 4, 6, 5, 6,
+    7, 14, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0, 6, 0, 2, 5, 0, 3, 5, 1, 3, 6, 1, 4, 6, 2, 4,
+    7, 14, 0, 3, 0, 4, 0, 5, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 6, 4, 5,
+    7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5,
+    7, 15, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 1, 3, 2, 0, 4, 0, 5, 3, 1, 6,
+    7, 15, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 2, 4, 5, 2, 1, 5, 1, 4, 1, 3, 2, 0, 4, 0, 5, 3, 0, 6,
+    7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 3, 4, 3, 5, 3, 6, 5, 6,
+    7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 15, 3, 4, 4, 5, 0, 3, 0, 4, 0, 5, 0, 6, 3, 6, 4, 6, 5, 6, 1, 5, 3, 5, 2, 3, 2, 4, 1, 6, 0, 1,
+    7, 15, 3, 4, 4, 5, 0, 3, 0, 4, 0, 5, 0, 6, 3, 6, 1, 3, 1, 4, 1, 5, 3, 5, 2, 3, 2, 4, 6, 1, 4, 6,
+    7, 15, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 1, 4, 2, 4, 3, 5, 1, 0, 5, 5, 2, 3, 5, 4, 5, 6, 1, 5, 6,
+    7, 15, 4, 3, 4, 5, 5, 3, 0, 1, 0, 5, 0, 3, 2, 4, 1, 5, 1, 3, 6, 5, 3, 6, 6, 0, 1, 6, 6, 2, 4, 6,
+    7, 15, 3, 4, 4, 5, 5, 6, 0, 4, 0, 5, 0, 6, 3, 6, 1, 3, 4, 6, 1, 5, 3, 5, 2, 3, 2, 4, 1, 6, 0, 1,
+    7, 15, 0, 2, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 15, 6, 1, 4, 5, 0, 3, 0, 4, 0, 5, 0, 6, 3, 6, 1, 3, 1, 4, 1, 5, 3, 5, 2, 3, 2, 4, 5, 6, 4, 6,
+    7, 15, 3, 4, 4, 5, 0, 3, 0, 4, 0, 5, 4, 6, 3, 6, 1, 3, 1, 4, 1, 5, 3, 5, 2, 3, 2, 4, 5, 6, 5, 2,
+    7, 15, 3, 4, 4, 5, 0, 3, 0, 4, 6, 0, 4, 6, 3, 6, 1, 3, 1, 4, 1, 5, 3, 5, 2, 3, 2, 4, 5, 6, 5, 2,
+    7, 15, 0, 1, 1, 2, 0, 2, 3, 0, 1, 3, 2, 3, 5, 1, 3, 5, 4, 3, 2, 4, 6, 2, 3, 6, 6, 1, 0, 5, 4, 0,
+    7, 15, 0, 1, 2, 0, 3, 2, 4, 3, 1, 4, 3, 1, 4, 2, 0, 4, 3, 0, 6, 3, 4, 6, 5, 4, 3, 5, 6, 2, 5, 1,
+    7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 6, 3,
+    7, 15, 0, 1, 1, 2, 2, 3, 0, 3, 4, 0, 4, 3, 4, 2, 6, 1, 2, 6, 5, 2, 4, 5, 6, 4, 0, 6, 6, 5, 3, 6,
+    7, 15, 0, 1, 5, 3, 1, 3, 0, 4, 3, 0, 4, 3, 2, 4, 5, 2, 4, 5, 6, 4, 2, 6, 6, 5, 3, 6, 6, 1, 0, 6,
+    7, 15, 5, 2, 4, 5, 3, 1, 0, 4, 0, 5, 0, 3, 2, 4, 1, 5, 1, 4, 6, 3, 1, 6, 6, 0, 5, 6, 6, 2, 4, 6,
+    7, 15, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 0, 5, 0, 3, 2, 0, 3, 1, 6, 4, 5, 6, 6, 3, 0, 6, 6, 2, 1, 6,
+    7, 15, 5, 2, 3, 0, 5, 3, 0, 4, 0, 5, 4, 3, 2, 4, 1, 5, 1, 4, 6, 4, 2, 6, 6, 0, 5, 6, 6, 3, 1, 6,
+    7, 15, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 15, 6, 1, 4, 5, 0, 3, 0, 4, 0, 5, 4, 6, 3, 6, 1, 3, 1, 4, 0, 6, 3, 5, 2, 3, 2, 4, 5, 6, 5, 2,
+    7, 15, 3, 4, 0, 1, 0, 3, 0, 4, 0, 5, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 1, 6, 2, 3, 2, 4, 5, 6, 5, 2,
+    7, 15, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 6,
+    7, 15, 5, 2, 4, 5, 5, 3, 0, 4, 0, 1, 1, 3, 2, 4, 3, 0, 1, 4, 6, 4, 1, 6, 6, 0, 3, 6, 6, 2, 5, 6,
+    7, 15, 5, 0, 4, 3, 5, 3, 5, 2, 0, 1, 1, 3, 2, 4, 3, 0, 1, 4, 6, 2, 5, 6, 6, 4, 3, 6, 6, 0, 1, 6,
+    7, 15, 3, 4, 4, 5, 0, 3, 4, 6, 0, 1, 1, 6, 3, 6, 1, 3, 1, 4, 6, 0, 0, 5, 2, 3, 2, 4, 5, 6, 5, 2,
+    7, 15, 0, 2, 0, 3, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 15, 0, 4, 0, 5, 0, 6, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 15, 3, 4, 5, 0, 0, 3, 0, 4, 4, 6, 1, 6, 3, 6, 1, 3, 1, 4, 6, 0, 1, 5, 2, 3, 2, 4, 5, 6, 5, 2,
+    7, 15, 6, 4, 5, 2, 0, 3, 0, 4, 2, 4, 1, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 0, 1, 5, 6, 4, 5,
+    7, 15, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 5, 1, 6, 2, 3, 2, 4, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 15, 0, 1, 0, 2, 0, 3, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 15, 2, 3, 0, 2, 3, 0, 4, 3, 1, 4, 5, 1, 4, 5, 1, 0, 5, 2, 6, 2, 5, 6, 6, 1, 0, 6, 6, 4, 3, 6,
+    7, 15, 3, 0, 3, 5, 3, 4, 2, 0, 2, 5, 2, 4, 1, 4, 1, 5, 1, 0, 6, 0, 1, 6, 6, 5, 3, 6, 6, 4, 2, 6,
+    7, 15, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 15, 3, 4, 6, 2, 0, 3, 0, 4, 5, 0, 1, 6, 3, 6, 1, 3, 1, 4, 6, 0, 4, 5, 2, 3, 2, 4, 5, 1, 5, 2,
+    7, 15, 3, 4, 6, 2, 0, 3, 0, 4, 5, 0, 5, 6, 3, 6, 1, 3, 1, 4, 0, 1, 4, 6, 2, 3, 2, 4, 5, 1, 5, 2,
+    7, 15, 0, 1, 1, 2, 2, 3, 3, 4, 0, 4, 6, 2, 1, 6, 6, 0, 4, 6, 5, 4, 0, 5, 3, 5, 6, 3, 5, 2, 1, 5,
+    7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, 2, 6,
+    7, 16, 3, 0, 4, 1, 4, 3, 1, 3, 4, 0, 2, 5, 6, 2, 5, 6, 1, 5, 4, 5, 3, 5, 0, 5, 0, 6, 3, 6, 4, 6, 6, 1,
+    7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 6, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 16, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 16, 3, 4, 5, 1, 0, 3, 0, 4, 5, 0, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 2, 4, 5, 6, 4, 5, 2, 5,
+    7, 16, 2, 4, 3, 1, 3, 0, 4, 3, 4, 0, 5, 2, 4, 5, 5, 0, 3, 5, 5, 1, 6, 5, 1, 6, 3, 6, 6, 0, 4, 6, 6, 2,
+    7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 16, 2, 4, 4, 1, 3, 0, 3, 1, 4, 0, 5, 2, 4, 5, 5, 0, 3, 5, 6, 5, 1, 5, 6, 1, 3, 6, 4, 6, 6, 2, 6, 0,
+    7, 16, 0, 1, 0, 3, 0, 5, 0, 6, 1, 3, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 16, 2, 5, 0, 1, 4, 5, 1, 3, 5, 0, 4, 3, 5, 3, 2, 4, 1, 4, 3, 0, 6, 3, 2, 6, 6, 4, 5, 6, 6, 1, 0, 6,
+    7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 3, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 4, 5, 4, 6, 5, 6,
+    7, 16, 2, 5, 5, 1, 3, 1, 0, 4, 5, 0, 4, 3, 5, 3, 2, 4, 1, 4, 3, 0, 6, 2, 4, 6, 5, 6, 6, 1, 0, 6, 3, 6,
+    7, 16, 1, 6, 0, 1, 0, 3, 0, 4, 5, 0, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 2, 4, 5, 6, 4, 5, 2, 5,
+    7, 16, 3, 4, 5, 1, 0, 3, 0, 4, 5, 0, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 1, 6, 2, 3, 2, 4, 5, 6, 0, 1, 2, 5,
+    7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5,
+    7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 5, 6,
+    7, 16, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 5, 6,
+    7, 16, 2, 5, 5, 1, 3, 5, 0, 4, 0, 1, 4, 3, 3, 2, 2, 4, 1, 4, 0, 5, 6, 4, 2, 6, 6, 3, 5, 6, 6, 1, 0, 6,
+    7, 16, 5, 6, 5, 1, 0, 3, 0, 4, 0, 1, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 2, 4, 6, 2, 4, 5, 2, 5,
+    7, 16, 3, 4, 5, 1, 0, 3, 0, 4, 0, 1, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 5, 0, 2, 3, 2, 4, 6, 2, 6, 5, 2, 5,
+    7, 16, 5, 0, 5, 1, 0, 3, 0, 4, 6, 1, 4, 6, 3, 6, 1, 3, 1, 4, 6, 0, 3, 5, 2, 3, 2, 4, 6, 2, 4, 5, 2, 5,
+    7, 17, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, 6, 2, 1, 6,
+    7, 17, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 4, 5, 4, 6,
+    7, 17, 4, 0, 4, 3, 0, 1, 3, 0, 2, 4, 3, 1, 5, 3, 4, 5, 5, 2, 6, 5, 5, 0, 1, 5, 6, 1, 0, 6, 6, 4, 2, 6, 3, 6,
+    7, 17, 0, 1, 5, 1, 5, 3, 0, 4, 5, 0, 4, 3, 3, 1, 2, 5, 1, 4, 3, 0, 2, 4, 6, 2, 5, 6, 6, 3, 1, 6, 6, 0, 4, 6,
+    7, 17, 3, 4, 5, 1, 0, 3, 0, 4, 4, 5, 4, 6, 3, 6, 1, 3, 1, 4, 0, 1, 3, 5, 2, 3, 2, 4, 2, 5, 5, 0, 5, 6, 6, 2,
+    7, 17, 3, 2, 4, 1, 0, 1, 3, 0, 2, 4, 4, 3, 5, 1, 4, 5, 5, 2, 0, 5, 5, 3, 6, 5, 2, 6, 6, 3, 0, 6, 1, 6, 4, 6,
+    7, 17, 3, 2, 4, 1, 4, 0, 3, 0, 2, 4, 3, 1, 5, 2, 4, 5, 5, 0, 3, 5, 5, 1, 6, 5, 2, 6, 6, 0, 3, 6, 6, 4, 1, 6,
+    7, 17, 3, 2, 5, 1, 5, 0, 0, 4, 0, 1, 4, 3, 5, 3, 2, 5, 1, 4, 3, 0, 2, 4, 6, 4, 5, 6, 6, 2, 3, 6, 6, 0, 1, 6,
+    7, 17, 3, 2, 5, 1, 5, 0, 0, 4, 4, 5, 0, 1, 3, 1, 2, 5, 1, 4, 3, 0, 2, 4, 6, 0, 3, 6, 6, 1, 5, 6, 6, 2, 4, 6,
+    7, 17, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6,
+    7, 18, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, 6, 1, 0, 6, 5, 6,
+    7, 18, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6,
+    7, 18, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 4, 1, 5, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 18, 0, 1, 0, 2, 0, 3, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 18, 4, 0, 4, 5, 3, 0, 3, 5, 2, 0, 2, 5, 1, 3, 1, 4, 1, 5, 1, 0, 2, 3, 2, 4, 6, 0, 5, 6, 6, 1, 2, 6, 6, 4, 3, 6,
+    7, 19, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 19, 0, 1, 0, 2, 0, 3, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 20, 0, 1, 0, 2, 0, 3, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+    7, 21, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 2, 3, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 4, 5, 4, 6, 5, 6,
+};
+
+const long int igraph_i_atlas_edges_pos[] = {0, 2, 4, 6, 10, 12, 16, 22, 30, 32, 36, 42, 48, 56, 64, 72, 82, 92, 104, 118, 120, 124, 130, 136, 144, 152, 160, 168, 178, 188, 198, 208, 218, 228, 240, 252, 264, 276, 288, 300, 314, 328, 342, 356, 370, 384, 400, 416, 432, 448, 466, 484, 504, 526, 528, 532, 538, 544, 552, 560, 568, 576, 584, 594, 604, 614, 624, 634, 644, 654, 664, 674, 686, 698, 710, 722, 734, 746, 758, 770, 782, 794, 806, 818, 830, 842, 854, 868, 882, 896, 910, 924, 938, 952, 966, 980, 994, 1008, 1022, 1036, 1050, 1064, 1078, 1092, 1106, 1120, 1134, 1148, 1164, 1180, 1196, 1212, 1228, 1244, 1260, 1276, 1292, 1308, 1324, 1340, 1356, 1372, 1388, 1404, 1420, 1436, 1452, 1468, 1484, 1500, 1516, 1532, 1550, 1568, 1586, 1604, 1622, 1640, 1658, 1676, 1694, 1712, 1730, 1748, 1766, 1784, 1802, 1820, 1838, 1856, 1874, 1892, 1910, 1928, 1946, 1964, 1984, 2004, 2024, 2044, 2064, 2084, 2104, 2124, 2144, 2164, 2184, 2204, 2224, 2244, 2264, 2284, 2304, 2324, 2344, 2364, 2384, 2406, 2428, 2450, 2472, 2494, 2516, 2538, 2560, 2582, 2604, 2626, 2648, 2670, 2692, 2714, 2738, 2762, 2786, 2810, 2834, 2858, 2882, 2906, 2930, 2956, 2982, 3008, 3034, 3060, 3088, 3116, 3146, 3178, 3180, 3184, 3190, 3196, 3204, 3212, 3220, 3228, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316, 3326, 3336, 3348, 3360, 3372, 3384, 3396, 3408, 3420, 3432, 3444, 3456, 3468, 3480, 3492, 3504, 3516, 3528, 3540, 3552, 3564, 3576, 3588, 3602, 3616, 3630, 3644, 3658, 3672, 3686, 3700, 3714, 3728, 3742, 3756, 3770, 3784, 3798, 3812, 3826, 3840, 3854, 3868, 3882, 3896, 3910, 3924, 3938, 3952, 3966, 3980, 3994, 4008, 4022, 4036, 4050, 4064, 4078, 4092, 4106, 4120, 4134, 4148, 4162, 4178, 4194, 4210, 4226, 4242, 4258, 4274, 4290, 4306, 4322, 4338, 4354, 4370, 4386, 4402, 4418, 4434, 4450, 4466, 4482, 4498, 4514, 4530, 4546, 4562, 4578, 4594, 4610, 4626, 4642, 4658, 4674, 4690, 4706, 4722, 4738, 4754, 4770, 4786, 4802, 4818, 4834, 4850, 4866, 4882, 4898, 4914, 4930, 4946, 4962, 4978, 4994, 5010, 5026, 5042, 5058, 5074, 5090, 5106, 5122, 5138, 5154, 5170, 5186, 5202, 5220, 5238, 5256, 5274, 5292, 5310, 5328, 5346, 5364, 5382, 5400, 5418, 5436, 5454, 5472, 5490, 5508, 5526, 5544, 5562, 5580, 5598, 5616, 5634, 5652, 5670, 5688, 5706, 5724, 5742, 5760, 5778, 5796, 5814, 5832, 5850, 5868, 5886, 5904, 5922, 5940, 5958, 5976, 5994, 6012, 6030, 6048, 6066, 6084, 6102, 6120, 6138, 6156, 6174, 6192, 6210, 6228, 6246, 6264, 6282, 6300, 6318, 6336, 6354, 6372, 6390, 6408, 6426, 6444, 6462, 6480, 6498, 6516, 6534, 6552, 6570, 6588, 6606, 6624, 6642, 6660, 6678, 6696, 6714, 6732, 6750, 6768, 6786, 6804, 6822, 6840, 6858, 6876, 6894, 6912, 6930, 6948, 6968, 6988, 7008, 7028, 7048, 7068, 7088, 7108, 7128, 7148, 7168, 7188, 7208, 7228, 7248, 7268, 7288, 7308, 7328, 7348, 7368, 7388, 7408, 7428, 7448, 7468, 7488, 7508, 7528, 7548, 7568, 7588, 7608, 7628, 7648, 7668, 7688, 7708, 7728, 7748, 7768, 7788, 7808, 7828, 7848, 7868, 7888, 7908, 7928, 7948, 7968, 7988, 8008, 8028, 8048, 8068, 8088, 8108, 8128, 8148, 8168, 8188, 8208, 8228, 8248, 8268, 8288, 8308, 8328, 8348, 8368, 8388, 8408, 8428, 8448, 8468, 8488, 8508, 8528, 8548, 8568, 8588, 8608, 8628, 8648, 8668, 8688, 8708, 8728, 8748, 8768, 8788, 8808, 8828, 8848, 8868, 8888, 8908, 8928, 8948, 8968, 8988, 9008, 9028, 9048, 9068, 9088, 9108, 9128, 9148, 9168, 9188, 9208, 9228, 9248, 9268, 9288, 9308, 9328, 9348, 9368, 9388, 9408, 9428, 9448, 9468, 9488, 9508, 9528, 9548, 9568, 9590, 9612, 9634, 9656, 9678, 9700, 9722, 9744, 9766, 9788, 9810, 9832, 9854, 9876, 9898, 9920, 9942, 9964, 9986, 10008, 10030, 10052, 10074, 10096, 10118, 10140, 10162, 10184, 10206, 10228, 10250, 10272, 10294, 10316, 10338, 10360, 10382, 10404, 10426, 10448, 10470, 10492, 10514, 10536, 10558, 10580, 10602, 10624, 10646, 10668, 10690, 10712, 10734, 10756, 10778, 10800, 10822, 10844, 10866, 10888, 10910, 10932, 10954, 10976, 10998, 11020, 11042, 11064, 11086, 11108, 11130, 11152, 11174, 11196, 11218, 11240, 11262, 11284, 11306, 11328, 11350, 11372, 11394, 11416, 11438, 11460, 11482, 11504, 11526, 11548, 11570, 11592, 11614, 11636, 11658, 11680, 11702, 11724, 11746, 11768, 11790, 11812, 11834, 11856, 11878, 11900, 11922, 11944, 11966, 11988, 12010, 12032, 12054, 12076, 12098, 12120, 12142, 12164, 12186, 12208, 12230, 12252, 12274, 12296, 12318, 12340, 12362, 12384, 12406, 12428, 12450, 12472, 12494, 12516, 12538, 12560, 12582, 12604, 12626, 12648, 12670, 12692, 12714, 12736, 12758, 12780, 12802, 12824, 12848, 12872, 12896, 12920, 12944, 12968, 12992, 13016, 13040, 13064, 13088, 13112, 13136, 13160, 13184, 13208, 13232, 13256, 13280, 13304, 13328, 13352, 13376, 13400, 13424, 13448, 13472, 13496, 13520, 13544, 13568, 13592, 13616, 13640, 13664, 13688, 13712, 13736, 13760, 13784, 13808, 13832, 13856, 13880, 13904, 13928, 13952, 13976, 14000, 14024, 14048, 14072, 14096, 14120, 14144, 14168, 14192, 14216, 14240, 14264, 14288, 14312, 14336, 14360, 14384, 14408, 14432, 14456, 14480, 14504, 14528, 14552, 14576, 14600, 14624, 14648, 14672, 14696, 14720, 14744, 14768, 14792, 14816, 14840, 14864, 14888, 14912, 14936, 14960, 14984, 15008, 15032, 15056, 15080, 15104, 15128, 15152, 15176, 15200, 15224, 15248, 15272, 15296, 15320, 15344, 15368, 15392, 15416, 15440, 15464, 15488, 15512, 15536, 15560, 15584, 15608, 15632, 15656, 15680, 15704, 15728, 15752, 15776, 15800, 15824, 15848, 15872, 15896, 15920, 15944, 15968, 15992, 16016, 16040, 16064, 16088, 16112, 16136, 16160, 16184, 16208, 16232, 16256, 16280, 16304, 16328, 16352, 16376, 16402, 16428, 16454, 16480, 16506, 16532, 16558, 16584, 16610, 16636, 16662, 16688, 16714, 16740, 16766, 16792, 16818, 16844, 16870, 16896, 16922, 16948, 16974, 17000, 17026, 17052, 17078, 17104, 17130, 17156, 17182, 17208, 17234, 17260, 17286, 17312, 17338, 17364, 17390, 17416, 17442, 17468, 17494, 17520, 17546, 17572, 17598, 17624, 17650, 17676, 17702, 17728, 17754, 17780, 17806, 17832, 17858, 17884, 17910, 17936, 17962, 17988, 18014, 18040, 18066, 18092, 18118, 18144, 18170, 18196, 18222, 18248, 18274, 18300, 18326, 18352, 18378, 18404, 18430, 18456, 18482, 18508, 18534, 18560, 18586, 18612, 18638, 18664, 18690, 18716, 18742, 18768, 18794, 18820, 18846, 18872, 18898, 18924, 18950, 18976, 19002, 19028, 19054, 19080, 19106, 19132, 19158, 19184, 19210, 19236, 19262, 19288, 19314, 19340, 19366, 19392, 19418, 19444, 19470, 19496, 19522, 19548, 19574, 19600, 19626, 19652, 19678, 19704, 19730, 19756, 19782, 19810, 19838, 19866, 19894, 19922, 19950, 19978, 20006, 20034, 20062, 20090, 20118, 20146, 20174, 20202, 20230, 20258, 20286, 20314, 20342, 20370, 20398, 20426, 20454, 20482, 20510, 20538, 20566, 20594, 20622, 20650, 20678, 20706, 20734, 20762, 20790, 20818, 20846, 20874, 20902, 20930, 20958, 20986, 21014, 21042, 21070, 21098, 21126, 21154, 21182, 21210, 21238, 21266, 21294, 21322, 21350, 21378, 21406, 21434, 21462, 21490, 21518, 21546, 21574, 21602, 21630, 21658, 21686, 21714, 21742, 21770, 21798, 21826, 21854, 21882, 21910, 21938, 21966, 21994, 22022, 22050, 22078, 22106, 22134, 22162, 22190, 22218, 22246, 22274, 22302, 22330, 22358, 22386, 22414, 22442, 22470, 22498, 22528, 22558, 22588, 22618, 22648, 22678, 22708, 22738, 22768, 22798, 22828, 22858, 22888, 22918, 22948, 22978, 23008, 23038, 23068, 23098, 23128, 23158, 23188, 23218, 23248, 23278, 23308, 23338, 23368, 23398, 23428, 23458, 23488, 23518, 23548, 23578, 23608, 23638, 23668, 23698, 23728, 23758, 23788, 23818, 23848, 23878, 23908, 23938, 23968, 23998, 24028, 24058, 24088, 24118, 24148, 24178, 24208, 24238, 24268, 24298, 24328, 24358, 24388, 24418, 24448, 24480, 24512, 24544, 24576, 24608, 24640, 24672, 24704, 24736, 24768, 24800, 24832, 24864, 24896, 24928, 24960, 24992, 25024, 25056, 25088, 25120, 25152, 25184, 25216, 25248, 25280, 25312, 25344, 25376, 25408, 25440, 25472, 25504, 25536, 25568, 25600, 25632, 25664, 25696, 25728, 25760, 25794, 25828, 25862, 25896, 25930, 25964, 25998, 26032, 26066, 26100, 26134, 26168, 26202, 26236, 26270, 26304, 26338, 26372, 26406, 26440, 26474, 26510, 26546, 26582, 26618, 26654, 26690, 26726, 26762, 26798, 26834, 26872, 26910, 26948, 26986, 27024, 27064, 27104, 27146};
+
+__END_DECLS
diff --git a/igraph/include/bigint.h b/igraph/include/bigint.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/bigint.h
@@ -0,0 +1,107 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_BIGINT_H
+#define IGRAPH_BIGINT_H
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "bignum.h"
+
+#include <stdio.h>
+
+/* Arbitrary precision integer */
+
+#define BASE_LIMB
+#include "igraph_pmt.h"
+#include "igraph_vector_type.h"
+#include "igraph_vector_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_LIMB
+
+__BEGIN_DECLS
+
+typedef struct igraph_biguint_t {
+    igraph_vector_limb_t v;
+} igraph_biguint_t;
+
+#define IGRAPH_BIGUINT_DEFAULT_SIZE 5
+
+int igraph_biguint_init(igraph_biguint_t *b);
+void igraph_biguint_destroy(igraph_biguint_t *b);
+int igraph_biguint_copy(igraph_biguint_t *to, igraph_biguint_t *from);
+
+int igraph_biguint_extend(igraph_biguint_t *b, limb_t l);
+
+int igraph_biguint_size(igraph_biguint_t *b);
+int igraph_biguint_resize(igraph_biguint_t *b, int newlength);
+int igraph_biguint_reserve(igraph_biguint_t *b, int length);
+
+int igraph_biguint_zero(igraph_biguint_t *b);
+int igraph_biguint_set_limb(igraph_biguint_t *b, int value);
+
+igraph_real_t igraph_biguint_get(igraph_biguint_t *b);
+
+int igraph_biguint_compare_limb(igraph_biguint_t *b, limb_t l);
+int igraph_biguint_compare(igraph_biguint_t *left, igraph_biguint_t *right);
+igraph_bool_t igraph_biguint_equal(igraph_biguint_t *left, igraph_biguint_t *right);
+igraph_bool_t igraph_biguint_bigger(igraph_biguint_t *left,
+                                    igraph_biguint_t *right);
+igraph_bool_t igraph_biguint_biggerorequal(igraph_biguint_t *left,
+        igraph_biguint_t *right);
+
+int igraph_biguint_inc(igraph_biguint_t *res, igraph_biguint_t *b);
+int igraph_biguint_dec(igraph_biguint_t *res, igraph_biguint_t *b);
+
+int igraph_biguint_add_limb(igraph_biguint_t *res, igraph_biguint_t *b,
+                            limb_t l);
+int igraph_biguint_sub_limb(igraph_biguint_t *res, igraph_biguint_t *b,
+                            limb_t l);
+int igraph_biguint_mul_limb(igraph_biguint_t *res, igraph_biguint_t *b,
+                            limb_t l);
+
+int igraph_biguint_add(igraph_biguint_t *res, igraph_biguint_t *left,
+                       igraph_biguint_t *right);
+int igraph_biguint_sub(igraph_biguint_t *res, igraph_biguint_t *left,
+                       igraph_biguint_t *right);
+int igraph_biguint_mul(igraph_biguint_t *res, igraph_biguint_t *left,
+                       igraph_biguint_t *right);
+int igraph_biguint_div(igraph_biguint_t *q, igraph_biguint_t *r,
+                       igraph_biguint_t *u, igraph_biguint_t *v);
+
+int igraph_biguint_print(igraph_biguint_t *b);
+int igraph_biguint_fprint(igraph_biguint_t *b, FILE *file);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/bignum.h b/igraph/include/bignum.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/bignum.h
@@ -0,0 +1,125 @@
+/*****************************************************************************
+ *  Entropy - Emerging Network To Reduce Orwellian Potency Yield
+ *
+ *  Copyright (C) 2005 Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ *
+ *  $Id: bignum.h,v 1.6 2005/08/11 17:57:39 pullmoll Exp $
+ *****************************************************************************/
+#ifndef _bignum_h_
+#define _bignum_h_
+
+#include "config.h"
+#ifdef HAVE_STDINT_H
+    #include <stdint.h>
+#else
+    #ifdef HAVE_SYS_INT_TYPES_H
+        #include <sys/int_types.h>
+    #else
+        #include "pstdint.h"
+    #endif
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+#ifndef O_BINARY
+    #define O_BINARY 0
+#endif
+
+#ifndef HAVE_U64
+    #define HAVE_U64 1
+#endif
+
+/* up to 512 limbs (512 * 32 = 16384 bits) numbers */
+/* BN_MAXSIZE used to be 512 here, allowing us to go up to 512*32 = 16384 bits.
+ * However, this has caused compilation problems with clang 7.3 (unless
+ * compiling with -O2 -g). Since it is unlikely that we'll need that many bits,
+ * I have changed this to 128, which still yields 4096 bits of precision but
+ * does not cause problems with clang -- TN, 2016-04-18 */
+#define BN_MAXSIZE 128
+#define LIMBBITS 32
+#define LIMBMASK 0xfffffffful
+#define HALFMASK 0x0000fffful
+#define DIGMSB 0x80000000ul
+#define DIGLSB 0x00000001ul
+
+typedef uint32_t count_t;
+typedef uint16_t half_t;
+typedef uint32_t limb_t;
+#if HAVE_U64
+    typedef uint64_t dlimb_t;
+#endif
+
+/* less significant half limb */
+#define LSH(d)  ((half_t)(d))
+/* more significant half limb */
+#define MSH(d)  ((limb_t)(d)>>16)
+/* shift left half limb */
+#define SHL(d)  ((limb_t)(d)<<16)
+
+/* single limb functions */
+limb_t sl_div(limb_t *q, limb_t *r, limb_t u[2], limb_t v);
+limb_t sl_gcd(limb_t x, limb_t y);
+int sl_modexp(limb_t *exp, limb_t x, limb_t n, limb_t d);
+int sl_modinv(limb_t *inv, limb_t u, limb_t v);
+int sl_modmul(limb_t *a, limb_t x, limb_t y, limb_t m);
+int sl_mul(limb_t p[2], limb_t x, limb_t y);
+
+/* big number functions (max. MAXSIZE limbs) */
+void bn_zero(limb_t a[], count_t nlimb);
+void bn_limb(limb_t a[], limb_t d, count_t nlimb);
+void bn_copy(limb_t a[], limb_t b[], count_t nlimb);
+count_t bn_sizeof(limb_t a[], count_t nlimb);
+int bn_cmp_limb(limb_t a[], limb_t b, count_t nlimb);
+int bn_cmp(limb_t a[], limb_t b[], count_t nlimb);
+
+/* big number to hex, decimal, binary */
+const char *bn2x(limb_t a[], count_t nlimb);
+const char *bn2d(limb_t a[], count_t nlimb);
+const char *bn2f(limb_t a[], count_t alimb, limb_t b[], count_t blimb);
+const char *bn2b(limb_t a[], count_t nlimb);
+
+/* big number with single limb operations */
+limb_t bn_add_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb);
+limb_t bn_sub_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb);
+limb_t bn_div_limb(limb_t q[], limb_t u[], limb_t v, count_t nlimb);
+limb_t bn_mod_limb(limb_t u[], limb_t d, count_t nlimb);
+limb_t bn_mul_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb);
+
+/* big number with single limb <= HALFMASK operations */
+limb_t bn_div_half(limb_t q[], limb_t u[], limb_t v, count_t nlimb);
+limb_t bn_mod_half(limb_t a[], limb_t d, count_t nlimb);
+
+/* big number operations */
+limb_t bn_add(limb_t w[], limb_t u[], limb_t v[], count_t nlimb);
+limb_t bn_sub(limb_t w[], limb_t u[], limb_t v[], count_t nlimb);
+limb_t bn_shl(limb_t a[], limb_t b[], count_t x, count_t nlimb);
+limb_t bn_shr(limb_t a[], limb_t b[], count_t x, count_t nlimb);
+int bn_mul(limb_t w[], limb_t u[], limb_t v[], count_t nlimb);
+int bn_div(limb_t q[], limb_t r[], limb_t u[], limb_t v[], count_t ulimb, count_t vlimb);
+limb_t bn_mod(limb_t r[], limb_t u[], count_t ulimb, limb_t v[], count_t vlimb);
+int bn_gcd(limb_t g[], limb_t x[], limb_t y[], count_t nlimb);
+int bn_sqrt(limb_t g[], limb_t x[], limb_t y[], count_t rlimb, count_t nlimb);
+int bn_modexp(limb_t y[], limb_t x[], limb_t e[], limb_t m[], count_t nlimb);
+int bn_modinv(limb_t inv[], limb_t u[], limb_t v[], count_t nlimb);
+limb_t bn_modmul(limb_t a[], limb_t x[], limb_t y[], limb_t m[], count_t nlimb);
+
+#endif  /* !defined(_bignum_h_) */
diff --git a/igraph/include/bliss/bignum.hh b/igraph/include/bliss/bignum.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/bignum.hh
@@ -0,0 +1,133 @@
+#ifndef BLISS_BIGNUM_HH
+#define BLISS_BIGNUM_HH
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cstdlib>
+#include <cstdio>
+#include <cmath>
+#include <cstring>
+#include <sstream>
+#include "defs.hh"
+
+#include "igraph_memory.h"
+#include "igraph_error.h"
+
+#if defined(BLISS_USE_GMP)
+#include <gmp.h>
+#endif
+
+namespace bliss {
+
+/**
+ * \brief A very simple class for big integers (or approximation of them).
+ *
+ * If the compile time flag BLISS_USE_GMP is set,
+ * then the GNU Multiple Precision Arithmetic library (GMP) is used to
+ * obtain arbitrary precision, otherwise "long double" is used to
+ * approximate big integers.
+ */
+
+#if defined(BLISS_USE_GMP)
+
+class BigNum
+{
+  mpz_t v;
+public:
+  /**
+   * Create a new big number and set it to zero.
+   */
+  BigNum() {mpz_init(v); }
+
+  /**
+   * Destroy the number.
+   */
+  ~BigNum() {mpz_clear(v); }
+
+  /**
+   * Set the number to \a n.
+   */
+  void assign(const int n) {mpz_set_si(v, n); }
+
+  /**
+   * Multiply the number with \a n.
+   */
+  void multiply(const int n) {mpz_mul_si(v, v, n); }
+
+  /**
+   * Print the number in the file stream \a fp.
+   */
+  size_t print(FILE* const fp) const {return mpz_out_str(fp, 10, v); }
+
+  int tostring(char **str) const {
+    *str=igraph_Calloc(mpz_sizeinbase(v, 10)+2, char);
+    if (! *str) {
+      IGRAPH_ERROR("Cannot convert big number to string", IGRAPH_ENOMEM);
+    }
+    mpz_get_str(*str, 10, v);
+    return 0;
+  }
+
+};
+
+#else
+
+class BigNum
+{
+  long double v;
+public:
+  /**
+   * Create a new big number and set it to zero.
+   */
+  BigNum(): v(0.0) {}
+
+  /**
+   * Set the number to \a n.
+   */
+  void assign(const int n) {v = (long double)n; }
+
+  /**
+   * Multiply the number with \a n.
+   */
+  void multiply(const int n) {v *= (long double)n; }
+
+  /**
+   * Print the number in the file stream \a fp.
+   */
+  size_t print(FILE* const fp) const {return fprintf(fp, "%Lg", v); }
+
+  int tostring(char **str) const {
+    int size=static_cast<int>( (std::log(std::abs(v))/std::log(10.0))+4 );
+    *str=igraph_Calloc(size, char );
+    if (! *str) {
+      IGRAPH_ERROR("Cannot convert big number to string", IGRAPH_ENOMEM);
+    }
+    std::stringstream ss;
+    ss << v;
+    strncpy(*str, ss.str().c_str(), size);
+    return 0;
+  }
+};
+
+#endif
+
+} //namespace bliss
+
+#endif
diff --git a/igraph/include/bliss/defs.hh b/igraph/include/bliss/defs.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/defs.hh
@@ -0,0 +1,128 @@
+#ifndef BLISS_DEFS_HH
+#define BLISS_DEFS_HH
+
+#include <cassert>
+#include <cstdarg>
+
+#include "config.h"
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if HAVE_GMP == 1
+#  define BLISS_USE_GMP
+#endif
+
+#ifdef USING_R
+#include <R.h>
+#define fatal_error(...) (error(__VA_ARGS__))
+#endif
+
+namespace bliss {
+
+/**
+ * The version number of bliss.
+ */
+static const char * const version = "0.73";
+
+/*
+ * If a fatal error (out of memory, internal error) is encountered,
+ * this function is called.
+ * There should not be a return from this function but exit or
+ * a jump to code that deallocates the AbstractGraph instance that called this.
+ */
+#ifndef USING_R
+void fatal_error(const char* fmt, ...);
+#endif
+
+
+#if defined(BLISS_DEBUG)
+#define BLISS_CONSISTENCY_CHECKS
+#define BLISS_EXPENSIVE_CONSISTENCY_CHECKS
+#endif
+
+
+#if defined(BLISS_CONSISTENCY_CHECKS)
+/* Force a check that the found automorphisms are valid */
+#define BLISS_VERIFY_AUTOMORPHISMS
+#endif
+
+
+#if defined(BLISS_CONSISTENCY_CHECKS)
+/* Force a check that the generated partitions are equitable */
+#define BLISS_VERIFY_EQUITABLEDNESS
+#endif
+
+} // namespace bliss
+
+
+
+/*! \mainpage Bliss
+ *
+ * \section intro_sec Introduction
+ *
+ * This is the source code documentation of bliss,
+ * produced by running <A href="http://www.doxygen.org">doxygen</A> in
+ * the source directory.
+ * The algorithms and data structures used in bliss are documented in
+ * the papers found at the
+ * <A href="http://www.tcs.hut.fi/Software/bliss">bliss web site</A>.
+ *
+ *
+ * \section compile_sec Compiling
+ *
+ * Compiling bliss in Linux should be easy, just execute
+ * \code
+ * make
+ * \endcode
+ * in the bliss source directory.
+ * This will produce the executable program \c bliss as well as
+ * the library file \c libbliss.a that can be linked in other programs.
+ * If you have the <A href="http://gmplib.org/">GNU Multiple Precision
+ * Arithmetic Library</A> (GMP) installed in your machine, you can also use
+ * \code
+ * make gmp
+ * \endcode
+ * to enable exact computation of automorphism group sizes.
+ *
+ * When linking the bliss library \c libbliss.a in other programs,
+ * remember to include the standard c++ library
+ * (and the GMP library if you compiled bliss to include it).
+ * For instance,
+ * \code gcc -o test test.c -lstdc++ -lgmp -lbliss\endcode
+ *
+ * \section cppapi_sec The C++ language API
+ *
+ * The C++ language API is the main API to bliss;
+ * all other APIs are just more or less complete variants of it.
+ * The C++ API consists basically of the public methods in
+ * the classes bliss::AbstractGraph, bliss::Graph, and bliss::Digraph.
+ * For an example of its use,
+ * see the \ref executable "source of the bliss executable".
+ *
+ *
+ * \section capi_sec The C language API
+ *
+ * The C language API is given in the file bliss_C.h.
+ * It is currently more restricted than the C++ API so
+ * consider using the C++ API whenever possible.
+ */
+
+
+#endif
diff --git a/igraph/include/bliss/graph.hh b/igraph/include/bliss/graph.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/graph.hh
@@ -0,0 +1,997 @@
+#ifndef BLISS_GRAPH_HH
+#define BLISS_GRAPH_HH
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \namespace bliss
+ * The namespace bliss contains all the classes and functions of the bliss
+ * tool except for the C programming language API.
+ */
+namespace bliss {
+  class AbstractGraph;
+}
+
+#include <cstdio>
+#include <vector>
+#include "kstack.hh"
+#include "kqueue.hh"
+#include "heap.hh"
+#include "orbit.hh"
+#include "partition.hh"
+#include "bignum.hh"
+#include "uintseqhash.hh"
+
+namespace bliss {
+
+/**
+ * \brief Statistics returned by the bliss search algorithm.
+ */
+class Stats
+{
+  friend class AbstractGraph;
+public:
+  /** \internal The size of the automorphism group. */
+  BigNum group_size;
+private:
+  /** \internal An approximation (due to possible overflows) of
+   * the size of the automorphism group. */
+  long double group_size_approx;
+  /** \internal The number of nodes in the search tree. */
+  long unsigned int nof_nodes;
+  /** \internal The number of leaf nodes in the search tree. */
+  long unsigned int nof_leaf_nodes;
+  /** \internal The number of bad nodes in the search tree. */
+  long unsigned int nof_bad_nodes;
+  /** \internal The number of canonical representative updates. */
+  long unsigned int nof_canupdates;
+  /** \internal The number of generator permutations. */
+  long unsigned int nof_generators;
+  /** \internal The maximal depth of the search tree. */
+  unsigned long int max_level;
+  /** */
+  void reset()
+  {
+    group_size.assign(1);
+    group_size_approx = 1.0;
+    nof_nodes = 0;
+    nof_leaf_nodes = 0;
+    nof_bad_nodes = 0;
+    nof_canupdates = 0;
+    nof_generators = 0;
+    max_level = 0;
+  }
+public:
+  Stats() { reset(); }
+  /** Print the statistics. */
+  size_t print(FILE* const fp) const
+  {
+    size_t r = 0;
+    r += fprintf(fp, "Nodes:          %lu\n", nof_nodes);
+    r += fprintf(fp, "Leaf nodes:     %lu\n", nof_leaf_nodes);
+    r += fprintf(fp, "Bad nodes:      %lu\n", nof_bad_nodes);
+    r += fprintf(fp, "Canrep updates: %lu\n", nof_canupdates);
+    r += fprintf(fp, "Generators:     %lu\n", nof_generators);
+    r += fprintf(fp, "Max level:      %lu\n", max_level);
+    r += fprintf(fp, "|Aut|:          ")+group_size.print(fp)+fprintf(fp, "\n");
+    fflush(fp);
+    return r;
+  }
+  /** An approximation (due to possible overflows/rounding errors) of
+   * the size of the automorphism group. */
+  long double get_group_size_approx() const {return group_size_approx;}
+  /** The number of nodes in the search tree. */
+  long unsigned int get_nof_nodes() const {return nof_nodes;}
+  /** The number of leaf nodes in the search tree. */
+  long unsigned int get_nof_leaf_nodes() const {return nof_leaf_nodes;}
+  /** The number of bad nodes in the search tree. */
+  long unsigned int get_nof_bad_nodes() const {return nof_bad_nodes;}
+  /** The number of canonical representative updates. */
+  long unsigned int get_nof_canupdates() const {return nof_canupdates;}
+  /** The number of generator permutations. */
+  long unsigned int get_nof_generators() const {return nof_generators;}
+  /** The maximal depth of the search tree. */
+  unsigned long int get_max_level() const {return max_level;}
+};
+
+
+
+
+
+
+/**
+ * \brief An abstract base class for different types of graphs.
+ */
+class AbstractGraph
+{
+  friend class Partition;
+
+public:
+  AbstractGraph();
+  virtual ~AbstractGraph();
+
+  /**
+   * Set the verbose output level for the algorithms.
+   * \param level  the level of verbose output, 0 means no verbose output
+   */
+  void set_verbose_level(const unsigned int level);
+
+  /**
+   * Set the file stream for the verbose output.
+   * \param fp  the file stream; if null, no verbose output is written
+   */
+  void set_verbose_file(FILE * const fp);
+
+  /**
+   * Add a new vertex with color \a color in the graph and return its index.
+   */
+  virtual unsigned int add_vertex(const unsigned int color = 0) = 0;
+
+  /**
+   * Add an edge between vertices \a source and \a target.
+   * Duplicate edges between vertices are ignored but try to avoid introducing
+   * them in the first place as they are not ignored immediately but will
+   * consume memory and computation resources for a while.
+   */
+  virtual void add_edge(const unsigned int source, const unsigned int target) = 0;
+
+  /**
+   * Change the color of the vertex \a vertex to \a color.
+   */
+  virtual void change_color(const unsigned int vertex, const unsigned int color) = 0;
+
+  /**
+   * Check whether \a perm is an automorphism of this graph.
+   * Unoptimized, mainly for debugging purposes.
+   */
+  virtual bool is_automorphism(const std::vector<unsigned int>& perm) const;
+
+
+  /** Activate/deactivate failure recording.
+   * May not be called during the search, i.e. from an automorphism reporting
+   * hook function.
+   * \param active  if true, activate failure recording, deactivate otherwise
+   */
+  void set_failure_recording(const bool active) {assert(!in_search); opt_use_failure_recording = active;}
+
+  /** Activate/deactivate component recursion.
+   * The choice affects the computed canonical labelings;
+   * therefore, if you want to compare whether two graphs are isomorphic by
+   * computing and comparing (for equality) their canonical versions,
+   * be sure to use the same choice for both graphs.
+   * May not be called during the search, i.e. from an automorphism reporting
+   * hook function.
+   * \param active  if true, activate component recursion, deactivate otherwise
+   */
+  void set_component_recursion(const bool active) {assert(!in_search); opt_use_comprec = active;}
+
+
+
+  /**
+   * Return the number of vertices in the graph.
+   */
+  virtual unsigned int get_nof_vertices() const = 0;
+
+  /**
+   * Return a new graph that is the result of applying the permutation \a perm
+   * to this graph. This graph is not modified.
+   * \a perm must contain N=this.get_nof_vertices() elements and be a bijection
+   * on {0,1,...,N-1}, otherwise the result is undefined or a segfault.
+   */
+  virtual AbstractGraph* permute(const unsigned int* const perm) const = 0;
+  virtual AbstractGraph* permute(const std::vector<unsigned int>& perm) const = 0;
+
+  /**
+   * Find a set of generators for the automorphism group of the graph.
+   * The function \a hook (if non-null) is called each time a new generator
+   * for the automorphism group is found.
+   * The first argument \a user_param for the hook is the
+   * \a hook_user_param given below,
+   * the second argument \a n is the length of the automorphism (equal to
+   * get_nof_vertices()) and
+   * the third argument \a aut is the automorphism
+   * (a bijection on {0,...,get_nof_vertices()-1}).
+   * The memory for the automorphism \a aut will be invalidated immediately
+   * after the return from the hook function;
+   * if you want to use the automorphism later, you have to take a copy of it.
+   * Do not call any member functions in the hook.
+   * The search statistics are copied in \a stats.
+   */
+  void find_automorphisms(Stats& stats,
+			  void (*hook)(void* user_param,
+				       unsigned int n,
+				       const unsigned int* aut),
+			  void* hook_user_param);
+
+  /**
+   * Otherwise the same as find_automorphisms() except that
+   * a canonical labeling of the graph (a bijection on
+   * {0,...,get_nof_vertices()-1}) is returned.
+   * The memory allocated for the returned canonical labeling will remain
+   * valid only until the next call to a member function with the exception
+   * that constant member functions (for example, bliss::Graph::permute()) can
+   * be called without invalidating the labeling.
+   * To compute the canonical version of an undirected graph, call this
+   * function and then bliss::Graph::permute() with the returned canonical
+   * labeling.
+   * Note that the computed canonical version may depend on the applied version
+   * of bliss as well as on some other options (for instance, the splitting
+   * heuristic selected with bliss::Graph::set_splitting_heuristic()).
+   */
+  const unsigned int* canonical_form(Stats& stats,
+				     void (*hook)(void* user_param,
+						  unsigned int n,
+						  const unsigned int* aut),
+				     void* hook_user_param);
+
+  /**
+   * Write the graph to a file in a variant of the DIMACS format.
+   * See the <A href="http://www.tcs.hut.fi/Software/bliss/">bliss website</A>
+   * for the definition of the file format.
+   * Note that in the DIMACS file the vertices are numbered from 1 to N while
+   * in this C++ API they are from 0 to N-1.
+   * Thus the vertex n in the file corresponds to the vertex n-1 in the API.
+   * \param fp  the file stream where the graph is written
+   */
+  virtual void write_dimacs(FILE * const fp) = 0;
+
+  /**
+   * Write the graph to a file in the graphviz dotty format.
+   * \param fp  the file stream where the graph is written
+   */
+  virtual void write_dot(FILE * const fp) = 0;
+
+  /**
+   * Write the graph in a file in the graphviz dotty format.
+   * Do nothing if the file cannot be written.
+   * \param file_name  the name of the file to which the graph is written
+   */
+  virtual void write_dot(const char * const file_name) = 0;
+
+  /**
+   * Get a hash value for the graph.
+   * \return  the hash value
+   */ 
+  virtual unsigned int get_hash() = 0;
+
+  /**
+   * Disable/enable the "long prune" method.
+   * The choice affects the computed canonical labelings;
+   * therefore, if you want to compare whether two graphs are isomorphic by
+   * computing and comparing (for equality) their canonical versions,
+   * be sure to use the same choice for both graphs.
+   * May not be called during the search, i.e. from an automorphism reporting
+   * hook function.
+   * \param active  if true, activate "long prune", deactivate otherwise
+   */
+  void set_long_prune_activity(const bool active) {
+    assert(!in_search);
+    opt_use_long_prune = active;
+  }
+
+
+
+protected:
+  /** \internal
+   * How much verbose output is produced (0 means none) */
+  unsigned int verbose_level;
+  /** \internal
+   * The output stream for verbose output. */
+  FILE *verbstr;
+protected:
+
+  /** \internal
+   * The ordered partition used in the search algorithm. */
+  Partition p;
+
+  /** \internal
+   * Whether the search for automorphisms and a canonical labeling is
+   * in progress.
+   */
+  bool in_search;
+
+  /** \internal
+   * Is failure recording in use?
+   */
+  bool opt_use_failure_recording;
+  /* The "tree-specific" invariant value for the point when current path
+   * got different from the first path */
+  unsigned int failure_recording_fp_deviation;
+
+  /** \internal
+   * Is component recursion in use?
+   */
+  bool opt_use_comprec;
+
+
+  unsigned int refine_current_path_certificate_index;
+  bool refine_compare_certificate;
+  bool refine_equal_to_first;
+  unsigned int refine_first_path_subcertificate_end;
+  int refine_cmp_to_best;
+  unsigned int refine_best_path_subcertificate_end;
+
+  static const unsigned int CERT_SPLIT = 0; //UINT_MAX;
+  static const unsigned int CERT_EDGE  = 1; //UINT_MAX-1;
+  /** \internal
+   * Add a triple (v1,v2,v3) in the certificate.
+   * May modify refine_equal_to_first and refine_cmp_to_best.
+   * May also update eqref_hash and failure_recording_fp_deviation. */
+  void cert_add(const unsigned int v1,
+		const unsigned int v2,
+		const unsigned int v3);
+
+  /** \internal
+   * Add a redundant triple (v1,v2,v3) in the certificate.
+   * Can also just dicard the triple.
+   * May modify refine_equal_to_first and refine_cmp_to_best.
+   * May also update eqref_hash and failure_recording_fp_deviation. */
+  void cert_add_redundant(const unsigned int x,
+			  const unsigned int y,
+			  const unsigned int z);
+
+  /**\internal
+   * Is the long prune method in use?
+   */
+  bool opt_use_long_prune;
+  /**\internal
+   * Maximum amount of memory (in megabytes) available for
+   * the long prune method
+   */
+  static const unsigned int long_prune_options_max_mem = 50;
+  /**\internal
+   * Maximum amount of automorphisms stored for the long prune method;
+   * less than this is stored if the memory limit above is reached first
+   */
+  static const unsigned int long_prune_options_max_stored_auts = 100;
+
+  unsigned int long_prune_max_stored_autss;
+  std::vector<std::vector<bool> *> long_prune_fixed;
+  std::vector<std::vector<bool> *> long_prune_mcrs;
+  std::vector<bool> long_prune_temp;
+  unsigned int long_prune_begin;
+  unsigned int long_prune_end;
+  /** \internal
+   * Initialize the "long prune" data structures.
+   */
+  void long_prune_init();
+  /** \internal
+   * Release the memory allocated for "long prune" data structures.
+   */
+  void long_prune_deallocate();
+  void long_prune_add_automorphism(const unsigned int *aut);
+  std::vector<bool>& long_prune_get_fixed(const unsigned int index);
+  std::vector<bool>& long_prune_allocget_fixed(const unsigned int index);
+  std::vector<bool>& long_prune_get_mcrs(const unsigned int index);
+  std::vector<bool>& long_prune_allocget_mcrs(const unsigned int index);
+  /** \internal
+   * Swap the i:th and j:th stored automorphism information;
+   * i and j must be "in window, i.e. in [long_prune_begin,long_prune_end[
+   */
+  void long_prune_swap(const unsigned int i, const unsigned int j);
+
+  /*
+   * Data structures and routines for refining the partition p into equitable
+   */
+  Heap neighbour_heap;
+  virtual bool split_neighbourhood_of_unit_cell(Partition::Cell *) = 0;
+  virtual bool split_neighbourhood_of_cell(Partition::Cell * const) = 0;
+  void refine_to_equitable();
+  void refine_to_equitable(Partition::Cell * const unit_cell);
+  void refine_to_equitable(Partition::Cell * const unit_cell1,
+			   Partition::Cell * const unit_cell2);
+
+
+  /** \internal
+   * \return false if it was detected that the current certificate
+   *         is different from the first and/or best (whether this is checked
+   *         depends on in_search and refine_compare_certificate flags.
+   */
+  bool do_refine_to_equitable();
+
+  unsigned int eqref_max_certificate_index;
+  /** \internal
+   * Whether eqref_hash is updated during equitable refinement process.
+   */
+  bool compute_eqref_hash;
+  UintSeqHash eqref_hash;
+
+
+  /** \internal
+   * Check whether the current partition p is equitable.
+   * Performance: very slow, use only for debugging purposes.
+   */
+  virtual bool is_equitable() const = 0;
+
+  unsigned int *first_path_labeling;
+  unsigned int *first_path_labeling_inv;
+  Orbit         first_path_orbits;
+  unsigned int *first_path_automorphism;
+
+  unsigned int *best_path_labeling;
+  unsigned int *best_path_labeling_inv;
+  Orbit         best_path_orbits;
+  unsigned int *best_path_automorphism;
+
+  void update_labeling(unsigned int * const lab);
+  void update_labeling_and_its_inverse(unsigned int * const lab,
+				       unsigned int * const lab_inv);
+  void update_orbit_information(Orbit &o, const unsigned int *perm);
+
+  void reset_permutation(unsigned int *perm);
+
+  /* Mainly for debugging purposes */
+  virtual bool is_automorphism(unsigned int* const perm);
+
+  std::vector<unsigned int> certificate_current_path;
+  std::vector<unsigned int> certificate_first_path;
+  std::vector<unsigned int> certificate_best_path;
+
+  unsigned int certificate_index;
+  virtual void initialize_certificate() = 0;
+
+  virtual void remove_duplicate_edges() = 0;
+  virtual void make_initial_equitable_partition() = 0;
+  virtual Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell) = 0;
+
+
+  void search(const bool canonical, Stats &stats);
+
+
+  void (*report_hook)(void *user_param,
+		      unsigned int n,
+		      const unsigned int *aut);
+  void *report_user_param;
+
+
+  /*
+   *
+   * Nonuniform component recursion (NUCR)
+   *
+   */
+
+  /** The currently traversed component */
+  unsigned int cr_level;
+
+  /** \internal
+   * The "Component End Point" data structure
+   */
+  class CR_CEP {
+  public:
+    /** At which level in the search was this CEP created */
+    unsigned int creation_level;
+    /** The current component has been fully traversed when the partition has
+     * this many discrete cells left */
+    unsigned int discrete_cell_limit;
+    /** The component to be traversed after the current one */
+    unsigned int next_cr_level;
+    /** The next component end point */
+    unsigned int next_cep_index;
+    bool first_checked;
+    bool best_checked;
+  };
+  /** \internal
+   * A stack for storing Component End Points
+   */
+  std::vector<CR_CEP> cr_cep_stack;
+
+  /** \internal
+   * Find the first non-uniformity component at the component recursion
+   * level \a level.
+   * The component is stored in \a cr_component.
+   * If no component is found, \a cr_component is empty.
+   * Returns false if all the cells in the component recursion level \a level
+   * were discrete.
+   * Modifies the max_ival and max_ival_count fields of Partition:Cell
+   * (assumes that they are 0 when called and
+   *  quarantees that they are 0 when returned).
+   */
+  virtual bool nucr_find_first_component(const unsigned int level) = 0;
+  virtual bool nucr_find_first_component(const unsigned int level,
+					 std::vector<unsigned int>& component,
+					 unsigned int& component_elements,
+					 Partition::Cell*& sh_return) = 0;
+  /** \internal
+   * The non-uniformity component found by nucr_find_first_component()
+   * is stored here.
+   */
+  std::vector<unsigned int> cr_component;
+  /** \internal
+   * The number of vertices in the component \a cr_component
+   */
+  unsigned int cr_component_elements;
+
+
+
+
+};
+
+
+
+/**
+ * \brief The class for undirected, vertex colored graphs.
+ *
+ * Multiple edges between vertices are not allowed (i.e., are ignored).
+ */
+class Graph : public AbstractGraph
+{
+public:
+  /**
+   * The possible splitting heuristics.
+   * The selected splitting heuristics affects the computed canonical
+   * labelings; therefore, if you want to compare whether two graphs
+   * are isomorphic by computing and comparing (for equality) their
+   * canonical versions, be sure to use the same splitting heuristics
+   * for both graphs.
+   */
+  typedef enum {
+    /** First non-unit cell.
+     * Very fast but may result in large search spaces on difficult graphs.
+     * Use for large but easy graphs. */
+    shs_f = 0,
+    /** First smallest non-unit cell.
+     * Fast, should usually produce smaller search spaces than shs_f. */
+    shs_fs,
+    /** First largest non-unit cell.
+     * Fast, should usually produce smaller search spaces than shs_f. */
+    shs_fl,
+    /** First maximally non-trivially connected non-unit cell.
+     * Not so fast, should usually produce smaller search spaces than shs_f,
+     * shs_fs, and shs_fl. */
+    shs_fm,
+    /** First smallest maximally non-trivially connected non-unit cell.
+     * Not so fast, should usually produce smaller search spaces than shs_f,
+     * shs_fs, and shs_fl. */
+    shs_fsm,
+    /** First largest maximally non-trivially connected non-unit cell.
+     * Not so fast, should usually produce smaller search spaces than shs_f,
+     * shs_fs, and shs_fl. */
+    shs_flm
+  } SplittingHeuristic;
+
+protected:
+  class Vertex {
+  public:
+    Vertex();
+    ~Vertex();
+    void add_edge(const unsigned int other_vertex);
+    void remove_duplicate_edges(std::vector<bool>& tmp);
+    void sort_edges();
+
+    unsigned int color;
+    std::vector<unsigned int> edges;
+    unsigned int nof_edges() const {return edges.size(); }
+  };
+  std::vector<Vertex> vertices;
+  void sort_edges();
+  void remove_duplicate_edges();
+
+  /** \internal
+   * Partition independent invariant.
+   * Returns the color of the vertex.
+   * Time complexity: O(1).
+   */
+  static unsigned int vertex_color_invariant(const Graph* const g,
+					     const unsigned int v);
+  /** \internal
+   * Partition independent invariant.
+   * Returns the degree of the vertex.
+   * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE.
+   * Time complexity: O(1).
+   */
+  static unsigned int degree_invariant(const Graph* const g,
+				       const unsigned int v);
+  /** \internal
+   * Partition independent invariant.
+   * Returns 1 if there is an edge from the vertex to itself, 0 if not.
+   * Time complexity: O(k), where k is the number of edges leaving the vertex.
+   */
+  static unsigned int selfloop_invariant(const Graph* const g,
+					 const unsigned int v);
+
+
+  bool refine_according_to_invariant(unsigned int (*inv)(const Graph* const g,
+							 const unsigned int v));
+
+  /*
+   * Routines needed when refining the partition p into equitable
+   */
+  bool split_neighbourhood_of_unit_cell(Partition::Cell *);
+  bool split_neighbourhood_of_cell(Partition::Cell * const);
+
+  /** \internal
+   * \copydoc AbstractGraph::is_equitable() const
+   */
+  bool is_equitable() const;
+
+  /* Splitting heuristics, documented in more detail in graph.cc */
+  SplittingHeuristic sh;
+  Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell);
+  Partition::Cell* sh_first();
+  Partition::Cell* sh_first_smallest();
+  Partition::Cell* sh_first_largest();
+  Partition::Cell* sh_first_max_neighbours();
+  Partition::Cell* sh_first_smallest_max_neighbours();
+  Partition::Cell* sh_first_largest_max_neighbours();
+
+
+  void make_initial_equitable_partition();
+
+  void initialize_certificate();
+  
+  bool is_automorphism(unsigned int* const perm);
+
+
+  bool nucr_find_first_component(const unsigned int level);
+  bool nucr_find_first_component(const unsigned int level,
+				 std::vector<unsigned int>& component,
+				 unsigned int& component_elements,
+				 Partition::Cell*& sh_return);
+
+
+
+public:
+  /**
+   * Create a new graph with \a N vertices and no edges.
+   */
+  Graph(const unsigned int N = 0);
+
+  /**
+   * Destroy the graph.
+   */
+  ~Graph();
+
+  /**
+   * Read the graph from the file \a fp in a variant of the DIMACS format.
+   * See the <A href="http://www.tcs.hut.fi/Software/bliss/">bliss website</A>
+   * for the definition of the file format.
+   * Note that in the DIMACS file the vertices are numbered from 1 to N while
+   * in this C++ API they are from 0 to N-1.
+   * Thus the vertex n in the file corresponds to the vertex n-1 in the API.
+   *
+   * \param fp      the file stream for the graph file
+   * \param errstr  if non-null, the possible error messages are printed
+   *                in this file stream
+   * \return        a new Graph object or 0 if reading failed for some
+   *                reason
+   */
+  static Graph* read_dimacs(FILE* const fp, FILE* const errstr = stderr);
+
+  /**
+   * Write the graph to a file in a variant of the DIMACS format.
+   * See the <A href="http://www.tcs.hut.fi/Software/bliss/">bliss website</A>
+   * for the definition of the file format.
+   */
+  void write_dimacs(FILE* const fp);
+
+  /**
+   * \copydoc AbstractGraph::write_dot(FILE * const fp)
+   */
+  void write_dot(FILE* const fp);
+
+  /**
+   * \copydoc AbstractGraph::write_dot(const char * const file_name)
+   */
+  void write_dot(const char* const file_name);
+
+  /**
+   * \copydoc AbstractGraph::is_automorphism(const std::vector<unsigned int>& perm) const
+   */
+  bool is_automorphism(const std::vector<unsigned int>& perm) const;
+
+
+  /**
+   * \copydoc AbstractGraph::get_hash()
+   */ 
+  virtual unsigned int get_hash();
+
+  /**
+   * Return the number of vertices in the graph.
+   */
+  unsigned int get_nof_vertices() const {return vertices.size(); }
+
+  /**
+   * \copydoc AbstractGraph::permute(const unsigned int* const perm) const
+   */
+  Graph* permute(const unsigned int* const perm) const;
+  Graph* permute(const std::vector<unsigned int>& perm) const;
+  
+  /**
+   * Add a new vertex with color \a color in the graph and return its index.
+   */
+  unsigned int add_vertex(const unsigned int color = 0);
+
+  /**
+   * Add an edge between vertices \a v1 and \a v2.
+   * Duplicate edges between vertices are ignored but try to avoid introducing
+   * them in the first place as they are not ignored immediately but will
+   * consume memory and computation resources for a while.
+   */
+  void add_edge(const unsigned int v1, const unsigned int v2);
+
+  /**
+   * Change the color of the vertex \a vertex to \a color.
+   */
+  void change_color(const unsigned int vertex, const unsigned int color);
+
+  /**
+   * Compare this graph with the graph \a other.
+   * Returns 0 if the graphs are equal, and a negative (positive) integer
+   * if this graph is "smaller than" ("greater than", resp.) than \a other.
+   */
+  int cmp(Graph& other);
+
+  /**
+   * Set the splitting heuristic used by the automorphism and canonical
+   * labeling algorithm.
+   * The selected splitting heuristics affects the computed canonical
+   * labelings; therefore, if you want to compare whether two graphs
+   * are isomorphic by computing and comparing (for equality) their
+   * canonical versions, be sure to use the same splitting heuristics
+   * for both graphs.
+   */
+  void set_splitting_heuristic(const SplittingHeuristic shs) {sh = shs; }
+  
+
+};
+
+
+
+/**
+ * \brief The class for directed, vertex colored graphs.
+ *
+ * Multiple edges between vertices are not allowed (i.e., are ignored).
+ */
+class Digraph : public AbstractGraph
+{
+public:
+  /**
+   * The possible splitting heuristics.
+   * The selected splitting heuristics affects the computed canonical
+   * labelings; therefore, if you want to compare whether two graphs
+   * are isomorphic by computing and comparing (for equality) their
+   * canonical versions, be sure to use the same splitting heuristics
+   * for both graphs.
+   */
+  typedef enum {
+    /** First non-unit cell.
+     * Very fast but may result in large search spaces on difficult graphs.
+     * Use for large but easy graphs. */
+    shs_f = 0,
+    /** First smallest non-unit cell.
+     * Fast, should usually produce smaller search spaces than shs_f. */
+    shs_fs,
+    /** First largest non-unit cell.
+     * Fast, should usually produce smaller search spaces than shs_f. */
+    shs_fl,
+    /** First maximally non-trivially connected non-unit cell.
+     * Not so fast, should usually produce smaller search spaces than shs_f,
+     * shs_fs, and shs_fl. */
+    shs_fm,
+    /** First smallest maximally non-trivially connected non-unit cell.
+     * Not so fast, should usually produce smaller search spaces than shs_f,
+     * shs_fs, and shs_fl. */
+    shs_fsm,
+    /** First largest maximally non-trivially connected non-unit cell.
+     * Not so fast, should usually produce smaller search spaces than shs_f,
+     * shs_fs, and shs_fl. */
+    shs_flm
+  } SplittingHeuristic;
+
+protected:
+  class Vertex {
+  public:
+    Vertex();
+    ~Vertex();
+    void add_edge_to(const unsigned int dest_vertex);
+    void add_edge_from(const unsigned int source_vertex);
+    void remove_duplicate_edges(std::vector<bool>& tmp);
+    void sort_edges();
+    unsigned int color;
+    std::vector<unsigned int> edges_out;
+    std::vector<unsigned int> edges_in;
+    unsigned int nof_edges_in() const {return edges_in.size(); }
+    unsigned int nof_edges_out() const {return edges_out.size(); }
+  };
+  std::vector<Vertex> vertices;
+  void remove_duplicate_edges();
+
+  /** \internal
+   * Partition independent invariant.
+   * Returns the color of the vertex.
+   * Time complexity: O(1).
+   */
+  static unsigned int vertex_color_invariant(const Digraph* const g,
+					     const unsigned int v);
+  /** \internal
+   * Partition independent invariant.
+   * Returns the indegree of the vertex.
+   * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE.
+   * Time complexity: O(1).
+   */
+  static unsigned int indegree_invariant(const Digraph* const g,
+					 const unsigned int v);
+  /** \internal
+   * Partition independent invariant.
+   * Returns the outdegree of the vertex.
+   * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE.
+   * Time complexity: O(1).
+   */
+  static unsigned int outdegree_invariant(const Digraph* const g,
+					  const unsigned int v);
+  /** \internal
+   * Partition independent invariant.
+   * Returns 1 if there is an edge from the vertex to itself, 0 if not.
+   * Time complexity: O(k), where k is the number of edges leaving the vertex.
+   */
+  static unsigned int selfloop_invariant(const Digraph* const g,
+					 const unsigned int v);
+
+  /** \internal
+   * Refine the partition \a p according to
+   * the partition independent invariant \a inv.
+   */
+  bool refine_according_to_invariant(unsigned int (*inv)(const Digraph* const g,
+							 const unsigned int v));
+
+  /*
+   * Routines needed when refining the partition p into equitable
+   */
+  bool split_neighbourhood_of_unit_cell(Partition::Cell* const);
+  bool split_neighbourhood_of_cell(Partition::Cell* const);
+
+
+  /** \internal
+   * \copydoc AbstractGraph::is_equitable() const
+   */
+  bool is_equitable() const;
+
+  /* Splitting heuristics, documented in more detail in the cc-file. */
+  SplittingHeuristic sh;
+  Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell);
+  Partition::Cell* sh_first();
+  Partition::Cell* sh_first_smallest();
+  Partition::Cell* sh_first_largest();
+  Partition::Cell* sh_first_max_neighbours();
+  Partition::Cell* sh_first_smallest_max_neighbours();
+  Partition::Cell* sh_first_largest_max_neighbours();
+
+  void make_initial_equitable_partition();
+
+  void initialize_certificate();
+
+  bool is_automorphism(unsigned int* const perm);
+
+  void sort_edges();
+
+  bool nucr_find_first_component(const unsigned int level);
+  bool nucr_find_first_component(const unsigned int level,
+				 std::vector<unsigned int>& component,
+				 unsigned int& component_elements,
+				 Partition::Cell*& sh_return);
+
+public:
+  /**
+   * Create a new directed graph with \a N vertices and no edges.
+   */
+  Digraph(const unsigned int N = 0);
+
+  /**
+   * Destroy the graph.
+   */
+  ~Digraph();
+
+  /**
+   * Read the graph from the file \a fp in a variant of the DIMACS format.
+   * See the <A href="http://www.tcs.hut.fi/Software/bliss/">bliss website</A>
+   * for the definition of the file format.
+   * Note that in the DIMACS file the vertices are numbered from 1 to N while
+   * in this C++ API they are from 0 to N-1.
+   * Thus the vertex n in the file corresponds to the vertex n-1 in the API.
+   * \param fp      the file stream for the graph file
+   * \param errstr  if non-null, the possible error messages are printed
+   *                in this file stream
+   * \return        a new Digraph object or 0 if reading failed for some
+   *                reason
+   */
+  static Digraph* read_dimacs(FILE* const fp, FILE* const errstr = stderr);
+
+  /**
+   * \copydoc AbstractGraph::write_dimacs(FILE * const fp)
+   */
+  void write_dimacs(FILE* const fp);
+
+
+  /**
+   * \copydoc AbstractGraph::write_dot(FILE *fp)
+   */
+  void write_dot(FILE * const fp);
+
+  /**
+   * \copydoc AbstractGraph::write_dot(const char * const file_name)
+   */
+  void write_dot(const char * const file_name);
+
+  /**
+   * \copydoc AbstractGraph::is_automorphism(const std::vector<unsigned int>& perm) const
+   */
+  bool is_automorphism(const std::vector<unsigned int>& perm) const;
+
+
+
+  /**
+   * \copydoc AbstractGraph::get_hash()
+   */ 
+  virtual unsigned int get_hash();
+
+  /**
+   * Return the number of vertices in the graph.
+   */
+  unsigned int get_nof_vertices() const {return vertices.size(); }
+  
+  /**
+   * Add a new vertex with color 'color' in the graph and return its index.
+   */
+  unsigned int add_vertex(const unsigned int color = 0);
+
+  /**
+   * Add an edge from the vertex \a source to the vertex \a target.
+   * Duplicate edges are ignored but try to avoid introducing
+   * them in the first place as they are not ignored immediately but will
+   * consume memory and computation resources for a while.
+   */
+  void add_edge(const unsigned int source, const unsigned int target);
+
+  /**
+   * Change the color of the vertex 'vertex' to 'color'.
+   */
+  void change_color(const unsigned int vertex, const unsigned int color);
+
+  /**
+   * Compare this graph with the graph \a other.
+   * Returns 0 if the graphs are equal, and a negative (positive) integer
+   * if this graph is "smaller than" ("greater than", resp.) than \a other.
+   */
+  int cmp(Digraph& other);
+
+  /**
+   * Set the splitting heuristic used by the automorphism and canonical
+   * labeling algorithm.
+   * The selected splitting heuristics affects the computed canonical
+   * labelings; therefore, if you want to compare whether two graphs
+   * are isomorphic by computing and comparing (for equality) their
+   * canonical versions, be sure to use the same splitting heuristics
+   * for both graphs.
+   */
+  void set_splitting_heuristic(SplittingHeuristic shs) {sh = shs; }
+
+  /**
+   * \copydoc AbstractGraph::permute(const unsigned int* const perm) const
+   */
+  Digraph* permute(const unsigned int* const perm) const;  
+  Digraph* permute(const std::vector<unsigned int>& perm) const;
+};
+
+
+
+
+}
+
+#endif
diff --git a/igraph/include/bliss/heap.hh b/igraph/include/bliss/heap.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/heap.hh
@@ -0,0 +1,83 @@
+#ifndef BLISS_HEAP_HH
+#define BLISS_HEAP_HH
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+/** \internal
+ * \brief A capacity bounded heap data structure.
+ */
+
+class Heap
+{
+  unsigned int N;
+  unsigned int n;
+  unsigned int *array;
+  void upheap(unsigned int k);
+  void downheap(unsigned int k);
+public:
+  /**
+   * Create a new heap.
+   * init() must be called after this.
+   */
+  Heap() {array = 0; n = 0; N = 0; }
+  ~Heap();
+
+  /**
+   * Initialize the heap to have the capacity to hold \e size elements.
+   */
+  void init(const unsigned int size);
+
+  /**
+   * Is the heap empty?
+   * Time complexity is O(1).
+   */
+  bool is_empty() const {return(n==0); }
+
+  /**
+   * Remove all the elements in the heap.
+   * Time complexity is O(1).
+   */
+  void clear() {n = 0;}
+
+  /**
+   * Insert the element \a e in the heap.
+   * Time complexity is O(log(N)), where N is the number of elements
+   * currently in the heap.
+   */
+  void insert(const unsigned int e);
+
+  /**
+   * Remove and return the smallest element in the heap.
+   * Time complexity is O(log(N)), where N is the number of elements
+   * currently in the heap.
+   */
+  unsigned int remove();
+
+  /**
+   * Get the number of elements in the heap.
+   */
+  unsigned int size() const {return n; }
+};
+
+} // namespace bliss
+
+#endif
diff --git a/igraph/include/bliss/kqueue.hh b/igraph/include/bliss/kqueue.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/kqueue.hh
@@ -0,0 +1,162 @@
+#ifndef BLISS_KQUEUE_HH
+#define BLISS_KQUEUE_HH
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "defs.hh"
+
+namespace bliss {
+
+/** \internal
+ * \brief A very simple implementation of queues with fixed capacity.
+ */
+
+template <class Type>
+class KQueue
+{
+public:
+  /**
+   * Create a new queue with capacity zero.
+   * The function init() should be called next.
+   */
+  KQueue();
+
+  ~KQueue();
+
+  /**
+   * Initialize the queue to have the capacity to hold at most \a N elements.
+   */
+  void init(const unsigned int N);
+  
+  /** Is the queue empty? */
+  bool is_empty() const;
+
+  /** Return the number of elements in the queue. */
+  unsigned int size() const;
+
+  /** Remove all the elements in the queue. */
+  void clear();
+
+  /** Return (but don't remove) the first element in the queue. */
+  Type front() const;
+
+  /** Remove and return the first element of the queue. */
+  Type pop_front();
+
+  /** Push the element \a e in the front of the queue. */
+  void push_front(Type e);
+
+  /** Remove and return the last element of the queue. */
+  Type pop_back();
+
+  /** Push the element \a e in the back of the queue. */
+  void push_back(Type e);
+private:
+  Type *entries, *end;
+  Type *head, *tail;
+};
+
+template <class Type>
+KQueue<Type>::KQueue()
+{
+  entries = 0;
+  end = 0;
+  head = 0;
+  tail = 0;
+}
+
+template <class Type>
+KQueue<Type>::~KQueue()
+{
+  if(entries)
+    free(entries);
+}
+
+template <class Type>
+void KQueue<Type>::init(const unsigned int k)
+{
+  assert(k > 0);
+  if(entries)
+    free(entries);
+  entries = (Type*)malloc((k + 1) * sizeof(Type));
+  end = entries + k + 1;
+  head = entries;
+  tail = head;
+}
+
+template <class Type>
+void KQueue<Type>::clear()
+{
+  head = entries;
+  tail = head;
+}
+
+template <class Type>
+bool KQueue<Type>::is_empty() const
+{
+  return(head == tail);
+}
+
+template <class Type>
+unsigned int KQueue<Type>::size() const
+{
+  if(tail >= head)
+    return(tail - head);
+  return((end - head) + (tail - entries));
+}
+
+template <class Type>
+Type KQueue<Type>::front() const
+{
+  return *head;
+}
+
+template <class Type>
+Type KQueue<Type>::pop_front()
+{
+  Type *old_head = head;
+  head++;
+  if(head == end)
+    head = entries;
+  return *old_head;
+}
+
+template <class Type>
+void KQueue<Type>::push_front(Type e)
+{
+  if(head == entries)
+    head = end - 1;
+  else
+    head--;
+  *head = e;
+}
+
+template <class Type>
+void KQueue<Type>::push_back(Type e)
+{
+  *tail = e;
+  tail++;
+  if(tail == end)
+    tail = entries;
+}
+
+} // namespace bliss
+
+#endif
diff --git a/igraph/include/bliss/kstack.hh b/igraph/include/bliss/kstack.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/kstack.hh
@@ -0,0 +1,141 @@
+#ifndef BLISS_KSTACK_H
+#define BLISS_KSTACK_H
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cstdlib>
+#include "defs.hh"
+
+namespace bliss {
+
+/** \internal
+ * \brief A very simple implementation of a stack with fixed capacity.
+ */
+template <class Type>
+class KStack {
+public:
+  /**
+   * Create a new stack with zero capacity.
+   * The function init() should be called next.
+   */
+  KStack();
+
+  /**
+   * Create a new stack with the capacity to hold at most \a N elements.
+   */
+  KStack(int N);
+
+  ~KStack();
+
+  /**
+   * Initialize the stack to have the capacity to hold at most \a N elements.
+   */
+  void init(int N);
+
+  /**
+   * Is the stack empty?
+   */
+  bool is_empty() const {return(cursor == entries); }
+
+  /**
+   * Return (but don't remove) the top element of the stack.
+   */
+  Type top() const {BLISS_ASSERT(cursor > entries); return *cursor; }
+
+  /**
+   * Pop (remove) the top element of the stack.
+   */
+  Type pop()
+  {
+    return *cursor--;
+  }
+
+  /**
+   * Push the element \a e in the stack.
+   */
+  void push(Type e)
+  {
+    *(++cursor) = e;
+  }
+
+  /** Remove all the elements in the stack. */
+  void clean() {cursor = entries; }
+
+  /**
+   * Get the number of elements in the stack.
+   */
+  unsigned int size() const {return(cursor - entries); }
+
+  /**
+   * Return the i:th element in the stack, where \a i is in the range
+   * 0,...,this.size()-1; the 0:th element is the bottom element
+   * in the stack.
+   */
+  Type element_at(unsigned int i)
+  {
+    assert(i < size());
+    return entries[i+1];
+  }
+
+  /** Return the capacity (NOT the number of elements) of the stack. */
+  int capacity() {return kapacity; }
+private:
+  int kapacity;
+  Type *entries;
+  Type *cursor;
+};
+
+template <class Type>
+KStack<Type>::KStack()
+{
+  kapacity = 0;
+  entries = 0;
+  cursor = 0;
+}
+
+template <class Type>
+KStack<Type>::KStack(int k)
+{
+  assert(k > 0);
+  kapacity = k;
+  entries = (Type*)malloc((k+1) * sizeof(Type));
+  cursor = entries;
+}
+
+template <class Type>
+void KStack<Type>::init(int k)
+{
+  assert(k > 0);
+  if(entries)
+    free(entries);
+  kapacity = k;
+  entries = (Type*)malloc((k+1) * sizeof(Type));
+  cursor = entries;
+}
+
+template <class Type>
+KStack<Type>::~KStack()
+{
+  free(entries);
+}
+
+} // namespace bliss
+
+#endif
diff --git a/igraph/include/bliss/orbit.hh b/igraph/include/bliss/orbit.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/orbit.hh
@@ -0,0 +1,111 @@
+#ifndef BLISS_ORBIT_HH
+#define BLISS_ORBIT_HH
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+/** \internal
+ * \brief A class for representing orbit information.
+ *
+ * Given a set {0,...,N-1} of N elements, represent equivalence
+ * classes (that is, unordered partitions) of the elements.
+ * Supports only equivalence class merging, not splitting.
+ * Merging two classes requires time O(k), where k is the number of
+ * the elements in the smaller of the merged classes.
+ * Getting the smallest representative in a class (and thus testing
+ * whether two elements belong to the same class) is a constant time operation.
+ */
+class Orbit
+{
+  class OrbitEntry
+  {
+  public:
+    unsigned int element;
+    OrbitEntry *next;
+    unsigned int size;
+  };
+
+  OrbitEntry *orbits;
+  OrbitEntry **in_orbit;
+  unsigned int nof_elements;
+  unsigned int _nof_orbits;
+  void merge_orbits(OrbitEntry *o1, OrbitEntry *o2);
+
+public:
+  /**
+   * Create a new orbit information object.
+   * The init() function must be called next to actually initialize
+   * the object.
+   */
+  Orbit();
+  ~Orbit();
+
+  /**
+   * Initialize the orbit information to consider sets of \a N elements.
+   * It is required that \a N > 0.
+   * The orbit information is reset so that each element forms
+   * an orbit of its own.
+   * Time complexity is O(N).
+   * \sa reset()
+   */
+  void init(const unsigned int N);
+
+  /**
+   * Reset the orbits so that each element forms an orbit of its own.
+   * Time complexity is O(N).
+   */
+  void reset();
+
+  /**
+   * Merge the orbits of the elements \a e1 and \a e2.
+   * Time complexity is O(k), where k is the number of elements in
+   * the smaller of the merged orbits.
+   */
+  void merge_orbits(unsigned int e1, unsigned int e2);
+
+  /**
+   * Is the element \a e the smallest element in its orbit?
+   * Time complexity is O(1).
+   */
+  bool is_minimal_representative(unsigned int e) const;
+
+  /**
+   * Get the smallest element in the orbit of the element \a e.
+   * Time complexity is O(1).
+   */
+  unsigned int get_minimal_representative(unsigned int e) const;
+
+  /**
+   * Get the number of elements in the orbit of the element \a e.
+   * Time complexity is O(1).
+   */
+  unsigned int orbit_size(unsigned int e) const;
+
+  /**
+   * Get the number of orbits.
+   * Time complexity is O(1).
+   */
+  unsigned int nof_orbits() const {return _nof_orbits; }
+};
+
+} // namespace bliss
+
+#endif
diff --git a/igraph/include/bliss/partition.hh b/igraph/include/bliss/partition.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/partition.hh
@@ -0,0 +1,308 @@
+#ifndef BLISS_PARTITION_HH
+#define BLISS_PARTITION_HH
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+  class Partition;
+}
+
+#include <cstdlib>
+#include <cstdio>
+#include <climits>
+#include "kstack.hh"
+#include "kqueue.hh"
+#include "heap.hh"
+#include "orbit.hh"
+#include "graph.hh"
+
+
+namespace bliss {
+
+/** \internal
+ * \brief A class for refinable, backtrackable ordered partitions.
+ *
+ * This is rather a data structure with some helper functions than
+ * a proper self-contained class.
+ * That is, for efficiency reasons the fields of this class are directly
+ * manipulated from bliss::AbstractGraph and its subclasses.
+ * Conversely, some methods of this class modify the fields of
+ * bliss::AbstractGraph, too.
+ */
+class Partition
+{
+public:
+  /**
+   * \brief Data structure for holding information about a cell in a Partition.
+   */
+  class Cell
+  {
+    friend class Partition;
+  public:
+    unsigned int length;
+    /* Index of the first element of the cell in
+       the Partition::elements array */
+    unsigned int first;
+    unsigned int max_ival;
+    unsigned int max_ival_count;
+  private:
+    bool in_splitting_queue;
+  public:
+    bool in_neighbour_heap;
+    /* Pointer to the next cell, null if this is the last one. */
+    Cell* next;
+    Cell* prev;
+    Cell* next_nonsingleton;
+    Cell* prev_nonsingleton;
+    unsigned int split_level;
+    /** Is this a unit cell? */
+    bool is_unit() const {return(length == 1); }
+    /** Is this cell in splitting queue? */
+    bool is_in_splitting_queue() const {return(in_splitting_queue); }
+  };
+
+
+private:
+
+  /** \internal
+   * Data structure for remembering information about splits in order to
+   * perform efficient backtracking over the splits.
+   */
+  class RefInfo {
+  public:
+    unsigned int split_cell_first;
+    int prev_nonsingleton_first;
+    int next_nonsingleton_first;
+  };
+  /** \internal
+   * A stack for remembering the splits, used for backtracking.
+   */
+  KStack<RefInfo> refinement_stack;
+
+  class BacktrackInfo {
+  public:
+    unsigned int refinement_stack_size;
+    unsigned int cr_backtrack_point;
+  };
+
+  /** \internal
+   * The main stack for enabling backtracking.
+   */
+  std::vector<BacktrackInfo> bt_stack;
+
+public:
+  AbstractGraph* graph;
+
+  /* Used during equitable partition refinement */
+  KQueue<Cell*> splitting_queue;
+  void  splitting_queue_add(Cell* const cell);
+  Cell* splitting_queue_pop();
+  bool  splitting_queue_is_empty() const;
+  void  splitting_queue_clear();
+
+
+  /** Type for backtracking points. */
+  typedef unsigned int BacktrackPoint;
+
+  /**
+   * Get a new backtrack point for the current partition
+   */
+  BacktrackPoint set_backtrack_point();
+
+  /**
+   * Backtrack to the point \a p and remove it.
+   */
+  void goto_backtrack_point(BacktrackPoint p);
+
+  /**
+   * Split the non-unit Cell \a cell = {\a element,e1,e2,...,en} containing
+   * the element \a element in two:
+   * \a cell = {e1,...,en} and \a newcell = {\a element}.
+   * @param cell     a non-unit Cell
+   * @param element  an element in \a cell
+   * @return         the new unit Cell \a newcell
+   */
+  Cell* individualize(Cell* const cell,
+		      const unsigned int element);
+
+  Cell* aux_split_in_two(Cell* const cell,
+			 const unsigned int first_half_size);
+
+
+private:
+  unsigned int N;
+  Cell* cells;
+  Cell* free_cells;
+  unsigned int discrete_cell_count;
+public:
+  Cell* first_cell;
+  Cell* first_nonsingleton_cell;
+  unsigned int *elements;
+  /* invariant_values[e] gives the invariant value of the element e */
+  unsigned int *invariant_values;
+  /* element_to_cell_map[e] gives the cell of the element e */
+  Cell **element_to_cell_map;
+  /** Get the cell of the element \a e */
+  Cell* get_cell(const unsigned int e) const {
+    return element_to_cell_map[e];
+  }
+  /* in_pos[e] points to the elements array s.t. *in_pos[e] = e  */
+  unsigned int **in_pos;
+
+  Partition();
+  ~Partition();
+
+  /**
+   * Initialize the partition to the unit partition (all elements in one cell)
+   * over the \a N > 0 elements {0,...,\a N-1}.
+   */
+  void init(const unsigned int N);
+
+  /**
+   * Returns true iff the partition is discrete, meaning that all
+   * the elements are in their own cells.
+   */
+  bool is_discrete() const {return(free_cells == 0); }
+
+  unsigned int nof_discrete_cells() const {return(discrete_cell_count); }
+
+  /**
+   * Print the partition into the file stream \a fp.
+   */
+  size_t print(FILE* const fp, const bool add_newline = true) const;
+
+  /**
+   * Print the partition cell sizes into the file stream \a fp.
+   */
+  size_t print_signature(FILE* const fp, const bool add_newline = true) const;
+
+  /*
+   * Splits the Cell \a cell into [cell_1,...,cell_n]
+   * according to the invariant_values of the elements in \a cell.
+   * After splitting, cell_1 == \a cell.
+   * Returns the pointer to the Cell cell_n;
+   * cell_n != cell iff the Cell \a cell was actually splitted.
+   * The flag \a max_ival_info_ok indicates whether the max_ival and
+   * max_ival_count fields of the Cell \a cell have consistent values
+   * when the method is called.
+   * Clears the invariant values of elements in the Cell \a cell as well as
+   * the max_ival and max_ival_count fields of the Cell \a cell.
+   */
+  Cell *zplit_cell(Cell * const cell, const bool max_ival_info_ok);
+
+  /*
+   * Routines for component recursion
+   */
+  void cr_init();
+  void cr_free();
+  unsigned int cr_get_level(const unsigned int cell_index) const;
+  unsigned int cr_split_level(const unsigned int level,
+			      const std::vector<unsigned int>& cells);
+
+  /** Clear the invariant_values of the elements in the Cell \a cell. */
+  void clear_ivs(Cell* const cell);
+
+private:
+  /*
+   * Component recursion data structures
+   */
+
+  /* Is component recursion support in use? */
+  bool cr_enabled;
+
+  class CRCell {
+  public:
+    unsigned int level;
+    CRCell* next;
+    CRCell** prev_next_ptr;
+    void detach() {
+      if(next)
+	next->prev_next_ptr = prev_next_ptr;
+      *(prev_next_ptr) = next;
+      level = UINT_MAX;
+      next = 0;
+      prev_next_ptr = 0;
+    }
+  };
+  CRCell* cr_cells;
+  CRCell** cr_levels;
+  class CR_BTInfo {
+  public:
+    unsigned int created_trail_index;
+    unsigned int splitted_level_trail_index;
+  };
+  std::vector<unsigned int> cr_created_trail;
+  std::vector<unsigned int> cr_splitted_level_trail;
+  std::vector<CR_BTInfo> cr_bt_info;
+  unsigned int cr_max_level;
+  void cr_create_at_level(const unsigned int cell_index, unsigned int level);
+  void cr_create_at_level_trailed(const unsigned int cell_index, unsigned int level);
+  unsigned int cr_get_backtrack_point();
+  void cr_goto_backtrack_point(const unsigned int btpoint);
+
+
+  /*
+   *
+   * Auxiliary routines for sorting and splitting cells
+   *
+   */
+  Cell* sort_and_split_cell1(Cell* cell);
+  Cell* sort_and_split_cell255(Cell* const cell, const unsigned int max_ival);
+  bool shellsort_cell(Cell* cell);
+  Cell* split_cell(Cell* const cell);
+
+  /*
+   * Some auxiliary stuff needed for distribution count sorting.
+   * To make the code thread-safe (modulo the requirement that each graph is
+   * only accessed in one thread at a time), the arrays are owned by
+   * the partition instance, not statically defined.
+   */
+  unsigned int dcs_count[256];
+  unsigned int dcs_start[256];
+  void dcs_cumulate_count(const unsigned int max);
+};
+
+
+inline Partition::Cell*
+Partition::splitting_queue_pop()
+{
+  Cell* const cell = splitting_queue.pop_front();
+  cell->in_splitting_queue = false;
+  return cell;
+}
+
+inline bool
+Partition::splitting_queue_is_empty() const
+{
+  return splitting_queue.is_empty();
+}
+
+
+inline unsigned int
+Partition::cr_get_level(const unsigned int cell_index) const
+{
+  return(cr_cells[cell_index].level);
+}
+
+
+
+} // namespace bliss
+
+#endif
diff --git a/igraph/include/bliss/uintseqhash.hh b/igraph/include/bliss/uintseqhash.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/uintseqhash.hh
@@ -0,0 +1,65 @@
+#ifndef BLISS_UINTSEQHASH_HH
+#define BLISS_UINTSEQHASH_HH
+
+#include <cstdio>
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+/** \internal
+ * \brief A hash for sequences of unsigned ints.
+ */
+class UintSeqHash
+{
+protected:
+  unsigned int h;
+public:
+  UintSeqHash() {h = 0; }
+  UintSeqHash(const UintSeqHash &other) {h = other.h; }
+  UintSeqHash& operator=(const UintSeqHash &other) {h = other.h; return *this; }
+  
+  /** Reset the hash value. */
+  void reset() {h = 0; }
+
+  /** Add the unsigned int \a n to the sequence. */
+  void update(unsigned int n);
+
+  /** Get the hash value of the sequence seen so far. */
+  unsigned int get_value() const {return h; }
+
+  /** Compare the hash values of this and \a other.
+   * Return -1/0/1 if the value of this is smaller/equal/greater than
+   * that of \a other. */
+  int cmp(const UintSeqHash &other) const {
+    return (h < other.h)?-1:((h == other.h)?0:1);
+  }
+  /** An abbreviation for cmp(other) < 0 */
+  bool is_lt(const UintSeqHash &other) const {return(cmp(other) < 0); }
+  /** An abbreviation for cmp(other) <= 0 */
+  bool is_le(const UintSeqHash &other) const {return(cmp(other) <= 0); }
+  /** An abbreviation for cmp(other) == 0 */
+  bool is_equal(const UintSeqHash &other) const {return(cmp(other) == 0); }
+};
+
+
+} // namespace bliss
+
+#endif
diff --git a/igraph/include/bliss/utils.hh b/igraph/include/bliss/utils.hh
new file mode 100644
--- /dev/null
+++ b/igraph/include/bliss/utils.hh
@@ -0,0 +1,69 @@
+#ifndef BLISS_UTILS_HH
+#define BLISS_UTILS_HH
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \file
+ * \brief Some small utilities.
+ *
+ */
+
+#include <cstdio>
+using namespace std;
+
+namespace bliss {
+
+/**
+ * Print the permutation \a perm of {0,...,N-1} in the cycle format
+ * in the file stream \a fp.
+ * The amount \a offset is added to each element before printing,
+ * e.g. the permutation (2 4) is printed as (3 5) when \a offset is 1.
+ */
+void print_permutation(FILE* fp,
+		       const unsigned int N,
+		       const unsigned int* perm,
+		       const unsigned int offset = 0);
+
+/**
+ * Print the permutation \a perm of {0,...,N-1} in the cycle format
+ * in the file stream \a fp.
+ * The amount \a offset is added to each element before printing,
+ * e.g. the permutation (2 4) is printed as (3 5) when \a offset is 1.
+ */
+void print_permutation(FILE* fp,
+		       const std::vector<unsigned int>& perm,
+		       const unsigned int offset = 0);
+
+/**
+ * Check whether \a perm is a valid permutation on {0,...,N-1}.
+ * Slow, mainly for debugging and validation purposes.
+ */
+bool is_permutation(const unsigned int N, const unsigned int* perm);
+
+/**
+ * Check whether \a perm is a valid permutation on {0,...,N-1}.
+ * Slow, mainly for debugging and validation purposes.
+ */
+bool is_permutation(const std::vector<unsigned int>& perm);
+
+} // namespace bliss
+
+#endif
diff --git a/igraph/include/cliquer/cliquer.h b/igraph/include/cliquer/cliquer.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/cliquer/cliquer.h
@@ -0,0 +1,57 @@
+
+#ifndef CLIQUER_H
+#define CLIQUER_H
+
+#include <string.h>
+
+#include "set.h"
+#include "graph.h"
+#include "reorder.h"
+
+typedef struct _clique_options clique_options;
+struct _clique_options {
+	int *(*reorder_function)(graph_t *, boolean);
+	int *reorder_map;
+
+	/* arguments:  level, n, max, user_time, system_time, opts */
+	boolean (*time_function)(int,int,int,int,double,double,
+				 clique_options *);
+	FILE *output;
+
+	boolean (*user_function)(set_t,graph_t *,clique_options *);
+	void *user_data;
+	set_t *clique_list;
+	int clique_list_length;
+};
+
+/* Weighted clique functions */
+extern int clique_max_weight(graph_t *g,clique_options *opts);
+extern set_t clique_find_single(graph_t *g,int min_weight,int max_weight,
+				boolean maximal, clique_options *opts);
+extern int clique_find_all(graph_t *g, int req_weight, boolean exact,
+			   boolean maximal, clique_options *opts);
+
+/* Unweighted clique functions */
+#define clique_unweighted_max_size clique_unweighted_max_weight
+extern int clique_unweighted_max_weight(graph_t *g, clique_options *opts);
+extern set_t clique_unweighted_find_single(graph_t *g,int min_size,
+					   int max_size,boolean maximal,
+					   clique_options *opts);
+extern int clique_unweighted_find_all(graph_t *g, int min_size, int max_size,
+				      boolean maximal, clique_options *opts);
+
+/* Time printing functions */
+/*
+extern boolean clique_print_time(int level, int i, int n, int max,
+				 double cputime, double realtime,
+				 clique_options *opts);
+extern boolean clique_print_time_always(int level, int i, int n, int max,
+					double cputime, double realtime,
+					clique_options *opts);
+*/
+
+/* Alternate spelling (let's be a little forgiving): */
+#define cliquer_options clique_options
+#define cliquer_default_options clique_default_options
+
+#endif /* !CLIQUER_H */
diff --git a/igraph/include/cliquer/cliquerconf.h b/igraph/include/cliquer/cliquerconf.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/cliquer/cliquerconf.h
@@ -0,0 +1,68 @@
+
+#ifndef CLIQUERCONF_H
+#define CLIQUERCONF_H
+
+/*
+ * setelement is the basic memory type used in sets.  It is often fastest
+ * to be as large as can fit into the CPU registers.
+ *
+ * ELEMENTSIZE is the size of one setelement, measured in bits.  It must
+ * be either 16, 32 or 64  (otherwise additional changes must be made to
+ * the source).
+ *
+ * The default is to use "unsigned long int" and attempt to guess the
+ * size using <limits.h>, which should work pretty well.  Check functioning
+ * with "make test".
+ */
+
+/* typedef unsigned long int setelement; */
+/* #define ELEMENTSIZE 64 */
+
+
+/*
+ * INLINE is a command prepended to function declarations to instruct the
+ * compiler to inline the function.  If inlining is not desired, define blank.
+ *
+ * The default is to use "inline", which is recognized by most compilers.
+ */
+
+/* #define INLINE */
+/* #define INLINE __inline__ */
+#if __STDC_VERSION__ >= 199901L
+ #define INLINE inline
+#else
+ #if defined(_MSC_VER)
+  #define INLINE __inline
+ #elif defined(__GNUC__)
+  #define INLINE __inline__
+ #else
+  #define INLINE
+ #endif
+#endif
+
+
+/*
+ * Set handling functions are defined as static functions in set.h for
+ * performance reasons.  This may cause unnecessary warnings from the
+ * compiler.  Some compilers (such as GCC) have the possibility to turn
+ * off the warnings on a per-function basis using a flag prepended to
+ * the function declaration.
+ *
+ * The default is to use the correct attribute when compiling with GCC,
+ * or no flag otherwise.
+ */
+
+/* #define UNUSED_FUNCTION __attribute__((unused)) */
+/* #define UNUSED_FUNCTION */
+
+
+/*
+ * Uncommenting the following will disable all assertions  (checks that
+ * function arguments and other variables are correct).  This is highly
+ * discouraged, as it allows bugs to go unnoticed easier.  The assertions
+ * are set so that they do not slow down programs notably.
+ */
+
+/* #define ASSERT(x) */
+
+#endif /* !CLIQUERCONF_H */
diff --git a/igraph/include/cliquer/graph.h b/igraph/include/cliquer/graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/cliquer/graph.h
@@ -0,0 +1,75 @@
+
+#ifndef CLIQUER_GRAPH_H
+#define CLIQUER_GRAPH_H
+
+#include "set.h"
+
+typedef struct _graph_t graph_t;
+struct _graph_t {
+	int n;             /* Vertices numbered 0...n-1 */
+	set_t *edges;      /* A list of n sets (the edges). */
+	int *weights;      /* A list of n vertex weights. */
+};
+
+
+#define GRAPH_IS_EDGE_FAST(g,i,j)  (SET_CONTAINS_FAST((g)->edges[(i)],(j)))
+#define GRAPH_IS_EDGE(g,i,j) (((i)<((g)->n))?SET_CONTAINS((g)->edges[(i)], \
+							  (j)):FALSE)
+#define GRAPH_ADD_EDGE(g,i,j) do {            \
+	SET_ADD_ELEMENT((g)->edges[(i)],(j)); \
+	SET_ADD_ELEMENT((g)->edges[(j)],(i)); \
+} while (FALSE)
+#define GRAPH_DEL_EDGE(g,i,j) do {            \
+	SET_DEL_ELEMENT((g)->edges[(i)],(j)); \
+	SET_DEL_ELEMENT((g)->edges[(j)],(i)); \
+} while (FALSE)
+
+
+extern graph_t *graph_new(int n);
+extern void graph_free(graph_t *g);
+extern void graph_resize(graph_t *g, int size);
+extern void graph_crop(graph_t *g);
+
+extern boolean graph_weighted(graph_t *g);
+extern int graph_edge_count(graph_t *g);
+
+/*
+extern graph_t *graph_read_dimacs(FILE *fp);
+extern graph_t *graph_read_dimacs_file(char *file);
+extern boolean graph_write_dimacs_ascii(graph_t *g, char *comment,FILE *fp);
+extern boolean graph_write_dimacs_ascii_file(graph_t *g,char *comment,
+					     char *file);
+extern boolean graph_write_dimacs_binary(graph_t *g, char *comment,FILE *fp);
+extern boolean graph_write_dimacs_binary_file(graph_t *g, char *comment,
+					      char *file);
+*/
+
+extern void graph_print(graph_t *g);
+extern boolean graph_test(graph_t *g, FILE *output);
+extern int graph_test_regular(graph_t *g);
+
+UNUSED_FUNCTION INLINE
+static int graph_subgraph_weight(graph_t *g,set_t s) {
+	int i,j;
+	int count=0;
+	setelement e;
+
+	for (i=0; i<SET_ARRAY_LENGTH(s); i++) {
+		if (s[i]) {
+			e=s[i];
+			for (j=0; j<ELEMENTSIZE; j++) {
+				if (e&1)
+					count+=g->weights[i*ELEMENTSIZE+j];
+				e = e>>1;
+			}
+		}
+	}
+	return count;
+}
+
+UNUSED_FUNCTION INLINE
+static int graph_vertex_degree(graph_t *g, int v) {
+	return set_size(g->edges[v]);
+}
+
+#endif /* !CLIQUER_GRAPH_H */
diff --git a/igraph/include/cliquer/misc.h b/igraph/include/cliquer/misc.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/cliquer/misc.h
@@ -0,0 +1,73 @@
+
+#ifndef CLIQUER_MISC_H
+#define CLIQUER_MISC_H
+
+#include "cliquerconf.h"
+
+/*
+ * We #define boolean instead of using a typedef because nauty.h uses it
+ * also.  AFAIK, there is no way to check for an existing typedef, and
+ * re-typedefing is illegal (even when using exactly the same datatype!).
+ */
+#ifndef boolean
+#define boolean int
+#endif
+
+
+/*
+ * The original cliquer source has some functions incorrectly marked as unused,
+ * thus leave this undefined.
+ */
+#define UNUSED_FUNCTION
+
+
+/*
+ * Default inlining directive:  "inline"
+ */
+#ifndef INLINE
+#define INLINE inline
+#endif
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef ASSERT
+#ifdef USING_R
+#include <R.h>
+#define ASSERT(expr) \
+        if (!(expr)) { \
+	        error("cliquer file %s: line %d: assertion failed: " \
+			"(%s)\n",__FILE__,__LINE__,#expr); \
+	}
+#else
+#define ASSERT(expr) \
+        if (!(expr)) { \
+		fprintf(stderr,"cliquer file %s: line %d: assertion failed: " \
+			"(%s)\n",__FILE__,__LINE__,#expr); \
+		abort(); \
+	}
+#endif
+#endif /* !ASSERT */
+
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+#ifndef ABS
+#define ABS(v)  (((v)<0)?(-(v)):(v))
+#endif
+
+#endif /* !CLIQUER_MISC_H */
+
diff --git a/igraph/include/cliquer/reorder.h b/igraph/include/cliquer/reorder.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/cliquer/reorder.h
@@ -0,0 +1,26 @@
+
+#ifndef CLIQUER_REORDER_H
+#define CLIQUER_REORDER_H
+
+#include "set.h"
+#include "graph.h"
+
+extern void reorder_set(set_t s,int *order);
+extern void reorder_graph(graph_t *g, int *order);
+extern int *reorder_duplicate(int *order,int n);
+extern void reorder_invert(int *order,int n);
+extern void reorder_reverse(int *order,int n);
+extern int *reorder_ident(int n);
+extern boolean reorder_is_bijection(int *order,int n);
+
+
+#define reorder_by_default reorder_by_greedy_coloring
+extern int *reorder_by_greedy_coloring(graph_t *g, boolean weighted);
+extern int *reorder_by_weighted_greedy_coloring(graph_t *g, boolean weighted);
+extern int *reorder_by_unweighted_greedy_coloring(graph_t *g,boolean weighted);
+extern int *reorder_by_degree(graph_t *g, boolean weighted);
+extern int *reorder_by_random(graph_t *g, boolean weighted);
+extern int *reorder_by_ident(graph_t *g, boolean weighted);
+extern int *reorder_by_reverse(graph_t *g, boolean weighted);
+
+#endif /* !CLIQUER_REORDER_H */
diff --git a/igraph/include/cliquer/set.h b/igraph/include/cliquer/set.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/cliquer/set.h
@@ -0,0 +1,389 @@
+
+/*
+ * This file contains the set handling routines.
+ *
+ * Copyright (C) 2002 Sampo Niskanen, Patric Östergård.
+ * Licensed under the GNU GPL, read the file LICENSE for details.
+ */
+
+#ifndef CLIQUER_SET_H
+#define CLIQUER_SET_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "misc.h"
+
+/*
+ * Sets are arrays of setelement's (typically unsigned long int's) with
+ * representative bits for each value they can contain.  The values
+ * are numbered 0,...,n-1.
+ */
+
+
+/*** Variable types and constants. ***/
+
+
+/*
+ * If setelement hasn't been declared:
+ *   - use "unsigned long int" as setelement
+ *   - try to deduce size from ULONG_MAX
+ */
+
+#ifndef ELEMENTSIZE
+typedef unsigned long int setelement;
+# if (ULONG_MAX == 65535)
+#  define ELEMENTSIZE 16
+# elif (ULONG_MAX == 4294967295)
+#  define ELEMENTSIZE 32
+# else
+#  define ELEMENTSIZE 64
+# endif
+#endif  /* !ELEMENTSIZE */
+
+typedef setelement * set_t;
+
+
+/*** Counting amount of 1 bits in a setelement ***/
+
+/* Array for amount of 1 bits in a byte. */
+static int set_bit_count[256] = {
+	0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
+	1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+	1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+	1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+	3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+	1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+	3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+	3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+	3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+	4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 };
+
+/* The following macros assume that all higher bits are 0.
+ * They may in some cases be useful also on with other ELEMENTSIZE's,
+ * so we define them all.  */
+#define SET_ELEMENT_BIT_COUNT_8(a)  (set_bit_count[(a)])
+#define SET_ELEMENT_BIT_COUNT_16(a) (set_bit_count[(a)>>8] + \
+				     set_bit_count[(a)&0xFF])
+#define SET_ELEMENT_BIT_COUNT_32(a) (set_bit_count[(a)>>24] + \
+				     set_bit_count[((a)>>16)&0xFF] + \
+				     set_bit_count[((a)>>8)&0xFF] + \
+				     set_bit_count[(a)&0xFF])
+#define SET_ELEMENT_BIT_COUNT_64(a) (set_bit_count[(a)>>56] + \
+				     set_bit_count[((a)>>48)&0xFF] + \
+				     set_bit_count[((a)>>40)&0xFF] + \
+				     set_bit_count[((a)>>32)&0xFF] + \
+				     set_bit_count[((a)>>24)&0xFF] + \
+				     set_bit_count[((a)>>16)&0xFF] + \
+				     set_bit_count[((a)>>8)&0xFF] + \
+				     set_bit_count[(a)&0xFF])
+#if (ELEMENTSIZE==64)
+# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_64(a)
+# define FULL_ELEMENT ((setelement)0xFFFFFFFFFFFFFFFF)
+#elif (ELEMENTSIZE==32)
+# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_32(a)
+# define FULL_ELEMENT ((setelement)0xFFFFFFFF)
+#elif (ELEMENTSIZE==16)
+# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_16(a)
+# define FULL_ELEMENT ((setelement)0xFFFF)
+#else
+# error "SET_ELEMENT_BIT_COUNT(a) not defined for current ELEMENTSIZE"
+#endif
+
+
+
+/*** Macros and functions ***/
+
+/*
+ * Gives a value with bit x (counting from lsb up) set.
+ *
+ * Making this as a table might speed up things on some machines
+ * (though on most modern machines it's faster to shift instead of
+ * using memory).  Making it a macro makes it easy to change.
+ */
+#define SET_BIT_MASK(x) ((setelement)1<<(x))
+
+
+
+/* Set element handling macros */
+
+#define SET_ELEMENT_INTERSECT(a,b)  ((a)&(b))
+#define SET_ELEMENT_UNION(a,b)      ((a)|(b))
+#define SET_ELEMENT_DIFFERENCE(a,b) ((a)&(~(b)))
+#define SET_ELEMENT_CONTAINS(e,v)   ((e)&SET_BIT_MASK(v))
+
+
+/* Set handling macros */
+
+#define SET_ADD_ELEMENT(s,a) \
+                       ((s)[(a)/ELEMENTSIZE] |= SET_BIT_MASK((a)%ELEMENTSIZE))
+#define SET_DEL_ELEMENT(s,a) \
+                       ((s)[(a)/ELEMENTSIZE] &= ~SET_BIT_MASK((a)%ELEMENTSIZE))
+#define SET_CONTAINS_FAST(s,a) (SET_ELEMENT_CONTAINS((s)[(a)/ELEMENTSIZE], \
+						      (a)%ELEMENTSIZE))
+#define SET_CONTAINS(s,a) (((a)<SET_MAX_SIZE(s))?SET_CONTAINS_FAST(s,a):FALSE)
+
+/* Sets can hold values between 0,...,SET_MAX_SIZE(s)-1 */
+#define SET_MAX_SIZE(s) ((s)[-1])
+/* Sets consist of an array of SET_ARRAY_LENGTH(s) setelements */
+#define SET_ARRAY_LENGTH(s) (((s)[-1]+ELEMENTSIZE-1)/ELEMENTSIZE)
+
+
+/*
+ * set_new()
+ *
+ * Create a new set that can hold values in the range 0,...,size-1.
+ */
+UNUSED_FUNCTION
+static set_t set_new(int size) {
+	int n;
+	set_t s;
+
+	ASSERT(size>0);
+
+	n=(size/ELEMENTSIZE+1)+1;
+	s=calloc(n,sizeof(setelement));
+	s[0]=size;
+
+	return &(s[1]);
+}
+
+/*
+ * set_free()
+ *
+ * Free the memory associated with set s.
+ */
+UNUSED_FUNCTION INLINE
+static void set_free(set_t s) {
+	ASSERT(s!=NULL);
+	free(&(s[-1]));
+}
+
+/*
+ * set_resize()
+ *
+ * Resizes set s to given size.  If the size is less than SET_MAX_SIZE(s),
+ * the last elements are dropped.
+ *
+ * Returns a pointer to the new set.
+ */
+UNUSED_FUNCTION INLINE
+static set_t set_resize(set_t s, int size) {
+	int n;
+
+	ASSERT(size>0);
+
+	n=(size/ELEMENTSIZE+1);
+	s=((setelement *)realloc(s-1,(n+1)*sizeof(setelement)))+1;
+
+	if (n>SET_ARRAY_LENGTH(s))
+		memset(s+SET_ARRAY_LENGTH(s),0,
+		       (n-SET_ARRAY_LENGTH(s))*sizeof(setelement));
+	if (size < SET_MAX_SIZE(s))
+		s[(size-1)/ELEMENTSIZE] &= (FULL_ELEMENT >>
+					    (ELEMENTSIZE-size%ELEMENTSIZE));
+	s[-1]=size;
+
+	return s;
+}
+
+/*
+ * set_size()
+ *
+ * Returns the number of elements in set s.
+ */
+UNUSED_FUNCTION INLINE
+static int set_size(set_t s) {
+	int count=0;
+	setelement *c;
+
+	for (c=s; c < s+SET_ARRAY_LENGTH(s); c++)
+		count+=SET_ELEMENT_BIT_COUNT(*c);
+	return count;
+}
+
+/*
+ * set_duplicate()
+ *
+ * Returns a newly allocated duplicate of set s.
+ */
+UNUSED_FUNCTION INLINE
+static set_t set_duplicate(set_t s) {
+	set_t new;
+
+	new=set_new(SET_MAX_SIZE(s));
+	memcpy(new,s,SET_ARRAY_LENGTH(s)*sizeof(setelement));
+	return new;
+}
+
+/*
+ * set_copy()
+ *
+ * Copies set src to dest.  If dest is NULL, is equal to set_duplicate.
+ * If dest smaller than src, it is freed and a new set of the same size as
+ * src is returned.
+ */
+UNUSED_FUNCTION INLINE
+static set_t set_copy(set_t dest,set_t src) {
+	if (dest==NULL)
+		return set_duplicate(src);
+	if (SET_MAX_SIZE(dest)<SET_MAX_SIZE(src)) {
+		set_free(dest);
+		return set_duplicate(src);
+	}
+	memcpy(dest,src,SET_ARRAY_LENGTH(src)*sizeof(setelement));
+	memset(dest+SET_ARRAY_LENGTH(src),0,((SET_ARRAY_LENGTH(dest) -
+					      SET_ARRAY_LENGTH(src)) *
+					     sizeof(setelement)));
+	return dest;
+}
+
+/*
+ * set_empty()
+ *
+ * Removes all elements from the set s.
+ */
+UNUSED_FUNCTION INLINE
+static void set_empty(set_t s) {
+	memset(s,0,SET_ARRAY_LENGTH(s)*sizeof(setelement));
+	return;
+}
+
+/*
+ * set_intersection()
+ *
+ * Store the intersection of sets a and b into res.  If res is NULL,
+ * a new set is created and the result is written to it.  If res is
+ * smaller than the larger one of a and b, it is freed and a new set
+ * is created and the result is returned.
+ *
+ * Returns either res or a new set that has been allocated in its stead.
+ *
+ * Note:  res may not be a or b.
+ */
+UNUSED_FUNCTION INLINE
+static set_t set_intersection(set_t res,set_t a,set_t b) {
+	int i,max;
+
+	if (res==NULL) {
+		res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b)));
+	} else if (SET_MAX_SIZE(res) < MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))) {
+		set_free(res);
+		res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b)));
+	} else {
+		set_empty(res);
+	}
+
+	max=MIN(SET_ARRAY_LENGTH(a),SET_ARRAY_LENGTH(b));
+	for (i=0; i<max; i++) {
+		res[i]=SET_ELEMENT_INTERSECT(a[i],b[i]);
+	}
+
+	return res;
+}
+
+/*
+ * set_union()
+ *
+ * Store the union of sets a and b into res.  If res is NULL, a new set
+ * is created and the result is written to it.  If res is smaller than
+ * the larger one of a and b, it is freed and a new set is created and
+ * the result is returned.
+ *
+ * Returns either res or a new set that has been allocated in its stead.
+ *
+ * Note:  res may not be a or b.
+ */
+UNUSED_FUNCTION INLINE
+static set_t set_union(set_t res,set_t a,set_t b) {
+	int i,max;
+
+	if (res==NULL) {
+		res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b)));
+	} else if (SET_MAX_SIZE(res) < MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))) {
+		set_free(res);
+		res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b)));
+	} else {
+		set_empty(res);
+	}
+
+	max=MAX(SET_ARRAY_LENGTH(a),SET_ARRAY_LENGTH(b));
+	for (i=0; i<max; i++) {
+		res[i]=SET_ELEMENT_UNION(a[i],b[i]);
+	}
+
+	return res;
+}
+
+
+/*
+ * set_return_next()
+ *
+ * Returns the smallest value in set s which is greater than n, or -1 if
+ * such a value does not exist.
+ *
+ * Can be used to iterate through all values of s:
+ *
+ * int i=-1;
+ * while ((i=set_return_next(s,i))>=0) {
+ *         // i is in set s
+ * }
+ */
+UNUSED_FUNCTION INLINE
+static int set_return_next(set_t s, int n) {
+	if (n<0)
+		n=0;
+	else
+		n++;
+	if (n >= SET_MAX_SIZE(s))
+		return -1;
+
+	while (n%ELEMENTSIZE) {
+		if (SET_CONTAINS(s,n))
+			return n;
+		n++;
+		if (n >= SET_MAX_SIZE(s))
+			return -1;
+	}
+
+	while (s[n/ELEMENTSIZE]==0) {
+		n+=ELEMENTSIZE;
+		if (n >= SET_MAX_SIZE(s))
+			return -1;
+	}
+	while (!SET_CONTAINS(s,n)) {
+		n++;
+		if (n >= SET_MAX_SIZE(s))
+			return -1;
+	}
+	return n;
+}
+
+
+/*
+ * set_print()
+ *
+ * Prints the size and contents of set s to stdout.
+ * Mainly useful for debugging purposes and trivial output.
+ */
+/*
+UNUSED_FUNCTION
+static void set_print(set_t s) {
+	int i;
+	printf("size=%d(max %d)",set_size(s),(int)SET_MAX_SIZE(s));
+	for (i=0; i<SET_MAX_SIZE(s); i++)
+		if (SET_CONTAINS(s,i))
+			printf(" %d",i);
+	printf("\n");
+	return;
+}
+*/
+
+#endif /* !CLIQUER_SET_H */
diff --git a/igraph/include/config.h b/igraph/include/config.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/config.h
@@ -0,0 +1,39 @@
+/* functions */
+#define HAVE_EXPM1 1
+#define HAVE_FABSL 1
+#define HAVE_FINITE 1
+#define HAVE_FMIN 1
+#define HAVE_FTRUNCATE 1
+#define HAVE_ISNAN 1
+#define HAVE_LOG1P 1
+#define HAVE_LOG2 1
+#define HAVE_RINT 1
+#define HAVE_RINTF 1
+#define HAVE_ROUND 1
+#define HAVE_SNPRINTF 1
+
+/* libraries */
+#define HAVE_MEMORY_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+
+#define IGRAPH_F77_SAVE static IGRAPH_THREAD_LOCAL
+#define IGRAPH_THREAD_LOCAL 
+
+#define INTERNAL_ARPACK 1
+#define INTERNAL_BLAS 1
+#define INTERNAL_F2C 1
+#define INTERNAL_GLPK 1
+#define INTERNAL_LAPACK 1
+
+#define LT_OBJDIR ".libs/"
+#define PACKAGE "igraph"
+#define PACKAGE_BUGREPORT "igraph@igraph.org"
+#define PACKAGE_NAME "igraph"
+#define PACKAGE_STRING "igraph 0.8.0"
+#define PACKAGE_TARNAME "igraph"
+#define PACKAGE_URL ""
+#define PACKAGE_VERSION "0.8.0"
+#define STDC_HEADERS 1
+#define VERSION "0.8.0"
diff --git a/igraph/include/cs/UFconfig.h b/igraph/include/cs/UFconfig.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/cs/UFconfig.h
@@ -0,0 +1,118 @@
+/* ========================================================================== */
+/* === UFconfig.h =========================================================== */
+/* ========================================================================== */
+
+/* Configuration file for SuiteSparse: a Suite of Sparse matrix packages
+ * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others).
+ *
+ * UFconfig.h provides the definition of the long integer.  On most systems,
+ * a C program can be compiled in LP64 mode, in which long's and pointers are
+ * both 64-bits, and int's are 32-bits.  Windows 64, however, uses the LLP64
+ * model, in which int's and long's are 32-bits, and long long's and pointers
+ * are 64-bits.
+ *
+ * SuiteSparse packages that include long integer versions are
+ * intended for the LP64 mode.  However, as a workaround for Windows 64
+ * (and perhaps other systems), the long integer can be redefined.
+ *
+ * If _WIN64 is defined, then the __int64 type is used instead of long.
+ *
+ * The long integer can also be defined at compile time.  For example, this
+ * could be added to UFconfig.mk:
+ *
+ * CFLAGS = -O -D'UF_long=long long' -D'UF_long_max=9223372036854775801' \
+ *   -D'UF_long_id="%lld"'
+ *
+ * This file defines UF_long as either long (on all but _WIN64) or
+ * __int64 on Windows 64.  The intent is that a UF_long is always a 64-bit
+ * integer in a 64-bit code.  ptrdiff_t might be a better choice than long;
+ * it is always the same size as a pointer.
+ *
+ * This file also defines the SUITESPARSE_VERSION and related definitions.
+ *
+ * Copyright (c) 2007, University of Florida.  No licensing restrictions
+ * apply to this file or to the UFconfig directory.  Author: Timothy A. Davis.
+ */
+
+#ifndef _UFCONFIG_H
+#define _UFCONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <limits.h>
+
+/* ========================================================================== */
+/* === UF_long ============================================================== */
+/* ========================================================================== */
+
+#ifndef UF_long
+
+#ifdef _WIN64
+
+#define UF_long __int64
+#define UF_long_max _I64_MAX
+#define UF_long_id "%I64d"
+
+#else
+
+#define UF_long long
+#define UF_long_max LONG_MAX
+#define UF_long_id "%ld"
+
+#endif
+#endif
+
+/* ========================================================================== */
+/* === SuiteSparse version ================================================== */
+/* ========================================================================== */
+
+/* SuiteSparse is not a package itself, but a collection of packages, some of
+ * which must be used together (UMFPACK requires AMD, CHOLMOD requires AMD,
+ * COLAMD, CAMD, and CCOLAMD, etc).  A version number is provided here for the
+ * collection itself.  The versions of packages within each version of
+ * SuiteSparse are meant to work together.  Combining one packge from one
+ * version of SuiteSparse, with another package from another version of
+ * SuiteSparse, may or may not work.
+ *
+ * SuiteSparse Version 3.3.0 contains the following packages:
+ *
+ *  AMD		    version 2.2.0
+ *  CAMD	    version 2.2.0
+ *  COLAMD	    version 2.7.1
+ *  CCOLAMD	    version 2.7.1
+ *  CHOLMOD	    version 1.7.1
+ *  CSparse	    version 2.2.3
+ *  CXSparse	    version 2.2.3
+ *  KLU		    version 1.1.0
+ *  BTF		    version 1.0.1
+ *  LDL		    version 2.0.1
+ *  UFconfig	    version number is the same as SuiteSparse
+ *  UMFPACK	    version 5.3.0
+ *  RBio	    version 1.1.1
+ *  UFcollection    version 1.2.0
+ *  LINFACTOR       version 1.1.0
+ *  MESHND          version 1.1.1
+ *  SSMULT          version 2.0.0
+ *  MATLAB_Tools    no specific version number
+ *  SuiteSparseQR   version 1.1.1
+ *
+ * Other package dependencies:
+ *  BLAS	    required by CHOLMOD and UMFPACK
+ *  LAPACK	    required by CHOLMOD
+ *  METIS 4.0.1	    required by CHOLMOD (optional) and KLU (optional)
+ */
+
+#define SUITESPARSE_DATE "Mar 24, 2009"
+#define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub))
+#define SUITESPARSE_MAIN_VERSION 3
+#define SUITESPARSE_SUB_VERSION 3
+#define SUITESPARSE_SUBSUB_VERSION 0
+#define SUITESPARSE_VERSION \
+    SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION)
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/igraph/include/cs/cs.h b/igraph/include/cs/cs.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/cs/cs.h
@@ -0,0 +1,756 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CXS_H
+#define _CXS_H
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#ifdef MATLAB_MEX_FILE
+#include "mex.h"
+#endif
+
+
+#ifdef __cplusplus
+#ifndef NCOMPLEX
+#include <complex>
+typedef std::complex<double> cs_complex_t ;
+#endif
+extern "C" {
+#else
+#ifndef NCOMPLEX
+#include <complex.h>
+#define cs_complex_t double _Complex
+#endif
+#endif
+
+#define CS_VER 2                    /* CXSparse Version 2.2.3 */
+#define CS_SUBVER 2
+#define CS_SUBSUB 3
+#define CS_DATE "Mar 24, 2009"      /* CXSparse release date */
+#define CS_COPYRIGHT "Copyright (c) Timothy A. Davis, 2006-2009"
+#define CXSPARSE
+
+/* define UF_long */
+#include "UFconfig.h"
+
+/* -------------------------------------------------------------------------- */
+/* double/int version of CXSparse */
+/* -------------------------------------------------------------------------- */
+
+/* --- primary CSparse routines and data structures ------------------------- */
+
+typedef struct cs_di_sparse  /* matrix in compressed-column or triplet form */
+{
+    int nzmax ;     /* maximum number of entries */
+    int m ;         /* number of rows */
+    int n ;         /* number of columns */
+    int *p ;        /* column pointers (size n+1) or col indices (size nzmax) */
+    int *i ;        /* row indices, size nzmax */
+    double *x ;     /* numerical values, size nzmax */
+    int nz ;        /* # of entries in triplet matrix, -1 for compressed-col */
+} cs_di ;
+
+cs_di *cs_di_add (const cs_di *A, const cs_di *B, double alpha, double beta) ;
+int cs_di_cholsol (int order, const cs_di *A, double *b) ;
+int cs_di_dupl (cs_di *A) ;
+int cs_di_entry (cs_di *T, int i, int j, double x) ;
+int cs_di_lusol (int order, const cs_di *A, double *b, double tol) ;
+int cs_di_gaxpy (const cs_di *A, const double *x, double *y) ;
+cs_di *cs_di_multiply (const cs_di *A, const cs_di *B) ;
+int cs_di_qrsol (int order, const cs_di *A, double *b) ;
+cs_di *cs_di_transpose (const cs_di *A, int values) ;
+cs_di *cs_di_compress (const cs_di *T) ;
+double cs_di_norm (const cs_di *A) ;
+int cs_di_print (const cs_di *A, int brief) ;
+cs_di *cs_di_load (FILE *f) ;
+
+/* utilities */
+void *cs_di_calloc (int n, size_t size) ;
+void *cs_di_free (void *p) ;
+void *cs_di_realloc (void *p, int n, size_t size, int *ok) ;
+cs_di *cs_di_spalloc (int m, int n, int nzmax, int values, int t) ;
+cs_di *cs_di_spfree (cs_di *A) ;
+int cs_di_sprealloc (cs_di *A, int nzmax) ;
+void *cs_di_malloc (int n, size_t size) ;
+
+/* --- secondary CSparse routines and data structures ----------------------- */
+
+typedef struct cs_di_symbolic  /* symbolic Cholesky, LU, or QR analysis */
+{
+    int *pinv ;     /* inverse row perm. for QR, fill red. perm for Chol */
+    int *q ;        /* fill-reducing column permutation for LU and QR */
+    int *parent ;   /* elimination tree for Cholesky and QR */
+    int *cp ;       /* column pointers for Cholesky, row counts for QR */
+    int *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */
+    int m2 ;        /* # of rows for QR, after adding fictitious rows */
+    double lnz ;    /* # entries in L for LU or Cholesky; in V for QR */
+    double unz ;    /* # entries in U for LU; in R for QR */
+} cs_dis ;
+
+typedef struct cs_di_numeric   /* numeric Cholesky, LU, or QR factorization */
+{
+    cs_di *L ;      /* L for LU and Cholesky, V for QR */
+    cs_di *U ;      /* U for LU, r for QR, not used for Cholesky */
+    int *pinv ;     /* partial pivoting for LU */
+    double *B ;     /* beta [0..n-1] for QR */
+} cs_din ;
+
+typedef struct cs_di_dmperm_results    /* cs_di_dmperm or cs_di_scc output */
+{
+    int *p ;        /* size m, row permutation */
+    int *q ;        /* size n, column permutation */
+    int *r ;        /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */
+    int *s ;        /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */
+    int nb ;        /* # of blocks in fine dmperm decomposition */
+    int rr [5] ;    /* coarse row decomposition */
+    int cc [5] ;    /* coarse column decomposition */
+} cs_did ;
+
+int *cs_di_amd (int order, const cs_di *A) ;
+cs_din *cs_di_chol (const cs_di *A, const cs_dis *S) ;
+cs_did *cs_di_dmperm (const cs_di *A, int seed) ;
+int cs_di_droptol (cs_di *A, double tol) ;
+int cs_di_dropzeros (cs_di *A) ;
+int cs_di_happly (const cs_di *V, int i, double beta, double *x) ;
+int cs_di_ipvec (const int *p, const double *b, double *x, int n) ;
+int cs_di_lsolve (const cs_di *L, double *x) ;
+int cs_di_ltsolve (const cs_di *L, double *x) ;
+cs_din *cs_di_lu (const cs_di *A, const cs_dis *S, double tol) ;
+cs_di *cs_di_permute (const cs_di *A, const int *pinv, const int *q,
+    int values) ;
+int *cs_di_pinv (const int *p, int n) ;
+int cs_di_pvec (const int *p, const double *b, double *x, int n) ;
+cs_din *cs_di_qr (const cs_di *A, const cs_dis *S) ;
+cs_dis *cs_di_schol (int order, const cs_di *A) ;
+cs_dis *cs_di_sqr (int order, const cs_di *A, int qr) ;
+cs_di *cs_di_symperm (const cs_di *A, const int *pinv, int values) ;
+int cs_di_usolve (const cs_di *U, double *x) ;
+int cs_di_utsolve (const cs_di *U, double *x) ;
+int cs_di_updown (cs_di *L, int sigma, const cs_di *C, const int *parent) ;
+
+/* utilities */
+cs_dis *cs_di_sfree (cs_dis *S) ;
+cs_din *cs_di_nfree (cs_din *N) ;
+cs_did *cs_di_dfree (cs_did *D) ;
+
+/* --- tertiary CSparse routines -------------------------------------------- */
+
+int *cs_di_counts (const cs_di *A, const int *parent, const int *post,
+    int ata) ;
+double cs_di_cumsum (int *p, int *c, int n) ;
+int cs_di_dfs (int j, cs_di *G, int top, int *xi, int *pstack,
+    const int *pinv) ;
+int *cs_di_etree (const cs_di *A, int ata) ;
+int cs_di_fkeep (cs_di *A, int (*fkeep) (int, int, double, void *),
+    void *other) ;
+double cs_di_house (double *x, double *beta, int n) ;
+int *cs_di_maxtrans (const cs_di *A, int seed) ;
+int *cs_di_post (const int *parent, int n) ;
+cs_did *cs_di_scc (cs_di *A) ;
+int cs_di_scatter (const cs_di *A, int j, double beta, int *w, double *x,
+    int mark, cs_di *C, int nz) ;
+int cs_di_tdfs (int j, int k, int *head, const int *next, int *post,
+    int *stack) ;
+int cs_di_leaf (int i, int j, const int *first, int *maxfirst, int *prevleaf,
+    int *ancestor, int *jleaf) ;
+int cs_di_reach (cs_di *G, const cs_di *B, int k, int *xi, const int *pinv) ;
+int cs_di_spsolve (cs_di *L, const cs_di *B, int k, int *xi, double *x,
+    const int *pinv, int lo) ;
+int cs_di_ereach (const cs_di *A, int k, const int *parent, int *s, int *w) ;
+int *cs_di_randperm (int n, int seed) ;
+
+/* utilities */
+cs_did *cs_di_dalloc (int m, int n) ;
+cs_di *cs_di_done (cs_di *C, void *w, void *x, int ok) ;
+int *cs_di_idone (int *p, cs_di *C, void *w, int ok) ;
+cs_din *cs_di_ndone (cs_din *N, cs_di *C, void *w, void *x, int ok) ;
+cs_did *cs_di_ddone (cs_did *D, cs_di *C, void *w, int ok) ;
+
+
+/* -------------------------------------------------------------------------- */
+/* double/UF_long version of CXSparse */
+/* -------------------------------------------------------------------------- */
+
+/* --- primary CSparse routines and data structures ------------------------- */
+
+typedef struct cs_dl_sparse  /* matrix in compressed-column or triplet form */
+{
+    UF_long nzmax ; /* maximum number of entries */
+    UF_long m ;     /* number of rows */
+    UF_long n ;     /* number of columns */
+    UF_long *p ;    /* column pointers (size n+1) or col indlces (size nzmax) */
+    UF_long *i ;    /* row indices, size nzmax */
+    double *x ;     /* numerical values, size nzmax */
+    UF_long nz ;    /* # of entries in triplet matrix, -1 for compressed-col */
+} cs_dl ;
+
+cs_dl *cs_dl_add (const cs_dl *A, const cs_dl *B, double alpha, double beta) ;
+UF_long cs_dl_cholsol (UF_long order, const cs_dl *A, double *b) ;
+UF_long cs_dl_dupl (cs_dl *A) ;
+UF_long cs_dl_entry (cs_dl *T, UF_long i, UF_long j, double x) ;
+UF_long cs_dl_lusol (UF_long order, const cs_dl *A, double *b, double tol) ;
+UF_long cs_dl_gaxpy (const cs_dl *A, const double *x, double *y) ;
+cs_dl *cs_dl_multiply (const cs_dl *A, const cs_dl *B) ;
+UF_long cs_dl_qrsol (UF_long order, const cs_dl *A, double *b) ;
+cs_dl *cs_dl_transpose (const cs_dl *A, UF_long values) ;
+cs_dl *cs_dl_compress (const cs_dl *T) ;
+double cs_dl_norm (const cs_dl *A) ;
+UF_long cs_dl_print (const cs_dl *A, UF_long brief) ;
+cs_dl *cs_dl_load (FILE *f) ;
+
+/* utilities */
+void *cs_dl_calloc (UF_long n, size_t size) ;
+void *cs_dl_free (void *p) ;
+void *cs_dl_realloc (void *p, UF_long n, size_t size, UF_long *ok) ;
+cs_dl *cs_dl_spalloc (UF_long m, UF_long n, UF_long nzmax, UF_long values,
+    UF_long t) ;
+cs_dl *cs_dl_spfree (cs_dl *A) ;
+UF_long cs_dl_sprealloc (cs_dl *A, UF_long nzmax) ;
+void *cs_dl_malloc (UF_long n, size_t size) ;
+
+/* --- secondary CSparse routines and data structures ----------------------- */
+
+typedef struct cs_dl_symbolic  /* symbolic Cholesky, LU, or QR analysis */
+{
+    UF_long *pinv ;     /* inverse row perm. for QR, fill red. perm for Chol */
+    UF_long *q ;        /* fill-reducing column permutation for LU and QR */
+    UF_long *parent ;   /* elimination tree for Cholesky and QR */
+    UF_long *cp ;       /* column pointers for Cholesky, row counts for QR */
+    UF_long *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */
+    UF_long m2 ;        /* # of rows for QR, after adding fictitious rows */
+    double lnz ;        /* # entries in L for LU or Cholesky; in V for QR */
+    double unz ;        /* # entries in U for LU; in R for QR */
+} cs_dls ;
+
+typedef struct cs_dl_numeric   /* numeric Cholesky, LU, or QR factorization */
+{
+    cs_dl *L ;      /* L for LU and Cholesky, V for QR */
+    cs_dl *U ;      /* U for LU, r for QR, not used for Cholesky */
+    UF_long *pinv ; /* partial pivoting for LU */
+    double *B ;     /* beta [0..n-1] for QR */
+} cs_dln ;
+
+typedef struct cs_dl_dmperm_results    /* cs_dl_dmperm or cs_dl_scc output */
+{
+    UF_long *p ;    /* size m, row permutation */
+    UF_long *q ;    /* size n, column permutation */
+    UF_long *r ;    /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */
+    UF_long *s ;    /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */
+    UF_long nb ;    /* # of blocks in fine dmperm decomposition */
+    UF_long rr [5] ;    /* coarse row decomposition */
+    UF_long cc [5] ;    /* coarse column decomposition */
+} cs_dld ;
+
+UF_long *cs_dl_amd (UF_long order, const cs_dl *A) ;
+cs_dln *cs_dl_chol (const cs_dl *A, const cs_dls *S) ;
+cs_dld *cs_dl_dmperm (const cs_dl *A, UF_long seed) ;
+UF_long cs_dl_droptol (cs_dl *A, double tol) ;
+UF_long cs_dl_dropzeros (cs_dl *A) ;
+UF_long cs_dl_happly (const cs_dl *V, UF_long i, double beta, double *x) ;
+UF_long cs_dl_ipvec (const UF_long *p, const double *b, double *x, UF_long n) ;
+UF_long cs_dl_lsolve (const cs_dl *L, double *x) ;
+UF_long cs_dl_ltsolve (const cs_dl *L, double *x) ;
+cs_dln *cs_dl_lu (const cs_dl *A, const cs_dls *S, double tol) ;
+cs_dl *cs_dl_permute (const cs_dl *A, const UF_long *pinv, const UF_long *q,
+    UF_long values) ;
+UF_long *cs_dl_pinv (const UF_long *p, UF_long n) ;
+UF_long cs_dl_pvec (const UF_long *p, const double *b, double *x, UF_long n) ;
+cs_dln *cs_dl_qr (const cs_dl *A, const cs_dls *S) ;
+cs_dls *cs_dl_schol (UF_long order, const cs_dl *A) ;
+cs_dls *cs_dl_sqr (UF_long order, const cs_dl *A, UF_long qr) ;
+cs_dl *cs_dl_symperm (const cs_dl *A, const UF_long *pinv, UF_long values) ;
+UF_long cs_dl_usolve (const cs_dl *U, double *x) ;
+UF_long cs_dl_utsolve (const cs_dl *U, double *x) ;
+UF_long cs_dl_updown (cs_dl *L, UF_long sigma, const cs_dl *C,
+    const UF_long *parent) ;
+
+/* utilities */
+cs_dls *cs_dl_sfree (cs_dls *S) ;
+cs_dln *cs_dl_nfree (cs_dln *N) ;
+cs_dld *cs_dl_dfree (cs_dld *D) ;
+
+/* --- tertiary CSparse routines -------------------------------------------- */
+
+UF_long *cs_dl_counts (const cs_dl *A, const UF_long *parent,
+    const UF_long *post, UF_long ata) ;
+double cs_dl_cumsum (UF_long *p, UF_long *c, UF_long n) ;
+UF_long cs_dl_dfs (UF_long j, cs_dl *G, UF_long top, UF_long *xi,
+    UF_long *pstack, const UF_long *pinv) ;
+UF_long *cs_dl_etree (const cs_dl *A, UF_long ata) ;
+UF_long cs_dl_fkeep (cs_dl *A,
+    UF_long (*fkeep) (UF_long, UF_long, double, void *), void *other) ;
+double cs_dl_house (double *x, double *beta, UF_long n) ;
+UF_long *cs_dl_maxtrans (const cs_dl *A, UF_long seed) ;
+UF_long *cs_dl_post (const UF_long *parent, UF_long n) ;
+cs_dld *cs_dl_scc (cs_dl *A) ;
+UF_long cs_dl_scatter (const cs_dl *A, UF_long j, double beta, UF_long *w,
+    double *x, UF_long mark,cs_dl *C, UF_long nz) ;
+UF_long cs_dl_tdfs (UF_long j, UF_long k, UF_long *head, const UF_long *next,
+    UF_long *post, UF_long *stack) ;
+UF_long cs_dl_leaf (UF_long i, UF_long j, const UF_long *first,
+    UF_long *maxfirst, UF_long *prevleaf, UF_long *ancestor, UF_long *jleaf) ;
+UF_long cs_dl_reach (cs_dl *G, const cs_dl *B, UF_long k, UF_long *xi,
+    const UF_long *pinv) ;
+UF_long cs_dl_spsolve (cs_dl *L, const cs_dl *B, UF_long k, UF_long *xi,
+    double *x, const UF_long *pinv, UF_long lo) ;
+UF_long cs_dl_ereach (const cs_dl *A, UF_long k, const UF_long *parent,
+    UF_long *s, UF_long *w) ;
+UF_long *cs_dl_randperm (UF_long n, UF_long seed) ;
+
+/* utilities */
+cs_dld *cs_dl_dalloc (UF_long m, UF_long n) ;
+cs_dl *cs_dl_done (cs_dl *C, void *w, void *x, UF_long ok) ;
+UF_long *cs_dl_idone (UF_long *p, cs_dl *C, void *w, UF_long ok) ;
+cs_dln *cs_dl_ndone (cs_dln *N, cs_dl *C, void *w, void *x, UF_long ok) ;
+cs_dld *cs_dl_ddone (cs_dld *D, cs_dl *C, void *w, UF_long ok) ;
+
+
+/* -------------------------------------------------------------------------- */
+/* complex/int version of CXSparse */
+/* -------------------------------------------------------------------------- */
+
+#ifndef NCOMPLEX
+
+/* --- primary CSparse routines and data structures ------------------------- */
+
+typedef struct cs_ci_sparse  /* matrix in compressed-column or triplet form */
+{
+    int nzmax ;     /* maximum number of entries */
+    int m ;         /* number of rows */
+    int n ;         /* number of columns */
+    int *p ;        /* column pointers (size n+1) or col indices (size nzmax) */
+    int *i ;        /* row indices, size nzmax */
+    cs_complex_t *x ;    /* numerical values, size nzmax */
+    int nz ;        /* # of entries in triplet matrix, -1 for compressed-col */
+} cs_ci ;
+
+cs_ci *cs_ci_add (const cs_ci *A, const cs_ci *B, cs_complex_t alpha,
+    cs_complex_t beta) ;
+int cs_ci_cholsol (int order, const cs_ci *A, cs_complex_t *b) ;
+int cs_ci_dupl (cs_ci *A) ;
+int cs_ci_entry (cs_ci *T, int i, int j, cs_complex_t x) ;
+int cs_ci_lusol (int order, const cs_ci *A, cs_complex_t *b, double tol) ;
+int cs_ci_gaxpy (const cs_ci *A, const cs_complex_t *x, cs_complex_t *y) ;
+cs_ci *cs_ci_multiply (const cs_ci *A, const cs_ci *B) ;
+int cs_ci_qrsol (int order, const cs_ci *A, cs_complex_t *b) ;
+cs_ci *cs_ci_transpose (const cs_ci *A, int values) ;
+cs_ci *cs_ci_compress (const cs_ci *T) ;
+double cs_ci_norm (const cs_ci *A) ;
+int cs_ci_print (const cs_ci *A, int brief) ;
+cs_ci *cs_ci_load (FILE *f) ;
+
+/* utilities */
+void *cs_ci_calloc (int n, size_t size) ;
+void *cs_ci_free (void *p) ;
+void *cs_ci_realloc (void *p, int n, size_t size, int *ok) ;
+cs_ci *cs_ci_spalloc (int m, int n, int nzmax, int values, int t) ;
+cs_ci *cs_ci_spfree (cs_ci *A) ;
+int cs_ci_sprealloc (cs_ci *A, int nzmax) ;
+void *cs_ci_malloc (int n, size_t size) ;
+
+/* --- secondary CSparse routines and data structures ----------------------- */
+
+typedef struct cs_ci_symbolic  /* symbolic Cholesky, LU, or QR analysis */
+{
+    int *pinv ;     /* inverse row perm. for QR, fill red. perm for Chol */
+    int *q ;        /* fill-reducing column permutation for LU and QR */
+    int *parent ;   /* elimination tree for Cholesky and QR */
+    int *cp ;       /* column pointers for Cholesky, row counts for QR */
+    int *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */
+    int m2 ;        /* # of rows for QR, after adding fictitious rows */
+    double lnz ;    /* # entries in L for LU or Cholesky; in V for QR */
+    double unz ;    /* # entries in U for LU; in R for QR */
+} cs_cis ;
+
+typedef struct cs_ci_numeric   /* numeric Cholesky, LU, or QR factorization */
+{
+    cs_ci *L ;      /* L for LU and Cholesky, V for QR */
+    cs_ci *U ;      /* U for LU, r for QR, not used for Cholesky */
+    int *pinv ;     /* partial pivoting for LU */
+    double *B ;     /* beta [0..n-1] for QR */
+} cs_cin ;
+
+typedef struct cs_ci_dmperm_results    /* cs_ci_dmperm or cs_ci_scc output */
+{
+    int *p ;        /* size m, row permutation */
+    int *q ;        /* size n, column permutation */
+    int *r ;        /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */
+    int *s ;        /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */
+    int nb ;        /* # of blocks in fine dmperm decomposition */
+    int rr [5] ;    /* coarse row decomposition */
+    int cc [5] ;    /* coarse column decomposition */
+} cs_cid ;
+
+int *cs_ci_amd (int order, const cs_ci *A) ;
+cs_cin *cs_ci_chol (const cs_ci *A, const cs_cis *S) ;
+cs_cid *cs_ci_dmperm (const cs_ci *A, int seed) ;
+int cs_ci_droptol (cs_ci *A, double tol) ;
+int cs_ci_dropzeros (cs_ci *A) ;
+int cs_ci_happly (const cs_ci *V, int i, double beta, cs_complex_t *x) ;
+int cs_ci_ipvec (const int *p, const cs_complex_t *b, cs_complex_t *x, int n) ;
+int cs_ci_lsolve (const cs_ci *L, cs_complex_t *x) ;
+int cs_ci_ltsolve (const cs_ci *L, cs_complex_t *x) ;
+cs_cin *cs_ci_lu (const cs_ci *A, const cs_cis *S, double tol) ;
+cs_ci *cs_ci_permute (const cs_ci *A, const int *pinv, const int *q,
+    int values) ;
+int *cs_ci_pinv (const int *p, int n) ;
+int cs_ci_pvec (const int *p, const cs_complex_t *b, cs_complex_t *x, int n) ;
+cs_cin *cs_ci_qr (const cs_ci *A, const cs_cis *S) ;
+cs_cis *cs_ci_schol (int order, const cs_ci *A) ;
+cs_cis *cs_ci_sqr (int order, const cs_ci *A, int qr) ;
+cs_ci *cs_ci_symperm (const cs_ci *A, const int *pinv, int values) ;
+int cs_ci_usolve (const cs_ci *U, cs_complex_t *x) ;
+int cs_ci_utsolve (const cs_ci *U, cs_complex_t *x) ;
+int cs_ci_updown (cs_ci *L, int sigma, const cs_ci *C, const int *parent) ;
+
+/* utilities */
+cs_cis *cs_ci_sfree (cs_cis *S) ;
+cs_cin *cs_ci_nfree (cs_cin *N) ;
+cs_cid *cs_ci_dfree (cs_cid *D) ;
+
+/* --- tertiary CSparse routines -------------------------------------------- */
+
+int *cs_ci_counts (const cs_ci *A, const int *parent, const int *post,
+    int ata) ;
+double cs_ci_cumsum (int *p, int *c, int n) ;
+int cs_ci_dfs (int j, cs_ci *G, int top, int *xi, int *pstack,
+    const int *pinv) ;
+int *cs_ci_etree (const cs_ci *A, int ata) ;
+int cs_ci_fkeep (cs_ci *A, int (*fkeep) (int, int, cs_complex_t, void *),
+    void *other) ;
+cs_complex_t cs_ci_house (cs_complex_t *x, double *beta, int n) ;
+int *cs_ci_maxtrans (const cs_ci *A, int seed) ;
+int *cs_ci_post (const int *parent, int n) ;
+cs_cid *cs_ci_scc (cs_ci *A) ;
+int cs_ci_scatter (const cs_ci *A, int j, cs_complex_t beta, int *w, 
+    cs_complex_t *x, int mark,cs_ci *C, int nz) ;
+int cs_ci_tdfs (int j, int k, int *head, const int *next, int *post,
+    int *stack) ;
+int cs_ci_leaf (int i, int j, const int *first, int *maxfirst, int *prevleaf,
+    int *ancestor, int *jleaf) ;
+int cs_ci_reach (cs_ci *G, const cs_ci *B, int k, int *xi, const int *pinv) ;
+int cs_ci_spsolve (cs_ci *L, const cs_ci *B, int k, int *xi, 
+    cs_complex_t *x, const int *pinv, int lo) ;
+int cs_ci_ereach (const cs_ci *A, int k, const int *parent, int *s, int *w) ;
+int *cs_ci_randperm (int n, int seed) ;
+
+/* utilities */
+cs_cid *cs_ci_dalloc (int m, int n) ;
+cs_ci *cs_ci_done (cs_ci *C, void *w, void *x, int ok) ;
+int *cs_ci_idone (int *p, cs_ci *C, void *w, int ok) ;
+cs_cin *cs_ci_ndone (cs_cin *N, cs_ci *C, void *w, void *x, int ok) ;
+cs_cid *cs_ci_ddone (cs_cid *D, cs_ci *C, void *w, int ok) ;
+
+
+/* -------------------------------------------------------------------------- */
+/* complex/UF_long version of CXSparse */
+/* -------------------------------------------------------------------------- */
+
+/* --- primary CSparse routines and data structures ------------------------- */
+
+typedef struct cs_cl_sparse  /* matrix in compressed-column or triplet form */
+{
+    UF_long nzmax ; /* maximum number of entries */
+    UF_long m ;     /* number of rows */
+    UF_long n ;     /* number of columns */
+    UF_long *p ;    /* column pointers (size n+1) or col indlces (size nzmax) */
+    UF_long *i ;    /* row indices, size nzmax */
+    cs_complex_t *x ;    /* numerical values, size nzmax */
+    UF_long nz ;    /* # of entries in triplet matrix, -1 for compressed-col */
+} cs_cl ;
+
+cs_cl *cs_cl_add (const cs_cl *A, const cs_cl *B, cs_complex_t alpha,
+    cs_complex_t beta) ;
+UF_long cs_cl_cholsol (UF_long order, const cs_cl *A, cs_complex_t *b) ;
+UF_long cs_cl_dupl (cs_cl *A) ;
+UF_long cs_cl_entry (cs_cl *T, UF_long i, UF_long j, cs_complex_t x) ;
+UF_long cs_cl_lusol (UF_long order, const cs_cl *A, cs_complex_t *b,
+    double tol) ;
+UF_long cs_cl_gaxpy (const cs_cl *A, const cs_complex_t *x, cs_complex_t *y) ;
+cs_cl *cs_cl_multiply (const cs_cl *A, const cs_cl *B) ;
+UF_long cs_cl_qrsol (UF_long order, const cs_cl *A, cs_complex_t *b) ;
+cs_cl *cs_cl_transpose (const cs_cl *A, UF_long values) ;
+cs_cl *cs_cl_compress (const cs_cl *T) ;
+double cs_cl_norm (const cs_cl *A) ;
+UF_long cs_cl_print (const cs_cl *A, UF_long brief) ;
+cs_cl *cs_cl_load (FILE *f) ;
+
+/* utilities */
+void *cs_cl_calloc (UF_long n, size_t size) ;
+void *cs_cl_free (void *p) ;
+void *cs_cl_realloc (void *p, UF_long n, size_t size, UF_long *ok) ;
+cs_cl *cs_cl_spalloc (UF_long m, UF_long n, UF_long nzmax, UF_long values,
+    UF_long t) ;
+cs_cl *cs_cl_spfree (cs_cl *A) ;
+UF_long cs_cl_sprealloc (cs_cl *A, UF_long nzmax) ;
+void *cs_cl_malloc (UF_long n, size_t size) ;
+
+/* --- secondary CSparse routines and data structures ----------------------- */
+
+typedef struct cs_cl_symbolic  /* symbolic Cholesky, LU, or QR analysis */
+{
+    UF_long *pinv ;     /* inverse row perm. for QR, fill red. perm for Chol */
+    UF_long *q ;        /* fill-reducing column permutation for LU and QR */
+    UF_long *parent ;   /* elimination tree for Cholesky and QR */
+    UF_long *cp ;       /* column pointers for Cholesky, row counts for QR */
+    UF_long *leftmost ; /* leftmost[i] = min(find(A(i,:))), for QR */
+    UF_long m2 ;        /* # of rows for QR, after adding fictitious rows */
+    double lnz ;        /* # entries in L for LU or Cholesky; in V for QR */
+    double unz ;        /* # entries in U for LU; in R for QR */
+} cs_cls ;
+
+typedef struct cs_cl_numeric   /* numeric Cholesky, LU, or QR factorization */
+{
+    cs_cl *L ;          /* L for LU and Cholesky, V for QR */
+    cs_cl *U ;          /* U for LU, r for QR, not used for Cholesky */
+    UF_long *pinv ;     /* partial pivoting for LU */
+    double *B ;         /* beta [0..n-1] for QR */
+} cs_cln ;
+
+typedef struct cs_cl_dmperm_results    /* cs_cl_dmperm or cs_cl_scc output */
+{
+    UF_long *p ;    /* size m, row permutation */
+    UF_long *q ;    /* size n, column permutation */
+    UF_long *r ;    /* size nb+1, block k is rows r[k] to r[k+1]-1 in A(p,q) */
+    UF_long *s ;    /* size nb+1, block k is cols s[k] to s[k+1]-1 in A(p,q) */
+    UF_long nb ;    /* # of blocks in fine dmperm decomposition */
+    UF_long rr [5] ;   /* coarse row decomposition */
+    UF_long cc [5] ;   /* coarse column decomposition */
+} cs_cld ;
+
+UF_long *cs_cl_amd (UF_long order, const cs_cl *A) ;
+cs_cln *cs_cl_chol (const cs_cl *A, const cs_cls *S) ;
+cs_cld *cs_cl_dmperm (const cs_cl *A, UF_long seed) ;
+UF_long cs_cl_droptol (cs_cl *A, double tol) ;
+UF_long cs_cl_dropzeros (cs_cl *A) ;
+UF_long cs_cl_happly (const cs_cl *V, UF_long i, double beta, cs_complex_t *x) ;
+UF_long cs_cl_ipvec (const UF_long *p, const cs_complex_t *b,
+    cs_complex_t *x, UF_long n) ;
+UF_long cs_cl_lsolve (const cs_cl *L, cs_complex_t *x) ;
+UF_long cs_cl_ltsolve (const cs_cl *L, cs_complex_t *x) ;
+cs_cln *cs_cl_lu (const cs_cl *A, const cs_cls *S, double tol) ;
+cs_cl *cs_cl_permute (const cs_cl *A, const UF_long *pinv, const UF_long *q,
+    UF_long values) ;
+UF_long *cs_cl_pinv (const UF_long *p, UF_long n) ;
+UF_long cs_cl_pvec (const UF_long *p, const cs_complex_t *b,
+    cs_complex_t *x, UF_long n) ;
+cs_cln *cs_cl_qr (const cs_cl *A, const cs_cls *S) ;
+cs_cls *cs_cl_schol (UF_long order, const cs_cl *A) ;
+cs_cls *cs_cl_sqr (UF_long order, const cs_cl *A, UF_long qr) ;
+cs_cl *cs_cl_symperm (const cs_cl *A, const UF_long *pinv, UF_long values) ;
+UF_long cs_cl_usolve (const cs_cl *U, cs_complex_t *x) ;
+UF_long cs_cl_utsolve (const cs_cl *U, cs_complex_t *x) ;
+UF_long cs_cl_updown (cs_cl *L, UF_long sigma, const cs_cl *C,
+    const UF_long *parent) ;
+
+/* utilities */
+cs_cls *cs_cl_sfree (cs_cls *S) ;
+cs_cln *cs_cl_nfree (cs_cln *N) ;
+cs_cld *cs_cl_dfree (cs_cld *D) ;
+
+/* --- tertiary CSparse routines -------------------------------------------- */
+
+UF_long *cs_cl_counts (const cs_cl *A, const UF_long *parent,
+    const UF_long *post, UF_long ata) ;
+double cs_cl_cumsum (UF_long *p, UF_long *c, UF_long n) ;
+UF_long cs_cl_dfs (UF_long j, cs_cl *G, UF_long top, UF_long *xi,
+    UF_long *pstack, const UF_long *pinv) ;
+UF_long *cs_cl_etree (const cs_cl *A, UF_long ata) ;
+UF_long cs_cl_fkeep (cs_cl *A,
+    UF_long (*fkeep) (UF_long, UF_long, cs_complex_t, void *), void *other) ;
+cs_complex_t cs_cl_house (cs_complex_t *x, double *beta, UF_long n) ;
+UF_long *cs_cl_maxtrans (const cs_cl *A, UF_long seed) ;
+UF_long *cs_cl_post (const UF_long *parent, UF_long n) ;
+cs_cld *cs_cl_scc (cs_cl *A) ;
+UF_long cs_cl_scatter (const cs_cl *A, UF_long j, cs_complex_t beta,
+    UF_long *w, cs_complex_t *x, UF_long mark,cs_cl *C, UF_long nz) ;
+UF_long cs_cl_tdfs (UF_long j, UF_long k, UF_long *head, const UF_long *next,
+    UF_long *post, UF_long *stack) ;
+UF_long cs_cl_leaf (UF_long i, UF_long j, const UF_long *first,
+    UF_long *maxfirst, UF_long *prevleaf, UF_long *ancestor, UF_long *jleaf) ;
+UF_long cs_cl_reach (cs_cl *G, const cs_cl *B, UF_long k, UF_long *xi,
+    const UF_long *pinv) ;
+UF_long cs_cl_spsolve (cs_cl *L, const cs_cl *B, UF_long k, UF_long *xi, 
+    cs_complex_t *x, const UF_long *pinv, UF_long lo) ;
+UF_long cs_cl_ereach (const cs_cl *A, UF_long k, const UF_long *parent,
+    UF_long *s, UF_long *w) ;
+UF_long *cs_cl_randperm (UF_long n, UF_long seed) ;
+
+/* utilities */
+cs_cld *cs_cl_dalloc (UF_long m, UF_long n) ;
+cs_cl *cs_cl_done (cs_cl *C, void *w, void *x, UF_long ok) ;
+UF_long *cs_cl_idone (UF_long *p, cs_cl *C, void *w, UF_long ok) ;
+cs_cln *cs_cl_ndone (cs_cln *N, cs_cl *C, void *w, void *x, UF_long ok) ;
+cs_cld *cs_cl_ddone (cs_cld *D, cs_cl *C, void *w, UF_long ok) ;
+
+#endif
+
+/* -------------------------------------------------------------------------- */
+/* Macros for constructing each version of CSparse */
+/* -------------------------------------------------------------------------- */
+
+#ifdef CS_LONG
+#define CS_INT UF_long
+#define CS_INT_MAX UF_long_max
+#define CS_ID UF_long_id
+#ifdef CS_COMPLEX
+#define CS_ENTRY cs_complex_t
+#define CS_NAME(nm) cs_cl ## nm
+#define cs cs_cl
+#else
+#define CS_ENTRY double
+#define CS_NAME(nm) cs_dl ## nm
+#define cs cs_dl
+#endif
+#else
+#define CS_INT int
+#define CS_INT_MAX INT_MAX
+#define CS_ID "%d"
+#ifdef CS_COMPLEX
+#define CS_ENTRY cs_complex_t
+#define CS_NAME(nm) cs_ci ## nm
+#define cs cs_ci
+#else
+#define CS_ENTRY double
+#define CS_NAME(nm) cs_di ## nm
+#define cs cs_di
+#endif
+#endif
+
+#ifdef CS_COMPLEX
+#define CS_REAL(x) creal(x)
+#define CS_IMAG(x) cimag(x)
+#define CS_CONJ(x) conj(x)
+#define CS_ABS(x) cabs(x)
+#else
+#define CS_REAL(x) (x)
+#define CS_IMAG(x) (0.)
+#define CS_CONJ(x) (x)
+#define CS_ABS(x) fabs(x)
+#endif
+
+#define CS_MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define CS_MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define CS_FLIP(i) (-(i)-2)
+#define CS_UNFLIP(i) (((i) < 0) ? CS_FLIP(i) : (i))
+#define CS_MARKED(w,j) (w [j] < 0)
+#define CS_MARK(w,j) { w [j] = CS_FLIP (w [j]) ; }
+#define CS_CSC(A) (A && (A->nz == -1))
+#define CS_TRIPLET(A) (A && (A->nz >= 0))
+
+/* --- primary CSparse routines and data structures ------------------------- */
+
+#define cs_add CS_NAME (_add)
+#define cs_cholsol CS_NAME (_cholsol)
+#define cs_dupl CS_NAME (_dupl)
+#define cs_entry CS_NAME (_entry)
+#define cs_lusol CS_NAME (_lusol)
+#define cs_gaxpy CS_NAME (_gaxpy)
+#define cs_multiply CS_NAME (_multiply)
+#define cs_qrsol CS_NAME (_qrsol)
+#define cs_transpose CS_NAME (_transpose)
+#define cs_compress CS_NAME (_compress)
+#define cs_norm CS_NAME (_norm)
+#define cs_print CS_NAME (_print)
+#define cs_load CS_NAME (_load)
+
+/* utilities */
+#define cs_calloc CS_NAME (_calloc)
+#define cs_free CS_NAME (_free)
+#define cs_realloc CS_NAME (_realloc)
+#define cs_spalloc CS_NAME (_spalloc)
+#define cs_spfree CS_NAME (_spfree)
+#define cs_sprealloc CS_NAME (_sprealloc)
+#define cs_malloc CS_NAME (_malloc)
+
+/* --- secondary CSparse routines and data structures ----------------------- */
+#define css CS_NAME (s)
+#define csn CS_NAME (n)
+#define csd CS_NAME (d)
+
+#define cs_amd CS_NAME (_amd)
+#define cs_chol CS_NAME (_chol)
+#define cs_dmperm CS_NAME (_dmperm)
+#define cs_droptol CS_NAME (_droptol)
+#define cs_dropzeros CS_NAME (_dropzeros)
+#define cs_happly CS_NAME (_happly)
+#define cs_ipvec CS_NAME (_ipvec)
+#define cs_lsolve CS_NAME (_lsolve)
+#define cs_ltsolve CS_NAME (_ltsolve)
+#define cs_lu CS_NAME (_lu)
+#define cs_permute CS_NAME (_permute)
+#define cs_pinv CS_NAME (_pinv)
+#define cs_pvec CS_NAME (_pvec)
+#define cs_qr CS_NAME (_qr)
+#define cs_schol CS_NAME (_schol)
+#define cs_sqr CS_NAME (_sqr)
+#define cs_symperm CS_NAME (_symperm)
+#define cs_usolve CS_NAME (_usolve)
+#define cs_utsolve CS_NAME (_utsolve)
+#define cs_updown CS_NAME (_updown)
+
+/* utilities */
+#define cs_sfree CS_NAME (_sfree)
+#define cs_nfree CS_NAME (_nfree)
+#define cs_dfree CS_NAME (_dfree)
+
+/* --- tertiary CSparse routines -------------------------------------------- */
+#define cs_counts CS_NAME (_counts)
+#define cs_cumsum CS_NAME (_cumsum)
+#define cs_dfs CS_NAME (_dfs)
+#define cs_etree CS_NAME (_etree)
+#define cs_fkeep CS_NAME (_fkeep)
+#define cs_house CS_NAME (_house)
+#define cs_invmatch CS_NAME (_invmatch)
+#define cs_maxtrans CS_NAME (_maxtrans)
+#define cs_post CS_NAME (_post)
+#define cs_scc CS_NAME (_scc)
+#define cs_scatter CS_NAME (_scatter)
+#define cs_tdfs CS_NAME (_tdfs)
+#define cs_reach CS_NAME (_reach)
+#define cs_spsolve CS_NAME (_spsolve)
+#define cs_ereach CS_NAME (_ereach)
+#define cs_randperm CS_NAME (_randperm)
+#define cs_leaf CS_NAME (_leaf)
+
+/* utilities */
+#define cs_dalloc CS_NAME (_dalloc)
+#define cs_done CS_NAME (_done)
+#define cs_idone CS_NAME (_idone)
+#define cs_ndone CS_NAME (_ndone)
+#define cs_ddone CS_NAME (_ddone)
+
+/* -------------------------------------------------------------------------- */
+/* Conversion routines */
+/* -------------------------------------------------------------------------- */
+
+#ifndef NCOMPLEX
+cs_di *cs_i_real (cs_ci *A, int real) ;
+cs_ci *cs_i_complex (cs_di *A, int real) ;
+cs_dl *cs_l_real (cs_cl *A, UF_long real) ;
+cs_cl *cs_l_complex (cs_dl *A, UF_long real) ;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/igraph/include/dqueue.pmt b/igraph/include/dqueue.pmt
new file mode 100644
--- /dev/null
+++ b/igraph/include/dqueue.pmt
@@ -0,0 +1,384 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+
+/**
+ * \section igraph_dqueue
+ * <para>
+ * This is the classic data type of the double ended queue. Most of
+ * the time it is used if a First-In-First-Out (FIFO) behavior is
+ * needed. See the operations below.
+ * </para>
+ *
+ * <para>
+ * \example examples/simple/dqueue.c
+ * </para>
+ */
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_init
+ * \brief Initialize a double ended queue (deque).
+ *
+ * The queue will be always empty.
+ * \param q Pointer to an uninitialized deque.
+ * \param size How many elements to allocate memory for.
+ * \return Error code.
+ *
+ * Time complexity: O(\p size).
+ */
+
+int FUNCTION(igraph_dqueue, init) (TYPE(igraph_dqueue)* q, long int size) {
+    assert(q != 0);
+    if (size <= 0 ) {
+        size = 1;
+    }
+    q->stor_begin = igraph_Calloc(size, BASE);
+    if (q->stor_begin == 0) {
+        IGRAPH_ERROR("dqueue init failed", IGRAPH_ENOMEM);
+    }
+    q->stor_end = q->stor_begin + size;
+    q->begin = q->stor_begin;
+    q->end = NULL;
+
+    return 0;
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_destroy
+ * \brief Destroy a double ended queue.
+ *
+ * \param q The queue to destroy
+ *
+ * Time complexity: O(1).
+ */
+
+void FUNCTION(igraph_dqueue, destroy) (TYPE(igraph_dqueue)* q) {
+    assert(q != 0);
+    if (q->stor_begin != 0) {
+        igraph_Free(q->stor_begin);
+        q->stor_begin = 0;
+    }
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_empty
+ * \brief Decide whether the queue is empty.
+ *
+ * \param q The queue.
+ * \return Boolean, \c TRUE if \p q contains at least one element, \c
+ * FALSE otherwise.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_bool_t FUNCTION(igraph_dqueue, empty) (const TYPE(igraph_dqueue)* q) {
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    return q->end == NULL;
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_clear
+ * \brief Remove all elements from the queue.
+ *
+ * \param q The queue
+ *
+ * Time complexity: O(1).
+ */
+
+void FUNCTION(igraph_dqueue, clear)   (TYPE(igraph_dqueue)* q) {
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    q->begin = q->stor_begin;
+    q->end = NULL;
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_full
+ * \brief Check whether the queue is full.
+ *
+ * If a queue is full the next igraph_dqueue_push() operation will allocate
+ * more memory.
+ * \param q The queue.
+ * \return \c TRUE if \p q is full, \c FALSE otherwise.
+ *
+ * Time complecity: O(1).
+ */
+
+igraph_bool_t FUNCTION(igraph_dqueue, full) (TYPE(igraph_dqueue)* q) {
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    return q->begin == q->end;
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_size
+ * \brief Number of elements in the queue.
+ *
+ * \param q The queue.
+ * \return Integer, the number of elements currently in the queue.
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_dqueue, size) (const TYPE(igraph_dqueue)* q) {
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    if (q->end == NULL) {
+        return 0;
+    } else if (q->begin < q->end) {
+        return q->end - q->begin;
+    } else {
+        return q->stor_end - q->begin + q->end - q->stor_begin;
+    }
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_head
+ * \brief Head of the queue.
+ *
+ * The queue must contain at least one element.
+ * \param q The queue.
+ * \return The first element in the queue.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_dqueue, head) (const TYPE(igraph_dqueue)* q) {
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    return *(q->begin);
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_back
+ * \brief Tail of the queue.
+ *
+ * The queue must contain at least one element.
+ * \param q The queue.
+ * \return The last element in the queue.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_dqueue, back) (const TYPE(igraph_dqueue)* q) {
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    if (q->end == q->stor_begin) {
+        return *(q->stor_end - 1);
+    }
+    return *(q->end - 1);
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_pop
+ * \brief Remove the head.
+ *
+ * Removes and returns the first element in the queue. The queue must
+ * be non-empty.
+ * \param q The input queue.
+ * \return The first element in the queue.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_dqueue, pop) (TYPE(igraph_dqueue)* q) {
+    BASE tmp = *(q->begin);
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    (q->begin)++;
+    if (q->begin == q->stor_end) {
+        q->begin = q->stor_begin;
+    }
+    if (q->begin == q->end) {
+        q->end = NULL;
+    }
+
+    return tmp;
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_pop_back
+ * \brief Remove the tail
+ *
+ * Removes and returns the last element in the queue. The queue must
+ * be non-empty.
+ * \param q The queue.
+ * \return The last element in the queue.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_dqueue, pop_back) (TYPE(igraph_dqueue)* q) {
+    BASE tmp;
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    if (q->end != q->stor_begin) {
+        tmp = *((q->end) - 1);
+        q->end = (q->end) - 1;
+    } else {
+        tmp = *((q->stor_end) - 1);
+        q->end = (q->stor_end) - 1;
+    }
+    if (q->begin == q->end) {
+        q->end = NULL;
+    }
+
+    return tmp;
+}
+
+/**
+ * \ingroup dqueue
+ * \function igraph_dqueue_push
+ * \brief Appends an element.
+ *
+ * Append an element to the end of the queue.
+ * \param q The queue.
+ * \param elem The element to append.
+ * \return Error code.
+ *
+ * Time complexity: O(1) if no memory allocation is needed, O(n), the
+ * number of elements in the queue otherwise. But not that by
+ * allocating always twice as much memory as the current size of the
+ * queue we ensure that n push operations can always be done in at
+ * most O(n) time. (Assuming memory allocation is at most linear.)
+ */
+
+int FUNCTION(igraph_dqueue, push) (TYPE(igraph_dqueue)* q, BASE elem) {
+    assert(q != 0);
+    assert(q->stor_begin != 0);
+    if (q->begin != q->end) {
+        /* not full */
+        if (q->end == NULL) {
+            q->end = q->begin;
+        }
+        *(q->end) = elem;
+        (q->end)++;
+        if (q->end == q->stor_end) {
+            q->end = q->stor_begin;
+        }
+    } else {
+        /* full, allocate more storage */
+
+        BASE *bigger = NULL, *old = q->stor_begin;
+
+        bigger = igraph_Calloc( 2 * (q->stor_end - q->stor_begin) + 1, BASE );
+        if (bigger == 0) {
+            IGRAPH_ERROR("dqueue push failed", IGRAPH_ENOMEM);
+        }
+
+        if (q->stor_end - q->begin) {
+            memcpy(bigger, q->begin,
+                   (size_t)(q->stor_end - q->begin) * sizeof(BASE));
+        }
+        if (q->end - q->stor_begin > 0) {
+            memcpy(bigger + (q->stor_end - q->begin), q->stor_begin,
+                   (size_t)(q->end - q->stor_begin) * sizeof(BASE));
+        }
+
+        q->end       = bigger + (q->stor_end - q->stor_begin);
+        q->stor_end  = bigger + 2 * (q->stor_end - q->stor_begin) + 1;
+        q->stor_begin = bigger;
+        q->begin     = bigger;
+
+        *(q->end) = elem;
+        (q->end)++;
+        if (q->end == q->stor_end) {
+            q->end = q->stor_begin;
+        }
+
+        igraph_Free(old);
+    }
+
+    return 0;
+}
+
+#if defined (OUT_FORMAT)
+
+#ifndef USING_R
+int FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q) {
+    return FUNCTION(igraph_dqueue, fprint)(q, stdout);
+}
+#endif
+
+int FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file) {
+    if (q->end != NULL) {
+        /* There is one element at least */
+        BASE *p = q->begin;
+        fprintf(file, OUT_FORMAT, *p);
+        p++;
+        if (q->end > q->begin) {
+            /* Q is in one piece */
+            while (p != q->end) {
+                fprintf(file, " " OUT_FORMAT, *p);
+                p++;
+            }
+        } else {
+            /* Q is in two pieces */
+            while (p != q->stor_end) {
+                fprintf(file, " " OUT_FORMAT, *p);
+                p++;
+            }
+            p = q->stor_begin;
+            while (p != q->end) {
+                fprintf(file, " " OUT_FORMAT, *p);
+                p++;
+            }
+        }
+    }
+
+    fprintf(file, "\n");
+
+    return 0;
+}
+
+#endif
+
+BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, long int idx) {
+    if ((q->begin + idx < q->end) ||
+        (q->begin >= q->end && q->begin + idx < q->stor_end)) {
+        return q->begin[idx];
+    } else if (q->begin >= q->end && q->stor_begin + idx < q->end) {
+        idx = idx - (q->stor_end - q->begin);
+        return q->stor_begin[idx];
+    } else {
+        return 0;           /* Error */
+    }
+}
diff --git a/igraph/include/drl_Node.h b/igraph/include/drl_Node.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/drl_Node.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __NODE_H__
+#define __NODE_H__
+
+// The node class contains information about a given node for
+// use by the density server process.
+
+// structure coord used to pass position information between
+// density server and graph class
+
+namespace drl {
+
+class Node {
+
+public:
+
+    bool fixed;   // if true do not change the
+    // position of this node
+    int id;
+
+    float x, y;
+    float sub_x, sub_y;
+    float energy;
+
+public:
+
+    Node( int node_id ) {
+        x = y = 0.0; fixed = false;
+        id = node_id;
+    }
+    ~Node() { }
+
+};
+
+} // namespace drl
+
+#endif //__NODE_H__
diff --git a/igraph/include/drl_Node_3d.h b/igraph/include/drl_Node_3d.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/drl_Node_3d.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __NODE_H__
+#define __NODE_H__
+
+// The node class contains information about a given node for
+// use by the density server process.
+
+// structure coord used to pass position information between
+// density server and graph class
+
+namespace drl3d {
+
+class Node {
+
+public:
+
+    bool fixed;   // if true do not change the
+    // position of this node
+    int id;
+
+    float x, y, z;
+    float sub_x, sub_y, sub_z;
+    float energy;
+
+public:
+
+    Node( int node_id ) {
+        x = y = z = 0.0; fixed = false;
+        id = node_id;
+    }
+    ~Node() { }
+
+};
+
+} // namespace drl3d
+
+#endif //__NODE_H__
diff --git a/igraph/include/drl_graph.h b/igraph/include/drl_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/drl_graph.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// The graph class contains the methods necessary to draw the
+// graph.  It calls on the density server class to obtain
+// position and density information
+
+#include "DensityGrid.h"
+#include "igraph_layout.h"
+
+namespace drl {
+
+// layout schedule information
+struct layout_schedule {
+    int iterations;
+    float temperature;
+    float attraction;
+    float damping_mult;
+    time_t time_elapsed;
+};
+
+class graph {
+
+public:
+
+    // Methods
+    void init_parms ( int rand_seed, float edge_cut, float real_parm );
+    void init_parms ( const igraph_layout_drl_options_t *options );
+    void read_parms ( char *parms_file );
+    void read_real ( char *real_file );
+    int read_real ( const igraph_matrix_t *real_mat,
+                    const igraph_vector_bool_t *fixed);
+    void scan_int ( char *filename );
+    void read_int ( char *file_name );
+    void draw_graph ( int int_out, char *coord_file );
+    int draw_graph (igraph_matrix_t *res);
+    void write_coord ( const char *file_name );
+    void write_sim ( const char *file_name );
+    float get_tot_energy ( );
+
+    // Con/Decon
+    graph( int proc_id, int tot_procs, char *int_file );
+    ~graph( ) { }
+    graph( const igraph_t *igraph,
+           const igraph_layout_drl_options_t *options,
+           const igraph_vector_t *weights);
+
+private:
+
+    // Methods
+    int ReCompute ( );
+    void update_nodes ( );
+    float Compute_Node_Energy ( int node_ind );
+    void Solve_Analytic ( int node_ind, float &pos_x, float &pos_y );
+    void get_positions ( vector<int> &node_indices, float return_positions[2 * MAX_PROCS] );
+    void update_density ( vector<int> &node_indices,
+                          float old_positions[2 * MAX_PROCS],
+                          float new_positions[2 * MAX_PROCS] );
+    void update_node_pos ( int node_ind,
+                           float old_positions[2 * MAX_PROCS],
+                           float new_positions[2 * MAX_PROCS] );
+
+    // MPI information
+    int myid, num_procs;
+
+    // graph decomposition information
+    int num_nodes;                  // number of nodes in graph
+    float highest_sim;              // highest sim for normalization
+    map <int, int> id_catalog;      // id_catalog[file id] = internal id
+    map <int, map <int, float> > neighbors;     // neighbors of nodes on this proc.
+
+    // graph layout information
+    vector<Node> positions;
+    DensityGrid density_server;
+
+    // original VxOrd information
+    int STAGE, iterations;
+    float temperature, attraction, damping_mult;
+    float min_edges, CUT_END, cut_length_end, cut_off_length, cut_rate;
+    bool first_add, fine_first_add, fineDensity;
+
+    // scheduling variables
+    layout_schedule liquid;
+    layout_schedule expansion;
+    layout_schedule cooldown;
+    layout_schedule crunch;
+    layout_schedule simmer;
+
+    // timing statistics
+    time_t start_time, stop_time;
+
+    // online clustering information
+    int real_iterations;    // number of iterations to hold .real input fixed
+    int tot_iterations;
+    int tot_expected_iterations; // for progress bar
+    bool real_fixed;
+};
+
+} // namespace drl
diff --git a/igraph/include/drl_graph_3d.h b/igraph/include/drl_graph_3d.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/drl_graph_3d.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// The graph class contains the methods necessary to draw the
+// graph.  It calls on the density server class to obtain
+// position and density information
+
+#include "DensityGrid_3d.h"
+#include "igraph_layout.h"
+
+namespace drl3d {
+
+// layout schedule information
+struct layout_schedule {
+    int iterations;
+    float temperature;
+    float attraction;
+    float damping_mult;
+    time_t time_elapsed;
+};
+
+class graph {
+
+public:
+
+    // Methods
+    void init_parms ( int rand_seed, float edge_cut, float real_parm );
+    void init_parms ( const igraph_layout_drl_options_t *options );
+    int read_real ( const igraph_matrix_t *real_mat,
+                    const igraph_vector_bool_t *fixed);
+    int draw_graph (igraph_matrix_t *res);
+    float get_tot_energy ( );
+
+    // Con/Decon
+    graph( const igraph_t *igraph,
+           const igraph_layout_drl_options_t *options,
+           const igraph_vector_t *weights);
+    ~graph( ) { }
+
+private:
+
+    // Methods
+    int ReCompute ( );
+    void update_nodes ( );
+    float Compute_Node_Energy ( int node_ind );
+    void Solve_Analytic ( int node_ind, float &pos_x, float &pos_y, float &pos_z );
+    void get_positions ( vector<int> &node_indices, float return_positions[3 * MAX_PROCS] );
+    void update_density ( vector<int> &node_indices,
+                          float old_positions[3 * MAX_PROCS],
+                          float new_positions[3 * MAX_PROCS] );
+    void update_node_pos ( int node_ind,
+                           float old_positions[3 * MAX_PROCS],
+                           float new_positions[3 * MAX_PROCS] );
+
+    // MPI information
+    int myid, num_procs;
+
+    // graph decomposition information
+    int num_nodes;                  // number of nodes in graph
+    float highest_sim;              // highest sim for normalization
+    map <int, int> id_catalog;      // id_catalog[file id] = internal id
+    map <int, map <int, float> > neighbors;     // neighbors of nodes on this proc.
+
+    // graph layout information
+    vector<Node> positions;
+    DensityGrid density_server;
+
+    // original VxOrd information
+    int STAGE, iterations;
+    float temperature, attraction, damping_mult;
+    float min_edges, CUT_END, cut_length_end, cut_off_length, cut_rate;
+    bool first_add, fine_first_add, fineDensity;
+
+    // scheduling variables
+    layout_schedule liquid;
+    layout_schedule expansion;
+    layout_schedule cooldown;
+    layout_schedule crunch;
+    layout_schedule simmer;
+
+    // timing statistics
+    time_t start_time, stop_time;
+
+    // online clustering information
+    int real_iterations;    // number of iterations to hold .real input fixed
+    int tot_iterations;
+    int tot_expected_iterations; // for progress bar
+    bool real_fixed;
+};
+
+} // namespace drl3d
diff --git a/igraph/include/drl_layout.h b/igraph/include/drl_layout.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/drl_layout.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// This file contains compile time parameters which affect the entire
+// DrL program.
+
+#define DRL_VERSION "3.2 5/5/2006"
+
+// compile time parameters for MPI message passing
+#define MAX_PROCS 256      // maximum number of processors
+#define MAX_FILE_NAME 250   // max length of filename
+#define MAX_INT_LENGTH 4   // max length of integer suffix of intermediate .coord file
+
+// Compile time adjustable parameters for the Density grid
+
+#define GRID_SIZE 1000          // size of Density grid
+#define VIEW_SIZE 4000.0        // actual physical size of layout plane
+// these values use more memory but have
+// little effect on performance or layout
+
+#define RADIUS 10               // radius for density fall-off:
+// larger values tends to slow down
+// the program and clump the data
+
+#define HALF_VIEW 2000          // 1/2 of VIEW_SIZE
+#define VIEW_TO_GRID .25        // ratio of GRID_SIZE to VIEW_SIZE
+
+/*
+// original values for VxOrd
+#define GRID_SIZE 400           // size of VxOrd Density grid
+#define VIEW_SIZE 1600.0        // actual physical size of VxOrd plane
+#define RADIUS 10               // radius for density fall-off
+
+#define HALF_VIEW 800           // 1/2 of VIEW_SIZE
+#define VIEW_TO_GRID .25        // ratio of GRID_SIZE to VIEW_SIZE
+*/
diff --git a/igraph/include/drl_layout_3d.h b/igraph/include/drl_layout_3d.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/drl_layout_3d.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// This file contains compile time parameters which affect the entire
+// DrL program.
+
+#define DRL_VERSION "3.2 5/5/2006"
+
+// compile time parameters for MPI message passing
+#define MAX_PROCS 256      // maximum number of processors
+#define MAX_FILE_NAME 250   // max length of filename
+#define MAX_INT_LENGTH 4   // max length of integer suffix of intermediate .coord file
+
+// Compile time adjustable parameters for the Density grid
+
+#define GRID_SIZE 100           // size of Density grid
+#define VIEW_SIZE 250.0     // actual physical size of layout plane
+// these values use more memory but have
+// little effect on performance or layout
+
+#define RADIUS 10               // radius for density fall-off:
+// larger values tends to slow down
+// the program and clump the data
+
+#define HALF_VIEW 125.0         // 1/2 of VIEW_SIZE
+#define VIEW_TO_GRID .4         // ratio of GRID_SIZE to VIEW_SIZE
+
+/*
+// original values for VxOrd
+#define GRID_SIZE 400           // size of VxOrd Density grid
+#define VIEW_SIZE 1600.0        // actual physical size of VxOrd plane
+#define RADIUS 10               // radius for density fall-off
+
+#define HALF_VIEW 800           // 1/2 of VIEW_SIZE
+#define VIEW_TO_GRID .25        // ratio of GRID_SIZE to VIEW_SIZE
+*/
diff --git a/igraph/include/drl_parse.h b/igraph/include/drl_parse.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/drl_parse.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// The parse class contains the methods necessary to parse
+// the command line, print help, and do error checking
+
+#ifdef MUSE_MPI
+    #include <mpi.h>
+#endif
+
+namespace drl {
+
+class parse {
+
+public:
+
+    // Methods
+
+    parse ( int argc, char **argv );
+    ~parse () {}
+
+    // user parameters
+    string sim_file;        // .sim file
+    string coord_file;      // .coord file
+    string parms_file;      // .parms file
+    string real_file;       // .real file
+
+    int rand_seed;      // random seed int >= 0
+    float edge_cut;         // edge cutting real [0,1]
+    int int_out;            // intermediate output, int >= 1
+    int edges_out;                  // true if .edges file is requested
+    int parms_in;           // true if .parms file is to be read
+    float real_in;          // true if .real file is to be read
+
+private:
+
+    void print_syntax ( const char *error_string );
+
+};
+
+} // namespace drl
diff --git a/igraph/include/f2c.h b/igraph/include/f2c.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/f2c.h
@@ -0,0 +1,234 @@
+/* f2c.h  --  Standard Fortran to C header file */
+
+/**  barf  [ba:rf]  2.  "He suggested using FORTRAN, and everybody barfed."
+
+    - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */
+
+#ifndef F2C_INCLUDE
+#define F2C_INCLUDE
+
+#include "igraph_blas_internal.h"
+#include "igraph_lapack_internal.h"
+#include "igraph_arpack_internal.h"
+
+typedef int integer;
+typedef unsigned int uinteger;
+typedef char *address;
+typedef short int shortint;
+typedef float real;
+typedef double doublereal;
+typedef struct {
+    real r, i;
+} f2c_complex;
+typedef struct {
+    doublereal r, i;
+} doublecomplex;
+typedef int logical;
+typedef short int shortlogical;
+typedef char logical1;
+typedef char integer1;
+#ifdef INTEGER_STAR_8   /* Adjust for integer*8. */
+    typedef long longint;       /* system-dependent */
+    typedef unsigned long ulongint; /* system-dependent */
+    #define qbit_clear(a,b) ((a) & ~((ulongint)1 << (b)))
+    #define qbit_set(a,b)   ((a) |  ((ulongint)1 << (b)))
+#endif
+
+#define TRUE_ (1)
+#define FALSE_ (0)
+
+/* Extern is for use with -E */
+#ifndef Extern
+    #define Extern extern
+#endif
+
+/* I/O stuff */
+
+#ifdef f2c_i2
+    /* for -i2 */
+    typedef short flag;
+    typedef short ftnlen;
+    typedef short ftnint;
+#else
+    typedef int flag;
+    typedef int ftnlen;
+    typedef int ftnint;
+#endif
+
+/*external read, write*/
+typedef struct {
+    flag cierr;
+    ftnint ciunit;
+    flag ciend;
+    char *cifmt;
+    ftnint cirec;
+} cilist;
+
+/*internal read, write*/
+typedef struct {
+    flag icierr;
+    char *iciunit;
+    flag iciend;
+    char *icifmt;
+    ftnint icirlen;
+    ftnint icirnum;
+} icilist;
+
+/*open*/
+typedef struct {
+    flag oerr;
+    ftnint ounit;
+    char *ofnm;
+    ftnlen ofnmlen;
+    char *osta;
+    char *oacc;
+    char *ofm;
+    ftnint orl;
+    char *oblnk;
+} olist;
+
+/*close*/
+typedef struct {
+    flag cerr;
+    ftnint cunit;
+    char *csta;
+} cllist;
+
+/*rewind, backspace, endfile*/
+typedef struct {
+    flag aerr;
+    ftnint aunit;
+} alist;
+
+/* inquire */
+typedef struct {
+    flag inerr;
+    ftnint inunit;
+    char *infile;
+    ftnlen infilen;
+    ftnint  *inex;  /*parameters in standard's order*/
+    ftnint  *inopen;
+    ftnint  *innum;
+    ftnint  *innamed;
+    char    *inname;
+    ftnlen  innamlen;
+    char    *inacc;
+    ftnlen  inacclen;
+    char    *inseq;
+    ftnlen  inseqlen;
+    char    *indir;
+    ftnlen  indirlen;
+    char    *infmt;
+    ftnlen  infmtlen;
+    char    *inform;
+    ftnint  informlen;
+    char    *inunf;
+    ftnlen  inunflen;
+    ftnint  *inrecl;
+    ftnint  *innrec;
+    char    *inblank;
+    ftnlen  inblanklen;
+} inlist;
+
+#define VOID void
+
+union Multitype {   /* for multiple entry points */
+    integer1 g;
+    shortint h;
+    integer i;
+    /* longint j; */
+    real r;
+    doublereal d;
+    f2c_complex c;
+    doublecomplex z;
+};
+
+typedef union Multitype Multitype;
+
+/*typedef long int Long;*/  /* No longer used; formerly in Namelist */
+
+struct Vardesc {    /* for Namelist */
+    char *name;
+    char *addr;
+    ftnlen *dims;
+    int  type;
+};
+typedef struct Vardesc Vardesc;
+
+struct Namelist {
+    char *name;
+    Vardesc **vars;
+    int nvars;
+};
+typedef struct Namelist Namelist;
+
+#define abs(x) ((x) >= 0 ? (x) : -(x))
+#define dabs(x) (doublereal)abs(x)
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+#define dmin(a,b) (doublereal)min(a,b)
+#define dmax(a,b) (doublereal)max(a,b)
+#define bit_test(a,b)   ((a) >> (b) & 1)
+#define bit_clear(a,b)  ((a) & ~((uinteger)1 << (b)))
+#define bit_set(a,b)    ((a) |  ((uinteger)1 << (b)))
+
+/* procedure parameter types for -A and -C++ */
+
+#define F2C_proc_par_types 1
+#ifdef __cplusplus
+    typedef int /* Unknown procedure type */ (*U_fp)(...);
+    typedef shortint (*J_fp)(...);
+    typedef integer (*I_fp)(...);
+    typedef real (*R_fp)(...);
+    typedef doublereal (*D_fp)(...), (*E_fp)(...);
+    typedef /* Complex */ VOID (*C_fp)(...);
+    typedef /* Double Complex */ VOID (*Z_fp)(...);
+    typedef logical (*L_fp)(...);
+    typedef shortlogical (*K_fp)(...);
+    typedef /* Character */ VOID (*H_fp)(...);
+    typedef /* Subroutine */ int (*S_fp)(...);
+#else
+    typedef int /* Unknown procedure type */ (*U_fp)();
+    typedef shortint (*J_fp)();
+    typedef integer (*I_fp)();
+    typedef real (*R_fp)();
+    typedef doublereal (*D_fp)(), (*E_fp)();
+    typedef /* Complex */ VOID (*C_fp)();
+    typedef /* Double Complex */ VOID (*Z_fp)();
+    typedef logical (*L_fp)();
+    typedef shortlogical (*K_fp)();
+    typedef /* Character */ VOID (*H_fp)();
+    typedef /* Subroutine */ int (*S_fp)();
+#endif
+/* E_fp is for real functions when -R is not specified */
+typedef VOID C_f;   /* complex function */
+typedef VOID H_f;   /* character function */
+typedef VOID Z_f;   /* double complex function */
+typedef doublereal E_f; /* real function with -R not specified */
+
+/* undef any lower-case symbols that your C compiler predefines, e.g.: */
+
+#ifndef Skip_f2c_Undefs
+    #undef cray
+    #undef gcos
+    #undef mc68010
+    #undef mc68020
+    #undef mips
+    #undef pdp11
+    #undef sgi
+    #undef sparc
+    #undef sun
+    #undef sun2
+    #undef sun3
+    #undef sun4
+    #undef u370
+    #undef u3b
+    #undef u3b2
+    #undef u3b5
+    #undef unix
+    #undef vax
+#endif
+
+#include "config.h"
+
+#endif
diff --git a/igraph/include/f2c/fio.h b/igraph/include/f2c/fio.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/f2c/fio.h
@@ -0,0 +1,141 @@
+#ifndef SYSDEP_H_INCLUDED
+#include "sysdep1.h"
+#endif
+#include "stdio.h"
+#include "errno.h"
+#ifndef NULL
+/* ANSI C */
+#include "stddef.h"
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+#ifndef FOPEN
+#define FOPEN fopen
+#endif
+
+#ifndef FREOPEN
+#define FREOPEN freopen
+#endif
+
+#ifndef FSEEK
+#define FSEEK fseek
+#endif
+
+#ifndef FSTAT
+#define FSTAT fstat
+#endif
+
+#ifndef FTELL
+#define FTELL ftell
+#endif
+
+#ifndef OFF_T
+#define OFF_T long
+#endif
+
+#ifndef STAT_ST
+#define STAT_ST stat
+#endif
+
+#ifndef STAT
+#define STAT stat
+#endif
+
+#ifdef MSDOS
+#ifndef NON_UNIX_STDIO
+#define NON_UNIX_STDIO
+#endif
+#endif
+
+#ifdef UIOLEN_int
+typedef int uiolen;
+#else
+typedef long uiolen;
+#endif
+
+/*units*/
+typedef struct
+{	FILE *ufd;	/*0=unconnected*/
+	char *ufnm;
+#ifndef MSDOS
+	long uinode;
+	int udev;
+#endif
+	int url;	/*0=sequential*/
+	flag useek;	/*true=can backspace, use dir, ...*/
+	flag ufmt;
+	flag urw;	/* (1 for can read) | (2 for can write) */
+	flag ublnk;
+	flag uend;
+	flag uwrt;	/*last io was write*/
+	flag uscrtch;
+} unit;
+
+#undef Void
+#ifdef KR_headers
+#define Void /*void*/
+extern int (*f__getn)();	/* for formatted input */
+extern void (*f__putn)();	/* for formatted output */
+extern void x_putc();
+extern long f__inode();
+extern VOID sig_die();
+extern int (*f__donewrec)(), t_putc(), x_wSL();
+extern int c_sfe(), err__fl(), xrd_SL(), f__putbuf();
+#else
+#define Void void
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int (*f__getn)(void);	/* for formatted input */
+extern void (*f__putn)(int);	/* for formatted output */
+extern void x_putc(int);
+extern long f__inode(char*,int*);
+extern void sig_die(const char*,int);
+extern void f__fatal(int, const char*);
+extern int t_runc(alist*);
+extern int f__nowreading(unit*), f__nowwriting(unit*);
+extern int fk_open(int,int,ftnint);
+extern int en_fio(void);
+extern void f_init(void);
+extern int (*f__donewrec)(void), t_putc(int), x_wSL(void);
+extern void b_char(const char*,char*,ftnlen), g_char(const char*,ftnlen,char*);
+extern int c_sfe(cilist*), z_rnew(void);
+extern int err__fl(int,int,const char*);
+extern int xrd_SL(void);
+extern int f__putbuf(int);
+#endif
+extern flag f__init;
+extern cilist *f__elist;	/*active external io list*/
+extern flag f__reading,f__external,f__sequential,f__formatted;
+extern int (*f__doend)(Void);
+extern FILE *f__cf;	/*current file*/
+extern unit *f__curunit;	/*current unit*/
+extern unit f__units[];
+#define err(f,m,s) {if(f) errno= m; else f__fatal(m,s); return(m);}
+#define errfl(f,m,s) return err__fl((int)f,m,s)
+
+/*Table sizes*/
+#define MXUNIT 100
+
+extern int f__recpos;	/*position in current record*/
+extern OFF_T f__cursor;	/* offset to move to */
+extern OFF_T f__hiwater;	/* so TL doesn't confuse us */
+#ifdef __cplusplus
+	}
+#endif
+
+#define WRITE	1
+#define READ	2
+#define SEQ	3
+#define DIR	4
+#define FMT	5
+#define UNF	6
+#define EXT	7
+#define INT	8
+
+#define buf_end(x) (x->_flag & _IONBF ? x->_ptr : x->_base + BUFSIZ)
diff --git a/igraph/include/f2c/fmt.h b/igraph/include/f2c/fmt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/f2c/fmt.h
@@ -0,0 +1,105 @@
+struct syl
+{	int op;
+	int p1;
+	union { int i[2]; char *s;} p2;
+	};
+#define RET1 1
+#define REVERT 2
+#define GOTO 3
+#define X 4
+#define SLASH 5
+#define STACK 6
+#define I 7
+#define ED 8
+#define NED 9
+#define IM 10
+#define APOS 11
+#define H 12
+#define TL 13
+#define TR 14
+#define T 15
+#define COLON 16
+#define S 17
+#define SP 18
+#define SS 19
+#define P 20
+#define BN 21
+#define BZ 22
+#define F 23
+#define E 24
+#define EE 25
+#define D 26
+#define G 27
+#define GE 28
+#define L 29
+#define A 30
+#define AW 31
+#define O 32
+#define NONL 33
+#define OM 34
+#define Z 35
+#define ZM 36
+typedef union
+{	real pf;
+	doublereal pd;
+} ufloat;
+typedef union
+{	short is;
+#ifndef KR_headers
+	signed
+#endif
+		char ic;
+	integer il;
+#ifdef Allow_TYQUAD
+	longint ili;
+#endif
+} Uint;
+#ifdef KR_headers
+extern int (*f__doed)(),(*f__doned)();
+extern int (*f__dorevert)();
+extern int rd_ed(),rd_ned();
+extern int w_ed(),w_ned();
+extern int signbit_f2c();
+extern char *f__fmtbuf;
+#else
+#ifdef __cplusplus
+extern "C" {
+#define Cextern extern "C"
+#else
+#define Cextern extern
+#endif
+extern const char *f__fmtbuf;
+extern int (*f__doed)(struct syl*, char*, ftnlen),(*f__doned)(struct syl*);
+extern int (*f__dorevert)(void);
+extern void fmt_bg(void);
+extern int pars_f(const char*);
+extern int rd_ed(struct syl*, char*, ftnlen),rd_ned(struct syl*);
+extern int signbit_f2c(double*);
+extern int w_ed(struct syl*, char*, ftnlen),w_ned(struct syl*);
+extern int wrt_E(ufloat*, int, int, int, ftnlen);
+extern int wrt_F(ufloat*, int, int, ftnlen);
+extern int wrt_L(Uint*, int, ftnlen);
+#endif
+extern int f__pc,f__parenlvl,f__revloc;
+extern flag f__cblank,f__cplus,f__workdone, f__nonl;
+extern int f__scale;
+#ifdef __cplusplus
+	}
+#endif
+#define GET(x) if((x=(*f__getn)())<0) return(x)
+#define VAL(x) (x!='\n'?x:' ')
+#define PUT(x) (*f__putn)(x)
+
+#undef TYQUAD
+#ifndef Allow_TYQUAD
+#undef longint
+#define longint long
+#else
+#define TYQUAD 14
+#endif
+
+#ifdef KR_headers
+extern char *f__icvt();
+#else
+Cextern char *f__icvt(longint, int*, int*, int);
+#endif
diff --git a/igraph/include/f2c/fp.h b/igraph/include/f2c/fp.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/f2c/fp.h
@@ -0,0 +1,28 @@
+#define FMAX 40
+#define EXPMAXDIGS 8
+#define EXPMAX 99999999
+/* FMAX = max number of nonzero digits passed to atof() */
+/* EXPMAX = 10^EXPMAXDIGS - 1 = largest allowed exponent absolute value */
+
+#ifdef V10 /* Research Tenth-Edition Unix */
+#include "local.h"
+#endif
+
+/* MAXFRACDIGS and MAXINTDIGS are for wrt_F -- bounds (not necessarily
+   tight) on the maximum number of digits to the right and left of
+ * the decimal point.
+ */
+
+#ifdef VAX
+#define MAXFRACDIGS 56
+#define MAXINTDIGS 38
+#else
+#ifdef CRAY
+#define MAXFRACDIGS 9880
+#define MAXINTDIGS 9864
+#else
+/* values that suffice for IEEE double */
+#define MAXFRACDIGS 344
+#define MAXINTDIGS 308
+#endif
+#endif
diff --git a/igraph/include/f2c/lio.h b/igraph/include/f2c/lio.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/f2c/lio.h
@@ -0,0 +1,74 @@
+/*	copy of ftypes from the compiler */
+/* variable types
+ * numeric assumptions:
+ *	int < reals < complexes
+ *	TYDREAL-TYREAL = TYDCOMPLEX-TYCOMPLEX
+ */
+
+/* 0-10 retain their old (pre LOGICAL*1, etc.) */
+/* values to allow mixing old and new objects. */
+
+#define TYUNKNOWN 0
+#define TYADDR 1
+#define TYSHORT 2
+#define TYLONG 3
+#define TYREAL 4
+#define TYDREAL 5
+#define TYCOMPLEX 6
+#define TYDCOMPLEX 7
+#define TYLOGICAL 8
+#define TYCHAR 9
+#define TYSUBR 10
+#define TYINT1 11
+#define TYLOGICAL1 12
+#define TYLOGICAL2 13
+#ifdef Allow_TYQUAD
+#undef TYQUAD
+#define TYQUAD 14
+#endif
+
+#define	LINTW	24
+#define	LINE	80
+#define	LLOGW	2
+#ifdef Old_list_output
+#define	LLOW	1.0
+#define	LHIGH	1.e9
+#define	LEFMT	" %# .8E"
+#define	LFFMT	" %# .9g"
+#else
+#define	LGFMT	"%.9G"
+#endif
+/* LEFBL 20 should suffice; 24 overcomes a NeXT bug. */
+#define	LEFBL	24
+
+typedef union
+{
+	char	flchar;
+	short	flshort;
+	ftnint	flint;
+#ifdef Allow_TYQUAD
+	longint fllongint;
+#endif
+	real	flreal;
+	doublereal	fldouble;
+} flex;
+#ifdef KR_headers
+extern int (*f__lioproc)(), (*l_getc)(), (*l_ungetc)();
+extern int l_read(), l_write();
+#else
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int (*f__lioproc)(ftnint*, char*, ftnlen, ftnint);
+extern int l_write(ftnint*, char*, ftnlen, ftnint);
+extern void x_wsne(cilist*);
+extern int c_le(cilist*), (*l_getc)(void), (*l_ungetc)(int,FILE*);
+extern int l_read(ftnint*,char*,ftnlen,ftnint);
+extern integer e_rsle(void), e_wsle(void), s_wsne(cilist*);
+extern int z_rnew(void);
+#endif
+extern ftnint L_len;
+extern int f__scale;
+#ifdef __cplusplus
+	}
+#endif
diff --git a/igraph/include/f2c/signal1.h b/igraph/include/f2c/signal1.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/f2c/signal1.h
@@ -0,0 +1,35 @@
+/* You may need to adjust the definition of signal1 to supply a */
+/* cast to the correct argument type.  This detail is system- and */
+/* compiler-dependent.   The #define below assumes signal.h declares */
+/* type SIG_PF for the signal function's second argument. */
+
+/* For some C++ compilers, "#define Sigarg_t ..." may be appropriate. */
+
+#include <signal.h>
+
+#ifndef Sigret_t
+#define Sigret_t void
+#endif
+#ifndef Sigarg_t
+#ifdef KR_headers
+#define Sigarg_t
+#else
+#define Sigarg_t int
+#endif
+#endif /*Sigarg_t*/
+
+#ifdef USE_SIG_PF	/* compile with -DUSE_SIG_PF under IRIX */
+#define sig_pf SIG_PF
+#else
+typedef Sigret_t (*sig_pf)(Sigarg_t);
+#endif
+
+#define signal1(a,b) signal(a,(sig_pf)b)
+
+#ifdef __cplusplus
+#define Sigarg ...
+#define Use_Sigarg
+#else
+#define Sigarg Int n
+#define Use_Sigarg n = n	/* shut up compiler warning */
+#endif
diff --git a/igraph/include/f2c/sysdep1.h b/igraph/include/f2c/sysdep1.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/f2c/sysdep1.h
@@ -0,0 +1,76 @@
+#ifndef SYSDEP_H_INCLUDED
+#define SYSDEP_H_INCLUDED
+
+#ifdef _MSC_VER
+#define FTRUNCATE chsize
+#endif
+
+#undef USE_LARGEFILE
+#ifndef NO_LONG_LONG
+
+#ifdef __sun__
+#define USE_LARGEFILE
+#define OFF_T off64_t
+#endif
+
+#ifdef __linux__
+#define USE_LARGEFILE
+
+#ifdef __GLIBC__
+#define OFF_T __off64_t
+#else
+#define OFF_T off64_t
+#endif /* __GLIBC__ */
+#endif /* __linux__ */
+
+#ifdef _AIX43
+#define _LARGE_FILES
+#define _LARGE_FILE_API
+#define USE_LARGEFILE
+#endif /*_AIX43*/
+
+#ifdef __hpux
+#define _FILE64
+#define _LARGEFILE64_SOURCE
+#define USE_LARGEFILE
+#endif /*__hpux*/
+
+#ifdef __sgi
+#define USE_LARGEFILE
+#endif /*__sgi*/
+
+#ifdef __FreeBSD__
+#define OFF_T off_t
+#define FSEEK fseeko
+#define FTELL ftello
+#endif
+
+#ifdef USE_LARGEFILE
+#ifndef OFF_T
+#define OFF_T off64_t
+#endif
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#define FOPEN fopen64
+#define FREOPEN freopen64
+#define FSEEK fseeko64
+#define FSTAT fstat64
+#define FTELL ftello64
+#define FTRUNCATE ftruncate64
+#define STAT stat64
+#define STAT_ST stat64
+#endif /*USE_LARGEFILE*/
+#endif /*NO_LONG_LONG*/
+
+#ifndef NON_UNIX_STDIO
+#ifndef USE_LARGEFILE
+#define _INCLUDE_POSIX_SOURCE	/* for HP-UX */
+#define _INCLUDE_XOPEN_SOURCE	/* for HP-UX */
+#include "sys/types.h"
+#include "sys/stat.h"
+#endif
+#endif
+
+#endif /*SYSDEP_H_INCLUDED*/
diff --git a/igraph/include/foreign-dl-header.h b/igraph/include/foreign-dl-header.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-dl-header.h
@@ -0,0 +1,42 @@
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_types_internal.h"
+
+typedef enum { IGRAPH_DL_MATRIX,
+               IGRAPH_DL_EDGELIST1, IGRAPH_DL_NODELIST1
+             } igraph_i_dl_type_t;
+
+typedef struct {
+    void *scanner;
+    int eof;
+    int mode;
+    long int n;
+    long int from, to;
+    igraph_vector_t edges;
+    igraph_vector_t weights;
+    igraph_strvector_t labels;
+    igraph_trie_t trie;
+    igraph_i_dl_type_t type;
+    char errmsg[300];
+} igraph_i_dl_parsedata_t;
diff --git a/igraph/include/foreign-dl-parser.h b/igraph/include/foreign-dl-parser.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-dl-parser.h
@@ -0,0 +1,107 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     NUM = 258,
+     NEWLINE = 259,
+     DL = 260,
+     NEQ = 261,
+     DATA = 262,
+     LABELS = 263,
+     LABELSEMBEDDED = 264,
+     FORMATFULLMATRIX = 265,
+     FORMATEDGELIST1 = 266,
+     FORMATNODELIST1 = 267,
+     DIGIT = 268,
+     LABEL = 269,
+     EOFF = 270,
+     ERROR = 271
+   };
+#endif
+/* Tokens.  */
+#define NUM 258
+#define NEWLINE 259
+#define DL 260
+#define NEQ 261
+#define DATA 262
+#define LABELS 263
+#define LABELSEMBEDDED 264
+#define FORMATFULLMATRIX 265
+#define FORMATEDGELIST1 266
+#define FORMATNODELIST1 267
+#define DIGIT 268
+#define LABEL 269
+#define EOFF 270
+#define ERROR 271
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 86 "../../src/foreign-dl-parser.y"
+{
+  long int integer;
+  igraph_real_t real;
+}
+/* Line 1529 of yacc.c.  */
+#line 86 "foreign-dl-parser.h"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
diff --git a/igraph/include/foreign-gml-header.h b/igraph/include/foreign-gml-header.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-gml-header.h
@@ -0,0 +1,30 @@
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi@rmki.kfki.hu>
+   334 Harvard street, Cambridge MA, 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_gml_tree.h"
+
+typedef struct {
+    void *scanner;
+    int eof;
+    char errmsg[300];
+    igraph_gml_tree_t *tree;
+} igraph_i_gml_parsedata_t;
diff --git a/igraph/include/foreign-gml-parser.h b/igraph/include/foreign-gml-parser.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-gml-parser.h
@@ -0,0 +1,97 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     STRING = 258,
+     NUM = 259,
+     KEYWORD = 260,
+     LISTOPEN = 261,
+     LISTCLOSE = 262,
+     EOFF = 263,
+     ERROR = 264
+   };
+#endif
+/* Tokens.  */
+#define STRING 258
+#define NUM 259
+#define KEYWORD 260
+#define LISTOPEN 261
+#define LISTCLOSE 262
+#define EOFF 263
+#define ERROR 264
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 93 "../../src/foreign-gml-parser.y"
+{
+   struct {
+      char *s;
+      int len;
+   } str;
+   void *tree;
+   double real;
+}
+/* Line 1529 of yacc.c.  */
+#line 76 "foreign-gml-parser.h"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
diff --git a/igraph/include/foreign-lgl-header.h b/igraph/include/foreign-lgl-header.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-lgl-header.h
@@ -0,0 +1,35 @@
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi@rmki.kfki.hu>
+   334 Harvard street, Cambridge MA, 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_vector.h"
+#include "igraph_types_internal.h"
+
+typedef struct {
+    void *scanner;
+    int eof;
+    char errmsg[300];
+    int has_weights;
+    igraph_vector_t *vector;
+    igraph_vector_t *weights;
+    igraph_trie_t *trie;
+    int actvertex;
+} igraph_i_lgl_parsedata_t;
diff --git a/igraph/include/foreign-lgl-parser.h b/igraph/include/foreign-lgl-parser.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-lgl-parser.h
@@ -0,0 +1,87 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     ALNUM = 258,
+     NEWLINE = 259,
+     HASH = 260,
+     ERROR = 261
+   };
+#endif
+/* Tokens.  */
+#define ALNUM 258
+#define NEWLINE 259
+#define HASH 260
+#define ERROR 261
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 81 "../../src/foreign-lgl-parser.y"
+{
+  long int edgenum;
+  double weightnum;
+}
+/* Line 1529 of yacc.c.  */
+#line 66 "foreign-lgl-parser.h"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
diff --git a/igraph/include/foreign-ncol-header.h b/igraph/include/foreign-ncol-header.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-ncol-header.h
@@ -0,0 +1,34 @@
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi@rmki.kfki.hu>
+   334 Harvard street, Cambridge MA, 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_vector.h"
+#include "igraph_types_internal.h"
+
+typedef struct {
+    void *scanner;
+    int eof;
+    char errmsg[300];
+    int has_weights;
+    igraph_vector_t *vector;
+    igraph_vector_t *weights;
+    igraph_trie_t *trie;
+} igraph_i_ncol_parsedata_t;
diff --git a/igraph/include/foreign-ncol-parser.h b/igraph/include/foreign-ncol-parser.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-ncol-parser.h
@@ -0,0 +1,85 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     ALNUM = 258,
+     NEWLINE = 259,
+     ERROR = 260
+   };
+#endif
+/* Tokens.  */
+#define ALNUM 258
+#define NEWLINE 259
+#define ERROR 260
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 82 "../../src/foreign-ncol-parser.y"
+{
+  long int edgenum;
+  double weightnum;
+}
+/* Line 1529 of yacc.c.  */
+#line 64 "foreign-ncol-parser.h"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
diff --git a/igraph/include/foreign-pajek-header.h b/igraph/include/foreign-pajek-header.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-pajek-header.h
@@ -0,0 +1,43 @@
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi@rmki.kfki.hu>
+   334 Harvard street, Cambridge MA, 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_vector.h"
+#include "igraph_types_internal.h"
+
+typedef struct {
+    void *scanner;
+    int eof;
+    char errmsg[300];
+    igraph_vector_t *vector;
+    igraph_bool_t directed;
+    int vcount, vcount2;
+    int actfrom;
+    int actto;
+    int mode; /* 0: general, 1: vertex, 2: edge */
+    igraph_trie_t *vertex_attribute_names;
+    igraph_vector_ptr_t *vertex_attributes;
+    igraph_trie_t *edge_attribute_names;
+    igraph_vector_ptr_t *edge_attributes;
+    int vertexid;
+    int actvertex;
+    int actedge;
+} igraph_i_pajek_parsedata_t;
diff --git a/igraph/include/foreign-pajek-parser.h b/igraph/include/foreign-pajek-parser.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/foreign-pajek-parser.h
@@ -0,0 +1,181 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     NEWLINE = 258,
+     NUM = 259,
+     ALNUM = 260,
+     QSTR = 261,
+     PSTR = 262,
+     NETWORKLINE = 263,
+     VERTICESLINE = 264,
+     ARCSLINE = 265,
+     EDGESLINE = 266,
+     ARCSLISTLINE = 267,
+     EDGESLISTLINE = 268,
+     MATRIXLINE = 269,
+     ERROR = 270,
+     VP_X_FACT = 271,
+     VP_Y_FACT = 272,
+     VP_IC = 273,
+     VP_BC = 274,
+     VP_LC = 275,
+     VP_LR = 276,
+     VP_LPHI = 277,
+     VP_BW = 278,
+     VP_FOS = 279,
+     VP_PHI = 280,
+     VP_R = 281,
+     VP_Q = 282,
+     VP_LA = 283,
+     VP_FONT = 284,
+     VP_URL = 285,
+     VP_SIZE = 286,
+     EP_C = 287,
+     EP_S = 288,
+     EP_A = 289,
+     EP_W = 290,
+     EP_H1 = 291,
+     EP_H2 = 292,
+     EP_A1 = 293,
+     EP_A2 = 294,
+     EP_K1 = 295,
+     EP_K2 = 296,
+     EP_AP = 297,
+     EP_P = 298,
+     EP_L = 299,
+     EP_LP = 300,
+     EP_LR = 301,
+     EP_LPHI = 302,
+     EP_LC = 303,
+     EP_LA = 304,
+     EP_SIZE = 305,
+     EP_FOS = 306
+   };
+#endif
+/* Tokens.  */
+#define NEWLINE 258
+#define NUM 259
+#define ALNUM 260
+#define QSTR 261
+#define PSTR 262
+#define NETWORKLINE 263
+#define VERTICESLINE 264
+#define ARCSLINE 265
+#define EDGESLINE 266
+#define ARCSLISTLINE 267
+#define EDGESLISTLINE 268
+#define MATRIXLINE 269
+#define ERROR 270
+#define VP_X_FACT 271
+#define VP_Y_FACT 272
+#define VP_IC 273
+#define VP_BC 274
+#define VP_LC 275
+#define VP_LR 276
+#define VP_LPHI 277
+#define VP_BW 278
+#define VP_FOS 279
+#define VP_PHI 280
+#define VP_R 281
+#define VP_Q 282
+#define VP_LA 283
+#define VP_FONT 284
+#define VP_URL 285
+#define VP_SIZE 286
+#define EP_C 287
+#define EP_S 288
+#define EP_A 289
+#define EP_W 290
+#define EP_H1 291
+#define EP_H2 292
+#define EP_A1 293
+#define EP_A2 294
+#define EP_K1 295
+#define EP_K2 296
+#define EP_AP 297
+#define EP_P 298
+#define EP_L 299
+#define EP_LP 300
+#define EP_LR 301
+#define EP_LPHI 302
+#define EP_LC 303
+#define EP_LA 304
+#define EP_SIZE 305
+#define EP_FOS 306
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 118 "../../src/foreign-pajek-parser.y"
+{
+  long int intnum;
+  double   realnum;  
+  struct {
+    char *str;
+    int len;
+  } string;  
+}
+/* Line 1529 of yacc.c.  */
+#line 160 "foreign-pajek-parser.h"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
diff --git a/igraph/include/gengraph_box_list.h b/igraph/include/gengraph_box_list.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_box_list.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+// This class allows to maintain a list of vertices,
+// sorted by degree (largest degrees first)
+// Operations allowed :
+// - get the vertex having max degree -> Cost = O(1)
+// - remove any vertex from the graph -> Cost = Sum(degrees of neighbours)
+//                                       [ could be O(degree) if optimized ]
+
+#ifndef _BOX_LIST_H
+#define _BOX_LIST_H
+
+#ifndef _MSC_VER
+    #ifndef register
+        #define register
+    #endif
+#endif
+
+namespace gengraph {
+
+class box_list {
+
+private:
+    int n;     // INITIAL number of vertices
+    int dmax;  // CURRENT Maximum degree
+    int *deg;  // CURRENT Degrees (points directly to the deg[] of the graph
+
+    // Vertices are grouped by degree: one double-chained lists for each degree
+    int *list;        // list[d-1] is the head of list of vertices of degree d
+    int *next;        // next[v]/prev[v] are the vertices next/previous to v
+    int *prev;        //   in the list where v belongs
+    void pop(int);    // pop(v) just removes v from its list
+    void insert(int); // insert(v) insert v at the head of its list
+
+public:
+
+    // Ctor. Takes O(n) time.
+    box_list(int n0, int *deg0);
+
+    // Dtor
+    ~box_list();
+
+    // Self-explaining inline routines
+    inline bool is_empty() {
+        return dmax < 1;
+    };
+    inline int get_max()   {
+        return list[dmax - 1];
+    };
+    inline int get_one()   {
+        return list[0];
+    };
+    inline int get_min()   {
+        int i = 0;
+        while (list[i] < 0) {
+            i++;
+        }
+        return list[i];
+    };
+
+    // Remove v from box_list
+    // Also, semi-remove vertex v from graph: all neighbours of v will swap
+    // their last neighbour wit hv, and then decrease their degree, so
+    // that any arc w->v virtually disappear
+    // Actually, adjacency lists are just permuted, and deg[] is changed
+    void pop_vertex(int v, int **neigh);
+};
+
+} // namespace gengraph
+
+#endif //_BOX_LIST_H
diff --git a/igraph/include/gengraph_definitions.h b/igraph/include/gengraph_definitions.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_definitions.h
@@ -0,0 +1,216 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DEFINITIONS_H
+#define DEFINITIONS_H
+
+#ifndef _MSC_VER
+    #ifndef register
+        #define register
+    #endif
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+namespace gengraph {
+
+// Max line size in files
+#define FBUFF_SIZE 1000000
+
+// disable lousy VC++ warnings
+#ifdef _ATL_VER_
+    #pragma warning(disable : 4127)
+#endif //_ATL_VER_
+
+// Verbose
+#define VERBOSE_NONE 0
+#define VERBOSE_SOME 1
+#define VERBOSE_LOTS 2
+int VERBOSE();
+void SET_VERBOSE(int v);
+
+// Random number generator
+void my_srandom(int);
+int my_random();
+int my_binomial(double pp, int n);
+double my_random01(); // (0,1]
+
+#define MY_RAND_MAX 0x7FFFFFFF
+
+// IPv4 address direct translation into 32-bit uint + special IP defs
+typedef unsigned int ip_addr;
+#define IP_NONE   0x7FFFFFFF
+#define IP_STAR   0x00000000
+#define IP_MYSELF 0x7F000001
+
+// Compatibility
+#ifdef _WIN32
+    #define strcasecmp _stricmp
+#endif
+//inline double round(double x) throw () { return (floor(0.5+x)); }
+
+// No assert
+#ifndef _DEBUG
+    #ifndef NDEBUG
+        #define NDEBUG
+    #endif //NDEBUG
+#endif //_DEBUG
+
+// Min & Max
+#ifndef min
+    #define defmin(type) inline type min(type a, type b) { return a<b ? a : b; }
+    defmin(int)
+    defmin(double)
+    defmin(unsigned long)
+#endif //min
+#ifndef max
+    #define defmax(type) inline type max(type a, type b) { return a>b ? a : b; }
+    defmax(int)
+    defmax(double)
+    defmax(unsigned long)
+#endif //max
+
+// Traceroute Sampling
+#define MODE_USP 0
+#define MODE_ASP 1
+#define MODE_RSP 2
+
+// Debug definitions
+//#define PERFORMANCE_MONITOR
+//#define OPT_ISOLATED
+
+// Max Int
+#ifndef MAX_INT
+    #define MAX_INT 0x7FFFFFFF
+#endif //MAX_INT
+
+//Edge type
+typedef struct {
+    int from;
+    int to;
+} edge;
+
+// Tag Int
+#define TAG_INT 0x40000000
+
+// Oldies ....
+#define S_VECTOR_RAW
+
+//*********************
+// Routine definitions
+//*********************
+
+/* log(1+x)
+inline double logp(double x) {
+  if(fabs(x)<1e-6) return x+0.5*x*x+0.333333333333333*x*x*x;
+  else return log(1.0+x);
+}
+//*/
+
+
+//Fast search or replace
+inline int* fast_rpl(int *m, const int a, const int b) {
+    while (*m != a) {
+        m++;
+    }
+    *m = b;
+    return m;
+}
+inline int* fast_search(int *m, const int size, const int a) {
+    int *p = m + size;
+    while (m != p--) if (*p == a) {
+            return p;
+        }
+    return NULL;
+}
+
+// Lovely percentage print
+// inline void print_percent(double yo, FILE *f = stderr) {
+//   int arf = int(100.0*yo);
+//   if(double(arf)>100.0*yo) arf--;
+//   if(arf<100) fprintf(f," ");
+//   if(arf<10) fprintf(f," ");
+//   fprintf(f,"%d.%d%%",arf,int(1000.0*yo-double(10*arf)));
+// }
+
+// Skips non-numerical chars, then numerical chars, then non-numerical chars.
+inline char skip_int(char* &c) {
+    while (*c < '0' || *c > '9') {
+        c++;
+    }
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+    while (*c != 0 && (*c < '0' || *c > '9')) {
+        c++;
+    }
+    return *c;
+}
+
+// distance+1 modulo 255 for breadth-first search
+inline unsigned char next_dist(const unsigned char c) {
+    return c == 255 ? 1 : c + 1;
+}
+inline unsigned char prev_dist(const unsigned char c) {
+    return c == 1 ? 255 : c - 1;
+}
+
+// 1/(RANDMAX+1)
+#define inv_RANDMAX (1.0/(1.0+double(MY_RAND_MAX)))
+
+// random number in ]0,1[, _very_ accurate around 0
+inline double random_float() {
+    int r = my_random();
+    double mul = inv_RANDMAX;
+    while (r <= 0x7FFFFF) {
+        r <<= 8;
+        r += (my_random() & 0xFF);
+        mul *= (1.0 / 256.0);
+    }
+    return double(r) * mul;
+}
+
+// Return true with probability p. Very accurate when p is small.
+#define test_proba(p) (random_float()<(p))
+
+// Random bit generator, sparwise.
+static int _random_bits_stored = 0;
+static int _random_bits = 0;
+
+inline int random_bit() {
+    register int a = _random_bits;
+    _random_bits = a >> 1;
+    if (_random_bits_stored--) {
+        return a & 0x1;
+    }
+    a = my_random();
+    _random_bits = a >> 1;
+    _random_bits_stored = 30;
+    return a & 0x1;
+}
+
+// Hash Profiling (see hash.h)
+void _hash_prof();
+
+} // namespace gengraph
+
+#endif //DEFINITIONS_H
diff --git a/igraph/include/gengraph_degree_sequence.h b/igraph/include/gengraph_degree_sequence.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_degree_sequence.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DEGREE_SEQUENCE_H
+#define DEGREE_SEQUENCE_H
+
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+
+namespace gengraph {
+
+class degree_sequence {
+
+private:
+    int n;
+    int * deg;
+    int total;
+
+public :
+    // #vertices
+    inline int size() {
+        return n;
+    };
+    inline int sum() {
+        return total;
+    };
+    inline int operator[](int i) {
+        return deg[i];
+    };
+    inline int *seq() {
+        return deg;
+    };
+    inline void assign(int n0, int* d0) {
+        n = n0;
+        deg = d0;
+    };
+    inline int dmax() {
+        int dm = deg[0];
+        for (int i = 1; i < n; i++) if (deg[i] > dm) {
+                dm = deg[i];
+            }
+        return dm;
+    }
+
+    void make_even(int mini = -1, int maxi = -1);
+    void sort();
+    void shuffle();
+
+    // raw constructor
+    degree_sequence(int n, int *degs);
+
+    // read-from-file constrictor
+    degree_sequence(FILE *f, bool DISTRIB = true);
+
+    // simple power-law constructor : Pk = int((x+k0)^(-exp),x=k..k+1), with k0 so that avg(X)=z
+    degree_sequence(int n, double exp, int degmin, int degmax, double avg_degree = -1.0);
+
+    // igraph constructor
+    degree_sequence(const igraph_vector_t *out_seq);
+
+    // destructor
+    ~degree_sequence();
+
+    // unbind the deg[] vector (so that it doesn't get deleted when the class is destroyed)
+    void detach();
+
+    // compute total number of arcs
+    void compute_total();
+
+    // raw print (vertex by vertex)
+    void print();
+
+    // distribution print (degree frequency)
+    void print_cumul();
+
+    // is degree sequence realizable ?
+    bool havelhakimi();
+
+};
+
+} // namespace gengraph
+
+#endif //DEGREE_SEQUENCE_H
+
diff --git a/igraph/include/gengraph_graph_molloy_hash.h b/igraph/include/gengraph_graph_molloy_hash.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_graph_molloy_hash.h
@@ -0,0 +1,219 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GRAPH_MOLLOY_HASH_H
+#define GRAPH_MOLLOY_HASH_H
+
+#include "gengraph_definitions.h"
+#include "gengraph_hash.h"
+#include "gengraph_degree_sequence.h"
+
+#include <string.h>
+#include <assert.h>
+// This class handles graphs with a constant degree sequence.
+
+#define FINAL_HEURISTICS        0
+#define GKAN_HEURISTICS         1
+#define FAB_HEURISTICS          2
+#define OPTIMAL_HEURISTICS      3
+#define BRUTE_FORCE_HEURISTICS  4
+
+namespace gengraph {
+
+//****************************
+//  class graph_molloy_hash
+//****************************
+
+class graph_molloy_hash {
+
+private:
+    // Number of vertices
+    int n;
+    //Number of arcs ( = #edges * 2 )
+    int a;
+    //Total size of links[]
+    int size;
+    // The degree sequence of the graph
+    int *deg;
+    // The array containing all links
+    int *links;
+    // The array containing pointers to adjacency list of every vertices
+    int **neigh;
+    // Counts total size
+    void compute_size();
+    // Build neigh with deg and links
+    void compute_neigh();
+    // Allocate memory according to degree_sequence (for constructor use only!!)
+    int alloc(degree_sequence &);
+    // Add edge (a,b). Return FALSE if vertex a is already full.
+    // WARNING : only to be used by havelhakimi(), restore() or constructors
+    inline bool add_edge(int a, int b, int *realdeg) {
+        int deg_a = realdeg[a];
+        if (deg_a == deg[a]) {
+            return false;
+        }
+        // Check that edge was not already inserted
+        assert(fast_search(neigh[a], int((a == n - 1 ? links + size : neigh[a + 1]) - neigh[a]), b) == NULL);
+        assert(fast_search(neigh[b], int((b == n - 1 ? links + size : neigh[b + 1]) - neigh[b]), a) == NULL);
+        assert(deg[a] < deg_a);
+        int deg_b = realdeg[b];
+        if (IS_HASH(deg_a)) {
+            *H_add(neigh[a], HASH_EXPAND(deg_a), b) = b;
+        } else {
+            neigh[a][deg[a]] = b;
+        }
+        if (IS_HASH(deg_b)) {
+            *H_add(neigh[b], HASH_EXPAND(deg_b), a) = a;
+        } else {
+            neigh[b][deg[b]] = a;
+        }
+        deg[a]++;
+        deg[b]++;
+        // Check that edge was actually inserted
+        assert(fast_search(neigh[a], int((a == n - 1 ? links + size : neigh[a + 1]) - neigh[a]), b) != NULL);
+        assert(fast_search(neigh[b], int((b == n - 1 ? links + size : neigh[b + 1]) - neigh[b]), a) != NULL);
+        return true;
+    }
+    // Swap edges
+    inline void swap_edges(int from1, int to1, int from2, int to2) {
+        H_rpl(neigh[from1], deg[from1], to1, to2);
+        H_rpl(neigh[from2], deg[from2], to2, to1);
+        H_rpl(neigh[to1], deg[to1], from1, from2);
+        H_rpl(neigh[to2], deg[to2], from2, from1);
+    }
+    // Backup graph [sizeof(int) bytes per edge]
+    int* backup();
+    // Test if vertex is in an isolated component of size<K
+    bool isolated(int v, int K, int *Kbuff, bool *visited);
+    // Pick random edge, and gives a corresponding vertex
+    inline int pick_random_vertex() {
+        int v;
+        do {
+            v = links[my_random() % size];
+        } while (v == HASH_NONE);
+        return v;
+    }
+    // Pick random neighbour
+    inline int* random_neighbour(const int v) {
+        return H_random(neigh[v], deg[v]);
+    }
+    // Depth-first search.
+    int depth_search(bool *visited, int *buff, int v0 = 0);
+    // Returns complexity of isolation test
+    long effective_isolated(int v, int K, int *Kbuff, bool *visited);
+    // Depth-Exploration. Returns number of steps done. Stops when encounter vertex of degree > dmax.
+    void depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited);
+
+
+public:
+    //degree of v
+    inline int degree(const int v) {
+        return deg[v];
+    };
+    // For debug purposes : verify validity of the graph (symetry, simplicity)
+    bool verify();
+    // Destroy deg[], neigh[] and links[]
+    ~graph_molloy_hash();
+    // Allocate memory for the graph. Create deg and links. No edge is created.
+    graph_molloy_hash(degree_sequence &);
+    // Create graph from hard copy
+    graph_molloy_hash(int *);
+    // Create hard copy of graph
+    int *hard_copy();
+    // Restore from backup
+    void restore(int* back);
+    //Clear hash tables
+    void init();
+    // nb arcs
+    inline int nbarcs() {
+        return a;
+    };
+    // nb vertices
+    inline int nbvertices() {
+        return n;
+    };
+    // print graph in SUCC_LIST mode, in stdout
+    void print(FILE *f = stdout);
+    int print(igraph_t *graph);
+    // Test if graph is connected
+    bool is_connected();
+    // is edge ?
+    inline bool is_edge(int a, int b) {
+        assert(H_is(neigh[a], deg[a], b) == (fast_search(neigh[a], HASH_SIZE(deg[a]), b) != NULL));
+        assert(H_is(neigh[b], deg[b], a) == (fast_search(neigh[b], HASH_SIZE(deg[b]), a) != NULL));
+        assert(H_is(neigh[a], deg[a], b) == H_is(neigh[b], deg[b], a));
+        if (deg[a] < deg[b]) {
+            return H_is(neigh[a], deg[a], b);
+        } else {
+            return H_is(neigh[b], deg[b], a);
+        }
+    }
+    // Random edge swap ATTEMPT. Return 1 if attempt was a succes, 0 otherwise
+    int random_edge_swap(int K = 0, int *Kbuff = NULL, bool *visited = NULL);
+    // Connected Shuffle
+    unsigned long shuffle(unsigned long, unsigned long, int type);
+    // Optimal window for the gkantsidis heuristics
+    int optimal_window();
+    // Average unitary cost per post-validated edge swap, for some window
+    double average_cost(int T, int *back, double min_cost);
+    // Get caracteristic K
+    double eval_K(int quality = 100);
+    // Get effective K
+    double effective_K(int K, int quality = 10000);
+    // Try to shuffle T times. Return true if at the end, the graph was still connected.
+    bool try_shuffle(int T, int K, int *back = NULL);
+
+
+    /*_____________________________________________________________________________
+      Not to use anymore : use graph_molloy_opt class instead
+
+    private:
+      // breadth-first search. Store the distance (modulo 3)  in dist[]. Returns eplorated component size.
+      int width_search(unsigned char *dist, int *buff, int v0=0);
+
+    public:
+      // Create graph
+      graph_molloy_hash(FILE *f);
+      // Bind the graph avoiding multiple edges or self-edges (return false if fail)
+      bool havelhakimi();
+      // Get the graph connected  (return false if fail)
+      bool make_connected();
+      // "Fab" Shuffle (Optimized heuristic of Gkantsidis algo.)
+      long long fab_connected_shuffle(long long);
+      // Naive Shuffle
+      long long slow_connected_shuffle(long long);
+      // Maximum degree
+      int max_degree();
+      // compute vertex betweenness : for each vertex, a unique random shortest path is chosen.
+      // this choice is consistent (if shortest path from a to c goes through b and then d,
+      // then shortest path from a to d goes through b). If(trivial path), also count all the
+      // shortest paths where vertex is an extremity
+      int *vertex_betweenness_rsp(bool trivial_path);
+      // same, but when multiple shortest path are possible, average the weights.
+      double *vertex_betweenness_asp(bool trivial_path);
+    //___________________________________________________________________________________
+    //*/
+
+};
+
+} // namespace gengraph
+
+#endif //GRAPH_MOLLOY_HASH_H
+
diff --git a/igraph/include/gengraph_graph_molloy_optimized.h b/igraph/include/gengraph_graph_molloy_optimized.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_graph_molloy_optimized.h
@@ -0,0 +1,288 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GRAPH_MOLLOY_OPT_H
+#define GRAPH_MOLLOY_OPT_H
+
+#include "gengraph_definitions.h"
+#include "gengraph_degree_sequence.h"
+#include "gengraph_qsort.h"
+
+#include <assert.h>
+#include "gengraph_random.h"
+
+namespace gengraph {
+
+// This class handles graphs with a constant degree sequence.
+
+class graph_molloy_opt {
+
+private:
+    // Random generator
+    KW_RNG::RNG rng;
+    // Number of vertices
+    int n;
+    //Number of arcs ( = #edges * 2 )
+    int a;
+    // The degree sequence of the graph
+    int *deg;
+    // The array containing all links
+    int *links;
+    // The array containing pointers to adjacency list of every vertices
+    int **neigh;
+    // Allocate memory according to degree_sequence (for constructor use only!!)
+    void alloc(degree_sequence &);
+    // Compute #edges
+    inline void refresh_nbarcs() {
+        a = 0;
+        for (int* d = deg + n; d != deg; ) {
+            a += *(--d);
+        }
+    }
+    // Build neigh with deg and links
+    void compute_neigh();
+    // Swap edges. The swap MUST be valid !!!
+    inline void swap_edges(int from1, int to1, int from2, int to2) {
+        fast_rpl(neigh[from1], to1, to2);
+        fast_rpl(neigh[from2], to2, to1);
+        fast_rpl(neigh[to1], from1, from2);
+        fast_rpl(neigh[to2], from2, from1);
+    }
+
+    // Swap edges only if they are simple. return false if unsuccessful.
+    bool swap_edges_simple(int, int, int, int);
+    // Test if vertex is in an isolated component of size<K
+    bool isolated(int v, int K, int *Kbuff, bool *visited);
+    // Pick random edge, and gives a corresponding vertex
+    inline int pick_random_vertex() {
+        return links[my_random() % a];
+    };
+    // Pick random neighbour
+    inline int* random_neighbour(const int v) {
+        return neigh[v] + (my_random() % deg[v]);
+    };
+    // Returns complexity of isolation test
+    long effective_isolated(int v, int K, int *Kbuff, bool *visited);
+    // Depth-Exploration. Returns number of steps done. Stops when encounter vertex of degree > dmax.
+    void depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited);
+    // breadth-first search. Store the distance (modulo 3)  in dist[]. Returns eplorated component size.
+    int width_search(unsigned char *dist, int *buff, int v0 = 0, int toclear = -1);
+    // depth-first search.
+    int depth_search(bool *visited, int *buff, int v0 = 0);
+    // breadth-first search that count the number of shortest paths going from src to each vertex
+    int breadth_path_search(int src, int *buff, double *paths, unsigned char *dist);
+    // Used by traceroute_sample() ONLY
+    void add_traceroute_edge(int, int, int*, double** red = NULL, double t = 1.0);
+    // Used by traceroute() and betweenness(). if newdeg[]=NULL, do not discover edges.
+    // breadth_path_search() must have been called to give the corresponding buff[],dist[],paths[] and nb_vertices
+    void explore_usp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg = NULL, double **edge_redudancy = NULL);
+    void explore_asp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg = NULL, double **edge_redudancy = NULL);
+    void explore_rsp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg = NULL, double **edge_redudancy = NULL);
+    // Return component indexes where vertices belong to, starting from 0,
+    // sorted by size (biggest component has index 0)
+    int *components(int *comp = NULL);
+    // pick k random vertices of degree > 0.
+    int *pick_random_vertices(int &k, int *output = NULL, int nb_v = -1, int *among = NULL);
+
+public:
+    // neigh[]
+    inline int** neighbors() {
+        return neigh;
+    };
+    // deg[]
+    inline int* degrees() {
+        return deg;
+    };
+    //adjacency list of v
+    inline int* operator[](const int v) {
+        return neigh[v];
+    };
+    //degree of v
+    inline int degree(const int v) {
+        return deg[v];
+    };
+    //compare adjacency lists
+    inline int compare(const int v, const int w) {
+        return deg[v] == deg[w] ? lex_comp(neigh[v], neigh[w], deg[v]) : (deg[v] > deg[w] ? -1 : 1);
+    };
+    // Detach deg[] and neigh[]
+    void detach();
+    // Destroy deg and links
+    ~graph_molloy_opt();
+    // Create graph from file (stdin not supported unless rewind() possible)
+    graph_molloy_opt(FILE *f);
+    // Allocate memory for the graph. Create deg and links. No edge is created.
+    graph_molloy_opt(degree_sequence &);
+    // Create graph from hard copy
+    graph_molloy_opt(int *);
+    // Create hard copy of graph
+    int *hard_copy();
+    // Remove unused edges, updates neigh[], recreate links[]
+    void clean();
+    // nb arcs
+    inline int nbarcs() {
+        return a;
+    };
+    // last degree
+    inline int last_degree() {
+        return deg[n - 1];
+    };
+    // nb vertices
+    inline int nbvertices() {
+        return n;
+    };
+    // nb vertices having degree > 0
+    inline int nbvertices_real() {
+        int s = 0;
+        for (int *d = deg + n; d-- != deg; ) if (*d) {
+                s++;
+            }
+        return s;
+    };
+    // return list of vertices with degree > 0. Compute #vertices, if not given.
+    int *vertices_real(int &nb_v);
+    // Keep only giant component
+    void giant_comp();
+    // nb vertices in giant component
+    int nbvertices_comp();
+    // nb arcs in giant component
+    int nbarcs_comp();
+    // print graph in SUCC_LIST mode, in stdout
+    void print(FILE *f = stdout, bool NOZERO = true);
+    // Bind the graph avoiding multiple edges or self-edges (return false if fail)
+    bool havelhakimi();
+    // Get the graph connected  (return false if fail)
+    bool make_connected();
+    // Test if graph is connected
+    bool is_connected();
+    // Maximum degree
+    int max_degree();
+    // breadth-first search. Store the distance (modulo 3)  in dist[].
+    void breadth_search(int *dist, int v0 = 0, int* buff = NULL);
+    // is edge ?
+    inline bool is_edge(const int a, const int b) {
+        if (deg[b] < deg[a]) {
+            return (fast_search(neigh[b], deg[b], a) != NULL);
+        } else {
+            return (fast_search(neigh[a], deg[a], b) != NULL);
+        }
+    }
+    // Backup graph [sizeof(int) bytes per edge]
+    int* backup(int *here = NULL);
+    // Restore from backup. Assume that degrees haven't changed
+    void restore(int* back);
+    // Resplace with hard backup.
+    void replace(int* _hardbackup);
+    // Backup degs of graph
+    int* backup_degs(int *here = NULL);
+    // Restore degs from neigh[]. Need last degree, though
+    void restore_degs(int last_degree);
+    // Restore degs[] from backup. Assume that links[] has only been permuted
+    void restore_degs_only(int* backup_degs);
+    // Restore degs[] and neigh[]. Assume that links[] has only been permuted
+    void restore_degs_and_neigh(int* backup_degs);
+// WARNING : the following shuffle() algorithms are slow.
+// Use graph_molloy_hash::connected_shuffle() instead.
+    // "Fab" Shuffle (Optimized heuristic of Gkantsidis algo.)
+    long fab_connected_shuffle(long);
+    // "Optimized-Fab" Shuffle (Optimized heuristic of Gkantsidis algo, with isolated pairs)
+    long opt_fab_connected_shuffle(long);
+    // Gkantsidis Shuffle
+    long gkantsidis_connected_shuffle(long);
+    // Connected Shuffle
+    long slow_connected_shuffle(long);
+    // shortest paths where vertex is an extremity
+    double *vertex_betweenness(int mode, bool trivial_path = false);
+    // Sample the graph with traceroute-like exploration from src[] to dst[].
+    // if dst[]=NULL, pick nb_dst new random destinations for each src
+    double traceroute_sample(int mode, int nb_src, int *src, int nb_dst, int* dst, double *redudancy = NULL, double **edge_redudancy = NULL);
+    // does one breadth-first search and returns the average_distance.
+    double avg_dist(unsigned char *dist, int *buff, int v0, int &nb_vertices, int toclear = -1);
+    // Number of edges needed to disconnect graph (one random instance)
+    int disconnecting_edges();
+    // Compute vertex covering of the graph. Warning : this modifies degs[]
+    void vertex_covering();
+    // Path sampling. Input is nb_dst[] and dst[]. nb_dst[v],dst[v] describe all paths (v,x)
+    double path_sampling(int *nb_dst, int *dst = NULL, double *redudancies = NULL, double **edge_redudancy = NULL);
+    // keep only core (tree parts are deleted). Returns number of removed vertices.
+    int core();
+    // try to disconnect the graph by swapping edges (with isolation tests)
+    int try_disconnect(int K, int max_tries = 10000000);
+    // Eric & Cun-Hui estimator
+    double rho(int mode, int nb_src, int *src, int nb_dst, int *dst = NULL);
+    // sort adjacency lists
+    void sort();
+    // sort the vertices according to their degrees (highest first) and to their adjacency lists (lexicographic)
+    int* sort_vertices(int *buff = NULL);
+    // count cycles passing through vertex v
+    int cycles(int v);
+    // remove vertex (i.e. remove all edges adjacent to vertex)
+    void remove_vertex(int v);
+    // pick k random vertices of degree > 0. If k \in [0,1[, k is understood as a density.
+    int *pick_random_src(double k, int *nb = NULL, int* buff = NULL, int nb_v = -1, int* among = NULL);
+    // pick k random vertices of degree > 0. If k \in [0,1], k is understood as a density.
+    int *pick_random_dst(double k, int *nb = NULL, int* buff = NULL, int nb_v = -1, int* among = NULL);
+
+    // For debug purposes : verify validity of the graph (symetry, simplicity)
+#define VERIFY_NORMAL  0
+#define VERIFY_NONEIGH 1
+#define VERIFY_NOARCS  2
+    bool verify(int mode = VERIFY_NORMAL);
+
+    /*___________________________________________________________________________________
+      Not to use anymore : use graph_molloy_hash class instead
+
+
+    public:
+      // Shuffle. returns number of swaps done.
+      void shuffle(long);
+      // Connected Shuffle
+      long connected_shuffle(long);
+      // Get caracteristic K
+      double eval_K(int quality = 100);
+      // Get effective K
+      double effective_K(int K, int quality = 10000);
+      // Test window
+      double window(int K, double ratio);
+      // Try to shuffle n times. Return true if at the end, the graph was still connected.
+      bool try_shuffle(int T, int K);
+
+    //___________________________________________________________________________________
+    //*/
+
+    /*___________________________________________________________________________________
+      Not to use anymore : replaced by vertex_betweenness()     22/04/2005
+
+      // shortest paths where vertex is an extremity
+      long long *vertex_betweenness_usp(bool trivial_path);
+      // shortest paths where vertex is an extremity
+      long long *vertex_betweenness_rsp(bool trivial_path);
+      // same, but when multiple shortest path are possible, average the weights.
+      double *vertex_betweenness_asp(bool trivial_path);
+    //___________________________________________________________________________________
+    //*/
+
+};
+
+} // namespace gengraph
+
+#endif //GRAPH_MOLLOY_OPT_H
+
+
diff --git a/igraph/include/gengraph_hash.h b/igraph/include/gengraph_hash.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_hash.h
@@ -0,0 +1,308 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef HASH_H
+#define HASH_H
+
+#include <assert.h>
+#include "gengraph_definitions.h"
+
+//_________________________________________________________________________
+// Hash table profiling... Active only if definition below is uncommented
+//_________________________________________________________________________
+//#define _HASH_PROFILE
+
+namespace gengraph {
+
+#ifdef _HASH_PROFILE
+    void _hash_add_iter();
+    void _hash_add_call();
+    void _hash_put_iter();
+    void _hash_put_call();
+    void _hash_rm_iter();
+    void _hash_rm_call();
+    void _hash_find_iter();
+    void _hash_find_call();
+    void _hash_rand_iter();
+    void _hash_rand_call();
+    void _hash_expand_call();
+    void _hash_prof();
+    #define _HASH_ADD_ITER()  _hash_add_iter()
+    #define _HASH_ADD_CALL()  _hash_add_call()
+    #define _HASH_PUT_ITER()  _hash_put_iter()
+    #define _HASH_PUT_CALL()  _hash_put_call()
+    #define _HASH_RM_ITER()   _hash_rm_iter()
+    #define _HASH_RM_CALL()   _hash_rm_call()
+    #define _HASH_FIND_ITER() _hash_find_iter()
+    #define _HASH_FIND_CALL() _hash_find_call()
+    #define _HASH_RAND_ITER() _hash_rand_iter()
+    #define _HASH_RAND_CALL() _hash_rand_call()
+    #define _HASH_EXP_CALL()  _hash_expand_call()
+#else
+    #define _HASH_ADD_ITER()  {}
+    #define _HASH_ADD_CALL()  {}
+    #define _HASH_PUT_ITER()  {}
+    #define _HASH_PUT_CALL()  {}
+    #define _HASH_RM_ITER()   {}
+    #define _HASH_RM_CALL()   {}
+    #define _HASH_FIND_ITER() {}
+    #define _HASH_FIND_CALL() {}
+    #define _HASH_RAND_ITER() {}
+    #define _HASH_RAND_CALL() {}
+    #define _HASH_EXP_CALL()  {}
+#endif
+
+//_________________________________________________________________________
+// Hash Table properties. Works best when HASH_SIZE_IS_POWER2 is uncommented
+// but takes 2.25 times the needed space, in average (from 1.5 to 3)
+// If you have memory issues, Try to comment it: tables will take 1.5 times
+// the minimal space
+//_________________________________________________________________________
+
+#define HASH_SIZE_IS_POWER2
+#define MACRO_RATHER_THAN_INLINE
+
+// under HASH_MIN_SIZE, vectors are not hash table (just a simle array)
+#define HASH_MIN_SIZE 100
+#define IS_HASH(x) ((x)>HASH_MIN_SIZE)
+#define HASH_NONE (-1)
+
+#ifdef HASH_SIZE_IS_POWER2
+inline int HASH_EXPAND(int x) {
+    _HASH_EXP_CALL();
+    x += x;
+    x |= x >> 1;  x |= x >> 2;  x |= x >> 4;  x |= x >> 8;  x |= x >> 16;
+    return x + 1;
+}
+#define HASH_KEY(x,size) ((x*2198737)&((size)-1))
+#endif //HASH_SIZE_IS_POWER2
+
+#ifdef MACRO_RATHER_THAN_INLINE
+#ifndef HASH_SIZE_IS_POWER2
+    #define HASH_EXPAND(x) ((x)+((x)>>1))
+    #define HASH_UNEXPAND(x) ((((x)<<1)+1)/3)
+    #define HASH_KEY(x,size) ((x)%(size))
+#endif //HASH_SIZE_IS_POWER2
+#define HASH_SIZE(x) (IS_HASH(x) ? HASH_EXPAND(x) : (x) )
+#define HASH_REKEY(k,size) ((k)==0 ? (size)-1 : (k)-1)
+#else //MACRO_RATHER_THAN_INLINE
+#ifndef HASH_SIZE_IS_POWER2
+inline int  HASH_KEY(const int x, const int size) {
+    assert(x >= 0);
+    return x % size;
+};
+inline int  HASH_EXPAND(const int x) {
+    _HASH_EXP_CALL();
+    return x + (x >> 1);
+};
+inline int  HASH_UNEXPAND(const int x) {
+    return ((x << 1) + 1) / 3;
+};
+#endif //HASH_SIZE_IS_POWER2
+inline int  HASH_REKEY(const int k, const int s) {
+    assert(k >= 0);
+    if (k == 0) {
+        return s - 1;
+    } else {
+        return k - 1;
+    }
+};
+inline int  HASH_SIZE(const int x) {
+    if (IS_HASH(x)) {
+        return HASH_EXPAND(x);
+    } else {
+        return x;
+    }
+};
+#endif //MACRO_RATHER_THAN_INLINE
+
+inline int HASH_PAIR_KEY(const int x, const int y, const int size) {
+    return HASH_KEY(x * 1434879443 + y, size);
+}
+
+//_________________________________________________________________________
+// Hash-only functions : table must NOT be Raw.
+// the argument 'size' is the total size of the hash table
+//_________________________________________________________________________
+
+// copy hash table into raw vector
+inline void H_copy(int *mem, int *h, int size) {
+    for (int i = HASH_EXPAND(size); i--; h++) if (*h != HASH_NONE) {
+            *(mem++) = *h;
+        }
+}
+
+// Look for the place to add an element. Return NULL if element is already here.
+inline int* H_add(int* h, const int size, int a) {
+    _HASH_ADD_CALL();
+    _HASH_ADD_ITER();
+    int k = HASH_KEY(a, size);
+    if (h[k] == HASH_NONE) {
+        return h + k;
+    }
+    while (h[k] != a) {
+        _HASH_ADD_ITER();
+        k = HASH_REKEY(k, size);
+        if (h[k] == HASH_NONE) {
+            return h + k;
+        }
+    }
+    return NULL;
+}
+
+// would element be well placed in newk ?
+inline bool H_better(const int a, const int size, const int currentk, const int newk) {
+    int k = HASH_KEY(a, size);
+    if (newk < currentk) {
+        return (k < currentk && k >= newk);
+    } else {
+        return (k < currentk || k >= newk);
+    }
+}
+
+// removes h[k]
+inline void H_rm(int* h, const int size, int k) {
+    _HASH_RM_CALL();
+    int lasthole = k;
+    do {
+        _HASH_RM_ITER();
+        k = HASH_REKEY(k, size);
+        int next = h[k];
+        if (next == HASH_NONE) {
+            break;
+        }
+        if (H_better(next, size, k, lasthole)) {
+            h[lasthole] = next;
+            lasthole = k;
+        }
+    } while (true);
+    h[lasthole] = HASH_NONE;
+}
+
+//put a
+inline int* H_put(int* h, const int size, const int a) {
+    assert(H_add(h, size, a) != NULL);
+    _HASH_PUT_CALL();
+    _HASH_PUT_ITER();
+    int k = HASH_KEY(a, size);
+    while (h[k] != HASH_NONE) {
+        k = HASH_REKEY(k, size);
+        _HASH_PUT_ITER();
+    }
+    h[k] = a;
+    assert(H_add(h, size, a) == NULL);
+    return h + k;
+}
+
+// find A
+inline int H_find(int *h, int size, const int a) {
+    assert(H_add(h, size, a) == NULL);
+    _HASH_FIND_CALL();
+    _HASH_FIND_ITER();
+    int k = HASH_KEY(a, size);
+    while (h[k] != a) {
+        k = HASH_REKEY(k, size);
+        _HASH_FIND_ITER();
+    }
+    return k;
+}
+
+// Look for the place to add an element. Return NULL if element is already here.
+inline bool H_pair_insert(int* h, const int size, int a, int b) {
+    _HASH_ADD_CALL();
+    _HASH_ADD_ITER();
+    int k = HASH_PAIR_KEY(a, b, size);
+    if (h[2 * k] == HASH_NONE) {
+        h[2 * k] = a;
+        h[2 * k + 1] = b;
+        return true;
+    }
+    while (h[2 * k] != a || h[2 * k + 1] != b) {
+        _HASH_ADD_ITER();
+        k = HASH_REKEY(k, size);
+        if (h[2 * k] == HASH_NONE) {
+            h[2 * k] = a;
+            h[2 * k + 1] = b;
+            return true;
+        }
+    }
+    return false;
+}
+
+
+//_________________________________________________________________________
+// Generic functions : table can be either Hash or Raw.
+// the argument 'size' is the number of elements
+//_________________________________________________________________________
+
+// Look for an element
+inline bool H_is(int *mem, const int size, const int elem) {
+    if (IS_HASH(size)) {
+        return (H_add(mem, HASH_EXPAND(size), elem) == NULL);
+    } else {
+        return fast_search(mem, size, elem) != NULL;
+    }
+}
+
+//pick random location (containing an element)
+inline int* H_random(int* mem, int size) {
+    if (!IS_HASH(size)) {
+        return mem + (my_random() % size);
+    }
+    _HASH_RAND_CALL();
+    size = HASH_EXPAND(size);
+    int* yo;
+    do {
+        yo = mem + HASH_KEY(my_random(), size);
+        _HASH_RAND_ITER();
+    } while (*yo == HASH_NONE);
+    return yo;
+}
+
+// replace *k by b
+inline int* H_rpl(int *mem, int size, int* k, const int b) {
+    assert(!H_is(mem, size, b));
+    if (!IS_HASH(size)) {
+        *k = b;
+        return k;
+    } else {
+        size = HASH_EXPAND(size);
+        assert(mem + int(k - mem) == k);
+        H_rm(mem, size, int(k - mem));
+        return H_put(mem, size, b);
+    }
+}
+
+// replace a by b
+inline int* H_rpl(int *mem, int size, const int a, const int b) {
+    assert(H_is(mem, size, a));
+    assert(!H_is(mem, size, b));
+    if (!IS_HASH(size)) {
+        return fast_rpl(mem, a, b);
+    } else {
+        size = HASH_EXPAND(size);
+        H_rm(mem, size, H_find(mem, size, a));
+        return H_put(mem, size, b);
+    }
+}
+
+} // namespace gengraph
+
+#endif //HASH_H
diff --git a/igraph/include/gengraph_header.h b/igraph/include/gengraph_header.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_header.h
@@ -0,0 +1,120 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "gengraph_definitions.h"
+#include <cstdlib>
+#include <stdio.h>
+
+#include "gengraph_random.h"
+
+namespace gengraph {
+
+static KW_RNG::RNG _my_random;
+int my_random() {
+    return _my_random.rand_int31();
+}
+void my_srandom(int x) {
+    _my_random.init(x, !x * 13, x * x + 1, (x >> 16) + (x << 16));
+}
+int my_binomial(double pp, int n) {
+    return _my_random.binomial(pp, n);
+}
+double my_random01() {
+    return _my_random.rand_halfopen01();
+}
+
+}
+
+#ifdef _WIN32
+#include <process.h>
+#include <windows.h>
+void set_priority_low() {
+    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, _getpid());
+    SetPriorityClass(hProcess, IDLE_PRIORITY_CLASS);
+}
+#else
+#include <unistd.h>
+#endif
+
+namespace gengraph {
+
+static int VERB;
+int VERBOSE() {
+    return VERB;
+}
+void SET_VERBOSE(int v) {
+    VERB = v;
+}
+
+//Hash profiling
+static unsigned long _hash_rm_i   = 0;
+static unsigned long _hash_rm_c   = 0;
+static unsigned long _hash_add_i  = 0;
+static unsigned long _hash_add_c  = 0;
+static unsigned long _hash_put_i  = 0;
+static unsigned long _hash_put_c  = 0;
+static unsigned long _hash_find_i = 0;
+static unsigned long _hash_find_c = 0;
+static unsigned long _hash_rand_i = 0;
+static unsigned long _hash_rand_c = 0;
+static unsigned long _hash_expand = 0;
+inline void _hash_add_iter()  {
+    _hash_add_i++;
+}
+inline void _hash_add_call()  {
+    _hash_add_c++;
+}
+inline void _hash_put_iter()  {
+    _hash_put_i++;
+}
+inline void _hash_put_call()  {
+    _hash_put_c++;
+}
+inline void _hash_rm_iter()   {
+    _hash_rm_i++;
+}
+inline void _hash_rm_call()   {
+    _hash_rm_c++;
+}
+inline void _hash_find_iter() {
+    _hash_find_i++;
+}
+inline void _hash_find_call() {
+    _hash_find_c++;
+}
+inline void _hash_rand_iter() {
+    _hash_rand_i++;
+}
+inline void _hash_rand_call() {
+    _hash_rand_c++;
+}
+inline void _hash_expand_call() {
+    _hash_expand++;
+}
+// void _hash_prof() {
+//   fprintf(stderr,"HASH_ADD : %lu / %lu\n", _hash_add_c , _hash_add_i);
+//   fprintf(stderr,"HASH_PUT : %lu / %lu\n", _hash_put_c , _hash_put_i);
+//   fprintf(stderr,"HASH_FIND: %lu / %lu\n", _hash_find_c, _hash_find_i);
+//   fprintf(stderr,"HASH_RM  : %lu / %lu\n", _hash_rm_c  , _hash_rm_i);
+//   fprintf(stderr,"HASH_RAND: %lu / %lu\n", _hash_rand_c, _hash_rand_i);
+//   fprintf(stderr,"HASH_EXPAND : %lu calls\n", _hash_expand);
+// }
+
+} // namespace gengraph
diff --git a/igraph/include/gengraph_powerlaw.h b/igraph/include/gengraph_powerlaw.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_powerlaw.h
@@ -0,0 +1,86 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _POWERLAW_H
+#define _POWERLAW_H
+
+// pascalou
+#ifndef pascalou
+    #include "gengraph_definitions.h"
+#endif
+
+// Discrete integer power-law : P(X=min+k) is proportionnal to (k+k0)^-alpha
+// - possibility to determine a range [Min, Max] of possible samples
+// - possibility to automatically compute k0 to obtain a given mean z
+
+namespace gengraph {
+
+#define POWERLAW_TABLE 10000
+
+class powerlaw {
+private:
+    double alpha;  // Exponent
+    int mini; // Minimum sample
+    int maxi; // Maximum sample
+    double offset; // Offset
+    int tabulated; // Number of values to tabulate
+    int *table;    // Table containing cumulative distribution for k=mini..mini+tabulated-1
+    int *dt;        // Table delimiters
+    int max_dt;     // number of delimiters - 1
+    double proba_big;   // Probability to take a non-tabulated value
+    double table_mul;   // equal to (1-proba_big)/(RAND_MAX+1)
+
+    // Sample a non-tabulated value >= mini+tabulated
+    inline double big_sample(double randomfloat) {
+        return double(mini) + pow(_a * randomfloat + _b, _exp) - offset;
+    }
+    inline double big_inv_sample(double s) {
+        return (pow(s - double(mini) + offset, 1.0 / _exp) - _b) / _a;
+    }
+    double _exp, _a, _b; // Cached values used by big_sample();
+
+    // Dichotomic adjust of offset, so that to_adjust() returns value with
+    // a precision of eps. Note that to_adjust() must be an increasing function of offset.
+    void adjust_offset_mean(double value, double eps, double fac);
+
+public:
+    int sample();      // Return a random integer
+    double proba(int); // Return probability to return integer
+    double error();    // Returns relative numerical error done by this class
+    double mean();     // Returns mean of the sampler
+    int median();      // Returns median of the sampler
+
+    // Initialize the power-law sampler.
+    void init_to_offset(double, int);
+    // Same, but also returns the offset found
+    double init_to_mean(double);
+    double init_to_median(double);
+
+    inline void init() {
+        init_to_offset(double(mini), POWERLAW_TABLE);
+    };
+
+    ~powerlaw();
+    powerlaw(double exponent, int mini, int maxi = -1);
+};
+
+} // namespace gengraph
+
+#endif //_POWERLAW_H
diff --git a/igraph/include/gengraph_qsort.h b/igraph/include/gengraph_qsort.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_qsort.h
@@ -0,0 +1,568 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QSORT_H
+#define QSORT_H
+
+#include <assert.h>
+#include <stdio.h>
+
+#ifndef register
+    #define register
+#endif
+
+namespace gengraph {
+
+//___________________________________________________________________________
+// check if every element is zero
+inline bool check_zero(int *mem, int n) {
+    for (int *v = mem + n; v != mem; ) if (*(--v) != 0) {
+            return false;
+        }
+    return true;
+}
+
+//___________________________________________________________________________
+//  Sort simple integer arrays in ASCENDING order
+//___________________________________________________________________________
+inline int med3(int a, int b, int c) {
+    if (a < b) {
+        if (c < b) {
+            return (a < c) ? c : a;
+        } else {
+            return b;
+        }
+    } else {
+        if (c < a) {
+            return (b < c) ? c : b;
+        } else {
+            return a;
+        }
+    }
+}
+
+inline void isort(int *v, int t) {
+    if (t < 2) {
+        return;
+    }
+    for (int i = 1; i < t; i++) {
+        register int *w = v + i;
+        int tmp = *w;
+        while (w != v && *(w - 1) > tmp) {
+            *w = *(w - 1);
+            w--;
+        }
+        *w = tmp;
+    }
+}
+
+inline int partitionne(int *v, int t, int p) {
+    int i = 0;
+    int j = t - 1;
+    while (i < j) {
+        while (i <= j && v[i] < p) {
+            i++;
+        }
+        while (i <= j && v[j] > p) {
+            j--;
+        }
+        if (i < j) {
+            int tmp = v[i];
+            v[i++] = v[j];
+            v[j--] = tmp;
+        }
+    }
+    if (i == j && v[i] < p) {
+        i++;
+    }
+    assert(i != 0 && i != t);
+    return i;
+}
+
+inline void qsort(int *v, int t) {
+    if (t < 15) {
+        isort(v, t);
+    } else {
+        int x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2]));
+        qsort(v, x);
+        qsort(v + x, t - x);
+    }
+}
+
+inline int qsort_median(int *v, int t, int pos) {
+    if (t < 10) {
+        isort(v, t);
+        return v[pos];
+    }
+    int x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2]));
+    if (pos < x) {
+        return qsort_median(v, x, pos);
+    } else {
+        return qsort_median(v + x, t - x, pos - x);
+    }
+}
+
+inline int qsort_median(int *v, int t) {
+    return qsort_median(v, t, t / 2);
+}
+
+//___________________________________________________________________________
+//  Sort simple double arrays in ASCENDING order
+//___________________________________________________________________________
+inline double med3(double a, double b, double c) {
+    if (a < b) {
+        if (c < b) {
+            return (a < c) ? c : a;
+        } else {
+            return b;
+        }
+    } else {
+        if (c < a) {
+            return (b < c) ? c : b;
+        } else {
+            return a;
+        }
+    }
+}
+
+inline void isort(double *v, int t) {
+    if (t < 2) {
+        return;
+    }
+    for (int i = 1; i < t; i++) {
+        register double *w = v + i;
+        double tmp = *w;
+        while (w != v && *(w - 1) > tmp) {
+            *w = *(w - 1);
+            w--;
+        }
+        *w = tmp;
+    }
+}
+
+inline int partitionne(double *v, int t, double p) {
+    int i = 0;
+    int j = t - 1;
+    while (i < j) {
+        while (i <= j && v[i] < p) {
+            i++;
+        }
+        while (i <= j && v[j] > p) {
+            j--;
+        }
+        if (i < j) {
+            double tmp = v[i];
+            v[i++] = v[j];
+            v[j--] = tmp;
+        }
+    }
+    if (i == j && v[i] < p) {
+        i++;
+    }
+    assert(i != 0 && i != t);
+    return i;
+}
+
+inline void qsort(double *v, int t) {
+    if (t < 15) {
+        isort(v, t);
+    } else {
+        int x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2]));
+        qsort(v, x);
+        qsort(v + x, t - x);
+    }
+}
+
+inline double qsort_median(double *v, int t, int pos) {
+    if (t < 10) {
+        isort(v, t);
+        return v[pos];
+    }
+    int x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2]));
+    if (pos < x) {
+        return qsort_median(v, x, pos);
+    } else {
+        return qsort_median(v + x, t - x, pos - x);
+    }
+}
+
+inline double qsort_median(double *v, int t) {
+    return qsort_median(v, t, t / 2);
+}
+
+//___________________________________________________________________________
+// Sort integer arrays according to value stored in mem[], in ASCENDING order
+inline void isort(int *mem, int *v, int t) {
+    if (t < 2) {
+        return;
+    }
+    for (int i = 1; i < t; i++) {
+        int vtmp = v[i];
+        int tmp = mem[vtmp];
+        int j;
+        for (j = i; j > 0 && tmp < mem[v[j - 1]]; j--) {
+            v[j] = v[j - 1];
+        }
+        v[j] = vtmp;
+    }
+}
+
+inline void qsort(int *mem, int *v, int t) {
+    if (t < 15) {
+        isort(mem, v, t);
+    } else {
+        int p = med3(mem[v[t >> 1]], mem[v[(t >> 2) + 3]], mem[v[t - (t >> 1) - 3]]);
+        int i = 0;
+        int j = t - 1;
+        while (i < j) {
+            while (i <= j && mem[v[i]] < p) {
+                i++;
+            }
+            while (i <= j && mem[v[j]] > p) {
+                j--;
+            }
+            if (i < j) {
+                int tmp = v[i];
+                v[i++] = v[j];
+                v[j--] = tmp;
+            }
+        }
+        if (i == j && mem[v[i]] < p) {
+            i++;
+        }
+        assert(i != 0 && i != t);
+        qsort(mem, v, i);
+        qsort(mem, v + i, t - i);
+    }
+}
+
+//Box-Sort 1..n according to value stored in mem[], in DESCENDING order.
+inline int *pre_boxsort(int *mem, int n, int &offset) {
+    int *yo;
+    // maximum and minimum
+    int mx = mem[0];
+    int mn = mem[0];
+    for (yo = mem + n - 1; yo != mem; yo--) {
+        register int x = *yo;
+        if (x > mx) {
+            mx = x;
+        }
+        if (x < mn) {
+            mn = x;
+        }
+    }
+    // box
+    int c = mx - mn + 1;
+    int *box = new int[c];
+    for (yo = box + c; yo != box; * (--yo) = 0) { }
+    for (yo = mem + n; yo != mem; box[*(--yo) - mn]++) { }
+    // cumul sum
+    int sum = 0;
+    for (yo = box + c; yo != box; ) {
+        sum += *(--yo);
+        *yo = sum;
+    }
+    offset = mn;
+    return box;
+}
+
+inline int *boxsort(int *mem, int n, int *buff = NULL) {
+    int i;
+    if (n <= 0) {
+        return buff;
+    }
+    int offset = 0;
+    int *box = pre_boxsort(mem, n, offset);
+    // sort
+    if (buff == NULL) {
+        buff = new int[n];
+    }
+    for (i = 0; i < n; i++) {
+        buff[--box[mem[i] - offset]] = i;
+    }
+    // clean
+    delete[] box;
+    return buff;
+}
+
+// merge two sorted arays in their intersection. Store the result in first array, and return length
+inline int intersect(int *a, int a_len, int *b, int b_len) {
+    if (a_len == 0 || b_len == 0) {
+        return 0;
+    }
+    int *asup = a + a_len;
+    int *bsup = b + b_len;
+    int len = 0;
+    int *p = a;
+    do {
+        if (*a == *b) {
+            p[len++] = *a;
+        }
+        do if (++a == asup) {
+                return len;
+            } while (*a < *b);
+        if (*a == *b) {
+            p[len++] = *a;
+        }
+        do if (++b == bsup) {
+                return len;
+            } while (*b < *a);
+    } while (true);
+}
+
+// merge two sorted arays in their union, store result in m
+inline int unify(int *m, int *a, int a_len, int *b, int b_len) {
+    int *asup = a + a_len;
+    int *bsup = b + b_len;
+    int len = 0;
+    while (a != asup && b != bsup) {
+        if (*a < *b) {
+            m[len++] = *(a++);
+        } else {
+            if (*a == *b) {
+                a++;
+            }
+            m[len++] = *(b++);
+        }
+    }
+    while (a != asup) {
+        m[len++] = *(a++);
+    }
+    while (b != asup) {
+        m[len++] = *(b++);
+    }
+    return len;
+}
+
+// lexicographic compare
+inline int lex_comp(int *v1, int *v2, int n) {
+    int *stop = v1 + n;
+    while (v1 != stop && *v1 == *v2) {
+        v1++;
+        v2++;
+    };
+    if (v1 == stop) {
+        return 0;
+    } else if (*v1 < *v2) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+// lexicographic median of three
+inline int *lex_med3(int *a, int *b, int *c, int s) {
+    int ab = lex_comp(a, b, s);
+    if (ab == 0) {
+        return a;
+    } else {
+        int cb = lex_comp(c, b, s);
+        if (cb == 0) {
+            return b;
+        }
+        int ca = lex_comp(c, a, s);
+        if (ab < 0) {
+            if (cb > 0) {
+                return b;
+            } else {
+                return (ca > 0) ? c : a;
+            }
+        } else     {
+            if (cb < 0) {
+                return b;
+            } else {
+                return (ca < 0) ? c : a;
+            }
+        }
+    }
+}
+
+// Lexicographic sort
+inline void lex_isort(int **l, int *v, int t, int s) {
+    if (t < 2) {
+        return;
+    }
+    for (int i = 1; i < t; i++) {
+        register int *w = v + i;
+        int tmp = *w;
+        while (w != v && lex_comp(l[tmp], l[*(w - 1)], s) < 0) {
+            *w = *(w - 1);
+            w--;
+        }
+        *w = tmp;
+    }
+}
+
+#ifdef _STABLE_SORT_ONLY
+    #define _CRITICAL_SIZE_QSORT 0x7FFFFFFF
+    #warning "lex_qsort will be replaced by lex_isort"
+#else
+    #define _CRITICAL_SIZE_QSORT 15
+#endif
+
+inline void lex_qsort(int **l, int *v, int t, int s) {
+
+    if (t < _CRITICAL_SIZE_QSORT) {
+        lex_isort(l, v, t, s);
+    } else {
+        int *p = lex_med3(l[v[t >> 1]], l[v[(t >> 2) + 2]], l[v[t - (t >> 1) - 2]], s);
+        int i = 0;
+        int j = t - 1;
+//    printf("pivot = %d\n",p);
+        while (i < j) {
+//      for(int k=0; k<t; k++) printf("%d ",v[k]);
+            while (i <= j && lex_comp(l[v[i]], p, s) < 0) {
+                i++;
+            }
+            while (i <= j && lex_comp(l[v[j]], p, s) > 0) {
+                j--;
+            }
+            if (i < j) {
+//        printf("  swap %d[%d] with %d[%d]\n",i,v[i],j,v[j]);
+                int tmp = v[i];
+                v[i++] = v[j];
+                v[j--] = tmp;
+            }
+        }
+        if (i == j && lex_comp(l[v[i]], p, s) < 0) {
+            i++;
+        }
+        assert(i != 0 && i != t);
+        lex_qsort(l, v, i, s);
+        lex_qsort(l, v + i, t - i, s);
+    }
+}
+
+// lexicographic indirect compare
+inline int lex_comp_indirect(int *key, int *v1, int *v2, int n) {
+    int *stop = v1 + n;
+    while (v1 != stop && key[*v1] == key[*v2]) {
+        v1++;
+        v2++;
+    };
+    if (v1 == stop) {
+        return 0;
+    } else if (key[*v1] < key[*v2]) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+inline int qsort_min(const int a, const int b) {
+    return a <= b ? a : b;
+}
+
+// mix indirect compare
+inline int mix_comp_indirect(int *key, int a, int b, int **neigh, int *degs) {
+    if (key[a] < key[b]) {
+        return -1;
+    } else if (key[a] > key[b]) {
+        return 1;
+    } else {
+        int cmp = lex_comp_indirect(key, neigh[a], neigh[b], qsort_min(degs[a], degs[b]));
+        if (cmp == 0) {
+            if (degs[a] > degs[b]) {
+                return -1;
+            }
+            if (degs[a] < degs[b]) {
+                return 1;
+            }
+        }
+        return cmp;
+    }
+}
+// lexicographic indirect median of three
+inline int mix_med3_indirect(int *key, int a, int b, int c, int **neigh, int *degs) {
+    int ab = mix_comp_indirect(key, a, b, neigh, degs);
+    if (ab == 0) {
+        return a;
+    } else {
+        int cb = mix_comp_indirect(key, c, b, neigh, degs);
+        if (cb == 0) {
+            return b;
+        }
+        int ca = mix_comp_indirect(key, c, a, neigh, degs);
+        if (ab < 0) {
+            if (cb > 0) {
+                return b;
+            } else {
+                return (ca > 0) ? c : a;
+            }
+        } else     {
+            if (cb < 0) {
+                return b;
+            } else {
+                return (ca < 0) ? c : a;
+            }
+        }
+    }
+}
+
+// Sort integer arrays in ASCENDING order
+inline void mix_isort_indirect(int *key, int *v, int t, int **neigh, int *degs) {
+    if (t < 2) {
+        return;
+    }
+    for (int i = 1; i < t; i++) {
+        register int *w = v + i;
+        int tmp = *w;
+        while (w != v && mix_comp_indirect(key, tmp, *(w - 1), neigh, degs) < 0) {
+            *w = *(w - 1);
+            w--;
+        }
+        *w = tmp;
+    }
+}
+
+inline void mix_qsort_indirect(int *key, int *v, int t, int **neigh, int *degs) {
+    if (t < 15) {
+        mix_isort_indirect(key, v, t, neigh, degs);
+    } else {
+        int p = mix_med3_indirect(key, v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2], neigh, degs);
+        int i = 0;
+        int j = t - 1;
+//    printf("pivot = %d\n",p);
+        while (i < j) {
+//      for(int k=0; k<t; k++) printf("%d ",v[k]);
+            while (i <= j && mix_comp_indirect(key, v[i], p, neigh, degs) < 0) {
+                i++;
+            }
+            while (i <= j && mix_comp_indirect(key, v[j], p, neigh, degs) > 0) {
+                j--;
+            }
+            if (i < j) {
+//        printf("  swap %d[%d] with %d[%d]\n",i,v[i],j,v[j]);
+                int tmp = v[i];
+                v[i++] = v[j];
+                v[j--] = tmp;
+            }
+        }
+        if (i == j && mix_comp_indirect(key, v[i], p, neigh, degs) < 0) {
+            i++;
+        }
+        assert(i != 0 && i != t);
+        mix_qsort_indirect(key, v, i, neigh, degs);
+        mix_qsort_indirect(key, v + i, t - i, neigh, degs);
+    }
+}
+
+} // namespace gengraph
+
+#endif //QSORT_H
diff --git a/igraph/include/gengraph_random.h b/igraph/include/gengraph_random.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_random.h
@@ -0,0 +1,216 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef RNG_H
+#define RNG_H
+
+#include "igraph_random.h"
+#include <iostream>
+using namespace std;
+
+namespace KW_RNG {
+
+typedef signed int  sint;
+typedef unsigned int uint;
+typedef signed long  slong;
+typedef unsigned long ulong;
+
+class RNG {
+public:
+    RNG() { }
+    RNG(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) {
+        IGRAPH_UNUSED(z_); IGRAPH_UNUSED(w_); IGRAPH_UNUSED(jsr_);
+        IGRAPH_UNUSED(jcong_);
+    };
+    ~RNG() { }
+
+    void init(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) {
+        IGRAPH_UNUSED(z_); IGRAPH_UNUSED(w_); IGRAPH_UNUSED(jsr_);
+        IGRAPH_UNUSED(jcong_);
+    }
+    long rand_int31() {
+        return RNG_INT31();
+    }
+    double rand_halfopen01() { // (0,1]
+        return RNG_UNIF01();
+    }
+    int binomial(double pp, int n) {
+        return RNG_BINOM(n, pp);
+    }
+};
+
+} // namespace KW_RNG
+
+/* This was the original RNG, but now we use the igraph version */
+
+// __________________________________________________________________________
+// random.h   - a Random Number Generator Class
+// random.cpp - contains the non-inline class methods
+
+// __________________________________________________________________________
+// This C++ code uses the simple, very fast "KISS" (Keep It Simple
+// Stupid) random number generator suggested by George Marsaglia in a
+// Usenet posting from 1999.  He describes it as "one of my favorite
+// generators".  It generates high-quality random numbers that
+// apparently pass all commonly used tests for randomness.  In fact, it
+// generates random numbers by combining the results of three other good
+// random number generators that have different periods and are
+// constructed from completely different algorithms.  It does not have
+// the ultra-long period of some other generators - a "problem" that can
+// be fixed fairly easily - but that seems to be its only potential
+// problem.  The period is about 2^123.
+
+// The ziggurat method of Marsaglia is used to generate exponential and
+// normal variates.  The method as well as source code can be found in
+// the article "The Ziggurat Method for Generating Random Variables" by
+// Marsaglia and Tsang, Journal of Statistical Software 5, 2000.
+
+// The method for generating gamma variables appears in "A Simple Method
+// for Generating Gamma Variables" by Marsaglia and Tsang, ACM
+// Transactions on Mathematical Software, Vol. 26, No 3, Sep 2000, pages
+// 363-372.
+
+// The code for Poisson and Binomial random numbers comes from
+// Numerical Recipes in C.
+
+// Some of this code is unlikely to work correctly as is on 64 bit
+// machines.
+
+// #include <cstdlib>
+// #include <ctime>
+// #ifdef _WIN32
+// #include <process.h>
+// #define getpid _getpid
+// #else
+// #include <unistd.h>
+// #endif
+
+// //#ifdef _WIN32
+//   static const double PI   =  3.1415926535897932;
+//   static const double AD_l =  0.6931471805599453;
+//   static const double AD_a =  5.7133631526454228;
+//   static const double AD_b =  3.4142135623730950;
+//   static const double AD_c = -1.6734053240284925;
+//   static const double AD_p =  0.9802581434685472;
+//   static const double AD_A =  5.6005707569738080;
+//   static const double AD_B =  3.3468106480569850;
+//   static const double AD_H =  0.0026106723602095;
+//   static const double AD_D =  0.0857864376269050;
+// //#endif //_WIN32
+
+// namespace KW_RNG {
+
+// class RNG
+// {
+// private:
+//   ulong z, w, jsr, jcong; // Seeds
+
+//   ulong kn[128], ke[256];
+//   double wn[128],fn[128], we[256],fe[256];
+
+// /*
+// #ifndef _WIN32
+//   static const double PI   =  3.1415926535897932;
+//   static const double AD_l =  0.6931471805599453;
+//   static const double AD_a =  5.7133631526454228;
+//   static const double AD_b =  3.4142135623730950;
+//   static const double AD_c = -1.6734053240284925;
+//   static const double AD_p =  0.9802581434685472;
+//   static const double AD_A =  5.6005707569738080;
+//   static const double AD_B =  3.3468106480569850;
+//   static const double AD_H =  0.0026106723602095;
+//   static const double AD_D =  0.0857864376269050;
+// #endif //_WIN32
+// */
+
+// public:
+//   RNG() { init(); zigset(); }
+//   RNG(ulong z_, ulong w_, ulong jsr_, ulong jcong_ ) :
+//     z(z_), w(w_), jsr(jsr_), jcong(jcong_) { zigset(); }
+//   ~RNG() { }
+
+
+//   inline ulong znew()
+//     { return (z = 36969 * (z & 65535) + (z >> 16)); }
+//   inline ulong wnew()
+//     { return (w = 18000 * (w & 65535) + (w >> 16)); }
+//   inline ulong MWC()
+//     { return (((znew() & 65535) << 16) + wnew()); }
+//   inline ulong SHR3()
+//     { jsr ^= ((jsr & 32767) << 17); jsr ^= (jsr >> 13); return (jsr ^= ((jsr << 5) & 0xFFFFFFFF)); }
+//   inline ulong CONG()
+//     { return (jcong = (69069 * jcong + 1234567) & 0xFFFFFFFF); }
+//   inline double RNOR() {
+//     slong h = rand_int32();
+//     ulong i = h & 127;
+//     return (((ulong) abs((sint) h) < kn[i]) ? h * wn[i] : nfix(h, i));
+//   }
+//   inline double REXP() {
+//     ulong j = rand_int32();
+//     ulong i = j & 255;
+//     return ((j < ke[i]) ? j * we[i] : efix(j, i));
+//   }
+
+//   double nfix(slong h, ulong i);
+//   double efix(ulong j, ulong i);
+//   void zigset();
+
+//   inline void init()
+//     { ulong yo = time(0) + getpid();
+//       z = w = jsr = jcong = yo; }
+//   inline void init(ulong z_, ulong w_, ulong jsr_, ulong jcong_ )
+//     { z = z_; w = w_; jsr = jsr_; jcong = jcong_; }
+
+//   inline ulong rand_int32()         // [0,2^32-1]
+//     { return ((MWC() ^ CONG()) + SHR3()) & 0xFFFFFFFF; }
+//   inline long rand_int31()          // [0,2^31-1]
+//     { return long(rand_int32() >> 1);}
+//   inline double rand_closed01()     // [0,1]
+//     { return ((double) rand_int32() / 4294967295.0); }
+//   inline double rand_open01()       // (0,1)
+//     { return (((double) rand_int32() + 0.5) / 4294967296.0); }
+//   inline double rand_halfclosed01() // [0,1)
+//     { return ((double) rand_int32() / 4294967296.0); }
+//   inline double rand_halfopen01()   // (0,1]
+//     { return (((double) rand_int32() + 0.5) / 4294967295.5); }
+
+//   // Continuous Distributions
+//   inline double uniform(double x = 0.0, double y = 1.0)
+//     { return rand_closed01() * (y - x) + x; }
+//   inline double normal(double mu = 0.0, double sd = 1.0)
+//     { return RNOR() * sd + mu; }
+//   inline double exponential(double lambda = 1)
+//     { return REXP() / lambda; }
+//   double gamma(double shape = 1, double scale = 1);
+//   double chi_square(double df)
+//     { return gamma(df / 2.0, 0.5); }
+//   double beta(double a1, double a2)
+//     { double x1 = gamma(a1, 1); return (x1 / (x1 + gamma(a2, 1))); }
+
+//   // Discrete Distributions
+//   double poisson(double lambda);
+//   int binomial(double pp, int n);
+
+// }; // class RNG
+
+// } // namespace
+
+#endif // RNG_H
+
diff --git a/igraph/include/gengraph_vertex_cover.h b/igraph/include/gengraph_vertex_cover.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/gengraph_vertex_cover.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _VERTEX_COVER_H
+#define _VERTEX_COVER_H
+
+// vertex_cover() builds a list of vertices which covers every edge of the graph
+// Input is a classical adjacency-list graph
+// As an output, vertex_cover() modify the degrees in degs[], so that
+// any vertex with a degree > 0 belongs to the vertex coverage.
+// Moreover, vertex_cover() keeps links[] intact, permuting only the adjacency lists
+
+#include "gengraph_box_list.h"
+
+#ifndef register
+    #define register
+#endif
+
+namespace gengraph {
+
+void vertex_cover(int n, int *links, int *deg, int **neigh = NULL) {
+    int i;
+    // create and initialize neigh[]
+    if (neigh == NULL) {
+        neigh = new int*[n];
+        neigh[0] = links;
+        for (i = 1; i < n; i++) {
+            neigh[i] = neigh[i - 1] + deg[i];
+        }
+    }
+    // create box_list
+    box_list bl(n, deg);
+    do {
+        int v;
+        // remove vertices adjacent to vertices of degree 1
+        while ((v = bl.get_one()) >= 0) {
+            bl.pop_vertex(v, neigh);
+        }
+        // remove vertex of max degree and its highest-degree neighbour
+        if (!bl.is_empty()) {
+            v = bl.get_max();
+            int *w = neigh[v];
+            register int v2 = *(w++);
+            register int dm = deg[v2];
+            register int k = deg[v] - 1;
+            while (k--) if (deg[*(w++)] > dm) {
+                    v2 = *(w - 1);
+                    dm = deg[v2];
+                };
+            bl.pop_vertex(v, neigh);
+            bl.pop_vertex(v2, neigh);
+        }
+    } while (!bl.is_empty());
+}
+
+} // namespace gengraph
+
+#endif //_VERTEX_COVER_H
diff --git a/igraph/include/heap.pmt b/igraph/include/heap.pmt
new file mode 100644
--- /dev/null
+++ b/igraph/include/heap.pmt
@@ -0,0 +1,350 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+
+#define PARENT(x)     (((x)+1)/2-1)
+#define LEFTCHILD(x)  (((x)+1)*2-1)
+#define RIGHTCHILD(x) (((x)+1)*2)
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_init
+ * \brief Initializes an empty heap object.
+ *
+ * Creates an empty heap, but allocates size for some elements.
+ * \param h Pointer to an uninitialized heap object.
+ * \param alloc_size Number of elements to allocate memory for.
+ * \return Error code.
+ *
+ * Time complexity: O(\p alloc_size), assuming memory allocation is a
+ * linear operation.
+ */
+
+int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int alloc_size) {
+    if (alloc_size <= 0 ) {
+        alloc_size = 1;
+    }
+    h->stor_begin = igraph_Calloc(alloc_size, BASE);
+    if (h->stor_begin == 0) {
+        IGRAPH_ERROR("heap init failed", IGRAPH_ENOMEM);
+    }
+    h->stor_end = h->stor_begin + alloc_size;
+    h->end = h->stor_begin;
+    h->destroy = 1;
+
+    return 0;
+}
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_init_array
+ * \brief Build a heap from an array.
+ *
+ * Initializes a heap object from an array, the heap is also
+ * built of course (constructor).
+ * \param h Pointer to an uninitialized heap object.
+ * \param data Pointer to an array of base data type.
+ * \param len The length of the array at \p data.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of elements in the heap.
+ */
+
+int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, BASE* data, long int len) {
+    h->stor_begin = igraph_Calloc(len, BASE);
+    if (h->stor_begin == 0) {
+        IGRAPH_ERROR("heap init from array failed", IGRAPH_ENOMEM);
+    }
+    h->stor_end = h->stor_begin + len;
+    h->end = h->stor_end;
+    h->destroy = 1;
+
+    memcpy(h->stor_begin, data, (size_t) len * sizeof(igraph_real_t));
+
+    FUNCTION(igraph_heap, i_build) (h->stor_begin, h->end - h->stor_begin, 0);
+
+    return 0;
+}
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_destroy
+ * \brief Destroys an initialized heap object.
+ *
+ * \param h The heap object.
+ *
+ * Time complexity: O(1).
+ */
+
+void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h) {
+    if (h->destroy) {
+        if (h->stor_begin != 0) {
+            igraph_Free(h->stor_begin);
+            h->stor_begin = 0;
+        }
+    }
+}
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_empty
+ * \brief Decides whether a heap object is empty.
+ *
+ * \param h The heap object.
+ * \return \c TRUE if the heap is empty, \c FALSE otherwise.
+ *
+ * TIme complexity: O(1).
+ */
+
+igraph_bool_t FUNCTION(igraph_heap, empty)(TYPE(igraph_heap)* h) {
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+    return h->stor_begin == h->end;
+}
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_push
+ * \brief Add an element.
+ *
+ * Adds an element to the heap.
+ * \param h The heap object.
+ * \param elem The element to add.
+ * \return Error code.
+ *
+ * Time complexity: O(log n), n is the number of elements in the
+ * heap if no reallocation is needed, O(n) otherwise. It is ensured
+ * that n push operations are performed in O(n log n) time.
+ */
+
+int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) {
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+
+    /* full, allocate more storage */
+    if (h->stor_end == h->end) {
+        long int new_size = FUNCTION(igraph_heap, size)(h) * 2;
+        if (new_size == 0) {
+            new_size = 1;
+        }
+        IGRAPH_CHECK(FUNCTION(igraph_heap, reserve)(h, new_size));
+    }
+
+    *(h->end) = elem;
+    h->end += 1;
+
+    /* maintain heap */
+    FUNCTION(igraph_heap, i_shift_up)(h->stor_begin, FUNCTION(igraph_heap, size)(h),
+                                      FUNCTION(igraph_heap, size)(h) - 1);
+
+    return 0;
+}
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_top
+ * \brief Top element.
+ *
+ * For maximum heaps this is the largest, for minimum heaps the
+ * smallest element of the heap.
+ * \param h The heap object.
+ * \return The top element.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_heap, top)(TYPE(igraph_heap)* h) {
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+    assert(h->stor_begin != h->end);
+
+    return h->stor_begin[0];
+}
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_delete_top
+ * \brief Return and removes the top element
+ *
+ * Removes and returns the top element of the heap. For maximum heaps
+ * this is the largest, for minimum heaps the smallest element.
+ * \param h The heap object.
+ * \return The top element.
+ *
+ * Time complexity: O(log n), n is the number of elements in the
+ * heap.
+ */
+
+BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h) {
+    BASE tmp;
+
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+
+    tmp = h->stor_begin[0];
+    FUNCTION(igraph_heap, i_switch)(h->stor_begin, 0, FUNCTION(igraph_heap, size)(h) - 1);
+    h->end -= 1;
+    FUNCTION(igraph_heap, i_sink)(h->stor_begin, h->end - h->stor_begin, 0);
+
+    return tmp;
+}
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_size
+ * \brief Number of elements
+ *
+ * Gives the number of elements in a heap.
+ * \param h The heap object.
+ * \return The number of elements in the heap.
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_heap, size)(TYPE(igraph_heap)* h) {
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+    return h->end - h->stor_begin;
+}
+
+/**
+ * \ingroup heap
+ * \function igraph_heap_reserve
+ * \brief Allocate more memory
+ *
+ * Allocates memory for future use. The size of the heap is
+ * unchanged. If the heap is larger than the \p size parameter then
+ * nothing happens.
+ * \param h The heap object.
+ * \param size The number of elements to allocate memory for.
+ * \return Error code.
+ *
+ * Time complexity: O(\p size) if \p size is larger than the current
+ * number of elements. O(1) otherwise.
+ */
+
+int FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, long int size) {
+    long int actual_size = FUNCTION(igraph_heap, size)(h);
+    BASE *tmp;
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+
+    if (size <= actual_size) {
+        return 0;
+    }
+
+    tmp = igraph_Realloc(h->stor_begin, (size_t) size, BASE);
+    if (tmp == 0) {
+        IGRAPH_ERROR("heap reserve failed", IGRAPH_ENOMEM);
+    }
+    h->stor_begin = tmp;
+    h->stor_end = h->stor_begin + size;
+    h->end = h->stor_begin + actual_size;
+
+    return 0;
+}
+
+/**
+ * \ingroup heap
+ * \brief Build a heap, this should not be called directly.
+ */
+
+void FUNCTION(igraph_heap, i_build)(BASE* arr,
+                                    long int size, long int head) {
+
+    if (RIGHTCHILD(head) < size) {
+        /* both subtrees */
+        FUNCTION(igraph_heap, i_build)(arr, size, LEFTCHILD(head) );
+        FUNCTION(igraph_heap, i_build)(arr, size, RIGHTCHILD(head));
+        FUNCTION(igraph_heap, i_sink)(arr, size, head);
+    } else if (LEFTCHILD(head) < size) {
+        /* only left */
+        FUNCTION(igraph_heap, i_build)(arr, size, LEFTCHILD(head));
+        FUNCTION(igraph_heap, i_sink)(arr, size, head);
+    } else {
+        /* none */
+    }
+}
+
+/**
+ * \ingroup heap
+ * \brief Shift an element upwards in a heap, this should not be
+ * called directly.
+ */
+
+void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, long int size, long int elem) {
+
+    if (elem == 0 || arr[elem] HEAPLESS arr[PARENT(elem)]) {
+        /* at the top */
+    } else {
+        FUNCTION(igraph_heap, i_switch)(arr, elem, PARENT(elem));
+        FUNCTION(igraph_heap, i_shift_up)(arr, size, PARENT(elem));
+    }
+}
+
+/**
+ * \ingroup heap
+ * \brief Moves an element down in a heap, this function should not be
+ * called directly.
+ */
+
+void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head) {
+
+    if (LEFTCHILD(head) >= size) {
+        /* no subtrees */
+    } else if (RIGHTCHILD(head) == size ||
+               arr[LEFTCHILD(head)] HEAPMOREEQ arr[RIGHTCHILD(head)]) {
+        /* sink to the left if needed */
+        if (arr[head] HEAPLESS arr[LEFTCHILD(head)]) {
+            FUNCTION(igraph_heap, i_switch)(arr, head, LEFTCHILD(head));
+            FUNCTION(igraph_heap, i_sink)(arr, size, LEFTCHILD(head));
+        }
+    } else {
+        /* sink to the right */
+        if (arr[head] HEAPLESS arr[RIGHTCHILD(head)]) {
+            FUNCTION(igraph_heap, i_switch)(arr, head, RIGHTCHILD(head));
+            FUNCTION(igraph_heap, i_sink)(arr, size, RIGHTCHILD(head));
+        }
+    }
+}
+
+/**
+ * \ingroup heap
+ * \brief Switches two elements in a heap, this function should not be
+ * called directly.
+ */
+
+void FUNCTION(igraph_heap, i_switch)(BASE* arr, long int e1, long int e2) {
+    if (e1 != e2) {
+        BASE tmp = arr[e1];
+        arr[e1] = arr[e2];
+        arr[e2] = tmp;
+    }
+}
diff --git a/igraph/include/hrg_dendro.h b/igraph/include/hrg_dendro.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/hrg_dendro.h
@@ -0,0 +1,316 @@
+/* -*- mode: C++ -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+// ****************************************************************************************************
+// *** COPYRIGHT NOTICE *******************************************************************************
+// dendro_eq.h - hierarchical random graph (hrg) data structure
+// Copyright (C) 2006-2008 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// ****************************************************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark E.J. Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute
+// Created      : 19 April 2006
+// Modified     : 19 May 2007
+//           : 19 May 2008 (cleaned up for public consumption)
+//
+// ****************************************************************************************************
+//
+// Maximum likelihood dendrogram data structure. This is the heart of the HRG algorithm: all
+// manipulations are done here and all data is stored here. The data structure uses the separate
+// graph data structure to store the basic adjacency information (in a dangerously mutable way).
+//
+// Note: This version (dendro_eq.h) differs from other versions because it includes methods for
+//       doing the consensus dendrogram calculation.
+//
+// ****************************************************************************************************
+
+#ifndef IGRAPH_HRG_DENDRO
+#define IGRAPH_HRG_DENDRO
+
+#include <iostream>
+#include <fstream>
+#include <cstdio>
+#include <cmath>
+
+#include "hrg_graph.h"
+#include "hrg_rbtree.h"
+#include "hrg_splittree_eq.h"
+
+#include "igraph_hrg.h"
+
+using namespace std;
+using namespace fitHRG;
+
+namespace fitHRG {
+
+// ***********************************************************************
+// ******** Basic Structures *********************************************
+
+#ifndef IGRAPH_HRG_LIST
+#define IGRAPH_HRG_LIST
+
+class list {
+public:
+    int x;            // stored elementd in linked-list
+    list* next;           // pointer to next elementd
+    list::list(): x(-1), next(0) { }
+    list::~list() { }
+};
+#endif
+
+enum {DENDRO, GRAPH, LEFT, RIGHT};
+struct block {
+    double x;
+    int y;
+};
+struct ipair {
+    int    x;
+    int y;
+    short int t;
+    string sp;
+};
+struct child {
+    int index;
+    short int type;
+    child* next;
+};
+
+// ***********************************************************************
+// ******** Cnode Class **************************************************
+
+#ifndef IGRAPH_HRG_CNODE
+#define IGRAPH_HRG_CNODE
+class cnode {
+public:
+    int index;            // array index of this node
+    int degree;           // number of children in list
+    int parent;           // index of parent node
+    double weight;        // sampled posterior weight
+    child* children;      // list of children (and their types)
+    child* lastChild;     // pointer to last child in list
+    cnode(): index(-1), degree(0), parent(-1), weight(0.0),
+        children(0), lastChild(0)  { }
+    ~cnode() {
+        child *curr, *prev;
+        curr = children;
+        while (curr != NULL) {
+            prev = curr;
+            curr = curr->next;
+            delete prev;
+            prev = NULL;
+        }
+        lastChild = NULL;
+    }
+};
+#endif
+
+// ***********************************************************************
+// ******** Split Class **************************************************
+
+class split {
+public:
+    string s;           // partition assignment of leaf vertices
+    split(): s("") { }
+    ~split() { }
+    void initializeSplit(const int n) {
+        s = "";
+        for (int i = 0; i < n; i++) {
+            s += "-";
+        }
+    }
+    bool checkSplit() {
+        if (s.empty() || s.find("-", 0) != string::npos) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+};
+
+// ***********************************************************************
+// ******** Internal Edge Class ******************************************
+// The usefulness of this data structure is to provide an easy to way
+// maintain the set of internal edges, and the corresponding splits,
+// in the dendrogram D. It allows for the selection of a random
+// internal edge in O(1) time, and it takes O(1) time to update its
+// structure given an internal move. This structure does not provide
+// any means to directly manipulate the splits, but does allow them to
+// be replaced. A split has the form "int.int...int#int.int...int",
+// where all ints on the left side of the # are in the left partition
+// and all ints on the right side of the # marker are in the right
+// partition defined by the split.
+
+class interns {
+private:
+    ipair* edgelist;   // list of internal edges represented
+    string* splitlist; // split representation of the internal edges
+    int** indexLUT;    // table of indices of internal edges in edgelist
+    int q;         // number of internal edges
+    int count;         // (for adding edges) edgelist index of new edge to add
+public:
+    interns(const int);
+    ~interns();
+
+    // add an internal edge, O(1)
+    bool addEdge(const int, const int, const short int);
+    // returns the ith edge of edgelist, O(1)
+    ipair* getEdge(const int);
+    // returns a uniformly random internal edge, O(1)
+    ipair* getRandomEdge();
+    // returns the ith split of the splitlist, O(1)
+    string getSplit(const int);
+    // replace an existing split, O(1)
+    bool replaceSplit(const int, const string);
+    // swaps two edges, O(1)
+    bool swapEdges(const int, const int, const short int, const int,
+                   const int, const short int);
+};
+
+// ***********************************************************************
+// ******** Tree elementd Class ******************************************
+
+class elementd {
+public:
+    short int type; // either DENDRO or GRAPH
+    double logL;    // log-likelihood contribution of this internal node
+    double p;       // probability p_i that an edge exists between L and
+    // R subtrees
+    int e;      // number of edges between L and R subtrees
+    int n;      // number of leafs in subtree rooted here
+    int label;      // subtree label: smallest leaf index
+    int index;      // index in containing array
+
+    elementd *M;          // pointer to parent node
+    elementd *L;          // pointer for L subtree
+    elementd *R;          // pointer for R subtree
+
+    elementd(): type(DENDRO), logL(0.0), p(0.0), e(0), n(0),
+        label(-1), index(-1), M(0), L(0), R(0) { }
+    ~elementd() { }
+};
+
+// ***********************************************************************
+// ******** Dendrogram Class *********************************************
+
+class dendro {
+private:
+    elementd* root;     // root of the dendrogram
+    elementd* internal; // array of n-1 internal vertices (the dendrogram D)
+    elementd* leaf;     // array of n   leaf vertices (the graph G)
+    int n;          // number of leaf vertices to allocate
+    interns* d;         // list of internal edges of dendrogram D
+    splittree* splithist;       // histogram of cumulative split weights
+    list** paths;           // array of path-lists from root to leaf
+    double L;        // log-likelihood of graph G given dendrogram D
+    rbtree subtreeL, subtreeR;  // trees for computeEdgeCount() function
+    cnode* ctree;       // (consensus tree) array of internal tree nodes
+    int* cancestor;     // (consensus tree) oldest ancetor's index for
+    // each leaf
+
+    // insert node i according to binary search property
+    void binarySearchInsert(elementd*, elementd*);
+    // return path to root from leaf
+    list* binarySearchFind(const double);
+    // build split for this internal edge
+    string buildSplit(elementd*);
+    // compute number of edges between two internal subtrees
+    int computeEdgeCount(const int, const short int, const int,
+                         const short int);
+    // (consensus tree) counts children
+    int countChildren(const string);
+    // find internal node of D that is common ancestor of i,j
+    elementd* findCommonAncestor(list**, const int, const int);
+    // return reverse of path to leaf from root
+    list* reversePathToRoot(const int);
+// quicksort functions
+    void QsortMain(block*, int, int);
+    int QsortPartition(block*, int, int, int);
+
+public:
+    // underlying G (dangerously accessible)
+    graph* g;
+
+    // constructor / destructor
+    dendro(); ~dendro();
+    // build dendrogram from g
+    void buildDendrogram();
+    // delete dendrograph in prep for importDendrogramStructure
+    void clearDendrograph();
+    // read dendrogram structure from HRG structure
+    bool importDendrogramStructure(const igraph_hrg_t *hrg);
+    // (consensus tree) delete splits with less than 0.5 weight
+    void cullSplitHist();
+    // return size of consensus split
+    int getConsensusSize();
+    // return split tree with consensus splits
+    splittree* getConsensusSplits();
+    // return likelihood of G given D
+    double getLikelihood();
+    // store splits in this splittree
+    void getSplitList(splittree*);
+    // return total weight of splittree
+    double getSplitTotalWeight();
+    // make random G from D
+    void makeRandomGraph();
+    // make single MCMC move
+    bool monteCarloMove(double&, bool&, const double);
+    // record consensus tree from splithist
+    void recordConsensusTree(igraph_vector_t *parents,
+                             igraph_vector_t *weights);
+    // record D structure
+    void recordDendrogramStructure(igraph_hrg_t *hrg);
+    // record G structure to igraph graph
+    void recordGraphStructure(igraph_t *graph);
+    // force refresh of log-likelihood value
+    void refreshLikelihood();
+    // sample dendrogram edge likelihoods and update edge histograms
+    void sampleAdjacencyLikelihoods();
+    // reset the dendrograph structures
+    void resetDendrograph();
+    // sample dendrogram's splits and update the split histogram
+    bool sampleSplitLikelihoods(int&);
+    // reset splits histogram
+    void resetAllSplits();
+};
+
+} // namespace fitHRG
+
+#endif
diff --git a/igraph/include/hrg_graph.h b/igraph/include/hrg_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/hrg_graph.h
@@ -0,0 +1,169 @@
+/* -*- mode: C++ -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+// ****************************************************************************************************
+// *** COPYRIGHT NOTICE *******************************************************************************
+// graph.h - graph data structure for hierarchical random graphs
+// Copyright (C) 2005-2008 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// ****************************************************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark E.J. Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute
+// Created      : 8 November 2005
+// Modified     : 23 December 2007 (cleaned up for public consumption)
+//
+// ****************************************************************************************************
+//
+// Graph data structure for hierarchical random graphs. The basic structure is an adjacency list of
+// edges; however, many additional pieces of metadata are stored as well. Each node stores its
+// external name, its degree and (if assigned) its group index.
+//
+// ****************************************************************************************************
+
+#ifndef IGRAPH_HRG_GRAPH
+#define IGRAPH_HRG_GRAPH
+
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+
+#include "hrg_rbtree.h"
+
+using namespace std;
+
+namespace fitHRG {
+
+// ******** Basic Structures *********************************************
+
+#ifndef IGRAPH_HRG_EDGE
+#define IGRAPH_HRG_EDGE
+class edge {
+public:
+    int x;            // stored integer value  (edge terminator)
+    double* h;            // (histogram) weights of edge existence
+    double total_weight;      // (histogram) total weight observed
+    int obs_count;        // number of observations in histogram
+    edge* next;           // pointer to next elementd
+    edge(): x(-1), h(0), total_weight(0.0), obs_count(0), next(0)  { }
+    ~edge() {
+        if (h != NULL) {
+            delete [] h;
+        }
+        h = NULL;
+    }
+};
+#endif
+
+#ifndef IGRAPH_HRG_VERT
+#define IGRAPH_HRG_VERT
+class vert {
+public:
+    string name;           // (external) name of vertex
+    int degree;            // degree of this vertex
+
+    vert(): name(""), degree(0) { }
+    ~vert() { }
+};
+#endif
+
+// ******** Graph Class with Edge Statistics *****************************
+
+class graph {
+public:
+    graph(const int, bool predict = false);
+    ~graph();
+
+    // add (i,j) to graph
+    bool addLink(const int, const int);
+    // add weight to (i,j)'s histogram
+    bool addAdjacencyObs(const int, const int, const double, const double);
+    // add to obs_count and total_weight
+    void addAdjacencyEnd();
+    // true if (i,j) is already in graph
+    bool doesLinkExist(const int, const int);
+    // returns degree of vertex i
+    int getDegree(const int);
+    // returns name of vertex i
+    string getName(const int);
+    // returns edge list of vertex i
+    edge* getNeighborList(const int);
+    // return ptr to histogram of edge (i,j)
+    double* getAdjacencyHist(const int, const int);
+    // return average value of adjacency A(i,j)
+    double getAdjacencyAverage(const int, const int);
+    // returns bin_resolution
+    double getBinResolution();
+    // returns num_bins
+    int getNumBins();
+    // returns m
+    int numLinks();
+    // returns n
+    int numNodes();
+    // returns total_weight
+    double getTotalWeight();
+    // reset edge (i,j)'s histogram
+    void resetAdjacencyHistogram(const int, const int);
+    // reset all edge histograms
+    void resetAllAdjacencies();
+    // clear all links from graph
+    void resetLinks();
+    // allocate edge histograms
+    void setAdjacencyHistograms(const int);
+    // set name of vertex i
+    bool setName(const int, const string);
+
+private:
+    bool predict;      // do we need prediction?
+    vert* nodes;       // list of nodes
+    edge** nodeLink;   // linked list of neighbors to vertex
+    edge** nodeLinkTail;   // pointers to tail of neighbor list
+    double*** A;       // stochastic adjacency matrix for this graph
+    int obs_count;     // number of observations in A
+    double total_weight;   // total weight added to A
+    int n;         // number of vertices
+    int m;         // number of directed edges
+    int num_bins;      // number of bins in edge histograms
+    double bin_resolution; // width of histogram bin
+};
+
+} // namespace fitHRG
+
+#endif
diff --git a/igraph/include/hrg_graph_simp.h b/igraph/include/hrg_graph_simp.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/hrg_graph_simp.h
@@ -0,0 +1,163 @@
+/* -*- mode: C++ -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+// ****************************************************************************************************
+// *** COPYRIGHT NOTICE *******************************************************************************
+// graph_simp.h - graph data structure
+// Copyright (C) 2006-2008 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// ****************************************************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark E.J. Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute
+// Created      : 21 June 2006
+// Modified     : 23 December 2007 (cleaned up for public consumption)
+//
+// ************************************************************************
+//
+// Simple graph data structure. The basic structure is an adjacency
+// list of edges, along with degree information for the vertices.
+//
+// ************************************************************************
+
+#ifndef IGRAPH_HRG_SIMPLEGRAPH
+#define IGRAPH_HRG_SIMPLEGRAPH
+
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+
+#include "hrg_rbtree.h"
+#include "hrg_dendro.h"
+
+using namespace std;
+
+namespace fitHRG {
+
+// ******** Basic Structures *********************************************
+
+#ifndef IGRAPH_HRG_SIMPLEEDGE
+#define IGRAPH_HRG_SIMPLEEDGE
+class simpleEdge {
+public:
+    int x;            // index of edge terminator
+    simpleEdge* next;     // pointer to next elementd
+
+    simpleEdge(): x(-1), next(0) { }
+    ~simpleEdge() { }
+};
+#endif
+
+#ifndef IGRAPH_HRG_SIMPLEVERT
+#define IGRAPH_HRG_SIMPLEVERT
+class simpleVert {
+public:
+    string name;          // (external) name of vertex
+    int degree;           // degree of this vertex
+    int group_true;       // index of vertex's true group
+
+    simpleVert(): name(""), degree(0), group_true(-1) { }
+    ~simpleVert() { }
+};
+#endif
+
+#ifndef IGRAPH_HRG_TWOEDGE
+#define IGRAPH_HRG_TWOEDGE
+class twoEdge {
+public:
+    int o;            // index of edge originator
+    int x;            // index of edge terminator
+
+    twoEdge(): o(-1), x(-1) { }
+    ~twoEdge() { }
+};
+#endif
+
+// ******** Graph Class with Edge Statistics *****************************
+
+class simpleGraph {
+public:
+    simpleGraph(const int); ~simpleGraph();
+
+    // add group label to vertex i
+    bool addGroup(const int, const int);
+    // add (i,j) to graph
+    bool addLink(const int, const int);
+    // true if (i,j) is already in graph
+    bool doesLinkExist(const int, const int);
+    // returns A(i,j)
+    double getAdjacency(const int, const int);
+    // returns degree of vertex i
+    int getDegree(const int);
+    // returns group label of vertex i
+    int getGroupLabel(const int);
+    // returns name of vertex i
+    string getName(const int);
+    // returns edge list of vertex i
+    simpleEdge* getNeighborList(const int);
+    // return pointer to a node
+    simpleVert* getNode(const int);
+    // returns num_groups
+    int getNumGroups();
+    // returns m
+    int getNumLinks();
+    // returns n
+    int getNumNodes();
+    // set name of vertex i
+    bool setName(const int, const string);
+
+private:
+    simpleVert* nodes;        // list of nodes
+    simpleEdge** nodeLink;    // linked list of neighbors to vertex
+    simpleEdge** nodeLinkTail;    // pointers to tail of neighbor list
+    double** A;           // adjacency matrix for this graph
+    twoEdge* E;           // list of all edges (array)
+    int n;            // number of vertices
+    int m;            // number of directed edges
+    int num_groups;       // number of bins in node histograms
+
+    // quicksort functions
+    void QsortMain(block*, int, int);
+    int QsortPartition(block*, int, int, int);
+};
+
+} // namespace fitHRG
+
+#endif
diff --git a/igraph/include/hrg_rbtree.h b/igraph/include/hrg_rbtree.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/hrg_rbtree.h
@@ -0,0 +1,164 @@
+/* -*- mode: C++ -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+// ****************************************************************************************************
+// *** COPYRIGHT NOTICE *******************************************************************************
+// rbtree - red-black tree (self-balancing binary tree data structure)
+// Copyright (C) 2004 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// ****************************************************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute
+// Created      : Spring 2004
+// Modified     : many, many times
+//
+// ****************************************************************************************************
+
+#ifndef IGRAPH_HRG_RBTREE
+#define IGRAPH_HRG_RBTREE
+
+#include <iostream>
+
+using namespace std;
+
+namespace fitHRG {
+
+// ******** Basic Structures *********************************************
+
+#ifndef IGRAPH_HRG_LIST
+#define IGRAPH_HRG_LIST
+
+class list {
+public:
+    int x;            // stored elementd in linked-list
+    list* next;           // pointer to next elementd
+    list(): x(-1), next(0) { }
+    ~list() { }
+};
+#endif
+
+class keyValuePair {
+public:
+    int x;            // elementrb key (int)
+    int y;            // stored value (int)
+    keyValuePair* next;       // linked-list pointer
+    keyValuePair(): x(-1), y(-1), next(0) { }
+    ~keyValuePair() { }
+};
+
+// ******** Tree elementrb Class *****************************************
+
+class elementrb {
+public:
+    int key;          // search key (int)
+    int value;            // stored value (int)
+
+    bool color;           // F: BLACK, T: RED
+    short int mark;       // marker
+
+    elementrb *parent;        // pointer to parent node
+    elementrb *left;      // pointer for left subtree
+    elementrb *right;     // pointer for right subtree
+
+    elementrb(): key(-1), value(-1), color(false), mark(0), parent(0),
+        left(0), right(0) { }
+    ~elementrb() { }
+};
+
+// ******** Red-Black Tree Class *****************************************
+// This vector implementation is a red-black balanced binary tree data
+// structure. It provides find a stored elementrb in time O(log n),
+// find the maximum elementrb in time O(1), delete an elementrb in
+// time O(log n), and insert an elementrb in time O(log n).
+//
+// Note that the key=0 is assumed to be a special value, and thus you
+// cannot insert such an item. Beware of this limitation.
+
+class rbtree {
+private:
+    elementrb* root;      // binary tree root
+    elementrb* leaf;      // all leaf nodes
+    int support;          // number of nodes in the tree
+
+    void rotateLeft(elementrb *x);    // left-rotation operator
+    void rotateRight(elementrb *y);   // right-rotation operator
+    void insertCleanup(elementrb *z); // house-keeping after insertion
+    void deleteCleanup(elementrb *x); // house-keeping after deletion
+    keyValuePair* returnSubtreeAsList(elementrb *z, keyValuePair *head);
+    void deleteSubTree(elementrb *z); // delete subtree rooted at z
+    elementrb* returnMinKey(elementrb *z); // returns minimum of subtree
+    // rooted at z
+    elementrb* returnSuccessor(elementrb *z); // returns successor of z's key
+
+public:
+    rbtree(); ~rbtree(); // default constructor/destructor
+
+    // returns value associated with searchKey
+    int returnValue(const int searchKey);
+    // returns T if searchKey found, and points foundNode at the
+    // corresponding node
+    elementrb* findItem(const int searchKey);
+    // insert a new key with stored value
+    void insertItem(int newKey, int newValue);
+    // selete a node with given key
+    void deleteItem(int killKey);
+    // replace value of a node with given key
+    void replaceItem(int key, int newValue);
+    // increment the value of the given key
+    void incrementValue(int key);
+    // delete the entire tree
+    void deleteTree();
+    // return array of keys in tree
+    int* returnArrayOfKeys();
+    // return list of keys in tree
+    list* returnListOfKeys();
+    // return the tree as a list of keyValuePairs
+    keyValuePair* returnTreeAsList();
+    // returns the maximum key in the tree
+    keyValuePair returnMaxKey();
+    // returns the minimum key in the tree
+    keyValuePair returnMinKey();
+    // returns number of items in tree
+    int returnNodecount();
+};
+
+}
+#endif
diff --git a/igraph/include/hrg_splittree_eq.h b/igraph/include/hrg_splittree_eq.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/hrg_splittree_eq.h
@@ -0,0 +1,185 @@
+/* -*- mode: C++ -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+// ****************************************************************************************************
+// *** COPYRIGHT NOTICE *******************************************************************************
+// splittree_eq.h - a binary search tree data structure for storing dendrogram split frequencies
+// Copyright (C) 2006-2008 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// ****************************************************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu | http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark E.J. Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science AND Santa Fe Institute
+// Created      : 19 April 2006
+// Modified     : 19 May 2007
+//           : 20 May 2008 (cleaned up for public consumption)
+//
+// ***********************************************************************
+//
+// Data structure for storing the split frequences in the sampled
+// dendrograms. Data is stored efficiently as a red-black binary
+// search tree (this is a modified version of the rbtree.h file).
+//
+// ***********************************************************************
+
+#ifndef IGRAPH_HRG_SPLITTREE
+#define IGRAPH_HRG_SPLITTREE
+
+#include <iostream>
+
+using namespace std;
+
+namespace fitHRG {
+
+// ******** Basic Structures *********************************************
+
+#ifndef IGRAPH_HRG_SLIST
+#define IGRAPH_HRG_SLIST
+class slist {
+public:
+    string x;         // stored elementd in linked-list
+    slist* next;          // pointer to next elementd
+    slist(): x(""), next(0) { }
+    ~slist() { }
+};
+#endif
+
+class keyValuePairSplit {
+public:
+    string x;         // elementsp split (string)
+    double y;         // stored weight   (double)
+    int c;            // stored count    (int)
+    keyValuePairSplit* next;  // linked-list pointer
+    keyValuePairSplit(): x(""), y(0.0), c(0), next(0) { }
+    ~keyValuePairSplit() { }
+};
+
+// ******** Tree elementsp Class *****************************************
+
+class elementsp {
+public:
+    string split;             // split represented as a string
+    double weight;            // total weight of this split
+    int count;                // number of observations of this split
+
+    bool color;           // F: BLACK, T: RED
+    short int mark;       // marker
+
+    elementsp *parent;        // pointer to parent node
+    elementsp *left;      // pointer for left subtree
+    elementsp *right;     // pointer for right subtree
+
+    elementsp(): split(""), weight(0.0), count(0), color(false), mark(0),
+        parent(0), left(0), right(0) { }
+    ~elementsp() { }
+};
+
+// ******** Red-Black Tree Class *****************************************
+// This vector implementation is a red-black balanced binary tree data
+// structure. It provides find a stored elementsp in time O(log n),
+// find the maximum elementsp in time O(1), delete an elementsp in
+// time O(log n), and insert an elementsp in time O(log n).
+//
+// Note that the split="" is assumed to be a special value, and thus
+// you cannot insert such an item. Beware of this limitation.
+//
+
+class splittree {
+private:
+    elementsp* root;      // binary tree root
+    elementsp* leaf;      // all leaf nodes
+    int support;          // number of nodes in the tree
+    double total_weight;      // total weight stored
+    int total_count;      // total number of observations stored
+
+    // left-rotation operator
+    void rotateLeft(elementsp*);
+    // right-rotation operator
+    void rotateRight(elementsp*);
+    // house-keeping after insertion
+    void insertCleanup(elementsp*);
+    // house-keeping after deletion
+    void deleteCleanup(elementsp*);
+    keyValuePairSplit* returnSubtreeAsList(elementsp*, keyValuePairSplit*);
+    // delete subtree rooted at z
+    void deleteSubTree(elementsp*);
+    // returns minimum of subtree rooted at z
+    elementsp* returnMinKey(elementsp*);
+    // returns successor of z's key
+    elementsp* returnSuccessor(elementsp*);
+
+public:
+    // default constructor/destructor
+    splittree(); ~splittree();
+    // returns value associated with searchKey
+    double returnValue(const string);
+    // returns T if searchKey found, and points foundNode at the
+    // corresponding node
+    elementsp* findItem(const string);
+    // update total_count and total_weight
+    void finishedThisRound();
+    // insert a new key with stored value
+    bool insertItem(string, double);
+    void clearTree();
+    // delete a node with given key
+    void deleteItem(string);
+    // delete the entire tree
+    void deleteTree();
+    // return array of keys in tree
+    string* returnArrayOfKeys();
+    // return list of keys in tree
+    slist* returnListOfKeys();
+    // return the tree as a list of keyValuePairSplits
+    keyValuePairSplit* returnTreeAsList();
+    // returns the maximum key in the tree
+    keyValuePairSplit returnMaxKey();
+    // returns the minimum key in the tree
+    keyValuePairSplit returnMinKey();
+    // returns number of items in tree
+    int returnNodecount();
+    // returns list of splits with given number of Ms
+    keyValuePairSplit* returnTheseSplits(const int);
+    // returns sum of stored values
+    double returnTotal();
+};
+
+} // namespace fitHRG
+
+#endif
diff --git a/igraph/include/igraph.h b/igraph/include/igraph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph.h
@@ -0,0 +1,100 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_H
+#define IGRAPH_H
+
+#ifndef _GNU_SOURCE
+    #define _GNU_SOURCE 1
+#endif
+
+#include "igraph_version.h"
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "igraph_random.h"
+#include "igraph_progress.h"
+#include "igraph_statusbar.h"
+
+#include "igraph_types.h"
+#include "igraph_complex.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+#include "igraph_array.h"
+#include "igraph_dqueue.h"
+#include "igraph_stack.h"
+#include "igraph_heap.h"
+#include "igraph_psumtree.h"
+#include "igraph_strvector.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_spmatrix.h"
+#include "igraph_sparsemat.h"
+#include "igraph_qsort.h"
+
+#include "igraph_constants.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+#include "igraph_interface.h"
+#include "igraph_constructors.h"
+#include "igraph_games.h"
+#include "igraph_microscopic_update.h"
+#include "igraph_centrality.h"
+#include "igraph_paths.h"
+#include "igraph_components.h"
+#include "igraph_structural.h"
+#include "igraph_transitivity.h"
+#include "igraph_neighborhood.h"
+#include "igraph_topology.h"
+#include "igraph_bipartite.h"
+#include "igraph_cliques.h"
+#include "igraph_layout.h"
+#include "igraph_visitor.h"
+#include "igraph_community.h"
+#include "igraph_conversion.h"
+#include "igraph_foreign.h"
+#include "igraph_motifs.h"
+#include "igraph_operators.h"
+#include "igraph_flow.h"
+#include "igraph_nongraph.h"
+#include "igraph_cocitation.h"
+#include "igraph_adjlist.h"
+#include "igraph_attributes.h"
+#include "igraph_blas.h"
+#include "igraph_lapack.h"
+#include "igraph_arpack.h"
+#include "igraph_mixing.h"
+#include "igraph_separators.h"
+#include "igraph_cohesive_blocks.h"
+#include "igraph_eigen.h"
+#include "igraph_hrg.h"
+#include "igraph_threading.h"
+#include "igraph_interrupt.h"
+#include "igraph_scg.h"
+#include "igraph_matching.h"
+#include "igraph_embedding.h"
+#include "igraph_scan.h"
+#include "igraph_graphlets.h"
+#include "igraph_epidemics.h"
+#include "igraph_lsap.h"
+#include "igraph_coloring.h"
+
+#endif
diff --git a/igraph/include/igraph_adjlist.h b/igraph/include/igraph_adjlist.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_adjlist.h
@@ -0,0 +1,232 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_ADJLIST_H
+#define IGRAPH_ADJLIST_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+
+__BEGIN_DECLS
+
+typedef struct igraph_adjlist_t {
+    igraph_integer_t length;
+    igraph_vector_int_t *adjs;
+} igraph_adjlist_t;
+
+DECLDIR int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al,
+                                igraph_neimode_t mode);
+DECLDIR int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes);
+DECLDIR igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al);
+DECLDIR int igraph_adjlist_init_complementer(const igraph_t *graph,
+        igraph_adjlist_t *al,
+        igraph_neimode_t mode,
+        igraph_bool_t loops);
+DECLDIR void igraph_adjlist_destroy(igraph_adjlist_t *al);
+DECLDIR void igraph_adjlist_clear(igraph_adjlist_t *al);
+DECLDIR void igraph_adjlist_sort(igraph_adjlist_t *al);
+DECLDIR int igraph_adjlist_simplify(igraph_adjlist_t *al);
+DECLDIR int igraph_adjlist_remove_duplicate(const igraph_t *graph,
+        igraph_adjlist_t *al);
+DECLDIR int igraph_adjlist_print(const igraph_adjlist_t *al);
+DECLDIR int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile);
+DECLDIR igraph_bool_t igraph_adjlist_has_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed);
+DECLDIR int igraph_adjlist_replace_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t oldto, igraph_integer_t newto, igraph_bool_t directed);
+
+/* igraph_vector_int_t *igraph_adjlist_get(const igraph_adjlist_t *al,  */
+/*                 igraph_integer_t no); */
+/**
+ * \define igraph_adjlist_get
+ * Query a vector in an adjlist
+ *
+ * Returns a pointer to an <type>igraph_vector_int_t</type> object from an
+ * adjacency list. The vector can be modified as desired.
+ * \param al The adjacency list object.
+ * \param no The vertex of which the vertex of adjacent vertices are
+ *   returned.
+ * \return Pointer to the <type>igraph_vector_int_t</type> object.
+ *
+ * Time complexity: O(1).
+ */
+#define igraph_adjlist_get(al,no) (&(al)->adjs[(long int)(no)])
+
+DECLDIR int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist,
+                           igraph_neimode_t mode, igraph_bool_t duplicate);
+
+typedef struct igraph_inclist_t {
+    igraph_integer_t length;
+    igraph_vector_int_t *incs;
+} igraph_inclist_t;
+
+DECLDIR int igraph_inclist_init(const igraph_t *graph,
+                                igraph_inclist_t *il,
+                                igraph_neimode_t mode);
+DECLDIR int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n);
+DECLDIR void igraph_inclist_destroy(igraph_inclist_t *il);
+DECLDIR void igraph_inclist_clear(igraph_inclist_t *il);
+DECLDIR int igraph_inclist_remove_duplicate(const igraph_t *graph,
+        igraph_inclist_t *il);
+DECLDIR int igraph_inclist_print(const igraph_inclist_t *il);
+DECLDIR int igraph_inclist_fprint(const igraph_inclist_t *il, FILE *outfile);
+
+/**
+ * \define igraph_inclist_get
+ * Query a vector in an incidence list
+ *
+ * Returns a pointer to an <type>igraph_vector_int_t</type> object from an
+ * incidence list containing edge ids. The vector can be modified,
+ * resized, etc. as desired.
+ * \param il Pointer to the incidence list.
+ * \param no The vertex for which the incident edges are returned.
+ * \return Pointer to an <type>igraph_vector_int_t</type> object.
+ *
+ * Time complexity: O(1).
+ */
+#define igraph_inclist_get(il,no) (&(il)->incs[(long int)(no)])
+
+typedef struct igraph_lazy_adjlist_t {
+    const igraph_t *graph;
+    igraph_integer_t length;
+    igraph_vector_t **adjs;
+    igraph_neimode_t mode;
+    igraph_lazy_adlist_simplify_t simplify;
+} igraph_lazy_adjlist_t;
+
+DECLDIR int igraph_lazy_adjlist_init(const igraph_t *graph,
+                                     igraph_lazy_adjlist_t *al,
+                                     igraph_neimode_t mode,
+                                     igraph_lazy_adlist_simplify_t simplify);
+DECLDIR void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al);
+DECLDIR void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al);
+/* igraph_vector_t *igraph_lazy_adjlist_get(igraph_lazy_adjlist_t *al, */
+/*                     igraph_integer_t no); */
+/**
+ * \define igraph_lazy_adjlist_get
+ * Query neighbor vertices
+ *
+ * If the function is called for the first time for a vertex then the
+ * result is stored in the adjacency list and no further query
+ * operations are needed when the neighbors of the same vertex are
+ * queried again.
+ * \param al The lazy adjacency list.
+ * \param no The vertex id to query.
+ * \return Pointer to a vector. It is allowed to modify it and
+ *   modification does not affect the original graph.
+ *
+ * Time complexity: O(d), the number of neighbor vertices for the
+ * first time, O(1) for subsequent calls.
+ */
+#define igraph_lazy_adjlist_get(al,no) \
+    ((al)->adjs[(long int)(no)] != 0 ? ((al)->adjs[(long int)(no)]) : \
+     (igraph_lazy_adjlist_get_real(al, no)))
+DECLDIR igraph_vector_t *igraph_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al,
+        igraph_integer_t no);
+
+typedef struct igraph_lazy_inclist_t {
+    const igraph_t *graph;
+    igraph_integer_t length;
+    igraph_vector_t **incs;
+    igraph_neimode_t mode;
+} igraph_lazy_inclist_t;
+
+DECLDIR int igraph_lazy_inclist_init(const igraph_t *graph,
+                                     igraph_lazy_inclist_t *il,
+                                     igraph_neimode_t mode);
+DECLDIR void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il);
+DECLDIR void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il);
+
+/**
+ * \define igraph_lazy_inclist_get
+ * Query incident edges
+ *
+ * If the function is called for the first time for a vertex, then the
+ * result is stored in the incidence list and no further query
+ * operations are needed when the incident edges of the same vertex are
+ * queried again.
+ * \param al The lazy incidence list object.
+ * \param no The vertex id to query.
+ * \return Pointer to a vector. It is allowed to modify it and
+ *   modification does not affect the original graph.
+ *
+ * Time complexity: O(d), the number of incident edges for the first
+ * time, O(1) for subsequent calls with the same \p no argument.
+ */
+#define igraph_lazy_inclist_get(al,no) \
+    ((al)->incs[(long int)(no)] != 0 ? ((al)->incs[(long int)(no)]) : \
+     (igraph_lazy_inclist_get_real(al, no)))
+DECLDIR igraph_vector_t *igraph_lazy_inclist_get_real(igraph_lazy_inclist_t *al,
+        igraph_integer_t no);
+
+/*************************************************************************
+ * DEPRECATED TYPES AND FUNCTIONS
+ */
+
+typedef igraph_inclist_t igraph_adjedgelist_t;
+
+DECLDIR int igraph_adjedgelist_init(const igraph_t *graph,
+                                    igraph_inclist_t *il,
+                                    igraph_neimode_t mode);
+DECLDIR void igraph_adjedgelist_destroy(igraph_inclist_t *il);
+DECLDIR int igraph_adjedgelist_remove_duplicate(const igraph_t *graph,
+        igraph_inclist_t *il);
+DECLDIR int igraph_adjedgelist_print(const igraph_inclist_t *il, FILE *outfile);
+
+/**
+ * \define igraph_adjedgelist_get
+ * Query a vector in an incidence list
+ *
+ * This macro was superseded by \ref igraph_inclist_get() in igraph 0.6.
+ * Please use \ref igraph_inclist_get() instead of this macro.
+ *
+ * </para><para>
+ * Deprecated in version 0.6.
+ */
+#define igraph_adjedgelist_get(ael,no) (&(ael)->incs[(long int)(no)])
+
+typedef igraph_lazy_inclist_t igraph_lazy_adjedgelist_t;
+
+DECLDIR int igraph_lazy_adjedgelist_init(const igraph_t *graph,
+        igraph_lazy_inclist_t *il,
+        igraph_neimode_t mode);
+DECLDIR void igraph_lazy_adjedgelist_destroy(igraph_lazy_inclist_t *il);
+
+/**
+ * \define igraph_lazy_adjedgelist_get
+ * Query a vector in a lazy incidence list
+ *
+ * This macro was superseded by \ref igraph_lazy_inclist_get() in igraph 0.6.
+ * Please use \ref igraph_lazy_inclist_get() instead of this macro.
+ *
+ * </para><para>
+ * Deprecated in version 0.6.
+ */
+#define igraph_lazy_adjedgelist_get(al,no) \
+    ((al)->incs[(long int)(no)] != 0 ? ((al)->incs[(long int)(no)]) : \
+     (igraph_lazy_adjedgelist_get_real(al, no)))
+DECLDIR igraph_vector_t *igraph_lazy_adjedgelist_get_real(igraph_lazy_inclist_t *al,
+        igraph_integer_t no);
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_arpack.h b/igraph/include/igraph_arpack.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_arpack.h
@@ -0,0 +1,333 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+
+#ifndef ARPACK_H
+#define ARPACK_H
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/**
+ * \section about_arpack ARPACK interface in igraph
+ *
+ * <para>
+ * ARPACK is a library for solving large scale eigenvalue problems.
+ * The package is designed to compute a few eigenvalues and corresponding
+ * eigenvectors of a general \c n by \c n matrix \c A. It is
+ * most appropriate for large sparse or structured matrices \c A where
+ * structured means that a matrix-vector product <code>w &lt;- Av</code> requires
+ * order \c n rather than the usual order <code>n^2</code> floating point
+ * operations. Please see
+ * http://www.caam.rice.edu/software/ARPACK/ for details.
+ * </para>
+ *
+ * <para>
+ * The eigenvalue calculation in ARPACK (in the simplest
+ * case) involves the calculation of the \c Av product where \c A
+ * is the matrix we work with and \c v is an arbitrary vector. A
+ * user-defined function of type \ref igraph_arpack_function_t
+ * is expected to perform this product. If the product can be done
+ * efficiently, e.g. if the matrix is sparse, then ARPACK is usually
+ * able to calculate the eigenvalues very quickly.
+ * </para>
+ *
+ * <para>In igraph, eigenvalue/eigenvector calculations usually
+ * involve the following steps:
+ * \olist
+ *   \oli Initialization of an \ref igraph_arpack_options_t data
+ *        structure using \ref igraph_arpack_options_init.
+ *   \oli Setting some options in the initialized \ref
+ *        igraph_arpack_options_t object.
+ *   \oli Defining a function of type \ref igraph_arpack_function_t.
+ *        The input of this function is a vector, and the output
+ *        should be the output matrix multiplied by the input vector.
+ *   \oli Calling \ref igraph_arpack_rssolve() (is the matrix is
+ *        symmetric), or \ref igraph_arpack_rnsolve().
+ * \endolist
+ * The \ref igraph_arpack_options_t object can be used multiple
+ * times.
+ * </para>
+ *
+ * <para>
+ * If we have many eigenvalue problems to solve, then it might worth
+ * to create an \ref igraph_arpack_storage_t object, and initialize it
+ * via \ref igraph_arpack_storage_init(). This structure contains all
+ * memory needed for ARPACK (with the given upper limit regerding to
+ * the size of the eigenvalue problem). Then many problems can be
+ * solved using the same \ref igraph_arpack_storage_t object, without
+ * always reallocating the required memory.
+ * The \ref igraph_arpack_storage_t object needs to be destroyed by
+ * calling \ref igraph_arpack_storage_destroy() on it, when it is not
+ * needed any more.
+ * </para>
+ *
+ * <para>
+ * igraph does not contain all
+ * ARPACK routines, only the ones dealing with symmetric and
+ * non-symmetric eigenvalue problems using double precision real
+ * numbers.
+ * </para>
+ *
+ */
+
+/**
+ * \struct igraph_arpack_options_t
+ * \brief Options for ARPACK
+ *
+ * This data structure contains the options of thee ARPACK eigenvalue
+ * solver routines. It must be initialized by calling \ref
+ * igraph_arpack_options_init() on it. Then it can be used for
+ * multiple ARPACK calls, as the ARPACK solvers do not modify it.
+ *
+ * Input options:
+ * \member bmat Character. Whether to solve a standard ('I') ot a
+ *    generalized problem ('B').
+ * \member n Dimension of the eigenproblem.
+ * \member which Specifies which eigenvalues/vectors to
+ *    compute. Possible values for symmetric matrices:
+ *    \clist \cli LA
+ *                Compute \c nev largest (algebraic) eigenvalues.
+ *           \cli SA
+ *                Compute \c nev smallest (algebraic) eigenvalues.
+ *           \cli LM
+ *                Compute \c nev largest (in magnitude) eigenvalues.
+ *           \cli SM
+ *                Compute \c nev smallest (in magnitude) eigenvalues.
+ *           \cli BE
+ *                Compute \c nev eigenvalues, half from each end of
+ *                   the spectrum. When \c nev is odd, compute one
+ *                   more from the high en than from the low
+ *                   end. \endclist
+ *    Possible values for non-symmetric matrices:
+ *    \clist \cli LM
+ *                Compute \c nev largest (in magnitude) eigenvalues.
+ *           \cli SM
+ *                Compute \c nev smallest (in magnitude) eigenvalues.
+ *           \cli LR
+ *                Compute \c nev eigenvalues of largest real part.
+ *           \cli SR
+ *                Compute \c nev eigenvalues of smallest real part.
+ *           \cli LI
+ *                Compute \c nev eigenvalues of largest imaginary part.
+ *           \cli SI
+ *                Compute \c nev eigenvalues of smallest imaginary
+ *                    part. \endclist
+ * \member nev The number of eigenvalues to be computed.
+ * \member tol Stopping criterion: the relative accuracy
+ *    of the Ritz value is considered acceptable if its error is less
+ *    than \c tol times its estimated value. If this is set to zero
+ *    then machine precision is used.
+ * \member ncv Number of Lanczos vectors to be generated. Setting this
+ *    to zero means that \ref igraph_arpack_rssolve and \ref igraph_arpack_rnsolve
+ *    will determine a suitable value for \c ncv automatically.
+ * \member ldv Numberic scalar. It should be set to
+ *    zero in the current igraph implementation.
+ * \member ishift Either zero or one. If zero then the shifts are
+ *    provided by the user via reverse communication. If one then exact
+ *    shifts with respect to the reduced tridiagonal matrix \c T.
+ *    Please always set this to one.
+ * \member mxiter Maximum number of Arnoldi update iterations allowed.
+ * \member nb Blocksize to be used in the recurrence. Please always
+ *    leave this on the default value, one.
+ * \member mode The type of the eigenproblem to be solved.
+ *    Possible values if the input matrix is symmetric:
+ *    \olist
+ *      \oli A*x=lambda*x, A is symmetric.
+ *      \oli A*x=lambda*M*x, A is
+ *       symmetric, M is symmetric positive definite.
+ *      \oli K*x=lambda*M*x, K is
+ *        symmetric, M is symmetric positive semi-definite.
+ *      \oli K*x=lambda*KG*x, K is
+ *       symmetric positive semi-definite, KG is symmetric
+ *       indefinite.
+ *     \oli A*x=lambda*M*x, A is
+ *       symmetric, M is symmetric positive
+ *       semi-definite. (Cayley transformed mode.) \endolist
+ *    Please note that only \c mode ==1 was tested and other values
+ *    might not work properly.
+ *    Possible values if the input matrix is not symmetric:
+ *    \olist
+ *     \oli A*x=lambda*x.
+ *     \oli A*x=lambda*M*x, M is
+ *       symmetric positive definite.
+ *     \oli A*x=lambda*M*x, M is
+ *       symmetric semi-definite.
+ *     \oli A*x=lambda*M*x, M is
+ *           symmetric semi-definite. \endolist
+ *     Please note that only \c mode == 1 was tested and other values
+ *     might not work properly.
+ * \member start Whether to use the supplied starting vector (1), or
+ *    use a random starting vector (0). The starting vector must be
+ *    supplied in the first column of the \c vectors argument of the
+ *    \ref igraph_arpack_rssolve() of \ref igraph_arpack_rnsolve() call.
+ *
+ * Output options:
+ * \member info Error flag of ARPACK. Possible values:
+ *    \clist \cli 0
+ *                Normal exit.
+ *           \cli 1
+ *                Maximum number of iterations taken.
+ *           \cli 3
+ *                No shifts could be applied during a cycle of the
+ *         Implicitly restarted Arnoldi iteration. One possibility
+ *         is to increase the size of \c ncv relative to \c
+ *           nev. \endclist
+ *    ARPACK can return other error flags as well, but these are
+ *    converted to igraph errors, see \ref igraph_error_type_t.
+ * \member ierr Error flag of the second ARPACK call (one eigenvalue
+ *     computation usually involves two calls to ARPACK). This is
+ *     always zero, as other error codes are converted to igraph errors.
+ * \member noiter Number of Arnoldi iterations taken.
+ * \member nconv Number of converged Ritz values. This
+ *     represents the number of Ritz values that satisfy the
+ *     convergence critetion.
+ * \member numop Total number of matrix-vector multiplications.
+ * \member numopb Not used currently.
+ * \member numreo Total number of steps of re-orthogonalization.
+ *
+ * Internal options:
+ * \member lworkl Do not modify this option.
+ * \member sigma The shift for the shift-invert mode.
+ * \member sigmai The imaginary part of the shift, for the
+ *    non-symmetric or complex shift-invert mode.
+ * \member iparam Do not modify this option.
+ * \member ipntr Do not modify this option.
+ *
+ */
+
+typedef struct igraph_arpack_options_t {
+    /* INPUT */
+    char bmat[1];         /* I-standard problem, G-generalized */
+    int n;            /* Dimension of the eigenproblem */
+    char which[2];        /* LA, SA, LM, SM, BE */
+    int nev;                 /* Number of eigenvalues to be computed */
+    igraph_real_t tol;        /* Stopping criterion */
+    int ncv;          /* Number of columns in V */
+    int ldv;          /* Leading dimension of V */
+    int ishift;       /* 0-reverse comm., 1-exact with tridiagonal */
+    int mxiter;              /* Maximum number of update iterations to take */
+    int nb;           /* Block size on the recurrence, only 1 works */
+    int mode;     /* The kind of problem to be solved (1-5)
+                   1: A*x=l*x, A symmetric
+                   2: A*x=l*M*x, A symm. M pos. def.
+                   3: K*x = l*M*x, K symm., M pos. semidef.
+                   4: K*x = l*KG*x, K s. pos. semidef. KG s. indef.
+                   5: A*x = l*M*x, A symm., M symm. pos. semidef. */
+    int start;        /* 0: random, 1: use the supplied vector */
+    int lworkl;       /* Size of temporary storage, default is fine */
+    igraph_real_t sigma;          /* The shift for modes 3,4,5 */
+    igraph_real_t sigmai;     /* The imaginary part of shift for rnsolve */
+    /* OUTPUT */
+    int info;     /* What happened, see docs */
+    int ierr;     /* What happened  in the dseupd call */
+    int noiter;       /* The number of iterations taken */
+    int nconv;
+    int numop;        /* Number of OP*x operations */
+    int numopb;       /* Number of B*x operations if BMAT='G' */
+    int numreo;       /* Number of steps of re-orthogonalizations */
+    /* INTERNAL */
+    int iparam[11];
+    int ipntr[14];
+} igraph_arpack_options_t;
+
+/**
+ * \struct igraph_arpack_storage_t
+ * \brief Storage for ARPACK
+ *
+ * Public members, do not modify them directly, these are considered
+ * to be read-only.
+ * \member maxn Maximum rank of matrix.
+ * \member maxncv Maximum NCV.
+ * \member maxldv Maximum LDV.
+ *
+ * These members are considered to be private:
+ * \member workl Working memory.
+ * \member workd Working memory.
+ * \member d Memory for eigenvalues.
+ * \member resid Memory for residuals.
+ * \member ax Working memory.
+ * \member select Working memory.
+ * \member di Memory for eigenvalues, non-symmetric case only.
+ * \member workev Working memory, non-symmetric case only.
+ */
+
+typedef struct igraph_arpack_storage_t {
+    int maxn, maxncv, maxldv;
+    igraph_real_t *v;
+    igraph_real_t *workl;
+    igraph_real_t *workd;
+    igraph_real_t *d;
+    igraph_real_t *resid;
+    igraph_real_t *ax;
+    int *select;
+    igraph_real_t *di;        /* These two only for non-symmetric problems */
+    igraph_real_t *workev;
+} igraph_arpack_storage_t;
+
+DECLDIR void igraph_arpack_options_init(igraph_arpack_options_t *o);
+
+DECLDIR int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn,
+                                       long int maxncv, long int maxldv, igraph_bool_t symm);
+DECLDIR void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s);
+
+/**
+ * \typedef igraph_arpack_function_t
+ * Type of the ARPACK callback function
+ *
+ * \param to Pointer to an \c igraph_real_t, the result of the
+ *    matrix-vector product is expected to be stored here.
+ * \param from Pointer to an \c igraph_real_t, the input matrix should
+ *    be multiplied by the vector stored here.
+ * \param n The length of the vector (which is the same as the order
+ *    of the input matrix).
+ * \param extra Extra argument to the matrix-vector calculation
+ *    function. This is coming from the \ref igraph_arpack_rssolve()
+ *    or \ref igraph_arpack_rnsolve() function.
+ * \return Error code, if not zero, then the ARPACK solver considers
+ *    this as an error, stops and calls the igraph error handler.
+ */
+
+typedef int igraph_arpack_function_t(igraph_real_t *to, const igraph_real_t *from,
+                                     int n, void *extra);
+
+DECLDIR int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra,
+                                  igraph_arpack_options_t *options,
+                                  igraph_arpack_storage_t *storage,
+                                  igraph_vector_t *values, igraph_matrix_t *vectors);
+
+DECLDIR int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra,
+                                  igraph_arpack_options_t *options,
+                                  igraph_arpack_storage_t *storage,
+                                  igraph_matrix_t *values, igraph_matrix_t *vectors);
+
+DECLDIR int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values,
+        long int nev);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_arpack_internal.h b/igraph/include/igraph_arpack_internal.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_arpack_internal.h
@@ -0,0 +1,219 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef ARPACK_INTERNAL_H
+#define ARPACK_INTERNAL_H
+
+/* Note: only files calling the arpack routines directly need to
+   include this header.
+*/
+
+#include "igraph_types.h"
+#include "config.h"
+
+#ifndef INTERNAL_ARPACK
+    #define igraphdsaupd_   dsaupd_
+    #define igraphdseupd_   dseupd_
+    #define igraphdsaup2_   dsaup2_
+    #define igraphdstats_   dstats_
+    #define igraphdsesrt_   dsesrt_
+    #define igraphdsortr_   dsortr_
+    #define igraphdsortc_   dsortc_
+    #define igraphdgetv0_   dgetv0_
+    #define igraphdsaitr_   dsaitr_
+    #define igraphdsapps_   dsapps_
+    #define igraphdsconv_   dsconv_
+    #define igraphdseigt_   dseigt_
+    #define igraphdsgets_   dsgets_
+    #define igraphdstqrb_   dstqrb_
+    #define igraphdmout_    dmout_
+    #define igraphivout_    ivout_
+    #define igraphsecond_   second_
+    #define igraphdvout_    dvout_
+    #define igraphdnaitr_   dnaitr_
+    #define igraphdnapps_   dnapps_
+    #define igraphdnaup2_   dnaup2_
+    #define igraphdnaupd_   dnaupd_
+    #define igraphdnconv_   dnconv_
+    #define igraphdlabad_   dlabad_
+    #define igraphdlanhs_   dlanhs_
+    #define igraphdsortc_   dsortc_
+    #define igraphdneigh_   dneigh_
+    #define igraphdngets_   dngets_
+    #define igraphdstatn_   dstatn_
+    #define igraphdlaqrb_   dlaqrb_
+
+    #define igraphdsaupd_   dsaupd_
+    #define igraphdseupd_   dseupd_
+    #define igraphdnaupd_   dnaupd_
+    #define igraphdneupd_   dneupd_
+#endif
+
+#ifndef INTERNAL_LAPACK
+    #define igraphdlarnv_   dlarnv_
+    #define igraphdlascl_   dlascl_
+    #define igraphdlartg_   dlartg_
+    #define igraphdlaset_   dlaset_
+    #define igraphdlae2_    dlae2_
+    #define igraphdlaev2_   dlaev2_
+    #define igraphdlasr_    dlasr_
+    #define igraphdlasrt_   dlasrt_
+    #define igraphdgeqr2_   dgeqr2_
+    #define igraphdlacpy_   dlacpy_
+    #define igraphdorm2r_   dorm2r_
+    #define igraphdsteqr_   dsteqr_
+    #define igraphdlanst_   dlanst_
+    #define igraphdlapy2_   dlapy2_
+    #define igraphdlamch_   dlamch_
+    #define igraphdlaruv_   dlaruv_
+    #define igraphdlarfg_   dlarfg_
+    #define igraphdlarf_    dlarf_
+    #define igraphdlassq_   dlassq_
+    #define igraphdlamc2_   dlamc2_
+    #define igraphdlamc1_   dlamc1_
+    #define igraphdlamc2_   dlamc2_
+    #define igraphdlamc3_   dlamc3_
+    #define igraphdlamc4_   dlamc4_
+    #define igraphdlamc5_   dlamc5_
+    #define igraphdlabad_   dlabad_
+    #define igraphdlanhs_   dlanhs_
+    #define igraphdtrevc_   dtrevc_
+    #define igraphdlanv2_   dlanv2_
+    #define igraphdlaln2_   dlaln2_
+    #define igraphdladiv_   dladiv_
+    #define igraphdtrsen_   dtrsen_
+    #define igraphdlahqr_   dlahqr_
+    #define igraphdtrsen_   dtrsen_
+    #define igraphdlacon_   dlacon_
+    #define igraphdtrsyl_   dtrsyl_
+    #define igraphdtrexc_   dtrexc_
+    #define igraphdlange_   dlange_
+    #define igraphdlaexc_   dlaexc_
+    #define igraphdlasy2_   dlasy2_
+    #define igraphdlarfx_   dlarfx_
+#endif
+
+#if 0               /* internal f2c functions always used */
+    #define igraphd_sign    d_sign
+    #define igraphetime_    etime_
+    #define igraphpow_dd    pow_dd
+    #define igraphpow_di    pow_di
+    #define igraphs_cmp s_cmp
+    #define igraphs_copy    s_copy
+    #define igraphd_lg10_   d_lg10_
+    #define igraphi_dnnt_   i_dnnt_
+#endif
+
+#ifdef HAVE_GFORTRAN
+
+int igraphdsaupd_(int *ido, char *bmat, int *n,
+                  char *which, int *nev, igraph_real_t *tol,
+                  igraph_real_t *resid, int *ncv, igraph_real_t *v,
+                  int *ldv, int *iparam, int *ipntr,
+                  igraph_real_t *workd, igraph_real_t *workl,
+                  int *lworkl, int *info,
+                  int bmat_len, int which_len);
+
+int igraphdseupd_(int *rvec, char *howmny, int *select,
+                  igraph_real_t *d, igraph_real_t *z, int *ldz,
+                  igraph_real_t *sigma, char *bmat, int *n,
+                  char *which, int *nev, igraph_real_t *tol,
+                  igraph_real_t *resid, int *ncv, igraph_real_t *v,
+                  int *ldv, int *iparam, int *ipntr,
+                  igraph_real_t *workd, igraph_real_t *workl,
+                  int *lworkl, int *info,
+                  int howmny_len, int bmat_len, int which_len);
+
+int igraphdnaupd_(int *ido, char *bmat, int *n,
+                  char *which, int *nev, igraph_real_t *tol,
+                  igraph_real_t *resid, int *ncv, igraph_real_t *v,
+                  int *ldv, int *iparam, int *ipntr,
+                  igraph_real_t *workd, igraph_real_t *workl,
+                  int *lworkl, int *info,
+                  int bmat_len, int which_len);
+
+int igraphdneupd_(int *rvec, char *howmny, int *select,
+                  igraph_real_t *dr, igraph_real_t *di,
+                  igraph_real_t *z, int *ldz,
+                  igraph_real_t *sigmar, igraph_real_t *sigmai,
+                  igraph_real_t *workev, char *bmat, int *n,
+                  char *which, int *nev, igraph_real_t *tol,
+                  igraph_real_t *resid, int *ncv, igraph_real_t *v,
+                  int *ldv, int *iparam, int *ipntr,
+                  igraph_real_t *workd, igraph_real_t *workl,
+                  int *lworkl, int *info,
+                  int howmny_len, int bmat_len, int which_len);
+
+int igraphdsortr_(char *which, int *apply, int* n, igraph_real_t *x1,
+                  igraph_real_t *x2,
+                  int which_len);
+
+int igraphdsortc_(char *which, int *apply, int* n, igraph_real_t *xreal,
+                  igraph_real_t *ximag, igraph_real_t *y,
+                  int which_len);
+
+#else
+
+int igraphdsaupd_(int *ido, char *bmat, int *n,
+                  char *which, int *nev, igraph_real_t *tol,
+                  igraph_real_t *resid, int *ncv, igraph_real_t *v,
+                  int *ldv, int *iparam, int *ipntr,
+                  igraph_real_t *workd, igraph_real_t *workl,
+                  int *lworkl, int *info);
+
+int igraphdseupd_(int *rvec, char *howmny, int *select,
+                  igraph_real_t *d, igraph_real_t *z, int *ldz,
+                  igraph_real_t *sigma, char *bmat, int *n,
+                  char *which, int *nev, igraph_real_t *tol,
+                  igraph_real_t *resid, int *ncv, igraph_real_t *v,
+                  int *ldv, int *iparam, int *ipntr,
+                  igraph_real_t *workd, igraph_real_t *workl,
+                  int *lworkl, int *info);
+
+int igraphdnaupd_(int *ido, char *bmat, int *n,
+                  char *which, int *nev, igraph_real_t *tol,
+                  igraph_real_t *resid, int *ncv, igraph_real_t *v,
+                  int *ldv, int *iparam, int *ipntr,
+                  igraph_real_t *workd, igraph_real_t *workl,
+                  int *lworkl, int *info);
+
+int igraphdneupd_(int *rvec, char *howmny, int *select,
+                  igraph_real_t *dr, igraph_real_t *di,
+                  igraph_real_t *z, int *ldz,
+                  igraph_real_t *sigmar, igraph_real_t *sigmai,
+                  igraph_real_t *workev, char *bmat, int *n,
+                  char *which, int *nev, igraph_real_t *tol,
+                  igraph_real_t *resid, int *ncv, igraph_real_t *v,
+                  int *ldv, int *iparam, int *ipntr,
+                  igraph_real_t *workd, igraph_real_t *workl,
+                  int *lworkl, int *info);
+
+int igraphdsortr_(char *which, int *apply, int* n, igraph_real_t *x1,
+                  igraph_real_t *x2);
+
+int igraphdsortc_(char *which, int *apply, int* n, igraph_real_t *xreal,
+                  igraph_real_t *ximag, igraph_real_t *y);
+
+#endif
+
+#endif  /* ARPACK_INTERNAL_H */
diff --git a/igraph/include/igraph_array.h b/igraph/include/igraph_array.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_array.h
@@ -0,0 +1,61 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_ARRAY_H
+#define IGRAPH_ARRAY_H
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* 3D array                                           */
+/* -------------------------------------------------- */
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "igraph_array_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "igraph_array_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "igraph_array_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "igraph_array_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_array_pmt.h b/igraph/include/igraph_array_pmt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_array_pmt.h
@@ -0,0 +1,51 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+typedef struct TYPE(igraph_array3) {
+    TYPE(igraph_vector) data;
+    long int n1, n2, n3, n1n2;
+} TYPE(igraph_array3);
+
+#ifndef IGRAPH_ARRAY3_INIT_FINALLY
+#define IGRAPH_ARRAY3_INIT_FINALLY(a, n1, n2, n3) \
+    do { IGRAPH_CHECK(igraph_array3_init(a, n1, n2, n3)); \
+        IGRAPH_FINALLY(igraph_array3_destroy, a); } while (0)
+#endif
+
+#ifndef ARRAY3
+    #define ARRAY3(m,i,j,k) ((m).data.stor_begin[(m).n1n2*(k)+(m).n1*(j)+(i)])
+#endif
+
+int FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, long int n1, long int n2,
+                                  long int n3);
+void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a);
+long int FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a);
+long int FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, long int idx);
+int FUNCTION(igraph_array3, resize)(TYPE(igraph_array3) *a, long int n1, long int n2,
+                                    long int n3);
+void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a);
+BASE FUNCTION(igraph_array3, sum)(const TYPE(igraph_array3) *a);
+void FUNCTION(igraph_array3, scale)(TYPE(igraph_array3) *a, BASE by);
+void FUNCTION(igraph_array3, fill)(TYPE(igraph_array3) *a, BASE e);
+int FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to,
+                                    const TYPE(igraph_array3) *from);
diff --git a/igraph/include/igraph_attributes.h b/igraph/include/igraph_attributes.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_attributes.h
@@ -0,0 +1,873 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef REST_ATTRIBUTES_H
+#define REST_ATTRIBUTES_H
+
+#include "igraph_decls.h"
+#include "igraph_datatype.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_strvector.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_iterators.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Attributes                                         */
+/* -------------------------------------------------- */
+
+/**
+ * \section about_attributes
+ *
+ * <para>Attributes are numbers or strings (or basically any kind
+ * of data) associated with the vertices or edges of a graph, or
+ * with the graph itself. Eg. you may label vertices with symbolic names
+ * or attach numeric weights to the edges of a graph. </para>
+ *
+ * <para>igraph attributes are designed to be flexible and extensible.
+ * In igraph attributes are implemented via an interface abstraction:
+ * any type implementing the functions in the interface, can be used
+ * for storing vertex, edge and graph attributes. This means that
+ * different attribute implementations can be used together with
+ * igraph. This is reasonable: if igraph is used from Python attributes can be
+ * of any Python type, from GNU R all R types are allowed. There is an
+ * experimental attribute implementation to be used when programming
+ * in C, but by default it is currently turned off.</para>
+ *
+ * <para>First we briefly look over how attribute handlers can be
+ * implemented. This is not something a user does every day. It is
+ * rather typically the job of the high level interface writers. (But
+ * it is possible to write an interface without implementing
+ * attributes.) Then we show the experimental C attribute handler.</para>
+ */
+
+/**
+ * \section about_attribute_table
+ * <para>It is possible to attach an attribute handling
+ * interface to \a igraph. This is simply a table of functions, of
+ * type \ref igraph_attribute_table_t. These functions are invoked to
+ * notify the attribute handling code about the structural changes in
+ * a graph. See the documentation of this type for details.</para>
+ *
+ * <para>By default there is no attribute interface attached to \a igraph,
+ * to attach one, call \ref igraph_i_set_attribute_table with your new
+ * table. </para>
+ *
+ */
+
+/**
+ * \typedef igraph_attribute_type_t
+ * The possible types of the attributes.
+ *
+ * Note that this is only the
+ * type communicated by the attribute interface towards igraph
+ * functions. Eg. in the GNU R attribute handler, it is safe to say
+ * that all complex R object attributes are strings, as long as this
+ * interface is able to serialize them into strings. See also \ref
+ * igraph_attribute_table_t.
+ * \enumval IGRAPH_ATTRIBUTE_DEFAULT Currently not used for anything.
+ * \enumval IGRAPH_ATTRIBUTE_NUMERIC Numeric attribute.
+ * \enumval IGRAPH_ATTRIBUTE_BOOLEAN Logical values, true or false.
+ * \enumval IGRAPH_ATTRIBUTE_STRING Attribute that can be converted to
+ *   a string.
+ * \enumval IGRAPH_ATTRIBUTE_R_OBJECT An R object. This is usually
+ *   ignored by the igraph functions.
+ * \enumval IGRAPH_ATTRIBUTE_PY_OBJECT A Python object. Usually
+ *   ignored by the igraph functions.
+ *
+ */
+typedef enum { IGRAPH_ATTRIBUTE_DEFAULT = 0,
+               IGRAPH_ATTRIBUTE_NUMERIC = 1,
+               IGRAPH_ATTRIBUTE_BOOLEAN = 5,
+               IGRAPH_ATTRIBUTE_STRING = 2,
+               IGRAPH_ATTRIBUTE_R_OBJECT = 3,
+               IGRAPH_ATTRIBUTE_PY_OBJECT = 4
+             } igraph_attribute_type_t;
+
+typedef struct igraph_attribute_record_t {
+    const char *name;
+    igraph_attribute_type_t type;
+    const void *value;
+} igraph_attribute_record_t;
+
+typedef enum { IGRAPH_ATTRIBUTE_GRAPH = 0,
+               IGRAPH_ATTRIBUTE_VERTEX,
+               IGRAPH_ATTRIBUTE_EDGE
+             } igraph_attribute_elemtype_t;
+
+typedef enum {
+    IGRAPH_ATTRIBUTE_COMBINE_IGNORE = 0,
+    IGRAPH_ATTRIBUTE_COMBINE_DEFAULT = 1,
+    IGRAPH_ATTRIBUTE_COMBINE_FUNCTION = 2,
+    IGRAPH_ATTRIBUTE_COMBINE_SUM = 3,
+    IGRAPH_ATTRIBUTE_COMBINE_PROD = 4,
+    IGRAPH_ATTRIBUTE_COMBINE_MIN = 5,
+    IGRAPH_ATTRIBUTE_COMBINE_MAX = 6,
+    IGRAPH_ATTRIBUTE_COMBINE_RANDOM = 7,
+    IGRAPH_ATTRIBUTE_COMBINE_FIRST = 8,
+    IGRAPH_ATTRIBUTE_COMBINE_LAST = 9,
+    IGRAPH_ATTRIBUTE_COMBINE_MEAN = 10,
+    IGRAPH_ATTRIBUTE_COMBINE_MEDIAN = 11,
+    IGRAPH_ATTRIBUTE_COMBINE_CONCAT = 12
+} igraph_attribute_combination_type_t;
+
+typedef void (*igraph_function_pointer_t)(void);
+
+typedef struct igraph_attribute_combination_record_t {
+    const char *name;     /* can be NULL, meaning: the rest */
+    igraph_attribute_combination_type_t type;
+    igraph_function_pointer_t func;
+} igraph_attribute_combination_record_t;
+
+typedef struct igraph_attribute_combination_t {
+    igraph_vector_ptr_t list;
+} igraph_attribute_combination_t;
+
+#define IGRAPH_NO_MORE_ATTRIBUTES ((const char*)0)
+
+DECLDIR int igraph_attribute_combination_init(igraph_attribute_combination_t *comb);
+DECLDIR int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...);
+DECLDIR void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb);
+DECLDIR int igraph_attribute_combination_add(igraph_attribute_combination_t *comb,
+        const char *name,
+        igraph_attribute_combination_type_t type,
+        igraph_function_pointer_t func);
+DECLDIR int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb,
+        const char *name);
+DECLDIR int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb,
+        const char *name,
+        igraph_attribute_combination_type_t *type,
+        igraph_function_pointer_t *func);
+
+/**
+ * \struct igraph_attribute_table_t
+ * \brief Table of functions to perform operations on attributes
+ *
+ * This type collects the functions defining an attribute handler.
+ * It has the following members:
+ * \member init This function is called whenever a new graph object is
+ *    created, right after it is created but before any vertices or
+ *    edges are added. It is supposed to set the \c attr member of the \c
+ *    igraph_t object. It is expected to return an error code.
+ * \member destroy This function is called whenever the graph object
+ *    is destroyed, right before freeing the allocated memory.
+ * \member copy This function is called when copying a graph with \ref
+ *    igraph_copy, after the structure of the graph has been already
+ *    copied. It is expected to return an error code.
+ * \member add_vertices Called when vertices are added to a
+ *    graph, before adding the vertices themselves.
+ *    The number of vertices to add is supplied as an
+ *    argument. Expected to return an error code.
+ * \member permute_vertices Typically called when a new graph is
+ *    created based on an existing one, e.g. if vertices are removed
+ *    from a graph. The supplied index vector defines which old vertex
+ *    a new vertex corresponds to. Its length must be the same as the
+ *    number of vertices in the new graph.
+ * \member combine_vertices This function is called when the creation
+ *    of a new graph involves a merge (contraction, etc.) of vertices
+ *    from another graph. The function is after the new graph was created.
+ *    An argument specifies how several vertices from the old graph map to a
+ *    single vertex in the new graph.
+ * \member add_edges Called when new edges have been added. The number
+ *    of new edges are supplied as well. It is expected to return an
+ *    error code.
+ * \member permute_edges Typically called when a new graph is created and
+ *    some of the new edges should carry the attributes of some of the
+ *    old edges. The idx vector shows the mapping between the old edges and
+ *    the new ones. Its length is the same as the number of edges in the new
+ *    graph, and for each edge it gives the id of the old edge (the edge in
+ *    the old graph).
+ * \member combine_edges This function is called when the creation
+ *    of a new graph involves a merge (contraction, etc.) of edges
+ *    from another graph. The function is after the new graph was created.
+ *    An argument specifies how several edges from the old graph map to a
+ *    single edge in the new graph.
+ * \member get_info Query the attributes of a graph, the names and
+ *    types should be returned.
+ * \member has_attr Check whether a graph has the named
+ *    graph/vertex/edge attribute.
+ * \member gettype Query the type of a graph/vertex/edge attribute.
+ * \member get_numeric_graph_attr Query a numeric graph attribute. The
+ *    value should be placed as the first element of the \p value
+ *    vector.
+ * \member get_string_graph_attr Query a string graph attribute. The
+ *    value should be placed as the first element of the \p value
+ *    string vector.
+ * \member get_bool_graph_attr Query a boolean graph attribute. The
+ *    value should be placed as the first element of the \p value
+ *    boolean vector.
+ * \member get_numeric_vertex_attr Query a numeric vertex attribute,
+ *    for the vertices included in \p vs.
+ * \member get_string_vertex_attr Query a string vertex attribute,
+ *    for the vertices included in \p vs.
+ * \member get_bool_vertex_attr Query a boolean vertex attribute,
+ *    for the vertices included in \p vs.
+ * \member get_numeric_edge_attr Query a numeric edge attribute, for
+ *    the edges included in \p es.
+ * \member get_string_edge_attr Query a string edge attribute, for the
+ *    edges included in \p es.
+ * \member get_bool_edge_attr Query a boolean edge attribute, for the
+ *    edges included in \p es.
+ *
+ * Note that the <function>get_*_*_attr</function> are allowed to
+ * convert the attributes to numeric or string. E.g. if a vertex attribute
+ * is a GNU R complex data type, then
+ * <function>get_string_vertex_attribute</function> may serialize it
+ * into a string, but this probably makes sense only if
+ * <function>add_vertices</function> is able to deserialize it.
+ */
+
+typedef struct igraph_attribute_table_t {
+    int (*init)(igraph_t *graph, igraph_vector_ptr_t *attr);
+    void (*destroy)(igraph_t *graph);
+    int (*copy)(igraph_t *to, const igraph_t *from, igraph_bool_t ga,
+                igraph_bool_t va, igraph_bool_t ea);
+    int (*add_vertices)(igraph_t *graph, long int nv, igraph_vector_ptr_t *attr);
+    int (*permute_vertices)(const igraph_t *graph,
+                            igraph_t *newgraph,
+                            const igraph_vector_t *idx);
+    int (*combine_vertices)(const igraph_t *graph,
+                            igraph_t *newgraph,
+                            const igraph_vector_ptr_t *merges,
+                            const igraph_attribute_combination_t *comb);
+    int (*add_edges)(igraph_t *graph, const igraph_vector_t *edges,
+                     igraph_vector_ptr_t *attr);
+    int (*permute_edges)(const igraph_t *graph,
+                         igraph_t *newgraph, const igraph_vector_t *idx);
+    int (*combine_edges)(const igraph_t *graph,
+                         igraph_t *newgraph,
+                         const igraph_vector_ptr_t *merges,
+                         const igraph_attribute_combination_t *comb);
+    int (*get_info)(const igraph_t *graph,
+                    igraph_strvector_t *gnames, igraph_vector_t *gtypes,
+                    igraph_strvector_t *vnames, igraph_vector_t *vtypes,
+                    igraph_strvector_t *enames, igraph_vector_t *etypes);
+    igraph_bool_t (*has_attr)(const igraph_t *graph, igraph_attribute_elemtype_t type,
+                              const char *name);
+    int (*gettype)(const igraph_t *graph, igraph_attribute_type_t *type,
+                   igraph_attribute_elemtype_t elemtype, const char *name);
+    int (*get_numeric_graph_attr)(const igraph_t *graph, const char *name,
+                                  igraph_vector_t *value);
+    int (*get_string_graph_attr)(const igraph_t *graph, const char *name,
+                                 igraph_strvector_t *value);
+    int (*get_bool_graph_attr)(const igraph_t *igraph, const char *name,
+                               igraph_vector_bool_t *value);
+    int (*get_numeric_vertex_attr)(const igraph_t *graph, const char *name,
+                                   igraph_vs_t vs,
+                                   igraph_vector_t *value);
+    int (*get_string_vertex_attr)(const igraph_t *graph, const char *name,
+                                  igraph_vs_t vs,
+                                  igraph_strvector_t *value);
+    int (*get_bool_vertex_attr)(const igraph_t *graph, const char *name,
+                                igraph_vs_t vs,
+                                igraph_vector_bool_t *value);
+    int (*get_numeric_edge_attr)(const igraph_t *graph, const char *name,
+                                 igraph_es_t es,
+                                 igraph_vector_t *value);
+    int (*get_string_edge_attr)(const igraph_t *graph, const char *name,
+                                igraph_es_t es,
+                                igraph_strvector_t *value);
+    int (*get_bool_edge_attr)(const igraph_t *graph, const char *name,
+                              igraph_es_t es,
+                              igraph_vector_bool_t *value);
+} igraph_attribute_table_t;
+
+DECLDIR igraph_attribute_table_t * igraph_i_set_attribute_table(const igraph_attribute_table_t * table);
+
+DECLDIR igraph_bool_t igraph_has_attribute_table(void);
+
+#define IGRAPH_I_ATTRIBUTE_DESTROY(graph) \
+    do {if ((graph)->attr) igraph_i_attribute_destroy(graph);} while(0)
+#define IGRAPH_I_ATTRIBUTE_COPY(to,from,ga,va,ea) do { \
+        int igraph_i_ret2=0; \
+        if ((from)->attr) { \
+            IGRAPH_CHECK(igraph_i_ret2=igraph_i_attribute_copy((to),(from),(ga),(va),(ea))); \
+        } else { \
+            (to)->attr = 0; \
+        } \
+        if (igraph_i_ret2 != 0) { \
+            IGRAPH_ERROR("", igraph_i_ret2); \
+        } \
+    } while(0)
+
+int igraph_i_attribute_init(igraph_t *graph, void *attr);
+void igraph_i_attribute_destroy(igraph_t *graph);
+int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from,
+                            igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea);
+int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr);
+int igraph_i_attribute_permute_vertices(const igraph_t *graph,
+                                        igraph_t *newgraph,
+                                        const igraph_vector_t *idx);
+int igraph_i_attribute_combine_vertices(const igraph_t *graph,
+                                        igraph_t *newgraph,
+                                        const igraph_vector_ptr_t *merges,
+                                        const igraph_attribute_combination_t *comb);
+int igraph_i_attribute_add_edges(igraph_t *graph,
+                                 const igraph_vector_t *edges, void *attr);
+int igraph_i_attribute_permute_edges(const igraph_t *graph,
+                                     igraph_t *newgraph,
+                                     const igraph_vector_t *idx);
+int igraph_i_attribute_combine_edges(const igraph_t *graph,
+                                     igraph_t *newgraph,
+                                     const igraph_vector_ptr_t *merges,
+                                     const igraph_attribute_combination_t *comb);
+
+int igraph_i_attribute_get_info(const igraph_t *graph,
+                                igraph_strvector_t *gnames,
+                                igraph_vector_t *gtypes,
+                                igraph_strvector_t *vnames,
+                                igraph_vector_t *vtypes,
+                                igraph_strvector_t *enames,
+                                igraph_vector_t *etypes);
+igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph,
+        igraph_attribute_elemtype_t type,
+        const char *name);
+int igraph_i_attribute_gettype(const igraph_t *graph,
+                               igraph_attribute_type_t *type,
+                               igraph_attribute_elemtype_t elemtype,
+                               const char *name);
+
+int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vector_t *value);
+int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_vector_t *value);
+int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_vector_t *value);
+int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_strvector_t *value);
+int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_strvector_t *value);
+int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_strvector_t *value);
+int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vector_bool_t *value);
+int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_vector_bool_t *value);
+int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_vector_bool_t *value);
+
+/* Experimental attribute handler in C */
+
+extern const igraph_attribute_table_t igraph_cattribute_table;
+
+DECLDIR igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name);
+DECLDIR igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name);
+DECLDIR const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name);
+DECLDIR igraph_real_t igraph_cattribute_VAN(const igraph_t *graph, const char *name,
+        igraph_integer_t vid);
+DECLDIR igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name,
+        igraph_integer_t vid);
+DECLDIR const char* igraph_cattribute_VAS(const igraph_t *graph, const char *name,
+        igraph_integer_t vid);
+DECLDIR igraph_real_t igraph_cattribute_EAN(const igraph_t *graph, const char *name,
+        igraph_integer_t eid);
+DECLDIR igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name,
+        igraph_integer_t eid);
+DECLDIR const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name,
+        igraph_integer_t eid);
+
+DECLDIR int igraph_cattribute_VANV(const igraph_t *graph, const char *name,
+                                   igraph_vs_t vids, igraph_vector_t *result);
+DECLDIR int igraph_cattribute_EANV(const igraph_t *graph, const char *name,
+                                   igraph_es_t eids, igraph_vector_t *result);
+DECLDIR int igraph_cattribute_VASV(const igraph_t *graph, const char *name,
+                                   igraph_vs_t vids, igraph_strvector_t *result);
+DECLDIR int igraph_cattribute_EASV(const igraph_t *graph, const char *name,
+                                   igraph_es_t eids, igraph_strvector_t *result);
+DECLDIR int igraph_cattribute_VABV(const igraph_t *graph, const char *name,
+                                   igraph_vs_t vids, igraph_vector_bool_t *result);
+DECLDIR int igraph_cattribute_EABV(const igraph_t *graph, const char *name,
+                                   igraph_es_t eids, igraph_vector_bool_t *result);
+
+DECLDIR int igraph_cattribute_list(const igraph_t *graph,
+                                   igraph_strvector_t *gnames, igraph_vector_t *gtypes,
+                                   igraph_strvector_t *vnames, igraph_vector_t *vtypes,
+                                   igraph_strvector_t *enames, igraph_vector_t *etypes);
+DECLDIR igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph,
+        igraph_attribute_elemtype_t type,
+        const char *name);
+
+DECLDIR int igraph_cattribute_GAN_set(igraph_t *graph, const char *name,
+                                      igraph_real_t value);
+DECLDIR int igraph_cattribute_GAB_set(igraph_t *graph, const char *name,
+                                      igraph_bool_t value);
+DECLDIR int igraph_cattribute_GAS_set(igraph_t *graph, const char *name,
+                                      const char *value);
+DECLDIR int igraph_cattribute_VAN_set(igraph_t *graph, const char *name,
+                                      igraph_integer_t vid, igraph_real_t value);
+DECLDIR int igraph_cattribute_VAB_set(igraph_t *graph, const char *name,
+                                      igraph_integer_t vid, igraph_bool_t value);
+DECLDIR int igraph_cattribute_VAS_set(igraph_t *graph, const char *name,
+                                      igraph_integer_t vid, const char *value);
+DECLDIR int igraph_cattribute_EAN_set(igraph_t *graph, const char *name,
+                                      igraph_integer_t eid, igraph_real_t value);
+DECLDIR int igraph_cattribute_EAB_set(igraph_t *graph, const char *name,
+                                      igraph_integer_t eid, igraph_bool_t value);
+DECLDIR int igraph_cattribute_EAS_set(igraph_t *graph, const char *name,
+                                      igraph_integer_t eid, const char *value);
+
+DECLDIR int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name,
+                                       const igraph_vector_t *v);
+DECLDIR int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name,
+                                       const igraph_vector_bool_t *v);
+DECLDIR int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name,
+                                       const igraph_strvector_t *sv);
+DECLDIR int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name,
+                                       const igraph_vector_t *v);
+DECLDIR int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name,
+                                       const igraph_vector_bool_t *v);
+DECLDIR int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name,
+                                       const igraph_strvector_t *sv);
+
+DECLDIR void igraph_cattribute_remove_g(igraph_t *graph, const char *name);
+DECLDIR void igraph_cattribute_remove_v(igraph_t *graph, const char *name);
+DECLDIR void igraph_cattribute_remove_e(igraph_t *graph, const char *name);
+DECLDIR void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g,
+        igraph_bool_t v, igraph_bool_t e);
+
+/**
+ * \define GAN
+ * Query a numeric graph attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_GAN().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \return The value of the attribute.
+ */
+#define GAN(graph,n) (igraph_cattribute_GAN((graph), (n)))
+/**
+ * \define GAB
+ * Query a boolean graph attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_GAB().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \return The value of the attribute.
+ */
+#define GAB(graph,n) (igraph_cattribute_GAB((graph), (n)))
+/**
+ * \define GAS
+ * Query a string graph attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_GAS().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \return The value of the attribute.
+ */
+#define GAS(graph,n) (igraph_cattribute_GAS((graph), (n)))
+/**
+ * \define VAN
+ * Query a numeric vertex attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_VAN().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v The id of the vertex.
+ * \return The value of the attribute.
+ */
+#define VAN(graph,n,v) (igraph_cattribute_VAN((graph), (n), (v)))
+/**
+ * \define VAB
+ * Query a boolean vertex attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_VAB().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v The id of the vertex.
+ * \return The value of the attribute.
+ */
+#define VAB(graph,n,v) (igraph_cattribute_VAB((graph), (n), (v)))
+/**
+ * \define VAS
+ * Query a string vertex attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_VAS().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v The id of the vertex.
+ * \return The value of the attribute.
+ */
+#define VAS(graph,n,v) (igraph_cattribute_VAS((graph), (n), (v)))
+/**
+ * \define VANV
+ * Query a numeric vertex attribute for all vertices.
+ *
+ * This is a shorthand for \ref igraph_cattribute_VANV().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vec Pointer to an initialized vector, the result is
+ *        stored here. It will be resized, if needed.
+ * \return Error code.
+ */
+#define VANV(graph,n,vec) (igraph_cattribute_VANV((graph),(n), \
+                           igraph_vss_all(), (vec)))
+/**
+ * \define VABV
+ * Query a boolean vertex attribute for all vertices.
+ *
+ * This is a shorthand for \ref igraph_cattribute_VABV().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vec Pointer to an initialized boolean vector, the result is
+ *        stored here. It will be resized, if needed.
+ * \return Error code.
+ */
+#define VABV(graph,n,vec) (igraph_cattribute_VABV((graph),(n), \
+                           igraph_vss_all(), (vec)))
+/**
+ * \define VASV
+ * Query a string vertex attribute for all vertices.
+ *
+ * This is a shorthand for \ref igraph_cattribute_VASV().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vec Pointer to an initialized string vector, the result is
+ *        stored here. It will be resized, if needed.
+ * \return Error code.
+ */
+#define VASV(graph,n,vec) (igraph_cattribute_VASV((graph),(n), \
+                           igraph_vss_all(), (vec)))
+/**
+ * \define EAN
+ * Query a numeric edge attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_EAN().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param e The id of the edge.
+ * \return The value of the attribute.
+ */
+#define EAN(graph,n,e) (igraph_cattribute_EAN((graph), (n), (e)))
+/**
+ * \define EAB
+ * Query a boolean edge attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_EAB().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param e The id of the edge.
+ * \return The value of the attribute.
+ */
+#define EAB(graph,n,e) (igraph_cattribute_EAB((graph), (n), (e)))
+/**
+ * \define EAS
+ * Query a string edge attribute.
+ *
+ * This is shorthand for \ref igraph_cattribute_EAS().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param e The id of the edge.
+ * \return The value of the attribute.
+ */
+#define EAS(graph,n,e) (igraph_cattribute_EAS((graph), (n), (e)))
+/**
+ * \define EANV
+ * Query a numeric edge attribute for all edges.
+ *
+ * This is a shorthand for \ref igraph_cattribute_EANV().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vec Pointer to an initialized vector, the result is
+ *        stored here. It will be resized, if needed.
+ * \return Error code.
+ */
+#define EANV(graph,n,vec) (igraph_cattribute_EANV((graph),(n), \
+                           igraph_ess_all(IGRAPH_EDGEORDER_ID), (vec)))
+/**
+ * \define EABV
+ * Query a boolean edge attribute for all edges.
+ *
+ * This is a shorthand for \ref igraph_cattribute_EABV().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vec Pointer to an initialized vector, the result is
+ *        stored here. It will be resized, if needed.
+ * \return Error code.
+ */
+#define EABV(graph,n,vec) (igraph_cattribute_EABV((graph),(n), \
+                           igraph_ess_all(IGRAPH_EDGEORDER_ID), (vec)))
+
+/**
+ * \define EASV
+ * Query a string edge attribute for all edges.
+ *
+ * This is a shorthand for \ref igraph_cattribute_EASV().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vec Pointer to an initialized string vector, the result is
+ *        stored here. It will be resized, if needed.
+ * \return Error code.
+ */
+#define EASV(graph,n,vec) (igraph_cattribute_EASV((graph),(n), \
+                           igraph_ess_all(IGRAPH_EDGEORDER_ID), (vec)))
+/**
+ * \define SETGAN
+ * Set a numeric graph attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_GAN_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETGAN(graph,n,value) (igraph_cattribute_GAN_set((graph),(n),(value)))
+/**
+ * \define SETGAB
+ * Set a boolean graph attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_GAB_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETGAB(graph,n,value) (igraph_cattribute_GAB_set((graph),(n),(value)))
+/**
+ * \define SETGAS
+ * Set a string graph attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_GAS_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETGAS(graph,n,value) (igraph_cattribute_GAS_set((graph),(n),(value)))
+/**
+ * \define SETVAN
+ * Set a numeric vertex attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_VAN_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vid Ids of the vertices to set.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETVAN(graph,n,vid,value) (igraph_cattribute_VAN_set((graph),(n),(vid),(value)))
+/**
+ * \define SETVAB
+ * Set a boolean vertex attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_VAB_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vid Ids of the vertices to set.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETVAB(graph,n,vid,value) (igraph_cattribute_VAB_set((graph),(n),(vid),(value)))
+/**
+ * \define SETVAS
+ * Set a string vertex attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_VAS_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param vid Ids of the vertices to set.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETVAS(graph,n,vid,value) (igraph_cattribute_VAS_set((graph),(n),(vid),(value)))
+/**
+ * \define SETEAN
+ * Set a numeric edge attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_EAN_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param eid Ids of the edges to set.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETEAN(graph,n,eid,value) (igraph_cattribute_EAN_set((graph),(n),(eid),(value)))
+/**
+ * \define SETEAB
+ * Set a boolean edge attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_EAB_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param eid Ids of the edges to set.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETEAB(graph,n,eid,value) (igraph_cattribute_EAB_set((graph),(n),(eid),(value)))
+/**
+ * \define SETEAS
+ * Set a string edge attribute
+ *
+ * This is a shorthand for \ref igraph_cattribute_EAS_set().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param eid Ids of the edges to set.
+ * \param value The new value of the attribute.
+ * \return Error code.
+ */
+#define SETEAS(graph,n,eid,value) (igraph_cattribute_EAS_set((graph),(n),(eid),(value)))
+
+/**
+ * \define SETVANV
+ *  Set a numeric vertex attribute for all vertices
+ *
+ * This is a shorthand for \ref igraph_cattribute_VAN_setv().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v Vector containing the new values of the attributes.
+ * \return Error code.
+ */
+#define SETVANV(graph,n,v) (igraph_cattribute_VAN_setv((graph),(n),(v)))
+/**
+ * \define SETVABV
+ *  Set a boolean vertex attribute for all vertices
+ *
+ * This is a shorthand for \ref igraph_cattribute_VAB_setv().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v Vector containing the new values of the attributes.
+ * \return Error code.
+ */
+#define SETVABV(graph,n,v) (igraph_cattribute_VAB_setv((graph),(n),(v)))
+/**
+ * \define SETVASV
+ *  Set a string vertex attribute for all vertices
+ *
+ * This is a shorthand for \ref igraph_cattribute_VAS_setv().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v Vector containing the new values of the attributes.
+ * \return Error code.
+ */
+#define SETVASV(graph,n,v) (igraph_cattribute_VAS_setv((graph),(n),(v)))
+/**
+ * \define SETEANV
+ *  Set a numeric edge attribute for all vertices
+ *
+ * This is a shorthand for \ref igraph_cattribute_EAN_setv().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v Vector containing the new values of the attributes.
+ */
+#define SETEANV(graph,n,v) (igraph_cattribute_EAN_setv((graph),(n),(v)))
+/**
+ * \define SETEABV
+ *  Set a boolean edge attribute for all vertices
+ *
+ * This is a shorthand for \ref igraph_cattribute_EAB_setv().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v Vector containing the new values of the attributes.
+ */
+#define SETEABV(graph,n,v) (igraph_cattribute_EAB_setv((graph),(n),(v)))
+/**
+ * \define SETEASV
+ *  Set a string edge attribute for all vertices
+ *
+ * This is a shorthand for \ref igraph_cattribute_EAS_setv().
+ * \param graph The graph.
+ * \param n The name of the attribute.
+ * \param v Vector containing the new values of the attributes.
+ */
+#define SETEASV(graph,n,v) (igraph_cattribute_EAS_setv((graph),(n),(v)))
+
+/**
+ * \define DELGA
+ * Remove a graph attribute.
+ *
+ * A shorthand for \ref igraph_cattribute_remove_g().
+ * \param graph The graph.
+ * \param n The name of the attribute to remove.
+ */
+#define DELGA(graph,n) (igraph_cattribute_remove_g((graph),(n)))
+/**
+ * \define DELVA
+ * Remove a vertex attribute.
+ *
+ * A shorthand for \ref igraph_cattribute_remove_v().
+ * \param graph The graph.
+ * \param n The name of the attribute to remove.
+ */
+#define DELVA(graph,n) (igraph_cattribute_remove_v((graph),(n)))
+/**
+ * \define DELEA
+ * Remove an edge attribute.
+ *
+ * A shorthand for \ref igraph_cattribute_remove_e().
+ * \param graph The graph.
+ * \param n The name of the attribute to remove.
+ */
+#define DELEA(graph,n) (igraph_cattribute_remove_e((graph),(n)))
+/**
+ * \define DELGAS
+ * Remove all graph attributes.
+ *
+ * Calls \ref igraph_cattribute_remove_all().
+ * \param graph The graph.
+ */
+#define DELGAS(graph) (igraph_cattribute_remove_all((graph),1,0,0))
+/**
+ * \define DELVAS
+ * Remove all vertex attributes.
+ *
+ * Calls \ref igraph_cattribute_remove_all().
+ * \param graph The graph.
+ */
+#define DELVAS(graph) (igraph_cattribute_remove_all((graph),0,1,0))
+/**
+ * \define DELEAS
+ * Remove all edge attributes.
+ *
+ * Calls \ref igraph_cattribute_remove_all().
+ * \param graph The graph.
+ */
+#define DELEAS(graph) (igraph_cattribute_remove_all((graph),0,0,1))
+/**
+ * \define DELALL
+ * Remove all attributes.
+ *
+ * All graph, vertex and edges attributes will be removed.
+ * Calls \ref igraph_cattribute_remove_all().
+ * \param graph The graph.
+ */
+#define DELALL(graph) (igraph_cattribute_remove_all((graph),1,1,1))
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_bipartite.h b/igraph/include/igraph_bipartite.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_bipartite.h
@@ -0,0 +1,97 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_BIPARTITE_H
+#define IGRAPH_BIPARTITE_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+#include "igraph_datatype.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Bipartite networks                                 */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_full_bipartite(igraph_t *graph,
+                                  igraph_vector_bool_t *types,
+                                  igraph_integer_t n1, igraph_integer_t n2,
+                                  igraph_bool_t directed,
+                                  igraph_neimode_t mode);
+
+DECLDIR int igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types,
+                                    const igraph_vector_t *edges,
+                                    igraph_bool_t directed);
+
+DECLDIR int igraph_bipartite_projection_size(const igraph_t *graph,
+        const igraph_vector_bool_t *types,
+        igraph_integer_t *vcount1,
+        igraph_integer_t *ecount1,
+        igraph_integer_t *vcount2,
+        igraph_integer_t *ecount2);
+
+DECLDIR int igraph_bipartite_projection(const igraph_t *graph,
+                                        const igraph_vector_bool_t *types,
+                                        igraph_t *proj1,
+                                        igraph_t *proj2,
+                                        igraph_vector_t *multiplicity1,
+                                        igraph_vector_t *multiplicity2,
+                                        igraph_integer_t probe1);
+
+DECLDIR int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types,
+                             const igraph_matrix_t *incidence,  igraph_bool_t directed,
+                             igraph_neimode_t mode, igraph_bool_t multiple);
+
+DECLDIR int igraph_get_incidence(const igraph_t *graph,
+                                 const igraph_vector_bool_t *types,
+                                 igraph_matrix_t *res,
+                                 igraph_vector_t *row_ids,
+                                 igraph_vector_t *col_ids);
+
+DECLDIR int igraph_is_bipartite(const igraph_t *graph,
+                                igraph_bool_t *res,
+                                igraph_vector_bool_t *type);
+
+DECLDIR int igraph_bipartite_game(igraph_t *graph, igraph_vector_bool_t *types,
+                                  igraph_erdos_renyi_t type,
+                                  igraph_integer_t n1, igraph_integer_t n2,
+                                  igraph_real_t p, igraph_integer_t m,
+                                  igraph_bool_t directed, igraph_neimode_t mode);
+
+DECLDIR int igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types,
+                                      igraph_integer_t n1, igraph_integer_t n2,
+                                      igraph_real_t p, igraph_bool_t directed,
+                                      igraph_neimode_t mode);
+
+DECLDIR int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types,
+                                      igraph_integer_t n1, igraph_integer_t n2,
+                                      igraph_integer_t m, igraph_bool_t directed,
+                                      igraph_neimode_t mode);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_blas.h b/igraph/include/igraph_blas.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_blas.h
@@ -0,0 +1,65 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef BLAS_H
+#define BLAS_H
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/**
+ * \section about_blas BLAS interface in igraph
+ *
+ * <para>
+ * BLAS is a highly optimized library for basic linear algebra operations
+ * such as vector-vector, matrix-vector and matrix-matrix product.
+ * Please see http://www.netlib.org/blas/ for details and a reference
+ * implementation in Fortran. igraph contains some wrapper functions
+ * that can be used to call BLAS routines in a somewhat more
+ * user-friendly way. Not all BLAS routines are included in igraph,
+ * and even those which are included might not have wrappers;
+ * the extension of the set of wrapped functions will probably be driven
+ * by igraph's internal requirements. The wrapper functions usually
+ * substitute double-precision floating point arrays used by BLAS with
+ * \type igraph_vector_t and \type igraph_matrix_t instances and also
+ * remove those parameters (such as the number of rows/columns) that
+ * can be inferred from the passed arguments directly.
+ * </para>
+ */
+
+DECLDIR void igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha,
+                               const igraph_matrix_t* a, const igraph_vector_t* x,
+                               igraph_real_t beta, igraph_vector_t* y);
+DECLDIR void igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha,
+                                     const igraph_matrix_t* a, const igraph_real_t* x,
+                                     igraph_real_t beta, igraph_real_t* y);
+
+DECLDIR igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_blas_internal.h b/igraph/include/igraph_blas_internal.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_blas_internal.h
@@ -0,0 +1,65 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef BLAS_INTERNAL_H
+#define BLAS_INTERNAL_H
+
+/* Note: only files calling the BLAS routines directly need to
+   include this header.
+*/
+
+#include "igraph_types.h"
+#include "config.h"
+
+#ifndef INTERNAL_BLAS
+    #define igraphdaxpy_    daxpy_
+    #define igraphdger_ dger_
+    #define igraphdcopy_    dcopy_
+    #define igraphdscal_    dscal_
+    #define igraphdswap_    dswap_
+    #define igraphdgemm_    dgemm_
+    #define igraphdgemv_    dgemv_
+    #define igraphddot_ ddot_
+    #define igraphdnrm2_    dnrm2_
+    #define igraphlsame_    lsame_
+    #define igraphdrot_     drot_
+    #define igraphidamax_   idamax_
+    #define igraphdtrmm_    dtrmm_
+    #define igraphdasum_    dasum_
+    #define igraphdtrsm_    dtrsm_
+    #define igraphdtrsv_    dtrsv_
+    #define igraphdnrm2_    dnrm2_
+#endif
+
+int igraphdgemv_(char *trans, int *m, int *n, igraph_real_t *alpha,
+                 igraph_real_t *a, int *lda, igraph_real_t *x, int *incx,
+                 igraph_real_t *beta, igraph_real_t *y, int *incy);
+
+int igraphdgemm_(char *transa, char *transb, int *m, int *n, int *k,
+                 double *alpha, double *a, int *lda, double *b, int *ldb,
+                 double *beta, double *c__, int *ldc);
+
+double igraphdnrm2_(int *n, double *x, int *incx);
+
+#endif
diff --git a/igraph/include/igraph_centrality.h b/igraph/include/igraph_centrality.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_centrality.h
@@ -0,0 +1,212 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_CENTRALITY_H
+#define IGRAPH_CENTRALITY_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+#include "igraph_arpack.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Centrality                                         */
+/* -------------------------------------------------- */
+
+DECLDIR int 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);
+DECLDIR int igraph_closeness_estimate(const igraph_t *graph, igraph_vector_t *res,
+                                      const igraph_vs_t vids, igraph_neimode_t mode,
+                                      igraph_real_t cutoff,
+                                      const igraph_vector_t *weights,
+                                      igraph_bool_t normalized);
+
+DECLDIR int 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);
+DECLDIR int igraph_betweenness_estimate(const igraph_t *graph, igraph_vector_t *res,
+                                        const igraph_vs_t vids, igraph_bool_t directed,
+                                        igraph_real_t cutoff,
+                                        const igraph_vector_t *weights,
+                                        igraph_bool_t nobigint);
+DECLDIR int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result,
+                                    igraph_bool_t directed,
+                                    const igraph_vector_t *weigths);
+DECLDIR int igraph_edge_betweenness_estimate(const igraph_t *graph, igraph_vector_t *result,
+        igraph_bool_t directed, igraph_real_t cutoff,
+        const igraph_vector_t *weights);
+DECLDIR int igraph_pagerank_old(const igraph_t *graph, igraph_vector_t *res,
+                                const igraph_vs_t vids, igraph_bool_t directed,
+                                igraph_integer_t niter, igraph_real_t eps,
+                                igraph_real_t damping, igraph_bool_t old);
+
+/**
+ * \typedef igraph_pagerank_algo_t
+ * \brief PageRank algorithm implementation
+ *
+ * Algorithms to calculate PageRank.
+ * \enumval IGRAPH_PAGERANK_ALGO_POWER Use a simple power iteration,
+ *   as it was implemented before igraph version 0.5.
+ * \enumval IGRAPH_PAGERANK_ALGO_ARPACK Use the ARPACK library, this
+ *   was the PageRank implementation in igraph from version 0.5, until
+ *   version 0.7.
+ * \enumval IGRAPH_PAGERANK_ALGO_PRPACK Use the PRPACK
+ *   library. Currently this implementation is recommended.
+ */
+
+typedef enum {
+    IGRAPH_PAGERANK_ALGO_POWER = 0,
+    IGRAPH_PAGERANK_ALGO_ARPACK = 1,
+    IGRAPH_PAGERANK_ALGO_PRPACK = 2
+} igraph_pagerank_algo_t;
+
+/**
+ * \struct igraph_pagerank_power_options_t
+ * \brief Options for the power method
+ *
+ * \member niter The number of iterations to perform, integer.
+ * \member eps  The algorithm will consider the calculation as complete
+ *        if the difference of values between iterations change
+ *        less than this value for every vertex.
+ */
+
+typedef struct igraph_pagerank_power_options_t {
+    igraph_integer_t niter;
+    igraph_real_t eps;
+} igraph_pagerank_power_options_t;
+
+DECLDIR int 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);
+DECLDIR int 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);
+DECLDIR int igraph_personalized_pagerank_vs(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_vs_t reset_vids,
+        const igraph_vector_t *weights, void *options);
+
+DECLDIR int igraph_eigenvector_centrality(const igraph_t *graph, igraph_vector_t *vector,
+        igraph_real_t *value,
+        igraph_bool_t directed, igraph_bool_t scale,
+        const igraph_vector_t *weights,
+        igraph_arpack_options_t *options);
+
+DECLDIR int igraph_hub_score(const igraph_t *graph, igraph_vector_t *vector,
+                             igraph_real_t *value, igraph_bool_t scale,
+                             const igraph_vector_t *weights,
+                             igraph_arpack_options_t *options);
+DECLDIR int igraph_authority_score(const igraph_t *graph, igraph_vector_t *vector,
+                                   igraph_real_t *value, igraph_bool_t scale,
+                                   const igraph_vector_t *weights,
+                                   igraph_arpack_options_t *options);
+
+DECLDIR int igraph_constraint(const igraph_t *graph, igraph_vector_t *res,
+                              igraph_vs_t vids, const igraph_vector_t *weights);
+
+DECLDIR int igraph_strength(const igraph_t *graph, igraph_vector_t *res,
+                            const igraph_vs_t vids, igraph_neimode_t mode,
+                            igraph_bool_t loops, const igraph_vector_t *weights);
+
+DECLDIR int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result,
+                                      igraph_vector_t *ins, igraph_vector_t *outs);
+
+DECLDIR int igraph_sort_vertex_ids_by_degree(const igraph_t *graph,
+        igraph_vector_t *outvids,
+        igraph_vs_t vids,
+        igraph_neimode_t mode,
+        igraph_bool_t loops,
+        igraph_order_t order,
+        igraph_bool_t only_indices);
+
+DECLDIR igraph_real_t igraph_centralization(const igraph_vector_t *scores,
+        igraph_real_t theoretical_max,
+        igraph_bool_t normalized);
+
+DECLDIR int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res,
+        igraph_neimode_t mode, igraph_bool_t loops,
+        igraph_real_t *centralization,
+        igraph_real_t *theoretical_max,
+        igraph_bool_t normalized);
+DECLDIR int igraph_centralization_degree_tmax(const igraph_t *graph,
+        igraph_integer_t nodes,
+        igraph_neimode_t mode,
+        igraph_bool_t loops,
+        igraph_real_t *res);
+
+DECLDIR int igraph_centralization_betweenness(const igraph_t *graph,
+        igraph_vector_t *res,
+        igraph_bool_t directed,
+        igraph_bool_t nobigint,
+        igraph_real_t *centralization,
+        igraph_real_t *theoretical_max,
+        igraph_bool_t normalized);
+DECLDIR int igraph_centralization_betweenness_tmax(const igraph_t *graph,
+        igraph_integer_t nodes,
+        igraph_bool_t directed,
+        igraph_real_t *res);
+
+DECLDIR int igraph_centralization_closeness(const igraph_t *graph,
+        igraph_vector_t *res,
+        igraph_neimode_t mode,
+        igraph_real_t *centralization,
+        igraph_real_t *theoretical_max,
+        igraph_bool_t normalized);
+DECLDIR int igraph_centralization_closeness_tmax(const igraph_t *graph,
+        igraph_integer_t nodes,
+        igraph_neimode_t mode,
+        igraph_real_t *res);
+
+DECLDIR int igraph_centralization_eigenvector_centrality(
+    const igraph_t *graph,
+    igraph_vector_t *vector,
+    igraph_real_t *value,
+    igraph_bool_t directed,
+    igraph_bool_t scale,
+    igraph_arpack_options_t *options,
+    igraph_real_t *centralization,
+    igraph_real_t *theoretical_max,
+    igraph_bool_t normalized);
+DECLDIR int igraph_centralization_eigenvector_centrality_tmax(
+    const igraph_t *graph,
+    igraph_integer_t nodes,
+    igraph_bool_t directed,
+    igraph_bool_t scale,
+    igraph_real_t *res);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_cliquer.h b/igraph/include/igraph_cliquer.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_cliquer.h
@@ -0,0 +1,29 @@
+#ifndef IGRAPH_CLIQUER_H
+#define IGRAPH_CLIQUER_H
+
+#include "igraph_types_internal.h"
+#include "igraph_interface.h"
+#include "igraph_cliques.h"
+
+int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
+                             igraph_integer_t min_size, igraph_integer_t max_size);
+
+int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist,
+                               igraph_integer_t min_size, igraph_integer_t max_size);
+
+int igraph_i_cliquer_callback(const igraph_t *graph,
+                              igraph_integer_t min_size, igraph_integer_t max_size,
+                              igraph_clique_handler_t *cliquehandler_fn, void *arg);
+
+int igraph_i_weighted_cliques(const igraph_t *graph,
+                              const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res,
+                              igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal);
+
+int igraph_i_largest_weighted_cliques(const igraph_t *graph,
+                                      const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res);
+
+int igraph_i_weighted_clique_number(const igraph_t *graph,
+                                    const igraph_vector_t *vertex_weights, igraph_real_t *res);
+
+#endif // IGRAPH_CLIQUER_H
+
diff --git a/igraph/include/igraph_cliques.h b/igraph/include/igraph_cliques.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_cliques.h
@@ -0,0 +1,114 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_CLIQUES_H
+#define IGRAPH_CLIQUES_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Cliques, maximal independent vertex sets           */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_maximal_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
+                                   igraph_integer_t min_size, igraph_integer_t max_size);
+DECLDIR int igraph_maximal_cliques_file(const igraph_t *graph,
+                                        FILE *outfile,
+                                        igraph_integer_t min_size,
+                                        igraph_integer_t max_size);
+DECLDIR int igraph_maximal_cliques_count(const igraph_t *graph,
+        igraph_integer_t *res,
+        igraph_integer_t min_size,
+        igraph_integer_t max_size);
+DECLDIR int igraph_maximal_cliques_subset(const igraph_t *graph,
+        igraph_vector_int_t *subset,
+        igraph_vector_ptr_t *res,
+        igraph_integer_t *no,
+        FILE *outfile,
+        igraph_integer_t min_size,
+        igraph_integer_t max_size);
+DECLDIR int igraph_maximal_cliques_hist(const igraph_t *graph,
+                                        igraph_vector_t *hist,
+                                        igraph_integer_t min_size,
+                                        igraph_integer_t max_size);
+
+DECLDIR int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
+                           igraph_integer_t min_size, igraph_integer_t max_size);
+DECLDIR int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist,
+                                    igraph_integer_t min_size, igraph_integer_t max_size);
+DECLDIR int igraph_largest_cliques(const igraph_t *graph,
+                                   igraph_vector_ptr_t *cliques);
+DECLDIR int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no);
+DECLDIR int igraph_weighted_cliques(const igraph_t *graph,
+                                    const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res,
+                                    igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal);
+DECLDIR int igraph_largest_weighted_cliques(const igraph_t *graph,
+        const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res);
+DECLDIR int igraph_weighted_clique_number(const igraph_t *graph,
+        const igraph_vector_t *vertex_weights, igraph_real_t *res);
+DECLDIR int igraph_independent_vertex_sets(const igraph_t *graph,
+        igraph_vector_ptr_t *res,
+        igraph_integer_t min_size,
+        igraph_integer_t max_size);
+DECLDIR int igraph_largest_independent_vertex_sets(const igraph_t *graph,
+        igraph_vector_ptr_t *res);
+DECLDIR int igraph_maximal_independent_vertex_sets(const igraph_t *graph,
+        igraph_vector_ptr_t *res);
+DECLDIR int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no);
+
+/**
+ * \typedef igraph_clique_handler_t
+ * \brief Type of clique handler functions
+ *
+ * Callback type, called when a clique was found.
+ *
+ * See the details at the documentation of \ref
+ * igraph_cliques_callback().
+ *
+ * \param clique The current clique. Destroying and freeing
+ *   this vector is left to the user.
+ *   Use \ref igraph_vector_destroy() and \ref igraph_free()
+ *   to do this.
+ * \param arg This extra argument was passed to \ref
+ *   igraph_cliques_callback() when it was called.
+ * \return Boolean, whether to continue with the clique search.
+ */
+typedef igraph_bool_t igraph_clique_handler_t(igraph_vector_t *clique, void *arg);
+
+DECLDIR int igraph_cliques_callback(const igraph_t *graph,
+                                    igraph_integer_t min_size, igraph_integer_t max_size,
+                                    igraph_clique_handler_t *cliquehandler_fn, void *arg);
+
+DECLDIR int igraph_maximal_cliques_callback(const igraph_t *graph,
+        igraph_clique_handler_t *cliquehandler_fn, void *arg,
+        igraph_integer_t min_size, igraph_integer_t max_size);
+
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_cocitation.h b/igraph/include/igraph_cocitation.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_cocitation.h
@@ -0,0 +1,66 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_COCITATION_H
+#define IGRAPH_COCITATION_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+#include "igraph_matrix.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Cocitation and other similarity measures           */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res,
+                              const igraph_vs_t vids);
+DECLDIR int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res,
+                               const igraph_vs_t vids);
+
+DECLDIR int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res,
+                                      const igraph_vs_t vids, igraph_neimode_t mode,
+                                      igraph_bool_t loops);
+DECLDIR int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res,
+        const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops);
+DECLDIR int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res,
+        const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops);
+
+DECLDIR int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res,
+                                   const igraph_vs_t vids, igraph_neimode_t mode,
+                                   igraph_bool_t loops);
+DECLDIR int igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res,
+        const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops);
+DECLDIR int igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res,
+                                      const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops);
+
+DECLDIR int igraph_similarity_inverse_log_weighted(const igraph_t *graph,
+        igraph_matrix_t *res, const igraph_vs_t vids,
+        igraph_neimode_t mode);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_cohesive_blocks.h b/igraph/include/igraph_cohesive_blocks.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_cohesive_blocks.h
@@ -0,0 +1,41 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_COHESIVE_BLOCKS_H
+#define IGRAPH_COHESIVE_BLOCKS_H
+
+#include "igraph_datatype.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_cohesive_blocks(const igraph_t *graph,
+                           igraph_vector_ptr_t *blocks,
+                           igraph_vector_t *cohesion,
+                           igraph_vector_t *parent,
+                           igraph_t *block_tree);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_coloring.h b/igraph/include/igraph_coloring.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_coloring.h
@@ -0,0 +1,23 @@
+#ifndef IGRAPH_COLORING_H
+#define IGRAPH_COLORING_H
+
+#include "igraph_datatype.h"
+
+__BEGIN_DECLS
+
+/**
+ * \typedef igraph_coloring_greedy_t
+ * Ordering heuristics for igraph_vertex_coloring_greedy
+ *
+ * \enumval IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS  Choose vertex with largest number of already colored neighbors.
+ *
+ */
+typedef enum {
+    IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS = 0
+} igraph_coloring_greedy_t;
+
+DECLDIR int igraph_vertex_coloring_greedy(const igraph_t *graph, igraph_vector_int_t *colors, igraph_coloring_greedy_t heuristic);
+
+__END_DECLS
+
+#endif /* IGRAPH_COLORING_H */
diff --git a/igraph/include/igraph_community.h b/igraph/include/igraph_community.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_community.h
@@ -0,0 +1,247 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_COMMUNITY_H
+#define IGRAPH_COMMUNITY_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_datatype.h"
+#include "igraph_types.h"
+#include "igraph_arpack.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* K-Cores                                            */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores,
+                            igraph_neimode_t mode);
+
+/* -------------------------------------------------- */
+/* Community Structure                                */
+/* -------------------------------------------------- */
+
+/* TODO: cut.community */
+/* TODO: edge.type.matrix */
+/* TODO:  */
+
+DECLDIR int igraph_community_optimal_modularity(const igraph_t *graph,
+        igraph_real_t *modularity,
+        igraph_vector_t *membership,
+        const igraph_vector_t *weights);
+
+DECLDIR int igraph_community_spinglass(const igraph_t *graph,
+                                       const igraph_vector_t *weights,
+                                       igraph_real_t *modularity,
+                                       igraph_real_t *temperature,
+                                       igraph_vector_t *membership,
+                                       igraph_vector_t *csize,
+                                       igraph_integer_t spins,
+                                       igraph_bool_t parupdate,
+                                       igraph_real_t starttemp,
+                                       igraph_real_t stoptemp,
+                                       igraph_real_t coolfact,
+                                       igraph_spincomm_update_t update_rule,
+                                       igraph_real_t gamma,
+                                       /* the rest is for the NegSpin implementation */
+                                       igraph_spinglass_implementation_t implementation,
+                                       /*                    igraph_matrix_t *adhesion, */
+                                       /*                    igraph_matrix_t *normalised_adhesion, */
+                                       /*                    igraph_real_t *polarization, */
+                                       igraph_real_t lambda);
+
+DECLDIR int igraph_community_spinglass_single(const igraph_t *graph,
+        const igraph_vector_t *weights,
+        igraph_integer_t vertex,
+        igraph_vector_t *community,
+        igraph_real_t *cohesion,
+        igraph_real_t *adhesion,
+        igraph_integer_t *inner_links,
+        igraph_integer_t *outer_links,
+        igraph_integer_t spins,
+        igraph_spincomm_update_t update_rule,
+        igraph_real_t gamma);
+
+DECLDIR int igraph_community_walktrap(const igraph_t *graph,
+                                      const igraph_vector_t *weights,
+                                      int steps,
+                                      igraph_matrix_t *merges,
+                                      igraph_vector_t *modularity,
+                                      igraph_vector_t *membership);
+
+DECLDIR int igraph_community_infomap(const igraph_t * graph,
+                                     const igraph_vector_t *e_weights,
+                                     const igraph_vector_t *v_weights,
+                                     int nb_trials,
+                                     igraph_vector_t *membership,
+                                     igraph_real_t *codelength);
+
+DECLDIR int igraph_community_edge_betweenness(const igraph_t *graph,
+        igraph_vector_t *result,
+        igraph_vector_t *edge_betweenness,
+        igraph_matrix_t *merges,
+        igraph_vector_t *bridges,
+        igraph_vector_t *modularity,
+        igraph_vector_t *membership,
+        igraph_bool_t directed,
+        const igraph_vector_t *weights);
+DECLDIR int igraph_community_eb_get_merges(const igraph_t *graph,
+        const igraph_vector_t *edges,
+        const igraph_vector_t *weights,
+        igraph_matrix_t *merges,
+        igraph_vector_t *bridges,
+        igraph_vector_t *modularity,
+        igraph_vector_t *membership);
+
+DECLDIR int igraph_community_fastgreedy(const igraph_t *graph,
+                                        const igraph_vector_t *weights,
+                                        igraph_matrix_t *merges,
+                                        igraph_vector_t *modularity,
+                                        igraph_vector_t *membership);
+
+DECLDIR int igraph_community_to_membership(const igraph_matrix_t *merges,
+        igraph_integer_t nodes,
+        igraph_integer_t steps,
+        igraph_vector_t *membership,
+        igraph_vector_t *csize);
+DECLDIR int igraph_le_community_to_membership(const igraph_matrix_t *merges,
+        igraph_integer_t steps,
+        igraph_vector_t *membership,
+        igraph_vector_t *csize);
+
+DECLDIR int igraph_modularity(const igraph_t *graph,
+                              const igraph_vector_t *membership,
+                              igraph_real_t *modularity,
+                              const igraph_vector_t *weights);
+
+DECLDIR int igraph_modularity_matrix(const igraph_t *graph,
+                                     igraph_matrix_t *modmat,
+                                     const igraph_vector_t *weights);
+
+DECLDIR int igraph_reindex_membership(igraph_vector_t *membership,
+                                      igraph_vector_t *new_to_old,
+                                      igraph_integer_t *nb_clusters);
+
+typedef enum { IGRAPH_LEVC_HIST_SPLIT = 1,
+               IGRAPH_LEVC_HIST_FAILED,
+               IGRAPH_LEVC_HIST_START_FULL,
+               IGRAPH_LEVC_HIST_START_GIVEN
+             } igraph_leading_eigenvector_community_history_t;
+
+/**
+ * \typedef igraph_community_leading_eigenvector_callback_t
+ * Callback for the leading eigenvector community finding method.
+ *
+ * The leading eigenvector community finding implementation in igraph
+ * is able to call a callback function, after each eigenvalue
+ * calculation. This callback function must be of \c
+ * igraph_community_leading_eigenvector_callback_t type.
+ * The following arguments are passed to the callback:
+ * \param membership The actual membership vector, before recording
+ *    the potential change implied by the newly found eigenvalue.
+ * \param comm The id of the community that the algorithm tried to
+ *    split in the last iteration. The community ids are indexed from
+ *    zero here!
+ * \param eigenvalue The eigenvalue the algorithm has just found.
+ * \param eigenvector The eigenvector corresponding to the eigenvalue
+ *    the algorithm just found.
+ * \param arpack_multiplier A function that was passed to \ref
+ *    igraph_arpack_rssolve() to solve the last eigenproblem.
+ * \param arpack_extra The extra argument that was passed to the
+ *    ARPACK solver.
+ * \param extra Extra argument that as passed to \ref
+ *    igraph_community_leading_eigenvector().
+ *
+ * \sa \ref igraph_community_leading_eigenvector(), \ref
+ * igraph_arpack_function_t, \ref igraph_arpack_rssolve().
+ */
+
+typedef int igraph_community_leading_eigenvector_callback_t(
+    const igraph_vector_t *membership,
+    long int comm,
+    igraph_real_t eigenvalue,
+    const igraph_vector_t *eigenvector,
+    igraph_arpack_function_t *arpack_multiplier,
+    void *arpack_extra,
+    void *extra);
+
+DECLDIR int igraph_community_leading_eigenvector(const igraph_t *graph,
+        const igraph_vector_t *weights,
+        igraph_matrix_t *merges,
+        igraph_vector_t *membership,
+        igraph_integer_t steps,
+        igraph_arpack_options_t *options,
+        igraph_real_t *modularity,
+        igraph_bool_t start,
+        igraph_vector_t *eigenvalues,
+        igraph_vector_ptr_t *eigenvectors,
+        igraph_vector_t *history,
+        igraph_community_leading_eigenvector_callback_t *callback,
+        void *callback_extra);
+
+DECLDIR int igraph_community_fluid_communities(const igraph_t *graph,
+        igraph_integer_t no_of_communities,
+        igraph_vector_t *membership,
+        igraph_real_t *modularity);
+
+DECLDIR int igraph_community_label_propagation(const igraph_t *graph,
+        igraph_vector_t *membership,
+        const igraph_vector_t *weights,
+        const igraph_vector_t *initial,
+        igraph_vector_bool_t *fixed,
+        igraph_real_t *modularity);
+
+DECLDIR int igraph_community_multilevel(const igraph_t *graph,
+                                        const igraph_vector_t *weights,
+                                        igraph_vector_t *membership,
+                                        igraph_matrix_t *memberships,
+                                        igraph_vector_t *modularity);
+
+DECLDIR int igraph_community_leiden(const igraph_t *graph,
+                                    const igraph_vector_t *edge_weights,
+                                    const igraph_vector_t *node_weights,
+                                    const igraph_real_t resolution_parameter,
+                                    const igraph_real_t beta,
+                                    const igraph_bool_t start,
+                                    igraph_vector_t *membership,
+                                    igraph_integer_t *nb_clusters,
+                                    igraph_real_t *quality);
+/* -------------------------------------------------- */
+/* Community Structure Comparison                     */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_compare_communities(const igraph_vector_t *comm1,
+                                       const igraph_vector_t *comm2,
+                                       igraph_real_t* result,
+                                       igraph_community_comparison_t method);
+DECLDIR int igraph_split_join_distance(const igraph_vector_t *comm1,
+                                       const igraph_vector_t *comm2,
+                                       igraph_integer_t* distance12,
+                                       igraph_integer_t* distance21);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_complex.h b/igraph/include/igraph_complex.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_complex.h
@@ -0,0 +1,104 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_COMPLEX_H
+#define IGRAPH_COMPLEX_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+
+__BEGIN_DECLS
+
+typedef struct igraph_complex_t {
+    igraph_real_t dat[2];
+} igraph_complex_t;
+
+#define IGRAPH_REAL(x) ((x).dat[0])
+#define IGRAPH_IMAG(x) ((x).dat[1])
+#define IGRAPH_COMPLEX_EQ(x,y) ((x).dat[0]==(y).dat[0] && (x).dat[1]==(y).dat[1])
+
+DECLDIR igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y);
+DECLDIR igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta);
+
+DECLDIR igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1,
+        igraph_complex_t z2,
+        igraph_real_t tol);
+
+DECLDIR igraph_real_t igraph_complex_mod(igraph_complex_t z);
+DECLDIR igraph_real_t igraph_complex_arg(igraph_complex_t z);
+
+DECLDIR igraph_real_t igraph_complex_abs(igraph_complex_t z);
+DECLDIR igraph_real_t igraph_complex_logabs(igraph_complex_t z);
+
+DECLDIR igraph_complex_t igraph_complex_add(igraph_complex_t z1,
+        igraph_complex_t z2);
+DECLDIR igraph_complex_t igraph_complex_sub(igraph_complex_t z1,
+        igraph_complex_t z2);
+DECLDIR igraph_complex_t igraph_complex_mul(igraph_complex_t z1,
+        igraph_complex_t z2);
+DECLDIR igraph_complex_t igraph_complex_div(igraph_complex_t z1,
+        igraph_complex_t z2);
+
+DECLDIR igraph_complex_t igraph_complex_add_real(igraph_complex_t z,
+        igraph_real_t x);
+DECLDIR igraph_complex_t igraph_complex_add_imag(igraph_complex_t z,
+        igraph_real_t y);
+DECLDIR igraph_complex_t igraph_complex_sub_real(igraph_complex_t z,
+        igraph_real_t x);
+DECLDIR igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z,
+        igraph_real_t y);
+DECLDIR igraph_complex_t igraph_complex_mul_real(igraph_complex_t z,
+        igraph_real_t x);
+DECLDIR igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z,
+        igraph_real_t y);
+DECLDIR igraph_complex_t igraph_complex_div_real(igraph_complex_t z,
+        igraph_real_t x);
+DECLDIR igraph_complex_t igraph_complex_div_imag(igraph_complex_t z,
+        igraph_real_t y);
+
+DECLDIR igraph_complex_t igraph_complex_conj(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_neg(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_inv(igraph_complex_t z);
+
+DECLDIR igraph_complex_t igraph_complex_sqrt(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x);
+DECLDIR igraph_complex_t igraph_complex_exp(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_pow(igraph_complex_t z1,
+        igraph_complex_t z2);
+DECLDIR igraph_complex_t igraph_complex_pow_real(igraph_complex_t z,
+        igraph_real_t x);
+DECLDIR igraph_complex_t igraph_complex_log(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_log10(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_log_b(igraph_complex_t z,
+        igraph_complex_t b);
+
+DECLDIR igraph_complex_t igraph_complex_sin(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_cos(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_tan(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_sec(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_csc(igraph_complex_t z);
+DECLDIR igraph_complex_t igraph_complex_cot(igraph_complex_t z);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_components.h b/igraph/include/igraph_components.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_components.h
@@ -0,0 +1,61 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_COMPONENTS_H
+#define IGRAPH_COMPONENTS_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_datatype.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Components                                         */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership,
+                            igraph_vector_t *csize, igraph_integer_t *no,
+                            igraph_connectedness_t mode);
+DECLDIR int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res,
+                                igraph_connectedness_t mode);
+DECLDIR void igraph_decompose_destroy(igraph_vector_ptr_t *complist);
+DECLDIR int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components,
+                             igraph_connectedness_t mode,
+                             long int maxcompno, long int minelements);
+DECLDIR int igraph_articulation_points(const igraph_t *graph,
+                                       igraph_vector_t *res);
+DECLDIR int igraph_biconnected_components(const igraph_t *graph,
+        igraph_integer_t *no,
+        igraph_vector_ptr_t *tree_edges,
+        igraph_vector_ptr_t *component_edges,
+        igraph_vector_ptr_t *components,
+        igraph_vector_t *articulation_points);
+DECLDIR int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_constants.h b/igraph/include/igraph_constants.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_constants.h
@@ -0,0 +1,193 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_CONSTANTS_H
+#define IGRAPH_CONSTANTS_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Constants                                          */
+/* -------------------------------------------------- */
+
+typedef enum { IGRAPH_UNDIRECTED = 0, IGRAPH_DIRECTED = 1 } igraph_i_directed_t;
+
+typedef enum { IGRAPH_NO_LOOPS = 0, IGRAPH_LOOPS = 1 } igraph_i_loops_t;
+
+typedef enum { IGRAPH_NO_MULTIPLE = 0, IGRAPH_MULTIPLE = 1 } igraph_i_multiple_t;
+
+typedef enum { IGRAPH_ASCENDING = 0, IGRAPH_DESCENDING = 1 } igraph_order_t;
+
+typedef enum { IGRAPH_MINIMUM = 0, IGRAPH_MAXIMUM = 1 } igraph_optimal_t;
+
+typedef enum { IGRAPH_OUT = 1, IGRAPH_IN = 2, IGRAPH_ALL = 3,
+               IGRAPH_TOTAL = 3
+             } igraph_neimode_t;
+
+typedef enum { IGRAPH_WEAK = 1, IGRAPH_STRONG = 2 } igraph_connectedness_t;
+
+typedef enum { IGRAPH_RECIPROCITY_DEFAULT = 0,
+               IGRAPH_RECIPROCITY_RATIO = 1
+             } igraph_reciprocity_t;
+
+typedef enum { IGRAPH_ADJ_DIRECTED = 0,
+               IGRAPH_ADJ_UNDIRECTED = 1, IGRAPH_ADJ_MAX = 1,
+               IGRAPH_ADJ_UPPER, IGRAPH_ADJ_LOWER, IGRAPH_ADJ_MIN,
+               IGRAPH_ADJ_PLUS
+             } igraph_adjacency_t;
+
+typedef enum { IGRAPH_STAR_OUT = 0, IGRAPH_STAR_IN,
+               IGRAPH_STAR_UNDIRECTED,
+               IGRAPH_STAR_MUTUAL
+             } igraph_star_mode_t;
+
+typedef enum { IGRAPH_TREE_OUT = 0, IGRAPH_TREE_IN,
+               IGRAPH_TREE_UNDIRECTED
+             } igraph_tree_mode_t;
+
+typedef enum { IGRAPH_ERDOS_RENYI_GNP = 0,
+               IGRAPH_ERDOS_RENYI_GNM
+             } igraph_erdos_renyi_t;
+
+typedef enum { IGRAPH_GET_ADJACENCY_UPPER = 0,
+               IGRAPH_GET_ADJACENCY_LOWER,
+               IGRAPH_GET_ADJACENCY_BOTH
+             } igraph_get_adjacency_t;
+
+typedef enum { IGRAPH_DEGSEQ_SIMPLE = 0,
+               IGRAPH_DEGSEQ_VL,
+               IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE,
+               IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM
+             } igraph_degseq_t;
+
+typedef enum { IGRAPH_REALIZE_DEGSEQ_SMALLEST = 0,
+               IGRAPH_REALIZE_DEGSEQ_LARGEST,
+               IGRAPH_REALIZE_DEGSEQ_INDEX
+             } igraph_realize_degseq_t;
+
+typedef enum { IGRAPH_RANDOM_TREE_PRUFER = 0,
+               IGRAPH_RANDOM_TREE_LERW
+             } igraph_random_tree_t;
+
+typedef enum { IGRAPH_FILEFORMAT_EDGELIST = 0,
+               IGRAPH_FILEFORMAT_NCOL,
+               IGRAPH_FILEFORMAT_PAJEK,
+               IGRAPH_FILEFORMAT_LGL,
+               IGRAPH_FILEFORMAT_GRAPHML
+             } igraph_fileformat_type_t;
+
+typedef enum { IGRAPH_REWIRING_SIMPLE = 0,
+               IGRAPH_REWIRING_SIMPLE_LOOPS
+             } igraph_rewiring_t;
+
+typedef enum { IGRAPH_EDGEORDER_ID = 0,
+               IGRAPH_EDGEORDER_FROM,
+               IGRAPH_EDGEORDER_TO
+             } igraph_edgeorder_type_t;
+
+typedef enum { IGRAPH_TO_DIRECTED_ARBITRARY = 0,
+               IGRAPH_TO_DIRECTED_MUTUAL
+             } igraph_to_directed_t;
+
+typedef enum { IGRAPH_TO_UNDIRECTED_EACH = 0,
+               IGRAPH_TO_UNDIRECTED_COLLAPSE,
+               IGRAPH_TO_UNDIRECTED_MUTUAL
+             } igraph_to_undirected_t;
+
+typedef enum { IGRAPH_VCONN_NEI_ERROR = 0,
+               IGRAPH_VCONN_NEI_NUMBER_OF_NODES,
+               IGRAPH_VCONN_NEI_IGNORE,
+               IGRAPH_VCONN_NEI_NEGATIVE
+             } igraph_vconn_nei_t;
+
+typedef enum { IGRAPH_SPINCOMM_UPDATE_SIMPLE = 0,
+               IGRAPH_SPINCOMM_UPDATE_CONFIG
+             } igraph_spincomm_update_t;
+
+typedef enum { IGRAPH_DONT_SIMPLIFY = 0,
+               IGRAPH_SIMPLIFY
+             } igraph_lazy_adlist_simplify_t;
+
+typedef enum { IGRAPH_TRANSITIVITY_NAN = 0,
+               IGRAPH_TRANSITIVITY_ZERO
+             } igraph_transitivity_mode_t;
+
+typedef enum { IGRAPH_SPINCOMM_IMP_ORIG = 0,
+               IGRAPH_SPINCOMM_IMP_NEG
+             } igraph_spinglass_implementation_t;
+
+typedef enum { IGRAPH_COMMCMP_VI = 0,
+               IGRAPH_COMMCMP_NMI,
+               IGRAPH_COMMCMP_SPLIT_JOIN,
+               IGRAPH_COMMCMP_RAND,
+               IGRAPH_COMMCMP_ADJUSTED_RAND
+             } igraph_community_comparison_t;
+
+typedef enum { IGRAPH_ADD_WEIGHTS_NO = 0,
+               IGRAPH_ADD_WEIGHTS_YES,
+               IGRAPH_ADD_WEIGHTS_IF_PRESENT
+             } igraph_add_weights_t;
+
+typedef enum { IGRAPH_BARABASI_BAG = 0,
+               IGRAPH_BARABASI_PSUMTREE,
+               IGRAPH_BARABASI_PSUMTREE_MULTIPLE
+             } igraph_barabasi_algorithm_t;
+
+typedef enum { IGRAPH_FAS_EXACT_IP = 0,
+               IGRAPH_FAS_APPROX_EADES
+             } igraph_fas_algorithm_t;
+
+typedef enum { IGRAPH_SUBGRAPH_AUTO = 0,
+               IGRAPH_SUBGRAPH_COPY_AND_DELETE,
+               IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH
+             } igraph_subgraph_implementation_t;
+
+typedef enum { IGRAPH_IMITATE_AUGMENTED = 0,
+               IGRAPH_IMITATE_BLIND,
+               IGRAPH_IMITATE_CONTRACTED
+             } igraph_imitate_algorithm_t;
+
+typedef igraph_real_t  igraph_scalar_function_t(const igraph_vector_t *var,
+        const igraph_vector_t *par,
+        void* extra);
+typedef void igraph_vector_function_t(const igraph_vector_t *var,
+                                      const igraph_vector_t *par,
+                                      igraph_vector_t* res, void* extra);
+
+typedef enum { IGRAPH_LAYOUT_GRID = 0,
+               IGRAPH_LAYOUT_NOGRID,
+               IGRAPH_LAYOUT_AUTOGRID
+             } igraph_layout_grid_t;
+
+typedef enum { IGRAPH_RANDOM_WALK_STUCK_ERROR = 0,
+               IGRAPH_RANDOM_WALK_STUCK_RETURN
+             } igraph_random_walk_stuck_t;
+
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_constructors.h b/igraph/include/igraph_constructors.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_constructors.h
@@ -0,0 +1,80 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_CONSTRUCTORS_H
+#define IGRAPH_CONSTRUCTORS_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_matrix.h"
+#include "igraph_datatype.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Constructors, deterministic                        */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_create(igraph_t *graph, const igraph_vector_t *edges, igraph_integer_t n,
+                          igraph_bool_t directed);
+DECLDIR int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed,
+                         ...);
+DECLDIR int igraph_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix,
+                             igraph_adjacency_t mode);
+DECLDIR int igraph_weighted_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix,
+                                      igraph_adjacency_t mode, const char* attr,
+                                      igraph_bool_t loops);
+DECLDIR int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode,
+                        igraph_integer_t center);
+DECLDIR int igraph_lattice(igraph_t *graph, const igraph_vector_t *dimvector, igraph_integer_t nei,
+                           igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular);
+DECLDIR int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed,
+                        igraph_bool_t mutual, igraph_bool_t circular);
+DECLDIR int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children,
+                        igraph_tree_mode_t type);
+DECLDIR int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer);
+DECLDIR int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops);
+DECLDIR int igraph_full_citation(igraph_t *graph, igraph_integer_t n,
+                                 igraph_bool_t directed);
+DECLDIR int igraph_atlas(igraph_t *graph, int number);
+DECLDIR int igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes,
+        const igraph_matrix_t *W, igraph_bool_t directed);
+DECLDIR int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order,
+                                        igraph_neimode_t mode);
+DECLDIR int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph);
+
+DECLDIR int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n);
+DECLDIR int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n);
+DECLDIR int igraph_famous(igraph_t *graph, const char *name);
+DECLDIR int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n,
+                              const igraph_vector_t *shifts,
+                              igraph_integer_t repeats);
+DECLDIR int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...);
+DECLDIR int igraph_realize_degree_sequence(igraph_t *graph,
+        const igraph_vector_t *outdeg, const igraph_vector_t *indeg,
+        igraph_realize_degseq_t method);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_conversion.h b/igraph/include/igraph_conversion.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_conversion.h
@@ -0,0 +1,66 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_CONVERSION_H
+#define IGRAPH_CONVERSION_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_spmatrix.h"
+#include "igraph_matrix.h"
+#include "igraph_sparsemat.h"
+#include "igraph_attributes.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Conversion                                         */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res,
+                                 igraph_get_adjacency_t type, igraph_bool_t eids);
+DECLDIR int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res,
+                                        igraph_get_adjacency_t type);
+
+DECLDIR int igraph_get_stochastic(const igraph_t *graph,
+                                  igraph_matrix_t *matrix,
+                                  igraph_bool_t column_wise);
+
+DECLDIR int igraph_get_stochastic_sparsemat(const igraph_t *graph,
+        igraph_sparsemat_t *sparsemat,
+        igraph_bool_t column_wise);
+
+DECLDIR int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol);
+
+DECLDIR int igraph_to_directed(igraph_t *graph,
+                               igraph_to_directed_t flags);
+DECLDIR int igraph_to_undirected(igraph_t *graph,
+                                 igraph_to_undirected_t flags,
+                                 const igraph_attribute_combination_t *edge_comb);
+DECLDIR int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t *prufer);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_datatype.h b/igraph/include/igraph_datatype.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_datatype.h
@@ -0,0 +1,83 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_DATATYPE_H
+#define IGRAPH_DATATYPE_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+/**
+ * \ingroup internal
+ * \struct igraph_t
+ * \brief The internal data structure for storing graphs.
+ *
+ * It is simple and efficient. It has the following members:
+ * - <b>n</b> The number of vertices, reduntant.
+ * - <b>directed</b> Whether the graph is directed.
+ * - <b>from</b> The first column of the edge list.
+ * - <b>to</b> The second column of the edge list.
+ * - <b>oi</b> The index of the edge list by the first column. Thus
+ *   the first edge according to this order goes from
+ *   \c from[oi[0]] to \c to[oi[0]]. The length of
+ *   this vector is the same as the number of edges in the graph.
+ * - <b>ii</b> The index of the edge list by the second column.
+ *   The length of this vector is the same as the number of edges.
+ * - <b>os</b> Contains pointers to the edgelist (\c from
+ *   and \c to for every vertex. The first edge \em from
+ *   vertex \c v is edge no. \c from[oi[os[v]]] if
+ *   \c os[v]<os[v+1]. If \c os[v]==os[v+1] then
+ *   there are no edges \em from node \c v. Its length is
+ *   the number of vertices plus one, the last element is always the
+ *   same as the number of edges and is contained only to ease the
+ *   queries.
+ * - <b>is</b> This is basically the same as <b>os</b>, but this time
+ *   for the incoming edges.
+ *
+ * For undirected graph, the same edge list is stored, ie. an
+ * undirected edge is stored only once, and for checking whether there
+ * is an undirected edge from \c v1 to \c v2 one
+ * should search for both \c from=v1, \c to=v2 and
+ * \c from=v2, \c to=v1.
+ *
+ * The storage requirements for a graph with \c |V| vertices
+ * and \c |E| edges is \c O(|E|+|V|).
+ */
+typedef struct igraph_s {
+    igraph_integer_t n;
+    igraph_bool_t directed;
+    igraph_vector_t from;
+    igraph_vector_t to;
+    igraph_vector_t oi;
+    igraph_vector_t ii;
+    igraph_vector_t os;
+    igraph_vector_t is;
+    void *attr;
+} igraph_t;
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_decls.h b/igraph/include/igraph_decls.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_decls.h
@@ -0,0 +1,26 @@
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+#undef DECLDIR
+#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
+    #if defined (__MINGW32__) || defined (__CYGWIN32__)
+        #define DECLDIR /**/
+    #else
+        #ifdef IGRAPH_EXPORTS
+            #define DECLDIR __declspec(dllexport)
+        #elif defined(IGRAPH_STATIC)
+            #define DECLDIR /**/
+        #else
+            #define DECLDIR __declspec(dllimport)
+        #endif
+    #endif
+#else
+    #define DECLDIR /**/
+#endif
diff --git a/igraph/include/igraph_dqueue.h b/igraph/include/igraph_dqueue.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_dqueue.h
@@ -0,0 +1,73 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_DQUEUE_H
+#define IGRAPH_DQUEUE_H
+
+#include "igraph_types.h"
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* double ended queue, very useful                    */
+/* -------------------------------------------------- */
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "igraph_dqueue_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "igraph_dqueue_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "igraph_dqueue_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "igraph_dqueue_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "igraph_dqueue_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
+
+#define IGRAPH_DQUEUE_NULL { 0,0,0,0 }
+#define IGRAPH_DQUEUE_INIT_FINALLY(v, size) \
+    do { IGRAPH_CHECK(igraph_dqueue_init(v, size)); \
+        IGRAPH_FINALLY(igraph_dqueue_destroy, v); } while (0)
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_dqueue_pmt.h b/igraph/include/igraph_dqueue_pmt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_dqueue_pmt.h
@@ -0,0 +1,49 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/**
+ * Double ended queue data type.
+ * \ingroup internal
+ */
+
+typedef struct TYPE(igraph_dqueue) {
+    BASE *begin;
+    BASE *end;
+    BASE *stor_begin;
+    BASE *stor_end;
+} TYPE(igraph_dqueue);
+
+DECLDIR int FUNCTION(igraph_dqueue, init)    (TYPE(igraph_dqueue)* q, long int size);
+DECLDIR void FUNCTION(igraph_dqueue, destroy) (TYPE(igraph_dqueue)* q);
+DECLDIR igraph_bool_t FUNCTION(igraph_dqueue, empty)   (const TYPE(igraph_dqueue)* q);
+DECLDIR void FUNCTION(igraph_dqueue, clear)   (TYPE(igraph_dqueue)* q);
+DECLDIR igraph_bool_t FUNCTION(igraph_dqueue, full)    (TYPE(igraph_dqueue)* q);
+DECLDIR long int FUNCTION(igraph_dqueue, size)    (const TYPE(igraph_dqueue)* q);
+DECLDIR BASE FUNCTION(igraph_dqueue, pop)     (TYPE(igraph_dqueue)* q);
+DECLDIR BASE FUNCTION(igraph_dqueue, pop_back)(TYPE(igraph_dqueue)* q);
+DECLDIR BASE FUNCTION(igraph_dqueue, head)    (const TYPE(igraph_dqueue)* q);
+DECLDIR BASE FUNCTION(igraph_dqueue, back)    (const TYPE(igraph_dqueue)* q);
+DECLDIR int FUNCTION(igraph_dqueue, push)    (TYPE(igraph_dqueue)* q, BASE elem);
+int FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q);
+int FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file);
+DECLDIR BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, long int idx);
diff --git a/igraph/include/igraph_eigen.h b/igraph/include/igraph_eigen.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_eigen.h
@@ -0,0 +1,112 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_arpack.h"
+#include "igraph_lapack.h"
+#include "igraph_sparsemat.h"
+
+#ifndef IGRAPH_EIGEN_H
+#define IGRAPH_EIGEN_H
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+typedef enum { IGRAPH_EIGEN_AUTO = 0,
+               IGRAPH_EIGEN_LAPACK,
+               IGRAPH_EIGEN_ARPACK,
+               IGRAPH_EIGEN_COMP_AUTO,
+               IGRAPH_EIGEN_COMP_LAPACK,
+               IGRAPH_EIGEN_COMP_ARPACK
+             } igraph_eigen_algorithm_t;
+
+typedef enum { IGRAPH_EIGEN_LM = 0,
+               IGRAPH_EIGEN_SM, /* 1 */
+               IGRAPH_EIGEN_LA, /* 2 */
+               IGRAPH_EIGEN_SA, /* 3 */
+               IGRAPH_EIGEN_BE, /* 4 */
+               IGRAPH_EIGEN_LR, /* 5 */
+               IGRAPH_EIGEN_SR, /* 6 */
+               IGRAPH_EIGEN_LI, /* 7 */
+               IGRAPH_EIGEN_SI, /* 8 */
+               IGRAPH_EIGEN_ALL, /* 9 */
+               IGRAPH_EIGEN_INTERVAL, /* 10 */
+               IGRAPH_EIGEN_SELECT
+             }  /* 11 */
+igraph_eigen_which_position_t;
+
+typedef struct igraph_eigen_which_t {
+    igraph_eigen_which_position_t pos;
+    int howmany;
+    int il, iu;
+    igraph_real_t vl, vu;
+    int vestimate;
+    igraph_lapack_dgeevx_balance_t balance;
+} igraph_eigen_which_t;
+
+DECLDIR int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A,
+        const igraph_sparsemat_t *sA,
+        igraph_arpack_function_t *fun, int n,
+        void *extra,
+        igraph_eigen_algorithm_t algorithm,
+        const igraph_eigen_which_t *which,
+        igraph_arpack_options_t *options,
+        igraph_arpack_storage_t *storage,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors);
+
+DECLDIR int igraph_eigen_matrix(const igraph_matrix_t *A,
+                                const igraph_sparsemat_t *sA,
+                                igraph_arpack_function_t *fun, int n,
+                                void *extra,
+                                igraph_eigen_algorithm_t algorithm,
+                                const igraph_eigen_which_t *which,
+                                igraph_arpack_options_t *options,
+                                igraph_arpack_storage_t *storage,
+                                igraph_vector_complex_t *values,
+                                igraph_matrix_complex_t *vectors);
+
+DECLDIR int igraph_eigen_adjacency(const igraph_t *graph,
+                                   igraph_eigen_algorithm_t algorithm,
+                                   const igraph_eigen_which_t *which,
+                                   igraph_arpack_options_t *options,
+                                   igraph_arpack_storage_t *storage,
+                                   igraph_vector_t *values,
+                                   igraph_matrix_t *vectors,
+                                   igraph_vector_complex_t *cmplxvalues,
+                                   igraph_matrix_complex_t *cmplxvectors);
+
+DECLDIR int igraph_eigen_laplacian(const igraph_t *graph,
+                                   igraph_eigen_algorithm_t algorithm,
+                                   const igraph_eigen_which_t *which,
+                                   igraph_arpack_options_t *options,
+                                   igraph_arpack_storage_t *storage,
+                                   igraph_vector_t *values,
+                                   igraph_matrix_t *vectors,
+                                   igraph_vector_complex_t *cmplxvalues,
+                                   igraph_matrix_complex_t *cmplxvectors);
+
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_embedding.h b/igraph/include/igraph_embedding.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_embedding.h
@@ -0,0 +1,69 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_EMBEDDING_H
+#define IGRAPH_EMBEDDING_H
+
+#include "igraph_decls.h"
+#include "igraph_datatype.h"
+#include "igraph_arpack.h"
+#include "igraph_eigen.h"
+#include "igraph_constants.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_adjacency_spectral_embedding(const igraph_t *graph,
+        igraph_integer_t no,
+        const igraph_vector_t *weights,
+        igraph_eigen_which_position_t which,
+        igraph_bool_t scaled,
+        igraph_matrix_t *X,
+        igraph_matrix_t *Y,
+        igraph_vector_t *D,
+        const igraph_vector_t *cvec,
+        igraph_arpack_options_t *options);
+
+typedef enum {
+    IGRAPH_EMBEDDING_D_A = 0,
+    IGRAPH_EMBEDDING_I_DAD,
+    IGRAPH_EMBEDDING_DAD,
+    IGRAPH_EMBEDDING_OAP
+} igraph_laplacian_spectral_embedding_type_t;
+
+DECLDIR int igraph_laplacian_spectral_embedding(const igraph_t *graph,
+        igraph_integer_t no,
+        const igraph_vector_t *weights,
+        igraph_eigen_which_position_t which,
+        igraph_neimode_t degmode,
+        igraph_laplacian_spectral_embedding_type_t type,
+        igraph_bool_t scaled,
+        igraph_matrix_t *X,
+        igraph_matrix_t *Y,
+        igraph_vector_t *D,
+        igraph_arpack_options_t *options);
+
+DECLDIR int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_epidemics.h b/igraph/include/igraph_epidemics.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_epidemics.h
@@ -0,0 +1,66 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_EPIDEMICS_H
+#define IGRAPH_EPIDEMICS_H
+
+#include "igraph_decls.h"
+#include "igraph_datatype.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+/**
+ * \struct igraph_sir_t
+ *
+ * Data structure to store the results of one simulation
+ * of the SIR (susceptible-infected-recovered) model on a graph.
+ *
+ * It has the following members. They are all (real or integer)
+ * vectors, and they are of the same length.
+ *
+ * \member times A vector, the times of the events are stored here.
+ * \member no_s An integer vector, the number of susceptibles in
+ *              each time step is stored here.
+ * \member no_i An integer vector, the number of infected individuals
+ *              at each time step, is stored here.
+ * \member no_r An integer vector, the number of recovered individuals
+ *              is stored here at each time step.
+ */
+
+typedef struct igraph_sir_t {
+    igraph_vector_t times;
+    igraph_vector_int_t no_s, no_i, no_r;
+} igraph_sir_t;
+
+DECLDIR int igraph_sir_init(igraph_sir_t *sir);
+DECLDIR void igraph_sir_destroy(igraph_sir_t *sir);
+
+DECLDIR int igraph_sir(const igraph_t *graph, igraph_real_t beta,
+                       igraph_real_t gamma, igraph_integer_t no_sim,
+                       igraph_vector_ptr_t *result);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_error.h b/igraph/include/igraph_error.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_error.h
@@ -0,0 +1,720 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_ERROR_H
+#define IGRAPH_ERROR_H
+
+#include <stdarg.h>
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/* This file contains the igraph error handling.
+ * Most bits are taken literally from the GSL library (with the GSL_
+ * prefix renamed to IGRAPH_), as I couldn't find a better way to do
+ * them. */
+
+/**
+ * \section errorhandlingbasics Error handling basics
+ *
+ * <para>\a igraph functions can run into various problems preventing them
+ * from normal operation. The user might have supplied invalid arguments,
+ * e.g. a non-square matrix when a square-matrix was expected, or the program
+ * has run out of memory while some more memory allocation is required, etc.
+ * </para>
+ *
+ * <para>By default \a igraph aborts the program when it runs into an
+ * error. While this behavior might be good enough for smaller programs,
+ * it is without doubt avoidable in larger projects. Please read further
+ * if your project requires more sophisticated error handling. You can
+ * safely skip the rest of this chapter otherwise.
+ * </para>
+ */
+
+/**
+ * \section errorhandlers Error handlers
+ *
+ * <para>
+ * If \a igraph runs into an error - an invalid argument was supplied
+ * to a function, or we've ran out of memory - the control is
+ * transferred to the \emb error handler \eme function.
+ * </para><para>
+ * The default error handler is \ref igraph_error_handler_abort which
+ * prints an error message and aborts the program.
+ * </para>
+ * <para>
+ * The \ref igraph_set_error_handler() function can be used to set a new
+ * error handler function of type \ref igraph_error_handler_t; see the
+ * documentation of this type for details.
+ * </para>
+ * <para>
+ * There are two other predefined error handler functions,
+ * \ref igraph_error_handler_ignore and \ref igraph_error_handler_printignore.
+ * These deallocate the temporarily allocated memory (more about this
+ * later) and return with the error code. The latter also prints an
+ * error message. If you use these error handlers you need to take
+ * care about possible errors yourself by checking the return value of
+ * (almost) every non-void \a igraph function.
+ * </para><para>
+ * Independently of the error handler installed, all functions in the
+ * library do their best to leave their arguments
+ * \em semantically unchanged if an error
+ * happens. By semantically we mean that the implementation of an
+ * object supplied as an argument might change, but its
+ * \quote meaning \endquote in most cases does not. The rare occasions
+ * when this rule is violated are documented in this manual.
+ * </para>
+ */
+
+/**
+ * \section errorcodes Error codes
+ *
+ * <para>Every \a igraph function which can fail return a
+ * single integer error code. Some functions are very simple and
+ * cannot run into any error, these may return other types, or
+ * \type void as well. The error codes are defined by the
+ * \ref igraph_error_type_t enumeration.
+ * </para>
+ */
+
+/**
+ * \section writing_error_handlers Writing error handlers
+ *
+ * <para>
+ * The contents of the rest of this chapter might be useful only
+ * for those who want to create an interface to \a igraph from another
+ * language. Most readers can safely skip to the next chapter.
+ * </para>
+ *
+ * <para>
+ * You can write and install error handlers simply by defining a
+ * function of type \ref igraph_error_handler_t and calling
+ * \ref igraph_set_error_handler(). This feature is useful for interface
+ * writers, as \a igraph will have the chance to
+ * signal errors the appropriate way, eg. the R interface defines an
+ * error handler which calls the <function>error()</function>
+ * function, as required by R, while the Python interface has an error
+ * handler which raises an exception according to the Python way.
+ * </para>
+ * <para>
+ * If you want to write an error handler, your error handler should
+ * call \ref IGRAPH_FINALLY_FREE() to deallocate all temporary memory to
+ * prevent memory leaks.
+ * </para>
+ */
+
+/**
+ * \section error_handling_internals Error handling internals
+ *
+ * <para>
+ * If an error happens, the functions in the library call the
+ * \ref IGRAPH_ERROR macro with a textual description of the error and an
+ * \a igraph error code. This macro calls (through the \ref
+ * igraph_error() function) the installed error handler. Another useful
+ * macro is \ref IGRAPH_CHECK(). This checks the return value of its
+ * argument, which is normally a function call, and calls \ref
+ * IGRAPH_ERROR if it is not \c IGRAPH_SUCCESS.
+ * </para>
+ */
+
+/**
+ * \section deallocating_memory Deallocating memory
+ *
+ * <para>
+ * If a function runs into an error (and the program is not aborted)
+ * the error handler should deallocate all temporary memory. This is
+ * done by storing the address and the destroy function of all temporary
+ * objects in a stack. The \ref IGRAPH_FINALLY function declares an object as
+ * temporary by placing its address in the stack. If an \a igraph function returns
+ * with success it calls \ref IGRAPH_FINALLY_CLEAN() with the
+ * number of objects to remove from the stack. If an error happens
+ * however, the error handler should call \ref IGRAPH_FINALLY_FREE() to
+ * deallocate each object added to the stack. This means that the
+ * temporary objects allocated in the calling function (and etc.) will
+ * be freed as well.
+ * </para>
+ */
+
+/**
+ * \section writing_functions_error_handling Writing \a igraph functions with
+ * proper error handling
+ *
+ * <para>
+ * There are some simple rules to keep in order to have functions
+ * behaving well in erroneous situations. First, check the arguments
+ * of the functions and call \ref IGRAPH_ERROR if they are invalid. Second,
+ * call \ref IGRAPH_FINALLY on each dynamically allocated object and call
+ * \ref IGRAPH_FINALLY_CLEAN() with the proper argument before returning. Third, use
+ * \ref IGRAPH_CHECK on all \a igraph function calls which can generate errors.
+ * </para>
+ * <para>
+ * The size of the stack used for this bookkeeping is fixed, and
+ * small. If you want to allocate several objects, write a destroy
+ * function which can deallocate all of these. See the
+ * <filename>adjlist.c</filename> file in the
+ * \a igraph source for an example.
+ * </para>
+ * <para>
+ * For some functions these mechanisms are simply not flexible
+ * enough. These functions should define their own error handlers and
+ * restore the error handler before they return.
+ * </para>
+ */
+
+/**
+ * \section error_handling_threads Error handling and threads
+ *
+ * <para>
+ * It is likely that the \a igraph error handling
+ * method is \em not thread-safe, mainly because of
+ * the static global stack which is used to store the address of the
+ * temporarily allocated objects. This issue might be addressed in a
+ * later version of \a igraph.
+ * </para>
+ */
+
+/**
+ * \typedef igraph_error_handler_t
+ * \brief Type of error handler functions.
+ *
+ * This is the type of the error handler functions.
+ * \param reason Textual description of the error.
+ * \param file The source file in which the error is noticed.
+ * \param line The number of the line in the source file which triggered
+ *   the error
+ * \param igraph_errno The \a igraph error code.
+ */
+
+typedef void igraph_error_handler_t (const char * reason, const char * file,
+                                     int line, int igraph_errno);
+
+/**
+ * \var igraph_error_handler_abort
+ * \brief Abort program in case of error.
+ *
+ * The default error handler, prints an error message and aborts the
+ * program.
+ */
+
+extern igraph_error_handler_t igraph_error_handler_abort;
+
+/**
+ * \var igraph_error_handler_ignore
+ * \brief Ignore errors.
+ *
+ * This error handler frees the temporarily allocated memory and returns
+ * with the error code.
+ */
+
+extern igraph_error_handler_t igraph_error_handler_ignore;
+
+/**
+ * \var igraph_error_handler_printignore
+ * \brief Print and ignore errors.
+ *
+ * Frees temporarily allocated memory, prints an error message to the
+ * standard error and returns with the error code.
+ */
+
+extern igraph_error_handler_t igraph_error_handler_printignore;
+
+/**
+ * \function igraph_set_error_handler
+ * \brief Set a new error handler.
+ *
+ * Installs a new error handler. If called with 0, it installs the
+ * default error handler (which is currently
+ * \ref igraph_error_handler_abort).
+ * \param new_handler The error handler function to install.
+ * \return The old error handler function. This should be saved and
+ *   restored if \p new_handler is not needed any
+ *   more.
+ */
+
+DECLDIR igraph_error_handler_t* igraph_set_error_handler(igraph_error_handler_t* new_handler);
+
+/**
+ * \typedef igraph_error_type_t
+ * \brief Error code type.
+ * These are the possible values returned by \a igraph functions.
+ * Note that these are interesting only if you defined an error handler
+ * with \ref igraph_set_error_handler(). Otherwise the program is aborted
+ * and the function causing the error never returns.
+ *
+ * \enumval IGRAPH_SUCCESS The function successfully completed its task.
+ * \enumval IGRAPH_FAILURE Something went wrong. You'll almost never
+ *    meet this error as normally more specific error codes are used.
+ * \enumval IGRAPH_ENOMEM There wasn't enough memory to allocate
+ *    on the heap.
+ * \enumval IGRAPH_PARSEERROR A parse error was found in a file.
+ * \enumval IGRAPH_EINVAL A parameter's value is invalid. Eg. negative
+ *    number was specified as the number of vertices.
+ * \enumval IGRAPH_EXISTS A graph/vertex/edge attribute is already
+ *    installed with the given name.
+ * \enumval IGRAPH_EINVEVECTOR Invalid vector of vertex ids. A vertex id
+ *    is either negative or bigger than the number of vertices minus one.
+ * \enumval IGRAPH_EINVVID Invalid vertex id, negative or too big.
+ * \enumval IGRAPH_NONSQUARE A non-square matrix was received while a
+ *    square matrix was expected.
+ * \enumval IGRAPH_EINVMODE Invalid mode parameter.
+ * \enumval IGRAPH_EFILE A file operation failed. Eg. a file doesn't exist,
+ *   or the user has no rights to open it.
+ * \enumval IGRAPH_UNIMPLEMENTED Attempted to call an unimplemented or
+ *   disabled (at compile-time) function.
+ * \enumval IGRAPH_DIVERGED A numeric algorithm failed to converge.
+ * \enumval IGRAPH_ARPACK_PROD Matrix-vector product failed.
+ * \enumval IGRAPH_ARPACK_NPOS N must be positive.
+ * \enumval IGRAPH_ARPACK_NEVNPOS NEV must be positive.
+ * \enumval IGRAPH_ARPACK_NCVSMALL NCV must be bigger.
+ * \enumval IGRAPH_ARPACK_NONPOSI Maximum number of iterations should be positive.
+ * \enumval IGRAPH_ARPACK_WHICHINV Invalid WHICH parameter.
+ * \enumval IGRAPH_ARPACK_BMATINV Invalid BMAT parameter.
+ * \enumval IGRAPH_ARPACK_WORKLSMALL WORKL is too small.
+ * \enumval IGRAPH_ARPACK_TRIDERR LAPACK error in tridiagonal eigenvalue calculation.
+ * \enumval IGRAPH_ARPACK_ZEROSTART Starting vector is zero.
+ * \enumval IGRAPH_ARPACK_MODEINV MODE is invalid.
+ * \enumval IGRAPH_ARPACK_MODEBMAT MODE and BMAT are not compatible.
+ * \enumval IGRAPH_ARPACK_ISHIFT ISHIFT must be 0 or 1.
+ * \enumval IGRAPH_ARPACK_NEVBE NEV and WHICH='BE' are incompatible.
+ * \enumval IGRAPH_ARPACK_NOFACT Could not build an Arnoldi factorization.
+ * \enumval IGRAPH_ARPACK_FAILED No eigenvalues to sufficient accuracy.
+ * \enumval IGRAPH_ARPACK_HOWMNY HOWMNY is invalid.
+ * \enumval IGRAPH_ARPACK_HOWMNYS HOWMNY='S' is not implemented.
+ * \enumval IGRAPH_ARPACK_EVDIFF Different number of converged Ritz values.
+ * \enumval IGRAPH_ARPACK_SHUR Error from calculation of a real Schur form.
+ * \enumval IGRAPH_ARPACK_LAPACK LAPACK (dtrevc) error for calculating eigenvectors.
+ * \enumval IGRAPH_ARPACK_UNKNOWN Unknown ARPACK error.
+ * \enumval IGRAPH_ENEGLOOP Negative loop detected while calculating shortest paths.
+ * \enumval IGRAPH_EINTERNAL Internal error, likely a bug in igraph.
+ * \enumval IGRAPH_EDIVZERO Big integer division by zero.
+ * \enumval IGARPH_GLP_EBOUND GLPK error (GLP_EBOUND).
+ * \enumval IGARPH_GLP_EROOT GLPK error (GLP_EROOT).
+ * \enumval IGARPH_GLP_ENOPFS GLPK error (GLP_ENOPFS).
+ * \enumval IGARPH_GLP_ENODFS GLPK error (GLP_ENODFS).
+ * \enumval IGARPH_GLP_EFAIL GLPK error (GLP_EFAIL).
+ * \enumval IGARPH_GLP_EMIPGAP GLPK error (GLP_EMIPGAP).
+ * \enumval IGARPH_GLP_ETMLIM GLPK error (GLP_ETMLIM).
+ * \enumval IGARPH_GLP_ESTOP GLPK error (GLP_ESTOP).
+ * \enumval IGRAPH_EATTRIBUTES Attribute handler error. The user is not
+ *   expected to find this; it is signalled if some igraph function is
+ *   not using the attribute handler interface properly.
+ * \enumval IGRAPH_EATTRCOMBINE Unimplemented attribute combination
+ *   method for the given attribute type.
+ * \enumval IGRAPH_ELAPACK A LAPACK call resulted an error.
+ * \enumval IGRAPH_EDRL Internal error in the DrL layout generator.
+ * \enumval IGRAPH_EOVERFLOW Integer or double overflow.
+ * \enumval IGRAPH_EGLP Internal GLPK error.
+ * \enumval IGRAPH_CPUTIME CPU time exceeded.
+ * \enumval IGRAPH_EUNDERFLOW Integer or double underflow.
+ * \enumval IGRAPH_ERWSTUCK Random walk got stuck.
+ */
+
+/* Each enum value below must have a corresponding error string in
+ * igraph_i_error_strings[] in igraph_error.c */
+typedef enum {
+    IGRAPH_SUCCESS           = 0,
+    IGRAPH_FAILURE           = 1,
+    IGRAPH_ENOMEM            = 2,
+    IGRAPH_PARSEERROR        = 3,
+    IGRAPH_EINVAL            = 4,
+    IGRAPH_EXISTS            = 5,
+    IGRAPH_EINVEVECTOR       = 6,
+    IGRAPH_EINVVID           = 7,
+    IGRAPH_NONSQUARE         = 8,
+    IGRAPH_EINVMODE          = 9,
+    IGRAPH_EFILE             = 10,
+    IGRAPH_UNIMPLEMENTED     = 12,
+    IGRAPH_INTERRUPTED       = 13,
+    IGRAPH_DIVERGED          = 14,
+    IGRAPH_ARPACK_PROD       = 15,
+    IGRAPH_ARPACK_NPOS       = 16,
+    IGRAPH_ARPACK_NEVNPOS    = 17,
+    IGRAPH_ARPACK_NCVSMALL   = 18,
+    IGRAPH_ARPACK_NONPOSI    = 19,
+    IGRAPH_ARPACK_WHICHINV   = 20,
+    IGRAPH_ARPACK_BMATINV    = 21,
+    IGRAPH_ARPACK_WORKLSMALL = 22,
+    IGRAPH_ARPACK_TRIDERR    = 23,
+    IGRAPH_ARPACK_ZEROSTART  = 24,
+    IGRAPH_ARPACK_MODEINV    = 25,
+    IGRAPH_ARPACK_MODEBMAT   = 26,
+    IGRAPH_ARPACK_ISHIFT     = 27,
+    IGRAPH_ARPACK_NEVBE      = 28,
+    IGRAPH_ARPACK_NOFACT     = 29,
+    IGRAPH_ARPACK_FAILED     = 30,
+    IGRAPH_ARPACK_HOWMNY     = 31,
+    IGRAPH_ARPACK_HOWMNYS    = 32,
+    IGRAPH_ARPACK_EVDIFF     = 33,
+    IGRAPH_ARPACK_SHUR       = 34,
+    IGRAPH_ARPACK_LAPACK     = 35,
+    IGRAPH_ARPACK_UNKNOWN    = 36,
+    IGRAPH_ENEGLOOP          = 37,
+    IGRAPH_EINTERNAL         = 38,
+    IGRAPH_ARPACK_MAXIT      = 39,
+    IGRAPH_ARPACK_NOSHIFT    = 40,
+    IGRAPH_ARPACK_REORDER    = 41,
+    IGRAPH_EDIVZERO          = 42,
+    IGRAPH_GLP_EBOUND        = 43,
+    IGRAPH_GLP_EROOT         = 44,
+    IGRAPH_GLP_ENOPFS        = 45,
+    IGRAPH_GLP_ENODFS        = 46,
+    IGRAPH_GLP_EFAIL         = 47,
+    IGRAPH_GLP_EMIPGAP       = 48,
+    IGRAPH_GLP_ETMLIM        = 49,
+    IGRAPH_GLP_ESTOP         = 50,
+    IGRAPH_EATTRIBUTES       = 51,
+    IGRAPH_EATTRCOMBINE      = 52,
+    IGRAPH_ELAPACK           = 53,
+    IGRAPH_EDRL              = 54,
+    IGRAPH_EOVERFLOW         = 55,
+    IGRAPH_EGLP              = 56,
+    IGRAPH_CPUTIME           = 57,
+    IGRAPH_EUNDERFLOW        = 58,
+    IGRAPH_ERWSTUCK          = 59,
+    IGRAPH_STOP              = 60, /* undocumented, used internally; signals a request to stop in functions like igraph_i_maximal_cliques_bk */
+} igraph_error_type_t;
+
+/**
+ * \define IGRAPH_ERROR
+ * \brief Trigger an error.
+ *
+ * \a igraph functions usually use this macro when they notice an error.
+ * It calls
+ * \ref igraph_error() with the proper parameters and if that returns
+ * the macro returns the "calling" function as well, with the error
+ * code. If for some (suspicious) reason you want to call the error
+ * handler without returning from the current function, call
+ * \ref igraph_error() directly.
+ * \param reason Textual description of the error. This should be
+ *   something more descriptive than the text associated with the error
+ *   code. Eg. if the error code is \c IGRAPH_EINVAL,
+ *   its associated text (see  \ref igraph_strerror()) is "Invalid
+ *   value" and this string should explain which parameter was invalid
+ *   and maybe why.
+ * \param igraph_errno The \a igraph error code.
+ */
+
+#define IGRAPH_ERROR(reason,igraph_errno) \
+    do { \
+        igraph_error (reason, __FILE__, __LINE__, igraph_errno) ; \
+        return igraph_errno ; \
+    } while (0)
+
+/**
+ * \function igraph_error
+ * \brief Trigger an error.
+ *
+ * \a igraph functions usually call this function (most often via the
+ * \ref IGRAPH_ERROR macro) if they notice an error.
+ * It calls the currently installed error handler function with the
+ * supplied arguments.
+ *
+ * \param reason Textual description of the error.
+ * \param file The source file in which the error was noticed.
+ * \param line The number of line in the source file which triggered the
+ *   error.
+ * \param igraph_errno The \a igraph error code.
+ * \return the error code (if it returns)
+ *
+ * \sa igraph_errorf().
+ */
+
+DECLDIR int igraph_error(const char *reason, const char *file, int line,
+                         int igraph_errno);
+
+/**
+ * \function igraph_errorf
+ * \brief Trigger an error, printf-like version.
+ *
+ * \param reason Textual description of the error, interpreted as
+ *               a printf format string.
+ * \param file The source file in which the error was noticed.
+ * \param line The line in the source file which triggered the error.
+ * \param igraph_errno The \a igraph error code.
+ * \param ... Additional parameters, the values to substitute into the
+ *            format string.
+ *
+ * \sa igraph_error().
+ */
+
+DECLDIR int igraph_errorf(const char *reason, const char *file, int line,
+                          int igraph_errno, ...);
+
+DECLDIR int igraph_errorvf(const char *reason, const char *file, int line,
+                           int igraph_errno, va_list ap);
+
+/**
+ * \function igraph_strerror
+ * \brief Textual description of an error.
+ *
+ * This is a simple utility function, it gives a short general textual
+ * description for an \a igraph error code.
+ *
+ * \param igraph_errno The \a igraph error code.
+ * \return pointer to the textual description of the error code.
+ */
+
+DECLDIR const char* igraph_strerror(const int igraph_errno);
+
+#define IGRAPH_ERROR_SELECT_2(a,b)       ((a) != IGRAPH_SUCCESS ? (a) : ((b) != IGRAPH_SUCCESS ? (b) : IGRAPH_SUCCESS))
+#define IGRAPH_ERROR_SELECT_3(a,b,c)     ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_2(b,c))
+#define IGRAPH_ERROR_SELECT_4(a,b,c,d)   ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_3(b,c,d))
+#define IGRAPH_ERROR_SELECT_5(a,b,c,d,e) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_4(b,c,d,e))
+
+/* Now comes the more convenient error handling macro arsenal.
+ * Ideas taken from exception.{h,c} by Laurent Deniau see
+ * http://cern.ch/Laurent.Deniau/html/oopc/oopc.html#Exceptions for more
+ * information. We don't use the exception handling code though.  */
+
+struct igraph_i_protectedPtr {
+    int all;
+    void *ptr;
+    void (*func)(void*);
+};
+
+typedef void igraph_finally_func_t (void*);
+
+DECLDIR void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr);
+
+/**
+ * \function IGRAPH_FINALLY_CLEAN
+ * \brief Signal clean deallocation of objects.
+ *
+ * Removes the specified number of objects from the stack of
+ * temporarily allocated objects. Most often this is called just
+ * before returning from a function.
+ * \param num The number of objects to remove from the bookkeeping
+ *   stack.
+ */
+
+DECLDIR void IGRAPH_FINALLY_CLEAN(int num);
+
+/**
+ * \function IGRAPH_FINALLY_FREE
+ * \brief Deallocate all registered objects.
+ *
+ * Calls the destroy function for all objects in the stack of
+ * temporarily allocated objects. This is usually called only from an
+ * error handler. It is \em not appropriate to use it
+ * instead of destroying each unneeded object of a function, as it
+ * destroys the temporary objects of the caller function (and so on)
+ * as well.
+ */
+
+DECLDIR void IGRAPH_FINALLY_FREE(void);
+
+/**
+ * \function IGRAPH_FINALLY_STACK_SIZE
+ * \brief Returns the number of registered objects.
+ *
+ * Returns the number of objects in the stack of temporarily allocated
+ * objects. This function is handy if you write an own igraph routine and
+ * you want to make sure it handles errors properly. A properly written
+ * igraph routine should not leave pointers to temporarily allocated objects
+ * in the finally stack, because otherwise an \ref IGRAPH_FINALLY_FREE call
+ * in another igraph function would result in freeing these objects as well
+ * (and this is really hard to debug, since the error will be not in that
+ * function that shows erroneous behaviour). Therefore, it is advised to
+ * write your own test cases and examine \ref IGRAPH_FINALLY_STACK_SIZE
+ * before and after your test cases - the numbers should be equal.
+ */
+DECLDIR int IGRAPH_FINALLY_STACK_SIZE(void);
+
+/**
+ * \define IGRAPH_FINALLY_STACK_EMPTY
+ * \brief Returns true if there are no registered objects, false otherwise.
+ *
+ * This is just a shorthand notation for checking that
+ * \ref IGRAPH_FINALLY_STACK_SIZE is zero.
+ */
+#define IGRAPH_FINALLY_STACK_EMPTY (IGRAPH_FINALLY_STACK_SIZE() == 0)
+
+/**
+ * \define IGRAPH_FINALLY
+ * \brief Register an object for deallocation.
+ * \param func The address of the function which is normally called to
+ *   destroy the object.
+ * \param ptr Pointer to the object itself.
+ *
+ * This macro places the address of an object, together with the
+ * address of its destructor in a stack. This stack is used if an
+ * error happens to deallocate temporarily allocated objects to
+ * prevent memory leaks.
+ */
+
+#define IGRAPH_FINALLY(func,ptr) \
+    IGRAPH_FINALLY_REAL((igraph_finally_func_t*)(func), (ptr))
+
+#if !defined(GCC_VERSION_MAJOR) && defined(__GNUC__)
+    #define GCC_VERSION_MAJOR  __GNUC__
+#endif
+
+#if defined(GCC_VERSION_MAJOR) && (GCC_VERSION_MAJOR >= 3)
+    #define IGRAPH_UNLIKELY(a) __builtin_expect((a), 0)
+    #define IGRAPH_LIKELY(a)   __builtin_expect((a), 1)
+#else
+    #define IGRAPH_UNLIKELY(a) a
+    #define IGRAPH_LIKELY(a)   a
+#endif
+
+/**
+ * \define IGRAPH_CHECK
+ * \brief Check the return value of a function call.
+ *
+ * \param a An expression, usually a function call.
+ *
+ * Executes the expression and checks its value. If this is not
+ * \c IGRAPH_SUCCESS, it calls \ref IGRAPH_ERROR with
+ * the value as the error code. Here is an example usage:
+ * \verbatim IGRAPH_CHECK(vector_push_back(&amp;v, 100)); \endverbatim
+ *
+ * </para><para>There is only one reason to use this macro when writing
+ * \a igraph functions. If the user installs an error handler which
+ * returns to the auxiliary calling code (like \ref
+ * igraph_error_handler_ignore and \ref
+ * igraph_error_handler_printignore), and the \a igraph function
+ * signalling the error is called from another \a igraph function
+ * then we need to make sure that the error is propagated back to
+ * the auxiliary (ie. non-igraph) calling function. This is achieved
+ * by using <function>IGRAPH_CHECK</function> on every \a igraph
+ * call which can return an error code.
+ */
+
+#define IGRAPH_CHECK(a) do { \
+        int igraph_i_ret=(a); \
+        if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\
+            IGRAPH_ERROR("", igraph_i_ret); \
+        } } while (0)
+
+
+/**
+ * \section about_igraph_warnings Warning messages
+ *
+ * <para>
+ * Igraph also supports warning messages in addition to error
+ * messages. Warning messages typically do not terminate the
+ * program, but they are usually crucial to the user.
+ * </para>
+ *
+ * <para>
+ * Igraph warning are handled similarly to errors. There is a
+ * separate warning handler function that is called whenever
+ * an igraph function triggers a warning. This handler can be
+ * set by the \ref igraph_set_warning_handler() function. There are
+ * two predefined simple warning handlers,
+ * \ref igraph_warning_handler_ignore() and
+ * \ref igraph_warning_handler_print(), the latter being the default.
+ * </para>
+ *
+ * <para>
+ * To trigger a warning, igraph functions typically use the
+ * \ref IGRAPH_WARNING() macro, the \ref igraph_warning() function,
+ * or if more flexibility is needed, \ref igraph_warningf().
+ * </para>
+ */
+
+/**
+ * \typedef igraph_warning_handler_t
+ * Type of igraph warning handler functions
+ *
+ * Currently it is defined to have the same type as
+ * \ref igraph_error_handler_t, although the last (error code)
+ * argument is not used.
+ */
+
+typedef igraph_error_handler_t igraph_warning_handler_t;
+
+/**
+ * \function igraph_set_warning_handler
+ * Install a warning handler
+ *
+ * Install the supplied warning handler function.
+ * \param new_handler The new warning handler function to install.
+ *        Supply a null pointer here to uninstall the current
+ *        warning handler, without installing a new one.
+ * \return The current warning handler function.
+ */
+
+DECLDIR igraph_warning_handler_t* igraph_set_warning_handler(igraph_warning_handler_t* new_handler);
+
+extern igraph_warning_handler_t igraph_warning_handler_ignore;
+extern igraph_warning_handler_t igraph_warning_handler_print;
+
+/**
+ * \function igraph_warning
+ * Trigger a warning
+ *
+ * Call this function if you want to trigger a warning from within
+ * a function that uses igraph.
+ * \param reason Textual description of the warning.
+ * \param file The source file in which the warning was noticed.
+ * \param line The number of line in the source file which triggered the
+ *         warning.
+ * \param igraph_errno Warnings could have potentially error codes as well,
+ *        but this is currently not used in igraph.
+ * \return The supplied error code.
+ */
+
+DECLDIR int igraph_warning(const char *reason, const char *file, int line,
+                           int igraph_errno);
+
+/**
+ * \function igraph_warningf
+ * Trigger a warning, more flexible printf-like syntax
+ *
+ * This function is similar to \ref igraph_warning(), but
+ * uses a printf-like syntax. It substitutes the additional arguments
+ * into the \p reason template string and calls \ref igraph_warning().
+ * \param reason Textual description of the warning, a template string
+ *        with the same syntax as the standard printf C library function.
+ * \param file The source file in which the warning was noticed.
+ * \param line The number of line in the source file which triggered the
+ *         warning.
+ * \param igraph_errno Warnings could have potentially error codes as well,
+ *        but this is currently not used in igraph.
+ * \param ... The additional arguments to be substituted into the
+ *        template string.
+ * \return The supplied error code.
+ */
+
+DECLDIR int igraph_warningf(const char *reason, const char *file, int line,
+                            int igraph_errno, ...);
+
+/**
+ * \define IGRAPH_WARNING
+ * Trigger a warning.
+ *
+ * This is the usual way of triggering a warning from an igraph
+ * function. It calls \ref igraph_warning().
+ * \param reason The warning message.
+ */
+
+#define IGRAPH_WARNING(reason) \
+    do { \
+        igraph_warning(reason, __FILE__, __LINE__, -1); \
+    } while (0)
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_estack.h b/igraph/include/igraph_estack.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_estack.h
@@ -0,0 +1,47 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_ESTACK_H
+#define IGRAPH_ESTACK_H
+
+#include "igraph_stack.h"
+#include "igraph_vector.h"
+
+typedef struct igraph_estack_t {
+    igraph_stack_long_t stack;
+    igraph_vector_bool_t isin;
+} igraph_estack_t;
+
+int igraph_estack_init(igraph_estack_t *s, long int setsize,
+                       long int stacksize);
+void igraph_estack_destroy(igraph_estack_t *s);
+
+int igraph_estack_push(igraph_estack_t *s,  long int elem);
+long int igraph_estack_pop(igraph_estack_t *s);
+igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s,
+                                      long int elem);
+long int igraph_estack_size(const igraph_estack_t *s);
+
+int igraph_estack_print(const igraph_estack_t *s);
+
+#endif
diff --git a/igraph/include/igraph_flow.h b/igraph/include/igraph_flow.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_flow.h
@@ -0,0 +1,169 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_FLOW_H
+#define IGRAPH_FLOW_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* MAximum flows, minimum cuts & such                 */
+/* -------------------------------------------------- */
+
+/**
+ * \typedef igraph_maxflow_stats_t
+ * A simple data type to return some statistics from the
+ * push-relabel maximum flow solver.
+ *
+ * \param nopush The number of push operations performed.
+ * \param norelabel The number of relabel operarions performed.
+ * \param nogap The number of times the gap heuristics was used.
+ * \param nogapnodes The total number of vertices that were
+ *        omitted form further calculations because of the gap
+ *        heuristics.
+ * \param nobfs The number of times the reverse BFS was run to
+ *        assign good values to the height function. This includes
+ *        an initial run before the whole algorithm, so it is always
+ *        at least one.
+ */
+
+typedef struct {
+    int nopush, norelabel, nogap, nogapnodes, nobfs;
+} igraph_maxflow_stats_t;
+
+DECLDIR int igraph_maxflow(const igraph_t *graph, igraph_real_t *value,
+                           igraph_vector_t *flow, igraph_vector_t *cut,
+                           igraph_vector_t *partition, igraph_vector_t *partition2,
+                           igraph_integer_t source, igraph_integer_t target,
+                           const igraph_vector_t *capacity,
+                           igraph_maxflow_stats_t *stats);
+DECLDIR int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value,
+                                 igraph_integer_t source, igraph_integer_t target,
+                                 const igraph_vector_t *capacity,
+                                 igraph_maxflow_stats_t *stats);
+
+DECLDIR int igraph_st_mincut(const igraph_t *graph, igraph_real_t *value,
+                             igraph_vector_t *cut, igraph_vector_t *partition,
+                             igraph_vector_t *partition2,
+                             igraph_integer_t source, igraph_integer_t target,
+                             const igraph_vector_t *capacity);
+DECLDIR int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *res,
+                                   igraph_integer_t source, igraph_integer_t target,
+                                   const igraph_vector_t *capacity);
+
+DECLDIR int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res,
+                                const igraph_vector_t *capacity);
+DECLDIR int igraph_mincut(const igraph_t *graph,
+                          igraph_real_t *value,
+                          igraph_vector_t *partition,
+                          igraph_vector_t *partition2,
+                          igraph_vector_t *cut,
+                          const igraph_vector_t *capacity);
+
+DECLDIR int igraph_st_vertex_connectivity(const igraph_t *graph,
+        igraph_integer_t *res,
+        igraph_integer_t source,
+        igraph_integer_t target,
+        igraph_vconn_nei_t neighbors);
+DECLDIR int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res,
+                                       igraph_bool_t checks);
+
+DECLDIR int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res,
+                                        igraph_integer_t source,
+                                        igraph_integer_t target);
+DECLDIR int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res,
+                                     igraph_bool_t checks);
+
+DECLDIR int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res,
+                                       igraph_integer_t source,
+                                       igraph_integer_t target);
+DECLDIR int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res,
+        igraph_integer_t source,
+        igraph_integer_t target);
+
+DECLDIR int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res,
+                            igraph_bool_t checks);
+DECLDIR int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res,
+                            igraph_bool_t checks);
+
+/* s-t cut listing related stuff */
+
+DECLDIR int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar,
+        igraph_vector_t *capacity);
+
+DECLDIR int igraph_residual_graph(const igraph_t *graph,
+                                  const igraph_vector_t *capacity,
+                                  igraph_t *residual,
+                                  igraph_vector_t *residual_capacity,
+                                  const igraph_vector_t *flow);
+int igraph_i_residual_graph(const igraph_t *graph,
+                            const igraph_vector_t *capacity,
+                            igraph_t *residual,
+                            igraph_vector_t *residual_capacity,
+                            const igraph_vector_t *flow,
+                            igraph_vector_t *tmp);
+
+int igraph_i_reverse_residual_graph(const igraph_t *graph,
+                                    const igraph_vector_t *capacity,
+                                    igraph_t *residual,
+                                    const igraph_vector_t *flow,
+                                    igraph_vector_t *tmp);
+DECLDIR int igraph_reverse_residual_graph(const igraph_t *graph,
+        const igraph_vector_t *capacity,
+        igraph_t *residual,
+        const igraph_vector_t *flow);
+
+DECLDIR int igraph_dominator_tree(const igraph_t *graph,
+                                  igraph_integer_t root,
+                                  igraph_vector_t *dom,
+                                  igraph_t *domtree,
+                                  igraph_vector_t *leftout,
+                                  igraph_neimode_t mode);
+
+DECLDIR int igraph_all_st_cuts(const igraph_t *graph,
+                               igraph_vector_ptr_t *cuts,
+                               igraph_vector_ptr_t *partition1s,
+                               igraph_integer_t source,
+                               igraph_integer_t target);
+
+DECLDIR int igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value,
+                                  igraph_vector_ptr_t *cuts,
+                                  igraph_vector_ptr_t *partition1s,
+                                  igraph_integer_t source,
+                                  igraph_integer_t target,
+                                  const igraph_vector_t *capacity);
+
+DECLDIR int igraph_gomory_hu_tree(const igraph_t *graph,
+                                  igraph_t *tree,
+                                  igraph_vector_t *flows,
+                                  const igraph_vector_t *capacity);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_flow_internal.h b/igraph/include/igraph_flow_internal.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_flow_internal.h
@@ -0,0 +1,42 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_FLOW_INTERNAL_H
+#define IGRAPH_FLOW_INTERNAL_H
+
+#include "igraph_types.h"
+#include "igraph_marked_queue.h"
+#include "igraph_estack.h"
+#include "igraph_datatype.h"
+
+typedef int igraph_provan_shier_pivot_t(const igraph_t *graph,
+                                        const igraph_marked_queue_t *S,
+                                        const igraph_estack_t *T,
+                                        long int source,
+                                        long int target,
+                                        long int *v,
+                                        igraph_vector_t *Isv,
+                                        void *arg);
+
+#endif
+
diff --git a/igraph/include/igraph_foreign.h b/igraph/include/igraph_foreign.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_foreign.h
@@ -0,0 +1,85 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_FOREIGN_H
+#define IGRAPH_FOREIGN_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_datatype.h"
+#include "igraph_types.h"
+#include "igraph_strvector.h"
+
+#include <stdio.h>
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Read and write foreign formats                     */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream,
+                                       igraph_integer_t n, igraph_bool_t directed);
+DECLDIR int igraph_read_graph_ncol(igraph_t *graph, FILE *instream,
+                                   igraph_strvector_t *predefnames, igraph_bool_t names,
+                                   igraph_add_weights_t weights, igraph_bool_t directed);
+DECLDIR int igraph_read_graph_lgl(igraph_t *graph, FILE *instream,
+                                  igraph_bool_t names, igraph_add_weights_t weights,
+                                  igraph_bool_t directed);
+DECLDIR int igraph_read_graph_pajek(igraph_t *graph, FILE *instream);
+DECLDIR int igraph_read_graph_graphml(igraph_t *graph, FILE *instream,
+                                      int index);
+DECLDIR int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream,
+                                     igraph_strvector_t *problem,
+                                     igraph_vector_t *label,
+                                     igraph_integer_t *source,
+                                     igraph_integer_t *target,
+                                     igraph_vector_t *capacity,
+                                     igraph_bool_t directed);
+DECLDIR int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream,
+                                      igraph_bool_t directed);
+DECLDIR int igraph_read_graph_gml(igraph_t *graph, FILE *instream);
+DECLDIR int igraph_read_graph_dl(igraph_t *graph, FILE *instream,
+                                 igraph_bool_t directed);
+
+DECLDIR int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream);
+DECLDIR int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream,
+                                    const char *names, const char *weights);
+DECLDIR int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream,
+                                   const char *names, const char *weights,
+                                   igraph_bool_t isolates);
+DECLDIR int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream,
+                                       igraph_bool_t prefixattr);
+DECLDIR int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream);
+DECLDIR int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream,
+                                      long int source, long int target,
+                                      const igraph_vector_t *capacity);
+DECLDIR int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream,
+                                   const igraph_vector_t *id, const char *creator);
+DECLDIR int igraph_write_graph_dot(const igraph_t *graph, FILE *outstream);
+DECLDIR int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream,
+                                    const char* vertex_attr_name, const char* edge_attr_name);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_games.h b/igraph/include/igraph_games.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_games.h
@@ -0,0 +1,227 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_GAMES_H
+#define IGRAPH_GAMES_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_matrix.h"
+#include "igraph_vector.h"
+#include "igraph_datatype.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Constructors, games (=stochastic)                  */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n,
+                                 igraph_real_t power,
+                                 igraph_integer_t m,
+                                 const igraph_vector_t *outseq,
+                                 igraph_bool_t outpref,
+                                 igraph_real_t A,
+                                 igraph_bool_t directed,
+                                 igraph_barabasi_algorithm_t algo,
+                                 const igraph_t *start_from);
+DECLDIR int igraph_nonlinear_barabasi_game(igraph_t *graph, igraph_integer_t n,
+        igraph_real_t power,
+        igraph_integer_t m,
+        const igraph_vector_t *outseq,
+        igraph_bool_t outpref,
+        igraph_real_t zeroappeal,
+        igraph_bool_t directed);
+DECLDIR int igraph_erdos_renyi_game(igraph_t *graph, igraph_erdos_renyi_t type,
+                                    igraph_integer_t n, igraph_real_t p,
+                                    igraph_bool_t directed, igraph_bool_t loops);
+DECLDIR int igraph_erdos_renyi_game_gnp(igraph_t *graph, igraph_integer_t n, igraph_real_t p,
+                                        igraph_bool_t directed, igraph_bool_t loops);
+DECLDIR int igraph_erdos_renyi_game_gnm(igraph_t *graph, igraph_integer_t n, igraph_real_t m,
+                                        igraph_bool_t directed, igraph_bool_t loops);
+DECLDIR int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg,
+                                        const igraph_vector_t *in_deg,
+                                        igraph_degseq_t method);
+DECLDIR int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n,
+                                       igraph_integer_t m, igraph_bool_t directed, igraph_bool_t citation);
+DECLDIR int igraph_barabasi_aging_game(igraph_t *graph,
+                                       igraph_integer_t nodes,
+                                       igraph_integer_t m,
+                                       const igraph_vector_t *outseq,
+                                       igraph_bool_t outpref,
+                                       igraph_real_t pa_exp,
+                                       igraph_real_t aging_exp,
+                                       igraph_integer_t aging_bin,
+                                       igraph_real_t zero_deg_appeal,
+                                       igraph_real_t zero_age_appeal,
+                                       igraph_real_t deg_coef,
+                                       igraph_real_t age_coef,
+                                       igraph_bool_t directed);
+DECLDIR int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t n,
+                                      igraph_real_t power,
+                                      igraph_integer_t window,
+                                      igraph_integer_t m,
+                                      const igraph_vector_t *outseq,
+                                      igraph_bool_t outpref,
+                                      igraph_real_t zero_appeal,
+                                      igraph_bool_t directed);
+DECLDIR int igraph_recent_degree_aging_game(igraph_t *graph,
+        igraph_integer_t nodes,
+        igraph_integer_t m,
+        const igraph_vector_t *outseq,
+        igraph_bool_t outpref,
+        igraph_real_t pa_exp,
+        igraph_real_t aging_exp,
+        igraph_integer_t aging_bin,
+        igraph_integer_t window,
+        igraph_real_t zero_appeal,
+        igraph_bool_t directed);
+DECLDIR int igraph_callaway_traits_game (igraph_t *graph, igraph_integer_t nodes,
+        igraph_integer_t types, igraph_integer_t edges_per_step,
+        igraph_vector_t *type_dist,
+        igraph_matrix_t *pref_matrix,
+        igraph_bool_t directed);
+DECLDIR int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes,
+                                      igraph_integer_t types, igraph_integer_t k,
+                                      igraph_vector_t *type_dist,
+                                      igraph_matrix_t *pref_matrix,
+                                      igraph_bool_t directed);
+DECLDIR int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes,
+                            igraph_real_t radius, igraph_bool_t torus,
+                            igraph_vector_t *x, igraph_vector_t *y);
+DECLDIR int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes,
+                                   igraph_integer_t types,
+                                   const igraph_vector_t *type_dist,
+                                   igraph_bool_t fixed_sizes,
+                                   const igraph_matrix_t *pref_matrix,
+                                   igraph_vector_t *node_type_vec,
+                                   igraph_bool_t directed, igraph_bool_t loops);
+DECLDIR int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes,
+        igraph_integer_t types,
+        igraph_matrix_t *type_dist_matrix,
+        igraph_matrix_t *pref_matrix,
+        igraph_vector_t *node_type_in_vec,
+        igraph_vector_t *node_type_out_vec,
+        igraph_bool_t loops);
+
+DECLDIR int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob,
+                                igraph_bool_t loops, igraph_bool_t multiple);
+DECLDIR int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob,
+        igraph_bool_t loops, igraph_neimode_t mode);
+
+DECLDIR int igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim,
+                                       igraph_integer_t size, igraph_integer_t nei,
+                                       igraph_real_t p, igraph_bool_t loops,
+                                       igraph_bool_t multiple);
+
+DECLDIR int igraph_lastcit_game(igraph_t *graph,
+                                igraph_integer_t nodes, igraph_integer_t edges_per_node,
+                                igraph_integer_t agebins,
+                                const igraph_vector_t *preference, igraph_bool_t directed);
+
+DECLDIR int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes,
+                                   const igraph_vector_t *types,
+                                   const igraph_vector_t *pref,
+                                   igraph_integer_t edges_per_step,
+                                   igraph_bool_t directed);
+
+DECLDIR int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes,
+        const igraph_vector_t *types,
+        const igraph_matrix_t *pref,
+        igraph_integer_t edges_per_step,
+        igraph_bool_t directed);
+
+DECLDIR int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes,
+                                    igraph_real_t fw_prob, igraph_real_t bw_factor,
+                                    igraph_integer_t ambs, igraph_bool_t directed);
+
+
+DECLDIR int igraph_simple_interconnected_islands_game(
+    igraph_t *graph,
+    igraph_integer_t islands_n,
+    igraph_integer_t islands_size,
+    igraph_real_t islands_pin,
+    igraph_integer_t n_inter);
+
+DECLDIR int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges,
+                                       igraph_vector_t* fitness_out, igraph_vector_t* fitness_in,
+                                       igraph_bool_t loops, igraph_bool_t multiple);
+
+DECLDIR int igraph_static_power_law_game(igraph_t *graph,
+        igraph_integer_t no_of_nodes, igraph_integer_t no_of_edges,
+        igraph_real_t exponent_out, igraph_real_t exponent_in,
+        igraph_bool_t loops, igraph_bool_t multiple,
+        igraph_bool_t finite_size_correction);
+
+DECLDIR int igraph_k_regular_game(igraph_t *graph,
+                                  igraph_integer_t no_of_nodes, igraph_integer_t k,
+                                  igraph_bool_t directed, igraph_bool_t multiple);
+
+DECLDIR int igraph_sbm_game(igraph_t *graph, igraph_integer_t n,
+                            const igraph_matrix_t *pref_matrix,
+                            const igraph_vector_int_t *block_sizes,
+                            igraph_bool_t directed, igraph_bool_t loops);
+
+DECLDIR int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n,
+                             igraph_integer_t m, const igraph_vector_t *rho,
+                             const igraph_matrix_t *C, igraph_real_t p);
+
+DECLDIR int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n,
+                                  const igraph_vector_int_t *mlist,
+                                  const igraph_vector_ptr_t *rholist,
+                                  const igraph_vector_ptr_t *Clist,
+                                  igraph_real_t p);
+
+DECLDIR int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph,
+                                   igraph_real_t corr, igraph_real_t p,
+                                   const igraph_vector_t *permutation);
+
+DECLDIR int igraph_correlated_pair_game(igraph_t *graph1, igraph_t *graph2,
+                                        int n, igraph_real_t corr, igraph_real_t p,
+                                        igraph_bool_t directed,
+                                        const igraph_vector_t *permutation);
+
+DECLDIR int igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed,
+                             igraph_random_tree_t method);
+
+DECLDIR int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs,
+                                    igraph_bool_t directed);
+
+DECLDIR int igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n,
+        igraph_real_t radius,
+        igraph_bool_t positive,
+        igraph_matrix_t *res);
+
+DECLDIR int igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n,
+                                        igraph_real_t radius,
+                                        igraph_bool_t positive,
+                                        igraph_matrix_t *res);
+
+DECLDIR int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha,
+                                    igraph_matrix_t *res);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_glpk_support.h b/igraph/include/igraph_glpk_support.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_glpk_support.h
@@ -0,0 +1,48 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_GLPK_SUPPORT_H
+#define IGRAPH_GLPK_SUPPORT_H
+
+#include "config.h"
+
+/* Note: only files calling the GLPK routines directly need to
+   include this header.
+*/
+
+#ifdef HAVE_GLPK
+
+#include <glpk.h>
+
+int igraph_i_glpk_check(int retval, const char* message);
+void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info);
+#define IGRAPH_GLPK_CHECK(func, message) do {\
+        int igraph_i_ret = igraph_i_glpk_check(func, message); \
+        if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\
+            return igraph_i_ret; \
+        } } while (0)
+
+#endif
+
+#endif
diff --git a/igraph/include/igraph_gml_tree.h b/igraph/include/igraph_gml_tree.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_gml_tree.h
@@ -0,0 +1,91 @@
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef REST_GML_TREE_H
+#define REST_GML_TREE_H
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+typedef enum { IGRAPH_I_GML_TREE_TREE = 0,
+               IGRAPH_I_GML_TREE_INTEGER,
+               IGRAPH_I_GML_TREE_REAL,
+               IGRAPH_I_GML_TREE_STRING,
+               IGRAPH_I_GML_TREE_DELETED
+             } igraph_i_gml_tree_type_t;
+
+typedef struct igraph_gml_tree_t {
+    igraph_vector_ptr_t names;
+    igraph_vector_char_t types;
+    igraph_vector_ptr_t children;
+} igraph_gml_tree_t;
+
+int igraph_gml_tree_init_integer(igraph_gml_tree_t *t,
+                                 const char *name, int namelen,
+                                 igraph_integer_t value);
+int igraph_gml_tree_init_real(igraph_gml_tree_t *t,
+                              const char *name, int namelen,
+                              igraph_real_t value);
+int igraph_gml_tree_init_string(igraph_gml_tree_t *t,
+                                const char *name, int namelen,
+                                const char *value, int valuelen);
+int igraph_gml_tree_init_tree(igraph_gml_tree_t *t,
+                              const char *name, int namelen,
+                              igraph_gml_tree_t *value);
+void igraph_gml_tree_destroy(igraph_gml_tree_t *t);
+
+void igraph_gml_tree_delete(igraph_gml_tree_t *t, long int pos);
+int igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2);
+
+long int igraph_gml_tree_length(const igraph_gml_tree_t *t);
+long int igraph_gml_tree_find(const igraph_gml_tree_t *t,
+                              const char *name, long int from);
+long int igraph_gml_tree_findback(const igraph_gml_tree_t *t,
+                                  const char *name, long int from);
+int igraph_gml_tree_type(const igraph_gml_tree_t *t, long int pos);
+const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, long int pos);
+igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t,
+        long int pos);
+igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t,
+                                       long int pos);
+const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t,
+                                       long int pos);
+
+igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t,
+        long int pos);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_graphlets.h b/igraph/include/igraph_graphlets.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_graphlets.h
@@ -0,0 +1,52 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_GRAPHLETS_H
+#define IGRAPH_GRAPHLETS_H
+
+#include "igraph_decls.h"
+#include "igraph_datatype.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_interface.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_graphlets_candidate_basis(const igraph_t *graph,
+        const igraph_vector_t *weights,
+        igraph_vector_ptr_t *cliques,
+        igraph_vector_t *thresholds);
+
+DECLDIR int igraph_graphlets_project(const igraph_t *graph,
+                                     const igraph_vector_t *weights,
+                                     const igraph_vector_ptr_t *cliques,
+                                     igraph_vector_t *Mu, igraph_bool_t startMu,
+                                     int niter);
+
+DECLDIR int igraph_graphlets(const igraph_t *graph,
+                             const igraph_vector_t *weights,
+                             igraph_vector_ptr_t *cliques,
+                             igraph_vector_t *Mu, int niter);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_hacks_internal.h b/igraph/include/igraph_hacks_internal.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_hacks_internal.h
@@ -0,0 +1,57 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_HACKS_INTERNAL_H
+#define IGRAPH_HACKS_INTERNAL_H
+
+#include "config.h"
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+__BEGIN_DECLS
+
+#ifndef HAVE_STRDUP
+    #define strdup igraph_i_strdup
+    char* igraph_i_strdup(const char *s);
+#endif
+
+#ifndef HAVE_STPCPY
+    #define stpcpy igraph_i_stpcpy
+    char* igraph_i_stpcpy(char* s1, const char* s2);
+#else
+    #ifndef HAVE_STPCPY_SIGNATURE
+        char* stpcpy(char* s1, const char* s2);
+    #endif
+#endif
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_heap.h b/igraph/include/igraph_heap.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_heap.h
@@ -0,0 +1,83 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_HEAP_H
+#define IGRAPH_HEAP_H
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Heap                                               */
+/* -------------------------------------------------- */
+
+/**
+ * Heap data type.
+ * \ingroup internal
+ */
+
+#define BASE_IGRAPH_REAL
+#define HEAP_TYPE_MAX
+#include "igraph_pmt.h"
+#include "igraph_heap_pmt.h"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MAX
+#define HEAP_TYPE_MIN
+#include "igraph_pmt.h"
+#include "igraph_heap_pmt.h"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MIN
+#undef BASE_IGRAPH_REAL
+
+#define BASE_LONG
+#define HEAP_TYPE_MAX
+#include "igraph_pmt.h"
+#include "igraph_heap_pmt.h"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MAX
+#define HEAP_TYPE_MIN
+#include "igraph_pmt.h"
+#include "igraph_heap_pmt.h"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MIN
+#undef BASE_LONG
+
+#define BASE_CHAR
+#define HEAP_TYPE_MAX
+#include "igraph_pmt.h"
+#include "igraph_heap_pmt.h"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MAX
+#define HEAP_TYPE_MIN
+#include "igraph_pmt.h"
+#include "igraph_heap_pmt.h"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MIN
+#undef BASE_CHAR
+
+#define IGRAPH_HEAP_NULL { 0,0,0 }
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_heap_pmt.h b/igraph/include/igraph_heap_pmt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_heap_pmt.h
@@ -0,0 +1,45 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+typedef struct TYPE(igraph_heap) {
+    BASE* stor_begin;
+    BASE* stor_end;
+    BASE* end;
+    int destroy;
+} TYPE(igraph_heap);
+
+DECLDIR int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int size);
+DECLDIR int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *t, BASE* data, long int len);
+DECLDIR void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h);
+DECLDIR igraph_bool_t FUNCTION(igraph_heap, empty)(TYPE(igraph_heap)* h);
+DECLDIR int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem);
+DECLDIR BASE FUNCTION(igraph_heap, top)(TYPE(igraph_heap)* h);
+DECLDIR BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h);
+DECLDIR long int FUNCTION(igraph_heap, size)(TYPE(igraph_heap)* h);
+DECLDIR int FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, long int size);
+
+void FUNCTION(igraph_heap, i_build)(BASE* arr, long int size, long int head);
+void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, long int size, long int elem);
+void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head);
+void FUNCTION(igraph_heap, i_switch)(BASE* arr, long int e1, long int e2);
+
diff --git a/igraph/include/igraph_hrg.h b/igraph/include/igraph_hrg.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_hrg.h
@@ -0,0 +1,114 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_HRG_H
+#define IGRAPH_HRG_H
+
+#include "igraph_decls.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_datatype.h"
+
+__BEGIN_DECLS
+
+/**
+ * \struct igraph_hrg_t
+ * Data structure to store a hierarchical random graph
+ *
+ * A hierarchical random graph (HRG) can be given as a binary tree,
+ * where the internal vertices are labeled with real numbers.
+ *
+ * </para><para>Note that you don't necessarily have to know this
+ * internal representation for using the HRG functions, just pass the
+ * HRG objects created by one igraph function, to another igraph
+ * function.
+ *
+ * </para><para>
+ * It has the following members:
+ * \member left Vector that contains the left children of the internal
+ *    tree vertices. The first vertex is always the root vertex, so
+ *    the first element of the vector is the left child of the root
+ *    vertex. Internal vertices are denoted with negative numbers,
+ *    starting from -1 and going down, i.e. the root vertex is
+ *    -1. Leaf vertices are denoted by non-negative number, starting
+ *    from zero and up.
+ * \member right Vector that contains the right children of the
+ *    vertices, with the same encoding as the \c left vector.
+ * \member prob The connection probabilities attached to the internal
+ *    vertices, the first number belongs to the root vertex
+ *    (i.e. internal vertex -1), the second to internal vertex -2,
+ *    etc.
+ * \member edges The number of edges in the subtree below the given
+ *    internal vertex.
+ * \member vertices The number of vertices in the subtree below the
+ *    given internal vertex, including itself.
+ */
+
+typedef struct igraph_hrg_t {
+    igraph_vector_t left, right, prob, edges, vertices;
+} igraph_hrg_t;
+
+DECLDIR int igraph_hrg_init(igraph_hrg_t *hrg, int n);
+DECLDIR void igraph_hrg_destroy(igraph_hrg_t *hrg);
+DECLDIR int igraph_hrg_size(const igraph_hrg_t *hrg);
+DECLDIR int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize);
+
+DECLDIR int igraph_hrg_fit(const igraph_t *graph,
+                           igraph_hrg_t *hrg,
+                           igraph_bool_t start,
+                           int steps);
+
+DECLDIR int igraph_hrg_sample(const igraph_t *graph,
+                              igraph_t *sample,
+                              igraph_vector_ptr_t *samples,
+                              igraph_hrg_t *hrg,
+                              igraph_bool_t start);
+
+DECLDIR int igraph_hrg_game(igraph_t *graph,
+                            const igraph_hrg_t *hrg);
+
+DECLDIR int igraph_hrg_dendrogram(igraph_t *graph,
+                                  const igraph_hrg_t *hrg);
+
+DECLDIR int igraph_hrg_consensus(const igraph_t *graph,
+                                 igraph_vector_t *parents,
+                                 igraph_vector_t *weights,
+                                 igraph_hrg_t *hrg,
+                                 igraph_bool_t start,
+                                 int num_samples);
+
+DECLDIR int igraph_hrg_predict(const igraph_t *graph,
+                               igraph_vector_t *edges,
+                               igraph_vector_t *prob,
+                               igraph_hrg_t *hrg,
+                               igraph_bool_t start,
+                               int num_samples,
+                               int num_bins);
+
+DECLDIR int igraph_hrg_create(igraph_hrg_t *hrg,
+                              const igraph_t *graph,
+                              const igraph_vector_t *prob);
+
+__END_DECLS
+
+#endif  /* IGRAPH_HRG_H */
diff --git a/igraph/include/igraph_interface.h b/igraph/include/igraph_interface.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_interface.h
@@ -0,0 +1,86 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_INTERFACE_H
+#define IGRAPH_INTERFACE_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Interface                                          */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed);
+DECLDIR int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr);
+DECLDIR void igraph_destroy(igraph_t *graph);
+DECLDIR int igraph_copy(igraph_t *to, const igraph_t *from);
+DECLDIR int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges,
+                             void *attr);
+DECLDIR int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv,
+                                void *attr);
+DECLDIR int igraph_delete_edges(igraph_t *graph, igraph_es_t edges);
+DECLDIR int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices);
+DECLDIR int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices,
+                                       igraph_vector_t *idx,
+                                       igraph_vector_t *invidx);
+DECLDIR igraph_integer_t igraph_vcount(const igraph_t *graph);
+DECLDIR igraph_integer_t igraph_ecount(const igraph_t *graph);
+DECLDIR int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t vid,
+                             igraph_neimode_t mode);
+DECLDIR igraph_bool_t igraph_is_directed(const igraph_t *graph);
+DECLDIR int igraph_degree(const igraph_t *graph, igraph_vector_t *res,
+                          const igraph_vs_t vids, igraph_neimode_t mode,
+                          igraph_bool_t loops);
+DECLDIR int igraph_edge(const igraph_t *graph, igraph_integer_t eid,
+                        igraph_integer_t *from, igraph_integer_t *to);
+DECLDIR int igraph_edges(const igraph_t *graph, igraph_es_t eids,
+                         igraph_vector_t *edges);
+DECLDIR int igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid,
+                           igraph_integer_t from, igraph_integer_t to,
+                           igraph_bool_t directed, igraph_bool_t error);
+DECLDIR int igraph_get_eids(const igraph_t *graph, igraph_vector_t *eids,
+                            const igraph_vector_t *pairs,
+                            const igraph_vector_t *path,
+                            igraph_bool_t directed, igraph_bool_t error);
+DECLDIR int igraph_get_eids_multi(const igraph_t *graph, igraph_vector_t *eids,
+                                  const igraph_vector_t *pairs,
+                                  const igraph_vector_t *path,
+                                  igraph_bool_t directed, igraph_bool_t error);
+DECLDIR int igraph_adjacent(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t vid,
+                            igraph_neimode_t mode);          /* deprecated */
+DECLDIR int igraph_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t vid,
+                            igraph_neimode_t mode);
+
+#define IGRAPH_FROM(g,e) ((igraph_integer_t)(VECTOR((g)->from)[(long int)(e)]))
+#define IGRAPH_TO(g,e)   ((igraph_integer_t)(VECTOR((g)->to)  [(long int)(e)]))
+#define IGRAPH_OTHER(g,e,v) \
+    ((igraph_integer_t)(IGRAPH_TO(g,(e))==(v) ? IGRAPH_FROM((g),(e)) : IGRAPH_TO((g),(e))))
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_interrupt.h b/igraph/include/igraph_interrupt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_interrupt.h
@@ -0,0 +1,128 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_INTERRUPT_H
+#define IGRAPH_INTERRUPT_H
+
+#include "igraph_error.h"
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/* This file contains the igraph interruption handling. */
+
+/**
+ * \section interrupthandlers Interruption handlers
+ *
+ * <para>
+ * \a igraph is designed to be embeddable into several higher level
+ * languages (R and Python interfaces are included in the original
+ * package). Since most higher level languages consider internal \a igraph
+ * calls as atomic, interruption requests (like Ctrl-C in Python) must
+ * be handled differently depending on the environment \a igraph embeds
+ * into.</para>
+ * <para>
+ * An \emb interruption handler \eme is a function which is called regularly
+ * by \a igraph during long calculations. A typical usage of the interruption
+ * handler is to check whether the user tried to interrupt the calculation
+ * and return an appropriate value to signal this condition. For example,
+ * in R, one must call an internal R function regularly to check for
+ * interruption requests, and the \a igraph interruption handler is the
+ * perfect place to do that.</para>
+ * <para>
+ * If you are using the plain C interface of \a igraph or if you are
+ * allowed to replace the operating system's interruption handler (like
+ * SIGINT in Un*x systems), these calls are not of much use to you.</para>
+ * <para>
+ * The default interruption handler is empty.
+ * The \ref igraph_set_interruption_handler() function can be used to set a
+ * new interruption handler function of type
+ * \ref igraph_interruption_handler_t, see the
+ * documentation of this type for details.
+ * </para>
+ */
+
+/**
+ * \section writing_interruption_handlers Writing interruption handlers
+ *
+ * <para>
+ * You can write and install interruption handlers simply by defining a
+ * function of type \ref igraph_interruption_handler_t and calling
+ * \ref igraph_set_interruption_handler(). This feature is useful for
+ * interface writers, because usually this is the only way to allow handling
+ * of Ctrl-C and similar keypresses properly.
+ * </para>
+ * <para>
+ * Your interruption handler will be called regularly during long operations
+ * (so it is not guaranteed to be called during operations which tend to be
+ * short, like adding single edges). An interruption handler accepts no
+ * parameters and must return \c IGRAPH_SUCCESS if the calculation should go on. All
+ * other return values are considered to be a request for interruption,
+ * and the caller function would return a special error code, \c IGRAPH_INTERRUPTED.
+ * It is up to your error handler function to handle this error properly.
+ * </para>
+ */
+
+/**
+ * \section writing_functions_interruption_handling Writing \a igraph functions with
+ * proper interruption handling
+ *
+ * <para>
+ * There is practically a simple rule that should be obeyed when writing
+ * \a igraph functions. If the calculation is expected to take a long time
+ * in large graphs (a simple rule of thumb is to assume this for every
+ * function with a time complexity of at least O(n^2)), call
+ * \ref IGRAPH_ALLOW_INTERRUPTION in regular intervals like every 10th
+ * iteration or so.
+ * </para>
+ */
+
+/**
+ * \typedef igraph_interruption_handler_t
+ *
+ * This is the type of the interruption handler functions.
+ *
+ * \param data reserved for possible future use
+ * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise.
+ */
+
+typedef int igraph_interruption_handler_t (void* data);
+
+/**
+ * \function igraph_allow_interruption
+ *
+ * This is the function which is called (usually via the
+ * \ref IGRAPH_INTERRUPTION macro) if \a igraph is checking for interruption
+ * requests.
+ *
+ * \param data reserved for possible future use, now it is always \c NULL
+ * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise.
+ */
+
+DECLDIR int igraph_allow_interruption(void* data);
+
+DECLDIR igraph_interruption_handler_t * igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_interrupt_internal.h b/igraph/include/igraph_interrupt_internal.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_interrupt_internal.h
@@ -0,0 +1,69 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_INTERRUPT_INTERNAL_H
+#define IGRAPH_INTERRUPT_INTERNAL_H
+
+#include "config.h"
+#include "igraph_interrupt.h"
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+__BEGIN_DECLS
+
+extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t
+*igraph_i_interruption_handler;
+
+/**
+ * \define IGRAPH_ALLOW_INTERRUPTION
+ * \brief
+ *
+ * This macro should be called when interruption is allowed.  It calls
+ * \ref igraph_allow_interruption() with the proper parameters and if that returns
+ * anything but \c IGRAPH_SUCCESS then
+ * the macro returns the "calling" function as well, with the proper
+ * error code (\c IGRAPH_INTERRUPTED).
+ */
+
+#define IGRAPH_ALLOW_INTERRUPTION() \
+    do { \
+        if (igraph_i_interruption_handler) { if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) return IGRAPH_INTERRUPTED; \
+        } } while (0)
+
+#define IGRAPH_ALLOW_INTERRUPTION_NORETURN() \
+    do { \
+        if (igraph_i_interruption_handler) { igraph_allow_interruption(NULL); } \
+    } while (0)
+
+__END_DECLS
+
+#endif
+
diff --git a/igraph/include/igraph_iterators.h b/igraph/include/igraph_iterators.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_iterators.h
@@ -0,0 +1,401 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_ITERATORS_H
+#define IGRAPH_ITERATORS_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Vertex selectors                                   */
+/* -------------------------------------------------- */
+
+#define IGRAPH_VS_ALL       0
+#define IGRAPH_VS_ADJ       1
+#define IGRAPH_VS_NONE      2
+#define IGRAPH_VS_1         3
+#define IGRAPH_VS_VECTORPTR 4
+#define IGRAPH_VS_VECTOR    5
+#define IGRAPH_VS_SEQ       6
+#define IGRAPH_VS_NONADJ    7
+
+typedef struct igraph_vs_t {
+    int type;
+    union {
+        igraph_integer_t vid;               /* single vertex  */
+        const igraph_vector_t *vecptr;      /* vector of vertices  */
+        struct {
+            igraph_integer_t vid;
+            igraph_neimode_t mode;
+        } adj;                  /* adjacent vertices  */
+        struct {
+            igraph_integer_t from;
+            igraph_integer_t to;
+        } seq;                              /* sequence of vertices from:to */
+    } data;
+} igraph_vs_t;
+
+DECLDIR int igraph_vs_all(igraph_vs_t *vs);
+DECLDIR igraph_vs_t igraph_vss_all(void);
+
+DECLDIR int igraph_vs_adj(igraph_vs_t *vs,
+                          igraph_integer_t vid, igraph_neimode_t mode);
+DECLDIR igraph_vs_t igraph_vss_adj(igraph_integer_t vid, igraph_neimode_t mode);
+
+DECLDIR int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid,
+                             igraph_neimode_t mode);
+
+DECLDIR int igraph_vs_none(igraph_vs_t *vs);
+DECLDIR igraph_vs_t igraph_vss_none(void);
+
+DECLDIR int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid);
+DECLDIR igraph_vs_t igraph_vss_1(igraph_integer_t vid);
+
+DECLDIR int igraph_vs_vector(igraph_vs_t *vs,
+                             const igraph_vector_t *v);
+DECLDIR igraph_vs_t igraph_vss_vector(const igraph_vector_t *v);
+
+DECLDIR int igraph_vs_vector_small(igraph_vs_t *vs, ...);
+
+DECLDIR int igraph_vs_vector_copy(igraph_vs_t *vs,
+                                  const igraph_vector_t *v);
+
+DECLDIR int igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to);
+DECLDIR igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to);
+
+DECLDIR void igraph_vs_destroy(igraph_vs_t *vs);
+
+DECLDIR igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs);
+
+DECLDIR int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src);
+
+DECLDIR int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs,
+                                igraph_vector_t *v);
+DECLDIR int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs,
+                           igraph_integer_t *result);
+DECLDIR int igraph_vs_type(const igraph_vs_t *vs);
+
+/* -------------------------------------------------- */
+/* Vertex iterators                                   */
+/* -------------------------------------------------- */
+
+#define IGRAPH_VIT_SEQ       0
+#define IGRAPH_VIT_VECTOR    1
+#define IGRAPH_VIT_VECTORPTR 2
+
+typedef struct igraph_vit_t {
+    int type;
+    long int pos;
+    long int start;
+    long int end;
+    const igraph_vector_t *vec;
+} igraph_vit_t;
+
+/**
+ * \section IGRAPH_VIT Stepping over the vertices
+ *
+ * <para>After creating an iterator with \ref igraph_vit_create(), it
+ * points to the first vertex in the vertex determined by the vertex
+ * selector (if there is any). The \ref IGRAPH_VIT_NEXT() macro steps
+ * to the next vertex, \ref IGRAPH_VIT_END() checks whether there are
+ * more vertices to visit, \ref IGRAPH_VIT_SIZE() gives the total size
+ * of the vertices visited so far and to be visited. \ref
+ * IGRAPH_VIT_RESET() resets the iterator, it will point to the first
+ * vertex again. Finally \ref IGRAPH_VIT_GET() gives the current vertex
+ * pointed to by the iterator (call this only if \ref IGRAPH_VIT_END()
+ * is false).
+ * </para>
+ * <para>
+ * Here is an example on how to step over the neighbors of vertex 0:
+ * <informalexample><programlisting>
+ * igraph_vs_t vs;
+ * igraph_vit_t vit;
+ * ...
+ * igraph_vs_adj(&amp;vs, 0, IGRAPH_ALL);
+ * igraph_vit_create(&amp;graph, vs, &amp;vit);
+ * while (!IGRAPH_VIT_END(vit)) {
+ *   printf(" %li", (long int) IGRAPH_VIT_GET(vit));
+ *   IGRAPH_VIT_NEXT(vit);
+ * }
+ * printf("\n");
+ * ...
+ * igraph_vit_destroy(&amp;vit);
+ * igraph_vs_destroy(&amp;vs);
+ * </programlisting></informalexample>
+ * </para>
+ */
+
+/**
+ * \define IGRAPH_VIT_NEXT
+ * \brief Next vertex.
+ *
+ * Steps the iterator to the next vertex. Only call this function if
+ * \ref IGRAPH_VIT_END() returns false.
+ * \param vit The vertex iterator to step.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_VIT_NEXT(vit)  (++((vit).pos))
+/**
+ * \define IGRAPH_VIT_END
+ * \brief Are we at the end?
+ *
+ * Checks whether there are more vertices to step to.
+ * \param vit The vertex iterator to check.
+ * \return Logical value, if true there are no more vertices to step
+ * to.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_VIT_END(vit)   ((vit).pos >= (vit).end)
+/**
+ * \define IGRAPH_VIT_SIZE
+ * \brief Size of a vertex iterator.
+ *
+ * Gives the number of vertices in a vertex iterator.
+ * \param vit The vertex iterator.
+ * \return The number of vertices.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_VIT_SIZE(vit)  ((vit).end - (vit).start)
+/**
+ * \define IGRAPH_VIT_RESET
+ * \brief Reset a vertex iterator.
+ *
+ * Resets a vertex iterator. After calling this macro the iterator
+ * will point to the first vertex.
+ * \param vit The vertex iterator.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_VIT_RESET(vit) ((vit).pos = (vit).start)
+/**
+ * \define IGRAPH_VIT_GET
+ * \brief Query the current position.
+ *
+ * Gives the vertex id of the current vertex pointed to by the
+ * iterator.
+ * \param vit The vertex iterator.
+ * \return The vertex id of the current vertex.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_VIT_GET(vit)  \
+    ((igraph_integer_t)(((vit).type == IGRAPH_VIT_SEQ) ? (vit).pos : \
+                        VECTOR(*(vit).vec)[(vit).pos]))
+
+DECLDIR int igraph_vit_create(const igraph_t *graph,
+                              igraph_vs_t vs, igraph_vit_t *vit);
+DECLDIR void igraph_vit_destroy(const igraph_vit_t *vit);
+
+DECLDIR int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v);
+
+/* -------------------------------------------------- */
+/* Edge Selectors                                     */
+/* -------------------------------------------------- */
+
+#define IGRAPH_ES_ALL       0
+#define IGRAPH_ES_ALLFROM   1
+#define IGRAPH_ES_ALLTO     2
+#define IGRAPH_ES_INCIDENT  3
+#define IGRAPH_ES_NONE      4
+#define IGRAPH_ES_1         5
+#define IGRAPH_ES_VECTORPTR 6
+#define IGRAPH_ES_VECTOR    7
+#define IGRAPH_ES_SEQ       8
+#define IGRAPH_ES_PAIRS     9
+#define IGRAPH_ES_PATH      10
+#define IGRAPH_ES_MULTIPAIRS 11
+
+typedef struct igraph_es_t {
+    int type;
+    union {
+        igraph_integer_t vid;
+        igraph_integer_t eid;
+        const igraph_vector_t *vecptr;
+        struct {
+            igraph_integer_t vid;
+            igraph_neimode_t mode;
+        } incident;
+        struct {
+            igraph_integer_t from;
+            igraph_integer_t to;
+        } seq;
+        struct {
+            const igraph_vector_t *ptr;
+            igraph_bool_t mode;
+        } path;
+    } data;
+} igraph_es_t;
+
+DECLDIR int igraph_es_all(igraph_es_t *es,
+                          igraph_edgeorder_type_t order);
+DECLDIR igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order);
+
+DECLDIR int igraph_es_adj(igraph_es_t *es,
+                          igraph_integer_t vid, igraph_neimode_t mode);     /* deprecated */
+DECLDIR int igraph_es_incident(igraph_es_t *es,
+                               igraph_integer_t vid, igraph_neimode_t mode);
+
+DECLDIR int igraph_es_none(igraph_es_t *es);
+DECLDIR igraph_es_t igraph_ess_none(void);
+
+DECLDIR int igraph_es_1(igraph_es_t *es, igraph_integer_t eid);
+DECLDIR igraph_es_t igraph_ess_1(igraph_integer_t eid);
+
+DECLDIR int igraph_es_vector(igraph_es_t *es,
+                             const igraph_vector_t *v);
+DECLDIR igraph_es_t igraph_ess_vector(const igraph_vector_t *v);
+
+DECLDIR int igraph_es_fromto(igraph_es_t *es,
+                             igraph_vs_t from, igraph_vs_t to);
+
+DECLDIR int igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to);
+DECLDIR igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to);
+
+DECLDIR int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v);
+
+DECLDIR int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v,
+                            igraph_bool_t directed);
+DECLDIR int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...);
+
+DECLDIR int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v,
+                                 igraph_bool_t directed);
+
+DECLDIR int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v,
+                           igraph_bool_t directed);
+DECLDIR int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...);
+
+DECLDIR void igraph_es_destroy(igraph_es_t *es);
+
+DECLDIR igraph_bool_t igraph_es_is_all(const igraph_es_t *es);
+
+DECLDIR int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src);
+
+DECLDIR int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es,
+                                igraph_vector_t *v);
+DECLDIR int igraph_es_size(const igraph_t *graph, const igraph_es_t *es,
+                           igraph_integer_t *result);
+DECLDIR int igraph_es_type(const igraph_es_t *es);
+
+
+/* -------------------------------------------------- */
+/* Edge Iterators                                     */
+/* -------------------------------------------------- */
+
+#define IGRAPH_EIT_SEQ       0
+#define IGRAPH_EIT_VECTOR    1
+#define IGRAPH_EIT_VECTORPTR 2
+
+typedef struct igraph_eit_t {
+    int type;
+    long int pos;
+    long int start;
+    long int end;
+    const igraph_vector_t *vec;
+} igraph_eit_t;
+
+/**
+ * \section IGRAPH_EIT Stepping over the edges
+ *
+ * <para>Just like for vertex iterators, macros are provided for
+ * stepping over a sequence of edges: \ref IGRAPH_EIT_NEXT() goes to
+ * the next edge, \ref IGRAPH_EIT_END() checks whether there are more
+ * edges to visit, \ref IGRAPH_EIT_SIZE() gives the number of edges in
+ * the edge sequence, \ref IGRAPH_EIT_RESET() resets the iterator to
+ * the first edge and \ref IGRAPH_EIT_GET() returns the id of the
+ * current edge.</para>
+ */
+
+/**
+ * \define IGRAPH_EIT_NEXT
+ * \brief Next edge.
+ *
+ * Steps the iterator to the next edge. Call this function only if
+ * \ref IGRAPH_EIT_END() returns false.
+ * \param eit The edge iterator to step.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_EIT_NEXT(eit) (++((eit).pos))
+/**
+ * \define IGRAPH_EIT_END
+ * \brief Are we at the end?
+ *
+ * Checks whether there are more edges to step to.
+ * \param wit The edge iterator to check.
+ * \return Logical value, if true there are no more edges
+ * to step to.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_EIT_END(eit)   ((eit).pos >= (eit).end)
+/**
+ * \define IGRAPH_EIT_SIZE
+ * \brief Number of edges in the iterator.
+ *
+ * Gives the number of edges in an edge iterator.
+ * \param eit The edge iterator.
+ * \return The number of edges.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_EIT_SIZE(eit)  ((eit).end - (eit).start)
+/**
+ * \define IGRAPH_EIT_RESET
+ * \brief Reset an edge iterator.
+ *
+ * Resets an edge iterator. After calling this macro the iterator will
+ * point to the first edge.
+ * \param eit The edge iterator.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_EIT_RESET(eit) ((eit).pos = (eit).start)
+/**
+ * \define IGRAPH_EIT_GET
+ * \brief Query an edge iterator.
+ *
+ * Gives the edge id of the current edge pointed to by an iterator.
+ * \param eit The edge iterator.
+ * \return The id of the current edge.
+ *
+ * Time complexity: O(1).
+ */
+#define IGRAPH_EIT_GET(eit)  \
+    (igraph_integer_t)((((eit).type == IGRAPH_EIT_SEQ) ? (eit).pos : \
+                        VECTOR(*(eit).vec)[(eit).pos]))
+
+DECLDIR int igraph_eit_create(const igraph_t *graph,
+                              igraph_es_t es, igraph_eit_t *eit);
+DECLDIR void igraph_eit_destroy(const igraph_eit_t *eit);
+
+DECLDIR int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_lapack.h b/igraph/include/igraph_lapack.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_lapack.h
@@ -0,0 +1,114 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_LAPACK_H
+#define IGRAPH_LAPACK_H
+
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/**
+ * \section about_lapack LAPACK interface in igraph
+ *
+ * <para>
+ * LAPACK is written in Fortran90 and provides routines for solving
+ * systems of simultaneous linear equations, least-squares solutions
+ * of linear systems of equations, eigenvalue problems, and singular
+ * value problems. The associated matrix factorizations (LU, Cholesky,
+ * QR, SVD, Schur, generalized Schur) are also provided, as are
+ * related computations such as reordering of the Schur factorizations
+ * and estimating condition numbers. Dense and banded matrices are
+ * handled, but not general sparse matrices. In all areas, similar
+ * functionality is provided for real and complex matrices, in both
+ * single and double precision.
+ * </para>
+ *
+ * <para>
+ * igraph provides an interface to a very limited set of LAPACK
+ * functions, using the regular igraph data structures.
+ * </para>
+ *
+ * <para>
+ * See more about LAPACK at http://www.netlib.org/lapack/
+ * </para>
+ */
+
+DECLDIR int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv,
+                                 int *info);
+DECLDIR int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a,
+                                 igraph_vector_int_t *ipiv, igraph_matrix_t *b);
+DECLDIR int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv,
+                                igraph_matrix_t *b, int *info);
+
+typedef enum { IGRAPH_LAPACK_DSYEV_ALL,
+               IGRAPH_LAPACK_DSYEV_INTERVAL,
+               IGRAPH_LAPACK_DSYEV_SELECT
+             } igraph_lapack_dsyev_which_t;
+
+DECLDIR int igraph_lapack_dsyevr(const igraph_matrix_t *A,
+                                 igraph_lapack_dsyev_which_t which,
+                                 igraph_real_t vl, igraph_real_t vu, int vestimate,
+                                 int il, int iu, igraph_real_t abstol,
+                                 igraph_vector_t *values, igraph_matrix_t *vectors,
+                                 igraph_vector_int_t *support);
+
+/* TODO: should we use complex vectors/matrices? */
+
+DECLDIR int igraph_lapack_dgeev(const igraph_matrix_t *A,
+                                igraph_vector_t *valuesreal,
+                                igraph_vector_t *valuesimag,
+                                igraph_matrix_t *vectorsleft,
+                                igraph_matrix_t *vectorsright, int *info);
+
+typedef enum { IGRAPH_LAPACK_DGEEVX_BALANCE_NONE = 0,
+               IGRAPH_LAPACK_DGEEVX_BALANCE_PERM,
+               IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE,
+               IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH
+             }
+igraph_lapack_dgeevx_balance_t;
+
+DECLDIR int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance,
+                                 const igraph_matrix_t *A,
+                                 igraph_vector_t *valuesreal,
+                                 igraph_vector_t *valuesimag,
+                                 igraph_matrix_t *vectorsleft,
+                                 igraph_matrix_t *vectorsright,
+                                 int *ilo, int *ihi, igraph_vector_t *scale,
+                                 igraph_real_t *abnrm,
+                                 igraph_vector_t *rconde,
+                                 igraph_vector_t *rcondv,
+                                 int *info);
+
+DECLDIR int igraph_lapack_dgehrd(const igraph_matrix_t *A,
+                                 int ilo, int ihi,
+                                 igraph_matrix_t *result);
+
+DECLDIR int igraph_lapack_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2,
+                               igraph_real_t *res);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_lapack_internal.h b/igraph/include/igraph_lapack_internal.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_lapack_internal.h
@@ -0,0 +1,184 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef LAPACK_INTERNAL_H
+#define LAPACK_INTERNAL_H
+
+/* Note: only files calling the LAPACK routines directly need to
+   include this header.
+*/
+
+#include "igraph_types.h"
+#include "config.h"
+
+#ifndef INTERNAL_LAPACK
+    #define igraphdgeevx_   dgeevx_
+    #define igraphdgeev_    dgeev_
+    #define igraphdgebak_   dgebak_
+    #define igraphxerbla_   xerbla_
+    #define igraphdgebal_   dgebal_
+    #define igraphdisnan_   disnan_
+    #define igraphdlaisnan_ dlaisnan_
+    #define igraphdgehrd_   dgehrd_
+    #define igraphdgehd2_   dgehd2_
+    #define igraphdlarf_    dlarf_
+    #define igraphiladlc_   iladlc_
+    #define igraphiladlr_   iladlr_
+    #define igraphdlarfg_   dlarfg_
+    #define igraphdlapy2_   dlapy2_
+    #define igraphdlahr2_   dlahr2_
+    #define igraphdlacpy_   dlacpy_
+    #define igraphdlarfb_   dlarfb_
+    #define igraphilaenv_   ilaenv_
+    #define igraphieeeck_   ieeeck_
+    #define igraphiparmq_   iparmq_
+    #define igraphdhseqr_   dhseqr_
+    #define igraphdlahqr_   dlahqr_
+    #define igraphdlabad_   dlabad_
+    #define igraphdlanv2_   dlanv2_
+    #define igraphdlaqr0_   dlaqr0_
+    #define igraphdlaqr3_   dlaqr3_
+    #define igraphdlaqr4_   dlaqr4_
+    #define igraphdlaqr2_   dlaqr2_
+    #define igraphdlaset_   dlaset_
+    #define igraphdormhr_   dormhr_
+    #define igraphdormqr_   dormqr_
+    #define igraphdlarft_   dlarft_
+    #define igraphdorm2r_   dorm2r_
+    #define igraphdtrexc_   dtrexc_
+    #define igraphdlaexc_   dlaexc_
+    #define igraphdlange_   dlange_
+    #define igraphdlassq_   dlassq_
+    #define igraphdlarfx_   dlarfx_
+    #define igraphdlartg_   dlartg_
+    #define igraphdlasy2_   dlasy2_
+    #define igraphdlaqr5_   dlaqr5_
+    #define igraphdlaqr1_   dlaqr1_
+    #define igraphdlascl_   dlascl_
+    #define igraphdorghr_   dorghr_
+    #define igraphdorgqr_   dorgqr_
+    #define igraphdorg2r_   dorg2r_
+    #define igraphdtrevc_   dtrevc_
+    #define igraphdlaln2_   dlaln2_
+    #define igraphdladiv_   dladiv_
+    #define igraphdsyevr_   dsyevr_
+    #define igraphdsyrk_    dsyrk_
+    #define igraphdlansy_   dlansy_
+    #define igraphdormtr_   dormtr_
+    #define igraphdormql_   dormql_
+    #define igraphdorm2l_   dorm2l_
+    #define igraphdstebz_   dstebz_
+    #define igraphdlaebz_   dlaebz_
+    #define igraphdstein_   dstein_
+    #define igraphdlagtf_   dlagtf_
+    #define igraphdlagts_   dlagts_
+    #define igraphdlarnv_   dlarnv_
+    #define igraphdlaruv_   dlaruv_
+    #define igraphdstemr_   dstemr_
+    #define igraphdlae2_    dlae2_
+    #define igraphdlaev2_   dlaev2_
+    #define igraphdlanst_   dlanst_
+    #define igraphdlarrc_   dlarrc_
+    #define igraphdlarre_   dlarre_
+    #define igraphdlarra_   dlarra_
+    #define igraphdlarrb_   dlarrb_
+    #define igraphdlaneg_   dlaneg_
+    #define igraphdlarrd_   dlarrd_
+    #define igraphdlarrk_   dlarrk_
+    #define igraphdlasq2_   dlasq2_
+    #define igraphdlasq3_   dlasq3_
+    #define igraphdlasq4_   dlasq4_
+    #define igraphdlasq5_   dlasq5_
+    #define igraphdlasq6_   dlasq6_
+    #define igraphdlasrt_   dlasrt_
+    #define igraphdlarrj_   dlarrj_
+    #define igraphdlarrr_   dlarrr_
+    #define igraphdlarrv_   dlarrv_
+    #define igraphdlar1v_   dlar1v_
+    #define igraphdlarrf_   dlarrf_
+    #define igraphdpotrf_   dpotrf_
+    #define igraphdsterf_   dsterf_
+    #define igraphdsytrd_   dsytrd_
+    #define igraphdlatrd_   dlatrd_
+    #define igraphdsytd2_   dsytd2_
+    #define igraphdlanhs_   dlanhs_
+    #define igraphdgeqr2_   dgeqr2_
+    #define igraphdtrsen_   dtrsen_
+    #define igraphdlacn2_   dlacn2_
+    #define igraphdtrsyl_   dtrsyl_
+    #define igraphdlasr_    dlasr_
+    #define igraphdsteqr_   dsteqr_
+    #define igraphdgesv_    dgesv_
+    #define igraphdgetrf_   dgetrf_
+    #define igraphdgetf2_   dgetf2_
+    #define igraphdlaswp_   dlaswp_
+    #define igraphdgetrs_   dgetrs_
+    #define igraphlen_trim_ len_trim_
+    #define igraph_dlamc1_  dlamc1_
+    #define igraph_dlamc2_  dlamc2_
+    #define igraph_dlamc3_  dlamc3_
+    #define igraph_dlamc4_  dlamc4_
+    #define igraph_dlamc5_  dlamc5_
+    #define igraphddot_     ddot_
+#endif
+
+int igraphdgetrf_(int *m, int *n, igraph_real_t *a, int *lda, int *ipiv,
+                  int *info);
+int igraphdgetrs_(char *trans, int *n, int *nrhs, igraph_real_t *a,
+                  int *lda, int *ipiv, igraph_real_t *b, int *ldb,
+                  int *info);
+int igraphdgesv_(int *n, int *nrhs, igraph_real_t *a, int *lda,
+                 int *ipiv, igraph_real_t *b, int *ldb, int *info);
+
+igraph_real_t igraphdlapy2_(igraph_real_t *x, igraph_real_t *y);
+
+int igraphdsyevr_(char *jobz, char *range, char *uplo, int *n,
+                  igraph_real_t *a, int *lda, igraph_real_t *vl,
+                  igraph_real_t *vu, int * il, int *iu,
+                  igraph_real_t *abstol, int *m, igraph_real_t *w,
+                  igraph_real_t *z, int *ldz, int *isuppz,
+                  igraph_real_t *work, int *lwork, int *iwork,
+                  int *liwork, int *info);
+
+int igraphdgeev_(char *jobvl, char *jobvr, int *n, igraph_real_t *a,
+                 int *lda, igraph_real_t *wr, igraph_real_t *wi,
+                 igraph_real_t *vl, int *ldvl, igraph_real_t *vr, int *ldvr,
+                 igraph_real_t *work, int *lwork, int *info);
+
+int igraphdgeevx_(char *balanc, char *jobvl, char *jobvr, char *sense,
+                  int *n, igraph_real_t *a, int *lda, igraph_real_t *wr,
+                  igraph_real_t *wi, igraph_real_t *vl, int *ldvl,
+                  igraph_real_t *vr, int *ldvr, int *ilo, int *ihi,
+                  igraph_real_t *scale, igraph_real_t *abnrm,
+                  igraph_real_t *rconde, igraph_real_t *rcondv,
+                  igraph_real_t *work, int *lwork, int *iwork, int *info);
+
+int igraphdgehrd_(int *n, int *ilo, int *ihi, igraph_real_t *A, int *lda,
+                  igraph_real_t *tau, igraph_real_t *work, int *lwork,
+                  int *info);
+
+igraph_real_t igraphddot_(int *n, igraph_real_t *dx, int *incx,
+                          igraph_real_t *dy, int *incy);
+
+#endif
diff --git a/igraph/include/igraph_layout.h b/igraph/include/igraph_layout.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_layout.h
@@ -0,0 +1,250 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_LAYOUT_H
+#define IGRAPH_LAYOUT_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_matrix.h"
+#include "igraph_datatype.h"
+#include "igraph_arpack.h"
+#include "igraph_iterators.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Layouts                                            */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res);
+DECLDIR int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res,
+                                 igraph_vs_t order);
+DECLDIR int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res,
+                               igraph_integer_t center, const igraph_vector_t *order);
+DECLDIR int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width);
+DECLDIR int igraph_layout_fruchterman_reingold(const igraph_t *graph,
+        igraph_matrix_t *res,
+        igraph_bool_t use_seed,
+        igraph_integer_t niter,
+        igraph_real_t start_temp,
+        igraph_layout_grid_t grid,
+        const igraph_vector_t *weight,
+        const igraph_vector_t *minx,
+        const igraph_vector_t *maxx,
+        const igraph_vector_t *miny,
+        const igraph_vector_t *maxy);
+
+DECLDIR int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res,
+                                       igraph_bool_t use_seed, igraph_integer_t maxiter,
+                                       igraph_real_t epsilon, igraph_real_t kkconst,
+                                       const igraph_vector_t *weights,
+                                       const igraph_vector_t *minx, const igraph_vector_t *maxx,
+                                       const igraph_vector_t *miny, const igraph_vector_t *maxy);
+
+DECLDIR int igraph_layout_springs(const igraph_t *graph, igraph_matrix_t *res,
+                                  igraph_real_t mass, igraph_real_t equil, igraph_real_t k,
+                                  igraph_real_t repeqdis, igraph_real_t kfr, igraph_bool_t repulse);
+DECLDIR int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res,
+                              igraph_integer_t maxiter, igraph_real_t maxdelta,
+                              igraph_real_t area, igraph_real_t coolexp,
+                              igraph_real_t repulserad, igraph_real_t cellsize, igraph_integer_t root);
+DECLDIR int igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res,
+        igraph_neimode_t mode,
+        const igraph_vector_t *roots,
+        const igraph_vector_t *rootlevel);
+DECLDIR int igraph_layout_reingold_tilford_circular(const igraph_t *graph,
+        igraph_matrix_t *res,
+        igraph_neimode_t mode,
+        const igraph_vector_t *roots,
+        const igraph_vector_t *rootlevel);
+DECLDIR int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res,
+                                   igraph_t *extd_graph, igraph_vector_t *extd_to_orig_eids,
+                                   const igraph_vector_t* layers, igraph_real_t hgap,
+                                   igraph_real_t vgap, long int maxiter, const igraph_vector_t *weights);
+
+DECLDIR int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res);
+DECLDIR int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res);
+DECLDIR int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res,
+                                  long int width, long int height);
+DECLDIR int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph,
+        igraph_matrix_t *res,
+        igraph_bool_t use_seed,
+        igraph_integer_t niter,
+        igraph_real_t start_temp,
+        const igraph_vector_t *weight,
+        const igraph_vector_t *minx,
+        const igraph_vector_t *maxx,
+        const igraph_vector_t *miny,
+        const igraph_vector_t *maxy,
+        const igraph_vector_t *minz,
+        const igraph_vector_t *maxz);
+
+DECLDIR int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res,
+        igraph_bool_t use_seed, igraph_integer_t maxiter,
+        igraph_real_t epsilon, igraph_real_t kkconst,
+        const igraph_vector_t *weights,
+        const igraph_vector_t *minx, const igraph_vector_t *maxx,
+        const igraph_vector_t *miny, const igraph_vector_t *maxy,
+        const igraph_vector_t *minz, const igraph_vector_t *maxz);
+
+DECLDIR int igraph_layout_graphopt(const igraph_t *graph,
+                                   igraph_matrix_t *res, igraph_integer_t niter,
+                                   igraph_real_t node_charge, igraph_real_t node_mass,
+                                   igraph_real_t spring_length,
+                                   igraph_real_t spring_constant,
+                                   igraph_real_t max_sa_movement,
+                                   igraph_bool_t use_seed);
+
+DECLDIR int igraph_layout_mds(const igraph_t *graph, igraph_matrix_t *res,
+                              const igraph_matrix_t *dist, long int dim,
+                              igraph_arpack_options_t *options);
+
+DECLDIR int igraph_layout_bipartite(const igraph_t *graph,
+                                    const igraph_vector_bool_t *types,
+                                    igraph_matrix_t *res, igraph_real_t hgap,
+                                    igraph_real_t vgap, long int maxiter);
+
+/**
+ * \struct igraph_layout_drl_options_t
+ * Parameters for the DrL layout generator
+ *
+ * \member edge_cut The edge cutting parameter.
+ *    Edge cutting is done in the late stages of the
+ *    algorithm in order to achieve less dense layouts.  Edges are cut
+ *    if there is a lot of stress on them (a large value in the
+ *    objective function sum).  The edge cutting parameter is a value
+ *    between 0 and 1 with 0 representing no edge cutting and 1
+ *    representing maximal edge cutting. The default value is 32/40.
+ * \member init_iterations Number of iterations, initial phase.
+ * \member init_temperature Start temperature, initial phase.
+ * \member init_attraction Attraction, initial phase.
+ * \member init_damping_mult Damping factor, initial phase.
+ * \member liquid_iterations Number of iterations in the liquid phase.
+ * \member liquid_temperature Start temperature in the liquid phase.
+ * \member liquid_attraction Attraction in the liquid phase.
+ * \member liquid_damping_mult Multiplicatie damping factor, liquid phase.
+ * \member expansion_iterations Number of iterations in the expansion phase.
+ * \member expansion_temperature Start temperature in the expansion phase.
+ * \member expansion_attraction Attraction, expansion phase.
+ * \member expansion_damping_mult Damping factor, expansion phase.
+ * \member cooldown_iterations Number of iterations in the cooldown phase.
+ * \member cooldown_temperature Start temperature in the cooldown phase.
+ * \member cooldown_attraction Attraction in the cooldown phase.
+ * \member cooldown_damping_mult Damping fact int the cooldown phase.
+ * \member crunch_iterations Number of iterations in the crunch phase.
+ * \member crunch_temperature Start temperature in the crunch phase.
+ * \member crunch_attraction Attraction in the crunch phase.
+ * \member crunch_damping_mult Damping factor in the crunch phase.
+ * \member simmer_iterations Number of iterations in the simmer phase.
+ * \member simmer_temperature Start temperature in te simmer phase.
+ * \member simmer_attraction Attraction in the simmer phase.
+ * \member simmer_damping_mult Multiplicative damping factor in the simmer phase.
+ */
+
+typedef struct igraph_layout_drl_options_t {
+    igraph_real_t    edge_cut;
+    igraph_integer_t init_iterations;
+    igraph_real_t    init_temperature;
+    igraph_real_t    init_attraction;
+    igraph_real_t    init_damping_mult;
+    igraph_integer_t liquid_iterations;
+    igraph_real_t    liquid_temperature;
+    igraph_real_t    liquid_attraction;
+    igraph_real_t    liquid_damping_mult;
+    igraph_integer_t expansion_iterations;
+    igraph_real_t    expansion_temperature;
+    igraph_real_t    expansion_attraction;
+    igraph_real_t    expansion_damping_mult;
+    igraph_integer_t cooldown_iterations;
+    igraph_real_t    cooldown_temperature;
+    igraph_real_t    cooldown_attraction;
+    igraph_real_t    cooldown_damping_mult;
+    igraph_integer_t crunch_iterations;
+    igraph_real_t    crunch_temperature;
+    igraph_real_t    crunch_attraction;
+    igraph_real_t    crunch_damping_mult;
+    igraph_integer_t simmer_iterations;
+    igraph_real_t    simmer_temperature;
+    igraph_real_t    simmer_attraction;
+    igraph_real_t    simmer_damping_mult;
+} igraph_layout_drl_options_t;
+
+/**
+ * \typedef igraph_layout_drl_default_t
+ * Predefined parameter templates for the DrL layout generator
+ *
+ * These constants can be used to initialize a set of DrL parameters.
+ * These can then be modified according to the user's needs.
+ * \enumval IGRAPH_LAYOUT_DRL_DEFAULT The deafult parameters.
+ * \enumval IGRAPH_LAYOUT_DRL_COARSEN Slightly modified parameters to
+ *      get a coarser layout.
+ * \enumval IGRAPH_LAYOUT_DRL_COARSEST An even coarser layout.
+ * \enumval IGRAPH_LAYOUT_DRL_REFINE Refine an already calculated layout.
+ * \enumval IGRAPH_LAYOUT_DRL_FINAL Finalize an already refined layout.
+ */
+
+typedef enum { IGRAPH_LAYOUT_DRL_DEFAULT = 0,
+               IGRAPH_LAYOUT_DRL_COARSEN,
+               IGRAPH_LAYOUT_DRL_COARSEST,
+               IGRAPH_LAYOUT_DRL_REFINE,
+               IGRAPH_LAYOUT_DRL_FINAL
+             } igraph_layout_drl_default_t;
+
+DECLDIR int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options,
+        igraph_layout_drl_default_t templ);
+DECLDIR int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res,
+                              igraph_bool_t use_seed,
+                              igraph_layout_drl_options_t *options,
+                              const igraph_vector_t *weights,
+                              const igraph_vector_bool_t *fixed);
+
+DECLDIR int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res,
+                                 igraph_bool_t use_seed,
+                                 igraph_layout_drl_options_t *options,
+                                 const igraph_vector_t *weights,
+                                 const igraph_vector_bool_t *fixed);
+
+DECLDIR int igraph_layout_merge_dla(igraph_vector_ptr_t *graphs,
+                                    igraph_vector_ptr_t *coords,
+                                    igraph_matrix_t *res);
+
+DECLDIR int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res,
+                              igraph_bool_t use_seed, igraph_integer_t maxiter,
+                              igraph_real_t temp_max, igraph_real_t temp_min,
+                              igraph_real_t temp_init);
+
+DECLDIR int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res,
+        igraph_bool_t use_seed, igraph_integer_t maxiter,
+        igraph_integer_t fineiter, igraph_real_t cool_fact,
+        igraph_real_t weight_node_dist, igraph_real_t weight_border,
+        igraph_real_t weight_edge_lengths,
+        igraph_real_t weight_edge_crossings,
+        igraph_real_t weight_node_edge_dist);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_lsap.h b/igraph/include/igraph_lsap.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_lsap.h
@@ -0,0 +1,16 @@
+
+#ifndef IGRAPH_LSAP
+#define IGRAPH_LSAP
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+
+__BEGIN_DECLS
+
+int igraph_solve_lsap(igraph_matrix_t *c, igraph_integer_t n,
+                      igraph_vector_int_t *p);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_marked_queue.h b/igraph/include/igraph_marked_queue.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_marked_queue.h
@@ -0,0 +1,70 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_MARKED_QUEUE_H
+#define IGRAPH_MARKED_QUEUE_H
+
+#include "igraph_vector.h"
+#include "igraph_dqueue.h"
+
+#include <stdio.h>
+
+/* This is essentially a double ended queue, with some extra features:
+   (1) The is-element? operation is fast, O(1). This requires that we
+       know a limit for the number of elements in the queue.
+   (2) We can insert elements in batches, and the whole batch can be
+      removed at once.
+
+  Currently only the top-end operations are implemented, so the queue
+  is essentially a stack.
+*/
+
+typedef struct igraph_marked_queue_t {
+    igraph_dqueue_t Q;
+    igraph_vector_long_t set;
+    long int mark;
+    long int size;
+} igraph_marked_queue_t;
+
+int igraph_marked_queue_init(igraph_marked_queue_t *q,
+                             long int size);
+void igraph_marked_queue_destroy(igraph_marked_queue_t *q);
+void igraph_marked_queue_reset(igraph_marked_queue_t *q);
+
+igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q);
+long int igraph_marked_queue_size(const igraph_marked_queue_t *q);
+int igraph_marked_queue_print(const igraph_marked_queue_t *q);
+int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file);
+
+igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q,
+        long int elem);
+
+int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem);
+
+int igraph_marked_queue_start_batch(igraph_marked_queue_t *q);
+void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q);
+
+int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q,
+                                  igraph_vector_t *vec);
+
+#endif
diff --git a/igraph/include/igraph_matching.h b/igraph/include/igraph_matching.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_matching.h
@@ -0,0 +1,56 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2012  Tamas Nepusz <ntamas@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_MATCHING_H
+#define IGRAPH_MATCHING_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_datatype.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Matchings in graphs                                */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_is_matching(const igraph_t* graph,
+                               const igraph_vector_bool_t* types, const igraph_vector_long_t* matching,
+                               igraph_bool_t* result);
+DECLDIR int igraph_is_maximal_matching(const igraph_t* graph,
+                                       const igraph_vector_bool_t* types, const igraph_vector_long_t* matching,
+                                       igraph_bool_t* result);
+
+DECLDIR int igraph_maximum_bipartite_matching(const igraph_t* graph,
+        const igraph_vector_bool_t* types, igraph_integer_t* matching_size,
+        igraph_real_t* matching_weight, igraph_vector_long_t* matching,
+        const igraph_vector_t* weights, igraph_real_t eps);
+
+DECLDIR int igraph_maximum_matching(const igraph_t* graph, igraph_integer_t* matching_size,
+                                    igraph_real_t* matching_weight, igraph_vector_long_t* matching,
+                                    const igraph_vector_t* weights);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_math.h b/igraph/include/igraph_math.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_math.h
@@ -0,0 +1,100 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2008-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_MATH_H
+#define IGRAPH_MATH_H
+
+#include "config.h"
+#include <math.h>
+#include <stddef.h>
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+__BEGIN_DECLS
+
+/**
+ * \def IGRAPH_SHORTEST_PATH_EPSILON
+ *
+ * Relative error threshold used in weighted shortest path calculations
+ * to decide whether two shortest paths are of equal length.
+ */
+#define IGRAPH_SHORTEST_PATH_EPSILON 1e-10
+
+/*
+ * Compiler-related hacks, mostly because of Microsoft Visual C++
+ */
+double igraph_i_round(double X);
+int igraph_i_snprintf(char *buffer, size_t count, const char *format, ...);
+
+double igraph_log2(const double a);
+double igraph_log1p(double a);
+long double igraph_fabsl(long double a);
+double igraph_fmin(double a, double b);
+#ifndef HAVE_LOG2
+    #define log2(a) igraph_log2(a)
+#endif
+#ifndef HAVE_LOG1P
+    #define log1p(a) igraph_log1p(a)
+#endif
+#ifndef HAVE_FABSL
+    #define fabsl(a) igraph_fabsl(a)
+#endif
+#ifndef HAVE_FMIN
+    #define fmin(a,b) igraph_fmin((a),(b))
+#endif
+#ifndef HAVE_ROUND
+    #define round igraph_i_round
+#endif
+
+#ifndef M_PI
+    #define M_PI 3.14159265358979323846
+#endif
+#ifndef M_PI_2
+    #define M_PI_2 1.57079632679489661923
+#endif
+#ifndef M_LN2
+    #define M_LN2 0.69314718055994530942
+#endif
+#ifndef M_SQRT2
+    #define M_SQRT2 1.4142135623730950488016887
+#endif
+#ifndef M_LN_SQRT_2PI
+    #define M_LN_SQRT_2PI   0.918938533204672741780329736406 /* log(sqrt(2*pi))
+    == log(2*pi)/2 */
+#endif
+
+int igraph_almost_equals(double a, double b, double eps);
+int igraph_cmp_epsilon(double a, double b, double eps);
+
+__END_DECLS
+
+#endif
+
diff --git a/igraph/include/igraph_matrix.h b/igraph/include/igraph_matrix.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_matrix.h
@@ -0,0 +1,100 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_MATRIX_H
+#define IGRAPH_MATRIX_H
+
+#include "igraph_decls.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Matrix, very similar to vector                     */
+/* -------------------------------------------------- */
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "igraph_matrix_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "igraph_matrix_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "igraph_matrix_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "igraph_matrix_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "igraph_matrix_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_COMPLEX
+#include "igraph_pmt.h"
+#include "igraph_matrix_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_COMPLEX
+
+#define IGRAPH_MATRIX_NULL { IGRAPH_VECTOR_NULL, 0, 0 }
+#define IGRAPH_MATRIX_INIT_FINALLY(m, nr, nc) \
+    do { IGRAPH_CHECK(igraph_matrix_init(m, nr, nc)); \
+        IGRAPH_FINALLY(igraph_matrix_destroy, m); } while (0)
+
+/**
+ * \ingroup matrix
+ * \define MATRIX
+ * \brief Accessing an element of a matrix.
+ *
+ * Note that there are no range checks right now.
+ * This functionality might be redefined as a proper function later.
+ * \param m The matrix object.
+ * \param i The index of the row, starting with zero.
+ * \param j The index of the column, starting with zero.
+ *
+ * Time complexity: O(1).
+ */
+#define MATRIX(m,i,j) ((m).data.stor_begin[(m).nrow*(j)+(i)])
+
+igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs,
+                                      const igraph_matrix_t *rhs,
+                                      igraph_real_t tol);
+
+int igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_matrix_pmt.h b/igraph/include/igraph_matrix_pmt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_matrix_pmt.h
@@ -0,0 +1,243 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+typedef struct TYPE(igraph_matrix) {
+    TYPE(igraph_vector) data;
+    long int nrow, ncol;
+} TYPE(igraph_matrix);
+
+/*---------------*/
+/* Allocation    */
+/*---------------*/
+
+DECLDIR int FUNCTION(igraph_matrix, init)(TYPE(igraph_matrix) *m,
+        long int nrow, long int ncol);
+DECLDIR int FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to,
+        const TYPE(igraph_matrix) *from);
+DECLDIR void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m);
+DECLDIR long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m);
+
+/*--------------------*/
+/* Accessing elements */
+/*--------------------*/
+
+/* MATRIX */
+DECLDIR BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m,
+                                        long int row, long int col);
+BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m,
+                                     long int row, long int col);
+DECLDIR void FUNCTION(igraph_matrix, set)(TYPE(igraph_matrix)* m, long int row, long int col,
+        BASE value);
+
+/*------------------------------*/
+/* Initializing matrix elements */
+/*------------------------------*/
+
+DECLDIR void FUNCTION(igraph_matrix, null)(TYPE(igraph_matrix) *m);
+DECLDIR void FUNCTION(igraph_matrix, fill)(TYPE(igraph_matrix) *m, BASE e);
+
+/*-----------------------*/
+/* Matrix views          */
+/*-----------------------*/
+
+const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view)(const TYPE(igraph_matrix) *m,
+        const BASE *data,
+        long int nrow,
+        long int ncol);
+
+/*------------------*/
+/* Copying matrices */
+/*------------------*/
+
+DECLDIR void FUNCTION(igraph_matrix, copy_to)(const TYPE(igraph_matrix) *m, BASE *to);
+DECLDIR int FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to,
+        const TYPE(igraph_matrix) *from);
+DECLDIR int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to,
+        const TYPE(igraph_matrix) *from);
+DECLDIR int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to,
+        const TYPE(igraph_matrix) *from);
+DECLDIR int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2);
+
+/*--------------------------*/
+/* Copying rows and columns */
+/*--------------------------*/
+
+DECLDIR int FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_vector) *res, long int index);
+DECLDIR int FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_vector) *res, long int index);
+DECLDIR int FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m,
+        const TYPE(igraph_vector) *v, long int index);
+DECLDIR int FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m,
+        const TYPE(igraph_vector) *v, long int index);
+DECLDIR int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_matrix) *res,
+        const igraph_vector_t *rows);
+DECLDIR int FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_matrix) *res,
+        const igraph_vector_t *cols);
+DECLDIR int FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_matrix) *res,
+        const igraph_vector_t *rows,
+        const igraph_vector_t *cols);
+
+/*-----------------------------*/
+/* Exchanging rows and columns */
+/*-----------------------------*/
+
+DECLDIR int FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m,
+        long int i, long int j);
+DECLDIR int FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m,
+        long int i, long int j);
+DECLDIR int FUNCTION(igraph_matrix, swap_rowcol)(TYPE(igraph_matrix) *m,
+        long int i, long int j);
+DECLDIR int FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m);
+
+/*-----------------------------*/
+/* Matrix operations           */
+/*-----------------------------*/
+
+DECLDIR int FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2);
+DECLDIR int FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2);
+DECLDIR int FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2);
+DECLDIR int FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2);
+DECLDIR void FUNCTION(igraph_matrix, scale)(TYPE(igraph_matrix) *m, BASE by);
+DECLDIR void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, BASE plus);
+
+/*-----------------------------*/
+/* Finding minimum and maximum */
+/*-----------------------------*/
+
+DECLDIR igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m);
+DECLDIR igraph_real_t FUNCTION(igraph_matrix, max)(const TYPE(igraph_matrix) *m);
+DECLDIR int FUNCTION(igraph_matrix, which_min)(const TYPE(igraph_matrix) *m,
+        long int *i, long int *j);
+DECLDIR int FUNCTION(igraph_matrix, which_max)(const TYPE(igraph_matrix) *m,
+        long int *i, long int *j);
+DECLDIR int FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m,
+        BASE *min, BASE *max);
+DECLDIR int FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m,
+        long int *imin, long int *jmin,
+        long int *imax, long int *jmax);
+
+/*------------------------------*/
+/* Comparison                   */
+/*------------------------------*/
+
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, all_e)(const TYPE(igraph_matrix) *lhs,
+        const TYPE(igraph_matrix) *rhs);
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, all_l)(const TYPE(igraph_matrix) *lhs,
+        const TYPE(igraph_matrix) *rhs);
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, all_g)(const TYPE(igraph_matrix) *lhs,
+        const TYPE(igraph_matrix) *rhs);
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, all_le)(const TYPE(igraph_matrix) *lhs,
+        const TYPE(igraph_matrix) *rhs);
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_matrix) *lhs,
+        const TYPE(igraph_matrix) *rhs);
+
+/*-------------------*/
+/* Matrix properties */
+/*-------------------*/
+
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m);
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m);
+DECLDIR long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m);
+DECLDIR long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m);
+DECLDIR long int FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m);
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m);
+DECLDIR BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m);
+DECLDIR BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m);
+DECLDIR int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_vector) *res);
+DECLDIR int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_vector) *res);
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, is_equal)(const TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2);
+DECLDIR igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2);
+
+/*------------------------*/
+/* Searching for elements */
+/*------------------------*/
+
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, contains)(const TYPE(igraph_matrix) *m,
+        BASE e);
+DECLDIR igraph_bool_t FUNCTION(igraph_matrix, search)(const TYPE(igraph_matrix) *m,
+        long int from, BASE what,
+        long int *pos,
+        long int *row, long int *col);
+
+/*------------------------*/
+/* Resizing operations    */
+/*------------------------*/
+
+DECLDIR int FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m,
+        long int nrow, long int ncol);
+DECLDIR int FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m);
+DECLDIR int FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, long int n);
+DECLDIR int FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, long int n);
+DECLDIR int FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, long int col);
+DECLDIR int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row);
+
+/*------------------------*/
+/* Print as text          */
+/*------------------------*/
+
+int FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m);
+int FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m,
+                                    const char *format);
+int FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m,
+                                    FILE *file);
+
+#ifdef BASE_COMPLEX
+
+int igraph_matrix_complex_real(const igraph_matrix_complex_t *v,
+                               igraph_matrix_t *real);
+int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v,
+                               igraph_matrix_t *imag);
+int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v,
+                                   igraph_matrix_t *real,
+                                   igraph_matrix_t *imag);
+int igraph_matrix_complex_create(igraph_matrix_complex_t *v,
+                                 const igraph_matrix_t *real,
+                                 const igraph_matrix_t *imag);
+int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v,
+                                       const igraph_matrix_t *r,
+                                       const igraph_matrix_t *theta);
+
+#endif
+
+/* ----------------------------------------------------------------------------*/
+/* For internal use only, may be removed, rewritten ... */
+/* ----------------------------------------------------------------------------*/
+
+int FUNCTION(igraph_matrix, permdelete_rows)(TYPE(igraph_matrix) *m,
+        long int *index, long int nremove);
+int FUNCTION(igraph_matrix, delete_rows_neg)(TYPE(igraph_matrix) *m,
+        const igraph_vector_t *neg,
+        long int nremove);
+
diff --git a/igraph/include/igraph_memory.h b/igraph/include/igraph_memory.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_memory.h
@@ -0,0 +1,47 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef REST_MEMORY_H
+#define REST_MEMORY_H
+
+#include <stdlib.h>
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+#define igraph_Calloc(n,t)    (t*) calloc( (size_t)(n), sizeof(t) )
+#define igraph_Realloc(p,n,t) (t*) realloc((void*)(p), (size_t)((n)*sizeof(t)))
+#define igraph_Free(p)        (free( (void *)(p) ), (p) = NULL)
+
+/* #ifndef IGRAPH_NO_CALLOC */
+/* #  define Calloc igraph_Calloc */
+/* #  define Realloc igraph_Realloc */
+/* #  define Free igraph_Free */
+/* #endif */
+
+DECLDIR int igraph_free(void *p);
+DECLDIR void *igraph_malloc(size_t n);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_microscopic_update.h b/igraph/include/igraph_microscopic_update.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_microscopic_update.h
@@ -0,0 +1,60 @@
+/* -*- mode: C -*-  */
+/*
+  Microscopic update rules for dealing with agent-level strategy revision.
+  Copyright (C) 2011 Minh Van Nguyen <nguyenminh2@gmail.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301 USA
+*/
+
+#ifndef IGRAPH_MICROSCOPIC_UPDATE_H
+#define IGRAPH_MICROSCOPIC_UPDATE_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_deterministic_optimal_imitation(const igraph_t *graph,
+        igraph_integer_t vid,
+        igraph_optimal_t optimality,
+        const igraph_vector_t *quantities,
+        igraph_vector_t *strategies,
+        igraph_neimode_t mode);
+DECLDIR int igraph_moran_process(const igraph_t *graph,
+                                 const igraph_vector_t *weights,
+                                 igraph_vector_t *quantities,
+                                 igraph_vector_t *strategies,
+                                 igraph_neimode_t mode);
+DECLDIR int igraph_roulette_wheel_imitation(const igraph_t *graph,
+        igraph_integer_t vid,
+        igraph_bool_t islocal,
+        const igraph_vector_t *quantities,
+        igraph_vector_t *strategies,
+        igraph_neimode_t mode);
+DECLDIR int igraph_stochastic_imitation(const igraph_t *graph,
+                                        igraph_integer_t vid,
+                                        igraph_imitate_algorithm_t algo,
+                                        const igraph_vector_t *quantities,
+                                        igraph_vector_t *strategies,
+                                        igraph_neimode_t mode);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_mixing.h b/igraph/include/igraph_mixing.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_mixing.h
@@ -0,0 +1,51 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_MIXING_H
+#define IGRAPH_MIXING_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_assortativity_nominal(const igraph_t *graph,
+        const igraph_vector_t *types,
+        igraph_real_t *res,
+        igraph_bool_t directed);
+
+DECLDIR int igraph_assortativity(const igraph_t *graph,
+                                 const igraph_vector_t *types1,
+                                 const igraph_vector_t *types2,
+                                 igraph_real_t *res,
+                                 igraph_bool_t directed);
+
+DECLDIR int igraph_assortativity_degree(const igraph_t *graph,
+                                        igraph_real_t *res,
+                                        igraph_bool_t directed);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_motifs.h b/igraph/include/igraph_motifs.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_motifs.h
@@ -0,0 +1,97 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_MOTIFS_H
+#define IGRAPH_MOTIFS_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Graph motifs                                       */
+/* -------------------------------------------------- */
+
+/**
+ * \typedef igraph_motifs_handler_t
+ * Callback type for \c igraph_motifs_randesu_callback
+ *
+ * \ref igraph_motifs_randesu_callback() calls a specified callback
+ * function whenever a new motif is found during a motif search. This
+ * callback function must be of type \c igraph_motifs_handler_t. It has
+ * the following arguments:
+ * \param graph The graph that that algorithm is working on. Of course
+ *   this must not be modified.
+ * \param vids The IDs of the vertices in the motif that has just been
+ *   found. This vector is owned by the motif search algorithm, so do not
+ *   modify or destroy it; make a copy of it if you need it later.
+ * \param isoclass The isomorphism class of the motif that has just been
+ *   found. Use \ref igraph_isoclass or \ref igraph_isoclass_subgraph to find
+ *   out which isomorphism class belongs to a given motif.
+ * \param extra The extra argument that was passed to \ref
+ *   igraph_motifs_randesu_callback().
+ * \return A logical value, if TRUE (=non-zero), that is interpreted
+ *    as a request to stop the motif search and return to the caller.
+ *
+ * \sa \ref igraph_motifs_randesu_callback()
+ */
+
+typedef igraph_bool_t igraph_motifs_handler_t(const igraph_t *graph,
+        igraph_vector_t *vids,
+        int isoclass,
+        void* extra);
+
+DECLDIR int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist,
+                                  int size, const igraph_vector_t *cut_prob);
+
+DECLDIR int igraph_motifs_randesu_callback(const igraph_t *graph, int size,
+        const igraph_vector_t *cut_prob,
+        igraph_motifs_handler_t *callback,
+        void* extra);
+
+DECLDIR int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est,
+        int size, const igraph_vector_t *cut_prob,
+        igraph_integer_t sample_size,
+        const igraph_vector_t *sample);
+DECLDIR int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no,
+                                     int size, const igraph_vector_t *cut_prob);
+
+DECLDIR int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut,
+                               igraph_integer_t *asym, igraph_integer_t *null);
+DECLDIR int igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res);
+DECLDIR int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2,
+                                   igraph_real_t *res4);
+
+DECLDIR int igraph_adjacent_triangles(const igraph_t *graph,
+                                      igraph_vector_t *res,
+                                      const igraph_vs_t vids);
+
+DECLDIR int igraph_list_triangles(const igraph_t *graph,
+                                  igraph_vector_int_t *res);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_neighborhood.h b/igraph/include/igraph_neighborhood.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_neighborhood.h
@@ -0,0 +1,47 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_NEIGHBORHOOD_H
+#define IGRAPH_NEIGHBORHOOD_H
+
+#include "igraph_decls.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res,
+                                     igraph_vs_t vids, igraph_integer_t order,
+                                     igraph_neimode_t mode, igraph_integer_t mindist);
+DECLDIR int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res,
+                                igraph_vs_t vids, igraph_integer_t order,
+                                igraph_neimode_t mode, igraph_integer_t mindist);
+DECLDIR int igraph_neighborhood_graphs(const igraph_t *graph, igraph_vector_ptr_t *res,
+                                       igraph_vs_t vids, igraph_integer_t order,
+                                       igraph_neimode_t mode,
+                                       igraph_integer_t mindist);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_nongraph.h b/igraph/include/igraph_nongraph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_nongraph.h
@@ -0,0 +1,93 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_NONGRAPH_H
+#define IGRAPH_NONGRAPH_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_matrix.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Other, not graph related                           */
+/* -------------------------------------------------- */
+
+/**
+ * \struct igraph_plfit_result_t
+ * \brief Result of fitting a power-law distribution to a vector
+ *
+ * This data structure contains the result of \ref igraph_power_law_fit(),
+ * which tries to fit a power-law distribution to a vector of numbers. The
+ * structure contains the following members:
+ *
+ * \member continuous Whether the fitted power-law distribution was continuous
+ *                    or discrete.
+ * \member alpha The exponent of the fitted power-law distribution.
+ * \member xmin  The minimum value from which the power-law distribution was
+ *               fitted. In other words, only the values larger than \c xmin
+ *               were used from the input vector.
+ * \member L     The log-likelihood of the fitted parameters; in other words,
+ *               the probability of observing the input vector given the
+ *               parameters.
+ * \member D     The test statistic of a Kolmogorov-Smirnov test that compares
+ *               the fitted distribution with the input vector. Smaller scores
+ *               denote better fit.
+ * \member p     The p-value of the Kolmogorov-Smirnov test. Small p-values
+ *               (less than 0.05) indicate that the test rejected the hypothesis
+ *               that the original data could have been drawn from the fitted
+ *               power-law distribution.
+ */
+typedef struct igraph_plfit_result_t {
+    igraph_bool_t continuous;
+    double alpha;
+    double xmin;
+    double L;
+    double D;
+    double p;
+} igraph_plfit_result_t;
+
+DECLDIR int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res,
+                                igraph_integer_t binwidth);
+DECLDIR int igraph_fisher_yates_shuffle(igraph_vector_t *seq);
+DECLDIR int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h,
+                                 igraph_integer_t length);
+DECLDIR int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts,
+                               igraph_matrix_t *rescoords);
+DECLDIR int igraph_zeroin(igraph_real_t *ax, igraph_real_t *bx,
+                          igraph_real_t (*f)(igraph_real_t x, void *info),
+                          void *info, igraph_real_t *Tol, int *Maxit, igraph_real_t *res);
+DECLDIR int igraph_bfgs(igraph_vector_t *b, igraph_real_t *Fmin,
+                        igraph_scalar_function_t fminfn, igraph_vector_function_t fmingr,
+                        int maxit, int trace,
+                        igraph_real_t abstol, igraph_real_t reltol, int nREPORT, void *ex,
+                        igraph_integer_t *fncount, igraph_integer_t *grcount);
+DECLDIR int igraph_power_law_fit(const igraph_vector_t* vector, igraph_plfit_result_t* result,
+                                 igraph_real_t xmin, igraph_bool_t force_continuous);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_operators.h b/igraph/include/igraph_operators.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_operators.h
@@ -0,0 +1,63 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_OPERATORS_H
+#define IGRAPH_OPERATORS_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Graph operators                                    */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_disjoint_union(igraph_t *res,
+                                  const igraph_t *left, const igraph_t *right);
+DECLDIR int igraph_disjoint_union_many(igraph_t *res,
+                                       const igraph_vector_ptr_t *graphs);
+DECLDIR int igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right,
+                         igraph_vector_t *edge_map1, igraph_vector_t *edge_map2);
+DECLDIR int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs,
+                              igraph_vector_ptr_t *edgemaps);
+DECLDIR int igraph_intersection(igraph_t *res,
+                                const igraph_t *left, const igraph_t *right,
+                                igraph_vector_t *edge_map1,
+                                igraph_vector_t *edge_map2);
+DECLDIR int igraph_intersection_many(igraph_t *res,
+                                     const igraph_vector_ptr_t *graphs,
+                                     igraph_vector_ptr_t *edgemaps);
+DECLDIR int igraph_difference(igraph_t *res,
+                              const igraph_t *orig, const igraph_t *sub);
+DECLDIR int igraph_complementer(igraph_t *res, const igraph_t *graph,
+                                igraph_bool_t loops);
+DECLDIR int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2,
+                           igraph_vector_t *edge_map1, igraph_vector_t *edge_map2);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_paths.h b/igraph/include/igraph_paths.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_paths.h
@@ -0,0 +1,146 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_PATHS_H
+#define IGRAPH_PATHS_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_matrix.h"
+#include "igraph_iterators.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_diameter(const igraph_t *graph, igraph_integer_t *res,
+                            igraph_integer_t *from, igraph_integer_t *to,
+                            igraph_vector_t *path,
+                            igraph_bool_t directed, igraph_bool_t unconn);
+DECLDIR int igraph_diameter_dijkstra(const igraph_t *graph,
+                                     const igraph_vector_t *weights,
+                                     igraph_real_t *pres,
+                                     igraph_integer_t *pfrom,
+                                     igraph_integer_t *pto,
+                                     igraph_vector_t *path,
+                                     igraph_bool_t directed,
+                                     igraph_bool_t unconn);
+
+DECLDIR int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res,
+                                  const igraph_vs_t from, const igraph_vs_t to,
+                                  igraph_neimode_t mode);
+DECLDIR int igraph_get_shortest_paths(const igraph_t *graph,
+                                      igraph_vector_ptr_t *vertices,
+                                      igraph_vector_ptr_t *edges,
+                                      igraph_integer_t from, const igraph_vs_t to,
+                                      igraph_neimode_t mode,
+                                      igraph_vector_long_t *predecessors,
+                                      igraph_vector_long_t *inbound_edges);
+DECLDIR int igraph_get_shortest_path(const igraph_t *graph,
+                                     igraph_vector_t *vertices,
+                                     igraph_vector_t *edges,
+                                     igraph_integer_t from,
+                                     igraph_integer_t to,
+                                     igraph_neimode_t mode);
+
+DECLDIR int igraph_get_all_shortest_paths(const igraph_t *graph,
+        igraph_vector_ptr_t *res,
+        igraph_vector_t *nrgeo,
+        igraph_integer_t from, const igraph_vs_t to,
+        igraph_neimode_t mode);
+DECLDIR int igraph_shortest_paths_dijkstra(const igraph_t *graph,
+        igraph_matrix_t *res,
+        const igraph_vs_t from,
+        const igraph_vs_t to,
+        const igraph_vector_t *weights,
+        igraph_neimode_t mode);
+DECLDIR int igraph_shortest_paths_bellman_ford(const igraph_t *graph,
+        igraph_matrix_t *res,
+        const igraph_vs_t from,
+        const igraph_vs_t to,
+        const igraph_vector_t *weights,
+        igraph_neimode_t mode);
+DECLDIR int igraph_get_shortest_paths_dijkstra(const igraph_t *graph,
+        igraph_vector_ptr_t *vertices,
+        igraph_vector_ptr_t *edges,
+        igraph_integer_t from,
+        igraph_vs_t to,
+        const igraph_vector_t *weights,
+        igraph_neimode_t mode,
+        igraph_vector_long_t *predecessors,
+        igraph_vector_long_t *inbound_edges);
+DECLDIR int igraph_get_shortest_path_dijkstra(const igraph_t *graph,
+        igraph_vector_t *vertices,
+        igraph_vector_t *edges,
+        igraph_integer_t from,
+        igraph_integer_t to,
+        const igraph_vector_t *weights,
+        igraph_neimode_t mode);
+DECLDIR int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph,
+        igraph_vector_ptr_t *res,
+        igraph_vector_t *nrgeo,
+        igraph_integer_t from, igraph_vs_t to,
+        const igraph_vector_t *weights,
+        igraph_neimode_t mode);
+DECLDIR int igraph_shortest_paths_johnson(const igraph_t *graph,
+        igraph_matrix_t *res,
+        const igraph_vs_t from,
+        const igraph_vs_t to,
+        const igraph_vector_t *weights);
+
+DECLDIR int igraph_average_path_length(const igraph_t *graph, igraph_real_t *res,
+                                       igraph_bool_t directed, igraph_bool_t unconn);
+DECLDIR int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res,
+                                    igraph_real_t *unconnected, igraph_bool_t directed);
+
+DECLDIR int igraph_eccentricity(const igraph_t *graph,
+                                igraph_vector_t *res,
+                                igraph_vs_t vids,
+                                igraph_neimode_t mode);
+
+DECLDIR int igraph_radius(const igraph_t *graph, igraph_real_t *radius,
+                          igraph_neimode_t mode);
+
+DECLDIR int igraph_get_all_simple_paths(const igraph_t *graph,
+                                        igraph_vector_int_t *res,
+                                        igraph_integer_t from,
+                                        const igraph_vs_t to,
+                                        igraph_integer_t cutoff,
+                                        igraph_neimode_t mode);
+
+DECLDIR int igraph_random_walk(const igraph_t *graph, igraph_vector_t *walk,
+                               igraph_integer_t start, igraph_neimode_t mode,
+                               igraph_integer_t steps,
+                               igraph_random_walk_stuck_t stuck);
+
+DECLDIR int igraph_random_edge_walk(const igraph_t *graph,
+                                    const igraph_vector_t *weights,
+                                    igraph_vector_t *edgewalk,
+                                    igraph_integer_t start, igraph_neimode_t mode,
+                                    igraph_integer_t steps,
+                                    igraph_random_walk_stuck_t stuck);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_pmt.h b/igraph/include/igraph_pmt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_pmt.h
@@ -0,0 +1,150 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#define CONCAT2x(a,b) a ## _ ## b
+#define CONCAT2(a,b) CONCAT2x(a,b)
+#define CONCAT3x(a,b,c) a ## _ ## b ## _ ## c
+#define CONCAT3(a,b,c) CONCAT3x(a,b,c)
+#define CONCAT4x(a,b,c,d) a ## _ ## b ## _ ## c ## _ ## d
+#define CONCAT4(a,b,c,d) CONCAT4x(a,b,c,d)
+
+#if defined(BASE_IGRAPH_REAL)
+    #define BASE igraph_real_t
+    #define SHORT
+    #define OUT_FORMAT "%G"
+    #define PRINTFUNC(val) igraph_real_printf(val)
+    #define FPRINTFUNC(file, val) igraph_real_fprintf(file, val)
+    #define ZERO 0.0
+    #define ONE 1.0
+    #define MULTIPLICITY 1
+
+#elif defined(BASE_FLOAT)
+    #define BASE float
+    #define SHORT float
+    #define OUT_FORMAT "%f"
+    #define ZERO 0.0F
+    #define ONE 1.0F
+    #define MULTIPLICITY 1
+
+#elif defined(BASE_LONG)
+    #define BASE long
+    #define SHORT long
+    #define OUT_FORMAT "%ld"
+    #define ZERO 0L
+    #define ONE 1L
+    #define MULTIPLICITY 1
+
+#elif defined(BASE_CHAR)
+    #define BASE char
+    #define SHORT char
+    #define OUT_FORMAT "%d"
+    #define ZERO 0
+    #define ONE 1
+    #define MULTIPLICITY 1
+
+#elif defined(BASE_BOOL)
+    #define BASE igraph_bool_t
+    #define SHORT bool
+    #define OUT_FORMAT "%d"
+    #define ZERO 0
+    #define ONE 1
+    #define MULTIPLICITY 1
+
+#elif defined(BASE_INT)
+    #define BASE int
+    #define SHORT int
+    #define OUT_FORMAT "%d"
+    #define ZERO 0
+    #define ONE 1
+    #define MULTIPLICITY 1
+
+#elif defined(BASE_LIMB)
+    #define BASE limb_t
+    #define SHORT limb
+    #define ZERO 0
+    #define ONE 1
+    #define MULTIPLICITY 1
+    #define UNSIGNED 1
+
+#elif defined(BASE_PTR)
+    #define BASE void*
+    #define SHORT ptr
+    #define ZERO 0
+    #define MULTIPLICITY 1
+
+#elif defined(BASE_COMPLEX)
+    #undef complex
+    #define BASE igraph_complex_t
+    #define SHORT complex
+    #define ZERO igraph_complex(0,0)
+    #define ONE {{1.0,0.0}}
+    #define MULTIPLICITY 2
+    #define NOTORDERED 1
+    #define NOABS 1
+    #define SUM(a,b,c) ((a) = igraph_complex_add((b),(c)))
+    #define DIFF(a,b,c) ((a) = igraph_complex_sub((b),(c)))
+    #define PROD(a,b,c) ((a) = igraph_complex_mul((b),(c)))
+    #define DIV(a,b,c) ((a) = igraph_complex_div((b),(c)))
+    #define EQ(a,b) IGRAPH_COMPLEX_EQ((a),(b))
+    #define SQ(a) IGRAPH_REAL(igraph_complex_mul((a),(a)))
+
+#else
+    #error unknown BASE_ directive
+#endif
+
+#if defined(BASE_IGRAPH_REAL)
+    #define FUNCTION(dir,name) CONCAT2(dir,name)
+    #define TYPE(dir) CONCAT2(dir,t)
+#elif defined(BASE_BOOL)
+    /* Special case because stdbool.h defines bool as a macro to _Bool which would
+    * screw things up */
+    #define FUNCTION(a,c) CONCAT3x(a,bool,c)
+    #define TYPE(dir) CONCAT3x(dir,bool,t)
+#else
+    #define FUNCTION(a,c) CONCAT3(a,SHORT,c)
+    #define TYPE(dir) CONCAT3(dir,SHORT,t)
+#endif
+
+#if defined(HEAP_TYPE_MIN)
+    #define HEAPMORE <
+    #define HEAPMOREEQ <=
+    #define HEAPLESS >
+    #define HEAPLESSEQ >=
+    #undef FUNCTION
+    #undef TYPE
+    #if defined(BASE_IGRAPH_REAL)
+        #define FUNCTION(dir,name) CONCAT3(dir,min,name)
+        #define TYPE(dir) CONCAT3(dir,min,t)
+    #else
+        #define FUNCTION(a,c) CONCAT4(a,min,SHORT,c)
+        #define TYPE(dir) CONCAT4(dir,min,SHORT,t)
+    #endif
+#endif
+
+#if defined(HEAP_TYPE_MAX)
+    #define HEAPMORE >
+    #define HEAPMOREEQ >=
+    #define HEAPLESS <
+    #define HEAPLESSEQ <=
+#endif
+
diff --git a/igraph/include/igraph_pmt_off.h b/igraph/include/igraph_pmt_off.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_pmt_off.h
@@ -0,0 +1,158 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifdef ATOMIC
+    #undef ATOMIC
+#endif
+
+#ifdef ATOMIC_IO
+    #undef ATOMIC_IO
+#endif
+
+#ifdef BASE
+    #undef BASE
+#endif
+
+#ifdef BASE_EPSILON
+    #undef BASE_EPSILON
+#endif
+
+#ifdef CONCAT2
+    #undef CONCAT2
+#endif
+
+#ifdef CONCAT2x
+    #undef CONCAT2x
+#endif
+
+#ifdef CONCAT3
+    #undef CONCAT3
+#endif
+
+#ifdef CONCAT3x
+    #undef CONCAT3x
+#endif
+
+#ifdef CONCAT4
+    #undef CONCAT4
+#endif
+
+#ifdef CONCAT4x
+    #undef CONCAT4x
+#endif
+
+#ifdef FP
+    #undef FP
+#endif
+
+#ifdef FUNCTION
+    #undef FUNCTION
+#endif
+
+#ifdef IN_FORMAT
+    #undef IN_FORMAT
+#endif
+
+#ifdef MULTIPLICITY
+    #undef MULTIPLICITY
+#endif
+
+#ifdef ONE
+    #undef ONE
+#endif
+
+#ifdef OUT_FORMAT
+    #undef OUT_FORMAT
+#endif
+
+#ifdef SHORT
+    #undef SHORT
+#endif
+
+#ifdef TYPE
+    #undef TYPE
+#endif
+
+#ifdef ZERO
+    #undef ZERO
+#endif
+
+#ifdef HEAPMORE
+    #undef HEAPMORE
+#endif
+
+#ifdef HEAPLESS
+    #undef HEAPLESS
+#endif
+
+#ifdef HEAPMOREEQ
+    #undef HEAPMOREEQ
+#endif
+
+#ifdef HEAPLESSEQ
+    #undef HEAPLESSEQ
+#endif
+
+#ifdef SUM
+    #undef SUM
+#endif
+
+#ifdef SQ
+    #undef SQ
+#endif
+
+#ifdef PROD
+    #undef PROD
+#endif
+
+#ifdef NOTORDERED
+    #undef NOTORDERED
+#endif
+
+#ifdef EQ
+    #undef EQ
+#endif
+
+#ifdef DIFF
+    #undef DIFF
+#endif
+
+#ifdef DIV
+    #undef DIV
+#endif
+
+#ifdef NOABS
+    #undef NOABS
+#endif
+
+#ifdef PRINTFUNC
+    #undef PRINTFUNC
+#endif
+
+#ifdef FPRINTFUNC
+    #undef PRINTFUNC
+#endif
+
+#ifdef UNSIGNED
+    #undef UNSIGNED
+#endif
diff --git a/igraph/include/igraph_progress.h b/igraph/include/igraph_progress.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_progress.h
@@ -0,0 +1,183 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_PROGRESS_H
+#define IGRAPH_PROGRESS_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+
+__BEGIN_DECLS
+
+/**
+ * \section about_progress_handlers About progress handlers
+ *
+ * <para>It is often useful to report the progress of some long
+ * calculation, to allow the user to follow the computation and
+ * guess the total running time. A couple of igraph functions
+ * support this at the time of writing, hopefully more will support it
+ * in the future.
+ * </para>
+ *
+ * <para>
+ * To see the progress of a computation, the user has to install a
+ * progress handler, as there is none installed by default.
+ * If an igraph function supports progress reporting, then it
+ * calls the installed progress handler periodically, and passes a
+ * percentage value to it, the percentage of computation already
+ * performed. To install a progress handler, you need to call
+ * \ref igraph_set_progress_handler(). Currently there is a single
+ * pre-defined progress handler, called \ref
+ * igraph_progress_handler_stderr().
+ * </para>
+ */
+
+/**
+ * \section writing_progress_handlers Writing progress handlers
+ *
+ * <para>
+ * To write a new progress handler, one needs to create a function of
+ * type \ref igraph_progress_handler_t. The new progress handler
+ * can then be installed with the \ref igraph_set_progress_handler()
+ * function.
+ * </para>
+ *
+ * <para>
+ * One can assume that the first progress handler call from a
+ * calculation will be call with zero as the \p percentage argument,
+ * and the last call from a function will have 100 as the \p
+ * percentage argument. Note, however, that if an error happens in the
+ * middle of a computation, then the 100 percent call might be
+ * omitted.
+ * </para>
+ */
+
+/**
+ * \section igraph_functions_with_progress Writing igraph functions with progress reporting
+ *
+ * <para>
+ * If you want to write a function that uses igraph and supports
+ * progress reporting, you need to include \ref igraph_progress()
+ * calls in your function, usually via the \ref IGRAPH_PROGRESS()
+ * macro.
+ * </para>
+ *
+ * <para>
+ * It is good practice to always include a call to \ref
+ * igraph_progress() with a zero \p percentage argument, before the
+ * computation; and another call with 100 \p percentage value
+ * after the computation is completed.
+ * </para>
+ *
+ * <para>
+ * It is also good practice \em not to call \ref igraph_progress() too
+ * often, as this would slow down the computation. It might not be
+ * worth to support progress reporting in functions with linear or
+ * log-linear time complexity, as these are fast, even with a large
+ * amount of data. For functions with quadratic or higher time
+ * complexity make sure that the time complexity of the progress
+ * reporting is constant or at least linear. In practice this means
+ * having at most O(n) progress checks and at most 100 \reg
+ * igraph_progress() calls.
+ * </para>
+ */
+
+/**
+ * \section progress_and_threads Multi-threaded programs
+ *
+ * <para>
+ * In multi-threaded programs, each thread has its own progress
+ * handler, if thread-local storage is supported and igraph is
+ * thread-safe. See the \ref IGRAPH_THREAD_SAFE macro for checking
+ * whether an igraph build is thread-safe.
+ * </para>
+ */
+
+/* -------------------------------------------------- */
+/* Progress handlers                                  */
+/* -------------------------------------------------- */
+
+/**
+ * \typedef igraph_progress_handler_t
+ * \brief Type of progress handler functions
+ *
+ * This is the type of the igraph progress handler functions.
+ * There is currently one such predefined function,
+ * \ref igraph_progress_handler_stderr(), but the user can
+ * write and set up more sophisticated ones.
+ * \param message A string describing the function or algorithm
+ *     that is reporting the progress. Current igraph functions
+ *     always use the name \p message argument if reporting from the
+ *     same function.
+ * \param percent Numeric, the percentage that was completed by the
+ *     algorithm or function.
+ * \param data User-defined data. Current igraph functions that
+ *     report progress pass a null pointer here. Users can
+ *     write their own progress handlers and functions with progress
+ *     reporting, and then pass some meaningfull context here.
+ * \return If the return value of the progress handler is not
+ *     IGRAPH_SUCCESS (=0), then \ref igraph_progress() returns the
+ *     error code \c IGRAPH_INTERRUPTED. The \ref IGRAPH_PROGRESS()
+ *     macro frees all memory and finishes the igraph function with
+ *     error code \c IGRAPH_INTERRUPTED in this case.
+ */
+
+typedef int igraph_progress_handler_t(const char *message, igraph_real_t percent,
+                                      void *data);
+
+extern igraph_progress_handler_t igraph_progress_handler_stderr;
+
+DECLDIR igraph_progress_handler_t * igraph_set_progress_handler(igraph_progress_handler_t new_handler);
+
+DECLDIR int igraph_progress(const char *message, igraph_real_t percent, void *data);
+
+DECLDIR int igraph_progressf(const char *message, igraph_real_t percent, void *data,
+                             ...);
+
+/**
+ * \define IGRAPH_PROGRESS
+ * \brief Report progress.
+ *
+ * The standard way to report progress from an igraph function
+ * \param message A string, a textual message that references the
+ *    calculation under progress.
+ * \param percent Numeric scalar, the percentage that is complete.
+ * \param data User-defined data, this can be used in user-defined
+ *    progress handler functions, from user-written igraph functions.
+ * \return If the progress handler returns with \c IGRAPH_INTERRUPTED,
+ *    then this macro frees up the igraph allocated memory for
+ *    temporary data and returns to the caller with \c
+ *    IGRAPH_INTERRUPTED.
+ */
+
+#define IGRAPH_PROGRESS(message, percent, data) \
+    do { \
+        if (igraph_progress((message), (percent), (data)) != IGRAPH_SUCCESS) { \
+            IGRAPH_FINALLY_FREE(); \
+            return IGRAPH_INTERRUPTED; \
+        } \
+    } while (0)
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_psumtree.h b/igraph/include/igraph_psumtree.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_psumtree.h
@@ -0,0 +1,58 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_PSUMTREE_H
+#define IGRAPH_PSUMTREE_H
+
+#include "igraph_decls.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+/*
+ * Defines a partial prefix sum tree which is handy for drawing random numbers
+ * from a dynamic discrete distribution. The first part (0,...,offset - 1) of
+ * the vector v contains the prefixes of the values contained in the latter part
+ * (offset, offset + size - 1) of vector v.
+ */
+
+typedef struct {
+    igraph_vector_t v;
+    long int size;
+    long int offset;
+} igraph_psumtree_t;
+
+DECLDIR int igraph_psumtree_init(igraph_psumtree_t *t, long int size);
+DECLDIR void igraph_psumtree_reset(igraph_psumtree_t *t);
+DECLDIR void igraph_psumtree_destroy(igraph_psumtree_t *t);
+DECLDIR igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx);
+DECLDIR long int igraph_psumtree_size(const igraph_psumtree_t *t);
+DECLDIR int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx,
+                                   igraph_real_t elem);
+DECLDIR int igraph_psumtree_update(igraph_psumtree_t *t, long int idx,
+                                   igraph_real_t new_value);
+DECLDIR igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_qsort.h b/igraph/include/igraph_qsort.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_qsort.h
@@ -0,0 +1,40 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA 02139, USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_QSORT_H
+#define IGRAPH_QSORT_H
+
+#include "igraph_decls.h"
+
+#include <stddef.h>
+
+__BEGIN_DECLS
+
+DECLDIR void igraph_qsort(void *base, size_t nel, size_t width,
+                          int (*compar)(const void *, const void *));
+DECLDIR void igraph_qsort_r(void *base, size_t nel, size_t width, void *thunk,
+                            int (*compar)(void *, const void *, const void *));
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_random.h b/igraph/include/igraph_random.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_random.h
@@ -0,0 +1,133 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef REST_RANDOM_H
+#define REST_RANDOM_H
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+
+/* The new RNG interface is (somewhat) modelled based on the GSL */
+
+typedef struct igraph_rng_type_t {
+    const char *name;
+    unsigned long int min;
+    unsigned long int max;
+    int (*init)(void **state);
+    void (*destroy)(void *state);
+    int (*seed)(void *state, unsigned long int seed);
+    unsigned long int (*get)(void *state);
+    igraph_real_t (*get_real)(void *state);
+    igraph_real_t (*get_norm)(void *state);
+    igraph_real_t (*get_geom)(void *state, igraph_real_t p);
+    igraph_real_t (*get_binom)(void *state, long int n, igraph_real_t p);
+    igraph_real_t (*get_exp)(void *state, igraph_real_t rate);
+    igraph_real_t (*get_gamma)(void *state, igraph_real_t shape,
+                               igraph_real_t scale);
+} igraph_rng_type_t;
+
+typedef struct igraph_rng_t {
+    const igraph_rng_type_t *type;
+    void *state;
+    int def;
+} igraph_rng_t;
+
+/* --------------------------------- */
+
+DECLDIR int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type);
+DECLDIR void igraph_rng_destroy(igraph_rng_t *rng);
+
+DECLDIR int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed);
+DECLDIR unsigned long int igraph_rng_max(igraph_rng_t *rng);
+DECLDIR unsigned long int igraph_rng_min(igraph_rng_t *rng);
+DECLDIR const char *igraph_rng_name(igraph_rng_t *rng);
+
+DECLDIR long int igraph_rng_get_integer(igraph_rng_t *rng,
+                                        long int l, long int h);
+DECLDIR igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng,
+        igraph_real_t m, igraph_real_t s);
+DECLDIR igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng,
+        igraph_real_t l, igraph_real_t h);
+DECLDIR igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng);
+DECLDIR igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p);
+DECLDIR igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n,
+        igraph_real_t p);
+DECLDIR igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate);
+DECLDIR unsigned long int igraph_rng_get_int31(igraph_rng_t *rng);
+DECLDIR igraph_real_t igraph_rng_get_gamma(igraph_rng_t *rng, igraph_real_t shape,
+        igraph_real_t scale);
+DECLDIR int igraph_rng_get_dirichlet(igraph_rng_t *rng,
+                                     const igraph_vector_t *alpha,
+                                     igraph_vector_t *result);
+
+/* --------------------------------- */
+
+extern const igraph_rng_type_t igraph_rngtype_glibc2;
+extern const igraph_rng_type_t igraph_rngtype_rand;
+extern const igraph_rng_type_t igraph_rngtype_mt19937;
+
+DECLDIR igraph_rng_t *igraph_rng_default(void);
+DECLDIR void igraph_rng_set_default(igraph_rng_t *rng);
+
+/* --------------------------------- */
+
+#ifdef USING_R
+
+void GetRNGstate(void);
+void PutRNGstate(void);
+#define RNG_BEGIN()    GetRNGstate()
+#define RNG_END()      PutRNGstate()
+
+double Rf_dnorm4(double x, double mu, double sigma, int give_log);
+#define igraph_dnorm Rf_dnorm4
+
+#else
+
+#define RNG_BEGIN()      if (igraph_rng_default()->def==1) {    \
+        igraph_rng_seed(igraph_rng_default(), time(0));       \
+        igraph_rng_default()->def=2;                  \
+    }
+#define RNG_END()       /* do nothing */
+
+DECLDIR double igraph_dnorm(double x, double mu, double sigma, int give_log);
+
+#endif
+
+#define RNG_INTEGER(l,h) (igraph_rng_get_integer(igraph_rng_default(),(l),(h)))
+#define RNG_NORMAL(m,s)  (igraph_rng_get_normal(igraph_rng_default(),(m),(s)))
+#define RNG_UNIF(l,h)    (igraph_rng_get_unif(igraph_rng_default(),(l),(h)))
+#define RNG_UNIF01()     (igraph_rng_get_unif01(igraph_rng_default()))
+#define RNG_GEOM(p)      (igraph_rng_get_geom(igraph_rng_default(),(p)))
+#define RNG_BINOM(n,p)   (igraph_rng_get_binom(igraph_rng_default(),(n),(p)))
+#define RNG_INT31()      (igraph_rng_get_int31(igraph_rng_default()))
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_scan.h b/igraph/include/igraph_scan.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_scan.h
@@ -0,0 +1,69 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_SCAN_H
+#define IGRAPH_SCAN_H
+
+#include "igraph_decls.h"
+#include "igraph_datatype.h"
+#include "igraph_arpack.h"
+#include "igraph_constants.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res,
+                                const igraph_vector_t *weights, igraph_neimode_t mode);
+
+DECLDIR int igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them,
+                                     igraph_vector_t *res,
+                                     const igraph_vector_t *weigths_them,
+                                     igraph_neimode_t mode);
+
+DECLDIR int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res,
+                                       const igraph_vector_t *weights,
+                                       igraph_neimode_t mode);
+
+DECLDIR int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them,
+        igraph_vector_t *res,
+        const igraph_vector_t *weights,
+        igraph_neimode_t mode);
+
+DECLDIR int igraph_local_scan_k_ecount(const igraph_t *graph, int k,
+                                       igraph_vector_t *res,
+                                       const igraph_vector_t *weights,
+                                       igraph_neimode_t mode);
+
+DECLDIR int igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them,
+        int k, igraph_vector_t *res,
+        const igraph_vector_t *weights_them,
+        igraph_neimode_t mode);
+
+DECLDIR int igraph_local_scan_neighborhood_ecount(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vector_t *weights,
+        const igraph_vector_ptr_t *neighborhoods);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_scg.h b/igraph/include/igraph_scg.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_scg.h
@@ -0,0 +1,142 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_SCG_H
+#define IGRAPH_SCG_H
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+#include "igraph_sparsemat.h"
+
+__BEGIN_DECLS
+
+typedef enum { IGRAPH_SCG_SYMMETRIC = 1, IGRAPH_SCG_LAPLACIAN = 2,
+               IGRAPH_SCG_STOCHASTIC = 3
+             } igraph_scg_matrix_t;
+
+typedef enum { IGRAPH_SCG_OPTIMUM = 1, IGRAPH_SCG_INTERV_KM = 2,
+               IGRAPH_SCG_INTERV = 3, IGRAPH_SCG_EXACT = 4
+             }
+igraph_scg_algorithm_t;
+
+typedef enum { IGRAPH_SCG_NORM_ROW = 1, IGRAPH_SCG_NORM_COL = 2 }
+igraph_scg_norm_t;
+
+typedef enum { IGRAPH_SCG_DIRECTION_DEFAULT = 1,
+               IGRAPH_SCG_DIRECTION_LEFT = 2,
+               IGRAPH_SCG_DIRECTION_RIGHT = 3
+             } igraph_scg_direction_t;
+
+int igraph_scg_grouping(const igraph_matrix_t *V,
+                        igraph_vector_t *groups,
+                        igraph_integer_t nt,
+                        const igraph_vector_t *nt_vec,
+                        igraph_scg_matrix_t mtype,
+                        igraph_scg_algorithm_t algo,
+                        const igraph_vector_t *p,
+                        igraph_integer_t maxiter);
+
+int igraph_scg_semiprojectors(const igraph_vector_t *groups,
+                              igraph_scg_matrix_t mtype,
+                              igraph_matrix_t *L,
+                              igraph_matrix_t *R,
+                              igraph_sparsemat_t *Lsparse,
+                              igraph_sparsemat_t *Rsparse,
+                              const igraph_vector_t *p,
+                              igraph_scg_norm_t norm);
+
+int igraph_scg_norm_eps(const igraph_matrix_t *V,
+                        const igraph_vector_t *groups,
+                        igraph_vector_t *eps,
+                        igraph_scg_matrix_t mtype,
+                        const igraph_vector_t *p,
+                        igraph_scg_norm_t norm);
+
+int igraph_scg_adjacency(const igraph_t *graph,
+                         const igraph_matrix_t *matrix,
+                         const igraph_sparsemat_t *sparsemat,
+                         const igraph_vector_t *ev,
+                         igraph_integer_t nt,
+                         const igraph_vector_t *nt_vec,
+                         igraph_scg_algorithm_t algo,
+                         igraph_vector_t *values,
+                         igraph_matrix_t *vectors,
+                         igraph_vector_t *groups,
+                         igraph_bool_t use_arpack,
+                         igraph_integer_t maxiter,
+                         igraph_t *scg_graph,
+                         igraph_matrix_t *scg_matrix,
+                         igraph_sparsemat_t *scg_sparsemat,
+                         igraph_matrix_t *L,
+                         igraph_matrix_t *R,
+                         igraph_sparsemat_t *Lsparse,
+                         igraph_sparsemat_t *Rsparse);
+
+int igraph_scg_stochastic(const igraph_t *graph,
+                          const igraph_matrix_t *matrix,
+                          const igraph_sparsemat_t *sparsemat,
+                          const igraph_vector_t *ev,
+                          igraph_integer_t nt,
+                          const igraph_vector_t *nt_vec,
+                          igraph_scg_algorithm_t algo,
+                          igraph_scg_norm_t norm,
+                          igraph_vector_complex_t *values,
+                          igraph_matrix_complex_t *vectors,
+                          igraph_vector_t *groups,
+                          igraph_vector_t *p,
+                          igraph_bool_t use_arpack,
+                          igraph_integer_t maxiter,
+                          igraph_t *scg_graph,
+                          igraph_matrix_t *scg_matrix,
+                          igraph_sparsemat_t *scg_sparsemat,
+                          igraph_matrix_t *L,
+                          igraph_matrix_t *R,
+                          igraph_sparsemat_t *Lsparse,
+                          igraph_sparsemat_t *Rsparse);
+
+int igraph_scg_laplacian(const igraph_t *graph,
+                         const igraph_matrix_t *matrix,
+                         const igraph_sparsemat_t *sparsemat,
+                         const igraph_vector_t *ev,
+                         igraph_integer_t nt,
+                         const igraph_vector_t *nt_vec,
+                         igraph_scg_algorithm_t algo,
+                         igraph_scg_norm_t norm,
+                         igraph_scg_direction_t direction,
+                         igraph_vector_complex_t *values,
+                         igraph_matrix_complex_t *vectors,
+                         igraph_vector_t *groups,
+                         igraph_bool_t use_arpack,
+                         igraph_integer_t maxiter,
+                         igraph_t *scg_graph,
+                         igraph_matrix_t *scg_matrix,
+                         igraph_sparsemat_t *scg_sparsemat,
+                         igraph_matrix_t *L,
+                         igraph_matrix_t *R,
+                         igraph_sparsemat_t *Lsparse,
+                         igraph_sparsemat_t *Rsparse);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_separators.h b/igraph/include/igraph_separators.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_separators.h
@@ -0,0 +1,53 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_SEPARATORS_H
+#define IGRAPH_SEPARATORS_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_is_separator(const igraph_t *graph,
+                                const igraph_vs_t candidate,
+                                igraph_bool_t *res);
+
+DECLDIR int igraph_all_minimal_st_separators(const igraph_t *graph,
+        igraph_vector_ptr_t *separators);
+
+DECLDIR int igraph_is_minimal_separator(const igraph_t *graph,
+                                        const igraph_vs_t candidate,
+                                        igraph_bool_t *res);
+
+DECLDIR int igraph_minimum_size_separators(const igraph_t *graph,
+        igraph_vector_ptr_t *separators);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_sparsemat.h b/igraph/include/igraph_sparsemat.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_sparsemat.h
@@ -0,0 +1,287 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_SPARSEMAT_H
+#define IGRAPH_SPARSEMAT_H
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_datatype.h"
+#include "igraph_arpack.h"
+
+#include <stdio.h>
+
+__BEGIN_DECLS
+
+struct cs_di_sparse;
+struct cs_di_symbolic;
+struct cs_di_numeric;
+
+typedef struct {
+    struct cs_di_sparse *cs;
+} igraph_sparsemat_t;
+
+typedef struct {
+    struct cs_di_symbolic *symbolic;
+} igraph_sparsemat_symbolic_t;
+
+typedef struct {
+    struct cs_di_numeric *numeric;
+} igraph_sparsemat_numeric_t;
+
+typedef enum { IGRAPH_SPARSEMAT_TRIPLET,
+               IGRAPH_SPARSEMAT_CC
+             } igraph_sparsemat_type_t;
+
+typedef struct {
+    igraph_sparsemat_t *mat;
+    int pos;
+    int col;
+} igraph_sparsemat_iterator_t;
+
+int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax);
+int igraph_sparsemat_copy(igraph_sparsemat_t *to,
+                          const igraph_sparsemat_t *from);
+void igraph_sparsemat_destroy(igraph_sparsemat_t *A);
+int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax);
+
+long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A);
+long int igraph_sparsemat_ncol(const igraph_sparsemat_t *B);
+igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A);
+igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A);
+igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A);
+
+int igraph_sparsemat_permute(const igraph_sparsemat_t *A,
+                             const igraph_vector_int_t *p,
+                             const igraph_vector_int_t *q,
+                             igraph_sparsemat_t *res);
+
+int igraph_sparsemat_index(const igraph_sparsemat_t *A,
+                           const igraph_vector_int_t *p,
+                           const igraph_vector_int_t *q,
+                           igraph_sparsemat_t *res,
+                           igraph_real_t *constres);
+
+int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col,
+                           igraph_real_t elem);
+int igraph_sparsemat_compress(const igraph_sparsemat_t *A,
+                              igraph_sparsemat_t *res);
+int igraph_sparsemat_transpose(const igraph_sparsemat_t *A,
+                               igraph_sparsemat_t *res, int values);
+igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A);
+int igraph_sparsemat_dupl(igraph_sparsemat_t *A);
+int igraph_sparsemat_fkeep(igraph_sparsemat_t *A,
+                           int (*fkeep)(int, int, igraph_real_t, void*),
+                           void *other);
+int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A);
+int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol);
+int igraph_sparsemat_multiply(const igraph_sparsemat_t *A,
+                              const igraph_sparsemat_t *B,
+                              igraph_sparsemat_t *res);
+int igraph_sparsemat_add(const igraph_sparsemat_t *A,
+                         const igraph_sparsemat_t *B,
+                         igraph_real_t alpha,
+                         igraph_real_t beta,
+                         igraph_sparsemat_t *res);
+int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A,
+                           const igraph_vector_t *x,
+                           igraph_vector_t *res);
+
+int igraph_sparsemat_lsolve(const igraph_sparsemat_t *A,
+                            const igraph_vector_t *b,
+                            igraph_vector_t *res);
+int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *A,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res);
+int igraph_sparsemat_usolve(const igraph_sparsemat_t *A,
+                            const igraph_vector_t *b,
+                            igraph_vector_t *res);
+int igraph_sparsemat_utsolve(const igraph_sparsemat_t *A,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res);
+
+int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res,
+                             int order);
+
+int igraph_sparsemat_lusol(const igraph_sparsemat_t *A,
+                           const igraph_vector_t *b,
+                           igraph_vector_t *res,
+                           int order,
+                           igraph_real_t tol);
+
+int igraph_sparsemat_print(const igraph_sparsemat_t *A,
+                           FILE *outstream);
+
+int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax,
+                         igraph_real_t value,
+                         igraph_bool_t compress);
+
+int igraph_sparsemat_diag(igraph_sparsemat_t *A, int nzmax,
+                          const igraph_vector_t *values,
+                          igraph_bool_t compress);
+
+int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A,
+                     igraph_bool_t directed);
+
+int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A,
+                              igraph_bool_t directed, const char *attr,
+                              igraph_bool_t loops);
+
+int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res);
+
+int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res,
+                               const igraph_matrix_t *mat,
+                               igraph_real_t tol);
+
+int igraph_sparsemat_as_matrix(igraph_matrix_t *res,
+                               const igraph_sparsemat_t *spmat);
+
+typedef enum { IGRAPH_SPARSEMAT_SOLVE_LU,
+               IGRAPH_SPARSEMAT_SOLVE_QR
+             } igraph_sparsemat_solve_t;
+
+int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A,
+                                    igraph_arpack_options_t *options,
+                                    igraph_arpack_storage_t *storage,
+                                    igraph_vector_t *values,
+                                    igraph_matrix_t *vectors,
+                                    igraph_sparsemat_solve_t solvemethod);
+
+int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A,
+                                    igraph_arpack_options_t *options,
+                                    igraph_arpack_storage_t *storage,
+                                    igraph_matrix_t *values,
+                                    igraph_matrix_t *vectors);
+
+int igraph_sparsemat_lu(const igraph_sparsemat_t *A,
+                        const igraph_sparsemat_symbolic_t *dis,
+                        igraph_sparsemat_numeric_t *din, double tol);
+
+int igraph_sparsemat_qr(const igraph_sparsemat_t *A,
+                        const igraph_sparsemat_symbolic_t *dis,
+                        igraph_sparsemat_numeric_t *din);
+
+int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis,
+                             const igraph_sparsemat_numeric_t *din,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res);
+
+int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis,
+                             const igraph_sparsemat_numeric_t *din,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res);
+
+int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A,
+                            igraph_sparsemat_symbolic_t *dis);
+
+int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A,
+                            igraph_sparsemat_symbolic_t *dis);
+
+
+void igraph_sparsemat_symbolic_destroy(igraph_sparsemat_symbolic_t *dis);
+void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din);
+
+igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A);
+igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A);
+int igraph_sparsemat_minmax(igraph_sparsemat_t *A,
+                            igraph_real_t *min, igraph_real_t *max);
+
+long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A);
+long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A,
+        igraph_real_t tol);
+int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A,
+                             igraph_vector_t *res);
+int igraph_sparsemat_colsums(const igraph_sparsemat_t *A,
+                             igraph_vector_t *res);
+
+int igraph_sparsemat_rowmins(igraph_sparsemat_t *A,
+                             igraph_vector_t *res);
+int igraph_sparsemat_colmins(igraph_sparsemat_t *A,
+                             igraph_vector_t *res);
+
+int igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A,
+                             igraph_vector_t *res);
+int igraph_sparsemat_colmaxs(igraph_sparsemat_t *A,
+                             igraph_vector_t *res);
+
+int igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A,
+                                    igraph_vector_t *res,
+                                    igraph_vector_int_t *pos);
+int igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A,
+                                    igraph_vector_t *res,
+                                    igraph_vector_int_t *pos);
+
+int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by);
+
+
+int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n);
+int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n);
+int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow,
+                            long int ncol, int nzmax);
+int igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A);
+int igraph_sparsemat_getelements(const igraph_sparsemat_t *A,
+                                 igraph_vector_int_t *i,
+                                 igraph_vector_int_t *j,
+                                 igraph_vector_t *x);
+int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A,
+                                        igraph_vector_int_t *i,
+                                        igraph_vector_int_t *j,
+                                        igraph_vector_t *x);
+int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A,
+                                const igraph_vector_t *fact);
+int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A,
+                                const igraph_vector_t *fact);
+int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A,
+                                       const igraph_matrix_t *B,
+                                       igraph_matrix_t *res);
+int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A,
+                                    const igraph_sparsemat_t *B,
+                                    igraph_matrix_t *res);
+
+int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n,
+                            int *p, int *i, double *x, int nz);
+
+int igraph_sparsemat_sort(const igraph_sparsemat_t *A,
+                          igraph_sparsemat_t *sorted);
+
+int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A);
+
+int igraph_sparsemat_neg(igraph_sparsemat_t *A);
+
+int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it,
+                                   igraph_sparsemat_t *sparsemat);
+int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it);
+igraph_bool_t
+igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it);
+int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it);
+int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it);
+int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it);
+igraph_real_t
+igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it);
+int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_spmatrix.h b/igraph/include/igraph_spmatrix.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_spmatrix.h
@@ -0,0 +1,114 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_SPMATRIX_H
+#define IGRAPH_SPMATRIX_H
+
+#include "igraph_decls.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Sparse matrix                                      */
+/* -------------------------------------------------- */
+
+/**
+ * \section about_igraph_spmatrix_t_objects About \type igraph_spmatrix_t objects
+ *
+ * <para>The \type igraph_spmatrix_t type stores a sparse matrix with the
+ * assumption that the number of nonzero elements in the matrix scales
+ * linearly with the row or column count of the matrix (so most of the
+ * elements are zero). Of course it can store an arbitrary real matrix,
+ * but if most of the elements are nonzero, one should use \type igraph_matrix_t
+ * instead.</para>
+ *
+ * <para>The elements are stored in column compressed format, so the elements
+ * in the same column are stored adjacent in the computer's memory. The storage
+ * requirement for a sparse matrix is O(n) where n is the number of nonzero
+ * elements. Actually it can be a bit larger, see the documentation of
+ * the vector type for an explanation.</para>
+ */
+typedef struct s_spmatrix {
+    igraph_vector_t ridx, cidx, data;
+    long int nrow, ncol;
+} igraph_spmatrix_t;
+
+#define IGRAPH_SPMATRIX_INIT_FINALLY(m, nr, nc) \
+    do { IGRAPH_CHECK(igraph_spmatrix_init(m, nr, nc)); \
+        IGRAPH_FINALLY(igraph_spmatrix_destroy, m); } while (0)
+
+DECLDIR int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol);
+DECLDIR void igraph_spmatrix_destroy(igraph_spmatrix_t *m);
+DECLDIR int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol);
+DECLDIR igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, long int row, long int col);
+DECLDIR int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col,
+                                igraph_real_t value);
+DECLDIR int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col,
+                                  igraph_real_t value);
+DECLDIR int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from);
+DECLDIR long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m);
+DECLDIR long int igraph_spmatrix_size(const igraph_spmatrix_t *m);
+DECLDIR long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m);
+DECLDIR long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m);
+DECLDIR int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to);
+DECLDIR int igraph_spmatrix_null(igraph_spmatrix_t *m);
+DECLDIR int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n);
+DECLDIR int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n);
+DECLDIR int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col);
+DECLDIR int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row);
+DECLDIR int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from);
+DECLDIR igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m,
+        igraph_real_t *ridx, igraph_real_t *cidx);
+DECLDIR igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m,
+        igraph_real_t *ridx, igraph_real_t *cidx);
+DECLDIR void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by);
+DECLDIR int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res);
+DECLDIR int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res);
+
+DECLDIR int igraph_spmatrix_print(const igraph_spmatrix_t *matrix);
+DECLDIR int igraph_spmatrix_fprint(const igraph_spmatrix_t *matrix, FILE* file);
+
+DECLDIR int igraph_i_spmatrix_get_col_nonzero_indices(const igraph_spmatrix_t *m,
+        igraph_vector_t *res, long int col);
+DECLDIR int igraph_i_spmatrix_clear_row_fast(igraph_spmatrix_t *m, long int row);
+DECLDIR int igraph_i_spmatrix_cleanup(igraph_spmatrix_t *m);
+
+
+typedef struct s_spmatrix_iter {
+    const igraph_spmatrix_t *m; /* pointer to the matrix we are iterating over */
+    long int pos;               /* internal index into the data vector */
+    long int ri;                /* row index */
+    long int ci;                /* column index */
+    igraph_real_t value;        /* value at the given cell */
+} igraph_spmatrix_iter_t;
+
+DECLDIR int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m);
+DECLDIR int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit);
+DECLDIR int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit);
+DECLDIR igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit);
+DECLDIR void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_stack.h b/igraph/include/igraph_stack.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_stack.h
@@ -0,0 +1,79 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_STACK_H
+#define IGRAPH_STACK_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Plain stack                                        */
+/* -------------------------------------------------- */
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "igraph_stack_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "igraph_stack_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "igraph_stack_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "igraph_stack_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "igraph_stack_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_PTR
+#include "igraph_pmt.h"
+#include "igraph_stack_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_PTR
+
+#define IGRAPH_STACK_NULL { 0,0,0 }
+
+void igraph_stack_ptr_free_all(igraph_stack_ptr_t* s);
+void igraph_stack_ptr_destroy_all(igraph_stack_ptr_t* s);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_stack_pmt.h b/igraph/include/igraph_stack_pmt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_stack_pmt.h
@@ -0,0 +1,47 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include <stdio.h>
+
+/**
+ * Stack data type.
+ * \ingroup internal
+ */
+
+typedef struct TYPE(igraph_stack) {
+    BASE* stor_begin;
+    BASE* stor_end;
+    BASE* end;
+} TYPE(igraph_stack);
+
+DECLDIR int FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, long int size);
+DECLDIR void FUNCTION(igraph_stack, destroy)(TYPE(igraph_stack)* s);
+DECLDIR int FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, long int size);
+DECLDIR igraph_bool_t FUNCTION(igraph_stack, empty)(TYPE(igraph_stack)* s);
+DECLDIR long int FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s);
+DECLDIR void FUNCTION(igraph_stack, clear)(TYPE(igraph_stack)* s);
+DECLDIR int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem);
+DECLDIR BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s);
+DECLDIR BASE FUNCTION(igraph_stack, top)(const TYPE(igraph_stack)* s);
+DECLDIR int FUNCTION(igraph_stack, print)(const TYPE(igraph_stack)* s);
+DECLDIR int FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack)* s, FILE *file);
diff --git a/igraph/include/igraph_statusbar.h b/igraph/include/igraph_statusbar.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_statusbar.h
@@ -0,0 +1,126 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_STATUSBAR
+#define IGRAPH_STATUSBAR
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/**
+ * \section about_status_handlers Status reporting
+ *
+ * <para>
+ * In addition to the possibility of reporting the progress of an
+ * igraph computation via \ref igraph_progress(), it is also possible
+ * to report simple status messages from within igraph functions,
+ * without having to judge how much of the computation was performed
+ * already. For this one needs to install a status handler function.
+ * </para>
+ *
+ * <para>
+ * Status handler functions must be of type \ref igraph_status_handler_t
+ * and they can be install by a call to \ref igraph_set_status_handler().
+ * Currently there is a simple predefined status handler function,
+ * called \ref igraph_status_handler_stderr(), but the user can define
+ * new ones.
+ * </para>
+ *
+ * <para>
+ * Igraph functions report their status via a call to the
+ * \ref IGRAPH_STATUS() or the \ref IGRAPH_STATUSF() macro.
+ * </para>
+ */
+
+/**
+ * \typedef igraph_status_handler_t
+ *
+ * The type of the igraph status handler functions
+ * \param message The status message.
+ * \param data Additional context, with user-defined semantics.
+ *        Existing igraph functions pass a null pointer here.
+ */
+
+typedef int igraph_status_handler_t(const char *message, void *data);
+
+extern igraph_status_handler_t igraph_status_handler_stderr;
+
+DECLDIR igraph_status_handler_t * igraph_set_status_handler(igraph_status_handler_t new_handler);
+
+DECLDIR int igraph_status(const char *message, void *data);
+
+/**
+ * \define IGRAPH_STATUS
+ * Report the status of an igraph function.
+ *
+ * Typically this function is called only a handful of times from
+ * an igraph function. E.g. if an algorithm has three major
+ * steps, then it is logical to call it three times, to
+ * signal the three major steps.
+ * \param message The status message.
+ * \param data Additional context, with user-defined semantics.
+ *        Existing igraph functions pass a null pointer here.
+ * \return If the status handler returns with a value other than
+ *        \c IGRAPH_SUCCESS, then the function that called this
+ *        macro returns as well, with error code
+ *        \c IGRAPH_INTERRUPTED.
+ */
+
+#define IGRAPH_STATUS(message, data) \
+    do { \
+        if (igraph_status((message), (data)) != IGRAPH_SUCCESS) { \
+            IGRAPH_FINALLY_FREE(); \
+            return IGRAPH_INTERRUPTED; \
+        } \
+    } while (0)
+
+DECLDIR int igraph_statusf(const char *message, void *data, ...);
+
+/**
+ * \define IGRAPH_STATUSF
+ * Report the status from an igraph function
+ *
+ * This is the more flexible version of \ref IGRAPH_STATUS(),
+ * having a printf-like syntax. As this macro takes variable
+ * number of arguments, they must be all supplied as a single
+ * argument, enclosed in parentheses. Then \ref igraph_statusf()
+ * is called with the given arguments.
+ * \param args The arguments to pass to \ref igraph_statusf().
+ * \return If the status handler returns with a value other than
+ *        \c IGRAPH_SUCCESS, then the function that called this
+ *        macro returns as well, with error code
+ *        \c IGRAPH_INTERRUPTED.
+ */
+
+#define IGRAPH_STATUSF(args) \
+    do { \
+        if (igraph_statusf args != IGRAPH_SUCCESS) { \
+            IGRAPH_FINALLY_FREE(); \
+            return IGRAPH_INTERRUPTED; \
+        } \
+    } while (0)
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_structural.h b/igraph/include/igraph_structural.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_structural.h
@@ -0,0 +1,151 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_STRUCTURAL_H
+#define IGRAPH_STRUCTURAL_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+#include "igraph_attributes.h"
+#include "igraph_sparsemat.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Basic query functions                              */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res);
+
+/* -------------------------------------------------- */
+/* Structural properties                              */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_t *res,
+        const igraph_vector_t *weights);
+DECLDIR int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph,
+        igraph_t *mst);
+DECLDIR int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst,
+        const igraph_vector_t *weights);
+DECLDIR int igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_t *res,
+                                        igraph_integer_t vid);
+
+DECLDIR int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vid,
+                                igraph_neimode_t mode);
+DECLDIR int igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode);
+DECLDIR int igraph_subgraph(const igraph_t *graph, igraph_t *res,
+                            const igraph_vs_t vids);
+DECLDIR int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res,
+                                        const igraph_vs_t vids,
+                                        igraph_subgraph_implementation_t impl,
+                                        igraph_vector_t *map,
+                                        igraph_vector_t *invmap);
+DECLDIR int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res,
+                                    const igraph_vs_t vids, igraph_subgraph_implementation_t impl);
+DECLDIR int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res,
+                                  const igraph_es_t eids, igraph_bool_t delete_vertices);
+DECLDIR int igraph_simplify(igraph_t *graph, igraph_bool_t multiple,
+                            igraph_bool_t loops,
+                            const igraph_attribute_combination_t *edge_comb);
+DECLDIR int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res,
+                               igraph_bool_t ignore_loops,
+                               igraph_reciprocity_t mode);
+
+DECLDIR int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res,
+                             igraph_vs_t vids, igraph_neimode_t mode,
+                             igraph_bool_t loops);
+DECLDIR int igraph_density(const igraph_t *graph, igraph_real_t *res,
+                           igraph_bool_t loops);
+
+DECLDIR int igraph_has_loop(const igraph_t *graph, igraph_bool_t *res);
+DECLDIR int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res,
+                           igraph_es_t es);
+DECLDIR int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res);
+DECLDIR int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res);
+DECLDIR int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res,
+                               igraph_es_t es);
+DECLDIR int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es);
+DECLDIR int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode);
+DECLDIR int igraph_girth(const igraph_t *graph, igraph_integer_t *girth,
+                         igraph_vector_t *circle);
+DECLDIR int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to);
+
+DECLDIR int igraph_unfold_tree(const igraph_t *graph, igraph_t *tree,
+                               igraph_neimode_t mode, const igraph_vector_t *roots,
+                               igraph_vector_t *vertex_index);
+
+DECLDIR int igraph_is_mutual(igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es);
+
+DECLDIR int igraph_maximum_cardinality_search(const igraph_t *graph,
+        igraph_vector_t *alpha,
+        igraph_vector_t *alpham1);
+DECLDIR int igraph_is_chordal(const igraph_t *graph,
+                              const igraph_vector_t *alpha,
+                              const igraph_vector_t *alpham1,
+                              igraph_bool_t *chordal,
+                              igraph_vector_t *fill_in,
+                              igraph_t *newgraph);
+DECLDIR int igraph_avg_nearest_neighbor_degree(const igraph_t *graph,
+        igraph_vs_t vids,
+        igraph_neimode_t mode,
+        igraph_neimode_t neighbor_degree_mode,
+        igraph_vector_t *knn,
+        igraph_vector_t *knnk,
+        const igraph_vector_t *weights);
+DECLDIR int igraph_contract_vertices(igraph_t *graph,
+                                     const igraph_vector_t *mapping,
+                                     const igraph_attribute_combination_t
+                                     *vertex_comb);
+
+DECLDIR int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result,
+                                    const igraph_vector_t *weights, igraph_fas_algorithm_t algo);
+
+DECLDIR int igraph_diversity(igraph_t *graph, const igraph_vector_t *weights,
+                             igraph_vector_t *res, const igraph_vs_t vs);
+
+/* -------------------------------------------------- */
+/* Spectral Properties                                */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_laplacian(const igraph_t *graph, igraph_matrix_t *res,
+                             igraph_sparsemat_t *sparseres,
+                             igraph_bool_t normalized,
+                             const igraph_vector_t *weights);
+
+/* -------------------------------------------------- */
+/* Internal functions, may change any time            */
+/* -------------------------------------------------- */
+
+int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t *result,
+        const igraph_vector_t *weights, igraph_vector_t *layering);
+int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *result,
+                                    const igraph_vector_t *weights, igraph_vector_t *layering);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_strvector.h b/igraph/include/igraph_strvector.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_strvector.h
@@ -0,0 +1,97 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_STRVECTOR_H
+#define IGRAPH_STRVECTOR_H
+
+#include "igraph_decls.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+/**
+ * Vector of strings
+ * \ingroup internal
+ */
+
+typedef struct s_igraph_strvector {
+    char **data;
+    long int len;
+} igraph_strvector_t;
+
+/**
+ * \define STR
+ * Indexing string vectors
+ *
+ * This is a macro which allows to query the elements of a string vector in
+ * simpler way than \ref igraph_strvector_get(). Note this macro cannot be
+ * used to set an element, for that use \ref igraph_strvector_set().
+ * \param sv The string vector
+ * \param i The the index of the element.
+ * \return The element at position \p i.
+ *
+ * Time complexity: O(1).
+ */
+#define STR(sv,i) ((const char *)((sv).data[(i)]))
+
+#define IGRAPH_STRVECTOR_NULL { 0,0 }
+#define IGRAPH_STRVECTOR_INIT_FINALLY(v, size) \
+    do { IGRAPH_CHECK(igraph_strvector_init(v, size)); \
+        IGRAPH_FINALLY( (igraph_finally_func_t*) igraph_strvector_destroy, v); } while (0)
+
+DECLDIR int igraph_strvector_init(igraph_strvector_t *sv, long int len);
+DECLDIR void igraph_strvector_destroy(igraph_strvector_t *sv);
+DECLDIR long int igraph_strvector_size(const igraph_strvector_t *sv);
+DECLDIR void igraph_strvector_get(const igraph_strvector_t *sv,
+                                  long int idx, char **value);
+DECLDIR int igraph_strvector_set(igraph_strvector_t *sv, long int idx,
+                                 const char *value);
+DECLDIR int igraph_strvector_set2(igraph_strvector_t *sv, long int idx,
+                                  const char *value, int len);
+DECLDIR void igraph_strvector_clear(igraph_strvector_t *sv);
+DECLDIR void igraph_strvector_remove_section(igraph_strvector_t *v, long int from,
+        long int to);
+DECLDIR void igraph_strvector_remove(igraph_strvector_t *v, long int elem);
+DECLDIR void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin,
+        long int end, long int to);
+DECLDIR int igraph_strvector_copy(igraph_strvector_t *to,
+                                  const igraph_strvector_t *from);
+DECLDIR int igraph_strvector_append(igraph_strvector_t *to,
+                                    const igraph_strvector_t *from);
+DECLDIR int igraph_strvector_resize(igraph_strvector_t* v, long int newsize);
+DECLDIR int igraph_strvector_add(igraph_strvector_t *v, const char *value);
+DECLDIR void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index,
+        long int nremove);
+DECLDIR void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg,
+        long int nremove);
+DECLDIR int igraph_strvector_print(const igraph_strvector_t *v, FILE *file,
+                                   const char *sep);
+
+DECLDIR int igraph_strvector_index(const igraph_strvector_t *v,
+                                   igraph_strvector_t *newv,
+                                   const igraph_vector_t *idx);
+
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_threading.h b/igraph/include/igraph_threading.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_threading.h
@@ -0,0 +1,43 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_THREADING_H
+#define IGRAPH_THREADING_H
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+/**
+ * \define IGRAPH_THREAD_SAFE
+ *
+ * Macro that is defined to be 1 if the current build of the
+ * igraph library is thread-safe, and 0 if it is not.
+ */
+
+#define IGRAPH_THREAD_SAFE 0
+
+__END_DECLS
+
+#endif
+
diff --git a/igraph/include/igraph_topology.h b/igraph/include/igraph_topology.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_topology.h
@@ -0,0 +1,292 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_TOPOLOGY_H
+#define IGRAPH_TOPOLOGY_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_datatype.h"
+#include "igraph_types.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Degree sequences                                   */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_is_degree_sequence(const igraph_vector_t *out_degrees,
+                                      const igraph_vector_t *in_degrees, igraph_bool_t *res);
+DECLDIR int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees,
+        const igraph_vector_t *in_degrees, igraph_bool_t *res);
+
+/* -------------------------------------------------- */
+/* Directed acyclic graphs                            */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_topological_sorting(const igraph_t *graph, igraph_vector_t *res,
+                                       igraph_neimode_t mode);
+DECLDIR int igraph_is_dag(const igraph_t *graph, igraph_bool_t *res);
+DECLDIR int igraph_transitive_closure_dag(const igraph_t *graph,
+        igraph_t *closure);
+
+/* -------------------------------------------------- */
+/* Graph isomorphisms                                 */
+/* -------------------------------------------------- */
+
+/* Common functions */
+DECLDIR int igraph_permute_vertices(const igraph_t *graph, igraph_t *res,
+                                    const igraph_vector_t *permutation);
+
+DECLDIR int igraph_simplify_and_colorize(
+    const igraph_t *graph, igraph_t *res,
+    igraph_vector_int_t *vertex_color, igraph_vector_int_t *edge_color);
+
+/* Generic interface */
+DECLDIR int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2,
+                              igraph_bool_t *iso);
+DECLDIR int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2,
+                                 igraph_bool_t *iso);
+
+/* LAD */
+DECLDIR int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target,
+                                     igraph_vector_ptr_t *domains,
+                                     igraph_bool_t *iso, igraph_vector_t *map,
+                                     igraph_vector_ptr_t *maps,
+                                     igraph_bool_t induced, int time_limit);
+
+/* VF2 family*/
+/**
+ * \typedef igraph_isohandler_t
+ * Callback type, called when an isomorphism was found
+ *
+ * See the details at the documentation of \ref
+ * igraph_isomorphic_function_vf2().
+ * \param map12 The mapping from the first graph to the second.
+ * \param map21 The mapping from the second graph to the first, the
+ *   inverse of \p map12 basically.
+ * \param arg This extra argument was passed to \ref
+ *   igraph_isomorphic_function_vf2() when it was called.
+ * \return Boolean, whether to continue with the isomorphism search.
+ */
+
+
+typedef igraph_bool_t igraph_isohandler_t(const igraph_vector_t *map12,
+        const igraph_vector_t *map21, void *arg);
+
+/**
+ * \typedef igraph_isocompat_t
+ * Callback type, called to check whether two vertices or edges are compatible
+ *
+ * VF2 (subgraph) isomorphism functions can be restricted by defining
+ * relations on the vertices and/or edges of the graphs, and then checking
+ * whether the vertices (edges) match according to these relations.
+ *
+ * </para><para>This feature is implemented by two callbacks, one for
+ * vertices, one for edges. Every time igraph tries to match a vertex (edge)
+ * of the first (sub)graph to a vertex of the second graph, the vertex
+ * (edge) compatibility callback is called. The callback returns a
+ * logical value, giving whether the two vertices match.
+ *
+ * </para><para>Both callback functions are of type \c igraph_isocompat_t.
+ * \param graph1 The first graph.
+ * \param graph2 The second graph.
+ * \param g1_num The id of a vertex or edge in the first graph.
+ * \param g2_num The id of a vertex or edge in the second graph.
+ * \param arg Extra argument to pass to the callback functions.
+ * \return Logical scalar, whether vertex (or edge) \p g1_num in \p graph1
+ *    is compatible with vertex (or edge) \p g2_num in \p graph2.
+ */
+
+typedef igraph_bool_t igraph_isocompat_t(const igraph_t *graph1,
+        const igraph_t *graph2,
+        const igraph_integer_t g1_num,
+        const igraph_integer_t g2_num,
+        void *arg);
+
+DECLDIR int igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2,
+                                  const igraph_vector_int_t *vertex_color1,
+                                  const igraph_vector_int_t *vertex_color2,
+                                  const igraph_vector_int_t *edge_color1,
+                                  const igraph_vector_int_t *edge_color2,
+                                  igraph_bool_t *iso,
+                                  igraph_vector_t *map12,
+                                  igraph_vector_t *map21,
+                                  igraph_isocompat_t *node_compat_fn,
+                                  igraph_isocompat_t *edge_compat_fn,
+                                  void *arg);
+DECLDIR int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph2,
+        const igraph_vector_int_t *vertex_color1,
+        const igraph_vector_int_t *vertex_color2,
+        const igraph_vector_int_t *edge_color1,
+        const igraph_vector_int_t *edge_color2,
+        igraph_vector_t *map12, igraph_vector_t *map21,
+        igraph_isohandler_t *isohandler_fn,
+        igraph_isocompat_t *node_compat_fn,
+        igraph_isocompat_t *edge_compat_fn,
+        void *arg);
+DECLDIR int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2,
+        const igraph_vector_int_t *vertex_color1,
+        const igraph_vector_int_t *vertex_color2,
+        const igraph_vector_int_t *edge_color1,
+        const igraph_vector_int_t *edge_color2,
+        igraph_integer_t *count,
+        igraph_isocompat_t *node_compat_fn,
+        igraph_isocompat_t *edge_compat_fn,
+        void *arg);
+DECLDIR int igraph_get_isomorphisms_vf2(const igraph_t *graph1,
+                                        const igraph_t *graph2,
+                                        const igraph_vector_int_t *vertex_color1,
+                                        const igraph_vector_int_t *vertex_color2,
+                                        const igraph_vector_int_t *edge_color1,
+                                        const igraph_vector_int_t *edge_color2,
+                                        igraph_vector_ptr_t *maps,
+                                        igraph_isocompat_t *node_compat_fn,
+                                        igraph_isocompat_t *edge_compat_fn,
+                                        void *arg);
+
+DECLDIR int igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2,
+                                     const igraph_vector_int_t *vertex_color1,
+                                     const igraph_vector_int_t *vertex_color2,
+                                     const igraph_vector_int_t *edge_color1,
+                                     const igraph_vector_int_t *edge_color2,
+                                     igraph_bool_t *iso,
+                                     igraph_vector_t *map12,
+                                     igraph_vector_t *map21,
+                                     igraph_isocompat_t *node_compat_fn,
+                                     igraph_isocompat_t *edge_compat_fn,
+                                     void *arg);
+DECLDIR int igraph_subisomorphic_function_vf2(const igraph_t *graph1,
+        const igraph_t *graph2,
+        const igraph_vector_int_t *vertex_color1,
+        const igraph_vector_int_t *vertex_color2,
+        const igraph_vector_int_t *edge_color1,
+        const igraph_vector_int_t *edge_color2,
+        igraph_vector_t *map12,
+        igraph_vector_t *map21,
+        igraph_isohandler_t *isohandler_fn,
+        igraph_isocompat_t *node_compat_fn,
+        igraph_isocompat_t *edge_compat_fn,
+        void *arg);
+DECLDIR int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2,
+        const igraph_vector_int_t *vertex_color1,
+        const igraph_vector_int_t *vertex_color2,
+        const igraph_vector_int_t *edge_color1,
+        const igraph_vector_int_t *edge_color2,
+        igraph_integer_t *count,
+        igraph_isocompat_t *node_compat_fn,
+        igraph_isocompat_t *edge_compat_fn,
+        void *arg);
+DECLDIR int igraph_get_subisomorphisms_vf2(const igraph_t *graph1,
+        const igraph_t *graph2,
+        const igraph_vector_int_t *vertex_color1,
+        const igraph_vector_int_t *vertex_color2,
+        const igraph_vector_int_t *edge_color1,
+        const igraph_vector_int_t *edge_color2,
+        igraph_vector_ptr_t *maps,
+        igraph_isocompat_t *node_compat_fn,
+        igraph_isocompat_t *edge_compat_fn,
+        void *arg);
+
+/* BLISS family */
+/**
+ * \struct igraph_bliss_info_t
+ * Information about a BLISS run
+ *
+ * Some secondary information found by the BLISS algorithm is stored
+ * here. It is useful if you wany to study the internal working of the
+ * algorithm.
+ * \member nof_nodes The number of nodes in the search tree.
+ * \member nof_leaf_nodes The number of leaf nodes in the search tree.
+ * \member nof_bad_nodes Number of bad nodes.
+ * \member nof_canupdates Number of canrep updates.
+ * \member nof_generators Number of generators of the automorphism group.
+ * \member max_level Maximum level.
+ * \member group_size The size of the automorphism group of the graph,
+ *    given as a string. It should be deallocated via
+ *    \ref igraph_free() if not needed any more.
+ *
+ * See http://www.tcs.hut.fi/Software/bliss/index.html
+ * for details about the algorithm and these parameters.
+ */
+typedef struct igraph_bliss_info_t {
+    unsigned long nof_nodes;
+    unsigned long nof_leaf_nodes;
+    unsigned long nof_bad_nodes;
+    unsigned long nof_canupdates;
+    unsigned long nof_generators;
+    unsigned long max_level;
+    char *group_size;
+} igraph_bliss_info_t;
+
+/**
+ * \typedef igraph_bliss_sh_t
+ * Splitting heuristics for BLISS
+ *
+ * \enumval IGRAPH_BLISS_F First non-singleton cell.
+ * \enumval IGRAPH_BLISS_FL First largest non-singleton cell.
+ * \enumval IGRAPH_BLISS_FS First smallest non-singleton cell.
+ * \enumval IGRAPH_BLISS_FM First maximally non-trivially connected
+ *      non-singleton cell.
+ * \enumval IGRAPH_BLISS_FLM Largest maximally non-trivially connected
+ *      non-singleton cell.
+ * \enumval IGRAPH_BLISS_FSM Smallest maximally non-trivially
+ *      connected non-singletion cell.
+ */
+
+typedef enum { IGRAPH_BLISS_F = 0, IGRAPH_BLISS_FL,
+               IGRAPH_BLISS_FS, IGRAPH_BLISS_FM,
+               IGRAPH_BLISS_FLM, IGRAPH_BLISS_FSM
+             } igraph_bliss_sh_t;
+
+DECLDIR int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_t *labeling,
+        igraph_bliss_sh_t sh, igraph_bliss_info_t *info);
+DECLDIR int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2,
+                                    const igraph_vector_int_t *colors1, const igraph_vector_int_t *colors2,
+                                    igraph_bool_t *iso, igraph_vector_t *map12,
+                                    igraph_vector_t *map21,
+                                    igraph_bliss_sh_t sh,
+                                    igraph_bliss_info_t *info1, igraph_bliss_info_t *info2);
+
+DECLDIR int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors,
+                                 igraph_bliss_sh_t sh, igraph_bliss_info_t *info);
+
+DECLDIR int igraph_automorphism_group(const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_ptr_t *generators,
+                                      igraph_bliss_sh_t sh, igraph_bliss_info_t *info);
+
+/* Functions for 3-4 graphs */
+DECLDIR int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2,
+                                 igraph_bool_t *iso);
+DECLDIR int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass);
+DECLDIR int igraph_isoclass_subgraph(const igraph_t *graph, igraph_vector_t *vids,
+                                     igraph_integer_t *isoclass);
+DECLDIR int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size,
+                                   igraph_integer_t number, igraph_bool_t directed);
+
+
+
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_transitivity.h b/igraph/include/igraph_transitivity.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_transitivity.h
@@ -0,0 +1,64 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_TRANSITIVITY_H
+#define IGRAPH_TRANSITIVITY_H
+
+#include "igraph_decls.h"
+#include "igraph_datatype.h"
+#include "igraph_constants.h"
+#include "igraph_iterators.h"
+
+__BEGIN_DECLS
+
+DECLDIR int igraph_transitivity_undirected(const igraph_t *graph,
+        igraph_real_t *res,
+        igraph_transitivity_mode_t mode);
+DECLDIR int igraph_transitivity_local_undirected(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_transitivity_mode_t mode);
+DECLDIR int igraph_transitivity_local_undirected1(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_transitivity_mode_t mode);
+DECLDIR int igraph_transitivity_local_undirected2(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_transitivity_mode_t mode);
+DECLDIR int igraph_transitivity_local_undirected4(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_transitivity_mode_t mode);
+DECLDIR int igraph_transitivity_avglocal_undirected(const igraph_t *graph,
+        igraph_real_t *res,
+        igraph_transitivity_mode_t mode);
+DECLDIR int igraph_transitivity_barrat(const igraph_t *graph,
+                                       igraph_vector_t *res,
+                                       const igraph_vs_t vids,
+                                       const igraph_vector_t *weights,
+                                       const igraph_transitivity_mode_t mode);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_types.h b/igraph/include/igraph_types.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_types.h
@@ -0,0 +1,91 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef REST_TYPES_H
+#define REST_TYPES_H
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+#ifndef _GNU_SOURCE
+    #define _GNU_SOURCE 1
+#endif
+
+#include "igraph_error.h"
+#include <stddef.h>
+#include <math.h>
+#include <stdio.h>
+
+/* This is to eliminate gcc warnings about unused parameters */
+#define IGRAPH_UNUSED(x) (void)(x)
+
+typedef int    igraph_integer_t;
+typedef double igraph_real_t;
+typedef int    igraph_bool_t;
+
+/* Replacements for printf that print doubles in the same way on all platforms
+ * (even for NaN and infinities) */
+DECLDIR int igraph_real_printf(igraph_real_t val);
+DECLDIR int igraph_real_fprintf(FILE *file, igraph_real_t val);
+DECLDIR int igraph_real_snprintf(char* str, size_t size, igraph_real_t val);
+
+/* Replacements for printf that print doubles in the same way on all platforms
+ * (even for NaN and infinities) with the largest possible precision */
+DECLDIR int igraph_real_printf_precise(igraph_real_t val);
+DECLDIR int igraph_real_fprintf_precise(FILE *file, igraph_real_t val);
+DECLDIR int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val);
+
+/* igraph_i_fdiv is needed here instead of in igraph_math.h because
+ * some constants use it */
+double igraph_i_fdiv(const double a, const double b);
+
+#if defined(INFINITY)
+    #define IGRAPH_INFINITY INFINITY
+    #define IGRAPH_POSINFINITY INFINITY
+    #define IGRAPH_NEGINFINITY (-INFINITY)
+#else
+    #define IGRAPH_INFINITY (igraph_i_fdiv(1.0, 0.0))
+    #define IGRAPH_POSINFINITY (igraph_i_fdiv(1.0, 0.0))
+    #define IGRAPH_NEGINFINITY (igraph_i_fdiv(-1.0, 0.0))
+#endif
+
+DECLDIR int igraph_finite(double x);
+#define IGRAPH_FINITE(x) igraph_finite(x)
+
+DECLDIR int igraph_is_nan(double x);
+DECLDIR int igraph_is_inf(double x);
+DECLDIR int igraph_is_posinf(double x);
+DECLDIR int igraph_is_neginf(double x);
+
+#if defined(NAN)
+    #define IGRAPH_NAN NAN
+#elif defined(INFINITY)
+    #define IGRAPH_NAN (INFINITY/INFINITY)
+#else
+    #define IGRAPH_NAN (igraph_i_fdiv(0.0, 0.0))
+#endif
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_types_internal.h b/igraph/include/igraph_types_internal.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_types_internal.h
@@ -0,0 +1,395 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_TYPES_INTERNAL_H
+#define IGRAPH_TYPES_INTERNAL_H
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+#include "igraph_types.h"
+#include "igraph_matrix.h"
+#include "igraph_stack.h"
+#include "igraph_strvector.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Indexed heap                                       */
+/* -------------------------------------------------- */
+
+/**
+ * Indexed heap data type.
+ * \ingroup internal
+ */
+
+typedef struct s_indheap {
+    igraph_real_t* stor_begin;
+    igraph_real_t* stor_end;
+    igraph_real_t* end;
+    int destroy;
+    long int* index_begin;
+} igraph_indheap_t;
+
+#define IGRAPH_INDHEAP_NULL { 0,0,0,0,0 }
+
+int igraph_indheap_init           (igraph_indheap_t* h, long int size);
+int igraph_indheap_init_array     (igraph_indheap_t *t, igraph_real_t* data, long int len);
+void igraph_indheap_destroy        (igraph_indheap_t* h);
+int igraph_indheap_clear(igraph_indheap_t *h);
+igraph_bool_t igraph_indheap_empty          (igraph_indheap_t* h);
+int igraph_indheap_push           (igraph_indheap_t* h, igraph_real_t elem);
+int igraph_indheap_push_with_index(igraph_indheap_t* h, long int idx, igraph_real_t elem);
+int igraph_indheap_modify(igraph_indheap_t* h, long int idx, igraph_real_t elem);
+igraph_real_t igraph_indheap_max       (igraph_indheap_t* h);
+igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h);
+long int igraph_indheap_size      (igraph_indheap_t* h);
+int igraph_indheap_reserve        (igraph_indheap_t* h, long int size);
+long int igraph_indheap_max_index(igraph_indheap_t *h);
+
+void igraph_indheap_i_build(igraph_indheap_t* h, long int head);
+void igraph_indheap_i_shift_up(igraph_indheap_t* h, long int elem);
+void igraph_indheap_i_sink(igraph_indheap_t* h, long int head);
+void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e2);
+
+/* -------------------------------------------------- */
+/* Doubly indexed heap                                */
+/* -------------------------------------------------- */
+
+/* This is a heap containing double elements and
+   two indices, its intended usage is the storage of
+   weighted edges.
+*/
+
+/**
+ * Doubly indexed heap data type.
+ * \ingroup internal
+ */
+
+typedef struct s_indheap_d {
+    igraph_real_t* stor_begin;
+    igraph_real_t* stor_end;
+    igraph_real_t* end;
+    int destroy;
+    long int* index_begin;
+    long int* index2_begin;
+} igraph_d_indheap_t;
+
+
+#define IGRAPH_D_INDHEAP_NULL { 0,0,0,0,0,0 }
+
+int igraph_d_indheap_init           (igraph_d_indheap_t* h, long int size);
+void igraph_d_indheap_destroy        (igraph_d_indheap_t* h);
+igraph_bool_t igraph_d_indheap_empty          (igraph_d_indheap_t* h);
+int igraph_d_indheap_push           (igraph_d_indheap_t* h, igraph_real_t elem,
+                                     long int idx, long int idx2);
+igraph_real_t igraph_d_indheap_max       (igraph_d_indheap_t* h);
+igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t* h);
+long int igraph_d_indheap_size      (igraph_d_indheap_t* h);
+int igraph_d_indheap_reserve        (igraph_d_indheap_t* h, long int size);
+void igraph_d_indheap_max_index(igraph_d_indheap_t *h, long int *idx, long int *idx2);
+
+void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head);
+void igraph_d_indheap_i_shift_up(igraph_d_indheap_t* h, long int elem);
+void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head);
+void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long int e2);
+
+/* -------------------------------------------------- */
+/* Two-way indexed heap                               */
+/* -------------------------------------------------- */
+
+/* This is a smart indexed heap. In addition to the "normal" indexed heap
+   it allows to access every element through its index in O(1) time.
+   In other words, for this heap the _modify operation is O(1), the
+   normal heap does this in O(n) time.... */
+
+typedef struct igraph_2wheap_t {
+    long int size;
+    igraph_vector_t data;
+    igraph_vector_long_t index;
+    igraph_vector_long_t index2;
+} igraph_2wheap_t;
+
+int igraph_2wheap_init(igraph_2wheap_t *h, long int size);
+void igraph_2wheap_destroy(igraph_2wheap_t *h);
+int igraph_2wheap_clear(igraph_2wheap_t *h);
+int igraph_2wheap_push_with_index(igraph_2wheap_t *h,
+                                  long int idx, igraph_real_t elem);
+igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h);
+long int igraph_2wheap_size(const igraph_2wheap_t *h);
+long int igraph_2wheap_max_size(const igraph_2wheap_t *h);
+igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h);
+long int igraph_2wheap_max_index(const igraph_2wheap_t *h);
+igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h);
+igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, long int idx);
+igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx);
+igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, long int idx);
+igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h);
+igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx);
+int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem);
+int igraph_2wheap_check(igraph_2wheap_t *h);
+
+/**
+ * Trie data type
+ * \ingroup internal
+ */
+
+typedef struct s_igraph_trie_node {
+    igraph_strvector_t strs;
+    igraph_vector_ptr_t children;
+    igraph_vector_t values;
+} igraph_trie_node_t;
+
+typedef struct s_igraph_trie {
+    igraph_strvector_t strs;
+    igraph_vector_ptr_t children;
+    igraph_vector_t values;
+    long int maxvalue;
+    igraph_bool_t storekeys;
+    igraph_strvector_t keys;
+} igraph_trie_t;
+
+#define IGRAPH_TRIE_NULL { IGRAPH_STRVECTOR_NULL, IGRAPH_VECTOR_PTR_NULL, \
+        IGRAPH_VECTOR_NULL, 0, 0, IGRAPH_STRVECTOR_NULL }
+#define IGRAPH_TRIE_INIT_FINALLY(tr, sk) \
+    do { IGRAPH_CHECK(igraph_trie_init(tr, sk)); \
+        IGRAPH_FINALLY(igraph_trie_destroy, tr); } while (0)
+
+int igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys);
+void igraph_trie_destroy(igraph_trie_t *t);
+int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id);
+int igraph_trie_check(igraph_trie_t *t, const char *key, long int *id);
+int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length,
+                     long int *id);
+void igraph_trie_idx(igraph_trie_t *t, long int idx, char **str);
+int igraph_trie_getkeys(igraph_trie_t *t, const igraph_strvector_t **strv);
+long int igraph_trie_size(igraph_trie_t *t);
+
+/**
+ * 2d grid containing points
+ */
+
+typedef struct igraph_2dgrid_t {
+    igraph_matrix_t *coords;
+    igraph_real_t minx, maxx, deltax;
+    igraph_real_t miny, maxy, deltay;
+    long int stepsx, stepsy;
+    igraph_matrix_t startidx;
+    igraph_vector_t next;
+    igraph_vector_t prev;
+    igraph_real_t massx, massy;       /* The sum of the coordinates */
+    long int vertices;        /* Number of active vertices  */
+} igraph_2dgrid_t;
+
+int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords,
+                       igraph_real_t minx, igraph_real_t maxx, igraph_real_t deltax,
+                       igraph_real_t miny, igraph_real_t maxy, igraph_real_t deltay);
+void igraph_2dgrid_destroy(igraph_2dgrid_t *grid);
+void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem,
+                       igraph_real_t xc, igraph_real_t yc);
+void igraph_2dgrid_add2(igraph_2dgrid_t *grid, long int elem);
+void igraph_2dgrid_move(igraph_2dgrid_t *grid, long int elem,
+                        igraph_real_t xc, igraph_real_t yc);
+void igraph_2dgrid_getcenter(const igraph_2dgrid_t *grid,
+                             igraph_real_t *massx, igraph_real_t *massy);
+igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, long int elem);
+igraph_real_t igraph_2dgrid_dist(const igraph_2dgrid_t *grid,
+                                 long int e1, long int e2);
+int igraph_2dgrid_neighbors(igraph_2dgrid_t *grid, igraph_vector_t *eids,
+                            igraph_integer_t vid, igraph_real_t r);
+
+typedef struct igraph_2dgrid_iterator_t {
+    long int vid, x, y;
+    long int nei;
+    long int nx[4], ny[4], ncells;
+} igraph_2dgrid_iterator_t;
+
+void igraph_2dgrid_reset(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it);
+igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid,
+                                    igraph_2dgrid_iterator_t *it);
+igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid,
+                                        igraph_2dgrid_iterator_t *it);
+
+/* Another type of grid, each cell is owned by exactly one graph */
+
+typedef struct igraph_i_layout_mergegrid_t {
+    long int *data;
+    long int stepsx, stepsy;
+    igraph_real_t minx, maxx, deltax;
+    igraph_real_t miny, maxy, deltay;
+} igraph_i_layout_mergegrid_t;
+
+int igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid,
+                                   igraph_real_t minx, igraph_real_t maxx, long int stepsx,
+                                   igraph_real_t miny, igraph_real_t maxy, long int stepsy);
+void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid);
+
+int igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid,
+                                       igraph_real_t x, igraph_real_t y, igraph_real_t r,
+                                       long int id);
+
+long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid,
+                                       igraph_real_t x, igraph_real_t y);
+
+long int igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *g,
+        igraph_real_t x, igraph_real_t y, igraph_real_t r);
+
+/* string -> string hash table */
+
+typedef struct igraph_hashtable_t {
+    igraph_trie_t keys;
+    igraph_strvector_t elements;
+    igraph_strvector_t defaults;
+} igraph_hashtable_t;
+
+int igraph_hashtable_init(igraph_hashtable_t *ht);
+void igraph_hashtable_destroy(igraph_hashtable_t *ht);
+int igraph_hashtable_addset(igraph_hashtable_t *ht,
+                            const char *key, const char *def,
+                            const char *elem);
+int igraph_hashtable_addset2(igraph_hashtable_t *ht,
+                             const char *key, const char *def,
+                             const char *elem, int elemlen);
+int igraph_hashtable_get(igraph_hashtable_t *ht,
+                         const char *key, char **elem);
+int igraph_hashtable_getkeys(igraph_hashtable_t *ht,
+                             const igraph_strvector_t **sv);
+int igraph_hashtable_reset(igraph_hashtable_t *ht);
+
+/* Buckets, needed for the maximum flow algorithm */
+
+typedef struct igraph_buckets_t {
+    igraph_vector_long_t bptr;
+    igraph_vector_long_t buckets;
+    igraph_integer_t max, no;
+} igraph_buckets_t;
+
+int igraph_buckets_init(igraph_buckets_t *b, long int bsize, long int size);
+void igraph_buckets_destroy(igraph_buckets_t *b);
+void igraph_buckets_clear(igraph_buckets_t *b);
+long int igraph_buckets_popmax(igraph_buckets_t *b);
+long int igraph_buckets_pop(igraph_buckets_t *b, long int bucket);
+igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b);
+igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b,
+        long int bucket);
+void igraph_buckets_add(igraph_buckets_t *b, long int bucket,
+                        long int elem);
+
+typedef struct igraph_dbuckets_t {
+    igraph_vector_long_t bptr;
+    igraph_vector_long_t next, prev;
+    igraph_integer_t max, no;
+} igraph_dbuckets_t;
+
+int igraph_dbuckets_init(igraph_dbuckets_t *b, long int bsize, long int size);
+void igraph_dbuckets_destroy(igraph_dbuckets_t *b);
+void igraph_dbuckets_clear(igraph_dbuckets_t *b);
+long int igraph_dbuckets_popmax(igraph_dbuckets_t *b);
+long int igraph_dbuckets_pop(igraph_dbuckets_t *b, long int bucket);
+igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b);
+igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b,
+        long int bucket);
+void igraph_dbuckets_add(igraph_dbuckets_t *b, long int bucket,
+                         long int elem);
+void igraph_dbuckets_delete(igraph_dbuckets_t *b, long int bucket,
+                            long int elem);
+
+/* Special maximum heap, needed for the minimum cut algorithm */
+
+typedef struct igraph_i_cutheap_t {
+    igraph_vector_t heap;
+    igraph_vector_t index;
+    igraph_vector_t hptr;
+    long int dnodes;
+} igraph_i_cutheap_t;
+
+int igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes);
+void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch);
+igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch);
+igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch);
+igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch);
+igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch);
+igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch);
+int igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index,
+                            igraph_real_t add);
+int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex);
+
+/* -------------------------------------------------- */
+/* Flexible set                                       */
+/* -------------------------------------------------- */
+
+/**
+ * Set containing integer numbers regardless of the order
+ * \ingroup types
+ */
+
+typedef struct s_set {
+    igraph_integer_t* stor_begin;
+    igraph_integer_t* stor_end;
+    igraph_integer_t* end;
+} igraph_set_t;
+
+#define IGRAPH_SET_NULL { 0,0,0 }
+#define IGRAPH_SET_INIT_FINALLY(v, size) \
+    do { IGRAPH_CHECK(igraph_set_init(v, size)); \
+        IGRAPH_FINALLY(igraph_set_destroy, v); } while (0)
+
+int igraph_set_init      (igraph_set_t* set, long int size);
+void igraph_set_destroy   (igraph_set_t* set);
+igraph_bool_t igraph_set_inited   (igraph_set_t* set);
+int igraph_set_reserve   (igraph_set_t* set, long int size);
+igraph_bool_t igraph_set_empty     (const igraph_set_t* set);
+void igraph_set_clear      (igraph_set_t* set);
+long int igraph_set_size      (const igraph_set_t* set);
+int igraph_set_add (igraph_set_t* v, igraph_integer_t e);
+igraph_bool_t igraph_set_contains (igraph_set_t* set, igraph_integer_t e);
+igraph_bool_t igraph_set_iterate (igraph_set_t* set, long int* state,
+                                  igraph_integer_t* element);
+
+/* -------------------------------------------------- */
+/* Vectorlist, fixed length                           */
+/* -------------------------------------------------- */
+
+typedef struct igraph_fixed_vectorlist_t {
+    igraph_vector_t *vecs;
+    igraph_vector_ptr_t v;
+    long int length;
+} igraph_fixed_vectorlist_t;
+
+void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l);
+int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l,
+                                    const igraph_vector_t *from,
+                                    long int size);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_vector.h b/igraph/include/igraph_vector.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_vector.h
@@ -0,0 +1,184 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_VECTOR_H
+#define IGRAPH_VECTOR_H
+
+#include "igraph_decls.h"
+#include "igraph_types.h"
+#include "igraph_complex.h"
+
+#ifdef HAVE_STDINT_H
+    #include <stdint.h>
+#else
+    #if defined(HAVE_SYS_INT_TYPES_H) && HAVE_SYS_INT_TYPES_H
+        #include <sys/int_types.h>    /* for Solaris */
+    #endif
+#endif
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Flexible vector                                    */
+/* -------------------------------------------------- */
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "igraph_vector_type.h"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_FLOAT
+#include "igraph_pmt.h"
+#include "igraph_vector_type.h"
+#include "igraph_pmt_off.h"
+#undef BASE_FLOAT
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "igraph_vector_type.h"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "igraph_vector_type.h"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "igraph_vector_type.h"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "igraph_vector_type.h"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
+
+#define BASE_COMPLEX
+#include "igraph_pmt.h"
+#include "igraph_vector_type.h"
+#include "igraph_pmt_off.h"
+#undef BASE_COMPLEX
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "igraph_vector_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_FLOAT
+#include "igraph_pmt.h"
+#include "igraph_vector_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_FLOAT
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "igraph_vector_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "igraph_vector_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "igraph_vector_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "igraph_vector_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
+
+#define BASE_COMPLEX
+#include "igraph_pmt.h"
+#include "igraph_vector_pmt.h"
+#include "igraph_pmt_off.h"
+#undef BASE_COMPLEX
+
+/* -------------------------------------------------- */
+/* Helper macros                                      */
+/* -------------------------------------------------- */
+
+#ifndef IGRAPH_VECTOR_NULL
+    #define IGRAPH_VECTOR_NULL { 0,0,0 }
+#endif
+
+#ifndef IGRAPH_VECTOR_INIT_FINALLY
+#define IGRAPH_VECTOR_INIT_FINALLY(v, size) \
+    do { IGRAPH_CHECK(igraph_vector_init(v, size)); \
+        IGRAPH_FINALLY(igraph_vector_destroy, v); } while (0)
+#endif
+#ifndef IGRAPH_VECTOR_BOOL_INIT_FINALLY
+#define IGRAPH_VECTOR_BOOL_INIT_FINALLY(v, size) \
+    do { IGRAPH_CHECK(igraph_vector_bool_init(v, size)); \
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, v); } while (0)
+#endif
+#ifndef IGRAPH_VECTOR_INT_INIT_FINALLY
+#define IGRAPH_VECTOR_INT_INIT_FINALLY(v, size) \
+    do { IGRAPH_CHECK(igraph_vector_int_init(v, size)); \
+        IGRAPH_FINALLY(igraph_vector_int_destroy, v); } while (0)
+#endif
+#ifndef IGRAPH_VECTOR_LONG_INIT_FINALLY
+#define IGRAPH_VECTOR_LONG_INIT_FINALLY(v, size) \
+    do { IGRAPH_CHECK(igraph_vector_long_init(v, size)); \
+        IGRAPH_FINALLY(igraph_vector_long_destroy, v); } while (0)
+#endif
+
+/* -------------------------------------------------- */
+/* Type-specific vector functions                     */
+/* -------------------------------------------------- */
+
+DECLDIR int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to);
+DECLDIR int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to);
+
+DECLDIR igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs,
+        const igraph_vector_t *rhs,
+        igraph_real_t tol);
+
+DECLDIR int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol);
+
+/* These are for internal use only */
+int igraph_vector_order(const igraph_vector_t* v, const igraph_vector_t *v2,
+                        igraph_vector_t* res, igraph_real_t maxval);
+int igraph_vector_order1(const igraph_vector_t* v,
+                         igraph_vector_t* res, igraph_real_t maxval);
+int igraph_vector_order1_int(const igraph_vector_t* v,
+                             igraph_vector_int_t* res, igraph_real_t maxval);
+int igraph_vector_order2(igraph_vector_t *v);
+int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res,
+                       long int nodes);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_vector_pmt.h b/igraph/include/igraph_vector_pmt.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_vector_pmt.h
@@ -0,0 +1,265 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/*--------------------*/
+/* Allocation         */
+/*--------------------*/
+
+DECLDIR int FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, long int size);
+DECLDIR int FUNCTION(igraph_vector, init_copy)(TYPE(igraph_vector)* v,
+        const BASE* data, long int length);
+DECLDIR int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to);
+DECLDIR int FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to,
+        const TYPE(igraph_vector) *from);
+DECLDIR void FUNCTION(igraph_vector, destroy)(TYPE(igraph_vector)* v);
+
+DECLDIR long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v);
+
+/*--------------------*/
+/* Accessing elements */
+/*--------------------*/
+
+#ifndef VECTOR
+    /**
+    * \ingroup vector
+    * \define VECTOR
+    * \brief Accessing an element of a vector.
+    *
+    * Usage:
+    * \verbatim VECTOR(v)[0] \endverbatim
+    * to access the first element of the vector, you can also use this in
+    * assignments, like:
+    * \verbatim VECTOR(v)[10]=5; \endverbatim
+    *
+    * Note that there are no range checks right now.
+    * This functionality might be redefined later as a real function
+    * instead of a <code>#define</code>.
+    * \param v The vector object.
+    *
+    * Time complexity: O(1).
+    */
+    #define VECTOR(v) ((v).stor_begin)
+#endif
+
+DECLDIR BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, long int pos);
+BASE* FUNCTION(igraph_vector, e_ptr)(const TYPE(igraph_vector)* v, long int pos);
+DECLDIR void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, long int pos, BASE value);
+DECLDIR BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v);
+
+/*-----------------------*/
+/* Initializing elements */
+/*-----------------------*/
+
+DECLDIR void FUNCTION(igraph_vector, null)(TYPE(igraph_vector)* v);
+DECLDIR void FUNCTION(igraph_vector, fill)(TYPE(igraph_vector)* v, BASE e);
+
+/*-----------------------*/
+/* Vector views          */
+/*-----------------------*/
+
+DECLDIR const TYPE(igraph_vector) *FUNCTION(igraph_vector, view)(const TYPE(igraph_vector) *v,
+        const BASE *data,
+        long int length);
+
+/*-----------------------*/
+/* Copying vectors       */
+/*-----------------------*/
+
+DECLDIR void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE* to);
+DECLDIR int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to,
+        const TYPE(igraph_vector) *from);
+DECLDIR int FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to,
+        const TYPE(igraph_vector) *from);
+DECLDIR int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2);
+
+/*-----------------------*/
+/* Exchanging elements   */
+/*-----------------------*/
+
+DECLDIR int FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v,
+        long int i, long int j);
+DECLDIR int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v);
+DECLDIR int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v);
+
+/*-----------------------*/
+/* Vector operations     */
+/*-----------------------*/
+
+DECLDIR void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus);
+DECLDIR void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by);
+DECLDIR int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1,
+        const TYPE(igraph_vector) *v2);
+DECLDIR int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1,
+        const TYPE(igraph_vector) *v2);
+DECLDIR int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1,
+        const TYPE(igraph_vector) *v2);
+DECLDIR int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1,
+        const TYPE(igraph_vector) *v2);
+DECLDIR int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to,
+        const TYPE(igraph_vector) *from);
+
+#ifndef NOABS
+    DECLDIR int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v);
+#endif
+
+/*------------------------------*/
+/* Comparison                   */
+/*------------------------------*/
+
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs);
+
+/*------------------------------*/
+/* Finding minimum and maximum  */
+/*------------------------------*/
+
+DECLDIR BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v);
+DECLDIR BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v);
+DECLDIR long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v);
+DECLDIR long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v);
+DECLDIR int FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v,
+        BASE *min, BASE *max);
+DECLDIR int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v,
+        long int *which_min, long int *which_max);
+
+/*-------------------*/
+/* Vector properties */
+/*-------------------*/
+
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, empty)     (const TYPE(igraph_vector)* v);
+DECLDIR long int FUNCTION(igraph_vector, size)      (const TYPE(igraph_vector)* v);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v);
+DECLDIR BASE FUNCTION(igraph_vector, sum)(const TYPE(igraph_vector) *v);
+DECLDIR igraph_real_t FUNCTION(igraph_vector, sumsq)(const TYPE(igraph_vector) *v);
+DECLDIR BASE FUNCTION(igraph_vector, prod)(const TYPE(igraph_vector) *v);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, isininterval)(const TYPE(igraph_vector) *v,
+        BASE low, BASE high);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, any_smaller)(const TYPE(igraph_vector) *v,
+        BASE limit);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, is_equal)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs);
+DECLDIR igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) *m1,
+        const TYPE(igraph_vector) *m2);
+
+/*------------------------*/
+/* Searching for elements */
+/*------------------------*/
+
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, BASE e);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v,
+        long int from, BASE what,
+        long int *pos);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v,
+        BASE what, long int *pos);
+DECLDIR igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v,
+        BASE what);
+
+/*------------------------*/
+/* Resizing operations    */
+/*------------------------*/
+
+DECLDIR void FUNCTION(igraph_vector, clear)(TYPE(igraph_vector)* v);
+DECLDIR int FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, long int newsize);
+DECLDIR int FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v);
+DECLDIR int FUNCTION(igraph_vector, reserve)(TYPE(igraph_vector)* v, long int size);
+DECLDIR int FUNCTION(igraph_vector, push_back)(TYPE(igraph_vector)* v, BASE e);
+DECLDIR BASE FUNCTION(igraph_vector, pop_back)(TYPE(igraph_vector)* v);
+DECLDIR int FUNCTION(igraph_vector, insert)(TYPE(igraph_vector) *v, long int pos, BASE value);
+DECLDIR void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, long int elem);
+DECLDIR void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v,
+        long int from, long int to);
+
+/*-----------*/
+/* Sorting   */
+/*-----------*/
+
+DECLDIR void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v);
+DECLDIR long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v,
+        igraph_vector_t *inds, igraph_bool_t descending);
+
+/*-----------*/
+/* Printing  */
+/*-----------*/
+
+int FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v);
+int FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v,
+                                    const char *format);
+int FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file);
+
+#ifdef BASE_COMPLEX
+
+DECLDIR int igraph_vector_complex_real(const igraph_vector_complex_t *v,
+                                       igraph_vector_t *real);
+DECLDIR int igraph_vector_complex_imag(const igraph_vector_complex_t *v,
+                                       igraph_vector_t *imag);
+DECLDIR int igraph_vector_complex_realimag(const igraph_vector_complex_t *v,
+        igraph_vector_t *real,
+        igraph_vector_t *imag);
+DECLDIR int igraph_vector_complex_create(igraph_vector_complex_t *v,
+        const igraph_vector_t *real,
+        const igraph_vector_t *imag);
+DECLDIR int igraph_vector_complex_create_polar(igraph_vector_complex_t *v,
+        const igraph_vector_t *r,
+        const igraph_vector_t *theta);
+
+#endif
+
+/* ----------------------------------------------------------------------------*/
+/* For internal use only, may be removed, rewritten ... */
+/* ----------------------------------------------------------------------------*/
+
+int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector)*v, int no, ...);
+int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector)*v, int no, ...);
+int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector)*v, BASE endmark, ...);
+int FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector)*v, int endmark, ...);
+
+int FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v,
+        long int begin, long int end, long int to);
+int FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v,
+        long int begin, long int end, long int to);
+void FUNCTION(igraph_vector, permdelete)(TYPE(igraph_vector) *v,
+        const igraph_vector_t *index,
+        long int nremove);
+int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem);
+int FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v,
+        TYPE(igraph_vector) *res,
+        long int from, long int to);
+int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1,
+        const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result);
+int FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1,
+        const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result);
+
+int FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v,
+                                   TYPE(igraph_vector) *newv,
+                                   const igraph_vector_t *idx);
+
+int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v,
+                                       const igraph_vector_int_t *idx);
diff --git a/igraph/include/igraph_vector_ptr.h b/igraph/include/igraph_vector_ptr.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_vector_ptr.h
@@ -0,0 +1,100 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_VECTOR_PTR_H
+#define IGRAPH_VECTOR_PTR_H
+
+#include "igraph_decls.h"
+#include "igraph_vector.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Flexible vector, storing pointers                  */
+/* -------------------------------------------------- */
+
+/**
+ * Vector, storing pointers efficiently
+ * \ingroup internal
+ *
+ */
+typedef struct s_vector_ptr {
+    void** stor_begin;
+    void** stor_end;
+    void** end;
+    igraph_finally_func_t* item_destructor;
+} igraph_vector_ptr_t;
+
+#define IGRAPH_VECTOR_PTR_NULL { 0,0,0,0 }
+#define IGRAPH_VECTOR_PTR_INIT_FINALLY(v, size) \
+    do { IGRAPH_CHECK(igraph_vector_ptr_init(v, size)); \
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, v); } while (0)
+
+DECLDIR int igraph_vector_ptr_init      (igraph_vector_ptr_t* v, long int size);
+DECLDIR int igraph_vector_ptr_init_copy (igraph_vector_ptr_t* v, void** data, long int length);
+DECLDIR const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v,
+        void *const *data, long int length);
+DECLDIR void igraph_vector_ptr_destroy   (igraph_vector_ptr_t* v);
+DECLDIR void igraph_vector_ptr_free_all   (igraph_vector_ptr_t* v);
+DECLDIR void igraph_vector_ptr_destroy_all   (igraph_vector_ptr_t* v);
+DECLDIR int igraph_vector_ptr_reserve   (igraph_vector_ptr_t* v, long int size);
+DECLDIR igraph_bool_t igraph_vector_ptr_empty     (const igraph_vector_ptr_t* v);
+DECLDIR long int igraph_vector_ptr_size      (const igraph_vector_ptr_t* v);
+DECLDIR void igraph_vector_ptr_clear     (igraph_vector_ptr_t* v);
+DECLDIR void igraph_vector_ptr_null      (igraph_vector_ptr_t* v);
+DECLDIR int igraph_vector_ptr_push_back (igraph_vector_ptr_t* v, void* e);
+DECLDIR int igraph_vector_ptr_append    (igraph_vector_ptr_t *to,
+        const igraph_vector_ptr_t *from);
+DECLDIR void *igraph_vector_ptr_pop_back (igraph_vector_ptr_t *v);
+DECLDIR int igraph_vector_ptr_insert(igraph_vector_ptr_t *v, long int pos, void* e);
+DECLDIR void* igraph_vector_ptr_e         (const igraph_vector_ptr_t* v, long int pos);
+DECLDIR void igraph_vector_ptr_set       (igraph_vector_ptr_t* v, long int pos, void* value);
+DECLDIR int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize);
+DECLDIR void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to);
+DECLDIR int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from);
+DECLDIR void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos);
+DECLDIR void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int(*compar)(const void*, const void*));
+DECLDIR int igraph_vector_ptr_index_int(igraph_vector_ptr_t *v,
+                                        const igraph_vector_int_t *idx);
+
+DECLDIR igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v);
+DECLDIR igraph_finally_func_t* igraph_vector_ptr_set_item_destructor(igraph_vector_ptr_t *v,
+        igraph_finally_func_t *func);
+
+/**
+ * \define IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR
+ * \brief Sets the item destructor for this pointer vector (macro version).
+ *
+ * This macro is expanded to \ref igraph_vector_ptr_set_item_destructor(), the
+ * only difference is that the second argument is automatically cast to an
+ * \c igraph_finally_func_t*. The cast is necessary in most cases as the
+ * destructor functions we use (such as \ref igraph_vector_destroy()) take a
+ * pointer to some concrete igraph data type, while \c igraph_finally_func_t
+ * expects \c void*
+ */
+#define IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(v, func) \
+    igraph_vector_ptr_set_item_destructor((v), (igraph_finally_func_t*)(func))
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/igraph_vector_type.h b/igraph/include/igraph_vector_type.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_vector_type.h
@@ -0,0 +1,34 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/**
+ * Vector, dealing with arrays efficiently.
+ * \ingroup types
+ */
+
+typedef struct TYPE(igraph_vector) {
+    BASE* stor_begin;
+    BASE* stor_end;
+    BASE* end;
+} TYPE(igraph_vector);
+
diff --git a/igraph/include/igraph_version.h b/igraph/include/igraph_version.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_version.h
@@ -0,0 +1,46 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_VERSION_H
+#define IGRAPH_VERSION_H
+
+#include "igraph_decls.h"
+
+__BEGIN_DECLS
+
+#define IGRAPH_VERSION "0.8.0"
+#define IGRAPH_VERSION_MAJOR 0
+#define IGRAPH_VERSION_MINOR 8
+#define IGRAPH_VERSION_PATCH 0
+#define IGRAPH_VERSION_PRERELEASE ""
+
+int igraph_version(const char **version_string,
+                   int *major,
+                   int *minor,
+                   int *subminor);
+
+__END_DECLS
+
+#endif
+
+
diff --git a/igraph/include/igraph_visitor.h b/igraph/include/igraph_visitor.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/igraph_visitor.h
@@ -0,0 +1,132 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_VISITOR_H
+#define IGRAPH_VISITOR_H
+
+#include "igraph_decls.h"
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+
+__BEGIN_DECLS
+
+/* -------------------------------------------------- */
+/* Visitor-like functions                             */
+/* -------------------------------------------------- */
+
+/**
+ * \typedef igraph_bfshandler_t
+ * Callback type for BFS function
+ *
+ * \ref igraph_bfs() is able to call a callback function, whenever a
+ * new vertex is found, while doing the breadth-first search. This
+ * callback function must be of type \c igraph_bfshandler_t. It has
+ * the following arguments:
+ * \param graph The graph that that algorithm is working on. Of course
+ *   this must not be modified.
+ * \param vid The id of the vertex just found by the breadth-first
+ *   search.
+ * \param pred The id of the previous vertex visited. It is -1 if
+ *   there is no previous vertex, because the current vertex is the root
+ *   is a search tree.
+ * \param succ The id of the next vertex that will be visited. It is
+ *   -1 if there is no next vertex, because the current vertex is the
+ *   last one in a search tree.
+ * \param rank The rank of the current vertex, it starts with zero.
+ * \param dist The distance (number of hops) of the current vertex
+ *   from the root of the current search tree.
+ * \param extra The extra argument that was passed to \ref
+ *   igraph_bfs().
+ * \return A logical value, if TRUE (=non-zero), that is interpreted
+ *    as a request to stop the BFS and return to the caller. If a BFS
+ *    is terminated like this, then all elements of the result vectors
+ *    that were not yet calculated at the point of the termination
+ *    contain \c IGRAPH_NAN.
+ *
+ * \sa \ref igraph_bfs()
+ */
+
+typedef igraph_bool_t igraph_bfshandler_t(const igraph_t *graph,
+        igraph_integer_t vid,
+        igraph_integer_t pred,
+        igraph_integer_t succ,
+        igraph_integer_t rank,
+        igraph_integer_t dist,
+        void *extra);
+
+DECLDIR int igraph_bfs(const igraph_t *graph,
+               igraph_integer_t root, const igraph_vector_t *roots,
+               igraph_neimode_t mode, igraph_bool_t unreachable,
+               const igraph_vector_t *restricted,
+               igraph_vector_t *order, igraph_vector_t *rank,
+               igraph_vector_t *father,
+               igraph_vector_t *pred, igraph_vector_t *succ,
+               igraph_vector_t *dist, igraph_bfshandler_t *callback,
+               void *extra);
+
+int igraph_i_bfs(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mode,
+                 igraph_vector_t *vids, igraph_vector_t *layers,
+                 igraph_vector_t *parents);
+
+/**
+ * \function igraph_dfshandler_t
+ * Callback type for the DFS function
+ *
+ * \ref igraph_dfs() is able to call a callback function, whenever a
+ * new vertex is discovered, and/or whenever a subtree is
+ * completed. These callbacks must be of type \c
+ * igraph_dfshandler_t. They have the following arguments:
+ * \param graph The graph that that algorithm is working on. Of course
+ *   this must not be modified.
+ * \param vid The id of the vertex just found by the depth-first
+ *   search.
+ * \param dist The distance (number of hops) of the current vertex
+ *   from the root of the current search tree.
+ * \param extra The extra argument that was passed to \ref
+ *   igraph_dfs().
+ * \return A logical value, if TRUE (=non-zero), that is interpreted
+ *    as a request to stop the DFS and return to the caller. If a DFS
+ *    is terminated like this, then all elements of the result vectors
+ *    that were not yet calculated at the point of the termination
+ *    contain \c IGRAPH_NAN.
+ *
+ * \sa \ref igraph_dfs()
+ */
+
+typedef igraph_bool_t igraph_dfshandler_t(const igraph_t *graph,
+        igraph_integer_t vid,
+        igraph_integer_t dist,
+        void *extra);
+
+DECLDIR int igraph_dfs(const igraph_t *graph, igraph_integer_t root,
+               igraph_neimode_t mode, igraph_bool_t unreachable,
+               igraph_vector_t *order,
+               igraph_vector_t *order_out, igraph_vector_t *father,
+               igraph_vector_t *dist, igraph_dfshandler_t *in_callback,
+               igraph_dfshandler_t *out_callback,
+               void *extra);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/infomap_FlowGraph.h b/igraph/include/infomap_FlowGraph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/infomap_FlowGraph.h
@@ -0,0 +1,78 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef FLOWGRAPH_H
+#define FLOWGRAPH_H
+
+#include <vector>
+#include <set>
+
+#include "igraph_interface.h"
+
+#include "infomap_Node.h"
+
+class FlowGraph {
+private:
+    void init(int n, const igraph_vector_t *nodeWeights);
+
+public:
+    FlowGraph(int n);
+    FlowGraph(int n, const igraph_vector_t *nodeWeights);
+    FlowGraph(FlowGraph * fgraph);
+    FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members);
+
+    FlowGraph(const igraph_t * graph, const igraph_vector_t *e_weights,
+              const igraph_vector_t *v_weights);
+
+    ~FlowGraph();
+
+    void swap(FlowGraph * fgraph);
+
+    void initiate();
+    void eigenvector();
+    void calibrate();
+
+    void back_to(FlowGraph * fgraph);
+
+    /*************************************************************************/
+    Node **node;
+    int  Nnode;
+
+    double alpha, beta;
+
+    int Ndanglings;
+    vector<int> danglings; // id of dangling nodes
+
+    double exit;                  //
+    double exitFlow;              //
+    double exit_log_exit;         //
+    double size_log_size;         //
+    double nodeSize_log_nodeSize; // \sum_{v in V} p log(p)
+
+    double codeLength;
+};
+
+void delete_FlowGraph(FlowGraph *fgraph);
+
+#endif
diff --git a/igraph/include/infomap_Greedy.h b/igraph/include/infomap_Greedy.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/infomap_Greedy.h
@@ -0,0 +1,85 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef GREEDY_H
+#define GREEDY_H
+
+#include <vector>
+#include <map>
+#include <utility>
+#include <climits>
+
+#include "igraph_random.h"
+
+#include "infomap_Node.h"
+#include "infomap_FlowGraph.h"
+
+class Greedy {
+public:
+    Greedy(FlowGraph * fgraph);
+    // initialise les attributs par rapport au graph
+
+    ~Greedy();
+
+    void setMove(int *moveTo);
+    //virtual void determMove(int *moveTo);
+
+    bool optimize();
+    //virtual void move(bool &moved);
+
+    void apply(bool sort);
+    //virtual void level(Node ***, bool sort);
+
+    void tune(void);
+
+    /**************************************************************************/
+
+    FlowGraph * graph;
+    int Nnode;
+
+    double exit;
+    double exitFlow;
+    double exit_log_exit;
+    double size_log_size;
+    double nodeSize_log_nodeSize;
+
+    double codeLength;
+
+    double alpha, beta;
+    // local copy of fgraph alpha, beta (=alpha -  Nnode = graph->Nnode;1)
+
+    vector<int> node_index;  // module number of each node
+
+    int Nempty;
+    vector<int> mod_empty;
+
+    vector<double> mod_exit;  // version tmp de node
+    vector<double> mod_size;
+    vector<double> mod_danglingSize;
+    vector<double> mod_teleportWeight;
+    vector<int> mod_members;
+};
+
+void delete_Greedy(Greedy *greedy);
+#endif
diff --git a/igraph/include/infomap_Node.h b/igraph/include/infomap_Node.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/infomap_Node.h
@@ -0,0 +1,55 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef NODE_H
+#define NODE_H
+
+#include <vector>
+#include <utility>
+
+#include "igraph_interface.h"
+
+class Node;
+using namespace std;
+
+class Node {
+public:
+
+    Node();
+    Node(int modulenr, double tpweight);
+
+    vector<int> members;
+    vector< pair<int, double> > inLinks;
+    vector< pair<int, double> > outLinks;
+    double selfLink;
+
+    double teleportWeight;
+    double danglingSize;
+    double exit;
+    double size;
+};
+
+void cpyNode(Node *newNode, Node *oldNode);
+
+#endif
diff --git a/igraph/include/matrix.pmt b/igraph/include/matrix.pmt
new file mode 100644
--- /dev/null
+++ b/igraph/include/matrix.pmt
@@ -0,0 +1,1634 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_error.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+
+/**
+ * \section about_igraph_matrix_t_objects About \type igraph_matrix_t objects
+ *
+ * <para>This type is just an interface to \type igraph_vector_t.</para>
+ *
+ * <para>The \type igraph_matrix_t type usually stores n
+ * elements in O(n) space, but not always. See the documentation of
+ * the vector type.</para>
+ */
+
+/**
+ * \section igraph_matrix_constructor_and_destructor Matrix constructors and
+ * destructors
+ */
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_init
+ * \brief Initializes a matrix.
+ *
+ * </para><para>
+ * Every matrix needs to be initialized before using it. This is done
+ * by calling this function. A matrix has to be destroyed if it is not
+ * needed any more; see \ref igraph_matrix_destroy().
+ * \param m Pointer to a not yet initialized matrix object to be
+ *        initialized.
+ * \param nrow The number of rows in the matrix.
+ * \param ncol The number of columns in the matrix.
+ * \return Error code.
+ *
+ * Time complexity: usually O(n),
+ * n is the
+ * number of elements in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, init)(TYPE(igraph_matrix) *m, long int nrow, long int ncol) {
+    int ret1;
+    ret1 = FUNCTION(igraph_vector, init)(&m->data, nrow * ncol);
+    m->nrow = nrow;
+    m->ncol = ncol;
+    return ret1;
+}
+
+const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view)(const TYPE(igraph_matrix) *m,
+        const BASE *data,
+        long int nrow,
+        long int ncol) {
+    TYPE(igraph_matrix) *m2 = (TYPE(igraph_matrix)*)m;
+    FUNCTION(igraph_vector, view)(&m2->data, data, nrow * ncol);
+    m2->nrow = nrow;
+    m2->ncol = ncol;
+    return m;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_destroy
+ * \brief Destroys a matrix object.
+ *
+ * </para><para>
+ * This function frees all the memory allocated for a matrix
+ * object. The destroyed object needs to be reinitialized before using
+ * it again.
+ * \param m The matrix to destroy.
+ *
+ * Time complexity: operating system dependent.
+ */
+
+void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m) {
+    FUNCTION(igraph_vector, destroy)(&m->data);
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_capacity
+ * \brief Returns the number of elements allocated for a matrix.
+ *
+ * Note that this might be different from the size of the matrix (as
+ * queried by \ref igraph_matrix_size(), and specifies how many elements
+ * the matrix can hold, without reallocation.
+ * \param v Pointer to the (previously initialized) matrix object
+ *          to query.
+ * \return The allocated capacity.
+ *
+ * \sa \ref igraph_matrix_size(), \ref igraph_matrix_nrow(),
+ * \ref igraph_matrix_ncol().
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m) {
+    return FUNCTION(igraph_vector, capacity)(&m->data);
+}
+
+
+/**
+ * \section igraph_matrix_accessing_elements Accessing elements of a matrix
+ */
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_resize
+ * \brief Resizes a matrix.
+ *
+ * </para><para>
+ * This function resizes a matrix by adding more elements to it.
+ * The matrix contains arbitrary data after resizing it.
+ * That is, after calling this function you cannot expect that element
+ * (i,j) in the matrix remains the
+ * same as before.
+ * \param m Pointer to an already initialized matrix object.
+ * \param nrow The number of rows in the resized matrix.
+ * \param ncol The number of columns in the resized matrix.
+ * \return Error code.
+ *
+ * Time complexity: O(1) if the
+ * matrix gets smaller, usually O(n)
+ * if it gets larger, n is the
+ * number of elements in the resized matrix.
+ */
+
+int FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, long int nrow, long int ncol) {
+    FUNCTION(igraph_vector, resize)(&m->data, nrow * ncol);
+    m->nrow = nrow;
+    m->ncol = ncol;
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_resize_min
+ * \brief Deallocates unused memory for a matrix.
+ *
+ * </para><para>
+ * Note that this function might fail if there is not enough memory
+ * available.
+ *
+ * </para><para>
+ * Also note, that this function leaves the matrix intact, i.e.
+ * it does not destroy any of the elements. However, usually it involves
+ * copying the matrix in memory.
+ * \param m Pointer to an initialized matrix.
+ * \return Error code.
+ *
+ * \sa \ref igraph_matrix_resize().
+ *
+ * Time complexity: operating system dependent.
+ */
+
+int FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m) {
+    TYPE(igraph_vector) tmp;
+    long int size = FUNCTION(igraph_matrix, size)(m);
+    long int capacity = FUNCTION(igraph_matrix, capacity)(m);
+    if (size == capacity) {
+        return 0;
+    }
+
+    IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&tmp, size));
+    FUNCTION(igraph_vector, update)(&tmp, &m->data);
+    FUNCTION(igraph_vector, destroy)(&m->data);
+    m->data = tmp;
+
+    return 0;
+}
+
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_size
+ * \brief The number of elements in a matrix.
+ *
+ * \param m Pointer to an initialized matrix object.
+ * \return The size of the matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m) {
+    return (m->nrow) * (m->ncol);
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_nrow
+ * \brief The number of rows in a matrix.
+ *
+ * \param m Pointer to an initialized matrix object.
+ * \return The number of rows in the matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m) {
+    return m->nrow;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_ncol
+ * \brief The number of columns in a matrix.
+ *
+ * \param m Pointer to an initialized matrix object.
+ * \return The number of columns in the matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m) {
+    return m->ncol;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_copy_to
+ * \brief Copies a matrix to a regular C array.
+ *
+ * </para><para>
+ * The matrix is copied columnwise, as this is the format most
+ * programs and languages use.
+ * The C array should be of sufficient size; there are (of course) no
+ * range checks.
+ * \param m Pointer to an initialized matrix object.
+ * \param to Pointer to a C array; the place to copy the data to.
+ * \return Error code.
+ *
+ * Time complexity: O(n),
+ * n is the number of
+ * elements in the matrix.
+ */
+
+void FUNCTION(igraph_matrix, copy_to)(const TYPE(igraph_matrix) *m, BASE *to) {
+    FUNCTION(igraph_vector, copy_to)(&m->data, to);
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_null
+ * \brief Sets all elements in a matrix to zero.
+ *
+ * \param m Pointer to an initialized matrix object.
+ *
+ * Time complexity: O(n),
+ * n is the number of  elements in
+ * the matrix.
+ */
+
+void FUNCTION(igraph_matrix, null)(TYPE(igraph_matrix) *m) {
+    FUNCTION(igraph_vector, null)(&m->data);
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_add_cols
+ * \brief Adds columns to a matrix.
+ * \param m The matrix object.
+ * \param n The number of columns to add.
+ * \return Error code, \c IGRAPH_ENOMEM if there is
+ *   not enough memory to perform the operation.
+ *
+ * Time complexity: linear with the number of elements of the new,
+ * resized matrix.
+ */
+
+int FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, long int n) {
+    FUNCTION(igraph_matrix, resize)(m, m->nrow, m->ncol + n);
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_add_rows
+ * \brief Adds rows to a matrix.
+ * \param m The matrix object.
+ * \param n The number of rows to add.
+ * \return Error code, \c IGRAPH_ENOMEM if there
+ *   isn't enough memory for the operation.
+ *
+ * Time complexity: linear with the number of elements of the new,
+ * resized matrix.
+ */
+
+int FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, long int n) {
+    long int i;
+    FUNCTION(igraph_vector, resize)(&m->data, (m->ncol) * (m->nrow + n));
+    for (i = m->ncol - 1; i >= 0; i--) {
+        FUNCTION(igraph_vector, move_interval2)(&m->data, (m->nrow)*i, (m->nrow) * (i + 1),
+                                                (m->nrow + n)*i);
+    }
+    m->nrow += n;
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_remove_col
+ * \brief Removes a column from a matrix.
+ *
+ * \param m The matrix object.
+ * \param col The column to remove.
+ * \return Error code, always returns with success.
+ *
+ * Time complexity: linear with the number of elements of the new,
+ * resized matrix.
+ */
+
+int FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, long int col) {
+    FUNCTION(igraph_vector, remove_section)(&m->data, (m->nrow)*col, (m->nrow) * (col + 1));
+    m->ncol--;
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_permdelete_rows
+ * \brief Removes rows from a matrix (for internal use).
+ *
+ * Time complexity: linear with the number of elements of the original
+ * matrix.
+ */
+
+int FUNCTION(igraph_matrix, permdelete_rows)(TYPE(igraph_matrix) *m, long int *index, long int nremove) {
+    long int i, j;
+    for (j = 0; j < m->nrow; j++) {
+        if (index[j] != 0) {
+            for (i = 0; i < m->ncol; i++) {
+                MATRIX(*m, index[j] - 1, i) = MATRIX(*m, j, i);
+            }
+        }
+    }
+    /* Remove unnecessary elements from the end of each column */
+    for (i = 0; i < m->ncol; i++)
+        FUNCTION(igraph_vector, remove_section)(&m->data,
+                                                (i + 1) * (m->nrow - nremove), (i + 1) * (m->nrow - nremove) + nremove);
+    FUNCTION(igraph_matrix, resize)(m, m->nrow - nremove, m->ncol);
+
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_delete_rows_neg
+ * \brief Removes columns from a matrix (for internal use).
+ *
+ * Time complexity: linear with the number of elements of the original
+ * matrix.
+ */
+
+int FUNCTION(igraph_matrix, delete_rows_neg)(TYPE(igraph_matrix) *m,
+        const igraph_vector_t *neg, long int nremove) {
+    long int i, j, idx = 0;
+    for (i = 0; i < m->ncol; i++) {
+        for (j = 0; j < m->nrow; j++) {
+            if (VECTOR(*neg)[j] >= 0) {
+                MATRIX(*m, idx++, i) = MATRIX(*m, j, i);
+            }
+        }
+        idx = 0;
+    }
+    FUNCTION(igraph_matrix, resize)(m, m->nrow - nremove, m->ncol);
+
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_matrix_copy
+ * \brief Copies a matrix.
+ *
+ * </para><para>
+ * Creates a matrix object by copying from an existing matrix.
+ * \param to Pointer to an uninitialized matrix object.
+ * \param from The initialized matrix object to copy.
+ * \return Error code, \c IGRAPH_ENOMEM if there
+ *   isn't enough memory to allocate the new matrix.
+ *
+ * Time complexity: O(n), the number
+ * of elements in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) {
+    to->nrow = from->nrow;
+    to->ncol = from->ncol;
+    return FUNCTION(igraph_vector, copy)(&to->data, &from->data);
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \function igraph_matrix_max
+ *
+ * Returns the maximal element of a matrix.
+ * \param m The matrix object.
+ * \return The maximum element. For empty matrix the returned value is
+ * undefined.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(n), the number of elements in the matrix.
+ */
+
+igraph_real_t FUNCTION(igraph_matrix, max)(const TYPE(igraph_matrix) *m) {
+    return FUNCTION(igraph_vector, max)(&m->data);
+}
+
+#endif
+
+/**
+ * \function igraph_matrix_scale
+ *
+ * Multiplies each element of the matrix by a constant.
+ * \param m The matrix.
+ * \param by The constant.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(n), the number of elements in the matrix.
+ */
+
+void FUNCTION(igraph_matrix, scale)(TYPE(igraph_matrix) *m, BASE by) {
+    FUNCTION(igraph_vector, scale)(&m->data, by);
+}
+
+/**
+ * \function igraph_matrix_select_rows
+ * \brief Select some rows of a matrix.
+ *
+ * This function selects some rows of a matrix and returns them in a
+ * new matrix. The result matrix should be initialized before calling
+ * the function.
+ * \param m The input matrix.
+ * \param res The result matrix. It should be initialized and will be
+ *    resized as needed.
+ * \param rows Vector; it contains the row indices (starting with
+ *    zero) to extract. Note that no range checking is performed.
+ * \return Error code.
+ *
+ * Time complexity: O(nm), n is the number of rows, m the number of
+ * columns of the result matrix.
+ */
+
+int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_matrix) *res,
+        const igraph_vector_t *rows) {
+    long int norows = igraph_vector_size(rows);
+    long int i, j, ncols = FUNCTION(igraph_matrix, ncol)(m);
+
+    IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(res, norows, ncols));
+    for (i = 0; i < norows; i++) {
+        for (j = 0; j < ncols; j++) {
+            MATRIX(*res, i, j) = MATRIX(*m, (long int)VECTOR(*rows)[i], j);
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_select_rows_cols
+ * \brief Select some rows and columns of a matrix.
+ *
+ * This function selects some rows and columns of a matrix and returns
+ * them in a new matrix. The result matrix should be initialized before
+ * calling the function.
+ * \param m The input matrix.
+ * \param res The result matrix. It should be initialized and will be
+ *    resized as needed.
+ * \param rows Vector; it contains the row indices (starting with
+ *    zero) to extract. Note that no range checking is performed.
+ * \param cols Vector; it contains the column indices (starting with
+ *    zero) to extract. Note that no range checking is performed.
+ * \return Error code.
+ *
+ * Time complexity: O(nm), n is the number of rows, m the number of
+ * columns of the result matrix.
+ */
+
+int FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_matrix) *res,
+        const igraph_vector_t *rows,
+        const igraph_vector_t *cols) {
+    long int nrows = igraph_vector_size(rows);
+    long int ncols = igraph_vector_size(cols);
+    long int i, j;
+
+    IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(res, nrows, ncols));
+    for (i = 0; i < nrows; i++) {
+        for (j = 0; j < ncols; j++) {
+            MATRIX(*res, i, j) = MATRIX(*m, (long int)VECTOR(*rows)[i],
+                                        (long int)VECTOR(*cols)[j]);
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_get_col
+ * \brief Select a column.
+ *
+ * Extract a column of a matrix and return it as a vector.
+ * \param m The input matrix.
+ * \param res The result will we stored in this vector. It should be
+ *   initialized and will be resized as needed.
+ * \param index The index of the column to select.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of rows in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m,
+                                     TYPE(igraph_vector) *res,
+                                     long int index) {
+    long int nrow = FUNCTION(igraph_matrix, nrow)(m);
+
+    if (index >= m->ncol) {
+        IGRAPH_ERROR("Index out of range for selecting matrix column", IGRAPH_EINVAL);
+    }
+    IGRAPH_CHECK(FUNCTION(igraph_vector, get_interval)(&m->data, res,
+                 nrow * index, nrow * (index + 1)));
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_sum
+ * \brief Sum of elements.
+ *
+ * Returns the sum of the elements of a matrix.
+ * \param m The input matrix.
+ * \return The sum of the elements.
+ *
+ * Time complexity: O(mn), the number of elements in the matrix.
+ */
+
+BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m) {
+    return FUNCTION(igraph_vector, sum)(&m->data);
+}
+
+/**
+ * \function igraph_matrix_all_e
+ * \brief Are all elements equal?
+ *
+ * \param lhs The first matrix.
+ * \param rhs The second matrix.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    equal to the corresponding elements in \p rhs. Returns \c 0
+ *    (=false) if the dimensions of the matrices don't match.
+ *
+ * Time complexity: O(nm), the size of the matrices.
+ */
+
+igraph_bool_t FUNCTION(igraph_matrix, all_e)(const TYPE(igraph_matrix) *lhs,
+        const TYPE(igraph_matrix) *rhs) {
+    return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow &&
+           FUNCTION(igraph_vector, all_e)(&lhs->data, &rhs->data);
+}
+
+igraph_bool_t
+FUNCTION(igraph_matrix, is_equal)(const TYPE(igraph_matrix) *lhs,
+                                  const TYPE(igraph_matrix) *rhs) {
+    return FUNCTION(igraph_matrix, all_e)(lhs, rhs);
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \function igraph_matrix_all_l
+ * \brief Are all elements less?
+ *
+ * \param lhs The first matrix.
+ * \param rhs The second matrix.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    less than the corresponding elements in \p rhs. Returns \c 0
+ *    (=false) if the dimensions of the matrices don't match.
+ *
+ * Time complexity: O(nm), the size of the matrices.
+ */
+
+igraph_bool_t FUNCTION(igraph_matrix, all_l)(const TYPE(igraph_matrix) *lhs,
+        const TYPE(igraph_matrix) *rhs) {
+    return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow &&
+           FUNCTION(igraph_vector, all_l)(&lhs->data, &rhs->data);
+}
+
+/**
+ * \function igraph_matrix_all_g
+ * \brief Are all elements greater?
+ *
+ * \param lhs The first matrix.
+ * \param rhs The second matrix.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    greater than the corresponding elements in \p rhs. Returns \c 0
+ *    (=false) if the dimensions of the matrices don't match.
+ *
+ * Time complexity: O(nm), the size of the matrices.
+ */
+
+igraph_bool_t FUNCTION(igraph_matrix, all_g)(const TYPE(igraph_matrix) *lhs,
+        const TYPE(igraph_matrix) *rhs) {
+    return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow &&
+           FUNCTION(igraph_vector, all_g)(&lhs->data, &rhs->data);
+}
+
+/**
+ * \function igraph_matrix_all_le
+ * \brief Are all elements less or equal?
+ *
+ * \param lhs The first matrix.
+ * \param rhs The second matrix.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    less than or equal to the corresponding elements in \p
+ *    rhs. Returns \c 0 (=false) if the dimensions of the matrices
+ *    don't match.
+ *
+ * Time complexity: O(nm), the size of the matrices.
+ */
+
+igraph_bool_t
+FUNCTION(igraph_matrix, all_le)(const TYPE(igraph_matrix) *lhs,
+                                const TYPE(igraph_matrix) *rhs) {
+    return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow &&
+           FUNCTION(igraph_vector, all_le)(&lhs->data, &rhs->data);
+}
+
+/**
+ * \function igraph_matrix_all_ge
+ * \brief Are all elements greater or equal?
+ *
+ * \param lhs The first matrix.
+ * \param rhs The second matrix.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    greater than or equal to the corresponding elements in \p
+ *    rhs. Returns \c 0 (=false) if the dimensions of the matrices
+ *    don't match.
+ *
+ * Time complexity: O(nm), the size of the matrices.
+ */
+
+igraph_bool_t
+FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_matrix) *lhs,
+                                const TYPE(igraph_matrix) *rhs) {
+    return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow &&
+           FUNCTION(igraph_vector, all_ge)(&lhs->data, &rhs->data);
+}
+
+#endif
+
+#ifndef NOTORDERED
+
+/**
+ * \function igraph_matrix_maxdifference
+ * \brief Maximum absolute difference between two matrices.
+ *
+ * Calculate the maximum absolute difference of two matrices. Both matrices
+ * must be non-empty. If their dimensions differ then a warning is given and
+ * the comparison is performed by vectors columnwise from both matrices.
+ * The remaining elements in the larger vector are ignored.
+ * \param m1 The first matrix.
+ * \param m2 The second matrix.
+ * \return The element with the largest absolute value in \c m1 - \c m2.
+ *
+ * Time complexity: O(mn), the elements in the smaller matrix.
+ */
+
+igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2) {
+    long int col1 = FUNCTION(igraph_matrix, ncol)(m1);
+    long int col2 = FUNCTION(igraph_matrix, ncol)(m2);
+    long int row1 = FUNCTION(igraph_matrix, nrow)(m1);
+    long int row2 = FUNCTION(igraph_matrix, nrow)(m2);
+    if (col1 != col2 || row1 != row2) {
+        IGRAPH_WARNING("Comparing non-conformant matrices");
+    }
+    return FUNCTION(igraph_vector, maxdifference)(&m1->data, &m2->data);
+}
+
+#endif
+
+/**
+ * \function igraph_matrix_transpose
+ * \brief Transpose a matrix.
+ *
+ * Calculate the transpose of a matrix. Note that the function
+ * reallocates the memory used for the matrix.
+ * \param m The input (and output) matrix.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m) {
+    long int nrow = m->nrow;
+    long int ncol = m->ncol;
+    if (nrow > 1 && ncol > 1) {
+        TYPE(igraph_vector) newdata;
+        long int i, size = nrow * ncol, mod = size - 1;
+        FUNCTION(igraph_vector, init)(&newdata, size);
+        IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), &newdata);
+        for (i = 0; i < size; i++) {
+            VECTOR(newdata)[i] = VECTOR(m->data)[ (i * nrow) % mod ];
+        }
+        VECTOR(newdata)[size - 1] = VECTOR(m->data)[size - 1];
+        FUNCTION(igraph_vector, destroy)(&m->data);
+        IGRAPH_FINALLY_CLEAN(1);
+        m->data = newdata;
+    }
+    m->nrow = ncol;
+    m->ncol = nrow;
+
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_e
+ * Extract an element from a matrix.
+ *
+ * Use this if you need a function for some reason and cannot use the
+ * \ref MATRIX macro. Note that no range checking is performed.
+ * \param m The input matrix.
+ * \param row The row index.
+ * \param col The column index.
+ * \return The element in the given row and column.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m,
+                                long int row, long int col) {
+    return MATRIX(*m, row, col);
+}
+
+/**
+ * \function igraph_matrix_e_ptr
+ * Pointer to an element of a matrix.
+ *
+ * The function returns a pointer to an element. No range checking is
+ * performed.
+ * \param m The input matrix.
+ * \param row The row index.
+ * \param col The column index.
+ * \return Pointer to the element in the given row and column.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m,
+                                     long int row, long int col) {
+    return &MATRIX(*m, row, col);
+}
+
+/**
+ * \function igraph_matrix_set
+ * Set an element.
+ *
+ * Set an element of a matrix. No range checking is performed.
+ * \param m The input matrix.
+ * \param row The row index.
+ * \param col The column index.
+ * \param value The new value of the element.
+ *
+ * Time complexity: O(1).
+ */
+
+void FUNCTION(igraph_matrix, set)(TYPE(igraph_matrix)* m, long int row, long int col,
+                                  BASE value) {
+    MATRIX(*m, row, col) = value;
+}
+
+/**
+ * \function igraph_matrix_fill
+ * Fill with an element.
+ *
+ * Set the matrix to a constant matrix.
+ * \param m The input matrix.
+ * \param e The element to set.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+void FUNCTION(igraph_matrix, fill)(TYPE(igraph_matrix) *m, BASE e) {
+    FUNCTION(igraph_vector, fill)(&m->data, e);
+}
+
+/**
+ * \function igraph_matrix_update
+ * Update from another matrix.
+ *
+ * This function replicates \p from in the matrix \p to.
+ * Note that \p to must be already initialized.
+ * \param to The result matrix.
+ * \param from The matrix to replicate; it is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to,
+                                    const TYPE(igraph_matrix) *from) {
+
+    IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(to, from->nrow, from->ncol));
+    FUNCTION(igraph_vector, update)(&to->data, &from->data);
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_rbind
+ * Combine two matrices rowwise.
+ *
+ * This function places the rows of \p from below the rows of \c to
+ * and stores the result in \p to. The number of columns in the two
+ * matrices must match.
+ * \param to The upper matrix; the result is also stored here.
+ * \param from The lower matrix. It is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements in the newly created
+ * matrix.
+ */
+
+int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to,
+                                   const TYPE(igraph_matrix) *from) {
+    long int tocols = to->ncol, fromcols = from->ncol;
+    long int torows = to->nrow, fromrows = from->nrow;
+    long int offset, c, r, index, offset2;
+    if (tocols != fromcols) {
+        IGRAPH_ERROR("Cannot do rbind, number of columns do not match", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&to->data,
+                 tocols * (fromrows + torows)));
+    to->nrow += fromrows;
+
+    offset = (tocols - 1) * fromrows;
+    index = tocols * torows - 1;
+    for (c = tocols - 1; c > 0; c--) {
+        for (r = 0; r < torows; r++, index--) {
+            VECTOR(to->data)[index + offset] = VECTOR(to->data)[index];
+        }
+        offset -= fromrows;
+    }
+
+    offset = torows; offset2 = 0;
+    for (c = 0; c < tocols; c++) {
+        memcpy(VECTOR(to->data) + offset, VECTOR(from->data) + offset2,
+               sizeof(BASE) * (size_t) fromrows);
+        offset += fromrows + torows;
+        offset2 += fromrows;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_cbind
+ * Combine matrices columnwise.
+ *
+ * This function places the columns of \p from on the right of \p to,
+ * and stores the result in \p to.
+ * \param to The left matrix; the result is stored here too.
+ * \param from The right matrix. It is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements on the new matrix.
+ */
+
+int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to,
+                                   const TYPE(igraph_matrix) *from) {
+
+    long int tocols = to->ncol, fromcols = from->ncol;
+    long int torows = to->nrow, fromrows = from->nrow;
+    if (torows != fromrows) {
+        IGRAPH_ERROR("Cannot do rbind, number of rows do not match", IGRAPH_EINVAL);
+    }
+    IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(to, torows, tocols + fromcols));
+    FUNCTION(igraph_vector, copy_to)(&from->data, VECTOR(to->data) + tocols * torows);
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_swap
+ * Swap two matrices.
+ *
+ * The contents of the two matrices will be swapped. They must have the
+ * same dimensions.
+ * \param m1 The first matrix.
+ * \param m2 The second matrix.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements in the matrices.
+ */
+
+int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2) {
+    if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) {
+        IGRAPH_ERROR("Cannot swap non-conformant matrices", IGRAPH_EINVAL);
+    }
+    return FUNCTION(igraph_vector, swap)(&m1->data, &m2->data);
+}
+
+/**
+ * \function igraph_matrix_get_row
+ * Extract a row.
+ *
+ * Extract a row from a matrix and return it as a vector.
+ * \param m The input matrix.
+ * \param res Pointer to an initialized vector; it will be resized if
+ *   needed.
+ * \param index The index of the row to select.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of columns in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m,
+                                     TYPE(igraph_vector) *res, long int index) {
+    long int rows = m->nrow, cols = m->ncol;
+    long int i, j;
+
+    if (index >= rows) {
+        IGRAPH_ERROR("Index out of range for selecting matrix row", IGRAPH_EINVAL);
+    }
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, cols));
+
+    for (i = index, j = 0; j < cols; i += rows, j++) {
+        VECTOR(*res)[j] = VECTOR(m->data)[i];
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_set_row
+ * Set a row from a vector.
+ *
+ * Sets the elements of a row with the given vector. This has the effect of
+ * setting row \c index to have the elements in the vector \c v. The length of
+ * the vector and the number of columns in the matrix must match,
+ * otherwise an error is triggered.
+ * \param m The input matrix.
+ * \param v The vector containing the new elements of the row.
+ * \param index Index of the row to set.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of columns in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m,
+                                     const TYPE(igraph_vector) *v, long int index) {
+    long int rows = m->nrow, cols = m->ncol;
+    long int i, j;
+
+    if (index >= rows) {
+        IGRAPH_ERROR("Index out of range for selecting matrix row", IGRAPH_EINVAL);
+    }
+    if (FUNCTION(igraph_vector, size)(v) != cols) {
+        IGRAPH_ERROR("Cannot set matrix row, invalid vector length", IGRAPH_EINVAL);
+    }
+    for (i = index, j = 0; j < cols; i += rows, j++) {
+        VECTOR(m->data)[i] = VECTOR(*v)[j];
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_set_col
+ * Set a column from a vector.
+ *
+ * Sets the elements of a column with the given vector. In effect, column
+ * \c index will be set with elements from the vector \c v. The length of
+ * the vector and the number of rows in the matrix must match,
+ * otherwise an error is triggered.
+ * \param m The input matrix.
+ * \param v The vector containing the new elements of the column.
+ * \param index Index of the column to set.
+ * \return Error code.
+ *
+ * Time complexity: O(m), the number of rows in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m,
+                                     const TYPE(igraph_vector) *v, long int index) {
+    long int rows = m->nrow, cols = m->ncol;
+    long int i, j;
+
+    if (index >= cols) {
+        IGRAPH_ERROR("Index out of range for setting matrix column", IGRAPH_EINVAL);
+    }
+    if (FUNCTION(igraph_vector, size)(v) != rows) {
+        IGRAPH_ERROR("Cannot set matrix column, invalid vector length", IGRAPH_EINVAL);
+    }
+    for (i = index * rows, j = 0; j < rows; i++, j++) {
+        VECTOR(m->data)[i] = VECTOR(*v)[j];
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_swap_rows
+ * Swap two rows.
+ *
+ * Swap two rows in the matrix.
+ * \param m The input matrix.
+ * \param i The index of the first row.
+ * \param j The index of the second row.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of columns.
+ */
+
+int FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m,
+                                       long int i, long int j) {
+    long int ncol = m->ncol, nrow = m->nrow;
+    long int n = nrow * ncol;
+    long int index1, index2;
+    if (i >= nrow || j >= nrow) {
+        IGRAPH_ERROR("Cannot swap rows, index out of range", IGRAPH_EINVAL);
+    }
+    if (i == j) {
+        return 0;
+    }
+    for (index1 = i, index2 = j; index1 < n; index1 += nrow, index2 += nrow) {
+        BASE tmp;
+        tmp = VECTOR(m->data)[index1];
+        VECTOR(m->data)[index1] = VECTOR(m->data)[index2];
+        VECTOR(m->data)[index2] = tmp;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_swap_cols
+ * Swap two columns.
+ *
+ * Swap two columns in the matrix.
+ * \param m The input matrix.
+ * \param i The index of the first column.
+ * \param j The index of the second column.
+ * \return Error code.
+ *
+ * Time complexity: O(m), the number of rows.
+ */
+
+int FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m,
+                                       long int i, long int j) {
+    long int ncol = m->ncol, nrow = m->nrow;
+    long int k, index1, index2;
+    if (i >= ncol || j >= ncol) {
+        IGRAPH_ERROR("Cannot swap columns, index out of range", IGRAPH_EINVAL);
+    }
+    if (i == j) {
+        return 0;
+    }
+    for (index1 = i * nrow, index2 = j * nrow, k = 0; k < nrow; k++, index1++, index2++) {
+        BASE tmp = VECTOR(m->data)[index1];
+        VECTOR(m->data)[index1] = VECTOR(m->data)[index2];
+        VECTOR(m->data)[index2] = tmp;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_add_constant
+ * Add a constant to every element.
+ *
+ * \param m The input matrix.
+ * \param plud The constant to add.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, BASE plus) {
+    FUNCTION(igraph_vector, add_constant)(&m->data, plus);
+}
+
+/**
+ * \function igraph_matrix_add
+ * Add two matrices.
+ *
+ * Add \p m2 to \p m1, and store the result in \p m1. The dimensions of the
+ * matrices must match.
+ * \param m1 The first matrix; the result will be stored here.
+ * \param m2 The second matrix; it is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1,
+                                 const TYPE(igraph_matrix) *m2) {
+    if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) {
+        IGRAPH_ERROR("Cannot add non-conformant matrices", IGRAPH_EINVAL);
+    }
+    return FUNCTION(igraph_vector, add)(&m1->data, &m2->data);
+}
+
+/**
+ * \function igraph_matrix_sub
+ * Difference of two matrices.
+ *
+ * Subtract \p m2 from \p m1 and store the result in \p m1.
+ * The dimensions of the two matrices must match.
+ * \param m1 The first matrix; the result is stored here.
+ * \param m2 The second matrix; it is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1,
+                                 const TYPE(igraph_matrix) *m2) {
+    if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) {
+        IGRAPH_ERROR("Cannot subtract non-conformant matrices", IGRAPH_EINVAL);
+    }
+    return FUNCTION(igraph_vector, sub)(&m1->data, &m2->data);
+}
+
+/**
+ * \function igraph_matrix_mul_elements
+ * Elementwise multiplication.
+ *
+ * Multiply \p m1 by \p m2 elementwise and store the result in \p m1.
+ * The dimensions of the two matrices must match.
+ * \param m1 The first matrix; the result is stored here.
+ * \param m2 The second matrix; it is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2) {
+    if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) {
+        IGRAPH_ERROR("Cannot multiply non-conformant matrices", IGRAPH_EINVAL);
+    }
+    return FUNCTION(igraph_vector, mul)(&m1->data, &m2->data);
+}
+
+/**
+ * \function igraph_matrix_div_elements
+ * Elementwise division.
+ *
+ * Divide \p m1 by \p m2 elementwise and store the result in \p m1.
+ * The dimensions of the two matrices must match.
+ * \param m1 The dividend. The result is store here.
+ * \param m2 The divisor. It is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1,
+        const TYPE(igraph_matrix) *m2) {
+    if (m1->nrow != m2->nrow || m1->ncol != m2->ncol) {
+        IGRAPH_ERROR("Cannot divide non-conformant matrices", IGRAPH_EINVAL);
+    }
+    return FUNCTION(igraph_vector, div)(&m1->data, &m2->data);
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \function igraph_matrix_min
+ * Minimum element.
+ *
+ * Returns the smallest element of a non-empty matrix.
+ * \param m The input matrix.
+ * \return The smallest element.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m) {
+    return FUNCTION(igraph_vector, min)(&m->data);
+}
+
+/**
+ * \function igraph_matrix_which_min
+ * Indices of the minimum.
+ *
+ * Gives the indices of the (first) smallest element in a non-empty
+ * matrix.
+ * \param m The matrix.
+ * \param i Pointer to a <type>long int</type>. The row index of the
+ *   minimum is stored here.
+ * \param j Pointer to a <type>long int</type>. The column index of
+ *   the minimum is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, which_min)(const TYPE(igraph_matrix) *m,
+                                       long int *i, long int *j) {
+    long int vmin = FUNCTION(igraph_vector, which_min)(&m->data);
+    *i = vmin % m->nrow;
+    *j = vmin / m->nrow;
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_which_max
+ * Indices of the maximum.
+ *
+ * Gives the indices of the (first) largest element in a non-empty
+ * matrix.
+ * \param m The matrix.
+ * \param i Pointer to a <type>long int</type>. The row index of the
+ *   maximum is stored here.
+ * \param j Pointer to a <type>long int</type>. The column index of
+ *   the maximum is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, which_max)(const TYPE(igraph_matrix) *m,
+                                       long int *i, long int *j) {
+    long int vmax = FUNCTION(igraph_vector, which_max)(&m->data);
+    *i = vmax % m->nrow;
+    *j = vmax / m->nrow;
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_minmax
+ * Minimum and maximum
+ *
+ * The maximum and minimum elements of a non-empty matrix.
+ * \param m The input matrix.
+ * \param min Pointer to a base type. The minimum is stored here.
+ * \param max Pointer to a base type. The maximum is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m,
+                                    BASE *min, BASE *max) {
+    return FUNCTION(igraph_vector, minmax)(&m->data, min, max);
+}
+
+/**
+ * \function igraph_matrix_which_minmax
+ * Indices of the minimum and maximum
+ *
+ * Find the positions of the smallest and largest elements of a
+ * non-empty matrix.
+ * \param m The input matrix.
+ * \param imin Pointer to a <type>long int</type>, the row index of
+ *   the minimum is stored here.
+ * \param jmin Pointer to a <type>long int</type>, the column index of
+ *   the minimum is stored here.
+ * \param imax Pointer to a <type>long int</type>, the row index of
+ *   the maximum is stored here.
+ * \param jmax Pointer to a <type>long int</type>, the column index of
+ *   the maximum is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+int FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m,
+        long int *imin, long int *jmin,
+        long int *imax, long int *jmax) {
+    long int vmin, vmax;
+    FUNCTION(igraph_vector, which_minmax)(&m->data, &vmin, &vmax);
+    *imin = vmin % m->nrow;
+    *jmin = vmin / m->nrow;
+    *imax = vmax % m->nrow;
+    *jmax = vmax / m->nrow;
+    return 0;
+}
+
+#endif
+
+/**
+ * \function igraph_matrix_isnull
+ * Check for a null matrix.
+ *
+ * Checks whether all elements are zero.
+ * \param m The input matrix.
+ * \return Boolean, \c TRUE is \p m contains only zeros and \c FALSE
+ *   otherwise.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m) {
+    return FUNCTION(igraph_vector, isnull)(&m->data);
+}
+
+/**
+ * \function igraph_matrix_empty
+ * Check for an empty matrix.
+ *
+ * It is possible to have a matrix with zero rows or zero columns, or
+ * even both. This functions checks for these.
+ * \param m The input matrix.
+ * \return Boolean, \c TRUE if the matrix contains zero elements, and
+ *    \c FALSE otherwise.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m) {
+    return FUNCTION(igraph_vector, empty)(&m->data);
+}
+
+/**
+ * \function igraph_matrix_is_symmetric
+ * Check for symmetric matrix.
+ *
+ * A non-square matrix is not symmetric by definition.
+ * \param m The input matrix.
+ * \return Boolean, \c TRUE if the matrix is square and symmetric, \c
+ *    FALSE otherwise.
+ *
+ * Time complexity: O(mn), the number of elements. O(1) for non-square
+ * matrices.
+ */
+
+igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m) {
+
+    long int n = m->nrow;
+    long int r, c;
+    if (m->ncol != n) {
+        return 0;
+    }
+    for (r = 1; r < n; r++) {
+        for (c = 0; c < r; c++) {
+            BASE a1 = MATRIX(*m, r, c);
+            BASE a2 = MATRIX(*m, c, r);
+#ifdef EQ
+            if (!EQ(a1, a2)) {
+                return 0;
+            }
+#else
+            if (a1 != a2) {
+                return 0;
+            }
+#endif
+        }
+    }
+    return 1;
+}
+
+/**
+ * \function igraph_matrix_prod
+ * Product of the elements.
+ *
+ * Note this function can result in overflow easily, even for not too
+ * big matrices.
+ * \param m The input matrix.
+ * \return The product of the elements.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m) {
+    return FUNCTION(igraph_vector, prod)(&m->data);
+}
+
+/**
+ * \function igraph_matrix_rowsum
+ * Rowwise sum.
+ *
+ * Calculate the sum of the elements in each row.
+ * \param m The input matrix.
+ * \param res Pointer to an initialized vector; the result is stored
+ *   here. It will be resized if necessary.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m,
+                                    TYPE(igraph_vector) *res) {
+    long int nrow = m->nrow, ncol = m->ncol;
+    long int r, c;
+    BASE sum;
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, nrow));
+    for (r = 0; r < nrow; r++) {
+        sum = ZERO;
+        for (c = 0; c < ncol; c++) {
+#ifdef SUM
+            SUM(sum, sum, MATRIX(*m, r, c));
+#else
+            sum += MATRIX(*m, r, c);
+#endif
+        }
+        VECTOR(*res)[r] = sum;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_colsum
+ * Columnwise sum.
+ *
+ * Calculate the sum of the elements in each column.
+ * \param m The input matrix.
+ * \param res Pointer to an initialized vector; the result is stored
+ *   here. It will be resized if necessary.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m,
+                                    TYPE(igraph_vector) *res) {
+    long int nrow = m->nrow, ncol = m->ncol;
+    long int r, c;
+    BASE sum;
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, ncol));
+    for (c = 0; c < ncol; c++) {
+        sum = ZERO;
+        for (r = 0; r < nrow; r++) {
+#ifdef SUM
+            SUM(sum, sum, MATRIX(*m, r, c));
+#else
+            sum += MATRIX(*m, r, c);
+#endif
+        }
+        VECTOR(*res)[c] = sum;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_contains
+ * Search for an element.
+ *
+ * Search for the given element in the matrix.
+ * \param m The input matrix.
+ * \param e The element to search for.
+ * \return Boolean, \c TRUE if the matrix contains \p e, \c FALSE
+ * otherwise.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+igraph_bool_t FUNCTION(igraph_matrix, contains)(const TYPE(igraph_matrix) *m,
+        BASE e) {
+    return FUNCTION(igraph_vector, contains)(&m->data, e);
+}
+
+/**
+ * \function igraph_matrix_search
+ * Search from a given position.
+ *
+ * Search for an element in a matrix and start the search from the
+ * given position. The search is performed columnwise.
+ * \param m The input matrix.
+ * \param from The position to search from, the positions are
+ *    enumerated columnwise.
+ * \param what The element to search for.
+ * \param pos Pointer to a <type>long int</type>. If the element is
+ *    found, then this is set to the position of its first appearance.
+ * \param row Pointer to a <type>long int</type>. If the element is
+ *    found, then this is set to its row index.
+ * \param col Pointer to a <type>long int</type>. If the element is
+ *    found, then this is set to its column index.
+ * \return Boolean, \c TRUE if the element is found, \c FALSE
+ *    otherwise.
+ *
+ * Time complexity: O(mn), the number of elements.
+ */
+
+igraph_bool_t FUNCTION(igraph_matrix, search)(const TYPE(igraph_matrix) *m,
+        long int from, BASE what,
+        long int *pos,
+        long int *row, long int *col) {
+    igraph_bool_t find = FUNCTION(igraph_vector, search)(&m->data, from, what, pos);
+    if (find) {
+        *row = *pos % m->nrow;
+        *col = *pos / m->nrow;
+    }
+    return find;
+}
+
+/**
+ * \function igraph_matrix_remove_row
+ * Remove a row.
+ *
+ * A row is removed from the matrix.
+ * \param m The input matrix.
+ * \param row The index of the row to remove.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements in the matrix.
+ */
+
+int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row) {
+
+    long int c, r, index = row + 1, leap = 1, n = m->nrow * m->ncol;
+    if (row >= m->nrow) {
+        IGRAPH_ERROR("Cannot remove row, index out of range", IGRAPH_EINVAL);
+    }
+
+    for (c = 0; c < m->ncol; c++) {
+        for (r = 0; r < m->nrow - 1 && index < n; r++) {
+            VECTOR(m->data)[index - leap] = VECTOR(m->data)[index];
+            index++;
+        }
+        leap++;
+        index++;
+    }
+    m->nrow--;
+    FUNCTION(igraph_vector, resize)(&m->data, m->nrow * m->ncol);
+    return 0;
+}
+
+/**
+ * \function igraph_matrix_select_cols
+ * \brief Select some columns of a matrix.
+ *
+ * This function selects some columns of a matrix and returns them in a
+ * new matrix. The result matrix should be initialized before calling
+ * the function.
+ * \param m The input matrix.
+ * \param res The result matrix. It should be initialized and will be
+ *    resized as needed.
+ * \param cols Vector; it contains the column indices (starting with
+ *    zero) to extract. Note that no range checking is performed.
+ * \return Error code.
+ *
+ * Time complexity: O(nm), n is the number of rows, m the number of
+ * columns of the result matrix.
+ */
+
+int FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m,
+        TYPE(igraph_matrix) *res,
+        const igraph_vector_t *cols) {
+    long int ncols = igraph_vector_size(cols);
+    long int nrows = m->nrow;
+    long int i, j;
+
+    IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(res, nrows, ncols));
+    for (i = 0; i < nrows; i++) {
+        for (j = 0; j < ncols; j++) {
+            MATRIX(*res, i, j) = MATRIX(*m, i, (long int)VECTOR(*cols)[j]);
+        }
+    }
+    return 0;
+}
+
+#ifdef OUT_FORMAT
+
+#ifndef USING_R
+int FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m) {
+
+    long int nr = FUNCTION(igraph_matrix, nrow)(m);
+    long int nc = FUNCTION(igraph_matrix, ncol)(m);
+    long int i, j;
+    for (i = 0; i < nr; i++) {
+        for (j = 0; j < nc; j++) {
+            if (j != 0) {
+                putchar(' ');
+            }
+            printf(OUT_FORMAT, MATRIX(*m, i, j));
+        }
+        printf("\n");
+    }
+
+    return 0;
+}
+
+int FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m,
+                                    const char *format) {
+    long int nr = FUNCTION(igraph_matrix, nrow)(m);
+    long int nc = FUNCTION(igraph_matrix, ncol)(m);
+    long int i, j;
+    for (i = 0; i < nr; i++) {
+        for (j = 0; j < nc; j++) {
+            if (j != 0) {
+                putchar(' ');
+            }
+            printf(format, MATRIX(*m, i, j));
+        }
+        printf("\n");
+    }
+
+    return 0;
+}
+
+#endif
+
+int FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m,
+                                    FILE *file) {
+
+    long int nr = FUNCTION(igraph_matrix, nrow)(m);
+    long int nc = FUNCTION(igraph_matrix, ncol)(m);
+    long int i, j;
+    for (i = 0; i < nr; i++) {
+        for (j = 0; j < nc; j++) {
+            if (j != 0) {
+                fputc(' ', file);
+            }
+            fprintf(file, OUT_FORMAT, MATRIX(*m, i, j));
+        }
+        fprintf(file, "\n");
+    }
+
+    return 0;
+}
+
+#endif
diff --git a/igraph/include/maximal_cliques_template.h b/igraph/include/maximal_cliques_template.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/maximal_cliques_template.h
@@ -0,0 +1,409 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifdef IGRAPH_MC_ORIG
+#define RESTYPE igraph_vector_ptr_t *res
+#define RESNAME res
+#define SUFFIX
+#define RECORD do {                         \
+        igraph_vector_t *cl=igraph_Calloc(1, igraph_vector_t);      \
+        int j;                              \
+        if (!cl) {                              \
+            IGRAPH_ERROR("Cannot list maximal cliques", IGRAPH_ENOMEM);   \
+        }                                   \
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(res, cl));             \
+        IGRAPH_CHECK(igraph_vector_init(cl, clsize));                   \
+        for (j=0; j<clsize; j++) { VECTOR(*cl)[j] = VECTOR(*R)[j]; }    \
+    } while (0)
+#define FINALLY do {                    \
+        igraph_vector_ptr_clear(res);           \
+        IGRAPH_FINALLY(igraph_i_maximal_cliques_free, res); \
+    } while (0)
+#define FOR_LOOP_OVER_VERTICES for (i=0; i<no_of_nodes; i++) {
+#define FOR_LOOP_OVER_VERTICES_PREPARE
+#endif
+
+#ifdef IGRAPH_MC_COUNT
+    #define RESTYPE igraph_integer_t *res
+    #define RESNAME res
+    #define SUFFIX _count
+    #define RECORD (*res)++
+    #define FINALLY *res=0;
+    #define FOR_LOOP_OVER_VERTICES for (i=0; i<no_of_nodes; i++) {
+    #define FOR_LOOP_OVER_VERTICES_PREPARE
+#endif
+
+#ifdef IGRAPH_MC_FILE
+    #define RESTYPE FILE *res
+    #define RESNAME res
+    #define SUFFIX _file
+    #define RECORD igraph_vector_int_fprint(R, res)
+    #define FINALLY
+    #define FOR_LOOP_OVER_VERTICES for (i=0; i<no_of_nodes; i++) {
+    #define FOR_LOOP_OVER_VERTICES_PREPARE
+#endif
+
+#ifdef IGRAPH_MC_FULL
+#define RESTYPE                 \
+    igraph_vector_int_t *subset,            \
+    igraph_vector_ptr_t *res,           \
+    igraph_integer_t *no,           \
+    FILE *outfile
+#define RESNAME subset, res, no, outfile
+#define SUFFIX _subset
+#define RECORD do {                         \
+        if (res) {                                \
+            igraph_vector_t *cl=igraph_Calloc(1, igraph_vector_t);      \
+            int j;                              \
+            if (!cl) {                              \
+                IGRAPH_ERROR("Cannot list maximal cliques", IGRAPH_ENOMEM);   \
+            }                                   \
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(res, cl));             \
+            IGRAPH_CHECK(igraph_vector_init(cl, clsize));                   \
+            for (j=0; j<clsize; j++) { VECTOR(*cl)[j] = VECTOR(*R)[j]; }    \
+        }                                 \
+        if (no) { (*no)++; }                              \
+        if (outfile) { igraph_vector_int_fprint(R, outfile); }        \
+    } while (0)
+#define FINALLY do {                        \
+        if (res) {                            \
+            igraph_vector_ptr_clear(res);               \
+            IGRAPH_FINALLY(igraph_i_maximal_cliques_free_full, res);    \
+        }                             \
+        if (no) { *no=0; }                        \
+    } while (0)
+#define FOR_LOOP_OVER_VERTICES                  \
+    nn= subset ? igraph_vector_int_size(subset) : no_of_nodes;    \
+    for (ii=0; ii<nn; ii++) {
+#define FOR_LOOP_OVER_VERTICES_PREPARE do {  \
+        i= subset ? VECTOR(*subset)[ii] : ii;    \
+    } while (0)
+#endif
+
+#ifdef IGRAPH_MC_CALLBACK
+#define RESTYPE \
+    igraph_clique_handler_t *cliquehandler_fn, \
+    void *arg
+#define RESNAME cliquehandler_fn, arg
+#define SUFFIX _callback
+#define RECORD do { \
+        igraph_vector_t *cl=igraph_Calloc(1, igraph_vector_t); \
+        long j; \
+        if (!cl) { \
+            IGRAPH_ERROR("Cannot list maximal cliques", IGRAPH_ENOMEM); \
+        } \
+        IGRAPH_CHECK(igraph_vector_init(cl, clsize)); \
+        for (j=0; j<clsize; j++) { VECTOR(*cl)[j] = VECTOR(*R)[j]; } \
+        if (!cliquehandler_fn(cl, arg)) \
+            return IGRAPH_STOP; \
+    } while (0)
+#define FINALLY
+#define FOR_LOOP_OVER_VERTICES for (i=0; i<no_of_nodes; i++) {
+#define FOR_LOOP_OVER_VERTICES_PREPARE
+#endif
+
+#ifdef IGRAPH_MC_HIST
+#define RESTYPE igraph_vector_t *hist
+#define RESNAME hist
+#define SUFFIX _hist
+#define RECORD do { \
+        long hsize = igraph_vector_size(hist); \
+        if (clsize > hsize) { \
+            long hcapacity = igraph_vector_capacity(hist); \
+            long j; \
+            int err; \
+            if (hcapacity < clsize && clsize < 2*hcapacity) \
+                err = igraph_vector_reserve(hist, 2*hcapacity); \
+            err = igraph_vector_resize(hist, clsize); \
+            if (err != IGRAPH_SUCCESS) \
+                IGRAPH_ERROR("Cannot count maximal cliques", IGRAPH_ENOMEM); \
+            for (j=hsize; j < clsize; j++) \
+                VECTOR(*hist)[j] = 0; \
+        } \
+        VECTOR(*hist)[clsize-1] += 1; \
+    } while (0)
+#define FINALLY \
+    igraph_vector_clear(hist); \
+    igraph_vector_reserve(hist, 50); /* initially reserve space for 50 elements */
+#define FOR_LOOP_OVER_VERTICES for (i=0; i<no_of_nodes; i++) {
+#define FOR_LOOP_OVER_VERTICES_PREPARE
+#endif
+
+#ifdef IGRAPH_MC_ORIG
+void igraph_i_maximal_cliques_free(void *ptr) {
+    igraph_vector_ptr_t *res = (igraph_vector_ptr_t*) ptr;
+    int i, n = igraph_vector_ptr_size(res);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *v = VECTOR(*res)[i];
+        if (v) {
+            igraph_Free(v);
+            igraph_vector_destroy(v);
+        }
+    }
+    igraph_vector_ptr_clear(res);
+}
+#endif
+
+#ifdef IGRAPH_MC_FULL
+void igraph_i_maximal_cliques_free_full(void *ptr) {
+    if (ptr) {
+        igraph_vector_ptr_t *res = (igraph_vector_ptr_t*) ptr;
+        int i, n = igraph_vector_ptr_size(res);
+        for (i = 0; i < n; i++) {
+            igraph_vector_t *v = VECTOR(*res)[i];
+            if (v) {
+                igraph_Free(v);
+                igraph_vector_destroy(v);
+            }
+        }
+        igraph_vector_ptr_clear(res);
+    }
+}
+#endif
+
+int FUNCTION(igraph_i_maximal_cliques_bk, SUFFIX)(
+    igraph_vector_int_t *PX, int PS, int PE,
+    int XS, int XE, int oldPS, int oldXE,
+    igraph_vector_int_t *R,
+    igraph_vector_int_t *pos,
+    igraph_adjlist_t *adjlist,
+    RESTYPE,
+    igraph_vector_int_t *nextv,
+    igraph_vector_int_t *H,
+    int min_size, int max_size) {
+
+    int err;
+
+    igraph_vector_int_push_back(H, -1); /* boundary */
+
+    if (PS > PE && XS > XE) {
+        /* Found a maximum clique, report it */
+        int clsize = igraph_vector_int_size(R);
+        if (min_size <= clsize && (clsize <= max_size || max_size <= 0)) {
+            RECORD;
+        }
+    } else if (PS <= PE) {
+        /* Select a pivot element */
+        int pivot, mynextv;
+        igraph_i_maximal_cliques_select_pivot(PX, PS, PE, XS, XE, pos,
+                                              adjlist, &pivot, nextv,
+                                              oldPS, oldXE);
+        while ((mynextv = igraph_vector_int_pop_back(nextv)) != -1) {
+            int newPS, newXE;
+
+            /* Going down, prepare */
+            igraph_i_maximal_cliques_down(PX, PS, PE, XS, XE, pos, adjlist,
+                                          mynextv, R, &newPS, &newXE);
+            /* Recursive call */
+            err = FUNCTION(igraph_i_maximal_cliques_bk, SUFFIX)(
+                      PX, newPS, PE, XS, newXE, PS, XE, R,
+                      pos, adjlist, RESNAME, nextv, H,
+                      min_size, max_size);
+
+            if (err == IGRAPH_STOP) {
+                return err;
+            } else {
+                IGRAPH_CHECK(err);
+            }
+            /* Putting v from P to X */
+            if (igraph_vector_int_tail(nextv) != -1) {
+                igraph_i_maximal_cliques_PX(PX, PS, &PE, &XS, XE, pos, adjlist,
+                                            mynextv, H);
+            }
+        }
+    }
+
+    /* Putting back vertices from X to P, see notes in H */
+    igraph_i_maximal_cliques_up(PX, PS, PE, XS, XE, pos, adjlist, R, H);
+
+    return 0;
+}
+
+int FUNCTION(igraph_maximal_cliques, SUFFIX)(
+    const igraph_t *graph,
+    RESTYPE,
+    igraph_integer_t min_size,
+    igraph_integer_t max_size) {
+
+    /* Implementation details. TODO */
+
+    igraph_vector_int_t PX, R, H, pos, nextv;
+    igraph_vector_t coreness, order;
+    igraph_vector_int_t rank; /* TODO: this is not needed */
+    int i, ii, nn, no_of_nodes = igraph_vcount(graph);
+    igraph_adjlist_t adjlist, fulladjlist;
+    igraph_real_t pgreset = round(no_of_nodes / 100.0), pg = pgreset, pgc = 0;
+    int err;
+    IGRAPH_UNUSED(nn);
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_WARNING("Edge directions are ignored for maximal clique "
+                       "calculation");
+    }
+
+    igraph_vector_init(&order, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_destroy, &order);
+    igraph_vector_int_init(&rank, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &rank);
+    igraph_vector_init(&coreness, no_of_nodes);
+    igraph_coreness(graph, &coreness, /*mode=*/ IGRAPH_ALL);
+    IGRAPH_FINALLY(igraph_vector_destroy, &coreness);
+    igraph_vector_qsort_ind(&coreness, &order, /*descending=*/ 0);
+    for (ii = 0; ii < no_of_nodes; ii++) {
+        int v = VECTOR(order)[ii];
+        VECTOR(rank)[v] = ii;
+    }
+
+    igraph_vector_destroy(&coreness);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL);
+
+    igraph_adjlist_simplify(&adjlist);
+    igraph_adjlist_init(graph, &fulladjlist, IGRAPH_ALL);
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &fulladjlist);
+    igraph_adjlist_simplify(&fulladjlist);
+    igraph_vector_int_init(&PX, 20);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &PX);
+    igraph_vector_int_init(&R,  20);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &R);
+    igraph_vector_int_init(&H, 100);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &H);
+    igraph_vector_int_init(&pos, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &pos);
+    igraph_vector_int_init(&nextv, 100);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &nextv);
+
+    FINALLY;
+
+    FOR_LOOP_OVER_VERTICES
+    int v;
+    int vrank;
+    igraph_vector_int_t *vneis;
+    int vdeg;
+    int Pptr, Xptr, PS, PE, XS, XE;
+    int j;
+
+    FOR_LOOP_OVER_VERTICES_PREPARE;
+
+    v = VECTOR(order)[i];
+    vrank = VECTOR(rank)[v];
+    vneis = igraph_adjlist_get(&fulladjlist, v);
+    vdeg = igraph_vector_int_size(vneis);
+    Pptr = 0; Xptr = vdeg - 1; PS = 0; XE = vdeg - 1;
+
+    pg--;
+    if (pg <= 0) {
+        IGRAPH_PROGRESS("Maximal cliques: ", pgc++, NULL);
+        pg = pgreset;
+    }
+
+    IGRAPH_ALLOW_INTERRUPTION();
+
+    igraph_vector_int_resize(&PX, vdeg);
+    igraph_vector_int_resize(&R, 1);
+    igraph_vector_int_resize(&H, 1);
+    igraph_vector_int_null(&pos); /* TODO: makes it quadratic? */
+    igraph_vector_int_resize(&nextv, 1);
+
+    VECTOR(H)[0] = -1;      /* marks the end of the recursion */
+    VECTOR(nextv)[0] = -1;
+
+    /* ================================================================*/
+    /* P <- G(v[i]) intersect { v[i+1], ..., v[n-1] }
+       X <- G(v[i]) intersect { v[0], ..., v[i-1] } */
+
+    VECTOR(R)[0] = v;
+    for (j = 0; j < vdeg; j++) {
+        int vx = VECTOR(*vneis)[j];
+        if (VECTOR(rank)[vx] > vrank) {
+            VECTOR(PX)[Pptr] = vx;
+            VECTOR(pos)[vx] = Pptr + 1;
+            Pptr++;
+        } else if (VECTOR(rank)[vx] < vrank) {
+            VECTOR(PX)[Xptr] = vx;
+            VECTOR(pos)[vx] = Xptr + 1;
+            Xptr--;
+        }
+    }
+
+    PE = Pptr - 1; XS = Xptr + 1; /* end of P, start of X in PX */
+
+    /* Create an adjacency list that is specific to the
+       v vertex. It only contains 'v' and its neighbors. Moreover, we
+       only deal with the vertices in P and X (and R). */
+    igraph_vector_int_update(igraph_adjlist_get(&adjlist, v),
+                             igraph_adjlist_get(&fulladjlist, v));
+    for (j = 0; j <= vdeg - 1; j++) {
+        int vv = VECTOR(PX)[j];
+        igraph_vector_int_t *fadj = igraph_adjlist_get(&fulladjlist, vv);
+        igraph_vector_int_t *radj = igraph_adjlist_get(&adjlist, vv);
+        int k, fn = igraph_vector_int_size(fadj);
+        igraph_vector_int_clear(radj);
+        for (k = 0; k < fn; k++) {
+            int nei = VECTOR(*fadj)[k];
+            int neipos = VECTOR(pos)[nei] - 1;
+            if (neipos >= PS && neipos <= XE) {
+                igraph_vector_int_push_back(radj, nei);
+            }
+        }
+    }
+
+    /* Reorder the adjacency lists, according to P and X. */
+    igraph_i_maximal_cliques_reorder_adjlists(&PX, PS, PE, XS, XE, &pos,
+            &adjlist);
+
+    err = FUNCTION(igraph_i_maximal_cliques_bk, SUFFIX)(
+              &PX, PS, PE, XS, XE, PS, XE, &R, &pos,
+              &adjlist, RESNAME, &nextv, &H, min_size,
+              max_size);
+    if (err == IGRAPH_STOP) {
+        break;
+    } else {
+        IGRAPH_CHECK(err);
+    }
+}
+
+IGRAPH_PROGRESS("Maximal cliques: ", 100.0, NULL);
+
+igraph_vector_int_destroy(&nextv);
+igraph_vector_int_destroy(&pos);
+igraph_vector_int_destroy(&H);
+igraph_vector_int_destroy(&R);
+igraph_vector_int_destroy(&PX);
+igraph_adjlist_destroy(&fulladjlist);
+igraph_adjlist_destroy(&adjlist);
+igraph_vector_int_destroy(&rank);
+igraph_vector_destroy(&order);
+IGRAPH_FINALLY_CLEAN(10); /* + res */
+
+return 0;
+}
+
+#undef RESTYPE
+#undef RESNAME
+#undef SUFFIX
+#undef RECORD
+#undef FINALLY
+#undef FOR_LOOP_OVER_VERTICES
+#undef FOR_LOOP_OVER_VERTICES_PREPARE
diff --git a/igraph/include/plfit/arithmetic_ansi.h b/igraph/include/plfit/arithmetic_ansi.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/arithmetic_ansi.h
@@ -0,0 +1,133 @@
+/*
+ *      ANSI C implementation of vector operations.
+ *
+ * Copyright (c) 2007-2010 Naoaki Okazaki
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/* $Id: arithmetic_ansi.h 65 2010-01-29 12:19:16Z naoaki $ */
+
+#include <stdlib.h>
+#include <memory.h>
+
+#if     LBFGS_FLOAT == 32 && LBFGS_IEEE_FLOAT
+#define fsigndiff(x, y) (((*(uint32_t*)(x)) ^ (*(uint32_t*)(y))) & 0x80000000U)
+#else
+#define fsigndiff(x, y) (*(x) * (*(y) / fabs(*(y))) < 0.)
+#endif/*LBFGS_IEEE_FLOAT*/
+
+inline static void* vecalloc(size_t size)
+{
+    void *memblock = malloc(size);
+    if (memblock) {
+        memset(memblock, 0, size);
+    }
+    return memblock;
+}
+
+inline static void vecfree(void *memblock)
+{
+    free(memblock);
+}
+
+inline static void vecset(lbfgsfloatval_t *x, const lbfgsfloatval_t c, const int n)
+{
+    int i;
+    
+    for (i = 0;i < n;++i) {
+        x[i] = c;
+    }
+}
+
+inline static void veccpy(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n)
+{
+    int i;
+
+    for (i = 0;i < n;++i) {
+        y[i] = x[i];
+    }
+}
+
+inline static void vecncpy(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n)
+{
+    int i;
+
+    for (i = 0;i < n;++i) {
+        y[i] = -x[i];
+    }
+}
+
+inline static void vecadd(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const lbfgsfloatval_t c, const int n)
+{
+    int i;
+
+    for (i = 0;i < n;++i) {
+        y[i] += c * x[i];
+    }
+}
+
+inline static void vecdiff(lbfgsfloatval_t *z, const lbfgsfloatval_t *x, const lbfgsfloatval_t *y, const int n)
+{
+    int i;
+
+    for (i = 0;i < n;++i) {
+        z[i] = x[i] - y[i];
+    }
+}
+
+inline static void vecscale(lbfgsfloatval_t *y, const lbfgsfloatval_t c, const int n)
+{
+    int i;
+
+    for (i = 0;i < n;++i) {
+        y[i] *= c;
+    }
+}
+
+inline static void vecmul(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n)
+{
+    int i;
+
+    for (i = 0;i < n;++i) {
+        y[i] *= x[i];
+    }
+}
+
+inline static void vecdot(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const lbfgsfloatval_t *y, const int n)
+{
+    int i;
+    *s = 0.;
+    for (i = 0;i < n;++i) {
+        *s += x[i] * y[i];
+    }
+}
+
+inline static void vec2norm(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const int n)
+{
+    vecdot(s, x, x, n);
+    *s = (lbfgsfloatval_t)sqrt(*s);
+}
+
+inline static void vec2norminv(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const int n)
+{
+    vec2norm(s, x, n);
+    *s = (lbfgsfloatval_t)(1.0 / *s);
+}
diff --git a/igraph/include/plfit/arithmetic_sse_double.h b/igraph/include/plfit/arithmetic_sse_double.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/arithmetic_sse_double.h
@@ -0,0 +1,294 @@
+/*
+ *      SSE2 implementation of vector oprations (64bit double).
+ *
+ * Copyright (c) 2007-2010 Naoaki Okazaki
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/* $Id: arithmetic_sse_double.h 65 2010-01-29 12:19:16Z naoaki $ */
+
+#include <stdlib.h>
+
+#if !defined(__APPLE__)
+#include <malloc.h>
+#endif
+
+#include <memory.h>
+
+#if     1400 <= _MSC_VER
+#include <intrin.h>
+#endif/*1400 <= _MSC_VER*/
+
+#if     HAVE_EMMINTRIN_H
+#include <emmintrin.h>
+#endif/*HAVE_EMMINTRIN_H*/
+
+inline static void* vecalloc(size_t size)
+{
+#ifdef	_MSC_VER
+    void *memblock = _aligned_malloc(size, 16);
+#elif defined(__APPLE__)
+	/* Memory on Mac OS X is already aligned to 16 bytes */
+	void *memblock = malloc(size);
+#else
+    void *memblock = memalign(16, size);
+#endif
+    if (memblock != NULL) {
+        memset(memblock, 0, size);
+    }
+    return memblock;
+}
+
+inline static void vecfree(void *memblock)
+{
+#ifdef	_MSC_VER
+    _aligned_free(memblock);
+#else
+    free(memblock);
+#endif
+}
+
+#define fsigndiff(x, y) \
+    ((_mm_movemask_pd(_mm_set_pd(*(x), *(y))) + 1) & 0x002)
+
+#define vecset(x, c, n) \
+{ \
+    int i; \
+    __m128d XMM0 = _mm_set1_pd(c); \
+    for (i = 0;i < (n);i += 8) { \
+        _mm_store_pd((x)+i  , XMM0); \
+        _mm_store_pd((x)+i+2, XMM0); \
+        _mm_store_pd((x)+i+4, XMM0); \
+        _mm_store_pd((x)+i+6, XMM0); \
+    } \
+}
+
+#define veccpy(y, x, n) \
+{ \
+    int i; \
+    for (i = 0;i < (n);i += 8) { \
+        __m128d XMM0 = _mm_load_pd((x)+i  ); \
+        __m128d XMM1 = _mm_load_pd((x)+i+2); \
+        __m128d XMM2 = _mm_load_pd((x)+i+4); \
+        __m128d XMM3 = _mm_load_pd((x)+i+6); \
+        _mm_store_pd((y)+i  , XMM0); \
+        _mm_store_pd((y)+i+2, XMM1); \
+        _mm_store_pd((y)+i+4, XMM2); \
+        _mm_store_pd((y)+i+6, XMM3); \
+    } \
+}
+
+#define vecncpy(y, x, n) \
+{ \
+    int i; \
+    for (i = 0;i < (n);i += 8) { \
+        __m128d XMM0 = _mm_setzero_pd(); \
+        __m128d XMM1 = _mm_setzero_pd(); \
+        __m128d XMM2 = _mm_setzero_pd(); \
+        __m128d XMM3 = _mm_setzero_pd(); \
+        __m128d XMM4 = _mm_load_pd((x)+i  ); \
+        __m128d XMM5 = _mm_load_pd((x)+i+2); \
+        __m128d XMM6 = _mm_load_pd((x)+i+4); \
+        __m128d XMM7 = _mm_load_pd((x)+i+6); \
+        XMM0 = _mm_sub_pd(XMM0, XMM4); \
+        XMM1 = _mm_sub_pd(XMM1, XMM5); \
+        XMM2 = _mm_sub_pd(XMM2, XMM6); \
+        XMM3 = _mm_sub_pd(XMM3, XMM7); \
+        _mm_store_pd((y)+i  , XMM0); \
+        _mm_store_pd((y)+i+2, XMM1); \
+        _mm_store_pd((y)+i+4, XMM2); \
+        _mm_store_pd((y)+i+6, XMM3); \
+    } \
+}
+
+#define vecadd(y, x, c, n) \
+{ \
+    int i; \
+    __m128d XMM7 = _mm_set1_pd(c); \
+    for (i = 0;i < (n);i += 4) { \
+        __m128d XMM0 = _mm_load_pd((x)+i  ); \
+        __m128d XMM1 = _mm_load_pd((x)+i+2); \
+        __m128d XMM2 = _mm_load_pd((y)+i  ); \
+        __m128d XMM3 = _mm_load_pd((y)+i+2); \
+        XMM0 = _mm_mul_pd(XMM0, XMM7); \
+        XMM1 = _mm_mul_pd(XMM1, XMM7); \
+        XMM2 = _mm_add_pd(XMM2, XMM0); \
+        XMM3 = _mm_add_pd(XMM3, XMM1); \
+        _mm_store_pd((y)+i  , XMM2); \
+        _mm_store_pd((y)+i+2, XMM3); \
+    } \
+}
+
+#define vecdiff(z, x, y, n) \
+{ \
+    int i; \
+    for (i = 0;i < (n);i += 8) { \
+        __m128d XMM0 = _mm_load_pd((x)+i  ); \
+        __m128d XMM1 = _mm_load_pd((x)+i+2); \
+        __m128d XMM2 = _mm_load_pd((x)+i+4); \
+        __m128d XMM3 = _mm_load_pd((x)+i+6); \
+        __m128d XMM4 = _mm_load_pd((y)+i  ); \
+        __m128d XMM5 = _mm_load_pd((y)+i+2); \
+        __m128d XMM6 = _mm_load_pd((y)+i+4); \
+        __m128d XMM7 = _mm_load_pd((y)+i+6); \
+        XMM0 = _mm_sub_pd(XMM0, XMM4); \
+        XMM1 = _mm_sub_pd(XMM1, XMM5); \
+        XMM2 = _mm_sub_pd(XMM2, XMM6); \
+        XMM3 = _mm_sub_pd(XMM3, XMM7); \
+        _mm_store_pd((z)+i  , XMM0); \
+        _mm_store_pd((z)+i+2, XMM1); \
+        _mm_store_pd((z)+i+4, XMM2); \
+        _mm_store_pd((z)+i+6, XMM3); \
+    } \
+}
+
+#define vecscale(y, c, n) \
+{ \
+    int i; \
+    __m128d XMM7 = _mm_set1_pd(c); \
+    for (i = 0;i < (n);i += 4) { \
+        __m128d XMM0 = _mm_load_pd((y)+i  ); \
+        __m128d XMM1 = _mm_load_pd((y)+i+2); \
+        XMM0 = _mm_mul_pd(XMM0, XMM7); \
+        XMM1 = _mm_mul_pd(XMM1, XMM7); \
+        _mm_store_pd((y)+i  , XMM0); \
+        _mm_store_pd((y)+i+2, XMM1); \
+    } \
+}
+
+#define vecmul(y, x, n) \
+{ \
+    int i; \
+    for (i = 0;i < (n);i += 8) { \
+        __m128d XMM0 = _mm_load_pd((x)+i  ); \
+        __m128d XMM1 = _mm_load_pd((x)+i+2); \
+        __m128d XMM2 = _mm_load_pd((x)+i+4); \
+        __m128d XMM3 = _mm_load_pd((x)+i+6); \
+        __m128d XMM4 = _mm_load_pd((y)+i  ); \
+        __m128d XMM5 = _mm_load_pd((y)+i+2); \
+        __m128d XMM6 = _mm_load_pd((y)+i+4); \
+        __m128d XMM7 = _mm_load_pd((y)+i+6); \
+        XMM4 = _mm_mul_pd(XMM4, XMM0); \
+        XMM5 = _mm_mul_pd(XMM5, XMM1); \
+        XMM6 = _mm_mul_pd(XMM6, XMM2); \
+        XMM7 = _mm_mul_pd(XMM7, XMM3); \
+        _mm_store_pd((y)+i  , XMM4); \
+        _mm_store_pd((y)+i+2, XMM5); \
+        _mm_store_pd((y)+i+4, XMM6); \
+        _mm_store_pd((y)+i+6, XMM7); \
+    } \
+}
+
+
+
+#if     3 <= __SSE__
+/*
+    Horizontal add with haddps SSE3 instruction. The work register (rw)
+    is unused.
+ */
+#define __horizontal_sum(r, rw) \
+    r = _mm_hadd_ps(r, r); \
+    r = _mm_hadd_ps(r, r);
+
+#else
+/*
+    Horizontal add with SSE instruction. The work register (rw) is used.
+ */
+#define __horizontal_sum(r, rw) \
+    rw = r; \
+    r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(1, 0, 3, 2)); \
+    r = _mm_add_ps(r, rw); \
+    rw = r; \
+    r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(2, 3, 0, 1)); \
+    r = _mm_add_ps(r, rw);
+
+#endif
+
+#define vecdot(s, x, y, n) \
+{ \
+    int i; \
+    __m128d XMM0 = _mm_setzero_pd(); \
+    __m128d XMM1 = _mm_setzero_pd(); \
+    __m128d XMM2, XMM3, XMM4, XMM5; \
+    for (i = 0;i < (n);i += 4) { \
+        XMM2 = _mm_load_pd((x)+i  ); \
+        XMM3 = _mm_load_pd((x)+i+2); \
+        XMM4 = _mm_load_pd((y)+i  ); \
+        XMM5 = _mm_load_pd((y)+i+2); \
+        XMM2 = _mm_mul_pd(XMM2, XMM4); \
+        XMM3 = _mm_mul_pd(XMM3, XMM5); \
+        XMM0 = _mm_add_pd(XMM0, XMM2); \
+        XMM1 = _mm_add_pd(XMM1, XMM3); \
+    } \
+    XMM0 = _mm_add_pd(XMM0, XMM1); \
+    XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \
+    XMM0 = _mm_add_pd(XMM0, XMM1); \
+    _mm_store_sd((s), XMM0); \
+}
+
+#define vec2norm(s, x, n) \
+{ \
+    int i; \
+    __m128d XMM0 = _mm_setzero_pd(); \
+    __m128d XMM1 = _mm_setzero_pd(); \
+    __m128d XMM2, XMM3, XMM4, XMM5; \
+    for (i = 0;i < (n);i += 4) { \
+        XMM2 = _mm_load_pd((x)+i  ); \
+        XMM3 = _mm_load_pd((x)+i+2); \
+        XMM4 = XMM2; \
+        XMM5 = XMM3; \
+        XMM2 = _mm_mul_pd(XMM2, XMM4); \
+        XMM3 = _mm_mul_pd(XMM3, XMM5); \
+        XMM0 = _mm_add_pd(XMM0, XMM2); \
+        XMM1 = _mm_add_pd(XMM1, XMM3); \
+    } \
+    XMM0 = _mm_add_pd(XMM0, XMM1); \
+    XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \
+    XMM0 = _mm_add_pd(XMM0, XMM1); \
+    XMM0 = _mm_sqrt_pd(XMM0); \
+    _mm_store_sd((s), XMM0); \
+}
+
+
+#define vec2norminv(s, x, n) \
+{ \
+    int i; \
+    __m128d XMM0 = _mm_setzero_pd(); \
+    __m128d XMM1 = _mm_setzero_pd(); \
+    __m128d XMM2, XMM3, XMM4, XMM5; \
+    for (i = 0;i < (n);i += 4) { \
+        XMM2 = _mm_load_pd((x)+i  ); \
+        XMM3 = _mm_load_pd((x)+i+2); \
+        XMM4 = XMM2; \
+        XMM5 = XMM3; \
+        XMM2 = _mm_mul_pd(XMM2, XMM4); \
+        XMM3 = _mm_mul_pd(XMM3, XMM5); \
+        XMM0 = _mm_add_pd(XMM0, XMM2); \
+        XMM1 = _mm_add_pd(XMM1, XMM3); \
+    } \
+    XMM2 = _mm_set1_pd(1.0); \
+    XMM0 = _mm_add_pd(XMM0, XMM1); \
+    XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \
+    XMM0 = _mm_add_pd(XMM0, XMM1); \
+    XMM0 = _mm_sqrt_pd(XMM0); \
+    XMM2 = _mm_div_pd(XMM2, XMM0); \
+    _mm_store_sd((s), XMM2); \
+}
diff --git a/igraph/include/plfit/arithmetic_sse_float.h b/igraph/include/plfit/arithmetic_sse_float.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/arithmetic_sse_float.h
@@ -0,0 +1,291 @@
+/*
+ *      SSE/SSE3 implementation of vector oprations (32bit float).
+ *
+ * Copyright (c) 2007-2010 Naoaki Okazaki
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/* $Id: arithmetic_sse_float.h 65 2010-01-29 12:19:16Z naoaki $ */
+
+#include <stdlib.h>
+
+#if !defined(__APPLE__)
+#include <malloc.h>
+#endif
+
+#include <memory.h>
+
+#if     1400 <= _MSC_VER
+#include <intrin.h>
+#endif/*_MSC_VER*/
+
+#if     HAVE_XMMINTRIN_H
+#include <xmmintrin.h>
+#endif/*HAVE_XMMINTRIN_H*/
+
+#if     LBFGS_FLOAT == 32 && LBFGS_IEEE_FLOAT
+#define fsigndiff(x, y) (((*(uint32_t*)(x)) ^ (*(uint32_t*)(y))) & 0x80000000U)
+#else
+#define fsigndiff(x, y) (*(x) * (*(y) / fabs(*(y))) < 0.)
+#endif/*LBFGS_IEEE_FLOAT*/
+
+inline static void* vecalloc(size_t size)
+{
+    void *memblock = _aligned_malloc(size, 16);
+    if (memblock != NULL) {
+        memset(memblock, 0, size);
+    }
+    return memblock;
+}
+
+inline static void vecfree(void *memblock)
+{
+    _aligned_free(memblock);
+}
+
+#define vecset(x, c, n) \
+{ \
+    int i; \
+    __m128 XMM0 = _mm_set_ps1(c); \
+    for (i = 0;i < (n);i += 16) { \
+        _mm_store_ps((x)+i   , XMM0); \
+        _mm_store_ps((x)+i+ 4, XMM0); \
+        _mm_store_ps((x)+i+ 8, XMM0); \
+        _mm_store_ps((x)+i+12, XMM0); \
+    } \
+}
+
+#define veccpy(y, x, n) \
+{ \
+    int i; \
+    for (i = 0;i < (n);i += 16) { \
+        __m128 XMM0 = _mm_load_ps((x)+i   ); \
+        __m128 XMM1 = _mm_load_ps((x)+i+ 4); \
+        __m128 XMM2 = _mm_load_ps((x)+i+ 8); \
+        __m128 XMM3 = _mm_load_ps((x)+i+12); \
+        _mm_store_ps((y)+i   , XMM0); \
+        _mm_store_ps((y)+i+ 4, XMM1); \
+        _mm_store_ps((y)+i+ 8, XMM2); \
+        _mm_store_ps((y)+i+12, XMM3); \
+    } \
+}
+
+#define vecncpy(y, x, n) \
+{ \
+    int i; \
+    const uint32_t mask = 0x80000000; \
+    __m128 XMM4 = _mm_load_ps1((float*)&mask); \
+    for (i = 0;i < (n);i += 16) { \
+        __m128 XMM0 = _mm_load_ps((x)+i   ); \
+        __m128 XMM1 = _mm_load_ps((x)+i+ 4); \
+        __m128 XMM2 = _mm_load_ps((x)+i+ 8); \
+        __m128 XMM3 = _mm_load_ps((x)+i+12); \
+        XMM0 = _mm_xor_ps(XMM0, XMM4); \
+        XMM1 = _mm_xor_ps(XMM1, XMM4); \
+        XMM2 = _mm_xor_ps(XMM2, XMM4); \
+        XMM3 = _mm_xor_ps(XMM3, XMM4); \
+        _mm_store_ps((y)+i   , XMM0); \
+        _mm_store_ps((y)+i+ 4, XMM1); \
+        _mm_store_ps((y)+i+ 8, XMM2); \
+        _mm_store_ps((y)+i+12, XMM3); \
+    } \
+}
+
+#define vecadd(y, x, c, n) \
+{ \
+    int i; \
+    __m128 XMM7 = _mm_set_ps1(c); \
+    for (i = 0;i < (n);i += 8) { \
+        __m128 XMM0 = _mm_load_ps((x)+i  ); \
+        __m128 XMM1 = _mm_load_ps((x)+i+4); \
+        __m128 XMM2 = _mm_load_ps((y)+i  ); \
+        __m128 XMM3 = _mm_load_ps((y)+i+4); \
+        XMM0 = _mm_mul_ps(XMM0, XMM7); \
+        XMM1 = _mm_mul_ps(XMM1, XMM7); \
+        XMM2 = _mm_add_ps(XMM2, XMM0); \
+        XMM3 = _mm_add_ps(XMM3, XMM1); \
+        _mm_store_ps((y)+i  , XMM2); \
+        _mm_store_ps((y)+i+4, XMM3); \
+    } \
+}
+
+#define vecdiff(z, x, y, n) \
+{ \
+    int i; \
+    for (i = 0;i < (n);i += 16) { \
+        __m128 XMM0 = _mm_load_ps((x)+i   ); \
+        __m128 XMM1 = _mm_load_ps((x)+i+ 4); \
+        __m128 XMM2 = _mm_load_ps((x)+i+ 8); \
+        __m128 XMM3 = _mm_load_ps((x)+i+12); \
+        __m128 XMM4 = _mm_load_ps((y)+i   ); \
+        __m128 XMM5 = _mm_load_ps((y)+i+ 4); \
+        __m128 XMM6 = _mm_load_ps((y)+i+ 8); \
+        __m128 XMM7 = _mm_load_ps((y)+i+12); \
+        XMM0 = _mm_sub_ps(XMM0, XMM4); \
+        XMM1 = _mm_sub_ps(XMM1, XMM5); \
+        XMM2 = _mm_sub_ps(XMM2, XMM6); \
+        XMM3 = _mm_sub_ps(XMM3, XMM7); \
+        _mm_store_ps((z)+i   , XMM0); \
+        _mm_store_ps((z)+i+ 4, XMM1); \
+        _mm_store_ps((z)+i+ 8, XMM2); \
+        _mm_store_ps((z)+i+12, XMM3); \
+    } \
+}
+
+#define vecscale(y, c, n) \
+{ \
+    int i; \
+    __m128 XMM7 = _mm_set_ps1(c); \
+    for (i = 0;i < (n);i += 8) { \
+        __m128 XMM0 = _mm_load_ps((y)+i  ); \
+        __m128 XMM1 = _mm_load_ps((y)+i+4); \
+        XMM0 = _mm_mul_ps(XMM0, XMM7); \
+        XMM1 = _mm_mul_ps(XMM1, XMM7); \
+        _mm_store_ps((y)+i  , XMM0); \
+        _mm_store_ps((y)+i+4, XMM1); \
+    } \
+}
+
+#define vecmul(y, x, n) \
+{ \
+    int i; \
+    for (i = 0;i < (n);i += 16) { \
+        __m128 XMM0 = _mm_load_ps((x)+i   ); \
+        __m128 XMM1 = _mm_load_ps((x)+i+ 4); \
+        __m128 XMM2 = _mm_load_ps((x)+i+ 8); \
+        __m128 XMM3 = _mm_load_ps((x)+i+12); \
+        __m128 XMM4 = _mm_load_ps((y)+i   ); \
+        __m128 XMM5 = _mm_load_ps((y)+i+ 4); \
+        __m128 XMM6 = _mm_load_ps((y)+i+ 8); \
+        __m128 XMM7 = _mm_load_ps((y)+i+12); \
+        XMM4 = _mm_mul_ps(XMM4, XMM0); \
+        XMM5 = _mm_mul_ps(XMM5, XMM1); \
+        XMM6 = _mm_mul_ps(XMM6, XMM2); \
+        XMM7 = _mm_mul_ps(XMM7, XMM3); \
+        _mm_store_ps((y)+i   , XMM4); \
+        _mm_store_ps((y)+i+ 4, XMM5); \
+        _mm_store_ps((y)+i+ 8, XMM6); \
+        _mm_store_ps((y)+i+12, XMM7); \
+    } \
+}
+
+
+
+#if     3 <= __SSE__
+/*
+    Horizontal add with haddps SSE3 instruction. The work register (rw)
+    is unused.
+ */
+#define __horizontal_sum(r, rw) \
+    r = _mm_hadd_ps(r, r); \
+    r = _mm_hadd_ps(r, r);
+
+#else
+/*
+    Horizontal add with SSE instruction. The work register (rw) is used.
+ */
+#define __horizontal_sum(r, rw) \
+    rw = r; \
+    r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(1, 0, 3, 2)); \
+    r = _mm_add_ps(r, rw); \
+    rw = r; \
+    r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(2, 3, 0, 1)); \
+    r = _mm_add_ps(r, rw);
+
+#endif
+
+#define vecdot(s, x, y, n) \
+{ \
+    int i; \
+    __m128 XMM0 = _mm_setzero_ps(); \
+    __m128 XMM1 = _mm_setzero_ps(); \
+    __m128 XMM2, XMM3, XMM4, XMM5; \
+    for (i = 0;i < (n);i += 8) { \
+        XMM2 = _mm_load_ps((x)+i  ); \
+        XMM3 = _mm_load_ps((x)+i+4); \
+        XMM4 = _mm_load_ps((y)+i  ); \
+        XMM5 = _mm_load_ps((y)+i+4); \
+        XMM2 = _mm_mul_ps(XMM2, XMM4); \
+        XMM3 = _mm_mul_ps(XMM3, XMM5); \
+        XMM0 = _mm_add_ps(XMM0, XMM2); \
+        XMM1 = _mm_add_ps(XMM1, XMM3); \
+    } \
+    XMM0 = _mm_add_ps(XMM0, XMM1); \
+    __horizontal_sum(XMM0, XMM1); \
+    _mm_store_ss((s), XMM0); \
+}
+
+#define vec2norm(s, x, n) \
+{ \
+    int i; \
+    __m128 XMM0 = _mm_setzero_ps(); \
+    __m128 XMM1 = _mm_setzero_ps(); \
+    __m128 XMM2, XMM3; \
+    for (i = 0;i < (n);i += 8) { \
+        XMM2 = _mm_load_ps((x)+i  ); \
+        XMM3 = _mm_load_ps((x)+i+4); \
+        XMM2 = _mm_mul_ps(XMM2, XMM2); \
+        XMM3 = _mm_mul_ps(XMM3, XMM3); \
+        XMM0 = _mm_add_ps(XMM0, XMM2); \
+        XMM1 = _mm_add_ps(XMM1, XMM3); \
+    } \
+    XMM0 = _mm_add_ps(XMM0, XMM1); \
+    __horizontal_sum(XMM0, XMM1); \
+    XMM2 = XMM0; \
+    XMM1 = _mm_rsqrt_ss(XMM0); \
+    XMM3 = XMM1; \
+    XMM1 = _mm_mul_ss(XMM1, XMM1); \
+    XMM1 = _mm_mul_ss(XMM1, XMM3); \
+    XMM1 = _mm_mul_ss(XMM1, XMM0); \
+    XMM1 = _mm_mul_ss(XMM1, _mm_set_ss(-0.5f)); \
+    XMM3 = _mm_mul_ss(XMM3, _mm_set_ss(1.5f)); \
+    XMM3 = _mm_add_ss(XMM3, XMM1); \
+    XMM3 = _mm_mul_ss(XMM3, XMM2); \
+    _mm_store_ss((s), XMM3); \
+}
+
+#define vec2norminv(s, x, n) \
+{ \
+    int i; \
+    __m128 XMM0 = _mm_setzero_ps(); \
+    __m128 XMM1 = _mm_setzero_ps(); \
+    __m128 XMM2, XMM3; \
+    for (i = 0;i < (n);i += 16) { \
+        XMM2 = _mm_load_ps((x)+i  ); \
+        XMM3 = _mm_load_ps((x)+i+4); \
+        XMM2 = _mm_mul_ps(XMM2, XMM2); \
+        XMM3 = _mm_mul_ps(XMM3, XMM3); \
+        XMM0 = _mm_add_ps(XMM0, XMM2); \
+        XMM1 = _mm_add_ps(XMM1, XMM3); \
+    } \
+    XMM0 = _mm_add_ps(XMM0, XMM1); \
+    __horizontal_sum(XMM0, XMM1); \
+    XMM2 = XMM0; \
+    XMM1 = _mm_rsqrt_ss(XMM0); \
+    XMM3 = XMM1; \
+    XMM1 = _mm_mul_ss(XMM1, XMM1); \
+    XMM1 = _mm_mul_ss(XMM1, XMM3); \
+    XMM1 = _mm_mul_ss(XMM1, XMM0); \
+    XMM1 = _mm_mul_ss(XMM1, _mm_set_ss(-0.5f)); \
+    XMM3 = _mm_mul_ss(XMM3, _mm_set_ss(1.5f)); \
+    XMM3 = _mm_add_ss(XMM3, XMM1); \
+    _mm_store_ss((s), XMM3); \
+}
diff --git a/igraph/include/plfit/error.h b/igraph/include/plfit/error.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/error.h
@@ -0,0 +1,86 @@
+/* error.h
+ *
+ * Copyright (C) 2010-2011 Tamas Nepusz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __ERROR_H__
+#define __ERROR_H__
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+__BEGIN_DECLS
+
+enum {
+	PLFIT_SUCCESS  = 0,
+	PLFIT_FAILURE  = 1,
+	PLFIT_EINVAL   = 2,
+	PLFIT_UNDRFLOW = 3,
+	PLFIT_OVERFLOW = 4,
+	PLFIT_ENOMEM   = 5
+};
+
+#if (defined(__GNUC__) && GCC_VERSION_MAJOR >= 3)
+#  define PLFIT_UNLIKELY(a) __builtin_expect((a), 0)
+#  define PLFIT_LIKELY(a)   __builtin_expect((a), 1)
+#else
+#  define PLFIT_UNLIKELY(a) a
+#  define PLFIT_LIKELY(a)   a
+#endif
+
+#define PLFIT_CHECK(a) \
+	do {\
+		int plfit_i_ret=(a); \
+		if (PLFIT_UNLIKELY(plfit_i_ret != PLFIT_SUCCESS)) {\
+			return plfit_i_ret; \
+		} \
+	} while(0)
+
+#define PLFIT_ERROR(reason,plfit_errno) \
+	do {\
+		plfit_error (reason, __FILE__, __LINE__, plfit_errno) ; \
+		return plfit_errno ; \
+	} while (0)
+
+typedef void plfit_error_handler_t(const char*, const char*, int, int);
+
+extern plfit_error_handler_t plfit_error_handler_abort;
+extern plfit_error_handler_t plfit_error_handler_ignore;
+extern plfit_error_handler_t plfit_error_handler_printignore;
+
+plfit_error_handler_t* plfit_set_error_handler(plfit_error_handler_t* new_handler);
+
+void plfit_error(const char *reason, const char *file, int line, int plfit_errno);
+const char* plfit_strerror(const int plfit_errno);
+
+void plfit_error_handler_abort(const char *reason, const char *file, int line,
+		int plfit_errno);
+void plfit_error_handler_ignore(const char *reason, const char *file, int line,
+		int plfit_errno);
+void plfit_error_handler_printignore(const char *reason, const char *file, int line,
+		int plfit_errno);
+
+__END_DECLS
+
+#endif /* __ERROR_H__ */
diff --git a/igraph/include/plfit/gss.h b/igraph/include/plfit/gss.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/gss.h
@@ -0,0 +1,146 @@
+/* gss.h
+ *
+ * Copyright (C) 2012 Tamas Nepusz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GSS_H__
+#define __GSS_H__
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+__BEGIN_DECLS
+
+/**
+ * Enum specifying what the search should do when the function is not U-shaped.
+ */
+typedef enum {
+	GSS_ERROR_STOP,              /**< Stop and return an error code */
+	GSS_ERROR_WARN               /**< Continue and set the warning flag */
+} gss_error_handling_t;
+
+/**
+ * Parameter settings for a golden section search.
+ */
+typedef struct {
+    double epsilon;
+	gss_error_handling_t on_error;
+} gss_parameter_t;
+
+/**
+ * Callback interface to provide objective function evaluations for the golden
+ * section search.
+ *
+ * The gss() function calls this function to obtain the values of the objective
+ * function when needed. A client program must implement this function to evaluate
+ * the value of the objective function, given the location.
+ *  
+ * @param  instance    The user data sent for the gss() function by the client.
+ * @param  x           The current value of the variable.
+ * @retval double      The value of the objective function for the current
+ *                      variable.
+ */
+typedef double (*gss_evaluate_t)(void *instance, double x);
+
+/**
+ * Callback interface to receive the progress of the optimization process for
+ * the golden section search.
+ *
+ * The gss() function calls this function for each iteration. Implementing
+ * this function, a client program can store or display the current progress
+ * of the optimization process.
+ *
+ * @param  instance    The user data sent for the gss() function by the client.
+ * @param  x           The current value of the variable.
+ * @param  fx          The value of the objective function at x.
+ * @param  min         The location of the minimum value of the objective
+ *                     function found so far.
+ * @param  fmin        The minimum value of the objective function found so far.
+ * @param  left        The left side of the current bracket.
+ * @param  right       The right side of the current bracket.
+ * @param  k           The index of the current iteration.
+ * @retval int         Zero to continue the optimization process. Returning a
+ *                     non-zero value will cancel the optimization process.
+ */
+typedef int (*gss_progress_t)(void *instance, double x, double fx, double min,
+        double fmin, double left, double right, int k);
+
+/**
+ * Start a golden section search optimization.
+ *
+ * @param  a    The left side of the bracket to start from
+ * @param  b    The right side of the bracket to start from
+ * @param  min  The pointer to the variable that receives the location of the
+ *              final value of the objective function. This argument can be set to
+ *              \c NULL if the location of the final value of the objective
+ *              function is unnecessary.
+ * @param  fmin The pointer to the variable that receives the final value of
+ *              the objective function. This argument can be st to \c NULL if the
+ *              final value of the objective function is unnecessary.
+ * @param  proc_evaluate  The callback function to evaluate the objective
+ *                        function at a given location.
+ * @param  proc_progress  The callback function to receive the progress (the
+ *                        last evaluated location, the value of the objective
+ *                        function at that location, the width of the current
+ *                        bracket, the minimum found so far and the step
+ *                        count). This argument can be set to \c NULL if
+ *                        a progress report is unnecessary.
+ * @param  instance    A user data for the client program. The callback
+ *                     functions will receive the value of this argument.
+ * @param  param       The pointer to a structure representing parameters for
+ *                     GSS algorithm. A client program can set this parameter
+ *                     to \c NULL to use the default parameters.
+ *                     Call the \ref gss_parameter_init() function to fill a
+ *                     structure with the default values.
+ * @retval int         The status code. This function returns zero if the
+ *                     minimization process terminates without an error. A
+ *                     non-zero value indicates an error; in particular,
+ *                     \c PLFIT_FAILURE means that the function is not
+ *                     U-shaped.
+ */
+int gss(double a, double b, double *min, double *fmin,
+        gss_evaluate_t proc_evaluate, gss_progress_t proc_progress,
+        void* instance, const gss_parameter_t *_param);
+
+/**
+ * Return the state of the warning flag.
+ *
+ * The warning flag is 1 if the last optimization was run on a function that
+ * was not U-shaped.
+ */
+unsigned short int gss_get_warning_flag();
+
+/**
+ * Initialize GSS parameters to the default values.
+ *
+ * Call this function to fill a parameter structure with the default values
+ * and overwrite parameter values if necessary.
+ *
+ * @param  param       The pointer to the parameter structure.
+ */
+void gss_parameter_init(gss_parameter_t *param);
+
+__END_DECLS
+
+#endif /* __GSS_H__ */
diff --git a/igraph/include/plfit/kolmogorov.h b/igraph/include/plfit/kolmogorov.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/kolmogorov.h
@@ -0,0 +1,43 @@
+/* kolmogorov.h
+ * 
+ * Copyright (C) 2010-2011 Tamas Nepusz
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KOLMOGOROV_H__
+#define __KOLMOGOROV_H__
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+#include <stdlib.h>
+
+__BEGIN_DECLS
+
+double plfit_kolmogorov(double z);
+double plfit_ks_test_one_sample_p(double d, size_t n);
+double plfit_ks_test_two_sample_p(double d, size_t n1, size_t n2);
+
+__END_DECLS
+
+#endif
diff --git a/igraph/include/plfit/lbfgs.h b/igraph/include/plfit/lbfgs.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/lbfgs.h
@@ -0,0 +1,736 @@
+/*
+ *      C library of Limited memory BFGS (L-BFGS).
+ *
+ * Copyright (c) 1990, Jorge Nocedal
+ * Copyright (c) 2007-2010 Naoaki Okazaki
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/* $Id: lbfgs.h 65 2010-01-29 12:19:16Z naoaki $ */
+
+#ifndef __LBFGS_H__
+#define __LBFGS_H__
+
+#ifdef  __cplusplus
+extern "C" {
+#endif/*__cplusplus*/
+
+/*
+ * The default precision of floating point values is 64bit (double).
+ */
+#ifndef LBFGS_FLOAT
+#define LBFGS_FLOAT     64
+#endif/*LBFGS_FLOAT*/
+
+/*
+ * Activate optimization routines for IEEE754 floating point values.
+ */
+#ifndef LBFGS_IEEE_FLOAT
+#define LBFGS_IEEE_FLOAT    1
+#endif/*LBFGS_IEEE_FLOAT*/
+
+#if     LBFGS_FLOAT == 32
+typedef float lbfgsfloatval_t;
+
+#elif   LBFGS_FLOAT == 64
+typedef double lbfgsfloatval_t;
+
+#else
+#error "libLBFGS supports single (float; LBFGS_FLOAT = 32) or double (double; LBFGS_FLOAT=64) precision only."
+
+#endif
+
+
+/** 
+ * \addtogroup liblbfgs_api libLBFGS API
+ * @{
+ *
+ *  The libLBFGS API.
+ */
+
+/**
+ * Return values of lbfgs().
+ * 
+ *  Roughly speaking, a negative value indicates an error.
+ */
+enum {
+    /** L-BFGS reaches convergence. */
+    LBFGS_SUCCESS = 0,
+    LBFGS_CONVERGENCE = 0,
+    LBFGS_STOP,
+    /** The initial variables already minimize the objective function. */
+    LBFGS_ALREADY_MINIMIZED,
+
+    /** Unknown error. */
+    LBFGSERR_UNKNOWNERROR = -1024,
+    /** Logic error. */
+    LBFGSERR_LOGICERROR,
+    /** Insufficient memory. */
+    LBFGSERR_OUTOFMEMORY,
+    /** The minimization process has been canceled. */
+    LBFGSERR_CANCELED,
+    /** Invalid number of variables specified. */
+    LBFGSERR_INVALID_N,
+    /** Invalid number of variables (for SSE) specified. */
+    LBFGSERR_INVALID_N_SSE,
+    /** The array x must be aligned to 16 (for SSE). */
+    LBFGSERR_INVALID_X_SSE,
+    /** Invalid parameter lbfgs_parameter_t::epsilon specified. */
+    LBFGSERR_INVALID_EPSILON,
+    /** Invalid parameter lbfgs_parameter_t::past specified. */
+    LBFGSERR_INVALID_TESTPERIOD,
+    /** Invalid parameter lbfgs_parameter_t::delta specified. */
+    LBFGSERR_INVALID_DELTA,
+    /** Invalid parameter lbfgs_parameter_t::linesearch specified. */
+    LBFGSERR_INVALID_LINESEARCH,
+    /** Invalid parameter lbfgs_parameter_t::max_step specified. */
+    LBFGSERR_INVALID_MINSTEP,
+    /** Invalid parameter lbfgs_parameter_t::max_step specified. */
+    LBFGSERR_INVALID_MAXSTEP,
+    /** Invalid parameter lbfgs_parameter_t::ftol specified. */
+    LBFGSERR_INVALID_FTOL,
+    /** Invalid parameter lbfgs_parameter_t::wolfe specified. */
+    LBFGSERR_INVALID_WOLFE,
+    /** Invalid parameter lbfgs_parameter_t::gtol specified. */
+    LBFGSERR_INVALID_GTOL,
+    /** Invalid parameter lbfgs_parameter_t::xtol specified. */
+    LBFGSERR_INVALID_XTOL,
+    /** Invalid parameter lbfgs_parameter_t::max_linesearch specified. */
+    LBFGSERR_INVALID_MAXLINESEARCH,
+    /** Invalid parameter lbfgs_parameter_t::orthantwise_c specified. */
+    LBFGSERR_INVALID_ORTHANTWISE,
+    /** Invalid parameter lbfgs_parameter_t::orthantwise_start specified. */
+    LBFGSERR_INVALID_ORTHANTWISE_START,
+    /** Invalid parameter lbfgs_parameter_t::orthantwise_end specified. */
+    LBFGSERR_INVALID_ORTHANTWISE_END,
+    /** The line-search step went out of the interval of uncertainty. */
+    LBFGSERR_OUTOFINTERVAL,
+    /** A logic error occurred; alternatively, the interval of uncertainty
+        became too small. */
+    LBFGSERR_INCORRECT_TMINMAX,
+    /** A rounding error occurred; alternatively, no line-search step
+        satisfies the sufficient decrease and curvature conditions. */
+    LBFGSERR_ROUNDING_ERROR,
+    /** The line-search step became smaller than lbfgs_parameter_t::min_step. */
+    LBFGSERR_MINIMUMSTEP,
+    /** The line-search step became larger than lbfgs_parameter_t::max_step. */
+    LBFGSERR_MAXIMUMSTEP,
+    /** The line-search routine reaches the maximum number of evaluations. */
+    LBFGSERR_MAXIMUMLINESEARCH,
+    /** The algorithm routine reaches the maximum number of iterations. */
+    LBFGSERR_MAXIMUMITERATION,
+    /** Relative width of the interval of uncertainty is at most
+        lbfgs_parameter_t::xtol. */
+    LBFGSERR_WIDTHTOOSMALL,
+    /** A logic error (negative line-search step) occurred. */
+    LBFGSERR_INVALIDPARAMETERS,
+    /** The current search direction increases the objective function value. */
+    LBFGSERR_INCREASEGRADIENT,
+};
+
+/**
+ * Line search algorithms.
+ */
+enum {
+    /** The default algorithm (MoreThuente method). */
+    LBFGS_LINESEARCH_DEFAULT = 0,
+    /** MoreThuente method proposd by More and Thuente. */
+    LBFGS_LINESEARCH_MORETHUENTE = 0,
+    /**
+     * Backtracking method with the Armijo condition.
+     *  The backtracking method finds the step length such that it satisfies
+     *  the sufficient decrease (Armijo) condition,
+     *    - f(x + a * d) <= f(x) + lbfgs_parameter_t::ftol * a * g(x)^T d,
+     *
+     *  where x is the current point, d is the current search direction, and
+     *  a is the step length.
+     */
+    LBFGS_LINESEARCH_BACKTRACKING_ARMIJO = 1,
+    /** The backtracking method with the defualt (regular Wolfe) condition. */
+    LBFGS_LINESEARCH_BACKTRACKING = 2,
+    /**
+     * Backtracking method with regular Wolfe condition.
+     *  The backtracking method finds the step length such that it satisfies
+     *  both the Armijo condition (LBFGS_LINESEARCH_BACKTRACKING_ARMIJO)
+     *  and the curvature condition,
+     *    - g(x + a * d)^T d >= lbfgs_parameter_t::wolfe * g(x)^T d,
+     *
+     *  where x is the current point, d is the current search direction, and
+     *  a is the step length.
+     */
+    LBFGS_LINESEARCH_BACKTRACKING_WOLFE = 2,
+    /**
+     * Backtracking method with strong Wolfe condition.
+     *  The backtracking method finds the step length such that it satisfies
+     *  both the Armijo condition (LBFGS_LINESEARCH_BACKTRACKING_ARMIJO)
+     *  and the following condition,
+     *    - |g(x + a * d)^T d| <= lbfgs_parameter_t::wolfe * |g(x)^T d|,
+     *
+     *  where x is the current point, d is the current search direction, and
+     *  a is the step length.
+     */
+    LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE = 3,
+};
+
+/**
+ * L-BFGS optimization parameters.
+ *  Call lbfgs_parameter_init() function to initialize parameters to the
+ *  default values.
+ */
+typedef struct {
+    /**
+     * The number of corrections to approximate the inverse hessian matrix.
+     *  The L-BFGS routine stores the computation results of previous \ref m
+     *  iterations to approximate the inverse hessian matrix of the current
+     *  iteration. This parameter controls the size of the limited memories
+     *  (corrections). The default value is \c 6. Values less than \c 3 are
+     *  not recommended. Large values will result in excessive computing time.
+     */
+    int             m;
+
+    /**
+     * Epsilon for convergence test.
+     *  This parameter determines the accuracy with which the solution is to
+     *  be found. A minimization terminates when
+     *      ||g|| < \ref epsilon * max(1, ||x||),
+     *  where ||.|| denotes the Euclidean (L2) norm. The default value is
+     *  \c 1e-5.
+     */
+    lbfgsfloatval_t epsilon;
+
+    /**
+     * Distance for delta-based convergence test.
+     *  This parameter determines the distance, in iterations, to compute
+     *  the rate of decrease of the objective function. If the value of this
+     *  parameter is zero, the library does not perform the delta-based
+     *  convergence test. The default value is \c 0.
+     */
+    int             past;
+
+    /**
+     * Delta for convergence test.
+     *  This parameter determines the minimum rate of decrease of the
+     *  objective function. The library stops iterations when the
+     *  following condition is met:
+     *      (f' - f) / f < \ref delta,
+     *  where f' is the objective value of \ref past iterations ago, and f is
+     *  the objective value of the current iteration.
+     *  The default value is \c 0.
+     */
+    lbfgsfloatval_t delta;
+
+    /**
+     * The maximum number of iterations.
+     *  The lbfgs() function terminates an optimization process with
+     *  ::LBFGSERR_MAXIMUMITERATION status code when the iteration count
+     *  exceedes this parameter. Setting this parameter to zero continues an
+     *  optimization process until a convergence or error. The default value
+     *  is \c 0.
+     */
+    int             max_iterations;
+
+    /**
+     * The line search algorithm.
+     *  This parameter specifies a line search algorithm to be used by the
+     *  L-BFGS routine.
+     */
+    int             linesearch;
+
+    /**
+     * The maximum number of trials for the line search.
+     *  This parameter controls the number of function and gradients evaluations
+     *  per iteration for the line search routine. The default value is \c 20.
+     */
+    int             max_linesearch;
+
+    /**
+     * The minimum step of the line search routine.
+     *  The default value is \c 1e-20. This value need not be modified unless
+     *  the exponents are too large for the machine being used, or unless the
+     *  problem is extremely badly scaled (in which case the exponents should
+     *  be increased).
+     */
+    lbfgsfloatval_t min_step;
+
+    /**
+     * The maximum step of the line search.
+     *  The default value is \c 1e+20. This value need not be modified unless
+     *  the exponents are too large for the machine being used, or unless the
+     *  problem is extremely badly scaled (in which case the exponents should
+     *  be increased).
+     */
+    lbfgsfloatval_t max_step;
+
+    /**
+     * A parameter to control the accuracy of the line search routine.
+     *  The default value is \c 1e-4. This parameter should be greater
+     *  than zero and smaller than \c 0.5.
+     */
+    lbfgsfloatval_t ftol;
+
+    /**
+     * A coefficient for the Wolfe condition.
+     *  This parameter is valid only when the backtracking line-search
+     *  algorithm is used with the Wolfe condition,
+     *  ::LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE or
+     *  ::LBFGS_LINESEARCH_BACKTRACKING_WOLFE .
+     *  The default value is \c 0.9. This parameter should be greater
+     *  the \ref ftol parameter and smaller than \c 1.0.
+     */
+    lbfgsfloatval_t wolfe;
+
+    /**
+     * A parameter to control the accuracy of the line search routine.
+     *  The default value is \c 0.9. If the function and gradient
+     *  evaluations are inexpensive with respect to the cost of the
+     *  iteration (which is sometimes the case when solving very large
+     *  problems) it may be advantageous to set this parameter to a small
+     *  value. A typical small value is \c 0.1. This parameter shuold be
+     *  greater than the \ref ftol parameter (\c 1e-4) and smaller than
+     *  \c 1.0.
+     */
+    lbfgsfloatval_t gtol;
+
+    /**
+     * The machine precision for floating-point values.
+     *  This parameter must be a positive value set by a client program to
+     *  estimate the machine precision. The line search routine will terminate
+     *  with the status code (::LBFGSERR_ROUNDING_ERROR) if the relative width
+     *  of the interval of uncertainty is less than this parameter.
+     */
+    lbfgsfloatval_t xtol;
+
+    /**
+     * Coeefficient for the L1 norm of variables.
+     *  This parameter should be set to zero for standard minimization
+     *  problems. Setting this parameter to a positive value activates
+     *  Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) method, which
+     *  minimizes the objective function F(x) combined with the L1 norm |x|
+     *  of the variables, {F(x) + C |x|}. This parameter is the coeefficient
+     *  for the |x|, i.e., C. As the L1 norm |x| is not differentiable at
+     *  zero, the library modifies function and gradient evaluations from
+     *  a client program suitably; a client program thus have only to return
+     *  the function value F(x) and gradients G(x) as usual. The default value
+     *  is zero.
+     */
+    lbfgsfloatval_t orthantwise_c;
+
+    /**
+     * Start index for computing L1 norm of the variables.
+     *  This parameter is valid only for OWL-QN method
+     *  (i.e., \ref orthantwise_c != 0). This parameter b (0 <= b < N)
+     *  specifies the index number from which the library computes the
+     *  L1 norm of the variables x,
+     *      |x| := |x_{b}| + |x_{b+1}| + ... + |x_{N}| .
+     *  In other words, variables x_1, ..., x_{b-1} are not used for
+     *  computing the L1 norm. Setting b (0 < b < N), one can protect
+     *  variables, x_1, ..., x_{b-1} (e.g., a bias term of logistic
+     *  regression) from being regularized. The default value is zero.
+     */
+    int             orthantwise_start;
+
+    /**
+     * End index for computing L1 norm of the variables.
+     *  This parameter is valid only for OWL-QN method
+     *  (i.e., \ref orthantwise_c != 0). This parameter e (0 < e <= N)
+     *  specifies the index number at which the library stops computing the
+     *  L1 norm of the variables x,
+     */
+    int             orthantwise_end;
+} lbfgs_parameter_t;
+
+
+/**
+ * Callback interface to provide objective function and gradient evaluations.
+ *
+ *  The lbfgs() function call this function to obtain the values of objective
+ *  function and its gradients when needed. A client program must implement
+ *  this function to evaluate the values of the objective function and its
+ *  gradients, given current values of variables.
+ *  
+ *  @param  instance    The user data sent for lbfgs() function by the client.
+ *  @param  x           The current values of variables.
+ *  @param  g           The gradient vector. The callback function must compute
+ *                      the gradient values for the current variables.
+ *  @param  n           The number of variables.
+ *  @param  step        The current step of the line search routine.
+ *  @retval lbfgsfloatval_t The value of the objective function for the current
+ *                          variables.
+ */
+typedef lbfgsfloatval_t (*lbfgs_evaluate_t)(
+    void *instance,
+    const lbfgsfloatval_t *x,
+    lbfgsfloatval_t *g,
+    const int n,
+    const lbfgsfloatval_t step
+    );
+
+/**
+ * Callback interface to receive the progress of the optimization process.
+ *
+ *  The lbfgs() function call this function for each iteration. Implementing
+ *  this function, a client program can store or display the current progress
+ *  of the optimization process.
+ *
+ *  @param  instance    The user data sent for lbfgs() function by the client.
+ *  @param  x           The current values of variables.
+ *  @param  g           The current gradient values of variables.
+ *  @param  fx          The current value of the objective function.
+ *  @param  xnorm       The Euclidean norm of the variables.
+ *  @param  gnorm       The Euclidean norm of the gradients.
+ *  @param  step        The line-search step used for this iteration.
+ *  @param  n           The number of variables.
+ *  @param  k           The iteration count.
+ *  @param  ls          The number of evaluations called for this iteration.
+ *  @retval int         Zero to continue the optimization process. Returning a
+ *                      non-zero value will cancel the optimization process.
+ */
+typedef int (*lbfgs_progress_t)(
+    void *instance,
+    const lbfgsfloatval_t *x,
+    const lbfgsfloatval_t *g,
+    const lbfgsfloatval_t fx,
+    const lbfgsfloatval_t xnorm,
+    const lbfgsfloatval_t gnorm,
+    const lbfgsfloatval_t step,
+    int n,
+    int k,
+    int ls
+    );
+
+/*
+A user must implement a function compatible with ::lbfgs_evaluate_t (evaluation
+callback) and pass the pointer to the callback function to lbfgs() arguments.
+Similarly, a user can implement a function compatible with ::lbfgs_progress_t
+(progress callback) to obtain the current progress (e.g., variables, function
+value, ||G||, etc) and to cancel the iteration process if necessary.
+Implementation of a progress callback is optional: a user can pass \c NULL if
+progress notification is not necessary.
+
+In addition, a user must preserve two requirements:
+    - The number of variables must be multiples of 16 (this is not 4).
+    - The memory block of variable array ::x must be aligned to 16.
+
+This algorithm terminates an optimization
+when:
+
+    ||G|| < \epsilon \cdot \max(1, ||x||) .
+
+In this formula, ||.|| denotes the Euclidean norm.
+*/
+
+/**
+ * Start a L-BFGS optimization.
+ *
+ *  @param  n           The number of variables.
+ *  @param  x           The array of variables. A client program can set
+ *                      default values for the optimization and receive the
+ *                      optimization result through this array. This array
+ *                      must be allocated by ::lbfgs_malloc function
+ *                      for libLBFGS built with SSE/SSE2 optimization routine
+ *                      enabled. The library built without SSE/SSE2
+ *                      optimization does not have such a requirement.
+ *  @param  ptr_fx      The pointer to the variable that receives the final
+ *                      value of the objective function for the variables.
+ *                      This argument can be set to \c NULL if the final
+ *                      value of the objective function is unnecessary.
+ *  @param  proc_evaluate   The callback function to provide function and
+ *                          gradient evaluations given a current values of
+ *                          variables. A client program must implement a
+ *                          callback function compatible with \ref
+ *                          lbfgs_evaluate_t and pass the pointer to the
+ *                          callback function.
+ *  @param  proc_progress   The callback function to receive the progress
+ *                          (the number of iterations, the current value of
+ *                          the objective function) of the minimization
+ *                          process. This argument can be set to \c NULL if
+ *                          a progress report is unnecessary.
+ *  @param  instance    A user data for the client program. The callback
+ *                      functions will receive the value of this argument.
+ *  @param  param       The pointer to a structure representing parameters for
+ *                      L-BFGS optimization. A client program can set this
+ *                      parameter to \c NULL to use the default parameters.
+ *                      Call lbfgs_parameter_init() function to fill a
+ *                      structure with the default values.
+ *  @retval int         The status code. This function returns zero if the
+ *                      minimization process terminates without an error. A
+ *                      non-zero value indicates an error.
+ */
+int lbfgs(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *ptr_fx,
+    lbfgs_evaluate_t proc_evaluate,
+    lbfgs_progress_t proc_progress,
+    void *instance,
+    lbfgs_parameter_t *param
+    );
+
+/**
+ * Initialize L-BFGS parameters to the default values.
+ *
+ *  Call this function to fill a parameter structure with the default values
+ *  and overwrite parameter values if necessary.
+ *
+ *  @param  param       The pointer to the parameter structure.
+ */
+void lbfgs_parameter_init(lbfgs_parameter_t *param);
+
+/**
+ * Allocate an array for variables.
+ *
+ *  This function allocates an array of variables for the convenience of
+ *  ::lbfgs function; the function has a requreiemt for a variable array
+ *  when libLBFGS is built with SSE/SSE2 optimization routines. A user does
+ *  not have to use this function for libLBFGS built without SSE/SSE2
+ *  optimization.
+ *  
+ *  @param  n           The number of variables.
+ */
+lbfgsfloatval_t* lbfgs_malloc(int n);
+
+/**
+ * Free an array of variables.
+ *  
+ *  @param  x           The array of variables allocated by ::lbfgs_malloc
+ *                      function.
+ */
+void lbfgs_free(lbfgsfloatval_t *x);
+
+/** @} */
+
+#ifdef  __cplusplus
+}
+#endif/*__cplusplus*/
+
+
+
+/**
+@mainpage libLBFGS: a library of Limited-memory Broyden-Fletcher-Goldfarb-Shanno (L-BFGS)
+
+@section intro Introduction
+
+This library is a C port of the implementation of Limited-memory
+Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) method written by Jorge Nocedal.
+The original FORTRAN source code is available at:
+http://www.ece.northwestern.edu/~nocedal/lbfgs.html
+
+The L-BFGS method solves the unconstrainted minimization problem,
+
+<pre>
+    minimize F(x), x = (x1, x2, ..., xN),
+</pre>
+
+only if the objective function F(x) and its gradient G(x) are computable. The
+well-known Newton's method requires computation of the inverse of the hessian
+matrix of the objective function. However, the computational cost for the
+inverse hessian matrix is expensive especially when the objective function
+takes a large number of variables. The L-BFGS method iteratively finds a
+minimizer by approximating the inverse hessian matrix by information from last
+m iterations. This innovation saves the memory storage and computational time
+drastically for large-scaled problems.
+
+Among the various ports of L-BFGS, this library provides several features:
+- <b>Optimization with L1-norm (Orthant-Wise Limited-memory Quasi-Newton
+  (OWL-QN) method)</b>:
+  In addition to standard minimization problems, the library can minimize
+  a function F(x) combined with L1-norm |x| of the variables,
+  {F(x) + C |x|}, where C is a constant scalar parameter. This feature is
+  useful for estimating parameters of sparse log-linear models (e.g.,
+  logistic regression and maximum entropy) with L1-regularization (or
+  Laplacian prior).
+- <b>Clean C code</b>:
+  Unlike C codes generated automatically by f2c (Fortran 77 into C converter),
+  this port includes changes based on my interpretations, improvements,
+  optimizations, and clean-ups so that the ported code would be well-suited
+  for a C code. In addition to comments inherited from the original code,
+  a number of comments were added through my interpretations.
+- <b>Callback interface</b>:
+  The library receives function and gradient values via a callback interface.
+  The library also notifies the progress of the optimization by invoking a
+  callback function. In the original implementation, a user had to set
+  function and gradient values every time the function returns for obtaining
+  updated values.
+- <b>Thread safe</b>:
+  The library is thread-safe, which is the secondary gain from the callback
+  interface.
+- <b>Cross platform.</b> The source code can be compiled on Microsoft Visual
+  Studio 2005, GNU C Compiler (gcc), etc.
+- <b>Configurable precision</b>: A user can choose single-precision (float)
+  or double-precision (double) accuracy by changing ::LBFGS_FLOAT macro.
+- <b>SSE/SSE2 optimization</b>:
+  This library includes SSE/SSE2 optimization (written in compiler intrinsics)
+  for vector arithmetic operations on Intel/AMD processors. The library uses
+  SSE for float values and SSE2 for double values. The SSE/SSE2 optimization
+  routine is disabled by default.
+
+This library is used by:
+- <a href="http://www.chokkan.org/software/crfsuite/">CRFsuite: A fast implementation of Conditional Random Fields (CRFs)</a>
+- <a href="http://www.chokkan.org/software/classias/">Classias: A collection of machine-learning algorithms for classification</a>
+- <a href="http://www.public.iastate.edu/~gdancik/mlegp/">mlegp: an R package for maximum likelihood estimates for Gaussian processes</a>
+- <a href="http://infmath.uibk.ac.at/~matthiasf/imaging2/">imaging2: the imaging2 class library</a>
+- <a href="http://search.cpan.org/~laye/Algorithm-LBFGS-0.16/">Algorithm::LBFGS - Perl extension for L-BFGS</a>
+- <a href="http://www.cs.kuleuven.be/~bernd/yap-lbfgs/">YAP-LBFGS (an interface to call libLBFGS from YAP Prolog)</a>
+
+@section download Download
+
+- <a href="http://www.chokkan.org/software/dist/liblbfgs-1.9.tar.gz">Source code</a>
+
+libLBFGS is distributed under the term of the
+<a href="http://opensource.org/licenses/mit-license.php">MIT license</a>.
+
+@section changelog History
+- Version 1.9 (2010-01-29):
+    - Fixed a mistake in checking the validity of the parameters "ftol" and
+      "wolfe"; this was discovered by Kevin S. Van Horn.
+- Version 1.8 (2009-07-13):
+    - Accepted the patch submitted by Takashi Imamichi;
+      the backtracking method now has three criteria for choosing the step
+      length:
+        - ::LBFGS_LINESEARCH_BACKTRACKING_ARMIJO: sufficient decrease (Armijo)
+          condition only
+        - ::LBFGS_LINESEARCH_BACKTRACKING_WOLFE: regular Wolfe condition
+          (sufficient decrease condition + curvature condition)
+        - ::LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE: strong Wolfe condition
+    - Updated the documentation to explain the above three criteria.
+- Version 1.7 (2009-02-28):
+    - Improved OWL-QN routines for stability.
+    - Removed the support of OWL-QN method in MoreThuente algorithm because
+      it accidentally fails in early stages of iterations for some objectives.
+      Because of this change, <b>the OW-LQN method must be used with the
+      backtracking algorithm (::LBFGS_LINESEARCH_BACKTRACKING)</b>, or the
+      library returns ::LBFGSERR_INVALID_LINESEARCH.
+    - Renamed line search algorithms as follows:
+        - ::LBFGS_LINESEARCH_BACKTRACKING: regular Wolfe condition.
+        - ::LBFGS_LINESEARCH_BACKTRACKING_LOOSE: regular Wolfe condition.
+        - ::LBFGS_LINESEARCH_BACKTRACKING_STRONG: strong Wolfe condition.
+    - Source code clean-up.
+- Version 1.6 (2008-11-02):
+    - Improved line-search algorithm with strong Wolfe condition, which was
+      contributed by Takashi Imamichi. This routine is now default for
+      ::LBFGS_LINESEARCH_BACKTRACKING. The previous line search algorithm
+      with regular Wolfe condition is still available as
+      ::LBFGS_LINESEARCH_BACKTRACKING_LOOSE.
+    - Configurable stop index for L1-norm computation. A member variable
+      ::lbfgs_parameter_t::orthantwise_end was added to specify the index
+      number at which the library stops computing the L1 norm of the
+      variables. This is useful to prevent some variables from being
+      regularized by the OW-LQN method.
+    - A sample program written in C++ (sample/sample.cpp).
+- Version 1.5 (2008-07-10):
+    - Configurable starting index for L1-norm computation. A member variable
+      ::lbfgs_parameter_t::orthantwise_start was added to specify the index
+      number from which the library computes the L1 norm of the variables.
+      This is useful to prevent some variables from being regularized by the
+      OWL-QN method.
+    - Fixed a zero-division error when the initial variables have already
+      been a minimizer (reported by Takashi Imamichi). In this case, the
+      library returns ::LBFGS_ALREADY_MINIMIZED status code.
+    - Defined ::LBFGS_SUCCESS status code as zero; removed unused constants,
+      LBFGSFALSE and LBFGSTRUE.
+    - Fixed a compile error in an implicit down-cast.
+- Version 1.4 (2008-04-25):
+    - Configurable line search algorithms. A member variable
+      ::lbfgs_parameter_t::linesearch was added to choose either MoreThuente
+      method (::LBFGS_LINESEARCH_MORETHUENTE) or backtracking algorithm
+      (::LBFGS_LINESEARCH_BACKTRACKING).
+    - Fixed a bug: the previous version did not compute psuedo-gradients
+      properly in the line search routines for OWL-QN. This bug might quit
+      an iteration process too early when the OWL-QN routine was activated
+      (0 < ::lbfgs_parameter_t::orthantwise_c).
+    - Configure script for POSIX environments.
+    - SSE/SSE2 optimizations with GCC.
+    - New functions ::lbfgs_malloc and ::lbfgs_free to use SSE/SSE2 routines
+      transparently. It is uncessary to use these functions for libLBFGS built
+      without SSE/SSE2 routines; you can still use any memory allocators if
+      SSE/SSE2 routines are disabled in libLBFGS.
+- Version 1.3 (2007-12-16):
+    - An API change. An argument was added to lbfgs() function to receive the
+      final value of the objective function. This argument can be set to
+      \c NULL if the final value is unnecessary.
+    - Fixed a null-pointer bug in the sample code (reported by Takashi Imamichi).
+    - Added build scripts for Microsoft Visual Studio 2005 and GCC.
+    - Added README file.
+- Version 1.2 (2007-12-13):
+    - Fixed a serious bug in orthant-wise L-BFGS.
+      An important variable was used without initialization.
+- Version 1.1 (2007-12-01):
+    - Implemented orthant-wise L-BFGS.
+    - Implemented lbfgs_parameter_init() function.
+    - Fixed several bugs.
+    - API documentation.
+- Version 1.0 (2007-09-20):
+    - Initial release.
+
+@section api Documentation
+
+- @ref liblbfgs_api "libLBFGS API"
+
+@section sample Sample code
+
+@include sample.c
+
+@section ack Acknowledgements
+
+The L-BFGS algorithm is described in:
+    - Jorge Nocedal.
+      Updating Quasi-Newton Matrices with Limited Storage.
+      <i>Mathematics of Computation</i>, Vol. 35, No. 151, pp. 773--782, 1980.
+    - Dong C. Liu and Jorge Nocedal.
+      On the limited memory BFGS method for large scale optimization.
+      <i>Mathematical Programming</i> B, Vol. 45, No. 3, pp. 503-528, 1989.
+
+The line search algorithms used in this implementation are described in:
+    - John E. Dennis and Robert B. Schnabel.
+      <i>Numerical Methods for Unconstrained Optimization and Nonlinear
+      Equations</i>, Englewood Cliffs, 1983.
+    - Jorge J. More and David J. Thuente.
+      Line search algorithm with guaranteed sufficient decrease.
+      <i>ACM Transactions on Mathematical Software (TOMS)</i>, Vol. 20, No. 3,
+      pp. 286-307, 1994.
+
+This library also implements Orthant-Wise Limited-memory Quasi-Newton (OWL-QN)
+method presented in:
+    - Galen Andrew and Jianfeng Gao.
+      Scalable training of L1-regularized log-linear models.
+      In <i>Proceedings of the 24th International Conference on Machine
+      Learning (ICML 2007)</i>, pp. 33-40, 2007.
+
+Special thanks go to:
+    - Yoshimasa Tsuruoka and Daisuke Okanohara for technical information about
+      OWL-QN
+    - Takashi Imamichi for the useful enhancements of the backtracking method
+
+Finally I would like to thank the original author, Jorge Nocedal, who has been
+distributing the effieicnt and explanatory implementation in an open source
+licence.
+
+@section reference Reference
+
+- <a href="http://www.ece.northwestern.edu/~nocedal/lbfgs.html">L-BFGS</a> by Jorge Nocedal.
+- <a href="http://research.microsoft.com/en-us/downloads/b1eb1016-1738-4bd5-83a9-370c9d498a03/default.aspx">Orthant-Wise Limited-memory Quasi-Newton Optimizer for L1-regularized Objectives</a> by Galen Andrew.
+- <a href="http://chasen.org/~taku/software/misc/lbfgs/">C port (via f2c)</a> by Taku Kudo.
+- <a href="http://www.alglib.net/optimization/lbfgs.php">C#/C++/Delphi/VisualBasic6 port</a> in ALGLIB.
+- <a href="http://cctbx.sourceforge.net/">Computational Crystallography Toolbox</a> includes
+  <a href="http://cctbx.sourceforge.net/current_cvs/c_plus_plus/namespacescitbx_1_1lbfgs.html">scitbx::lbfgs</a>.
+*/
+
+#endif/*__LBFGS_H__*/
diff --git a/igraph/include/plfit/platform.h b/igraph/include/plfit/platform.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/platform.h
@@ -0,0 +1,54 @@
+/* platform.h
+ *
+ * Copyright (C) 2010-2011 Tamas Nepusz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __PLATFORM_H__
+#define __PLATFORM_H__
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+#include <float.h>
+
+__BEGIN_DECLS
+
+#ifdef _MSC_VER
+#define snprintf sprintf_s
+#define inline  __inline
+#define isnan(x) _isnan(x)
+#define isfinite(x) _finite(x)
+#endif
+
+#ifndef INFINITY
+#  define INFINITY (1.0/0.0)
+#endif
+
+#ifndef NAN
+#  define NAN (INFINITY-INFINITY)
+#endif
+
+__END_DECLS
+
+#endif /* __PLATFORM_H__ */
diff --git a/igraph/include/plfit/plfit.h b/igraph/include/plfit/plfit.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/plfit.h
@@ -0,0 +1,109 @@
+/* plfit.h
+ * 
+ * Copyright (C) 2010-2011 Tamas Nepusz
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __PLFIT_H__
+#define __PLFIT_H__
+
+#include <stdlib.h>
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+__BEGIN_DECLS
+
+#define PLFIT_VERSION_MAJOR 0
+#define PLFIT_VERSION_MINOR 6
+#define PLFIT_VERSION_STRING "0.6"
+
+typedef unsigned short int plfit_bool_t;
+
+typedef enum {
+	PLFIT_GSS_OR_LINEAR,
+	PLFIT_LINEAR_ONLY,
+	PLFIT_DEFAULT_CONTINUOUS_METHOD = PLFIT_GSS_OR_LINEAR
+} plfit_continuous_method_t;
+
+typedef enum {
+	PLFIT_LBFGS,
+	PLFIT_LINEAR_SCAN,
+	PLFIT_PRETEND_CONTINUOUS,
+	PLFIT_DEFAULT_DISCRETE_METHOD = PLFIT_LBFGS
+} plfit_discrete_method_t;
+
+typedef struct _plfit_result_t {
+	double alpha;     /* fitted power-law exponent */
+	double xmin;      /* cutoff where the power-law behaviour kicks in */
+	double L;         /* log-likelihood of the sample */
+	double D;         /* test statistic for the KS test */
+	double p;         /* p-value of the KS test */
+} plfit_result_t;
+
+/********** structure that holds the options of plfit **********/
+
+typedef struct _plfit_continuous_options_t {
+	plfit_bool_t finite_size_correction;
+	plfit_continuous_method_t xmin_method;
+} plfit_continuous_options_t;
+
+typedef struct _plfit_discrete_options_t {
+	plfit_bool_t finite_size_correction;
+	plfit_discrete_method_t alpha_method;
+	struct {
+		double min;
+		double max;
+		double step;
+	} alpha;
+} plfit_discrete_options_t;
+
+int plfit_continuous_options_init(plfit_continuous_options_t* options);
+int plfit_discrete_options_init(plfit_discrete_options_t* options);
+
+extern const plfit_continuous_options_t plfit_continuous_default_options;
+extern const plfit_discrete_options_t plfit_discrete_default_options;
+
+/********** continuous power law distribution fitting **********/
+
+int plfit_log_likelihood_continuous(double* xs, size_t n, double alpha,
+		double xmin, double* l);
+int plfit_estimate_alpha_continuous(double* xs, size_t n, double xmin,
+        const plfit_continuous_options_t* options, plfit_result_t* result);
+int plfit_estimate_alpha_continuous_sorted(double* xs, size_t n, double xmin,
+        const plfit_continuous_options_t* options, plfit_result_t* result);
+int plfit_continuous(double* xs, size_t n,
+		const plfit_continuous_options_t* options, plfit_result_t* result);
+
+/********** discrete power law distribution fitting **********/
+
+int plfit_estimate_alpha_discrete(double* xs, size_t n, double xmin,
+        const plfit_discrete_options_t* options, plfit_result_t *result);
+int plfit_log_likelihood_discrete(double* xs, size_t n, double alpha, double xmin, double* l);
+int plfit_discrete(double* xs, size_t n, const plfit_discrete_options_t* options,
+		plfit_result_t* result);
+
+__END_DECLS
+
+#endif /* __PLFIT_H__ */
+
diff --git a/igraph/include/plfit/zeta.h b/igraph/include/plfit/zeta.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/plfit/zeta.h
@@ -0,0 +1,53 @@
+/* specfunc/gsl_sf_zeta.h
+ * 
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004 Gerard Jungman
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* Author:  G. Jungman */
+
+/* This file was taken from the GNU Scientific Library. Some modifications
+ * were done in order to make it independent from the rest of GSL
+ */
+
+#ifndef __ZETA_H__
+#define __ZETA_H__
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+__BEGIN_DECLS
+
+
+/* Hurwitz Zeta Function
+ * zeta(s,q) = Sum[ (k+q)^(-s), {k,0,Infinity} ]
+ *
+ * s > 1.0, q > 0.0
+ */
+double gsl_sf_hzeta(const double s, const double q);
+
+
+__END_DECLS
+
+#endif /* __ZETA_H__ */
+
diff --git a/igraph/include/pottsmodel_2.h b/igraph/include/pottsmodel_2.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/pottsmodel_2.h
@@ -0,0 +1,167 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Jörg Reichardt
+   This file was modified by Vincent Traag
+   The original copyright notice follows here */
+
+/***************************************************************************
+                          pottsmodel.h  -  description
+                             -------------------
+    begin                : Fri May 28 2004
+    copyright            : (C) 2004 by
+    email                :
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef POTTSMODEL_H
+#define POTTSMODEL_H
+
+#include "NetDataTypes.h"
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+
+#define qmax 500
+
+class PottsModel {
+private:
+    //  HugeArray<double> neg_gammalookup;
+    //  HugeArray<double> pos_gammalookup;
+    DL_Indexed_List<unsigned int*> *new_spins;
+    DL_Indexed_List<unsigned int*> *previous_spins;
+    HugeArray<HugeArray<double>*> correlation;
+    network *net;
+    unsigned int q;
+    unsigned int operation_mode;
+    FILE *Qfile, *Magfile;
+    double Qmatrix[qmax + 1][qmax + 1];
+    double* Qa;
+    double* weights;
+    double total_degree_sum;
+    unsigned long num_of_nodes;
+    unsigned long num_of_links;
+    unsigned long k_max;
+    double energy;
+    double acceptance;
+    double *neighbours;
+public:
+    PottsModel(network *net, unsigned int q, int norm_by_degree);
+    ~PottsModel();
+    double* color_field;
+    unsigned long assign_initial_conf(int spin);
+    unsigned long initialize_lookup(double kT, double gamma);
+    double initialize_Qmatrix(void);
+    double calculate_Q(void);
+    double calculate_genQ(double gamma);
+    double FindStartTemp(double gamma, double prob,  double ts);
+    long   HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps);
+    double HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps);
+    long   HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps);
+    double HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps);
+    double GammaSweep(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel = true, int repetitions = 1);
+    double GammaSweepZeroTemp(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel = true, int repetitions = 1);
+    long   WriteCorrelationMatrix(char *filename);
+    double calculate_energy(double gamma);
+    long   WriteClusters(igraph_real_t *modularity,
+                         igraph_real_t *temperature,
+                         igraph_vector_t *csize, igraph_vector_t *membership,
+                         double kT, double gamma);
+    long   WriteSoftClusters(char *filename, double threshold);
+    double Get_Energy(void) {
+        return energy;
+    }
+    double FindCommunityFromStart(double gamma, double prob, char *nodename,
+                                  igraph_vector_t *result,
+                                  igraph_real_t *cohesion,
+                                  igraph_real_t *adhesion,
+                                  igraph_integer_t *inner_links,
+                                  igraph_integer_t *outer_links);
+};
+
+
+class PottsModelN {
+private:
+    //  HugeArray<double> neg_gammalookup;
+    //  HugeArray<double> pos_gammalookup;
+    DL_Indexed_List<unsigned int*> *new_spins;
+    DL_Indexed_List<unsigned int*> *previous_spins;
+    HugeArray<HugeArray<double>*> correlation;
+    network *net;
+
+    unsigned int q; //number of communities
+    double m_p; //number of positive ties (or sum of degrees), this equals the number of edges only if it is undirected and each edge has a weight of 1
+    double m_n; //number of negative ties (or sum of degrees)
+    unsigned int num_nodes; //number of nodes
+    bool is_directed;
+
+    bool is_init;
+
+    double *degree_pos_in; //Postive indegree of the nodes (or sum of weights)
+    double *degree_neg_in; //Negative indegree of the nodes (or sum of weights)
+    double *degree_pos_out; //Postive outdegree of the nodes (or sum of weights)
+    double *degree_neg_out; //Negative outdegree of the nodes (or sum of weights)
+
+    double *degree_community_pos_in; //Positive sum of indegree for communities
+    double *degree_community_neg_in; //Negative sum of indegree for communities
+    double *degree_community_pos_out; //Positive sum of outegree for communities
+    double *degree_community_neg_out; //Negative sum of outdegree for communities
+
+    unsigned int *csize; //The number of nodes in each community
+    unsigned int *spin; //The membership of each node
+
+    double *neighbours; //Array of neighbours of a vertex in each community
+    double *weights; //Weights of all possible transitions to another community
+
+public:
+    PottsModelN(network *n, unsigned int num_communities, bool directed);
+    ~PottsModelN();
+    void assign_initial_conf(bool init_spins);
+    double FindStartTemp(double gamma, double lambda, double ts);
+    double HeatBathLookup(double gamma, double lambda, double t, unsigned int max_sweeps);
+    double HeatBathJoin(double gamma, double lambda);
+    double HeatBathLookupZeroTemp(double gamma, double lambda, unsigned int max_sweeps);
+    long WriteClusters(igraph_real_t *modularity,
+                       igraph_real_t *temperature,
+                       igraph_vector_t *community_size,
+                       igraph_vector_t *membership,
+                       igraph_matrix_t *adhesion,
+                       igraph_matrix_t *normalised_adhesion,
+                       igraph_real_t *polarization,
+                       double t,
+                       double d_p,
+                       double d_n,
+                       double gamma,
+                       double lambda);
+};
+
+#endif
diff --git a/igraph/include/prpack.h b/igraph/include/prpack.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack.h
@@ -0,0 +1,54 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef IGRAPH_PRPACK
+#define IGRAPH_PRPACK
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+    #define __BEGIN_DECLS extern "C" {
+    #define __END_DECLS }
+#else
+    #define __BEGIN_DECLS /* empty */
+    #define __END_DECLS /* empty */
+#endif
+
+#include "igraph_types.h"
+#include "igraph_datatype.h"
+#include "igraph_iterators.h"
+
+#include "igraph_interface.h"
+
+__BEGIN_DECLS
+
+int igraph_personalized_pagerank_prpack(const igraph_t *graph, 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);
+
+__END_DECLS
+
+#endif
+
diff --git a/igraph/include/prpack/prpack.h b/igraph/include/prpack/prpack.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack.h
@@ -0,0 +1,11 @@
+#ifndef PRPACK
+#define PRPACK
+
+#include "prpack_csc.h"
+#include "prpack_csr.h"
+#include "prpack_edge_list.h"
+#include "prpack_base_graph.h"
+#include "prpack_solver.h"
+#include "prpack_result.h"
+
+#endif
diff --git a/igraph/include/prpack/prpack_base_graph.h b/igraph/include/prpack/prpack_base_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_base_graph.h
@@ -0,0 +1,42 @@
+#ifndef PRPACK_ADJACENCY_LIST
+#define PRPACK_ADJACENCY_LIST
+#include "prpack_csc.h"
+#include "prpack_csr.h"
+#include "prpack_edge_list.h"
+#include <cstdio>
+#include <utility>
+
+namespace prpack {
+
+    class prpack_base_graph {
+        private:
+            // helper methods
+            void initialize();
+            void read_smat(std::FILE* f, const bool weighted);
+            void read_edges(std::FILE* f);
+            void read_ascii(std::FILE* f);
+        public:
+            // instance variables
+            int num_vs;
+            int num_es;
+            int num_self_es;
+            int* heads;
+            int* tails;
+            double* vals;
+            // constructors
+            prpack_base_graph();    // only to support inheritance
+            prpack_base_graph(const prpack_csc* g);
+            prpack_base_graph(const prpack_int64_csc* g);
+            prpack_base_graph(const prpack_csr* g);
+            prpack_base_graph(const prpack_edge_list* g);
+            prpack_base_graph(const char* filename, const char* format, const bool weighted);
+            prpack_base_graph(int nverts, int nedges, std::pair<int,int>* edges);
+            // destructor
+            ~prpack_base_graph();
+            // operations
+            void normalize_weights();
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_csc.h b/igraph/include/prpack/prpack_csc.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_csc.h
@@ -0,0 +1,30 @@
+#ifndef PRPACK_CSC
+#define PRPACK_CSC
+
+#if !defined(_MSC_VER) && !defined (__MINGW32__) && !defined (__MINGW64__)
+#  include <stdint.h>
+#else
+#  include <stdio.h>
+typedef __int64 int64_t;
+#endif
+
+namespace prpack {
+
+    class prpack_csc {
+        public:
+            int num_vs;
+            int num_es;
+            int* heads;
+            int* tails;
+    };
+
+    class prpack_int64_csc {
+        public:
+            int64_t num_vs;
+            int64_t num_es;
+            int64_t* heads;
+            int64_t* tails;
+    };
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_csr.h b/igraph/include/prpack/prpack_csr.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_csr.h
@@ -0,0 +1,16 @@
+#ifndef PRPACK_CSR
+#define PRPACK_CSR
+
+namespace prpack {
+
+    class prpack_csr {
+        public:
+            int num_vs;
+            int num_es;
+            int* heads;
+            int* tails;
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_edge_list.h b/igraph/include/prpack/prpack_edge_list.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_edge_list.h
@@ -0,0 +1,16 @@
+#ifndef PRPACK_EDGE_LIST
+#define PRPACK_EDGE_LIST
+
+namespace prpack {
+
+    class prpack_edge_list {
+        public:
+            int num_vs;
+            int num_es;
+            int* heads;
+            int* tails;
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_igraph_graph.h b/igraph/include/prpack/prpack_igraph_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_igraph_graph.h
@@ -0,0 +1,26 @@
+#ifndef PRPACK_IGRAPH_GRAPH
+#define PRPACK_IGRAPH_GRAPH
+
+#ifdef PRPACK_IGRAPH_SUPPORT
+
+#include "igraph_interface.h"
+#include "prpack_base_graph.h"
+
+namespace prpack {
+
+    class prpack_igraph_graph : public prpack_base_graph {
+
+        public:
+            // constructors
+            explicit prpack_igraph_graph(const igraph_t* g,
+					const igraph_vector_t* weights = 0,
+					igraph_bool_t directed = true);
+    };
+
+};
+
+// PRPACK_IGRAPH_SUPPORT 
+#endif 
+
+// PRPACK_IGRAPH_GRAPH
+#endif
diff --git a/igraph/include/prpack/prpack_preprocessed_ge_graph.h b/igraph/include/prpack/prpack_preprocessed_ge_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_preprocessed_ge_graph.h
@@ -0,0 +1,26 @@
+#ifndef PRPACK_PREPROCESSED_GE_GRAPH
+#define PRPACK_PREPROCESSED_GE_GRAPH
+#include "prpack_preprocessed_graph.h"
+#include "prpack_base_graph.h"
+
+namespace prpack {
+
+    // Pre-processed graph class
+    class prpack_preprocessed_ge_graph : public prpack_preprocessed_graph {
+        private:
+            // helper methods
+            void initialize();
+            void initialize_weighted(const prpack_base_graph* bg);
+            void initialize_unweighted(const prpack_base_graph* bg);
+        public:
+            // instance variables
+            double* matrix;
+            // constructors
+            prpack_preprocessed_ge_graph(const prpack_base_graph* bg);
+            // destructor
+            ~prpack_preprocessed_ge_graph();
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_preprocessed_graph.h b/igraph/include/prpack/prpack_preprocessed_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_preprocessed_graph.h
@@ -0,0 +1,17 @@
+#ifndef PRPACK_PREPROCESSED_GRAPH
+#define PRPACK_PREPROCESSED_GRAPH
+
+namespace prpack {
+
+    // TODO: this class should not be seeable by the users of the library.
+    // Super graph class.
+    class prpack_preprocessed_graph {
+        public:
+            int num_vs;
+            int num_es;
+            double* d;
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_preprocessed_gs_graph.h b/igraph/include/prpack/prpack_preprocessed_gs_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_preprocessed_gs_graph.h
@@ -0,0 +1,30 @@
+#ifndef PRPACK_PREPROCESSED_GS_GRAPH
+#define PRPACK_PREPROCESSED_GS_GRAPH
+#include "prpack_preprocessed_graph.h"
+#include "prpack_base_graph.h"
+
+namespace prpack {
+
+    // Pre-processed graph class
+    class prpack_preprocessed_gs_graph : public prpack_preprocessed_graph {
+        private:
+            // helper methods
+            void initialize();
+            void initialize_weighted(const prpack_base_graph* bg);
+            void initialize_unweighted(const prpack_base_graph* bg);
+        public:
+            // instance variables
+            int* heads;
+            int* tails;
+            double* vals;
+            double* ii;
+            double* num_outlinks;
+            // constructors
+            prpack_preprocessed_gs_graph(const prpack_base_graph* bg);
+            // destructor
+            ~prpack_preprocessed_gs_graph();
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_preprocessed_scc_graph.h b/igraph/include/prpack/prpack_preprocessed_scc_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_preprocessed_scc_graph.h
@@ -0,0 +1,39 @@
+#ifndef PRPACK_PREPROCESSED_SCC_GRAPH
+#define PRPACK_PREPROCESSED_SCC_GRAPH
+#include "prpack_preprocessed_graph.h"
+#include "prpack_base_graph.h"
+
+namespace prpack {
+
+    // Pre-processed graph class
+    class prpack_preprocessed_scc_graph : public prpack_preprocessed_graph {
+        private:
+            // helper methods
+            void initialize();
+            void initialize_weighted(const prpack_base_graph* bg);
+            void initialize_unweighted(const prpack_base_graph* bg);
+        public:
+            // instance variables
+            int num_es_inside;
+            int* heads_inside;
+            int* tails_inside;
+            double* vals_inside;
+            int num_es_outside;
+            int* heads_outside;
+            int* tails_outside;
+            double* vals_outside;
+            double* ii;
+            double* num_outlinks;
+            int num_comps;
+            int* divisions;
+            int* encoding;
+            int* decoding;
+            // constructors
+            prpack_preprocessed_scc_graph(const prpack_base_graph* bg);
+            // destructor
+            ~prpack_preprocessed_scc_graph();
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_preprocessed_schur_graph.h b/igraph/include/prpack/prpack_preprocessed_schur_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_preprocessed_schur_graph.h
@@ -0,0 +1,33 @@
+#ifndef PRPACK_PREPROCESSED_SCHUR_GRAPH
+#define PRPACK_PREPROCESSED_SCHUR_GRAPH
+#include "prpack_preprocessed_graph.h"
+#include "prpack_base_graph.h"
+
+namespace prpack {
+
+    class prpack_preprocessed_schur_graph : public prpack_preprocessed_graph {
+        private:
+            // helper methods
+            void initialize();
+            void initialize_weighted(const prpack_base_graph* bg);
+            void initialize_unweighted(const prpack_base_graph* bg);
+        public:
+            // instance variables
+            int num_no_in_vs;
+            int num_no_out_vs;
+            int* heads;
+            int* tails;
+            double* vals;
+            double* ii;
+            double* num_outlinks;
+            int* encoding;
+            int* decoding;
+            // constructors
+            prpack_preprocessed_schur_graph(const prpack_base_graph* bg);
+            // destructor
+            ~prpack_preprocessed_schur_graph();
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_result.h b/igraph/include/prpack/prpack_result.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_result.h
@@ -0,0 +1,27 @@
+#ifndef PRPACK_RESULT
+#define PRPACK_RESULT
+
+namespace prpack {
+
+    // Result class.
+    class prpack_result {
+        public:
+            // instance variables
+            int num_vs;
+            int num_es;
+            double* x;
+            double read_time;
+            double preprocess_time;
+            double compute_time;
+            long num_es_touched;
+            const char* method;
+            int converged;
+            // constructor
+            prpack_result();
+            // destructor
+            ~prpack_result();
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_solver.h b/igraph/include/prpack/prpack_solver.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_solver.h
@@ -0,0 +1,178 @@
+#ifndef PRPACK_SOLVER
+#define PRPACK_SOLVER
+#include "prpack_base_graph.h"
+#include "prpack_csc.h"
+#include "prpack_csr.h"
+#include "prpack_edge_list.h"
+#include "prpack_preprocessed_ge_graph.h"
+#include "prpack_preprocessed_gs_graph.h"
+#include "prpack_preprocessed_scc_graph.h"
+#include "prpack_preprocessed_schur_graph.h"
+#include "prpack_result.h"
+
+// TODO Make this a user configurable variable
+#define PRPACK_SOLVER_MAX_ITERS 1000000
+
+namespace prpack {
+
+    // Solver class.
+    class prpack_solver {
+        private:
+            // instance variables
+            double read_time;
+            prpack_base_graph* bg;
+            prpack_preprocessed_ge_graph* geg;
+            prpack_preprocessed_gs_graph* gsg;
+            prpack_preprocessed_schur_graph* sg;
+            prpack_preprocessed_scc_graph* sccg;
+			bool owns_bg;
+            // methods
+            void initialize();
+            static prpack_result* solve_via_ge(
+                    const double alpha,
+                    const double tol,
+                    const int num_vs,
+                    const double* matrix,
+                    const double* uv);
+            static prpack_result* solve_via_ge_uv(
+                    const double alpha,
+                    const double tol,
+                    const int num_vs,
+                    const double* matrix,
+                    const double* d,
+                    const double* u,
+                    const double* v);
+            static prpack_result* solve_via_gs(
+                    const double alpha,
+                    const double tol,
+                    const int num_vs,
+                    const int num_es,
+                    const int* heads,
+                    const int* tails,
+                    const double* vals,
+                    const double* ii,
+                    const double* d,
+                    const double* num_outlinks,
+                    const double* u,
+                    const double* v);
+            static prpack_result* solve_via_gs_err(
+                    const double alpha,
+                    const double tol,
+                    const int num_vs,
+                    const int num_es,
+                    const int* heads,
+                    const int* tails,
+                    const double* ii,
+                    const double* num_outlinks,
+                    const double* u,
+                    const double* v);
+            static prpack_result* solve_via_schur_gs(
+                    const double alpha,
+                    const double tol,
+                    const int num_vs,
+                    const int num_no_in_vs,
+                    const int num_no_out_vs,
+                    const int num_es,
+                    const int* heads,
+                    const int* tails,
+                    const double* vals,
+                    const double* ii,
+                    const double* d,
+                    const double* num_outlinks,
+                    const double* uv,
+                    const int* encoding,
+                    const int* decoding,
+                    const bool should_normalize = true);
+            static prpack_result* solve_via_schur_gs_uv(
+                    const double alpha,
+                    const double tol,
+                    const int num_vs,
+                    const int num_no_in_vs,
+                    const int num_no_out_vs,
+                    const int num_es,
+                    const int* heads,
+                    const int* tails,
+                    const double* vals,
+                    const double* ii,
+                    const double* d,
+                    const double* num_outlinks,
+                    const double* u,
+                    const double* v,
+                    const int* encoding,
+                    const int* decoding);
+            static prpack_result* solve_via_scc_gs(
+                    const double alpha,
+                    const double tol,
+                    const int num_vs,
+                    const int num_es_inside,
+                    const int* heads_inside,
+                    const int* tails_inside,
+                    const double* vals_inside,
+                    const int num_es_outside,
+                    const int* heads_outside,
+                    const int* tails_outside,
+                    const double* vals_outside,
+                    const double* ii,
+                    const double* d,
+                    const double* num_outlinks,
+                    const double* uv,
+                    const int num_comps,
+                    const int* divisions,
+                    const int* encoding,
+                    const int* decoding,
+                    const bool should_normalize = true);
+            static prpack_result* solve_via_scc_gs_uv(
+                    const double alpha,
+                    const double tol,
+                    const int num_vs,
+                    const int num_es_inside,
+                    const int* heads_inside,
+                    const int* tails_inside,
+                    const double* vals_inside,
+                    const int num_es_outside,
+                    const int* heads_outside,
+                    const int* tails_outside,
+                    const double* vals_outside,
+                    const double* ii,
+                    const double* d,
+                    const double* num_outlinks,
+                    const double* u,
+                    const double* v,
+                    const int num_comps,
+                    const int* divisions,
+                    const int* encoding,
+                    const int* decoding);
+            static void ge(const int sz, double* A, double* b);
+            static void normalize(const int length, double* x);
+            static prpack_result* combine_uv(
+                    const int num_vs,
+                    const double* d,
+                    const double* num_outlinks,
+                    const int* encoding,
+                    const double alpha,
+                    const prpack_result* ret_u,
+                    const prpack_result* ret_v);
+        public:
+            // constructors
+            prpack_solver(const prpack_csc* g);
+            prpack_solver(const prpack_int64_csc* g);
+            prpack_solver(const prpack_csr* g);
+            prpack_solver(const prpack_edge_list* g);
+            prpack_solver(prpack_base_graph* g, bool owns_bg=true);
+            prpack_solver(const char* filename, const char* format, const bool weighted);
+            // destructor
+            ~prpack_solver();
+            // methods
+            int get_num_vs();
+            prpack_result* solve(const double alpha, const double tol, const char* method);
+            prpack_result* solve(
+                    const double alpha,
+                    const double tol,
+                    const double* u,
+                    const double* v,
+                    const char* method);
+    };
+
+};
+
+#endif
diff --git a/igraph/include/prpack/prpack_utils.h b/igraph/include/prpack/prpack_utils.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/prpack/prpack_utils.h
@@ -0,0 +1,34 @@
+#ifndef PRPACK_UTILS
+#define PRPACK_UTILS
+#ifdef MATLAB_MEX_FILE
+#include "mex.h"
+#endif
+#include <string>
+
+// Computes the time taken to do X and stores it in T.
+#define TIME(T, X)                  \
+    (T) = prpack_utils::get_time(); \
+    (X);                            \
+    (T) = prpack_utils::get_time() - (T)
+
+// Computes S += A using C as a carry-over.
+// This is a macro over a function as it is faster this way.
+#define COMPENSATED_SUM(S, A, C)                        \
+    double compensated_sum_y = (A) - (C);               \
+    double compensated_sum_t = (S) + compensated_sum_y; \
+    (C) = compensated_sum_t - (S) - compensated_sum_y;  \
+    (S) = compensated_sum_t
+
+namespace prpack {
+
+    class prpack_utils {
+        public:
+            static double get_time();
+            static void validate(const bool condition, const std::string& msg);
+            static double* permute(const int length, const double* a, const int* coding);
+    };
+
+};
+
+#endif
+
diff --git a/igraph/include/scg_headers.h b/igraph/include/scg_headers.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/scg_headers.h
@@ -0,0 +1,128 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/*
+ *  SCGlib : A C library for the spectral coarse graining of matrices
+ *  as described in the paper: Shrinking Matrices while preserving their
+ *  eigenpairs with Application to the Spectral Coarse Graining of Graphs.
+ *  Preprint available at <http://people.epfl.ch/david.morton>
+ *
+ *  Copyright (C) 2008 David Morton de Lachapelle <david.morton@a3.epfl.ch>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301 USA
+ *
+ *  DESCRIPTION
+ *  -----------
+ *    This file contains the headers of the library SCGlib.
+ *    For use with R software <http://www.r-project.org/> define
+ *    the constant R_COMPIL and refer to the R documentation to compile
+ *    a dynamic library. The scg_r_wrapper function should be useful.
+ */
+
+#ifndef SCG_HEADERS_H
+#define SCG_HEADERS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+
+typedef struct ind_val {
+    int ind;
+    igraph_real_t val;
+} igraph_i_scg_indval_t;
+
+int igraph_i_compare_ind_val(const void *a, const void *b);
+
+typedef struct groups {
+    int ind;
+    int n;
+    int* gr;
+} igraph_i_scg_groups_t;
+
+/*-------------------------------------------------
+------------DEFINED IN scg_approximate_methods.c---
+---------------------------------------------------*/
+
+int igraph_i_breaks_computation(const igraph_vector_t *v,
+                                igraph_vector_t *breaks, int nb,
+                                int method);
+int igraph_i_intervals_plus_kmeans(const igraph_vector_t *v, int *gr,
+                                   int n, int n_interv,
+                                   int maxiter);
+int igraph_i_intervals_method(const igraph_vector_t *v, int *gr,
+                              int n, int n_interv);
+
+/*-------------------------------------------------
+------------DEFINED IN scg_optimal_method.c--------
+---------------------------------------------------*/
+
+int igraph_i_cost_matrix(igraph_real_t *Cv, const igraph_i_scg_indval_t *vs,
+                         int n, int matrix, const igraph_vector_t *ps);
+int igraph_i_optimal_partition(const igraph_real_t *v, int *gr, int n, int nt,
+                               int matrix, const igraph_real_t *p,
+                               igraph_real_t *value);
+
+/*-------------------------------------------------
+------------DEFINED IN scg_kmeans.c----------------
+---------------------------------------------------*/
+
+int igraph_i_kmeans_Lloyd(const igraph_vector_t *x, int n,
+                          int p, igraph_vector_t *centers,
+                          int k, int *cl, int maxiter);
+
+/*-------------------------------------------------
+------------DEFINED IN scg_exact_scg.c-------------
+---------------------------------------------------*/
+
+int igraph_i_exact_coarse_graining(const igraph_real_t *v, int *gr,
+                                   int n);
+
+/*-------------------------------------------------
+------------DEFINED IN scg_utils.c-----------------
+---------------------------------------------------*/
+
+int igraph_i_compare_groups(const void *a, const void *b);
+int igraph_i_compare_real(const void *a, const void *b);
+int igraph_i_compare_int(const void *a, const void *b);
+
+igraph_real_t *igraph_i_real_sym_matrix(int size);
+#define igraph_i_real_sym_mat_get(S,i,j) S[i+j*(j+1)/2]
+#define igraph_i_real_sym_mat_set(S,i,j,val) S[i+j*(j+1)/2] = val
+#define igraph_i_free_real_sym_matrix(S) igraph_Free(S)
+
+#endif
diff --git a/igraph/include/stack.pmt b/igraph/include/stack.pmt
new file mode 100644
--- /dev/null
+++ b/igraph/include/stack.pmt
@@ -0,0 +1,294 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_init
+ * \brief Initializes a stack.
+ *
+ * The initialized stack is always empty.
+ * \param s Pointer to an uninitialized stack.
+ * \param size The number of elements to allocate memory for.
+ * \return Error code.
+ *
+ * Time complexity: O(\p size).
+ */
+
+int FUNCTION(igraph_stack, init)       (TYPE(igraph_stack)* s, long int size) {
+    long int alloc_size = size > 0 ? size : 1;
+    assert (s != NULL);
+    if (size < 0) {
+        size = 0;
+    }
+    s->stor_begin = igraph_Calloc(alloc_size, BASE);
+    if (s->stor_begin == 0) {
+        IGRAPH_ERROR("stack init failed", IGRAPH_ENOMEM);
+    }
+    s->stor_end = s->stor_begin + alloc_size;
+    s->end = s->stor_begin;
+
+    return 0;
+}
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_destroy
+ * \brief Destroys a stack object.
+ *
+ * Deallocate the memory used for a stack.
+ * It is possible to reinitialize a destroyed stack again by
+ * \ref igraph_stack_init().
+ * \param s The stack to destroy.
+ *
+ * Time complexity: O(1).
+ */
+
+void FUNCTION(igraph_stack, destroy)    (TYPE(igraph_stack)* s) {
+    assert( s != NULL);
+    if (s->stor_begin != 0) {
+        igraph_Free(s->stor_begin);
+        s->stor_begin = NULL;
+    }
+}
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_reserve
+ * \brief Reserve memory.
+ *
+ * Reserve memory for future use. The actual size of the stack is
+ * unchanged.
+ * \param s The stack object.
+ * \param size The number of elements to reserve memory for. If it is
+ *     not bigger than the current size then nothing happens.
+ * \return Error code.
+ *
+ * Time complexity: should be around O(n), the new allocated size of
+ * the stack.
+ */
+
+int FUNCTION(igraph_stack, reserve)    (TYPE(igraph_stack)* s, long int size) {
+    long int actual_size = FUNCTION(igraph_stack, size)(s);
+    BASE *tmp;
+    assert(s != NULL);
+    assert(s->stor_begin != NULL);
+
+    if (size <= actual_size) {
+        return 0;
+    }
+
+    tmp = igraph_Realloc(s->stor_begin, (size_t) size, BASE);
+    if (tmp == 0) {
+        IGRAPH_ERROR("stack reserve failed", IGRAPH_ENOMEM);
+    }
+    s->stor_begin = tmp;
+    s->stor_end = s->stor_begin + size;
+    s->end = s->stor_begin + actual_size;
+
+    return 0;
+}
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_empty
+ * \brief Decides whether a stack object is empty.
+ *
+ * \param s The stack object.
+ * \return Boolean, \c TRUE if the stack is empty, \c FALSE
+ * otherwise.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_bool_t FUNCTION(igraph_stack, empty)      (TYPE(igraph_stack)* s) {
+    assert (s != NULL);
+    assert (s->stor_begin != NULL);
+    assert (s->end != NULL);
+    return s->stor_begin == s->end;
+}
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_size
+ * \brief Returns the number of elements in a stack.
+ *
+ * \param s The stack object.
+ * \return The number of elements in the stack.
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_stack, size)       (const TYPE(igraph_stack)* s) {
+    assert (s != NULL);
+    assert (s->stor_begin != NULL);
+    return s->end - s->stor_begin;
+}
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_clear
+ * \brief Removes all elements from a stack.
+ *
+ * \param s The stack object.
+ *
+ * Time complexity: O(1).
+ */
+
+void FUNCTION(igraph_stack, clear)      (TYPE(igraph_stack)* s) {
+    assert (s != NULL);
+    assert (s->stor_begin != NULL);
+    s->end = s->stor_begin;
+}
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_push
+ * \brief Places an element on the top of a stack.
+ *
+ * The capacity of the stack is increased, if needed.
+ * \param s The stack object.
+ * \param elem The element to push.
+ * \return Error code.
+ *
+ * Time complexity: O(1) is no reallocation is needed, O(n)
+ * otherwise, but it is ensured that n push operations are performed
+ * in O(n) time.
+ */
+
+int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem) {
+    assert (s != NULL);
+    assert (s->stor_begin != NULL);
+    if (s->end == s->stor_end) {
+        /* full, allocate more storage */
+
+        BASE *bigger = NULL, *old = s->stor_begin;
+
+        bigger = igraph_Calloc(2 * FUNCTION(igraph_stack, size)(s) + 1, BASE);
+        if (bigger == 0) {
+            IGRAPH_ERROR("stack push failed", IGRAPH_ENOMEM);
+        }
+        memcpy(bigger, s->stor_begin,
+               (size_t) FUNCTION(igraph_stack, size)(s)*sizeof(BASE));
+
+        s->end        = bigger + (s->stor_end - s->stor_begin);
+        s->stor_end   = bigger + 2 * (s->stor_end - s->stor_begin) + 1;
+        s->stor_begin = bigger;
+
+        *(s->end) = elem;
+        (s->end) += 1;
+
+        igraph_Free(old);
+    } else {
+        *(s->end) = elem;
+        (s->end) += 1;
+    }
+    return 0;
+}
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_pop
+ * \brief Removes and returns an element from the top of a stack.
+ *
+ * The stack must contain at least one element, call \ref
+ * igraph_stack_empty() to make sure of this.
+ * \param s The stack object.
+ * \return The removed top element.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_stack, pop)        (TYPE(igraph_stack)* s) {
+
+    assert (s != NULL);
+    assert (s->stor_begin != NULL);
+    assert (s->end != NULL);
+    assert (s->end != s->stor_begin);
+
+    (s->end)--;
+
+    return *(s->end);
+}
+
+/**
+ * \ingroup stack
+ * \function igraph_stack_top
+ * \brief Query top element.
+ *
+ * Returns the top element of the stack, without removing it.
+ * The stack must be non-empty.
+ * \param s The stack.
+ * \return The top element.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_stack, top)        (const TYPE(igraph_stack)* s) {
+
+    assert (s != NULL);
+    assert (s->stor_begin != NULL);
+    assert (s->end != NULL);
+    assert (s->end != s->stor_begin);
+
+    return *(s->end - 1);
+}
+
+#if defined (OUT_FORMAT)
+#ifndef USING_R
+
+int FUNCTION(igraph_stack, print)(const TYPE(igraph_stack) *s) {
+    long int i, n = FUNCTION(igraph_stack, size)(s);
+    if (n != 0) {
+        printf(OUT_FORMAT, s->stor_begin[0]);
+    }
+    for (i = 1; i < n; i++) {
+        printf(" " OUT_FORMAT, s->stor_begin[i]);
+    }
+    printf("\n");
+    return 0;
+}
+#endif
+
+int FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack) *s, FILE *file) {
+    long int i, n = FUNCTION(igraph_stack, size)(s);
+    if (n != 0) {
+        fprintf(file, OUT_FORMAT, s->stor_begin[0]);
+    }
+    for (i = 1; i < n; i++) {
+        fprintf(file, " " OUT_FORMAT, s->stor_begin[i]);
+    }
+    fprintf(file, "\n");
+    return 0;
+}
+
+#endif
+
diff --git a/igraph/include/structural_properties_internal.h b/igraph/include/structural_properties_internal.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/structural_properties_internal.h
@@ -0,0 +1,47 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2016  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#ifndef STRUCTURAL_PROPERTIES_INTERNAL_H
+#define STRUCTURAL_PROPERTIES_INTERNAL_H
+
+#include "igraph_constants.h"
+#include "igraph_types.h"
+#include "igraph_iterators.h"
+
+int igraph_i_induced_subgraph_suggest_implementation(
+    const igraph_t *graph, const igraph_vs_t vids,
+    igraph_subgraph_implementation_t* result
+);
+
+int igraph_i_subgraph_copy_and_delete(const igraph_t *graph, igraph_t *res,
+                                      const igraph_vs_t vids,
+                                      igraph_vector_t *map,
+                                      igraph_vector_t *invmap);
+
+int igraph_i_subgraph_create_from_scratch(const igraph_t *graph,
+        igraph_t *res,
+        const igraph_vs_t vids,
+        igraph_vector_t *map,
+        igraph_vector_t *invmap);
+
+#endif
diff --git a/igraph/include/triangles_template.h b/igraph/include/triangles_template.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/triangles_template.h
@@ -0,0 +1,118 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+long int no_of_nodes = igraph_vcount(graph);
+long int node, i, j, nn;
+igraph_adjlist_t allneis;
+igraph_vector_int_t *neis1, *neis2;
+long int neilen1, neilen2, deg1;
+long int *neis;
+long int maxdegree;
+
+igraph_vector_int_t order;
+igraph_vector_int_t rank;
+igraph_vector_t degree;
+
+igraph_vector_int_init(&order, no_of_nodes);
+IGRAPH_FINALLY(igraph_vector_int_destroy, &order);
+IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
+                           IGRAPH_LOOPS));
+maxdegree = (long int) igraph_vector_max(&degree) + 1;
+igraph_vector_order1_int(&degree, &order, maxdegree);
+igraph_vector_int_init(&rank, no_of_nodes);
+IGRAPH_FINALLY(igraph_vector_int_destroy, &rank);
+for (i = 0; i < no_of_nodes; i++) {
+    VECTOR(rank)[ VECTOR(order)[i] ] = no_of_nodes - i - 1;
+}
+
+IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL));
+IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+IGRAPH_CHECK(igraph_i_trans4_al_simplify(&allneis, &rank));
+
+neis = igraph_Calloc(no_of_nodes, long int);
+if (neis == 0) {
+    IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM);
+}
+IGRAPH_FINALLY(igraph_free, neis);
+
+#ifndef TRIANGLES
+    IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
+    igraph_vector_null(res);
+#else
+    igraph_vector_int_clear(res);
+#endif
+
+for (nn = no_of_nodes - 1; nn >= 0; nn--) {
+    node = VECTOR(order)[nn];
+
+    IGRAPH_ALLOW_INTERRUPTION();
+
+    neis1 = igraph_adjlist_get(&allneis, node);
+    neilen1 = igraph_vector_int_size(neis1);
+    deg1 = (long int) VECTOR(degree)[node];
+    /* Mark the neighbors of the node */
+    for (i = 0; i < neilen1; i++) {
+        neis[ (long int) VECTOR(*neis1)[i] ] = node + 1;
+    }
+
+    for (i = 0; i < neilen1; i++) {
+        long int nei = (long int) VECTOR(*neis1)[i];
+        neis2 = igraph_adjlist_get(&allneis, nei);
+        neilen2 = igraph_vector_int_size(neis2);
+        for (j = 0; j < neilen2; j++) {
+            long int nei2 = (long int) VECTOR(*neis2)[j];
+            if (neis[nei2] == node + 1) {
+#ifndef TRIANGLES
+                VECTOR(*res)[nei2] += 1;
+                VECTOR(*res)[nei] += 1;
+                VECTOR(*res)[node] += 1;
+#else
+                IGRAPH_CHECK(igraph_vector_int_push_back(res, node));
+                IGRAPH_CHECK(igraph_vector_int_push_back(res, nei));
+                IGRAPH_CHECK(igraph_vector_int_push_back(res, nei2));
+#endif
+            }
+        }
+    }
+
+#ifdef TRANSIT
+    if (mode == IGRAPH_TRANSITIVITY_ZERO && deg1 < 2) {
+        VECTOR(*res)[node] = 0.0;
+    } else {
+        VECTOR(*res)[node] = VECTOR(*res)[node] / deg1 / (deg1 - 1) * 2.0;
+    }
+#endif
+#ifdef TRIEDGES
+    VECTOR(*res)[node] += deg1;
+#endif
+}
+
+igraph_free(neis);
+igraph_adjlist_destroy(&allneis);
+igraph_vector_int_destroy(&rank);
+igraph_vector_destroy(&degree);
+igraph_vector_int_destroy(&order);
+IGRAPH_FINALLY_CLEAN(5);
diff --git a/igraph/include/triangles_template1.h b/igraph/include/triangles_template1.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/triangles_template1.h
@@ -0,0 +1,88 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+long int no_of_nodes = igraph_vcount(graph);
+igraph_vit_t vit;
+long int nodes_to_calc;
+igraph_vector_t *neis1, *neis2;
+igraph_real_t triangles;
+long int i, j, k;
+long int neilen1, neilen2;
+long int *neis;
+igraph_lazy_adjlist_t adjlist;
+
+IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+
+neis = igraph_Calloc(no_of_nodes, long int);
+if (neis == 0) {
+    IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM);
+}
+IGRAPH_FINALLY(igraph_free, neis);
+
+IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
+
+igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_SIMPLIFY);
+IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
+
+for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+    long int node = IGRAPH_VIT_GET(vit);
+
+    IGRAPH_ALLOW_INTERRUPTION();
+
+    neis1 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node);
+    neilen1 = igraph_vector_size(neis1);
+    for (j = 0; j < neilen1; j++) {
+        neis[ (long int)VECTOR(*neis1)[j] ] = i + 1;
+    }
+    triangles = 0;
+
+    for (j = 0; j < neilen1; j++) {
+        long int v = (long int) VECTOR(*neis1)[j];
+        neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v);
+        neilen2 = igraph_vector_size(neis2);
+        for (k = 0; k < neilen2; k++) {
+            long int v2 = (long int) VECTOR(*neis2)[k];
+            if (neis[v2] == i + 1) {
+                triangles += 1.0;
+            }
+        }
+    }
+
+#ifdef TRANSIT
+    if (mode == IGRAPH_TRANSITIVITY_ZERO && neilen1 < 2) {
+        VECTOR(*res)[i] = 0.0;
+    } else {
+        VECTOR(*res)[i] = triangles / neilen1 / (neilen1 - 1);
+    }
+#else
+    VECTOR(*res)[i] = triangles / 2;
+#endif
+}
+
+igraph_lazy_adjlist_destroy(&adjlist);
+igraph_Free(neis);
+igraph_vit_destroy(&vit);
+IGRAPH_FINALLY_CLEAN(3);
diff --git a/igraph/include/vector.pmt b/igraph/include/vector.pmt
new file mode 100644
--- /dev/null
+++ b/igraph/include/vector.pmt
@@ -0,0 +1,2684 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "igraph_random.h"
+#include "igraph_qsort.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+#include <stdarg.h>     /* va_start & co */
+#include <math.h>
+
+/**
+ * \ingroup vector
+ * \section about_igraph_vector_t_objects About \type igraph_vector_t objects
+ *
+ * <para>The \type igraph_vector_t data type is a simple and efficient
+ * interface to arrays containing numbers. It is something
+ * similar as (but much simpler than) the \type vector template
+ * in the C++ standard library.</para>
+ *
+ * <para>Vectors are used extensively in \a igraph, all
+ * functions which expect or return a list of numbers use
+ * igraph_vector_t to achieve this.</para>
+ *
+ * <para>The \type igraph_vector_t type usually uses
+ * O(n) space
+ * to store n elements. Sometimes it
+ * uses more, this is because vectors can shrink, but even if they
+ * shrink, the current implementation does not free a single bit of
+ * memory.</para>
+ *
+ * <para>The elements in an \type igraph_vector_t
+ * object are indexed from zero, we follow the usual C convention
+ * here.</para>
+ *
+ * <para>The elements of a vector always occupy a single block of
+ * memory, the starting address of this memory block can be queried
+ * with the \ref VECTOR macro. This way, vector objects can be used
+ * with standard mathematical libraries, like the GNU Scientific
+ * Library.</para>
+ */
+
+/**
+ * \ingroup vector
+ * \section igraph_vector_constructors_and_destructors Constructors and
+ * Destructors
+ *
+ * <para>\type igraph_vector_t objects have to be initialized before using
+ * them, this is analogous to calling a constructor on them. There are a
+ * number of \type igraph_vector_t constructors, for your
+ * convenience. \ref igraph_vector_init() is the basic constructor, it
+ * creates a vector of the given length, filled with zeros.
+ * \ref igraph_vector_copy() creates a new identical copy
+ * of an already existing and initialized vector. \ref
+ * igraph_vector_init_copy() creates a vector by copying a regular C array.
+ * \ref igraph_vector_init_seq() creates a vector containing a regular
+ * sequence with increment one.</para>
+ *
+ * <para>\ref igraph_vector_view() is a special constructor, it allows you to
+ * handle a regular C array as a \type vector without copying
+ * its elements.
+ * </para>
+ *
+ * <para>If a \type igraph_vector_t object is not needed any more, it
+ * should be destroyed to free its allocated memory by calling the
+ * \type igraph_vector_t destructor, \ref igraph_vector_destroy().</para>
+ *
+ * <para> Note that vectors created by \ref igraph_vector_view() are special,
+ * you mustn't call \ref igraph_vector_destroy() on these.</para>
+ */
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_init
+ * \brief Initializes a vector object (constructor).
+ *
+ * </para><para>
+ * Every vector needs to be initialized before it can be used, and
+ * there are a number of initialization functions or otherwise called
+ * constructors. This function constructs a vector of the given size and
+ * initializes each entry to 0. Note that \ref igraph_vector_null() can be
+ * used to set each element of a vector to zero. However, if you want a
+ * vector of zeros, it is much faster to use this function than to create a
+ * vector and then invoke \ref igraph_vector_null().
+ *
+ * </para><para>
+ * Every vector object initialized by this function should be
+ * destroyed (ie. the memory allocated for it should be freed) when it
+ * is not needed anymore, the \ref igraph_vector_destroy() function is
+ * responsible for this.
+ * \param v Pointer to a not yet initialized vector object.
+ * \param size The size of the vector.
+ * \return error code:
+ *       \c IGRAPH_ENOMEM if there is not enough memory.
+ *
+ * Time complexity: operating system dependent, the amount of
+ * \quote time \endquote required to allocate
+ * O(n) elements,
+ * n is the number of elements.
+ */
+
+int FUNCTION(igraph_vector, init)      (TYPE(igraph_vector)* v, int long size) {
+    long int alloc_size = size > 0 ? size : 1;
+    if (size < 0) {
+        size = 0;
+    }
+    v->stor_begin = igraph_Calloc(alloc_size, BASE);
+    if (v->stor_begin == 0) {
+        IGRAPH_ERROR("cannot init vector", IGRAPH_ENOMEM);
+    }
+    v->stor_end = v->stor_begin + alloc_size;
+    v->end = v->stor_begin + size;
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_view
+ * \brief Handle a regular C array as a \type igraph_vector_t.
+ *
+ * </para><para>
+ * This is a special \type igraph_vector_t constructor. It allows to
+ * handle a regular C array as a \type igraph_vector_t temporarily.
+ * Be sure that you \em don't ever call the destructor (\ref
+ * igraph_vector_destroy()) on objects created by this constructor.
+ * \param v Pointer to an uninitialized \type igraph_vector_t object.
+ * \param data Pointer, the C array. It may not be \c NULL.
+ * \param length The length of the C array.
+ * \return Pointer to the vector object, the same as the
+ *     \p v parameter, for convenience.
+ *
+ * Time complexity: O(1)
+ */
+
+const TYPE(igraph_vector)*FUNCTION(igraph_vector, view) (const TYPE(igraph_vector) *v,
+        const BASE *data,
+        long int length) {
+    TYPE(igraph_vector) *v2 = (TYPE(igraph_vector)*)v;
+
+    assert(data != 0);
+
+    v2->stor_begin = (BASE*)data;
+    v2->stor_end = (BASE*)data + length;
+    v2->end = v2->stor_end;
+    return v;
+}
+
+#ifndef BASE_COMPLEX
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_init_real
+ * \brief Create an \type igraph_vector_t from the parameters.
+ *
+ * </para><para>
+ * Because of how C and the C library handles variable length argument
+ * lists, it is required that you supply real constants to this
+ * function. This means that
+ * \verbatim igraph_vector_t v;
+ * igraph_vector_init_real(&amp;v, 5, 1,2,3,4,5); \endverbatim
+ * is an error at runtime and the results are undefined. This is
+ * the proper way:
+ * \verbatim igraph_vector_t v;
+ * igraph_vector_init_real(&amp;v, 5, 1.0,2.0,3.0,4.0,5.0); \endverbatim
+ * \param v Pointer to an uninitialized \type igraph_vector_t object.
+ * \param no Positive integer, the number of \type igraph_real_t
+ *    parameters to follow.
+ * \param ... The elements of the vector.
+ * \return Error code, this can be \c IGRAPH_ENOMEM
+ *     if there isn't enough memory to allocate the vector.
+ *
+ * \sa \ref igraph_vector_init_real_end(), \ref igraph_vector_init_int() for similar
+ * functions.
+ *
+ * Time complexity: depends on the time required to allocate memory,
+ * but at least O(n), the number of
+ * elements in the vector.
+ */
+
+int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector) *v, int no, ...) {
+    int i = 0;
+    va_list ap;
+    IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, no));
+
+    va_start(ap, no);
+    for (i = 0; i < no; i++) {
+        VECTOR(*v)[i] = (BASE) va_arg(ap, double);
+    }
+    va_end(ap);
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_init_real_end
+ * \brief Create an \type igraph_vector_t from the parameters.
+ *
+ * </para><para>
+ * This constructor is similar to \ref igraph_vector_init_real(), the only
+ * difference is that instead of giving the number of elements in the
+ * vector, a special marker element follows the last real vector
+ * element.
+ * \param v Pointer to an uninitialized \type igraph_vector_t object.
+ * \param endmark This element will signal the end of the vector. It
+ *    will \em not be part of the vector.
+ * \param ... The elements of the vector.
+ * \return Error code, \c IGRAPH_ENOMEM if there
+ *    isn't enough memory.
+ *
+ * \sa \ref igraph_vector_init_real() and \ref igraph_vector_init_int_end() for
+ * similar functions.
+ *
+ * Time complexity: at least O(n) for
+ * n elements plus the time
+ * complexity of the memory allocation.
+ */
+
+int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v,
+        BASE endmark, ...) {
+    int i = 0, n = 0;
+    va_list ap;
+
+    va_start(ap, endmark);
+    while (1) {
+        BASE num = (BASE) va_arg(ap, double);
+        if (num == endmark) {
+            break;
+        }
+        n++;
+    }
+    va_end(ap);
+
+    IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, n));
+    IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), v);
+
+    va_start(ap, endmark);
+    for (i = 0; i < n; i++) {
+        VECTOR(*v)[i] = (BASE) va_arg(ap, double);
+    }
+    va_end(ap);
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_init_int
+ * \brief Create an \type igraph_vector_t containing the parameters.
+ *
+ * </para><para>
+ * This function is similar to \ref igraph_vector_init_real(), but it expects
+ * \type int parameters. It is important that all parameters
+ * should be of this type, otherwise the result of the function call
+ * is undefined.
+ * \param v Pointer to an uninitialized \type igraph_vector_t object.
+ * \param no The number of \type int parameters to follow.
+ * \param ... The elements of the vector.
+ * \return Error code, \c IGRAPH_ENOMEM if there is
+ *    not enough memory.
+ * \sa \ref igraph_vector_init_real() and igraph_vector_init_int_end(), these are
+ *    similar functions.
+ *
+ * Time complexity: at least O(n) for
+ * n elements plus the time
+ * complexity of the memory allocation.
+ */
+
+int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector) *v, int no, ...) {
+    int i = 0;
+    va_list ap;
+    IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, no));
+
+    va_start(ap, no);
+    for (i = 0; i < no; i++) {
+        VECTOR(*v)[i] = (BASE) va_arg(ap, int);
+    }
+    va_end(ap);
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_init_int_end
+ * \brief Create an \type igraph_vector_t from the parameters.
+ *
+ * </para><para>
+ * This constructor is similar to \ref igraph_vector_init_int(), the only
+ * difference is that instead of giving the number of elements in the
+ * vector, a special marker element follows the last real vector
+ * element.
+ * \param v Pointer to an uninitialized \type igraph_vector_t object.
+ * \param endmark This element will signal the end of the vector. It
+ *    will \em not be part of the vector.
+ * \param ... The elements of the vector.
+ * \return Error code, \c IGRAPH_ENOMEM if there
+ *    isn't enough memory.
+ *
+ * \sa \ref igraph_vector_init_int() and \ref igraph_vector_init_real_end() for
+ * similar functions.
+ *
+ * Time complexity: at least O(n) for
+ * n elements plus the time
+ * complexity of the memory allocation.
+ */
+
+int FUNCTION(igraph_vector_init, int_end)(TYPE(igraph_vector) *v, int endmark, ...) {
+    int i = 0, n = 0;
+    va_list ap;
+
+    va_start(ap, endmark);
+    while (1) {
+        int num = va_arg(ap, int);
+        if (num == endmark) {
+            break;
+        }
+        n++;
+    }
+    va_end(ap);
+
+    IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, n));
+    IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), v);
+
+    va_start(ap, endmark);
+    for (i = 0; i < n; i++) {
+        VECTOR(*v)[i] = (BASE) va_arg(ap, int);
+    }
+    va_end(ap);
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+#endif /* ifndef BASE_COMPLEX */
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_destroy
+ * \brief Destroys a vector object.
+ *
+ * </para><para>
+ * All vectors initialized by \ref igraph_vector_init() should be properly
+ * destroyed by this function. A destroyed vector needs to be
+ * reinitialized by \ref igraph_vector_init(), \ref igraph_vector_init_copy() or
+ * another constructor.
+ * \param v Pointer to the (previously initialized) vector object to
+ *        destroy.
+ *
+ * Time complexity: operating system dependent.
+ */
+
+void FUNCTION(igraph_vector, destroy)   (TYPE(igraph_vector)* v) {
+    assert(v != 0);
+    if (v->stor_begin != 0) {
+        igraph_Free(v->stor_begin);
+        v->stor_begin = NULL;
+    }
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_capacity
+ * \brief Returns the allocated capacity of the vector
+ *
+ * Note that this might be different from the size of the vector (as
+ * queried by \ref igraph_vector_size(), and specifies how many elements
+ * the vector can hold, without reallocation.
+ * \param v Pointer to the (previously initialized) vector object
+ *          to query.
+ * \return The allocated capacity.
+ *
+ * \sa \ref igraph_vector_size().
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) {
+    return v->stor_end - v->stor_begin;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_reserve
+ * \brief Reserves memory for a vector.
+ *
+ * </para><para>
+ * \a igraph vectors are flexible, they can grow and
+ * shrink. Growing
+ * however occasionally needs the data in the vector to be copied.
+ * In order to avoid this, you can call this function to reserve space for
+ * future growth of the vector.
+ *
+ * </para><para>
+ * Note that this function does \em not change the size of the
+ * vector. Let us see a small example to clarify things: if you
+ * reserve space for 100 elements and the size of your
+ * vector was (and still is) 60, then you can surely add additional 40
+ * elements to your vector before it will be copied.
+ * \param v The vector object.
+ * \param size The new \em allocated size of the vector.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM if there is not enough memory.
+ *
+ * Time complexity: operating system dependent, should be around
+ * O(n), n
+ * is the new allocated size of the vector.
+ */
+
+int FUNCTION(igraph_vector, reserve)   (TYPE(igraph_vector)* v, long int size) {
+    long int actual_size = FUNCTION(igraph_vector, size)(v);
+    BASE *tmp;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    if (size <= FUNCTION(igraph_vector, size)(v)) {
+        return 0;
+    }
+
+    tmp = igraph_Realloc(v->stor_begin, (size_t) size, BASE);
+    if (tmp == 0) {
+        IGRAPH_ERROR("cannot reserve space for vector", IGRAPH_ENOMEM);
+    }
+    v->stor_begin = tmp;
+    v->stor_end = v->stor_begin + size;
+    v->end = v->stor_begin + actual_size;
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_empty
+ * \brief Decides whether the size of the vector is zero.
+ *
+ * \param v The vector object.
+ * \return Non-zero number (true) if the size of the vector is zero and
+ *         zero (false) otherwise.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, empty)     (const TYPE(igraph_vector)* v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    return v->stor_begin == v->end;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_size
+ * \brief Gives the size (=length) of the vector.
+ *
+ * \param v The vector object
+ * \return The size of the vector.
+ *
+ * Time complexity: O(1).
+ */
+
+long int FUNCTION(igraph_vector, size)      (const TYPE(igraph_vector)* v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    return v->end - v->stor_begin;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_clear
+ * \brief Removes all elements from a vector.
+ *
+ * </para><para>
+ * This function simply sets the size of the vector to zero, it does
+ * not free any allocated memory. For that you have to call
+ * \ref igraph_vector_destroy().
+ * \param v The vector object.
+ *
+ * Time complexity: O(1).
+ */
+
+void FUNCTION(igraph_vector, clear)     (TYPE(igraph_vector)* v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    v->end = v->stor_begin;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_push_back
+ * \brief Appends one element to a vector.
+ *
+ * </para><para>
+ * This function resizes the vector to be one element longer and
+ * sets the very last element in the vector to \p e.
+ * \param v The vector object.
+ * \param e The element to append to the vector.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM: not enough memory.
+ *
+ * Time complexity: operating system dependent. What is important is that
+ * a sequence of n
+ * subsequent calls to this function has time complexity
+ * O(n), even if there
+ * hadn't been any space reserved for the new elements by
+ * \ref igraph_vector_reserve(). This is implemented by a trick similar to the C++
+ * \type vector class: each time more memory is allocated for a
+ * vector, the size of the additionally allocated memory is the same
+ * as the vector's current length. (We assume here that the time
+ * complexity of memory allocation is at most linear.)
+ */
+
+int FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE e) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+
+    /* full, allocate more storage */
+    if (v->stor_end == v->end) {
+        long int new_size = FUNCTION(igraph_vector, size)(v) * 2;
+        if (new_size == 0) {
+            new_size = 1;
+        }
+        IGRAPH_CHECK(FUNCTION(igraph_vector, reserve)(v, new_size));
+    }
+
+    *(v->end) = e;
+    v->end += 1;
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_insert
+ * \brief Inserts a single element into a vector.
+ *
+ * Note that this function does not do range checking. Insertion will shift the
+ * elements from the position given to the end of the vector one position to the
+ * right, and the new element will be inserted in the empty space created at
+ * the given position. The size of the vector will increase by one.
+ *
+ * \param v The vector object.
+ * \param pos The position where the new element is to be inserted.
+ * \param value The new element to be inserted.
+ */
+int FUNCTION(igraph_vector, insert)(TYPE(igraph_vector) *v, long int pos,
+                                    BASE value) {
+    size_t size = (size_t) FUNCTION(igraph_vector, size)(v);
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, (long) size + 1));
+    if (pos < size) {
+        memmove(v->stor_begin + pos + 1, v->stor_begin + pos,
+                sizeof(BASE) * (size - (size_t) pos));
+    }
+    v->stor_begin[pos] = value;
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \section igraph_vector_accessing_elements Accessing elements
+ *
+ * <para>The simplest way to access an element of a vector is to use the
+ * \ref VECTOR macro. This macro can be used both for querying and setting
+ * \type igraph_vector_t elements. If you need a function, \ref
+ * igraph_vector_e() queries and \ref igraph_vector_set() sets an element of a
+ * vector. \ref igraph_vector_e_ptr() returns the address of an element.</para>
+ *
+ * <para>\ref igraph_vector_tail() returns the last element of a non-empty
+ * vector. There is no <function>igraph_vector_head()</function> function
+ * however, as it is easy to write <code>VECTOR(v)[0]</code>
+ * instead.</para>
+ */
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_e
+ * \brief Access an element of a vector.
+ * \param v The \type igraph_vector_t object.
+ * \param pos The position of the element, the index of the first
+ *    element is zero.
+ * \return The desired element.
+ * \sa \ref igraph_vector_e_ptr() and the \ref VECTOR macro.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_vector, e)         (const TYPE(igraph_vector)* v, long int pos) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    return * (v->stor_begin + pos);
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_e_ptr
+ * \brief Get the address of an element of a vector
+ * \param v The \type igraph_vector_t object.
+ * \param pos The position of the element, the position of the first
+ *   element is zero.
+ * \return Pointer to the desired element.
+ * \sa \ref igraph_vector_e() and the \ref VECTOR macro.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE* FUNCTION(igraph_vector, e_ptr)  (const TYPE(igraph_vector)* v, long int pos) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    return v->stor_begin + pos;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_set
+ * \brief Assignment to an element of a vector.
+ * \param v The \type igraph_vector_t element.
+ * \param pos Position of the element to set.
+ * \param value New value of the element.
+ * \sa \ref igraph_vector_e().
+ */
+
+void FUNCTION(igraph_vector, set)       (TYPE(igraph_vector)* v,
+        long int pos, BASE value) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    *(v->stor_begin + pos) = value;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_null
+ * \brief Sets each element in the vector to zero.
+ *
+ * </para><para>
+ * Note that \ref igraph_vector_init() sets the elements to zero as well, so
+ * it makes no sense to call this function on a just initialized
+ * vector. Thus if you want to construct a vector of zeros, then you should
+ * use \ref igraph_vector_init().
+ * \param v The vector object.
+ *
+ * Time complexity: O(n), the size of
+ * the vector.
+ */
+
+void FUNCTION(igraph_vector, null)      (TYPE(igraph_vector)* v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    if (FUNCTION(igraph_vector, size)(v) > 0) {
+        memset(v->stor_begin, 0,
+               sizeof(BASE) * (size_t) FUNCTION(igraph_vector, size)(v));
+    }
+}
+
+/**
+ * \function igraph_vector_fill
+ * \brief Fill a vector with a constant element
+ *
+ * Sets each element of the vector to the supplied constant.
+ * \param vector The vector to work on.
+ * \param e The element to fill with.
+ *
+ * Time complexity: O(n), the size of the vector.
+ */
+
+void FUNCTION(igraph_vector, fill)      (TYPE(igraph_vector)* v, BASE e) {
+    BASE *ptr;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    for (ptr = v->stor_begin; ptr < v->end; ptr++) {
+        *ptr = e;
+    }
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_tail
+ * \brief Returns the last element in a vector.
+ *
+ * </para><para>
+ * It is an error to call this function on an empty vector, the result
+ * is undefined.
+ * \param v The vector object.
+ * \return The last element.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    return *((v->end) - 1);
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_pop_back
+ * \brief Removes and returns the last element of a vector.
+ *
+ * </para><para>
+ * It is an error to call this function with an empty vector.
+ * \param v The vector object.
+ * \return The removed last element.
+ *
+ * Time complexity: O(1).
+ */
+
+BASE FUNCTION(igraph_vector, pop_back)(TYPE(igraph_vector)* v) {
+    BASE tmp;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    assert(v->end != v->stor_begin);
+    tmp = FUNCTION(igraph_vector, e)(v, FUNCTION(igraph_vector, size)(v) - 1);
+    v->end -= 1;
+    return tmp;
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_sort_cmp
+ * \brief Internal comparison function of vector elements, used by
+ * \ref igraph_vector_sort().
+ */
+
+int FUNCTION(igraph_vector, sort_cmp)(const void *a, const void *b) {
+    const BASE *da = (const BASE *) a;
+    const BASE *db = (const BASE *) b;
+
+    return (*da > *db) - (*da < *db);
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_sort
+ * \brief Sorts the elements of the vector into ascending order.
+ *
+ * </para><para>
+ * This function uses the built-in sort function of the C library.
+ * \param v Pointer to an initialized vector object.
+ *
+ * Time complexity: should be
+ * O(nlogn) for
+ * n
+ * elements.
+ */
+
+void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector, size)(v),
+                 sizeof(BASE), FUNCTION(igraph_vector, sort_cmp));
+}
+
+/**
+ * Ascending comparison function passed to qsort from  igraph_vector_qsort_ind
+ */
+int FUNCTION(igraph_vector, i_qsort_ind_cmp_asc)(const void *p1, const void *p2) {
+    BASE **pa = (BASE **) p1;
+    BASE **pb = (BASE **) p2;
+    if ( **pa < **pb ) {
+        return -1;
+    }
+    if ( **pa > **pb) {
+        return 1;
+    }
+    return 0;
+}
+
+/**
+ * Descending comparison function passed to qsort from  igraph_vector_qsort_ind
+ */
+int FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)(const void *p1, const void *p2) {
+    BASE **pa = (BASE **) p1;
+    BASE **pb = (BASE **) p2;
+    if ( **pa < **pb ) {
+        return 1;
+    }
+    if ( **pa > **pb) {
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_vector_qsort_ind
+ * \brief Return a permutation of indices that sorts a vector
+ *
+ * Takes an unsorted array \c v as input and computes an array of
+ * indices inds such that v[ inds[i] ], with i increasing from 0, is
+ * an ordered array (either ascending or descending, depending on
+ * \v order). The order of indices for identical elements is not
+ * defined.
+ *
+ * \param v the array to be sorted
+ * \param inds the output array of indices. this must be initialized,
+ *         but will be resized
+ * \param descending whether the output array should be sorted in descending
+ *        order.
+ * \return Error code.
+ *
+ * This routine uses the C library qsort routine.
+ * Algorithm: 1) create an array of pointers to the elements of v. 2)
+ * Pass this array to qsort. 3) after sorting the difference between
+ * the pointer value and the first pointer value gives its original
+ * position in the array. Use this to set the values of inds.
+ *
+ * Some tests show that this routine is faster than
+ * igraph_vector_heapsort_ind by about 10 percent
+ * for small vectors to a factor of two for large vectors.
+ */
+
+long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v,
+        igraph_vector_t *inds, igraph_bool_t descending) {
+    long int i;
+    BASE **vind, *first;
+    size_t n = (size_t) FUNCTION(igraph_vector, size)(v);
+    IGRAPH_CHECK(igraph_vector_resize(inds, (long) n));
+    if (n == 0) {
+        return 0;
+    }
+    vind = igraph_Calloc(n, BASE*);
+    if (vind == 0) {
+        IGRAPH_ERROR("igraph_vector_qsort_ind failed", IGRAPH_ENOMEM);
+    }
+    for (i = 0; i < n; i++) {
+        vind[i] = &VECTOR(*v)[i];
+    }
+    first = vind[0];
+    if (descending) {
+        igraph_qsort(vind, n, sizeof(BASE**), FUNCTION(igraph_vector, i_qsort_ind_cmp_desc));
+    } else {
+        igraph_qsort(vind, n, sizeof(BASE**), FUNCTION(igraph_vector, i_qsort_ind_cmp_asc));
+    }
+    for (i = 0; i < n; i++) {
+        VECTOR(*inds)[i] = vind[i] - first;
+    }
+    igraph_Free(vind);
+    return 0;
+}
+
+#endif
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_resize
+ * \brief Resize the vector.
+ *
+ * </para><para>
+ * Note that this function does not free any memory, just sets the
+ * size of the vector to the given one. It can on the other hand
+ * allocate more memory if the new size is larger than the previous
+ * one. In this case the newly appeared elements in the vector are
+ * \em not set to zero, they are uninitialized.
+ * \param v The vector object
+ * \param newsize The new size of the vector.
+ * \return Error code,
+ *         \c IGRAPH_ENOMEM if there is not enough
+ *         memory. Note that this function \em never returns an error
+ *         if the vector is made smaller.
+ * \sa \ref igraph_vector_reserve() for allocating memory for future
+ * extensions of a vector. \ref igraph_vector_resize_min() for
+ * deallocating the unnneded memory for a vector.
+ *
+ * Time complexity: O(1) if the new
+ * size is smaller, operating system dependent if it is larger. In the
+ * latter case it is usually around
+ * O(n),
+ * n is the new size of the vector.
+ */
+
+int FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, long int newsize) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    IGRAPH_CHECK(FUNCTION(igraph_vector, reserve)(v, newsize));
+    v->end = v->stor_begin + newsize;
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_resize_min
+ * \brief Deallocate the unused memory of a vector.
+ *
+ * </para><para>
+ * Note that this function involves additional memory allocation and
+ * may result an out-of-memory error.
+ * \param v Pointer to an initialized vector.
+ * \return Error code.
+ *
+ * \sa \ref igraph_vector_resize(), \ref igraph_vector_reserve().
+ *
+ * Time complexity: operating system dependent.
+ */
+
+int FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v) {
+    size_t size;
+    BASE *tmp;
+    if (v->stor_end == v->end) {
+        return 0;
+    }
+
+    size = (size_t) (v->end - v->stor_begin);
+    tmp = igraph_Realloc(v->stor_begin, size, BASE);
+    if (tmp == 0) {
+        IGRAPH_ERROR("cannot resize vector", IGRAPH_ENOMEM);
+    } else {
+        v->stor_begin = tmp;
+        v->stor_end = v->end = v->stor_begin + size;
+    }
+
+    return 0;
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_max
+ * \brief Gives the maximum element of the vector.
+ *
+ * </para><para>
+ * If the size of the vector is zero, an arbitrary number is
+ * returned.
+ * \param v The vector object.
+ * \return The maximum element.
+ *
+ * Time complexity: O(n),
+ * n is the size of the vector.
+ */
+
+BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v) {
+    BASE max;
+    BASE *ptr;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    max = *(v->stor_begin);
+    ptr = v->stor_begin + 1;
+    while (ptr < v->end) {
+        if ((*ptr) > max) {
+            max = *ptr;
+        }
+        ptr++;
+    }
+    return max;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_which_max
+ * \brief Gives the position of the maximum element of the vector.
+ *
+ * </para><para>
+ * If the size of the vector is zero, -1 is
+ * returned.
+ * \param v The vector object.
+ * \return The position of the first maximum element.
+ *
+ * Time complexity: O(n),
+ * n is the size of the vector.
+ */
+
+long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v) {
+    long int which = -1;
+    if (!FUNCTION(igraph_vector, empty)(v)) {
+        BASE max;
+        BASE *ptr;
+        long int pos;
+        assert(v != NULL);
+        assert(v->stor_begin != NULL);
+        max = *(v->stor_begin); which = 0;
+        ptr = v->stor_begin + 1; pos = 1;
+        while (ptr < v->end) {
+            if ((*ptr) > max) {
+                max = *ptr;
+                which = pos;
+            }
+            ptr++; pos++;
+        }
+    }
+    return which;
+}
+
+/**
+ * \function igraph_vector_min
+ * \brief Smallest element of a vector.
+ *
+ * The vector must be non-empty.
+ * \param v The input vector.
+ * \return The smallest element of \p v.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v) {
+    BASE min;
+    BASE *ptr;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    min = *(v->stor_begin);
+    ptr = v->stor_begin + 1;
+    while (ptr < v->end) {
+        if ((*ptr) < min) {
+            min = *ptr;
+        }
+        ptr++;
+    }
+    return min;
+}
+
+/**
+ * \function igraph_vector_which_min
+ * \brief Index of the smallest element.
+ *
+ * The vector must be non-empty.
+ * If the smallest element is not unique, then the index of the first
+ * is returned.
+ * \param v The input vector.
+ * \return Index of the smallest element.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) {
+    long int which = -1;
+    if (!FUNCTION(igraph_vector, empty)(v)) {
+        BASE min;
+        BASE *ptr;
+        long int pos;
+        assert(v != NULL);
+        assert(v->stor_begin != NULL);
+        min = *(v->stor_begin); which = 0;
+        ptr = v->stor_begin + 1; pos = 1;
+        while (ptr < v->end) {
+            if ((*ptr) < min) {
+                min = *ptr;
+                which = pos;
+            }
+            ptr++; pos++;
+        }
+    }
+    return which;
+}
+
+#endif
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_init_copy
+ * \brief Initializes a vector from an ordinary C array (constructor).
+ *
+ * \param v Pointer to an uninitialized vector object.
+ * \param data A regular C array.
+ * \param length The length of the C array.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM if there is not enough memory.
+ *
+ * Time complexity: operating system specific, usually
+ * O(\p length).
+ */
+
+int FUNCTION(igraph_vector, init_copy)(TYPE(igraph_vector) *v,
+                                       const BASE *data, long int length) {
+    v->stor_begin = igraph_Calloc(length, BASE);
+    if (v->stor_begin == 0) {
+        IGRAPH_ERROR("cannot init vector from array", IGRAPH_ENOMEM);
+    }
+    v->stor_end = v->stor_begin + length;
+    v->end = v->stor_end;
+    memcpy(v->stor_begin, data, (size_t) length * sizeof(BASE));
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_copy_to
+ * \brief Copies the contents of a vector to a C array.
+ *
+ * </para><para>
+ * The C array should have sufficient length.
+ * \param v The vector object.
+ * \param to The C array.
+ *
+ * Time complexity: O(n),
+ * n is the size of the vector.
+ */
+
+void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE *to) {
+
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    if (v->end != v->stor_begin) {
+        memcpy(to, v->stor_begin, sizeof(BASE) * (size_t) (v->end - v->stor_begin));
+    }
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_copy
+ * \brief Initializes a vector from another vector object (constructor).
+ *
+ * </para><para>
+ * The contents of the existing vector object will be copied to
+ * the new one.
+ * \param to Pointer to a not yet initialized vector object.
+ * \param from The original vector object to copy.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM if there is not enough memory.
+ *
+ * Time complexity: operating system dependent, usually
+ * O(n),
+ * n is the size of the vector.
+ */
+
+int FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to,
+                                  const TYPE(igraph_vector) *from) {
+    assert(from != NULL);
+    assert(from->stor_begin != NULL);
+    to->stor_begin = igraph_Calloc(FUNCTION(igraph_vector, size)(from), BASE);
+    if (to->stor_begin == 0) {
+        IGRAPH_ERROR("cannot copy vector", IGRAPH_ENOMEM);
+    }
+    to->stor_end = to->stor_begin + FUNCTION(igraph_vector, size)(from);
+    to->end = to->stor_end;
+    memcpy(to->stor_begin, from->stor_begin,
+           (size_t) FUNCTION(igraph_vector, size)(from) * sizeof(BASE));
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_sum
+ * \brief Calculates the sum of the elements in the vector.
+ *
+ * </para><para>
+ * For the empty vector 0.0 is returned.
+ * \param v The vector object.
+ * \return The sum of the elements.
+ *
+ * Time complexity: O(n), the size of
+ * the vector.
+ */
+
+BASE FUNCTION(igraph_vector, sum)(const TYPE(igraph_vector) *v) {
+    BASE res = ZERO;
+    BASE *p;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    for (p = v->stor_begin; p < v->end; p++) {
+#ifdef SUM
+        SUM(res, res, *p);
+#else
+        res += *p;
+#endif
+    }
+    return res;
+}
+
+igraph_real_t FUNCTION(igraph_vector, sumsq)(const TYPE(igraph_vector) *v) {
+    igraph_real_t res = 0.0;
+    BASE *p;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    for (p = v->stor_begin; p < v->end; p++) {
+#ifdef SQ
+        res += SQ(*p);
+#else
+        res += (*p) * (*p);
+#endif
+    }
+    return res;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_prod
+ * \brief Calculates the product of the elements in the vector.
+ *
+ * </para><para>
+ * For the empty vector one (1) is returned.
+ * \param v The vector object.
+ * \return The product of the elements.
+ *
+ * Time complexity: O(n), the size of
+ * the vector.
+ */
+
+BASE FUNCTION(igraph_vector, prod)(const TYPE(igraph_vector) *v) {
+    BASE res = ONE;
+    BASE *p;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    for (p = v->stor_begin; p < v->end; p++) {
+#ifdef PROD
+        PROD(res, res, *p);
+#else
+        res *= *p;
+#endif
+    }
+    return res;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_cumsum
+ * \brief Calculates the cumulative sum of the elements in the vector.
+ *
+ * </para><para>
+ * \param to An initialized vector object that will store the cumulative
+ *           sums. Element i of this vector will store the sum of the elements
+ *           of the 'from' vector, up to and including element i.
+ * \param from The input vector.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the size of the vector.
+ */
+
+int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to,
+                                    const TYPE(igraph_vector) *from) {
+    BASE res = ZERO;
+    BASE *p, *p2;
+
+    assert(from != NULL);
+    assert(from->stor_begin != NULL);
+    assert(to != NULL);
+    assert(to->stor_begin != NULL);
+
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, FUNCTION(igraph_vector, size)(from)));
+
+    for (p = from->stor_begin, p2 = to->stor_begin; p < from->end; p++, p2++) {
+#ifdef SUM
+        SUM(res, res, *p);
+#else
+        res += *p;
+#endif
+        *p2 = res;
+    }
+
+    return 0;
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_init_seq
+ * \brief Initializes a vector with a sequence.
+ *
+ * </para><para>
+ * The vector will contain the numbers \p from,
+ * \p from+1, ..., \p to.
+ * \param v Pointer to an uninitialized vector object.
+ * \param from The lower limit in the sequence (inclusive).
+ * \param to The upper limit in the sequence (inclusive).
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM: out of memory.
+ *
+ * Time complexity: O(n), the number
+ * of elements in the vector.
+ */
+
+int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector) *v,
+                                      BASE from, BASE to) {
+    BASE *p;
+    IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (long int) (to - from + 1)));
+
+    for (p = v->stor_begin; p < v->end; p++) {
+        *p = from++;
+    }
+
+    return 0;
+}
+
+#endif
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_remove_section
+ * \brief Deletes a section from a vector.
+ *
+ * </para><para>
+ * Note that this function does not do range checking. The result is
+ * undefined if you supply invalid limits.
+ * \param v The vector object.
+ * \param from The position of the first element to remove.
+ * \param to The position of the first element \em not to remove.
+ *
+ * Time complexity: O(n-from),
+ * n is the number of elements in the
+ * vector.
+ */
+
+void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v,
+        long int from, long int to) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    /* Not removing from the end? */
+    if (to < FUNCTION(igraph_vector, size)(v)) {
+        memmove(v->stor_begin + from, v->stor_begin + to,
+                sizeof(BASE) * (size_t) (v->end - v->stor_begin - to));
+    }
+    v->end -= (to - from);
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_remove
+ * \brief Removes a single element from a vector.
+ *
+ * Note that this function does not do range checking.
+ * \param v The vector object.
+ * \param elem The position of the element to remove.
+ *
+ * Time complexity: O(n-elem),
+ * n is the number of elements in the
+ * vector.
+ */
+
+void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, long int elem) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    FUNCTION(igraph_vector, remove_section)(v, elem, elem + 1);
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_move_interval
+ * \brief Copies a section of a vector.
+ *
+ * </para><para>
+ * The result of this function is undefined if the source and target
+ * intervals overlap.
+ * \param v The vector object.
+ * \param begin The position of the first element to move.
+ * \param end The position of the first element \em not to move.
+ * \param to The target position.
+ * \return Error code, the current implementation always returns with
+ *    success.
+ *
+ * Time complexity: O(end-begin).
+ */
+
+int FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v,
+        long int begin, long int end,
+        long int to) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    memcpy(v->stor_begin + to, v->stor_begin + begin,
+           sizeof(BASE) * (size_t) (end - begin));
+
+    return 0;
+}
+
+int FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v,
+        long int begin, long int end,
+        long int to) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    memmove(v->stor_begin + to, v->stor_begin + begin,
+            sizeof(BASE) * (size_t) (end - begin));
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_permdelete
+ * \brief Remove elements of a vector (for internal use).
+ */
+
+void FUNCTION(igraph_vector, permdelete)(TYPE(igraph_vector) *v,
+        const igraph_vector_t *index, long int nremove) {
+    long int i, n;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    n = FUNCTION(igraph_vector, size)(v);
+    for (i = 0; i < n; i++) {
+        if (VECTOR(*index)[i] != 0) {
+            VECTOR(*v)[ (long int)VECTOR(*index)[i] - 1 ] = VECTOR(*v)[i];
+        }
+    }
+    v->end -= nremove;
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_isininterval
+ * \brief Checks if all elements of a vector are in the given
+ * interval.
+ *
+ * \param v The vector object.
+ * \param low The lower limit of the interval (inclusive).
+ * \param high The higher limit of the interval (inclusive).
+ * \return True (positive integer) if all vector elements are in the
+ *   interval, false (zero) otherwise.
+ *
+ * Time complexity: O(n), the number
+ * of elements in the vector.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, isininterval)(const TYPE(igraph_vector) *v,
+        BASE low,
+        BASE high) {
+    BASE *ptr;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    for (ptr = v->stor_begin; ptr < v->end; ptr++) {
+        if (*ptr < low || *ptr > high) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_any_smaller
+ * \brief Checks if any element of a vector is smaller than a limit.
+ *
+ * \param v The \type igraph_vector_t object.
+ * \param limit The limit.
+ * \return True (positive integer) if the vector contains at least one
+ *   smaller element than \p limit, false (zero)
+ *   otherwise.
+ *
+ * Time complexity: O(n), the number
+ * of elements in the vector.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, any_smaller)(const TYPE(igraph_vector) *v,
+        BASE limit) {
+    BASE *ptr;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    for (ptr = v->stor_begin; ptr < v->end; ptr++) {
+        if (*ptr < limit) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+#endif
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_all_e
+ * \brief Are all elements equal?
+ *
+ * \param lhs The first vector.
+ * \param rhs The second vector.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    equal to the corresponding elements in \p rhs. Returns \c 0
+ *    (=false) if the lengths of the vectors don't match.
+ *
+ * Time complexity: O(n), the length of the vectors.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs) {
+    long int i, s;
+    assert(lhs != 0);
+    assert(rhs != 0);
+    assert(lhs->stor_begin != 0);
+    assert(rhs->stor_begin != 0);
+
+    s = FUNCTION(igraph_vector, size)(lhs);
+    if (s != FUNCTION(igraph_vector, size)(rhs)) {
+        return 0;
+    } else {
+        for (i = 0; i < s; i++) {
+            BASE l = VECTOR(*lhs)[i];
+            BASE r = VECTOR(*rhs)[i];
+#ifdef EQ
+            if (!EQ(l, r)) {
+#else
+            if (l != r) {
+#endif
+                return 0;
+            }
+        }
+        return 1;
+    }
+}
+
+igraph_bool_t
+FUNCTION(igraph_vector, is_equal)(const TYPE(igraph_vector) *lhs,
+                                  const TYPE(igraph_vector) *rhs) {
+    return FUNCTION(igraph_vector, all_e)(lhs, rhs);
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_all_l
+ * \brief Are all elements less?
+ *
+ * \param lhs The first vector.
+ * \param rhs The second vector.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    less than the corresponding elements in \p rhs. Returns \c 0
+ *    (=false) if the lengths of the vectors don't match.
+ *
+ * Time complexity: O(n), the length of the vectors.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs) {
+    long int i, s;
+    assert(lhs != 0);
+    assert(rhs != 0);
+    assert(lhs->stor_begin != 0);
+    assert(rhs->stor_begin != 0);
+
+    s = FUNCTION(igraph_vector, size)(lhs);
+    if (s != FUNCTION(igraph_vector, size)(rhs)) {
+        return 0;
+    } else {
+        for (i = 0; i < s; i++) {
+            BASE l = VECTOR(*lhs)[i];
+            BASE r = VECTOR(*rhs)[i];
+            if (l >= r) {
+                return 0;
+            }
+        }
+        return 1;
+    }
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_all_g
+ * \brief Are all elements greater?
+ *
+ * \param lhs The first vector.
+ * \param rhs The second vector.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    greater than the corresponding elements in \p rhs. Returns \c 0
+ *    (=false) if the lengths of the vectors don't match.
+ *
+ * Time complexity: O(n), the length of the vectors.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs,
+        const TYPE(igraph_vector) *rhs) {
+
+    long int i, s;
+    assert(lhs != 0);
+    assert(rhs != 0);
+    assert(lhs->stor_begin != 0);
+    assert(rhs->stor_begin != 0);
+
+    s = FUNCTION(igraph_vector, size)(lhs);
+    if (s != FUNCTION(igraph_vector, size)(rhs)) {
+        return 0;
+    } else {
+        for (i = 0; i < s; i++) {
+            BASE l = VECTOR(*lhs)[i];
+            BASE r = VECTOR(*rhs)[i];
+            if (l <= r) {
+                return 0;
+            }
+        }
+        return 1;
+    }
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_all_le
+ * \brief Are all elements less or equal?
+ *
+ * \param lhs The first vector.
+ * \param rhs The second vector.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    less than or equal to the corresponding elements in \p
+ *    rhs. Returns \c 0 (=false) if the lengths of the vectors don't
+ *    match.
+ *
+ * Time complexity: O(n), the length of the vectors.
+ */
+
+igraph_bool_t
+FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs,
+                                const TYPE(igraph_vector) *rhs) {
+    long int i, s;
+    assert(lhs != 0);
+    assert(rhs != 0);
+    assert(lhs->stor_begin != 0);
+    assert(rhs->stor_begin != 0);
+
+    s = FUNCTION(igraph_vector, size)(lhs);
+    if (s != FUNCTION(igraph_vector, size)(rhs)) {
+        return 0;
+    } else {
+        for (i = 0; i < s; i++) {
+            BASE l = VECTOR(*lhs)[i];
+            BASE r = VECTOR(*rhs)[i];
+            if (l > r) {
+                return 0;
+            }
+        }
+        return 1;
+    }
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_all_ge
+ * \brief Are all elements greater or equal?
+ *
+ * \param lhs The first vector.
+ * \param rhs The second vector.
+ * \return Positive integer (=true) if the elements in the \p lhs are all
+ *    greater than or equal to the corresponding elements in \p
+ *    rhs. Returns \c 0 (=false) if the lengths of the vectors don't
+ *    match.
+ *
+ * Time complexity: O(n), the length of the vectors.
+ */
+
+igraph_bool_t
+FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs,
+                                const TYPE(igraph_vector) *rhs) {
+    long int i, s;
+    assert(lhs != 0);
+    assert(rhs != 0);
+    assert(lhs->stor_begin != 0);
+    assert(rhs->stor_begin != 0);
+
+    s = FUNCTION(igraph_vector, size)(lhs);
+    if (s != FUNCTION(igraph_vector, size)(rhs)) {
+        return 0;
+    } else {
+        for (i = 0; i < s; i++) {
+            BASE l = VECTOR(*lhs)[i];
+            BASE r = VECTOR(*rhs)[i];
+            if (l < r) {
+                return 0;
+            }
+        }
+        return 1;
+    }
+}
+
+#endif
+
+igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v,
+        BASE what, long int *pos,
+        long int start, long int end);
+
+#ifndef NOTORDERED
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_binsearch
+ * \brief Finds an element by binary searching a sorted vector.
+ *
+ * </para><para>
+ * It is assumed that the vector is sorted. If the specified element
+ * (\p what) is not in the vector, then the
+ * position of where it should be inserted (to keep the vector sorted)
+ * is returned.
+ * \param v The \type igraph_vector_t object.
+ * \param what The element to search for.
+ * \param pos Pointer to a \type long int. This is set to the
+ *   position of an instance of \p what in the
+ *   vector if it is present. If \p v does not
+ *   contain \p what then
+ *   \p pos is set to the position to which it
+ *   should be inserted (to keep the the vector sorted of course).
+ * \return Positive integer (true) if \p what is
+ *   found in the vector, zero (false) otherwise.
+ *
+ * Time complexity: O(log(n)),
+ * n is the number of elements in
+ * \p v.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v,
+        BASE what, long int *pos) {
+    return FUNCTION(igraph_i_vector, binsearch_slice)(v, what, pos,
+            0, FUNCTION(igraph_vector, size)(v));
+}
+
+igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v,
+        BASE what, long int *pos,
+        long int start, long int end) {
+    long int left  = start;
+    long int right = end - 1;
+
+    while (left <= right) {
+        /* (right + left) / 2 could theoretically overflow for long vectors */
+        long int middle = left + ((right - left) >> 1);
+        if (VECTOR(*v)[middle] > what) {
+            right = middle - 1;
+        } else if (VECTOR(*v)[middle] < what) {
+            left = middle + 1;
+        } else {
+            if (pos != 0) {
+                *pos = middle;
+            }
+            return 1;
+        }
+    }
+
+    /* if we are here, the element was not found */
+    if (pos != 0) {
+        *pos = left;
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_binsearch2
+ * \brief Binary search, without returning the index.
+ *
+ * </para><para>
+ * It is assumed that the vector is sorted.
+ * \param v The \type igraph_vector_t object.
+ * \param what The element to search for.
+ * \return Positive integer (true) if \p what is
+ *   found in the vector, zero (false) otherwise.
+ *
+ * Time complexity: O(log(n)),
+ * n is the number of elements in
+ * \p v.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v,
+        BASE what) {
+    long int left = 0;
+    long int right = FUNCTION(igraph_vector, size)(v) - 1;
+
+    while (left <= right) {
+        /* (right + left) / 2 could theoretically overflow for long vectors */
+        long int middle = left + ((right - left) >> 1);
+        if (what < VECTOR(*v)[middle]) {
+            right = middle - 1;
+        } else if (what > VECTOR(*v)[middle]) {
+            left = middle + 1;
+        } else {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+#endif
+
+/**
+ * \function igraph_vector_scale
+ * \brief Multiply all elements of a vector by a constant
+ *
+ * \param v The vector.
+ * \param by The constant.
+ * \return Error code. The current implementation always returns with success.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(n), the number of elements in a vector.
+ */
+
+void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by) {
+    long int i;
+    for (i = 0; i < FUNCTION(igraph_vector, size)(v); i++) {
+#ifdef PROD
+        PROD(VECTOR(*v)[i], VECTOR(*v)[i], by);
+#else
+        VECTOR(*v)[i] *= by;
+#endif
+    }
+}
+
+/**
+ * \function igraph_vector_add_constant
+ * \brief Add a constant to the vector.
+ *
+ * \p plus is added to every element of \p v. Note that overflow
+ * might happen.
+ * \param v The input vector.
+ * \param plus The constant to add.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus) {
+    long int i, n = FUNCTION(igraph_vector, size)(v);
+    for (i = 0; i < n; i++) {
+#ifdef SUM
+        SUM(VECTOR(*v)[i], VECTOR(*v)[i], plus);
+#else
+        VECTOR(*v)[i] += plus;
+#endif
+    }
+}
+
+/**
+ * \function igraph_vector_contains
+ * \brief Linear search in a vector.
+ *
+ * Check whether the supplied element is included in the vector, by
+ * linear search.
+ * \param v The input vector.
+ * \param e The element to look for.
+ * \return \c TRUE if the element is found and \c FALSE otherwise.
+ *
+ * Time complexity: O(n), the length of the vector.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v,
+        BASE e) {
+    BASE *p = v->stor_begin;
+    while (p < v->end) {
+#ifdef EQ
+        if (EQ(*p, e)) {
+#else
+        if (*p == e) {
+#endif
+            return 1;
+        }
+        p++;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_vector_search
+ * \brief Search from a given position
+ *
+ * The supplied element \p what is searched in vector \p v, starting
+ * from element index \p from. If found then the index of the first
+ * instance (after \p from) is stored in \p pos.
+ * \param v The input vector.
+ * \param from The index to start searching from. No range checking is
+ *     performed.
+ * \param what The element to find.
+ * \param pos If not \c NULL then the index of the found element is
+ *    stored here.
+ * \return Boolean, \c TRUE if the element was found, \c FALSE
+ *   otherwise.
+ *
+ * Time complexity: O(m), the number of elements to search, the length
+ * of the vector minus the \p from argument.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v,
+        long int from, BASE what,
+        long int *pos) {
+    long int i, n = FUNCTION(igraph_vector, size)(v);
+    for (i = from; i < n; i++) {
+#ifdef EQ
+        if (EQ(VECTOR(*v)[i], what)) {
+            break;
+        }
+#else
+        if (VECTOR(*v)[i] == what) {
+            break;
+        }
+#endif
+    }
+
+    if (i < n) {
+        if (pos != 0) {
+            *pos = i;
+        }
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \function igraph_vector_filter_smaller
+ * \ingroup internal
+ */
+
+int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v,
+        BASE elem) {
+    long int i = 0, n = FUNCTION(igraph_vector, size)(v);
+    long int s;
+    while (i < n && VECTOR(*v)[i] < elem) {
+        i++;
+    }
+    s = i;
+
+    while (s < n && VECTOR(*v)[s] == elem) {
+        s++;
+    }
+
+    FUNCTION(igraph_vector, remove_section)(v, 0, i + (s - i) / 2);
+    return 0;
+}
+
+#endif
+
+/**
+ * \function igraph_vector_append
+ * \brief Append a vector to another one.
+ *
+ * The target vector will be resized (except \p from is empty).
+ * \param to The vector to append to.
+ * \param from The vector to append, it is kept unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of elements in the new vector.
+ */
+
+int FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to,
+                                    const TYPE(igraph_vector) *from) {
+    long tosize, fromsize;
+
+    tosize = FUNCTION(igraph_vector, size)(to);
+    fromsize = FUNCTION(igraph_vector, size)(from);
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, tosize + fromsize));
+    memcpy(to->stor_begin + tosize, from->stor_begin,
+           sizeof(BASE) * (size_t) fromsize);
+    to->end = to->stor_begin + tosize + fromsize;
+
+    return 0;
+}
+
+/**
+ * \function igraph_vector_get_interval
+ */
+
+int FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v,
+        TYPE(igraph_vector) *res,
+        long int from, long int to) {
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, to - from));
+    memcpy(res->stor_begin, v->stor_begin + from,
+           (size_t) (to - from) * sizeof(BASE));
+    return 0;
+}
+
+#ifndef NOTORDERED
+
+/**
+ * \function igraph_vector_maxdifference
+ * \brief The maximum absolute difference of \p m1 and \p m2
+ *
+ * The element with the largest absolute value in \p m1 - \p m2 is
+ * returned. Both vectors must be non-empty, but they not need to have
+ * the same length, the extra elements in the longer vector are ignored.
+ * \param m1 The first vector.
+ * \param m2 The second vector.
+ * \return The maximum absolute difference of \p m1 and \p m2.
+ *
+ * Time complexity: O(n), the number of elements in the shorter
+ * vector.
+ */
+
+igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) *m1,
+        const TYPE(igraph_vector) *m2) {
+    long int n1 = FUNCTION(igraph_vector, size)(m1);
+    long int n2 = FUNCTION(igraph_vector, size)(m2);
+    long int n = n1 < n2 ? n1 : n2;
+    long int i;
+    igraph_real_t diff = 0.0;
+
+    for (i = 0; i < n; i++) {
+        igraph_real_t d = fabs((igraph_real_t)(VECTOR(*m1)[i]) -
+                               (igraph_real_t)(VECTOR(*m2)[i]));
+        if (d > diff) {
+            diff = d;
+        }
+    }
+
+    return diff;
+}
+
+#endif
+
+/**
+ * \function igraph_vector_update
+ * \brief Update a vector from another one.
+ *
+ * After this operation the contents of \p to will be exactly the same
+ * \p from. \p to will be resized if it was originally shorter or
+ * longer than \p from.
+ * \param to The vector to update.
+ * \param from The vector to update from.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of elements in \p from.
+ */
+
+int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to,
+                                    const TYPE(igraph_vector) *from) {
+    size_t n = (size_t) FUNCTION(igraph_vector, size)(from);
+    FUNCTION(igraph_vector, resize)(to, (long) n);
+    memcpy(to->stor_begin, from->stor_begin, sizeof(BASE)*n);
+    return 0;
+}
+
+/**
+ * \function igraph_vector_swap
+ * \brief Swap elements of two vectors.
+ *
+ * The two vectors must have the same length, otherwise an error
+ * happens.
+ * \param v1 The first vector.
+ * \param v2 The second vector.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the length of the vectors.
+ */
+
+int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2) {
+
+    long int i, n1 = FUNCTION(igraph_vector, size)(v1);
+    long int n2 = FUNCTION(igraph_vector, size)(v2);
+    if (n1 != n2) {
+        IGRAPH_ERROR("Vectors must have the same number of elements for swapping",
+                     IGRAPH_EINVAL);
+    }
+
+    for (i = 0; i < n1; i++) {
+        BASE tmp;
+        tmp = VECTOR(*v1)[i];
+        VECTOR(*v1)[i] = VECTOR(*v2)[i];
+        VECTOR(*v2)[i] = tmp;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_vector_swap_elements
+ * \brief Swap two elements in a vector.
+ *
+ * Note that currently no range checking is performed.
+ * \param v The input vector.
+ * \param i Index of the first element.
+ * \param j index of the second element. (Might be the same as the
+ * first.)
+ * \return Error code, currently always \c IGRAPH_SUCCESS.
+ *
+ * Time complexity: O(1).
+ */
+
+int FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v,
+        long int i, long int j) {
+    BASE tmp = VECTOR(*v)[i];
+    VECTOR(*v)[i] = VECTOR(*v)[j];
+    VECTOR(*v)[j] = tmp;
+
+    return 0;
+}
+
+/**
+ * \function igraph_vector_reverse
+ * \brief Reverse the elements of a vector.
+ *
+ * The first element will be last, the last element will be
+ * first, etc.
+ * \param v The input vector.
+ * \return Error code, currently always \c IGRAPH_SUCCESS.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) {
+
+    long int n = FUNCTION(igraph_vector, size)(v), n2 = n / 2;
+    long int i, j;
+    for (i = 0, j = n - 1; i < n2; i++, j--) {
+        BASE tmp;
+        tmp = VECTOR(*v)[i];
+        VECTOR(*v)[i] = VECTOR(*v)[j];
+        VECTOR(*v)[j] = tmp;
+    }
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_shuffle
+ * \brief Shuffles a vector in-place using the Fisher-Yates method
+ *
+ * </para><para>
+ * The Fisher-Yates shuffle ensures that every implementation is
+ * equally probable when using a proper randomness source. Of course
+ * this does not apply to pseudo-random generators as the cycle of
+ * these generators is less than the number of possible permutations
+ * of the vector if the vector is long enough.
+ * \param v The vector object.
+ * \return Error code, currently always \c IGRAPH_SUCCESS.
+ *
+ * Time complexity: O(n),
+ * n is the number of elements in the
+ * vector.
+ *
+ * </para><para>
+ * References:
+ * \clist
+ * \cli (Fisher &amp; Yates 1963)
+ *   R. A. Fisher and F. Yates. \emb Statistical Tables for Biological,
+ *   Agricultural and Medical Research. \eme Oliver and Boyd, 6th edition,
+ *   1963, page 37.
+ * \cli (Knuth 1998)
+ *   D. E. Knuth. \emb Seminumerical Algorithms, \eme volume 2 of \emb The Art
+ *   of Computer Programming. \eme Addison-Wesley, 3rd edition, 1998, page 145.
+ * \endclist
+ *
+ * \example examples/simple/igraph_fisher_yates_shuffle.c
+ */
+
+int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v) {
+    long int n = FUNCTION(igraph_vector, size)(v);
+    long int k;
+    BASE dummy;
+
+    RNG_BEGIN();
+    while (n > 1) {
+        k = RNG_INTEGER(0, n - 1);
+        n--;
+        dummy = VECTOR(*v)[n];
+        VECTOR(*v)[n] = VECTOR(*v)[k];
+        VECTOR(*v)[k] = dummy;
+    }
+    RNG_END();
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_vector_add
+ * \brief Add two vectors.
+ *
+ * Add the elements of \p v2 to \p v1, the result is stored in \p
+ * v1. The two vectors must have the same length.
+ * \param v1 The first vector, the result will be stored here.
+ * \param v2 The second vector, its contents will be unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1,
+                                 const TYPE(igraph_vector) *v2) {
+
+    long int n1 = FUNCTION(igraph_vector, size)(v1);
+    long int n2 = FUNCTION(igraph_vector, size)(v2);
+    long int i;
+    if (n1 != n2) {
+        IGRAPH_ERROR("Vectors must have the same number of elements for swapping",
+                     IGRAPH_EINVAL);
+    }
+
+    for (i = 0; i < n1; i++) {
+#ifdef SUM
+        SUM(VECTOR(*v1)[i], VECTOR(*v1)[i], VECTOR(*v2)[i]);
+#else
+        VECTOR(*v1)[i] += VECTOR(*v2)[i];
+#endif
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_vector_sub
+ * \brief Subtract a vector from another one.
+ *
+ * Subtract the elements of \p v2 from \p v1, the result is stored in
+ * \p v1. The two vectors must have the same length.
+ * \param v1 The first vector, to subtract from. The result is stored
+ *    here.
+ * \param v2 The vector to subtract, it will be unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the length of the vectors.
+ */
+
+int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1,
+                                 const TYPE(igraph_vector) *v2) {
+
+    long int n1 = FUNCTION(igraph_vector, size)(v1);
+    long int n2 = FUNCTION(igraph_vector, size)(v2);
+    long int i;
+    if (n1 != n2) {
+        IGRAPH_ERROR("Vectors must have the same number of elements for swapping",
+                     IGRAPH_EINVAL);
+    }
+
+    for (i = 0; i < n1; i++) {
+#ifdef DIFF
+        DIFF(VECTOR(*v1)[i], VECTOR(*v1)[i], VECTOR(*v2)[i]);
+#else
+        VECTOR(*v1)[i] -= VECTOR(*v2)[i];
+#endif
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_vector_mul
+ * \brief Multiply two vectors.
+ *
+ * \p v1 will be multiplied by \p v2, elementwise. The two vectors
+ * must have the same length.
+ * \param v1 The first vector, the result will be stored here.
+ * \param v2 The second vector, it is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1,
+                                 const TYPE(igraph_vector) *v2) {
+
+    long int n1 = FUNCTION(igraph_vector, size)(v1);
+    long int n2 = FUNCTION(igraph_vector, size)(v2);
+    long int i;
+    if (n1 != n2) {
+        IGRAPH_ERROR("Vectors must have the same number of elements for swapping",
+                     IGRAPH_EINVAL);
+    }
+
+    for (i = 0; i < n1; i++) {
+#ifdef PROD
+        PROD(VECTOR(*v1)[i], VECTOR(*v1)[i], VECTOR(*v2)[i]);
+#else
+        VECTOR(*v1)[i] *= VECTOR(*v2)[i];
+#endif
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_vector_div
+ * \brief Divide a vector by another one.
+ *
+ * \p v1 is divided by \p v2, elementwise. They must have the same length. If the
+ * base type of the vector can generate divide by zero errors then
+ * please make sure that \p v2 contains no zero if you want to avoid
+ * trouble.
+ * \param v1 The dividend. The result is also stored here.
+ * \param v2 The divisor, it is left unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the length of the vectors.
+ */
+
+int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1,
+                                 const TYPE(igraph_vector) *v2) {
+
+    long int n1 = FUNCTION(igraph_vector, size)(v1);
+    long int n2 = FUNCTION(igraph_vector, size)(v2);
+    long int i;
+    if (n1 != n2) {
+        IGRAPH_ERROR("Vectors must have the same number of elements for swapping",
+                     IGRAPH_EINVAL);
+    }
+
+    for (i = 0; i < n1; i++) {
+#ifdef DIV
+        DIV(VECTOR(*v1)[i], VECTOR(*v1)[i], VECTOR(*v2)[i]);
+#else
+        VECTOR(*v1)[i] /= VECTOR(*v2)[i];
+#endif
+    }
+
+    return 0;
+}
+
+#ifndef NOABS
+
+int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v) {
+#ifdef UNSIGNED
+    /* Nothing do to, unsigned type */
+#else
+    long int i, n = FUNCTION(igraph_vector, size)(v);
+    for (i = 0; i < n; i++) {
+        VECTOR(*v)[i] = VECTOR(*v)[i] >= 0 ? VECTOR(*v)[i] : -VECTOR(*v)[i];
+    }
+#endif
+
+    return 0;
+}
+
+#endif
+
+#ifndef NOTORDERED
+
+/**
+ * \function igraph_vector_minmax
+ * \brief Minimum and maximum elements of a vector.
+ *
+ * Handy if you want to have both the smallest and largest element of
+ * a vector. The vector is only traversed once. The vector must by non-empty.
+ * \param v The input vector. It must contain at least one element.
+ * \param min Pointer to a base type variable, the minimum is stored
+ *     here.
+ * \param max Pointer to a base type variable, the maximum is stored
+ *     here.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+int FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v,
+                                    BASE *min, BASE *max) {
+    long int n = FUNCTION(igraph_vector, size)(v);
+    long int i;
+    *min = *max = VECTOR(*v)[0];
+    for (i = 1; i < n; i++) {
+        BASE tmp = VECTOR(*v)[i];
+        if (tmp > *max) {
+            *max = tmp;
+        } else if (tmp < *min) {
+            *min = tmp;
+        }
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_vector_which_minmax
+ * \brief Index of the minimum and maximum elements
+ *
+ * Handy if you need the indices of the smallest and largest
+ * elements. The vector is traversed only once. The vector must to
+ * non-empty.
+ * \param v The input vector. It must contain at least one element.
+ * \param which_min The index of the minimum element will be stored
+ *   here.
+ * \param which_max The index of the maximum element will be stored
+ *   here.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v,
+        long int *which_min, long int *which_max) {
+
+    long int n = FUNCTION(igraph_vector, size)(v);
+    long int i;
+    BASE min, max;
+    *which_min = *which_max = 0;
+    min = max = VECTOR(*v)[0];
+    for (i = 1; i < n; i++) {
+        BASE tmp = VECTOR(*v)[i];
+        if (tmp > max) {
+            max = tmp;
+            *which_max = i;
+        } else if (tmp < min) {
+            min = tmp;
+            *which_min = i;
+        }
+    }
+    return 0;
+}
+
+#endif
+
+/**
+ * \function igraph_vector_isnull
+ * \brief Are all elements zero?
+ *
+ * Checks whether all elements of a vector are zero.
+ * \param v The input vector
+ * \return Boolean, \c TRUE if the vector contains only zeros, \c
+ *    FALSE otherwise.
+ *
+ * Time complexity: O(n), the number of elements.
+ */
+
+igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v) {
+
+    long int n = FUNCTION(igraph_vector, size)(v);
+    long int i = 0;
+
+#ifdef EQ
+    while (i < n && EQ(VECTOR(*v)[i], ZERO)) {
+#else
+    while (i < n && VECTOR(*v)[i] == ZERO) {
+#endif
+        i++;
+    }
+
+    return i == n;
+}
+
+#ifndef NOTORDERED
+
+int FUNCTION(igraph_i_vector, intersect_sorted)(
+    const TYPE(igraph_vector) *v1, long int begin1, long int end1,
+    const TYPE(igraph_vector) *v2, long int begin2, long int end2,
+    TYPE(igraph_vector) *result);
+
+/**
+ * \function igraph_vector_intersect_sorted
+ * \brief Calculates the intersection of two sorted vectors
+ *
+ * The elements that are contained in both vectors are stored in the result
+ * vector. All three vectors must be initialized.
+ *
+ * </para><para>
+ * Instead of the naive intersection which takes O(n), this function uses
+ * the set intersection method of Ricardo Baeza-Yates, which is more efficient
+ * when one of the vectors is significantly smaller than the other, and
+ * gives similar performance on average when the two vectors are equal.
+ *
+ * </para><para>
+ * The algorithm keeps the multiplicities of the elements: if an element appears
+ * k1 times in the first vector and k2 times in the second, the result
+ * will include that element min(k1, k2) times.
+ *
+ * </para><para>
+ * Reference: Baeza-Yates R: A fast set intersection algorithm for sorted
+ * sequences. In: Lecture Notes in Computer Science, vol. 3109/2004, pp.
+ * 400--408, 2004. Springer Berlin/Heidelberg. ISBN: 978-3-540-22341-2.
+ *
+ * \param v1 the first vector
+ * \param v2 the second vector
+ * \param result the result vector, which will also be sorted.
+ *
+ * Time complexity: O(m log(n)) where m is the size of the smaller vector
+ * and n is the size of the larger one.
+ */
+int FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1,
+        const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) {
+    long int size1, size2;
+
+    size1 = FUNCTION(igraph_vector, size)(v1);
+    size2 = FUNCTION(igraph_vector, size)(v2);
+
+    FUNCTION(igraph_vector, clear)(result);
+
+    if (size1 == 0 || size2 == 0) {
+        return 0;
+    }
+
+    IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)(
+                     v1, 0, size1, v2, 0, size2, result));
+    return 0;
+}
+
+int FUNCTION(igraph_i_vector, intersect_sorted)(
+    const TYPE(igraph_vector) *v1, long int begin1, long int end1,
+    const TYPE(igraph_vector) *v2, long int begin2, long int end2,
+    TYPE(igraph_vector) *result) {
+    long int size1, size2, probe1, probe2;
+
+    if (begin1 == end1 || begin2 == end2) {
+        return 0;
+    }
+
+    size1 = end1 - begin1;
+    size2 = end2 - begin2;
+
+    if (size1 < size2) {
+        probe1 = begin1 + (size1 >> 1);      /* pick the median element */
+        FUNCTION(igraph_i_vector, binsearch_slice)(v2, VECTOR(*v1)[probe1], &probe2, begin2, end2);
+        IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)(
+                         v1, begin1, probe1, v2, begin2, probe2, result
+                     ));
+        if (!(probe2 == end2 || VECTOR(*v1)[probe1] < VECTOR(*v2)[probe2])) {
+            IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, VECTOR(*v2)[probe2]));
+            probe2++;
+        }
+        IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)(
+                         v1, probe1 + 1, end1, v2, probe2, end2, result
+                     ));
+    } else {
+        probe2 = begin2 + (size2 >> 1);      /* pick the median element */
+        FUNCTION(igraph_i_vector, binsearch_slice)(v1, VECTOR(*v2)[probe2], &probe1, begin1, end1);
+        IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)(
+                         v1, begin1, probe1, v2, begin2, probe2, result
+                     ));
+        if (!(probe1 == end1 || VECTOR(*v2)[probe2] < VECTOR(*v1)[probe1])) {
+            IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, VECTOR(*v2)[probe2]));
+            probe1++;
+        }
+        IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)(
+                         v1, probe1, end1, v2, probe2 + 1, end2, result
+                     ));
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_vector_difference_sorted
+ * \brief Calculates the difference between two sorted vectors (considered as sets)
+ *
+ * The elements that are contained in only the first vector but not the second are
+ * stored in the result vector. All three vectors must be initialized.
+ *
+ * \param v1 the first vector
+ * \param v2 the second vector
+ * \param result the result vector
+ */
+int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1,
+        const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) {
+    long int i, j, i0, j0;
+    i0 = FUNCTION(igraph_vector, size)(v1);
+    j0 = FUNCTION(igraph_vector, size)(v2);
+    i = j = 0;
+
+    if (i0 == 0) {
+        /* v1 is empty, this is easy */
+        FUNCTION(igraph_vector, clear)(result);
+        return IGRAPH_SUCCESS;
+    }
+
+    if (j0 == 0) {
+        /* v2 is empty, this is easy */
+        IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(result, i0));
+        memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i0);
+        return IGRAPH_SUCCESS;
+    }
+
+    FUNCTION(igraph_vector, clear)(result);
+
+    /* Copy the part of v1 that is less than the first element of v2 */
+    while (i < i0 && VECTOR(*v1)[i] < VECTOR(*v2)[j]) {
+        i++;
+    }
+    if (i > 0) {
+        IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(result, i));
+        memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i);
+    }
+
+    while (i < i0 && j < j0) {
+        BASE element = VECTOR(*v1)[i];
+        if (element == VECTOR(*v2)[j]) {
+            i++; j++;
+            while (i < i0 && VECTOR(*v1)[i] == element) {
+                i++;
+            }
+            while (j < j0 && VECTOR(*v2)[j] == element) {
+                j++;
+            }
+        } else if (element < VECTOR(*v2)[j]) {
+            IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, element));
+            i++;
+        } else {
+            j++;
+        }
+    }
+    if (i < i0) {
+        long int oldsize = FUNCTION(igraph_vector, size)(result);
+        IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(result, oldsize + i0 - i));
+        memcpy(result->stor_begin + oldsize, v1->stor_begin + i,
+               sizeof(BASE) * (size_t) (i0 - i));
+    }
+
+    return 0;
+}
+
+#endif
+
+#if defined(OUT_FORMAT)
+
+#ifndef USING_R
+int FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v) {
+    long int i, n = FUNCTION(igraph_vector, size)(v);
+    if (n != 0) {
+#ifdef PRINTFUNC
+        PRINTFUNC(VECTOR(*v)[0]);
+#else
+        printf(OUT_FORMAT, VECTOR(*v)[0]);
+#endif
+    }
+    for (i = 1; i < n; i++) {
+#ifdef PRINTFUNC
+        putchar(' '); PRINTFUNC(VECTOR(*v)[i]);
+#else
+        printf(" " OUT_FORMAT, VECTOR(*v)[i]);
+#endif
+    }
+    printf("\n");
+    return 0;
+}
+
+int FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v,
+                                    const char *format) {
+    long int i, n = FUNCTION(igraph_vector, size)(v);
+    if (n != 0) {
+        printf(format, VECTOR(*v)[0]);
+    }
+    for (i = 1; i < n; i++) {
+        putchar(' '); printf(format, VECTOR(*v)[i]);
+    }
+    printf("\n");
+    return 0;
+}
+
+#endif
+
+int FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file) {
+    long int i, n = FUNCTION(igraph_vector, size)(v);
+    if (n != 0) {
+#ifdef FPRINTFUNC
+        FPRINTFUNC(file, VECTOR(*v)[0]);
+#else
+        fprintf(file, OUT_FORMAT, VECTOR(*v)[0]);
+#endif
+    }
+    for (i = 1; i < n; i++) {
+#ifdef FPRINTFUNC
+        fputc(' ', file); FPRINTFUNC(file, VECTOR(*v)[i]);
+#else
+        fprintf(file, " " OUT_FORMAT, VECTOR(*v)[i]);
+#endif
+    }
+    fprintf(file, "\n");
+    return 0;
+}
+
+#endif
+
+int FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v,
+                                   TYPE(igraph_vector) *newv,
+                                   const igraph_vector_t *idx) {
+
+    long int i, newlen = igraph_vector_size(idx);
+    IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(newv, newlen));
+
+    for (i = 0; i < newlen; i++) {
+        long int j = (long int) VECTOR(*idx)[i];
+        VECTOR(*newv)[i] = VECTOR(*v)[j];
+    }
+
+    return 0;
+}
+
+int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v,
+                                       const igraph_vector_int_t *idx) {
+    BASE *tmp;
+    int i, n = igraph_vector_int_size(idx);
+
+    tmp = igraph_Calloc(n, BASE);
+    if (!tmp) {
+        IGRAPH_ERROR("Cannot index vector", IGRAPH_ENOMEM);
+    }
+
+    for (i = 0; i < n; i++) {
+        tmp[i] = VECTOR(*v)[ VECTOR(*idx)[i] ];
+    }
+
+    igraph_Free(v->stor_begin);
+    v->stor_begin = tmp;
+    v->stor_end = v->end = tmp + n;
+
+    return 0;
+}
diff --git a/igraph/include/walktrap_communities.h b/igraph/include/walktrap_communities.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/walktrap_communities.h
@@ -0,0 +1,176 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Pascal Pons
+   The original copyright notice follows here. The FSF address was
+   fixed by Tamas Nepusz */
+
+// File: communities.h
+//-----------------------------------------------------------------------------
+// Walktrap v0.2 -- Finds community structure of networks using random walks
+// Copyright (C) 2004-2005 Pascal Pons
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+//-----------------------------------------------------------------------------
+// Author   : Pascal Pons
+// Email    : pascal.pons@gmail.com
+// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html
+// Location : Paris, France
+// Time     : June 2005
+//-----------------------------------------------------------------------------
+// see readme.txt for more details
+
+
+#ifndef COMMUNITIES_H
+#define COMMUNITIES_H
+
+#include "walktrap_graph.h"
+#include "walktrap_heap.h"
+
+#include "igraph_community.h"
+#include "config.h"
+
+namespace igraph {
+
+namespace walktrap {
+
+class Communities;
+class Probabilities {
+public:
+    static IGRAPH_THREAD_LOCAL float* tmp_vector1;    //
+    static IGRAPH_THREAD_LOCAL float* tmp_vector2;    //
+    static IGRAPH_THREAD_LOCAL int* id;       //
+    static IGRAPH_THREAD_LOCAL int* vertices1;    //
+    static IGRAPH_THREAD_LOCAL int* vertices2;    //
+    static IGRAPH_THREAD_LOCAL int current_id;    //
+
+    static IGRAPH_THREAD_LOCAL Communities* C;                    // pointer to all the communities
+    static IGRAPH_THREAD_LOCAL int length;                        // length of the random walks
+
+
+    int size;                         // number of probabilities stored
+    int* vertices;                        // the vertices corresponding to the stored probabilities, 0 if all the probabilities are stored
+    float* P;                         // the probabilities
+
+    long memory();                        // the memory (in Bytes) used by the object
+    double compute_distance(const Probabilities* P2) const;   // compute the squared distance r^2 between this probability vector and P2
+    Probabilities(int community);                 // compute the probability vector of a community
+    Probabilities(int community1, int community2);        // merge the probability vectors of two communities in a new one
+    // the two communities must have their probability vectors stored
+
+    ~Probabilities();                     // destructor
+};
+
+class Community {
+public:
+
+    Neighbor* first_neighbor; // first item of the list of adjacent communities
+    Neighbor* last_neighbor;  // last item of the list of adjacent communities
+
+    int this_community;       // number of this community
+    int first_member;     // number of the first vertex of the community
+    int last_member;      // number of the last vertex of the community
+    int size;         // number of members of the community
+
+    Probabilities* P;     // the probability vector, 0 if not stored.
+
+
+    float sigma;          // sigma(C) of the community
+    float internal_weight;    // sum of the weight of the internal edges
+    float total_weight;       // sum of the weight of all the edges of the community (an edge between two communities is a half-edge for each community)
+
+    int sub_communities[2];   // the two sub sommunities, -1 if no sub communities;
+    int sub_community_of;     // number of the community in which this community has been merged
+    // 0 if the community is active
+    // -1 if the community is not used
+
+    void merge(Community &C1, Community &C2); // create a new community by merging C1 an C2
+    void add_neighbor(Neighbor* N);
+    void remove_neighbor(Neighbor* N);
+    float min_delta_sigma();          // compute the minimal delta sigma among all the neighbors of this community
+
+    Community();          // create an empty community
+    ~Community();         // destructor
+};
+
+class Communities {
+private:
+    long max_memory;  // size in Byte of maximal memory usage, -1 for no limit
+    igraph_matrix_t *merges;
+    long int mergeidx;
+    igraph_vector_t *modularity;
+
+public:
+
+    long memory_used;                 // in bytes
+    Min_delta_sigma_heap* min_delta_sigma;            // the min delta_sigma of the community with a saved probability vector (for memory management)
+
+    Graph* G;         // the graph
+    int* members;         // the members of each community represented as a chained list.
+    // a community points to the first_member the array which contains
+    // the next member (-1 = end of the community)
+    Neighbor_heap* H;     // the distances between adjacent communities.
+
+
+    Community* communities;   // array of the communities
+
+    int nb_communities;       // number of valid communities
+    int nb_active_communities;    // number of active communities
+
+    Communities(Graph* G, int random_walks_length = 3,
+                long max_memory = -1, igraph_matrix_t *merges = 0,
+                igraph_vector_t *modularity = 0);  // Constructor
+    ~Communities();                   // Destructor
+
+
+    void merge_communities(Neighbor* N);          // create a community by merging two existing communities
+    double merge_nearest_communities();
+
+
+    double compute_delta_sigma(int c1, int c2);       // compute delta_sigma(c1,c2)
+
+    void remove_neighbor(Neighbor* N);
+    void add_neighbor(Neighbor* N);
+    void update_neighbor(Neighbor* N, float new_delta_sigma);
+
+    void manage_memory();
+
+};
+
+}
+}       /* end of namespaces */
+
+#endif
diff --git a/igraph/include/walktrap_graph.h b/igraph/include/walktrap_graph.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/walktrap_graph.h
@@ -0,0 +1,108 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Pascal Pons
+   The original copyright notice follows here */
+
+// File: graph.h
+//-----------------------------------------------------------------------------
+// Walktrap v0.2 -- Finds community structure of networks using random walks
+// Copyright (C) 2004-2005 Pascal Pons
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+//-----------------------------------------------------------------------------
+// Author   : Pascal Pons
+// Email    : pascal.pons@gmail.com
+// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html
+// Location : Paris, France
+// Time     : June 2005
+//-----------------------------------------------------------------------------
+// see readme.txt for more details
+
+/* FSF address above was fixed by Tamas Nepusz */
+
+
+#ifndef GRAPH_H
+#define GRAPH_H
+#include <iostream>
+
+#include "igraph_community.h"
+
+namespace igraph {
+
+namespace walktrap {
+
+using namespace std;
+
+class Edge {            // code an edge of a given vertex
+public:
+    int neighbor;         // the number of the neighbor vertex
+    float weight;         // the weight of the edge
+};
+bool operator<(const Edge& E1, const Edge& E2);
+
+
+class Vertex {
+public:
+    Edge* edges;          // the edges of the vertex
+    int degree;           // number of neighbors
+    float total_weight;       // the total weight of the vertex
+
+    Vertex();         // creates empty vertex
+    ~Vertex();            // destructor
+};
+
+class Graph {
+public:
+    int nb_vertices;      // number of vertices
+    int nb_edges;         // number of edges
+    float total_weight;       // total weight of the edges
+    Vertex* vertices;     // array of the vertices
+
+    long memory();            // the total memory used in Bytes
+    Graph();          // create an empty graph
+    ~Graph();         // destructor
+    char** index;         // to keep the real name of the vertices
+
+    int convert_from_igraph(const igraph_t * igraph,
+                            const igraph_vector_t *weights);
+};
+
+}
+}        /* end of namespaces */
+
+#endif
+
diff --git a/igraph/include/walktrap_heap.h b/igraph/include/walktrap_heap.h
new file mode 100644
--- /dev/null
+++ b/igraph/include/walktrap_heap.h
@@ -0,0 +1,134 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Pascal Pons
+   The original copyright notice follows here. The FSF address was
+   fixed by Tamas Nepusz */
+
+// File: heap.h
+//-----------------------------------------------------------------------------
+// Walktrap v0.2 -- Finds community structure of networks using random walks
+// Copyright (C) 2004-2005 Pascal Pons
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+//-----------------------------------------------------------------------------
+// Author   : Pascal Pons
+// Email    : pons@liafa.jussieu.fr
+// Web page : http://www.liafa.jussieu.fr/~pons/
+// Location : Paris, France
+// Time     : June 2005
+//-----------------------------------------------------------------------------
+// see readme.txt for more details
+
+#ifndef HEAP_H
+#define HEAP_H
+
+namespace igraph {
+
+namespace walktrap {
+
+class Neighbor {
+public:
+    int community1;   // the two adjacent communities
+    int community2;   // community1 < community2
+
+    float delta_sigma;    // the delta sigma between the two communities
+    float weight;     // the total weight of the edges between the two communities
+    bool exact;       // true if delta_sigma is exact, false if it is only a lower bound
+
+    Neighbor* next_community1;        // pointers of two double
+    Neighbor* previous_community1;    // chained lists containing
+    Neighbor* next_community2;        // all the neighbors of
+    Neighbor* previous_community2;    // each communities.
+
+    int heap_index;   //
+
+    Neighbor();
+};
+
+
+class Neighbor_heap {
+private:
+    int size;
+    int max_size;
+
+    Neighbor** H;   // the heap that contains a pointer to each Neighbor object stored
+
+    void move_up(int index);
+    void move_down(int index);
+
+public:
+    void add(Neighbor* N);        // add a new distance
+    void update(Neighbor* N);     // update a distance
+    void remove(Neighbor* N);     // remove a distance
+    Neighbor* get_first();        // get the first item
+    long memory();
+    bool is_empty();
+
+    Neighbor_heap(int max_size);
+    ~Neighbor_heap();
+};
+
+
+class Min_delta_sigma_heap {
+private:
+    int size;
+    int max_size;
+
+    int* H;   // the heap that contains the number of each community
+    int* I;   // the index of each community in the heap (-1 = not stored)
+
+    void move_up(int index);
+    void move_down(int index);
+
+public:
+    int get_max_community();              // return the community with the maximal delta_sigma
+    void remove_community(int community);         // remove a community;
+    void update(int community);               // update (or insert if necessary) the community
+    long memory();                    // the memory used in Bytes.
+    bool is_empty();
+
+    float* delta_sigma;                    // the delta_sigma of the stored communities
+
+    Min_delta_sigma_heap(int max_size);
+    ~Min_delta_sigma_heap();
+};
+
+}
+}        /* end of namespaces */
+
+#endif
+
diff --git a/igraph/src/DensityGrid.cpp b/igraph/src/DensityGrid.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/DensityGrid.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// This file contains the member definitions of the DensityGrid.h class
+// This code is modified from the original code by B.N. Wylie
+
+#include <string>
+#include <deque>
+#include <iostream>
+#include <cmath>
+#include <cstdlib>
+
+using namespace std;
+
+#include "drl_Node.h"
+#include "DensityGrid.h"
+#include "igraph_error.h"
+
+#define GET_BIN(y, x) (Bins[y*GRID_SIZE+x])
+
+namespace drl {
+
+//*******************************************************
+// Density Grid Destructor -- deallocates memory used
+// for Density matrix, fall_off matrix, and node deque.
+
+DensityGrid::~DensityGrid () {
+    delete[] Density;
+    delete[] fall_off;
+    delete[] Bins;
+}
+
+/*********************************************
+* Function: Density_Grid::Reset              *
+* Description: Reset the density grid        *
+*********************************************/
+// changed from reset to init since we will only
+// call this once in the parallel version of layout
+
+void DensityGrid::Init() {
+
+    try {
+        Density = new float[GRID_SIZE][GRID_SIZE];
+        fall_off = new float[RADIUS * 2 + 1][RADIUS * 2 + 1];
+        Bins = new deque<Node>[GRID_SIZE * GRID_SIZE];
+    } catch (bad_alloc errora) {
+        // cout << "Error: Out of memory! Program stopped." << endl;
+#ifdef MUSE_MPI
+        MPI_Abort ( MPI_COMM_WORLD, 1 );
+#else
+        igraph_error("DrL is out of memory", __FILE__, __LINE__,
+                     IGRAPH_ENOMEM);
+        return;
+#endif
+    }
+
+    // Clear Grid
+    int i;
+    for (i = 0; i < GRID_SIZE; i++)
+        for (int j = 0; j < GRID_SIZE; j++) {
+            Density[i][j] = 0;
+            GET_BIN(i, j).erase(GET_BIN(i, j).begin(), GET_BIN(i, j).end());
+        }
+
+    // Compute fall off
+    for (i = -RADIUS; i <= RADIUS; i++)
+        for (int j = -RADIUS; j <= RADIUS; j++) {
+            fall_off[i + RADIUS][j + RADIUS] = (float)((RADIUS - fabs((float)i)) / RADIUS) *
+                                               (float)((RADIUS - fabs((float)j)) / RADIUS);
+        }
+
+}
+
+/***************************************************
+ * Function: DensityGrid::GetDensity               *
+ * Description: Get_Density from density grid      *
+ **************************************************/
+float DensityGrid::GetDensity(float Nx, float Ny, bool fineDensity) {
+    deque<Node>::iterator BI;
+    int x_grid, y_grid;
+    float x_dist, y_dist, distance, density = 0;
+    int boundary = 10;  // boundary around plane
+
+
+    /* Where to look */
+    x_grid = (int)((Nx + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((Ny + HALF_VIEW + .5) * VIEW_TO_GRID);
+
+    // Check for edges of density grid (10000 is arbitrary high density)
+    if (x_grid > GRID_SIZE - boundary || x_grid < boundary) {
+        return 10000;
+    }
+    if (y_grid > GRID_SIZE - boundary || y_grid < boundary) {
+        return 10000;
+    }
+
+    // Fine density?
+    if (fineDensity) {
+
+        // Go through nearest bins
+        for (int i = y_grid - 1; i <= y_grid + 1; i++)
+            for (int j = x_grid - 1; j <= x_grid + 1; j++) {
+
+                // Look through bin and add fine repulsions
+                for (BI = GET_BIN(i, j).begin(); BI != GET_BIN(i, j).end(); ++BI) {
+                    x_dist =  Nx - (BI->x);
+                    y_dist =  Ny - (BI->y);
+                    distance = x_dist * x_dist + y_dist * y_dist;
+                    density += 1e-4 / (distance + 1e-50);
+                }
+            }
+        // Course density
+    } else {
+
+        // Add rough estimate
+        density = Density[y_grid][x_grid];
+        density *= density;
+    }
+
+    return density;
+}
+
+/// Wrapper functions for the Add and subtract methods
+/// Nodes should all be passed by constant ref
+
+void DensityGrid::Add(Node &n, bool fineDensity) {
+    if (fineDensity) {
+        fineAdd(n);
+    } else {
+        Add(n);
+    }
+}
+
+void DensityGrid::Subtract( Node &n, bool first_add,
+                            bool fine_first_add, bool fineDensity) {
+    if ( fineDensity && !fine_first_add ) {
+        fineSubtract (n);
+    } else if ( !first_add ) {
+        Subtract(n);
+    }
+}
+
+
+/***************************************************
+ * Function: DensityGrid::Subtract                *
+ * Description: Subtract a node from density grid  *
+ **************************************************/
+void DensityGrid::Subtract(Node &N) {
+    int x_grid, y_grid, diam;
+    float *den_ptr, *fall_ptr;
+
+    /* Where to subtract */
+    x_grid = (int)((N.sub_x + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((N.sub_y + HALF_VIEW + .5) * VIEW_TO_GRID);
+    x_grid -= RADIUS;
+    y_grid -= RADIUS;
+    diam = 2 * RADIUS;
+
+    // check to see that we are inside grid
+    if ( (x_grid >= GRID_SIZE) || (x_grid < 0) ||
+         (y_grid >= GRID_SIZE) || (y_grid < 0) ) {
+#ifdef MUSE_MPI
+        MPI_Abort ( MPI_COMM_WORLD, 1 );
+#else
+        igraph_error("Exceeded density grid in DrL", __FILE__,
+                     __LINE__, IGRAPH_EDRL);
+        return;
+#endif
+    }
+
+    /* Subtract density values */
+    den_ptr = &Density[y_grid][x_grid];
+    fall_ptr = &fall_off[0][0];
+    for (int i = 0; i <= diam; i++) {
+        for (int j = 0; j <= diam; j++) {
+            *den_ptr++ -= *fall_ptr++;
+        }
+        den_ptr += GRID_SIZE - (diam + 1);
+    }
+}
+
+/***************************************************
+ * Function: DensityGrid::Add                     *
+ * Description: Add a node to the density grid     *
+ **************************************************/
+void DensityGrid::Add(Node &N) {
+
+    int x_grid, y_grid, diam;
+    float *den_ptr, *fall_ptr;
+
+
+    /* Where to add */
+    x_grid = (int)((N.x + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((N.y + HALF_VIEW + .5) * VIEW_TO_GRID);
+
+    N.sub_x = N.x;
+    N.sub_y = N.y;
+
+    x_grid -= RADIUS;
+    y_grid -= RADIUS;
+    diam = 2 * RADIUS;
+
+    // check to see that we are inside grid
+    if ( (x_grid >= GRID_SIZE) || (x_grid < 0) ||
+         (y_grid >= GRID_SIZE) || (y_grid < 0) ) {
+#ifdef MUSE_MPI
+        MPI_Abort ( MPI_COMM_WORLD, 1 );
+#else
+        igraph_error("Exceeded density grid in DrL", __FILE__,
+                     __LINE__, IGRAPH_EDRL);
+        return;
+#endif
+    }
+
+    /* Add density values */
+    den_ptr = &Density[y_grid][x_grid];
+    fall_ptr = &fall_off[0][0];
+    for (int i = 0; i <= diam; i++) {
+        for (int j = 0; j <= diam; j++) {
+            *den_ptr++ += *fall_ptr++;
+        }
+        den_ptr += GRID_SIZE - (diam + 1);
+    }
+
+}
+
+/***************************************************
+ * Function: DensityGrid::fineSubtract             *
+ * Description: Subtract a node from bins          *
+ **************************************************/
+void DensityGrid::fineSubtract(Node &N) {
+    int x_grid, y_grid;
+
+    /* Where to subtract */
+    x_grid = (int)((N.sub_x + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((N.sub_y + HALF_VIEW + .5) * VIEW_TO_GRID);
+    GET_BIN(y_grid, x_grid).pop_front();
+}
+
+/***************************************************
+ * Function: DensityGrid::fineAdd                  *
+ * Description: Add a node to the bins             *
+ **************************************************/
+void DensityGrid::fineAdd(Node &N) {
+    int x_grid, y_grid;
+
+    /* Where to add */
+    x_grid = (int)((N.x + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((N.y + HALF_VIEW + .5) * VIEW_TO_GRID);
+    N.sub_x = N.x;
+    N.sub_y = N.y;
+    GET_BIN(y_grid, x_grid).push_back(N);
+}
+
+} // namespace drl
diff --git a/igraph/src/DensityGrid_3d.cpp b/igraph/src/DensityGrid_3d.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/DensityGrid_3d.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// This file contains the member definitions of the DensityGrid.h class
+// This code is modified from the original code by B.N. Wylie
+
+#include <string>
+#include <deque>
+#include <iostream>
+#include <cmath>
+#include <cstdlib>
+
+using namespace std;
+
+#include "drl_Node_3d.h"
+#include "DensityGrid_3d.h"
+#include "igraph_error.h"
+
+#define GET_BIN(z, y, x) (Bins[(z*GRID_SIZE+y)*GRID_SIZE+x])
+
+namespace drl3d {
+
+//*******************************************************
+// Density Grid Destructor -- deallocates memory used
+// for Density matrix, fall_off matrix, and node deque.
+
+DensityGrid::~DensityGrid () {
+    delete[] Density;
+    delete[] fall_off;
+    delete[] Bins;
+}
+
+/*********************************************
+* Function: Density_Grid::Reset              *
+* Description: Reset the density grid        *
+*********************************************/
+// changed from reset to init since we will only
+// call this once in the parallel version of layout
+
+void DensityGrid::Init() {
+
+    try {
+        Density = new float[GRID_SIZE][GRID_SIZE][GRID_SIZE];
+        fall_off = new float[RADIUS * 2 + 1][RADIUS * 2 + 1][RADIUS * 2 + 1];
+        Bins = new deque<Node>[GRID_SIZE * GRID_SIZE * GRID_SIZE];
+    } catch (bad_alloc errora) {
+        // cout << "Error: Out of memory! Program stopped." << endl;
+#ifdef MUSE_MPI
+        MPI_Abort ( MPI_COMM_WORLD, 1 );
+#else
+        igraph_error("DrL is out of memory", __FILE__, __LINE__,
+                     IGRAPH_ENOMEM);
+        return;
+#endif
+    }
+
+    // Clear Grid
+    int i;
+    for (i = 0; i < GRID_SIZE; i++)
+        for (int j = 0; j < GRID_SIZE; j++)
+            for (int k = 0; k < GRID_SIZE; k++) {
+                Density[i][j][k] = 0;
+                GET_BIN(i, j, k).erase(GET_BIN(i, j, k).begin(), GET_BIN(i, j, k).end());
+            }
+
+    // Compute fall off
+    for (i = -RADIUS; i <= RADIUS; i++)
+        for (int j = -RADIUS; j <= RADIUS; j++)
+            for (int k = -RADIUS; k <= RADIUS; k++) {
+                fall_off[i + RADIUS][j + RADIUS][k + RADIUS] =
+                    (float)((RADIUS - fabs((float)i)) / RADIUS) *
+                    (float)((RADIUS - fabs((float)j)) / RADIUS) *
+                    (float)((RADIUS - fabs((float)k)) / RADIUS);
+            }
+
+}
+
+
+/***************************************************
+ * Function: DensityGrid::GetDensity               *
+ * Description: Get_Density from density grid      *
+ **************************************************/
+float DensityGrid::GetDensity(float Nx, float Ny, float Nz, bool fineDensity) {
+    deque<Node>::iterator BI;
+    int x_grid, y_grid, z_grid;
+    float x_dist, y_dist, z_dist, distance, density = 0;
+    int boundary = 10;  // boundary around plane
+
+
+    /* Where to look */
+    x_grid = (int)((Nx + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((Ny + HALF_VIEW + .5) * VIEW_TO_GRID);
+    z_grid = (int)((Nz + HALF_VIEW + .5) * VIEW_TO_GRID);
+
+    // Check for edges of density grid (10000 is arbitrary high density)
+    if (x_grid > GRID_SIZE - boundary || x_grid < boundary) {
+        return 10000;
+    }
+    if (y_grid > GRID_SIZE - boundary || y_grid < boundary) {
+        return 10000;
+    }
+    if (z_grid > GRID_SIZE - boundary || z_grid < boundary) {
+        return 10000;
+    }
+
+    // Fine density?
+    if (fineDensity) {
+
+        // Go through nearest bins
+        for (int k = z_grid - 1; k <= z_grid + 1; k++)
+            for (int i = y_grid - 1; i <= y_grid + 1; i++)
+                for (int j = x_grid - 1; j <= x_grid + 1; j++) {
+
+                    // Look through bin and add fine repulsions
+                    for (BI = GET_BIN(k, i, j).begin(); BI < GET_BIN(k, i, j).end(); ++BI) {
+                        x_dist =  Nx - (BI->x);
+                        y_dist =  Ny - (BI->y);
+                        z_dist =  Nz - (BI->z);
+                        distance = x_dist * x_dist + y_dist * y_dist + z_dist * z_dist;
+                        density += 1e-4 / (distance + 1e-50);
+                    }
+                }
+
+        // Course density
+    } else {
+
+        // Add rough estimate
+        density = Density[z_grid][y_grid][x_grid];
+        density *= density;
+    }
+
+    return density;
+}
+
+/// Wrapper functions for the Add and subtract methods
+/// Nodes should all be passed by constant ref
+
+void DensityGrid::Add(Node &n, bool fineDensity) {
+    if (fineDensity) {
+        fineAdd(n);
+    } else {
+        Add(n);
+    }
+}
+
+void DensityGrid::Subtract( Node &n, bool first_add,
+                            bool fine_first_add, bool fineDensity) {
+    if ( fineDensity && !fine_first_add ) {
+        fineSubtract (n);
+    } else if ( !first_add ) {
+        Subtract(n);
+    }
+}
+
+
+/***************************************************
+ * Function: DensityGrid::Subtract                *
+ * Description: Subtract a node from density grid  *
+ **************************************************/
+void DensityGrid::Subtract(Node &N) {
+    int x_grid, y_grid, z_grid, diam;
+    float *den_ptr, *fall_ptr;
+
+    /* Where to subtract */
+    x_grid = (int)((N.sub_x + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((N.sub_y + HALF_VIEW + .5) * VIEW_TO_GRID);
+    z_grid = (int)((N.sub_z + HALF_VIEW + .5) * VIEW_TO_GRID);
+    x_grid -= RADIUS;
+    y_grid -= RADIUS;
+    z_grid -= RADIUS;
+    diam = 2 * RADIUS;
+
+    // check to see that we are inside grid
+    if ( (x_grid >= GRID_SIZE) || (x_grid < 0) ||
+         (y_grid >= GRID_SIZE) || (y_grid < 0) ||
+         (z_grid >= GRID_SIZE) || (z_grid < 0) ) {
+#ifdef MUSE_MPI
+        MPI_Abort ( MPI_COMM_WORLD, 1 );
+#else
+        igraph_error("Exceeded density grid in DrL", __FILE__,
+                     __LINE__, IGRAPH_EDRL);
+        return;
+#endif
+    }
+
+    /* Subtract density values */
+    den_ptr = &Density[z_grid][y_grid][x_grid];
+    fall_ptr = &fall_off[0][0][0];
+    for (int i = 0; i <= diam; i++) {
+        for (int j = 0; j <= diam; j++)
+            for (int k = 0; k <= diam; k++) {
+                *den_ptr++ -= *fall_ptr++;
+            }
+        den_ptr += GRID_SIZE - (diam + 1);
+    }
+}
+
+/***************************************************
+ * Function: DensityGrid::Add                     *
+ * Description: Add a node to the density grid     *
+ **************************************************/
+void DensityGrid::Add(Node &N) {
+
+    int x_grid, y_grid, z_grid, diam;
+    float *den_ptr, *fall_ptr;
+
+
+    /* Where to add */
+    x_grid = (int)((N.x + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((N.y + HALF_VIEW + .5) * VIEW_TO_GRID);
+    z_grid = (int)((N.z + HALF_VIEW + .5) * VIEW_TO_GRID);
+
+    N.sub_x = N.x;
+    N.sub_y = N.y;
+    N.sub_z = N.z;
+
+    x_grid -= RADIUS;
+    y_grid -= RADIUS;
+    z_grid -= RADIUS;
+    diam = 2 * RADIUS;
+
+    // check to see that we are inside grid
+    if ( (x_grid >= GRID_SIZE) || (x_grid < 0) ||
+         (y_grid >= GRID_SIZE) || (y_grid < 0) ||
+         (z_grid >= GRID_SIZE) || (z_grid < 0) ) {
+#ifdef MUSE_MPI
+        MPI_Abort ( MPI_COMM_WORLD, 1 );
+#else
+        igraph_error("Exceeded density grid in DrL", __FILE__,
+                     __LINE__, IGRAPH_EDRL);
+        return;
+#endif
+    }
+
+    /* Add density values */
+    den_ptr = &Density[z_grid][y_grid][x_grid];
+    fall_ptr = &fall_off[0][0][0];
+    for (int i = 0; i <= diam; i++) {
+        for (int j = 0; j <= diam; j++)
+            for (int k = 0; k <= diam; k++) {
+                *den_ptr++ += *fall_ptr++;
+            }
+        den_ptr += GRID_SIZE - (diam + 1);
+    }
+
+}
+
+/***************************************************
+ * Function: DensityGrid::fineSubtract             *
+ * Description: Subtract a node from bins          *
+ **************************************************/
+void DensityGrid::fineSubtract(Node &N) {
+    int x_grid, y_grid, z_grid;
+
+    /* Where to subtract */
+    x_grid = (int)((N.sub_x + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((N.sub_y + HALF_VIEW + .5) * VIEW_TO_GRID);
+    z_grid = (int)((N.sub_z + HALF_VIEW + .5) * VIEW_TO_GRID);
+    GET_BIN(z_grid, y_grid, x_grid).pop_front();
+}
+
+/***************************************************
+ * Function: DensityGrid::fineAdd                  *
+ * Description: Add a node to the bins             *
+ **************************************************/
+void DensityGrid::fineAdd(Node &N) {
+    int x_grid, y_grid, z_grid;
+
+    /* Where to add */
+    x_grid = (int)((N.x + HALF_VIEW + .5) * VIEW_TO_GRID);
+    y_grid = (int)((N.y + HALF_VIEW + .5) * VIEW_TO_GRID);
+    z_grid = (int)((N.z + HALF_VIEW + .5) * VIEW_TO_GRID);
+    N.sub_x = N.x;
+    N.sub_y = N.y;
+    N.sub_z = N.z;
+    GET_BIN(z_grid, y_grid, x_grid).push_back(N);
+}
+
+} // namespace drl3d
diff --git a/igraph/src/NetDataTypes.cpp b/igraph/src/NetDataTypes.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/NetDataTypes.cpp
@@ -0,0 +1,222 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Jörg Reichardt
+   The original copyright notice follows here */
+
+/***************************************************************************
+                          NetDataTypes.cpp  -  description
+                             -------------------
+    begin                : Mon Oct 6 2003
+    copyright            : (C) 2003 by Joerg Reichardt
+    email                : reichardt@mitte
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include "NetDataTypes.h"
+
+//#################################################################################
+//###############################################################################
+//Constructor
+NNode::NNode(unsigned long ind, unsigned long c_ind, DLList<NLink*> *ll, char* n, int states) {
+    index = ind;
+    cluster_index = c_ind;
+    neighbours = new DLList<NNode*>();
+    n_links = new DLList<NLink*>();
+    global_link_list = ll;
+    strcpy(name, n);
+    color.red = 0;
+    color.green = 0;
+    color.blue = 0;
+    strcpy(color.pajek_c, "Green");
+    clustering = 0.0;
+    marker = 0;
+    affiliations = 0;
+    weight = 0.0;
+    affinity = 0.0;
+    distance = 0;
+    max_states = states;
+    state_history = new unsigned long[states + 1];
+}
+
+//Destructor
+NNode::~NNode() {
+    Disconnect_From_All();
+    delete neighbours;
+    delete n_links;
+    delete [] state_history;
+    neighbours = NULL;
+    n_links = NULL;
+    state_history = NULL;
+}
+
+void NNode::Add_StateHistory(unsigned int state) {
+    if (max_states >= state) {
+        state_history[state]++;
+    }
+}
+
+void NNode::Set_Color(RGBcolor c) {
+    color.red = c.red; color.blue = c.blue; color.green = c.green;
+    strcpy(color.pajek_c, c.pajek_c);
+}
+
+int NNode::Connect_To(NNode* neighbour, double weight) {
+    NLink *link;
+    //sollen doppelte Links erlaubt sein??  NEIN
+    if (!neighbour) {
+        return 0;
+    }
+    if (!(neighbours->Is_In_List(neighbour)) && (neighbour != this)) {
+        neighbours->Push(neighbour);        // nachbar hier eintragen
+        neighbour->neighbours->Push(this); // diesen knoten beim nachbarn eintragen
+
+        link = new NLink(this, neighbour, weight);     //link erzeugen
+        global_link_list->Push(link);                        // in globaler liste eintragen
+        n_links->Push(link);                                   // bei diesem Knoten eintragen
+        neighbour->n_links->Push(link);                  // beim nachbarn eintragen
+
+        return (1);
+    }
+    return (0);
+}
+
+NLink *NNode::Get_LinkToNeighbour(NNode* neighbour) {
+    DLList_Iter<NLink*> iter;
+    NLink *l_cur, *link = 0;
+    bool found = false;
+    // finde einen bestimmten Link aus der Liste der links eines Knotens
+    l_cur = iter.First(n_links);
+    while (!iter.End() && !found) {
+        if (((l_cur->Get_Start() == this) && (l_cur->Get_End() == neighbour)) || ((l_cur->Get_End() == this) && (l_cur->Get_Start() == neighbour))) {
+            found = true;
+            link = l_cur;
+        }
+        l_cur = iter.Next();
+    }
+    if (found) {
+        return link;
+    } else {
+        return NULL;
+    }
+}
+
+int NNode::Disconnect_From(NNode* neighbour) {
+    //sollen doppelte Links erlaubt sein??  s.o.
+    if (!neighbours) {
+        return 0;
+    }
+    neighbours->fDelete(neighbour);
+    n_links->fDelete(Get_LinkToNeighbour(neighbour));
+    neighbour->n_links->fDelete(neighbour->Get_LinkToNeighbour(this));
+    neighbour->neighbours->fDelete(this);
+    return 1;
+}
+
+int NNode::Disconnect_From_All() {
+    int number_of_neighbours = 0;
+    while (neighbours->Size()) {
+        Disconnect_From(neighbours->Pop());
+        number_of_neighbours++;
+    }
+    return (number_of_neighbours) ;
+}
+
+/*
+int NNode::Disconnect_From_All_Grandchildren()
+{
+ int n_l=links->Size();
+ unsigned long pos=0;
+ while ((n_l--)>1) {  //alle bis auf das erste loeschen
+      pos=(links->Get(n_l+1))->links->Is_In_List(this);
+     // printf("%d %d\n",n_l,pos);
+      (links->Get(n_l+1))->links->Delete(pos);
+  }
+ return(pos) ;
+}
+*/
+
+double NNode::Get_Links_Among_Neigbours(void) {
+//  long neighbours1, neighbours2;
+    double lam = 0;
+    DLList_Iter<NNode*> iter1, iter2;
+//  neighbours1=neighbours->Size();        //so viele Nachbarn hat die Betrachtete Node
+    NNode *step1, *step2;
+    step1 = iter1.First(neighbours);
+    while (!iter1.End()) { //  for (int n1=1;n1<=neighbours1; n1++)
+        //step1=neighbours->Get(n1);
+        //neighbours2=step1->neighbours->Size();  //so viele Nachbarn hat der n1-ste Nachbar
+        step2 = iter2.First(step1->Get_Neighbours());
+        while (!iter2.End()) { //for (int n2=1;n2<=neighbours2; n2++)
+            //step2=step1->neighbours->Get(n2);
+            if (step2->Get_Neighbours()->Is_In_List(this)) {
+                lam++;
+            }
+            step2 = iter2.Next();
+        }
+        step1 = iter1.Next();
+    }
+    return (lam / 2.0);
+}
+
+
+double NNode::Get_Clustering() {
+    double c;
+    unsigned long k;
+    k = neighbours->Size();
+    if (k <= 1) {
+        return (0);
+    }
+    c = 2.0 * Get_Links_Among_Neigbours() / double(k * k - k);
+    return (c);
+}
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+//Constructor
+NLink::NLink(NNode *s, NNode *e, double w) {
+    start = s;
+    end = e;
+    weight = w;
+    old_weight = 0;
+    marker = 0;
+}
+
+//Destructor
+NLink::~NLink() {
+    if (start && end) {
+        start->Disconnect_From(end);
+    }
+}
diff --git a/igraph/src/NetRoutines.cpp b/igraph/src/NetRoutines.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/NetRoutines.cpp
@@ -0,0 +1,286 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Jörg Reichardt
+   The original copyright notice follows here */
+
+/***************************************************************************
+                          NetRoutines.cpp  -  description
+                             -------------------
+    begin                : Tue Oct 28 2003
+    copyright            : (C) 2003 by Joerg Reichardt
+    email                : reichardt@mitte
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include "NetRoutines.h"
+#include "NetDataTypes.h"
+
+#include "igraph_types.h"
+#include "igraph_interface.h"
+#include "igraph_conversion.h"
+
+int igraph_i_read_network(const igraph_t *graph,
+                          const igraph_vector_t *weights,
+                          network *net, igraph_bool_t use_weights,
+                          unsigned int states) {
+
+    double av_k = 0.0, sum_weight = 0.0, min_weight = 1e60, max_weight = -1e60;
+    unsigned long min_k = 999999999, max_k = 0;
+    long max_index = 0;
+    char name[255];
+    NNode *node1, *node2;
+    DLList_Iter<NNode*> iter;
+    igraph_vector_t edgelist;
+    long int no_of_edges = (long int)igraph_ecount(graph);
+    long int ii;
+    char *empty = new char[1];
+    empty[0] = '\0';
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edgelist, no_of_edges * 2);
+    IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0 /* rowwise */));
+
+    for (ii = 0; ii < no_of_edges; ii++) {
+        long int i1 = (long int)VECTOR(edgelist)[2 * ii] + 1;
+        long int i2 = (long int)VECTOR(edgelist)[2 * ii + 1] + 1;
+        igraph_real_t Links;
+        if (use_weights) {
+            Links = VECTOR(*weights)[ii];
+        } else {
+            Links = 1.0;
+        }
+        // From the original source
+        if (max_index < i1) {
+            for (int i = max_index; i < i1; i++) {
+                net->node_list->Push(new NNode(i, 0, net->link_list, empty, states));
+            }
+            max_index = i1;
+        }
+        if (max_index < i2) {
+            for (int i = max_index; i < i2; i++) {
+                net->node_list->Push(new NNode(i, 0, net->link_list, empty, states));
+            }
+            max_index = i2;
+        }
+
+        node1 = net->node_list->Get(i1 - 1);
+        sprintf(name, "%li", i1);
+        node1->Set_Name(name);
+
+        node2 = net->node_list->Get(i2 - 1);
+        sprintf(name, "%li", i2);
+        node2->Set_Name(name);
+
+        node1->Connect_To(node2, Links);
+
+        if (Links < min_weight) {
+            min_weight = Links;
+        }
+        if (Links > max_weight) {
+            max_weight = Links;
+        }
+        sum_weight += Links;
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+    igraph_vector_destroy(&edgelist);
+
+    node1 = iter.First(net->node_list);
+    while (!iter.End()) {
+        if (node1->Get_Degree() > max_k) {
+            max_k = node1->Get_Degree();
+        }
+        if (node1->Get_Degree() < min_k) {
+            min_k = node1->Get_Degree();
+        }
+        av_k += node1->Get_Degree();
+        node1 = iter.Next();
+    }
+    net->av_k = av_k / double(net->node_list->Size());
+    net->sum_weights = sum_weight;
+    net->av_weight = sum_weight / double(net->link_list->Size());
+    net->min_k = min_k;
+    net->max_k = max_k;
+    net->min_weight = min_weight;
+    net->max_weight = max_weight;
+    net->sum_bids = 0;
+    net->min_bids = 0;
+    net->max_bids = 0;
+
+    delete [] empty;
+
+    return 0;
+}
+
+//###############################################################################################################
+void reduce_cliques(DLList<ClusterList<NNode*>*> *global_cluster_list, FILE *file) {
+    unsigned long size;
+    ClusterList<NNode*> *c_cur, *largest_c = 0;
+    DLList<ClusterList<NNode*>*> *subsets;
+    DLList_Iter<ClusterList<NNode*>*> c_iter, sub_iter;
+    DLList_Iter<NNode*> iter;
+    NNode *n_cur;
+
+    if (!(global_cluster_list->Size())) {
+        return;
+    }
+    //wir suchen den groessten Cluster
+
+    c_cur = c_iter.First(global_cluster_list);
+    size = 0;
+    while (!(c_iter.End())) {
+        if (c_cur->Size() > size) {
+            size = c_cur->Size();
+            largest_c = c_cur;
+        }
+        c_cur = c_iter.Next();
+    }
+// printf("Groesster Cluster hat %u Elemente.\n",largest_c->Size());
+
+    //Schauen, ob es Teilmengen gibt, die ebenfalls gefunden wurden
+    subsets = new DLList<ClusterList<NNode*>*>();
+    c_cur = c_iter.First(global_cluster_list);
+    while (!(c_iter.End())) {
+        if ((*c_cur < *largest_c || *c_cur == *largest_c) && c_cur != largest_c) { //alle echten Teilcluster von largest_c und die doppelten
+            subsets->Push(c_cur);
+        }
+        c_cur = c_iter.Next();
+    }
+    // die gefundenen Subsets werden aus der cluster_liste geloescht
+    while (subsets->Size()) {
+        global_cluster_list->fDelete(subsets->Pop());
+    }
+    delete subsets;
+    // Dann schreiben wir den groessten Cluster in das File
+    fprintf(file, "Energie: %1.12f   Nodes:%3lu    -   ", largest_c->Get_Energy(), largest_c->Size());
+
+    n_cur = iter.First(largest_c);
+    while (!(iter.End())) {
+        fprintf(file, "%s", n_cur->Get_Name());
+        n_cur = iter.Next();
+        if (n_cur) {
+            fprintf(file, ", ");
+        }
+    }
+    fprintf(file, "\n");
+
+
+    //Schliesslich schmeissen wir noch den eben gefundenen groessten Cluster raus
+    global_cluster_list->fDelete(largest_c);
+    //und dann geht es von vorn mit der Reduzierten ClusterListe los
+    reduce_cliques(global_cluster_list, file);
+
+}
+//##################################################################################
+void reduce_cliques2(network *net, bool only_double, long marker) {
+    unsigned long size;
+    ClusterList<NNode*> *c_cur, *largest_c = 0;
+    DLList_Iter<ClusterList<NNode*>*> c_iter;
+    do {
+        //wir suchen den groessten, nicht markierten Cluster
+        size = 0;
+        c_cur = c_iter.First(net->cluster_list);
+        while (!(c_iter.End())) {
+            if ((c_cur->Size() > size) && (c_cur->Get_Marker() != marker)) {
+                size = c_cur->Size();
+                largest_c = c_cur;
+            }
+            c_cur = c_iter.Next();
+        }
+        // printf("Groesster Cluster hat %u Elemente.\n",largest_c->Size());
+        //Schauen, ob es Teilmengen gibt, die ebenfalls gefunden wurden
+        c_cur = c_iter.First(net->cluster_list);
+        while (!(c_iter.End())) {
+            if (((!only_double && (*c_cur < *largest_c)) || (*c_cur == *largest_c)) && (c_cur != largest_c)) { //alle echten Teilcluster von largest_c und die doppelten
+                net->cluster_list->fDelete(c_cur);
+                while (c_cur->Get_Candidates()->Size()) {
+                    c_cur->Get_Candidates()->Pop();
+                }
+                while (c_cur->Size()) {
+                    c_cur->Pop();    // die knoten aber nicht loeschen!!
+                }
+                delete c_cur;    // nicht vergessen, die global geloeschte Clusterliste zu loeschen
+            }
+            c_cur = c_iter.Next();
+        }
+        //Schliesslich markieren wir noch den eben gefundenen groessten Cluster
+        largest_c->Set_Marker(marker);
+    } while (size);
+}
+
+//##################################################################################################
+unsigned long iterate_nsf_hierarchy(NNode *parent, unsigned long depth, FILE *file) {
+    NNode* next_node;
+    unsigned long newdepth, maxdepth;
+    bool first = true;
+    DLList_Iter<NNode*> *iter;
+    maxdepth = newdepth = depth;
+    iter = new DLList_Iter<NNode*>;
+    next_node = iter->First(parent->Get_Neighbours());
+    while (!(iter->End())) {
+        if (next_node->Get_Marker() > parent->Get_Marker()) { // wir gehen nach unten
+            if (first) {
+                fprintf(file, ",(");    // eine Neue Klammer auf
+            }
+            if (first) {
+                fprintf(file, "%s", next_node->Get_Name());    // nur vor dem ersten kein Komma
+            } else {
+                fprintf(file, ",%s", next_node->Get_Name());    // sonst immer mit Komma
+            }
+            first = false;
+            newdepth = iterate_nsf_hierarchy(next_node, depth + 1, file);
+            if (maxdepth < newdepth) {
+                maxdepth = newdepth;
+            }
+        }
+        next_node = iter->Next();
+    }
+    if (!first) {
+        fprintf(file, ")");    //hat es ueberhaupt einen gegeben?
+    }
+    //dann klamer zu!
+    delete iter;
+    return maxdepth;
+}
+
+//################################################################
+void clear_all_markers(network *net) {
+    DLList_Iter<NNode*> iter;
+    NNode *n_cur;
+    n_cur = iter.First(net->node_list);
+    while (!iter.End()) {
+        n_cur->Set_Marker(0);
+        n_cur = iter.Next();
+    }
+}
+
diff --git a/igraph/src/abort_.c b/igraph/src/abort_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/abort_.c
@@ -0,0 +1,22 @@
+#include "stdio.h"
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern VOID sig_die();
+
+int abort_()
+#else
+extern void sig_die(const char*,int);
+
+int abort_(void)
+#endif
+{
+sig_die("Fortran abort routine called", 1);
+return 0;	/* not reached */
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/adjlist.c b/igraph/src/adjlist.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/adjlist.c
@@ -0,0 +1,930 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_adjlist.h"
+#include "igraph_memory.h"
+#include "igraph_interface.h"
+#include "igraph_interrupt_internal.h"
+#include "config.h"
+
+#include <string.h>   /* memset */
+#include <stdio.h>
+
+/**
+ * \section about_adjlists
+ * <para>Sometimes it is easier to work with a graph which is in
+ * adjacency list format: a list of vectors; each vector contains the
+ * neighbor vertices or incident edges of a given vertex. Typically,
+ * this representation is good if we need to iterate over the neighbors
+ * of all vertices many times. E.g. when finding the shortest paths
+ * between every pairs of vertices or calculating closeness centrality
+ * for all the vertices.</para>
+ *
+ * <para>The <type>igraph_adjlist_t</type> stores the adjacency lists
+ * of a graph. After creation it is independent of the original graph,
+ * it can be modified freely with the usual vector operations, the
+ * graph is not affected. E.g. the adjacency list can be used to
+ * rewire the edges of a graph efficiently. If one used the
+ * straightforward \ref igraph_delete_edges() and \ref
+ * igraph_add_edges() combination for this that needs O(|V|+|E|) time
+ * for every single deletion and insertion operation, it is thus very
+ * slow if many edges are rewired. Extracting the graph into an
+ * adjacency list, do all the rewiring operations on the vectors of
+ * the adjacency list and then creating a new graph needs (depending
+ * on how exactly the rewiring is done) typically O(|V|+|E|) time for
+ * the whole rewiring process.</para>
+ *
+ * <para>Lazy adjacency lists are a bit different. When creating a
+ * lazy adjacency list, the neighbors of the vertices are not queried,
+ * only some memory is allocated for the vectors. When \ref
+ * igraph_lazy_adjlist_get() is called for vertex v the first time,
+ * the neighbors of v are queried and stored in a vector of the
+ * adjacency list, so they don't need to be queried again. Lazy
+ * adjacency lists are handy if you have an at least linear operation
+ * (because initialization is generally linear in terms of number of
+ * vertices), but you don't know how many vertices you will visit
+ * during the computation.
+ * </para>
+ *
+ * <para>
+ * \example examples/simple/adjlist.c
+ * </para>
+ */
+
+/**
+ * \function igraph_adjlist_init
+ * Initialize an adjacency list of vertices from a given graph
+ *
+ * Create a list of vectors containing the neighbors of all vertices
+ * in a graph. The adjacency list is independent of the graph after
+ * creation, e.g. the graph can be destroyed and modified, the
+ * adjacency list contains the state of the graph at the time of its
+ * initialization.
+ * \param graph The input graph.
+ * \param al Pointer to an uninitialized <type>igraph_adjlist_t</type> object.
+ * \param mode Constant specifying whether outgoing
+ *   (<code>IGRAPH_OUT</code>), incoming (<code>IGRAPH_IN</code>),
+ *   or both (<code>IGRAPH_ALL</code>) types of neighbors to include
+ *   in the adjacency list. It is ignored for undirected networks.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ */
+
+int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al,
+                        igraph_neimode_t mode) {
+    igraph_integer_t i;
+    igraph_vector_t tmp;
+
+    if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Cannot create adjlist view", IGRAPH_EINVMODE);
+    }
+
+    igraph_vector_init(&tmp, 0);
+    IGRAPH_FINALLY(igraph_vector_destroy, &tmp);
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    al->length = igraph_vcount(graph);
+    al->adjs = igraph_Calloc(al->length, igraph_vector_int_t);
+    if (al->adjs == 0) {
+        IGRAPH_ERROR("Cannot create adjlist view", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_FINALLY(igraph_adjlist_destroy, al);
+    for (i = 0; i < al->length; i++) {
+        int j, n;
+        IGRAPH_ALLOW_INTERRUPTION();
+        IGRAPH_CHECK(igraph_neighbors(graph, &tmp, i, mode));
+        n = igraph_vector_size(&tmp);
+        IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], n));
+        for (j = 0; j < n; j++) {
+            VECTOR(al->adjs[i])[j] = VECTOR(tmp)[j];
+        }
+    }
+
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_adjlist_init_empty
+ * Initialize an empty adjacency list
+ *
+ * Creates a list of vectors, one for each vertex. This is useful when you
+ * are \em constructing a graph using an adjacency list representation as
+ * it does not require your graph to exist yet.
+ * \param no_of_nodes The number of vertices
+ * \param al Pointer to an uninitialized <type>igraph_adjlist_t</type> object.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|), linear in the number of vertices.
+ */
+
+int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes) {
+    long int i;
+
+    al->length = no_of_nodes;
+    al->adjs = igraph_Calloc(al->length, igraph_vector_int_t);
+    if (al->adjs == 0) {
+        IGRAPH_ERROR("Cannot create adjlist view", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_FINALLY(igraph_adjlist_destroy, al);
+    for (i = 0; i < al->length; i++) {
+        IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], 0));
+    }
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_adjlist_init_complementer
+ * Adjacency lists for the complementer graph
+ *
+ * This function creates adjacency lists for the complementer
+ * of the input graph. In the complementer graph all edges are present
+ * which are not present in the original graph. Multiple edges in the
+ * input graph are ignored.
+ * \param graph The input graph.
+ * \param al Pointer to a not yet initialized adjacency list.
+ * \param mode Constant specifying whether outgoing
+ *   (<code>IGRAPH_OUT</code>), incoming (<code>IGRAPH_IN</code>),
+ *   or both (<code>IGRAPH_ALL</code>) types of neighbors (in the
+ *   complementer graph) to include in the adjacency list. It is
+ *   ignored for undirected networks.
+ * \param loops Whether to consider loop edges.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^2+|E|), quadratic in the number of vertices.
+ */
+
+int igraph_adjlist_init_complementer(const igraph_t *graph,
+                                     igraph_adjlist_t *al,
+                                     igraph_neimode_t mode,
+                                     igraph_bool_t loops) {
+    igraph_integer_t i, j, k, n;
+    igraph_bool_t* seen;
+    igraph_vector_t vec;
+
+    if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_EINVMODE);
+    }
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    al->length = igraph_vcount(graph);
+    al->adjs = igraph_Calloc(al->length, igraph_vector_int_t);
+    if (al->adjs == 0) {
+        IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_FINALLY(igraph_adjlist_destroy, al);
+
+    n = al->length;
+    seen = igraph_Calloc(n, igraph_bool_t);
+    if (seen == 0) {
+        IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, seen);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&vec, 0);
+
+    for (i = 0; i < al->length; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        igraph_neighbors(graph, &vec, i, mode);
+        memset(seen, 0, sizeof(igraph_bool_t) * (unsigned) al->length);
+        n = al->length;
+        if (!loops) {
+            seen[i] = 1;
+            n--;
+        }
+        for (j = 0; j < igraph_vector_size(&vec); j++) {
+            if (! seen [ (long int) VECTOR(vec)[j] ] ) {
+                n--;
+                seen[ (long int) VECTOR(vec)[j] ] = 1;
+            }
+        }
+        IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], n));
+        for (j = 0, k = 0; k < n; j++) {
+            if (!seen[j]) {
+                VECTOR(al->adjs[i])[k++] = j;
+            }
+        }
+    }
+
+    igraph_Free(seen);
+    igraph_vector_destroy(&vec);
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+/**
+ * \function igraph_adjlist_destroy
+ * Deallocate memory
+ *
+ * Free all memory allocated for an adjacency list.
+ * \param al The adjacency list to destroy.
+ *
+ * Time complexity: depends on memory management.
+ */
+
+void igraph_adjlist_destroy(igraph_adjlist_t *al) {
+    long int i;
+    for (i = 0; i < al->length; i++) {
+        if (&al->adjs[i]) {
+            igraph_vector_int_destroy(&al->adjs[i]);
+        }
+    }
+    igraph_Free(al->adjs);
+}
+
+/**
+ * \function igraph_adjlist_clear
+ * Removes all edges from an adjacency list.
+ *
+ * \param al The adjacency list.
+ * Time complexity: depends on memory management, typically O(n), where n is
+ * the total number of elements in the adjacency list.
+ */
+void igraph_adjlist_clear(igraph_adjlist_t *al) {
+    long int i;
+    for (i = 0; i < al->length; i++) {
+        igraph_vector_int_clear(&al->adjs[i]);
+    }
+}
+
+/**
+ * \function igraph_adjlist_size
+ * Number of vertices in an adjacency list.
+ *
+ * \param al The adjacency list.
+ * \return The number of elements.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al) {
+    return al->length;
+}
+
+/* igraph_vector_int_t *igraph_adjlist_get(igraph_adjlist_t *al, igraph_integer_t no) { */
+/*   return &al->adjs[(long int)no]; */
+/* } */
+
+/**
+ * \function igraph_adjlist_sort
+ * Sort each vector in an adjacency list.
+ *
+ * Sorts every vector of the adjacency list.
+ * \param al The adjacency list.
+ *
+ * Time complexity: O(n log n), n is the total number of elements in
+ * the adjacency list.
+ */
+
+void igraph_adjlist_sort(igraph_adjlist_t *al) {
+    long int i;
+    for (i = 0; i < al->length; i++) {
+        igraph_vector_int_sort(&al->adjs[i]);
+    }
+}
+
+/**
+ * \function igraph_adjlist_simplify
+ * Simplify
+ *
+ * Simplify an adjacency list, ie. remove loop and multiple edges.
+ * \param al The adjacency list.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of edges and
+ * vertices.
+ */
+
+int igraph_adjlist_simplify(igraph_adjlist_t *al) {
+    long int i;
+    long int n = al->length;
+    igraph_vector_int_t mark;
+    igraph_vector_int_init(&mark, n);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &mark);
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &al->adjs[i];
+        long int j, l = igraph_vector_int_size(v);
+        VECTOR(mark)[i] = i + 1;
+        for (j = 0; j < l; /* nothing */) {
+            long int e = (long int) VECTOR(*v)[j];
+            if (VECTOR(mark)[e] != i + 1) {
+                VECTOR(mark)[e] = i + 1;
+                j++;
+            } else {
+                VECTOR(*v)[j] = igraph_vector_int_tail(v);
+                igraph_vector_int_pop_back(v);
+                l--;
+            }
+        }
+    }
+
+    igraph_vector_int_destroy(&mark);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_adjlist_remove_duplicate(const igraph_t *graph,
+                                    igraph_adjlist_t *al) {
+    long int i;
+    long int n = al->length;
+    IGRAPH_UNUSED(graph);
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &al->adjs[i];
+        long int j, p = 1, l = igraph_vector_int_size(v);
+        for (j = 1; j < l; j++) {
+            long int e = (long int) VECTOR(*v)[j];
+            /* Non-loop edges, and one end of loop edges are fine. */
+            /* We use here, that the vector is sorted and we also keep it sorted */
+            if (e != i || VECTOR(*v)[j - 1] != e) {
+                VECTOR(*v)[p++] = e;
+            }
+        }
+        igraph_vector_int_resize(v, p);
+    }
+
+    return 0;
+}
+
+#ifndef USING_R
+int igraph_adjlist_print(const igraph_adjlist_t *al) {
+    long int i;
+    long int n = al->length;
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &al->adjs[i];
+        igraph_vector_int_print(v);
+    }
+    return 0;
+}
+#endif
+
+int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile) {
+    long int i;
+    long int n = al->length;
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &al->adjs[i];
+        igraph_vector_int_fprint(v, outfile);
+    }
+    return 0;
+}
+
+#define ADJLIST_CANON_EDGE(from, to, directed) \
+    do {                     \
+        igraph_integer_t temp;         \
+        if((!directed) && from < to) {     \
+            temp = to;               \
+            to = from;               \
+            from = temp;             \
+        }                      \
+    } while(0);
+
+igraph_bool_t igraph_adjlist_has_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed) {
+    igraph_vector_int_t* fromvec;
+    ADJLIST_CANON_EDGE(from, to, directed);
+    fromvec = igraph_adjlist_get(al, from);
+    return igraph_vector_int_binsearch2(fromvec, to);
+
+}
+
+int igraph_adjlist_replace_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t oldto, igraph_integer_t newto, igraph_bool_t directed) {
+    igraph_vector_int_t *oldfromvec, *newfromvec;
+    int err1, err2;
+    long int oldpos, newpos;
+    igraph_integer_t oldfrom = from, newfrom = from;
+    ADJLIST_CANON_EDGE(oldfrom, oldto, directed);
+    ADJLIST_CANON_EDGE(newfrom, newto, directed);
+
+    oldfromvec = igraph_adjlist_get(al, oldfrom);
+    newfromvec = igraph_adjlist_get(al, newfrom);
+
+
+    err1 = igraph_vector_int_binsearch(oldfromvec, oldto, &oldpos);
+    err2 = igraph_vector_int_binsearch(newfromvec, newto, &newpos);
+
+    /* oldfrom -> oldto should exist; newfrom -> newto should not. */
+    if ((!err1) || err2) {
+        return 1;
+    }
+
+    igraph_vector_int_remove(oldfromvec, oldpos);
+    if (oldfromvec == newfromvec && oldpos < newpos) {
+        --newpos;
+    }
+    IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto));
+
+    return 0;
+
+}
+
+int igraph_adjedgelist_remove_duplicate(const igraph_t *graph,
+                                        igraph_inclist_t *al) {
+    IGRAPH_WARNING("igraph_adjedgelist_remove_duplicate() is deprecated, use "
+                   "igraph_inclist_remove_duplicate() instead");
+    return igraph_inclist_remove_duplicate(graph, al);
+}
+
+#ifndef USING_R
+int igraph_adjedgelist_print(const igraph_inclist_t *al, FILE *outfile) {
+    IGRAPH_WARNING("igraph_adjedgelist_print() is deprecated, use "
+                   "igraph_inclist_print() instead");
+    return igraph_inclist_fprint(al, outfile);
+}
+#endif
+
+/**
+ * \function igraph_adjedgelist_init
+ * Initialize an incidence list of edges
+ *
+ * This function was superseded by \ref igraph_inclist_init() in igraph 0.6.
+ * Please use \ref igraph_inclist_init() instead of this function.
+ *
+ * </para><para>
+ * Deprecated in version 0.6.
+ */
+int igraph_adjedgelist_init(const igraph_t *graph,
+                            igraph_inclist_t *il,
+                            igraph_neimode_t mode) {
+    IGRAPH_WARNING("igraph_adjedgelist_init() is deprecated, use "
+                   "igraph_inclist_init() instead");
+    return igraph_inclist_init(graph, il, mode);
+}
+
+/**
+ * \function igraph_adjedgelist_destroy
+ * Frees all memory allocated for an incidence list.
+ *
+ * This function was superseded by \ref igraph_inclist_destroy() in igraph 0.6.
+ * Please use \ref igraph_inclist_destroy() instead of this function.
+ *
+ * </para><para>
+ * Deprecated in version 0.6.
+ */
+void igraph_adjedgelist_destroy(igraph_inclist_t *il) {
+    IGRAPH_WARNING("igraph_adjedgelist_destroy() is deprecated, use "
+                   "igraph_inclist_destroy() instead");
+    igraph_inclist_destroy(il);
+}
+
+int igraph_inclist_remove_duplicate(const igraph_t *graph,
+                                    igraph_inclist_t *al) {
+    long int i;
+    long int n = al->length;
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &al->incs[i];
+        long int j, p = 1, l = igraph_vector_int_size(v);
+        for (j = 1; j < l; j++) {
+            long int e = (long int) VECTOR(*v)[j];
+            /* Non-loop edges and one end of loop edges are fine. */
+            /* We use here, that the vector is sorted and we also keep it sorted */
+            if (IGRAPH_FROM(graph, e) != IGRAPH_TO(graph, e) ||
+                VECTOR(*v)[j - 1] != e) {
+                VECTOR(*v)[p++] = e;
+            }
+        }
+        igraph_vector_int_resize(v, p);
+    }
+
+    return 0;
+}
+
+#ifndef USING_R
+int igraph_inclist_print(const igraph_inclist_t *al) {
+    long int i;
+    long int n = al->length;
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &al->incs[i];
+        igraph_vector_int_print(v);
+    }
+    return 0;
+}
+#endif
+
+int igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) {
+    long int i;
+    long int n = al->length;
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &al->incs[i];
+        igraph_vector_int_fprint(v, outfile);
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_inclist_init
+ * Initialize an incidence list of edges
+ *
+ * Create a list of vectors containing the incident edges for all
+ * vertices. The incidence list is independent of the graph after
+ * creation, subsequent changes of the graph object do not update the
+ * incidence list, and changes to the incidence list do not update the
+ * graph.
+ * \param graph The input graph.
+ * \param il Pointer to an uninitialized incidence list.
+ * \param mode Constant specifying whether incoming edges
+ *   (<code>IGRAPH_IN</code>), outgoing edges (<code>IGRAPH_OUT</code>) or
+ *   both (<code>IGRAPH_ALL</code>) to include in the incidence lists
+ *   of directed graphs. It is ignored for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ */
+
+int igraph_inclist_init(const igraph_t *graph,
+                        igraph_inclist_t *il,
+                        igraph_neimode_t mode) {
+    igraph_integer_t i;
+    igraph_vector_t tmp;
+
+    if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_EINVMODE);
+    }
+
+    igraph_vector_init(&tmp, 0);
+    IGRAPH_FINALLY(igraph_vector_destroy, &tmp);
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    il->length = igraph_vcount(graph);
+    il->incs = igraph_Calloc(il->length, igraph_vector_int_t);
+    if (il->incs == 0) {
+        IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_FINALLY(igraph_inclist_destroy, il);
+    for (i = 0; i < il->length; i++) {
+        int j, n;
+        IGRAPH_ALLOW_INTERRUPTION();
+        IGRAPH_CHECK(igraph_incident(graph, &tmp, i, mode));
+        n = igraph_vector_size(&tmp);
+        IGRAPH_CHECK(igraph_vector_int_init(&il->incs[i], n));
+        for (j = 0; j < n; j++) {
+            VECTOR(il->incs[i])[j] = VECTOR(tmp)[j];
+        }
+    }
+
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_inclist_init_empty
+ * \brief Initialize an incidence list corresponding to an empty graph.
+ *
+ * This function essentially creates a list of empty vectors that may
+ * be treated as an incidence list for a graph with a given number of
+ * vertices.
+ *
+ * \param il Pointer to an uninitialized incidence list.
+ * \param n  The number of vertices in the incidence list.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|), linear in the number of vertices.
+ */
+
+int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n) {
+    long int i;
+
+    il->length = n;
+    il->incs = igraph_Calloc(il->length, igraph_vector_int_t);
+    if (il->incs == 0) {
+        IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_FINALLY(igraph_inclist_destroy, il);
+    for (i = 0; i < n; i++) {
+        IGRAPH_CHECK(igraph_vector_int_init(&il->incs[i], 0));
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_inclist_destroy
+ * Frees all memory allocated for an incidence list.
+ *
+ * \param eal The incidence list to destroy.
+ *
+ * Time complexity: depends on memory management.
+ */
+
+void igraph_inclist_destroy(igraph_inclist_t *il) {
+    long int i;
+    for (i = 0; i < il->length; i++) {
+        /* This works if some igraph_vector_int_t's are 0,
+           because igraph_vector_destroy can handle this. */
+        igraph_vector_int_destroy(&il->incs[i]);
+    }
+    igraph_Free(il->incs);
+}
+
+/**
+ * \function igraph_inclist_clear
+ * Removes all edges from an incidence list.
+ *
+ * \param il The incidence list.
+ * Time complexity: depends on memory management, typically O(n), where n is
+ * the total number of elements in the incidence list.
+ */
+void igraph_inclist_clear(igraph_inclist_t *il) {
+    long int i;
+    for (i = 0; i < il->length; i++) {
+        igraph_vector_int_clear(&il->incs[i]);
+    }
+}
+
+/**
+ * \function igraph_lazy_adjlist_init
+ * Constructor
+ *
+ * Create a lazy adjacency list for vertices. This function only
+ * allocates some memory for storing the vectors of an adjacency list,
+ * but the neighbor vertices are not queried, only at the \ref
+ * igraph_lazy_adjlist_get() calls.
+ * \param graph The input graph.
+ * \param al Pointer to an uninitialized adjacency list object.
+ * \param mode Constant, it gives whether incoming edges
+ *   (<code>IGRAPH_IN</code>), outgoing edges
+ *   (<code>IGRPAH_OUT</code>) or both types of edges
+ *   (<code>IGRAPH_ALL</code>) are considered. It is ignored for
+ *   undirected graphs.
+ * \param simplify Constant, it gives whether to simplify the vectors
+ *   in the adjacency list (<code>IGRAPH_SIMPLIFY</code>) or not
+ *   (<code>IGRAPH_DONT_SIMPLIFY</code>).
+ * \return Error code.
+ *
+ * Time complexity: O(|V|), the number of vertices, possibly, but
+ * depends on the underlying memory management too.
+ */
+
+int igraph_lazy_adjlist_init(const igraph_t *graph,
+                             igraph_lazy_adjlist_t *al,
+                             igraph_neimode_t mode,
+                             igraph_lazy_adlist_simplify_t simplify) {
+    if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Cannor create adjlist view", IGRAPH_EINVMODE);
+    }
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+    al->mode = mode;
+    al->simplify = simplify;
+    al->graph = graph;
+
+    al->length = igraph_vcount(graph);
+    al->adjs = igraph_Calloc(al->length, igraph_vector_t*);
+    if (al->adjs == 0) {
+        IGRAPH_ERROR("Cannot create lazy adjlist view", IGRAPH_ENOMEM);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_lazy_adjlist_destroy
+ * Deallocate memory
+ *
+ * Free all allocated memory for a lazy adjacency list.
+ * \param al The adjacency list to deallocate.
+ *
+ * Time complexity: depends on the memory management.
+ */
+
+void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al) {
+    igraph_lazy_adjlist_clear(al);
+    igraph_Free(al->adjs);
+}
+
+/**
+ * \function igraph_lazy_adjlist_clear
+ * Removes all edges from a lazy adjacency list.
+ *
+ * \param al The lazy adjacency list.
+ * Time complexity: depends on memory management, typically O(n), where n is
+ * the total number of elements in the adjacency list.
+ */
+void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al) {
+    long int i, n = al->length;
+    for (i = 0; i < n; i++) {
+        if (al->adjs[i] != 0) {
+            igraph_vector_destroy(al->adjs[i]);
+            igraph_Free(al->adjs[i]);
+        }
+    }
+}
+
+igraph_vector_t *igraph_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al,
+        igraph_integer_t pno) {
+    igraph_integer_t no = pno;
+    int ret;
+    if (al->adjs[no] == 0) {
+        al->adjs[no] = igraph_Calloc(1, igraph_vector_t);
+        if (al->adjs[no] == 0) {
+            igraph_error("Lazy adjlist failed", __FILE__, __LINE__,
+                         IGRAPH_ENOMEM);
+        }
+        ret = igraph_vector_init(al->adjs[no], 0);
+        if (ret != 0) {
+            igraph_error("", __FILE__, __LINE__, ret);
+        }
+        ret = igraph_neighbors(al->graph, al->adjs[no], no, al->mode);
+        if (ret != 0) {
+            igraph_error("", __FILE__, __LINE__, ret);
+        }
+
+        if (al->simplify == IGRAPH_SIMPLIFY) {
+            igraph_vector_t *v = al->adjs[no];
+            long int i, p = 0, n = igraph_vector_size(v);
+            for (i = 0; i < n; i++) {
+                if (VECTOR(*v)[i] != no &&
+                    (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i])) {
+                    VECTOR(*v)[p] = VECTOR(*v)[i];
+                    p++;
+                }
+            }
+            igraph_vector_resize(v, p);
+        }
+    }
+
+    return al->adjs[no];
+}
+
+/**
+ * \function igraph_lazy_adjedgelist_init
+ * Initializes a lazy incidence list of edges
+ *
+ * This function was superseded by \ref igraph_lazy_inclist_init() in igraph 0.6.
+ * Please use \ref igraph_lazy_inclist_init() instead of this function.
+ *
+ * </para><para>
+ * Deprecated in version 0.6.
+ */
+int igraph_lazy_adjedgelist_init(const igraph_t *graph,
+                                 igraph_lazy_inclist_t *il,
+                                 igraph_neimode_t mode) {
+    IGRAPH_WARNING("igraph_lazy_adjedgelist_init() is deprecated, use "
+                   "igraph_lazy_inclist_init() instead");
+    return igraph_lazy_inclist_init(graph, il, mode);
+}
+
+/**
+ * \function igraph_lazy_adjedgelist_destroy
+ * Frees all memory allocated for an incidence list.
+ *
+ * This function was superseded by \ref igraph_lazy_inclist_destroy() in igraph 0.6.
+ * Please use \ref igraph_lazy_inclist_destroy() instead of this function.
+ *
+ * </para><para>
+ * Deprecated in version 0.6.
+ */
+void igraph_lazy_adjedgelist_destroy(igraph_lazy_inclist_t *il) {
+    IGRAPH_WARNING("igraph_lazy_adjedgelist_destroy() is deprecated, use "
+                   "igraph_lazy_inclist_destroy() instead");
+    igraph_lazy_inclist_destroy(il);
+}
+
+igraph_vector_t *igraph_lazy_adjedgelist_get_real(igraph_lazy_adjedgelist_t *il,
+        igraph_integer_t pno) {
+    IGRAPH_WARNING("igraph_lazy_adjedgelist_get_real() is deprecated, use "
+                   "igraph_lazy_inclist_get_real() instead");
+    return igraph_lazy_inclist_get_real(il, pno);
+}
+
+/**
+ * \function igraph_lazy_inclist_init
+ * Initializes a lazy incidence list of edges
+ *
+ * Create a lazy incidence list for edges. This function only
+ * allocates some memory for storing the vectors of an incidence list,
+ * but the incident edges are not queried, only when \ref
+ * igraph_lazy_inclist_get() is called.
+ * \param graph The input graph.
+ * \param al Pointer to an uninitialized incidence list.
+ * \param mode Constant, it gives whether incoming edges
+ *   (<code>IGRAPH_IN</code>), outgoing edges
+ *   (<code>IGRPAH_OUT</code>) or both types of edges
+ *   (<code>IGRAPH_ALL</code>) are considered. It is ignored for
+ *   undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|), the number of vertices, possibly. But it
+ * also depends on the underlying memory management.
+ */
+
+int igraph_lazy_inclist_init(const igraph_t *graph,
+                             igraph_lazy_inclist_t *al,
+                             igraph_neimode_t mode) {
+
+    if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Cannot create lazy incidence list view", IGRAPH_EINVMODE);
+    }
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    al->mode = mode;
+    al->graph = graph;
+
+    al->length = igraph_vcount(graph);
+    al->incs = igraph_Calloc(al->length, igraph_vector_t*);
+    if (al->incs == 0) {
+        IGRAPH_ERROR("Cannot create lazy incidence list view", IGRAPH_ENOMEM);
+    }
+
+    return 0;
+
+}
+
+/**
+ * \function igraph_lazy_inclist_destroy
+ * Deallocates memory
+ *
+ * Frees all allocated memory for a lazy incidence list.
+ * \param al The incidence list to deallocate.
+ *
+ * Time complexity: depends on memory management.
+ */
+
+void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il) {
+    igraph_lazy_inclist_clear(il);
+    igraph_Free(il->incs);
+}
+
+/**
+ * \function igraph_lazy_inclist_clear
+ * Removes all edges from a lazy incidence list.
+ *
+ * \param il The lazy incidence list.
+ * Time complexity: depends on memory management, typically O(n), where n is
+ * the total number of elements in the incidence list.
+ */
+void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il) {
+    long int i, n = il->length;
+    for (i = 0; i < n; i++) {
+        if (il->incs[i] != 0) {
+            igraph_vector_destroy(il->incs[i]);
+            igraph_Free(il->incs[i]);
+        }
+    }
+}
+
+igraph_vector_t *igraph_lazy_inclist_get_real(igraph_lazy_inclist_t *il,
+        igraph_integer_t pno) {
+    igraph_integer_t no = pno;
+    int ret;
+    if (il->incs[no] == 0) {
+        il->incs[no] = igraph_Calloc(1, igraph_vector_t);
+        if (il->incs[no] == 0) {
+            igraph_error("Lazy incidence list query failed", __FILE__, __LINE__,
+                         IGRAPH_ENOMEM);
+        }
+        ret = igraph_vector_init(il->incs[no], 0);
+        if (ret != 0) {
+            igraph_error("", __FILE__, __LINE__, ret);
+        }
+        ret = igraph_incident(il->graph, il->incs[no], no, il->mode);
+        if (ret != 0) {
+            igraph_error("", __FILE__, __LINE__, ret);
+        }
+    }
+    return il->incs[no];
+}
diff --git a/igraph/src/arithchk.c b/igraph/src/arithchk.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/arithchk.c
@@ -0,0 +1,262 @@
+/****************************************************************
+Copyright (C) 1997, 1998, 2000 Lucent Technologies
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name of Lucent or any of its entities
+not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+/* Try to deduce arith.h from arithmetic properties. */
+
+#include <stdio.h>
+#include <math.h>
+#include <errno.h>
+
+#ifdef NO_FPINIT
+#define fpinit_ASL()
+#else
+#ifndef KR_headers
+extern
+#ifdef __cplusplus
+	"C"
+#endif
+	void fpinit_ASL(void);
+#endif /*KR_headers*/
+#endif /*NO_FPINIT*/
+
+ static int dalign;
+ typedef struct
+Akind {
+	char *name;
+	int   kind;
+	} Akind;
+
+ static Akind
+IEEE_8087	= { "IEEE_8087", 1 },
+IEEE_MC68k	= { "IEEE_MC68k", 2 },
+IBM		= { "IBM", 3 },
+VAX		= { "VAX", 4 },
+CRAY		= { "CRAY", 5};
+
+ static double t_nan;
+
+ static Akind *
+Lcheck(void)
+{
+	union {
+		double d;
+		long L[2];
+		} u;
+	struct {
+		double d;
+		long L;
+		} x[2];
+
+	if (sizeof(x) > 2*(sizeof(double) + sizeof(long)))
+		dalign = 1;
+	u.L[0] = u.L[1] = 0;
+	u.d = 1e13;
+	if (u.L[0] == 1117925532 && u.L[1] == -448790528)
+		return &IEEE_MC68k;
+	if (u.L[1] == 1117925532 && u.L[0] == -448790528)
+		return &IEEE_8087;
+	if (u.L[0] == -2065213935 && u.L[1] == 10752)
+		return &VAX;
+	if (u.L[0] == 1267827943 && u.L[1] == 704643072)
+		return &IBM;
+	return 0;
+	}
+
+ static Akind *
+icheck(void)
+{
+	union {
+		double d;
+		int L[2];
+		} u;
+	struct {
+		double d;
+		int L;
+		} x[2];
+
+	if (sizeof(x) > 2*(sizeof(double) + sizeof(int)))
+		dalign = 1;
+	u.L[0] = u.L[1] = 0;
+	u.d = 1e13;
+	if (u.L[0] == 1117925532 && u.L[1] == -448790528)
+		return &IEEE_MC68k;
+	if (u.L[1] == 1117925532 && u.L[0] == -448790528)
+		return &IEEE_8087;
+	if (u.L[0] == -2065213935 && u.L[1] == 10752)
+		return &VAX;
+	if (u.L[0] == 1267827943 && u.L[1] == 704643072)
+		return &IBM;
+	return 0;
+	}
+
+/* avoid possible warning message with printf("") */
+const char *const emptyfmt = "";
+
+#ifdef __GNUC__
+#  pragma GCC diagnostic push
+#  ifndef __clang__
+#    pragma GCC diagnostic ignored "-Wformat-security"
+#    pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#  else
+#    pragma GCC diagnostic ignored "-Wformat-zero-length"
+#  endif
+#endif
+
+ static Akind *
+ccheck(void)
+{
+	union {
+		double d;
+		long L;
+		} u;
+	long Cray1;
+
+	/* Cray1 = 4617762693716115456 -- without overflow on non-Crays */
+	Cray1 = printf(emptyfmt) < 0 ? 0 : 4617762;
+	if (printf(emptyfmt, Cray1) >= 0)
+		Cray1 = 1000000*Cray1 + 693716;
+	if (printf(emptyfmt, Cray1) >= 0)
+		Cray1 = 1000000*Cray1 + 115456;
+	u.d = 1e13;
+	if (u.L == Cray1)
+		return &CRAY;
+	return 0;
+	}
+
+ static int
+fzcheck(void)
+{
+	double a, b;
+	int i;
+
+	a = 1.;
+	b = .1;
+	for(i = 155;; b *= b, i >>= 1) {
+		if (i & 1) {
+			a *= b;
+			if (i == 1)
+				break;
+			}
+		}
+	b = a * a;
+	return b == 0.;
+	}
+
+ static int
+need_nancheck(void)
+{
+	double t;
+
+	errno = 0;
+	t = log(t_nan);
+	if (errno == 0)
+		return 1;
+	errno = 0;
+	t = sqrt(t_nan);
+	return errno == 0;
+	}
+
+#ifdef __GNUC__
+#  ifndef __clang__
+#    pragma GCC diagnostic pop
+#  endif
+#endif
+
+ void
+get_nanbits(unsigned int *b, int k)
+{
+	union { double d; unsigned int z[2]; } u, u1, u2;
+
+	k = 2 - k;
+	u1.z[k] = u2.z[k] = 0x7ff00000;
+	u1.z[1-k] = u2.z[1-k] = 0;
+	u.d = u1.d - u2.d;	/* Infinity - Infinity */
+	b[0] = u.z[0];
+	b[1] = u.z[1];
+	}
+
+ int
+main(void)
+{
+	FILE *f;
+	Akind *a = 0;
+	int Ldef = 0;
+	unsigned int nanbits[2];
+
+	fpinit_ASL();
+#ifdef WRITE_ARITH_H	/* for Symantec's buggy "make" */
+	f = fopen("arith.h", "w");
+	if (!f) {
+		printf("Cannot open arith.h\n");
+		return 1;
+		}
+#else
+	f = stdout;
+#endif
+
+	if (sizeof(double) == 2*sizeof(long))
+		a = Lcheck();
+	else if (sizeof(double) == 2*sizeof(int)) {
+		Ldef = 1;
+		a = icheck();
+		}
+	else if (sizeof(double) == sizeof(long))
+		a = ccheck();
+	if (a) {
+		fprintf(f, "#define %s\n#define Arith_Kind_ASL %d\n",
+			a->name, a->kind);
+		if (Ldef)
+			fprintf(f, "#define Long int\n#define Intcast (int)(long)\n");
+		if (dalign)
+			fprintf(f, "#define Double_Align\n");
+		if (sizeof(char*) == 8)
+			fprintf(f, "#define X64_bit_pointers\n");
+#ifndef NO_LONG_LONG
+		if (sizeof(long long) < 8)
+#endif
+			fprintf(f, "#define NO_LONG_LONG\n");
+		if (a->kind <= 2) {
+			if (fzcheck())
+				fprintf(f, "#define Sudden_Underflow\n");
+			t_nan = -a->kind;
+			if (need_nancheck())
+				fprintf(f, "#define NANCHECK\n");
+			if (sizeof(double) == 2*sizeof(unsigned int)) {
+				get_nanbits(nanbits, a->kind);
+				fprintf(f, "#define QNaN0 0x%x\n", nanbits[0]);
+				fprintf(f, "#define QNaN1 0x%x\n", nanbits[1]);
+				}
+			}
+		return 0;
+		}
+	fprintf(f, "/* Unknown arithmetic */\n");
+	return 1;
+	}
+
+#ifdef __sun
+#ifdef __i386
+/* kludge for Intel Solaris */
+void fpsetprec(int x) { }
+#endif
+#endif
diff --git a/igraph/src/arpack.c b/igraph/src/arpack.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/arpack.c
@@ -0,0 +1,1429 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 noet: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_arpack.h"
+#include "igraph_arpack_internal.h"
+#include "igraph_memory.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+/* The ARPACK example file dssimp.f is used as a template */
+
+int igraph_i_arpack_err_dsaupd(int error) {
+    switch (error) {
+    case  1:      return IGRAPH_ARPACK_MAXIT;
+    case  3:      return IGRAPH_ARPACK_NOSHIFT;
+    case -1:      return IGRAPH_ARPACK_NPOS;
+    case -2:      return IGRAPH_ARPACK_NEVNPOS;
+    case -3:      return IGRAPH_ARPACK_NCVSMALL;
+    case -4:      return IGRAPH_ARPACK_NONPOSI;
+    case -5:      return IGRAPH_ARPACK_WHICHINV;
+    case -6:      return IGRAPH_ARPACK_BMATINV;
+    case -7:      return IGRAPH_ARPACK_WORKLSMALL;
+    case -8:      return IGRAPH_ARPACK_TRIDERR;
+    case -9:      return IGRAPH_ARPACK_ZEROSTART;
+    case -10:     return IGRAPH_ARPACK_MODEINV;
+    case -11:     return IGRAPH_ARPACK_MODEBMAT;
+    case -12:     return IGRAPH_ARPACK_ISHIFT;
+    case -13:     return IGRAPH_ARPACK_NEVBE;
+    case -9999:   return IGRAPH_ARPACK_NOFACT;
+    default:      return IGRAPH_ARPACK_UNKNOWN;
+    }
+}
+
+int igraph_i_arpack_err_dseupd(int error) {
+    switch (error) {
+    case -1:      return IGRAPH_ARPACK_NPOS;
+    case -2:      return IGRAPH_ARPACK_NEVNPOS;
+    case -3:      return IGRAPH_ARPACK_NCVSMALL;
+    case -5:      return IGRAPH_ARPACK_WHICHINV;
+    case -6:      return IGRAPH_ARPACK_BMATINV;
+    case -7:      return IGRAPH_ARPACK_WORKLSMALL;
+    case -8:      return IGRAPH_ARPACK_TRIDERR;
+    case -9:      return IGRAPH_ARPACK_ZEROSTART;
+    case -10:     return IGRAPH_ARPACK_MODEINV;
+    case -11:     return IGRAPH_ARPACK_MODEBMAT;
+    case -12:     return IGRAPH_ARPACK_NEVBE;
+    case -14:     return IGRAPH_ARPACK_FAILED;
+    case -15:     return IGRAPH_ARPACK_HOWMNY;
+    case -16:     return IGRAPH_ARPACK_HOWMNYS;
+    case -17:     return IGRAPH_ARPACK_EVDIFF;
+    default:      return IGRAPH_ARPACK_UNKNOWN;
+    }
+
+}
+
+int igraph_i_arpack_err_dnaupd(int error) {
+    switch (error) {
+    case  1:      return IGRAPH_ARPACK_MAXIT;
+    case  3:      return IGRAPH_ARPACK_NOSHIFT;
+    case -1:      return IGRAPH_ARPACK_NPOS;
+    case -2:      return IGRAPH_ARPACK_NEVNPOS;
+    case -3:      return IGRAPH_ARPACK_NCVSMALL;
+    case -4:      return IGRAPH_ARPACK_NONPOSI;
+    case -5:      return IGRAPH_ARPACK_WHICHINV;
+    case -6:      return IGRAPH_ARPACK_BMATINV;
+    case -7:      return IGRAPH_ARPACK_WORKLSMALL;
+    case -8:      return IGRAPH_ARPACK_TRIDERR;
+    case -9:      return IGRAPH_ARPACK_ZEROSTART;
+    case -10:     return IGRAPH_ARPACK_MODEINV;
+    case -11:     return IGRAPH_ARPACK_MODEBMAT;
+    case -12:     return IGRAPH_ARPACK_ISHIFT;
+    case -9999:   return IGRAPH_ARPACK_NOFACT;
+    default:      return IGRAPH_ARPACK_UNKNOWN;
+    }
+}
+
+int igraph_i_arpack_err_dneupd(int error) {
+    switch (error) {
+    case  1:      return IGRAPH_ARPACK_REORDER;
+    case -1:      return IGRAPH_ARPACK_NPOS;
+    case -2:      return IGRAPH_ARPACK_NEVNPOS;
+    case -3:      return IGRAPH_ARPACK_NCVSMALL;
+    case -5:      return IGRAPH_ARPACK_WHICHINV;
+    case -6:      return IGRAPH_ARPACK_BMATINV;
+    case -7:      return IGRAPH_ARPACK_WORKLSMALL;
+    case -8:      return IGRAPH_ARPACK_SHUR;
+    case -9:      return IGRAPH_ARPACK_LAPACK;
+    case -10:     return IGRAPH_ARPACK_MODEINV;
+    case -11:     return IGRAPH_ARPACK_MODEBMAT;
+    case -12:     return IGRAPH_ARPACK_HOWMNYS;
+    case -13:     return IGRAPH_ARPACK_HOWMNY;
+    case -14:     return IGRAPH_ARPACK_FAILED;
+    case -15:     return IGRAPH_ARPACK_EVDIFF;
+    default:      return IGRAPH_ARPACK_UNKNOWN;
+    }
+}
+
+/**
+ * \function igraph_arpack_options_init
+ * Initialize ARPACK options
+ *
+ * Initializes ARPACK options, set them to default values.
+ * You can always pass the initialized \ref igraph_arpack_options_t
+ * object to built-in igraph functions without any modification. The
+ * built-in igraph functions modify the options to perform their
+ * calculation, e.g. \ref igraph_pagerank() always searches for the
+ * eigenvalue with the largest magnitude, regardless of the supplied
+ * value.
+ * </para><para>
+ * If you want to implement your own function involving eigenvalue
+ * calculation using ARPACK, however, you will likely need to set up
+ * the fields for yourself.
+ * \param o The \ref igraph_arpack_options_t object to initialize.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_arpack_options_init(igraph_arpack_options_t *o) {
+    o->bmat[0] = 'I';
+    o->n = 0;         /* needs to be updated! */
+    o->which[0] = 'X'; o->which[1] = 'X';
+    o->nev = 1;
+    o->tol = 0;
+    o->ncv = 0;       /* 0 means "automatic" */
+    o->ldv = o->n;        /* will be updated to (real) n */
+    o->ishift = 1;
+    o->mxiter = 3000;
+    o->nb = 1;
+    o->mode = 1;
+    o->start = 0;
+    o->lworkl = 0;
+    o->sigma = 0;
+    o->sigmai = 0;
+    o->info = o->start;
+
+    o->iparam[0] = o->ishift; o->iparam[1] = 0; o->iparam[2] = o->mxiter; o->iparam[3] = o->nb;
+    o->iparam[4] = 0; o->iparam[5] = 0; o->iparam[6] = o->mode; o->iparam[7] = 0;
+    o->iparam[8] = 0; o->iparam[9] = 0; o->iparam[10] = 0;
+}
+
+/**
+ * \function igraph_arpack_storage_init
+ * Initialize ARPACK storage
+ *
+ * You only need this function if you want to run multiple eigenvalue
+ * calculations using ARPACK, and want to spare the memory
+ * allocation/deallocation between each two runs. Otherwise it is safe
+ * to supply a null pointer as the \c storage argument of both \ref
+ * igraph_arpack_rssolve() and \ref igraph_arpack_rnsolve() to make
+ * memory allocated and deallocated automatically.
+ *
+ * </para><para>Don't forget to call the \ref
+ * igraph_arpack_storage_destroy() function on the storage object if
+ * you don't need it any more.
+ * \param s The \ref igraph_arpack_storage_t object to initialize.
+ * \param maxn The maximum order of the matrices.
+ * \param maxncv The maximum NCV parameter intended to use.
+ * \param maxldv The maximum LDV parameter intended to use.
+ * \param symm Whether symmetric or non-symmetric problems will be
+ *    solved using this \ref igraph_arpack_storage_t. (You cannot use
+ *    the same storage both with symmetric and non-symmetric solvers.)
+ * \return Error code.
+ *
+ * Time complexity: O(maxncv*(maxldv+maxn)).
+ */
+
+int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn,
+                               long int maxncv, long int maxldv,
+                               igraph_bool_t symm) {
+
+    /* TODO: check arguments */
+    s->maxn = (int) maxn;
+    s->maxncv = (int) maxncv;
+    s->maxldv = (int) maxldv;
+
+#define CHECKMEM(x) \
+    if (!x) { \
+        IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \
+    } \
+    IGRAPH_FINALLY(igraph_free, x);
+
+    s->v = igraph_Calloc(maxldv * maxncv, igraph_real_t); CHECKMEM(s->v);
+    s->workd = igraph_Calloc(3 * maxn, igraph_real_t); CHECKMEM(s->workd);
+    s->d = igraph_Calloc(2 * maxncv, igraph_real_t); CHECKMEM(s->d);
+    s->resid = igraph_Calloc(maxn, igraph_real_t); CHECKMEM(s->resid);
+    s->ax = igraph_Calloc(maxn, igraph_real_t); CHECKMEM(s->ax);
+    s->select = igraph_Calloc(maxncv, int); CHECKMEM(s->select);
+
+    if (symm) {
+        s->workl = igraph_Calloc(maxncv * (maxncv + 8), igraph_real_t); CHECKMEM(s->workl);
+        s->di = 0;
+        s->workev = 0;
+    } else {
+        s->workl = igraph_Calloc(3 * maxncv * (maxncv + 2), igraph_real_t); CHECKMEM(s->workl);
+        s->di = igraph_Calloc(2 * maxncv, igraph_real_t); CHECKMEM(s->di);
+        s->workev = igraph_Calloc(3 * maxncv, igraph_real_t); CHECKMEM(s->workev);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+#undef CHECKMEM
+
+    IGRAPH_FINALLY_CLEAN(7);
+    return 0;
+}
+
+/**
+ * \function igraph_arpack_storage_destroy
+ * Deallocate ARPACK storage
+ *
+ * \param s The \ref igraph_arpack_storage_t object for which the
+ *    memory will be deallocated.
+ *
+ * Time complexity: operating system dependent.
+ */
+
+void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s) {
+
+    if (s->di) {
+        igraph_Free(s->di);
+    }
+    if (s->workev) {
+        igraph_Free(s->workev);
+    }
+
+    igraph_Free(s->workl);
+    igraph_Free(s->select);
+    igraph_Free(s->ax);
+    igraph_Free(s->resid);
+    igraph_Free(s->d);
+    igraph_Free(s->workd);
+    igraph_Free(s->v);
+}
+
+/**
+ * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with
+ * these.
+ */
+int igraph_i_arpack_rssolve_1x1(igraph_arpack_function_t *fun, void *extra,
+                                igraph_arpack_options_t* options,
+                                igraph_vector_t* values, igraph_matrix_t* vectors) {
+    igraph_real_t a, b;
+    int nev = options->nev;
+
+    if (nev <= 0) {
+        IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS);
+    }
+
+    /* Probe the value in the matrix */
+    a = 1;
+    if (fun(&b, &a, 1, extra)) {
+        IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product",
+                     IGRAPH_ARPACK_PROD);
+    }
+
+    options->nconv = nev;
+
+    if (values != 0) {
+        IGRAPH_CHECK(igraph_vector_resize(values, 1));
+        VECTOR(*values)[0] = b;
+    }
+
+    if (vectors != 0) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, 1, 1));
+        MATRIX(*vectors, 0, 0) = 1;
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with
+ * these.
+ */
+int igraph_i_arpack_rnsolve_1x1(igraph_arpack_function_t *fun, void *extra,
+                                igraph_arpack_options_t* options,
+                                igraph_matrix_t* values, igraph_matrix_t* vectors) {
+    igraph_real_t a, b;
+    int nev = options->nev;
+
+    if (nev <= 0) {
+        IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS);
+    }
+
+    /* Probe the value in the matrix */
+    a = 1;
+    if (fun(&b, &a, 1, extra)) {
+        IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product",
+                     IGRAPH_ARPACK_PROD);
+    }
+
+    options->nconv = nev;
+
+    if (values != 0) {
+        IGRAPH_CHECK(igraph_matrix_resize(values, 1, 2));
+        MATRIX(*values, 0, 0) = b; MATRIX(*values, 0, 1) = 0;
+    }
+
+    if (vectors != 0) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, 1, 1));
+        MATRIX(*vectors, 0, 0) = 1;
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * "Solver" for 2x2 nonsymmetric eigenvalue problems since ARPACK sometimes
+ * blows up with these.
+ */
+int igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extra,
+                                igraph_arpack_options_t* options, igraph_matrix_t* values,
+                                igraph_matrix_t* vectors) {
+    igraph_real_t vec[2], mat[4];
+    igraph_real_t a, b, c, d;
+    igraph_real_t trace, det, tsq4_minus_d;
+    igraph_complex_t eval1, eval2;
+    igraph_complex_t evec1[2], evec2[2];
+    igraph_bool_t swap_evals = 0;
+    igraph_bool_t complex_evals = 0;
+    int nev = options->nev;
+
+    if (nev <= 0) {
+        IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS);
+    }
+    if (nev > 2) {
+        nev = 2;
+    }
+
+    /* Probe the values in the matrix */
+    vec[0] = 1; vec[1] = 0;
+    if (fun(mat, vec, 2, extra)) {
+        IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product",
+                     IGRAPH_ARPACK_PROD);
+    }
+    vec[0] = 0; vec[1] = 1;
+    if (fun(mat + 2, vec, 2, extra)) {
+        IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product",
+                     IGRAPH_ARPACK_PROD);
+    }
+    a = mat[0]; b = mat[2]; c = mat[1]; d = mat[3];
+
+    /* Get the trace and the determinant */
+    trace = a + d;
+    det = a * d - b * c;
+    tsq4_minus_d = trace * trace / 4 - det;
+
+    /* Calculate the eigenvalues */
+    complex_evals = tsq4_minus_d < 0;
+    eval1 = igraph_complex_sqrt_real(tsq4_minus_d);
+    if (complex_evals) {
+        eval2 = igraph_complex_mul_real(eval1, -1);
+    } else {
+        /* to avoid having -0 in the imaginary part */
+        eval2 = igraph_complex(-IGRAPH_REAL(eval1), 0);
+    }
+    eval1 = igraph_complex_add_real(eval1, trace / 2);
+    eval2 = igraph_complex_add_real(eval2, trace / 2);
+
+    if (c != 0) {
+        evec1[0] = igraph_complex_sub_real(eval1, d);
+        evec1[1] = igraph_complex(c, 0);
+        evec2[0] = igraph_complex_sub_real(eval2, d);
+        evec2[1] = igraph_complex(c, 0);
+    } else if (b != 0) {
+        evec1[0] = igraph_complex(b, 0);
+        evec1[1] = igraph_complex_sub_real(eval1, a);
+        evec2[0] = igraph_complex(b, 0);
+        evec2[1] = igraph_complex_sub_real(eval2, a);
+    } else {
+        evec1[0] = igraph_complex(1, 0);
+        evec1[1] = igraph_complex(0, 0);
+        evec2[0] = igraph_complex(0, 0);
+        evec2[1] = igraph_complex(1, 0);
+    }
+
+    /* Sometimes we have to swap eval1 with eval2 and evec1 with eval2;
+     * determine whether we have to do it now */
+    if (options->which[0] == 'S') {
+        if (options->which[1] == 'M') {
+            /* eval1 must be the one with the smallest magnitude */
+            swap_evals = (igraph_complex_mod(eval1) > igraph_complex_mod(eval2));
+        } else if (options->which[1] == 'R') {
+            /* eval1 must be the one with the smallest real part */
+            swap_evals = (IGRAPH_REAL(eval1) > IGRAPH_REAL(eval2));
+        } else if (options->which[1] == 'I') {
+            /* eval1 must be the one with the smallest imaginary part */
+            swap_evals = (IGRAPH_IMAG(eval1) > IGRAPH_IMAG(eval2));
+        } else {
+            IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV);
+        }
+    } else if (options->which[0] == 'L') {
+        if (options->which[1] == 'M') {
+            /* eval1 must be the one with the largest magnitude */
+            swap_evals = (igraph_complex_mod(eval1) < igraph_complex_mod(eval2));
+        } else if (options->which[1] == 'R') {
+            /* eval1 must be the one with the largest real part */
+            swap_evals = (IGRAPH_REAL(eval1) < IGRAPH_REAL(eval2));
+        } else if (options->which[1] == 'I') {
+            /* eval1 must be the one with the largest imaginary part */
+            swap_evals = (IGRAPH_IMAG(eval1) < IGRAPH_IMAG(eval2));
+        } else {
+            IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV);
+        }
+    } else if (options->which[0] == 'X' && options->which[1] == 'X') {
+        /* No preference on the ordering of eigenvectors */
+    } else {
+        /* fprintf(stderr, "%c%c\n", options->which[0], options->which[1]); */
+        IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV);
+    }
+
+    options->nconv = nev;
+
+    if (swap_evals) {
+        igraph_complex_t dummy;
+        dummy = eval1; eval1 = eval2; eval2 = dummy;
+        dummy = evec1[0]; evec1[0] = evec2[0]; evec2[0] = dummy;
+        dummy = evec1[1]; evec1[1] = evec2[1]; evec2[1] = dummy;
+    }
+
+    if (complex_evals) {
+        /* The eigenvalues are conjugate pairs, so we store only the
+         * one with positive imaginary part */
+        if (IGRAPH_IMAG(eval1) < 0) {
+            eval1 = eval2;
+            evec1[0] = evec2[0]; evec1[1] = evec2[1];
+        }
+    }
+
+    if (values != 0) {
+        IGRAPH_CHECK(igraph_matrix_resize(values, nev, 2));
+        MATRIX(*values, 0, 0) = IGRAPH_REAL(eval1);
+        MATRIX(*values, 0, 1) = IGRAPH_IMAG(eval1);
+        if (nev > 1) {
+            MATRIX(*values, 1, 0) = IGRAPH_REAL(eval2);
+            MATRIX(*values, 1, 1) = IGRAPH_IMAG(eval2);
+        }
+    }
+
+    if (vectors != 0) {
+        if (complex_evals) {
+            IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, 2));
+            MATRIX(*vectors, 0, 0) = IGRAPH_REAL(evec1[0]);
+            MATRIX(*vectors, 1, 0) = IGRAPH_REAL(evec1[1]);
+            MATRIX(*vectors, 0, 1) = IGRAPH_IMAG(evec1[0]);
+            MATRIX(*vectors, 1, 1) = IGRAPH_IMAG(evec1[1]);
+        } else {
+            IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, nev));
+            MATRIX(*vectors, 0, 0) = IGRAPH_REAL(evec1[0]);
+            MATRIX(*vectors, 1, 0) = IGRAPH_REAL(evec1[1]);
+            if (nev > 1) {
+                MATRIX(*vectors, 0, 1) = IGRAPH_REAL(evec2[0]);
+                MATRIX(*vectors, 1, 1) = IGRAPH_REAL(evec2[1]);
+            }
+        }
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * "Solver" for symmetric 2x2 eigenvalue problems since ARPACK sometimes blows
+ * up with these.
+ */
+int igraph_i_arpack_rssolve_2x2(igraph_arpack_function_t *fun, void *extra,
+                                igraph_arpack_options_t* options, igraph_vector_t* values,
+                                igraph_matrix_t* vectors) {
+    igraph_real_t vec[2], mat[4];
+    igraph_real_t a, b, c, d;
+    igraph_real_t trace, det, tsq4_minus_d;
+    igraph_real_t eval1, eval2;
+    int nev = options->nev;
+
+    if (nev <= 0) {
+        IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS);
+    }
+    if (nev > 2) {
+        nev = 2;
+    }
+
+    /* Probe the values in the matrix */
+    vec[0] = 1; vec[1] = 0;
+    if (fun(mat, vec, 2, extra)) {
+        IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product",
+                     IGRAPH_ARPACK_PROD);
+    }
+    vec[0] = 0; vec[1] = 1;
+    if (fun(mat + 2, vec, 2, extra)) {
+        IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product",
+                     IGRAPH_ARPACK_PROD);
+    }
+    a = mat[0]; b = mat[2]; c = mat[1]; d = mat[3];
+
+    /* Get the trace and the determinant */
+    trace = a + d;
+    det = a * d - b * c;
+    tsq4_minus_d = trace * trace / 4 - det;
+
+    if (tsq4_minus_d >= 0) {
+        /* Both eigenvalues are real */
+        eval1 = trace / 2 + sqrt(tsq4_minus_d);
+        eval2 = trace / 2 - sqrt(tsq4_minus_d);
+        if (c != 0) {
+            mat[0] = eval1 - d; mat[2] = eval2 - d;
+            mat[1] = c;       mat[3] = c;
+        } else if (b != 0) {
+            mat[0] = b;       mat[2] = b;
+            mat[1] = eval1 - a; mat[3] = eval2 - a;
+        } else {
+            mat[0] = 1; mat[2] = 0;
+            mat[1] = 0; mat[3] = 1;
+        }
+    } else {
+        /* Both eigenvalues are complex. Should not happen with symmetric
+         * matrices. */
+        IGRAPH_ERROR("ARPACK error, 2x2 matrix is not symmetric", IGRAPH_EINVAL);
+    }
+
+    /* eval1 is always the larger eigenvalue. If we want the smaller
+     * one, we have to swap eval1 with eval2 and also the columns of mat */
+    if (options->which[0] == 'S') {
+        trace = eval1; eval1 = eval2; eval2 = trace;
+        trace = mat[0]; mat[0] = mat[2]; mat[2] = trace;
+        trace = mat[1]; mat[1] = mat[3]; mat[3] = trace;
+    } else if (options->which[0] == 'L' || options->which[0] == 'B') {
+        /* Nothing to do here */
+    } else if (options->which[0] == 'X' && options->which[1] == 'X') {
+        /* No preference on the ordering of eigenvectors */
+    } else {
+        IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV);
+    }
+
+    options->nconv = nev;
+
+    if (values != 0) {
+        IGRAPH_CHECK(igraph_vector_resize(values, nev));
+        VECTOR(*values)[0] = eval1;
+        if (nev > 1) {
+            VECTOR(*values)[1] = eval2;
+        }
+    }
+
+    if (vectors != 0) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, nev));
+        MATRIX(*vectors, 0, 0) = mat[0];
+        MATRIX(*vectors, 1, 0) = mat[1];
+        if (nev > 1) {
+            MATRIX(*vectors, 0, 1) = mat[2];
+            MATRIX(*vectors, 1, 1) = mat[3];
+        }
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_arpack_rssort(igraph_vector_t *values, igraph_matrix_t *vectors,
+                         const igraph_arpack_options_t *options,
+                         igraph_real_t *d, const igraph_real_t *v) {
+
+    igraph_vector_t order;
+    char sort[2];
+    int apply = 1;
+    unsigned int n = (unsigned int) options->n;
+    int nconv = options->nconv;
+    int nev = options->nev;
+    unsigned int nans = (unsigned int) (nconv < nev ? nconv : nev);
+
+#define which(a,b) (options->which[0]==a && options->which[1]==b)
+
+    if (which('L', 'A')) {
+        sort[0] = 'S'; sort[1] = 'A';
+    } else if (which('S', 'A')) {
+        sort[0] = 'L'; sort[1] = 'A';
+    } else if (which('L', 'M')) {
+        sort[0] = 'S'; sort[1] = 'M';
+    } else if (which('S', 'M')) {
+        sort[0] = 'L'; sort[1] = 'M';
+    } else if (which('B', 'E')) {
+        sort[0] = 'L'; sort[1] = 'A';
+    }
+
+    IGRAPH_CHECK(igraph_vector_init_seq(&order, 0, nconv - 1));
+    IGRAPH_FINALLY(igraph_vector_destroy, &order);
+#ifdef HAVE_GFORTRAN
+    igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order), /*which_len=*/ 2);
+#else
+    igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order));
+#endif
+
+    /* BE is special */
+    if (which('B', 'E')) {
+        int w = 0, l1 = 0, l2 = nev - 1;
+        igraph_vector_t order2, d2;
+        IGRAPH_VECTOR_INIT_FINALLY(&order2, nev);
+        IGRAPH_VECTOR_INIT_FINALLY(&d2, nev);
+        while (l1 <= l2) {
+            VECTOR(order2)[w] = VECTOR(order)[l1];
+            VECTOR(d2)[w] = d[l1];
+            w++; l1++;
+            if (l1 <= l2) {
+                VECTOR(order2)[w] = VECTOR(order)[l2];
+                VECTOR(d2)[w] = d[l2];
+                w++; l2--;
+            }
+        }
+        igraph_vector_update(&order, &order2);
+        igraph_vector_copy_to(&d2, d);
+        igraph_vector_destroy(&order2);
+        igraph_vector_destroy(&d2);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+#undef which
+
+    /* Copy values */
+    if (values) {
+        IGRAPH_CHECK(igraph_vector_resize(values, nans));
+        memcpy(VECTOR(*values), d, sizeof(igraph_real_t) * nans);
+    }
+
+    /* Reorder vectors */
+    if (vectors) {
+        int i;
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, n, nans));
+        for (i = 0; i < nans; i++) {
+            unsigned int idx = (unsigned int) VECTOR(order)[i];
+            const igraph_real_t *ptr = v + n * idx;
+            memcpy(&MATRIX(*vectors, 0, i), ptr, sizeof(igraph_real_t) * n);
+        }
+    }
+
+    igraph_vector_destroy(&order);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *vectors,
+                         const igraph_arpack_options_t *options,
+                         igraph_real_t *dr, igraph_real_t *di,
+                         igraph_real_t *v) {
+
+    igraph_vector_t order;
+    char sort[2];
+    int apply = 1, i;
+    unsigned int n = (unsigned int) options->n;
+    int nconv = options->nconv;
+    int nev = options->nev;
+    unsigned int nans = (unsigned int) (nconv < nev ? nconv : nev);
+
+#define which(a,b) (options->which[0]==a && options->which[1]==b)
+
+    if (which('L', 'M')) {
+        sort[0] = 'S'; sort[1] = 'M';
+    } else if (which('S', 'M')) {
+        sort[0] = 'L'; sort[1] = 'M';
+    } else if (which('L', 'R')) {
+        sort[0] = 'S'; sort[1] = 'R';
+    } else if (which('S', 'R')) {
+        sort[0] = 'L'; sort[1] = 'R';
+    } else if (which('L', 'I')) {
+        sort[0] = 'S'; sort[1] = 'I';
+    } else if (which('S', 'I')) {
+        sort[0] = 'L'; sort[1] = 'I';
+    }
+
+#undef which
+
+    IGRAPH_CHECK(igraph_vector_init_seq(&order, 0, nconv - 1));
+    IGRAPH_FINALLY(igraph_vector_destroy, &order);
+#ifdef HAVE_GFORTRAN
+    igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order), /*which_len=*/ 2);
+#else
+    igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order));
+#endif
+
+    if (values) {
+        IGRAPH_CHECK(igraph_matrix_resize(values, nans, 2));
+        memcpy(&MATRIX(*values, 0, 0), dr, sizeof(igraph_real_t) * nans);
+        memcpy(&MATRIX(*values, 0, 1), di, sizeof(igraph_real_t) * nans);
+    }
+
+    if (vectors) {
+        int nc = 0, nr = 0, ncol, vx = 0;
+        for (i = 0; i < nans; i++) {
+            if (di[i] == 0) {
+                nr++;
+            } else {
+                nc++;
+            }
+        }
+        ncol = (nc / 2) * 2 + (nc % 2) * 2 + nr;
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, n, ncol));
+
+        for (i = 0; i < nans; i++) {
+            unsigned int idx;
+
+            idx = (unsigned int) VECTOR(order)[i];
+
+            if (di[i] == 0) {
+                /* real eigenvalue, single eigenvector */
+                memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * n);
+                vx++;
+            } else if (di[i] > 0) {
+                /* complex eigenvalue, positive imaginary part encountered first.
+                 * ARPACK stores its eigenvector directly in two consecutive columns.
+                 * The complex conjugate pair of the eigenvalue (if any) will be in
+                 * the next column and we will skip it because we advance 'i' below */
+                memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * 2 * n);
+                vx += 2;
+                i++;
+            } else {
+                /* complex eigenvalue, negative imaginary part encountered first.
+                     * The positive one will be the next one, but we need to copy the
+                     * eigenvector corresponding to the eigenvalue with the positive
+                     * imaginary part. */
+                idx = (unsigned int) VECTOR(order)[i + 1];
+                memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * 2 * n);
+                vx += 2;
+                i++;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&order);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    if (values) {
+        /* Strive to include complex conjugate eigenvalue pairs in a way that the
+         * positive imaginary part comes first */
+        for (i = 0; i < nans; i++) {
+            if (MATRIX(*values, i, 1) == 0) {
+                /* Real eigenvalue, nothing to do */
+            } else if (MATRIX(*values, i, 1) < 0) {
+                /* Negative imaginary part came first; negate the imaginary part for
+                 * this eigenvalue and the next one (which is the complex conjugate
+                 * pair), and skip it */
+                MATRIX(*values, i, 1) *= -1;
+                i++;
+                if (i < nans) {
+                    MATRIX(*values, i, 1) *= -1;
+                }
+            } else {
+                /* Positive imaginary part; skip the next eigenvalue, which is the
+                 * complex conjugate pair */
+                i++;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_i_arpack_auto_ncv
+ * \brief Tries to set up the value of \c ncv in an \c igraph_arpack_options_t
+ *        automagically.
+ */
+void igraph_i_arpack_auto_ncv(igraph_arpack_options_t* options) {
+    /* This is similar to how Octave determines the value of ncv, with some
+     * modifications. */
+    int min_ncv = options->nev * 2 + 1;
+
+    /* Use twice the number of desired eigenvectors plus one by default */
+    options->ncv = min_ncv;
+    /* ...but use at least 20 Lanczos vectors... */
+    if (options->ncv < 20) {
+        options->ncv = 20;
+    }
+    /* ...but having ncv close to n leads to some problems with small graphs
+     * (example: PageRank of "A <--> C, D <--> E, B"), so we don't let it
+     * to be larger than n / 2...
+     */
+    if (options->ncv > options->n / 2) {
+        options->ncv = options->n / 2;
+    }
+    /* ...but we need at least min_ncv. */
+    if (options->ncv < min_ncv) {
+        options->ncv = min_ncv;
+    }
+    /* ...but at most n */
+    if (options->ncv > options->n) {
+        options->ncv = options->n;
+    }
+}
+
+/**
+ * \function igraph_i_arpack_report_no_convergence
+ * \brief Prints a warning that informs the user that the ARPACK solver
+ *        did not converge.
+ */
+void igraph_i_arpack_report_no_convergence(const igraph_arpack_options_t* options) {
+    char buf[1024];
+    snprintf(buf, sizeof(buf), "ARPACK solver failed to converge (%d iterations, "
+             "%d/%d eigenvectors converged)", options->iparam[2],
+             options->iparam[4], options->nev);
+    IGRAPH_WARNING(buf);
+}
+
+/**
+ * \function igraph_arpack_rssolve
+ * \brief ARPACK solver for symmetric matrices
+ *
+ * This is the ARPACK solver for symmetric matrices. Please use
+ * \ref igraph_arpack_rnsolve() for non-symmetric matrices.
+ * \param fun Pointer to an \ref igraph_arpack_function_t object,
+ *     the function that performs the matrix-vector multiplication.
+ * \param extra An extra argument to be passed to \c fun.
+ * \param options An \ref igraph_arpack_options_t object.
+ * \param storage An \ref igraph_arpack_storage_t object, or a null
+ *     pointer. In the latter case memory allocation and deallocation
+ *     is performed automatically. Either this or the \p vectors argument
+ *     must be non-null if the ARPACK iteration is started from a
+ *     given starting vector. If both are given \p vectors take
+ *     precedence.
+ * \param values If not a null pointer, then it should be a pointer to an
+ *     initialized vector. The eigenvalues will be stored here. The
+ *     vector will be resized as needed.
+ * \param vectors If not a null pointer, then it must be a pointer to
+ *     an initialized matrix. The eigenvectors will be stored in the
+ *     columns of the matrix. The matrix will be resized as needed.
+ *     Either this or the \p vectors argument must be non-null if the
+ *     ARPACK iteration is started from a given starting vector. If
+ *     both are given \p vectors take precedence.
+ * \return Error code.
+ *
+ * Time complexity: depends on the matrix-vector
+ * multiplication. Usually a small number of iterations is enough, so
+ * if the matrix is sparse and the matrix-vector multiplication can be
+ * done in O(n) time (the number of vertices), then the eigenvalues
+ * are found in O(n) time as well.
+ */
+
+int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra,
+                          igraph_arpack_options_t *options,
+                          igraph_arpack_storage_t *storage,
+                          igraph_vector_t *values, igraph_matrix_t *vectors) {
+
+    igraph_real_t *v, *workl, *workd, *d, *resid, *ax;
+    igraph_bool_t free_them = 0;
+    int *select, i;
+
+    int ido = 0;
+    int rvec = vectors || storage ? 1 : 0; /* calculate eigenvectors? */
+    char *all = "All";
+
+    int origldv = options->ldv, origlworkl = options->lworkl,
+        orignev = options->nev, origncv = options->ncv;
+    char origwhich[2] = { options->which[0], options->which[1] };
+    igraph_real_t origtol = options->tol;
+
+    /* Special case for 1x1 and 2x2 matrices in mode 1 */
+    if (options->mode == 1 && options->n == 1) {
+        return igraph_i_arpack_rssolve_1x1(fun, extra, options, values, vectors);
+    } else if (options->mode == 1 && options->n == 2) {
+        return igraph_i_arpack_rssolve_2x2(fun, extra, options, values, vectors);
+    }
+
+    /* Brush up options if needed */
+    if (options->ldv == 0) {
+        options->ldv = options->n;
+    }
+    if (options->ncv == 0) {
+        igraph_i_arpack_auto_ncv(options);
+    }
+    if (options->lworkl == 0) {
+        options->lworkl = options->ncv * (options->ncv + 8);
+    }
+    if (options->which[0] == 'X') {
+        options->which[0] = 'L';
+        options->which[1] = 'M';
+    }
+
+    if (storage) {
+        /* Storage provided */
+        if (storage->maxn < options->n) {
+            IGRAPH_ERROR("Not enough storage for ARPACK (`n')", IGRAPH_EINVAL);
+        }
+        if (storage->maxncv < options->ncv) {
+            IGRAPH_ERROR("Not enough storage for ARPACK (`ncv')", IGRAPH_EINVAL);
+        }
+        if (storage->maxldv < options->ldv) {
+            IGRAPH_ERROR("Not enough storage for ARPACK (`ldv')", IGRAPH_EINVAL);
+        }
+
+        v      = storage->v;
+        workl  = storage->workl;
+        workd  = storage->workd;
+        d      = storage->d;
+        resid  = storage->resid;
+        ax     = storage->ax;
+        select = storage->select;
+
+    } else {
+        /* Storage not provided */
+        free_them = 1;
+
+#define CHECKMEM(x) \
+    if (!x) { \
+        IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \
+    } \
+    IGRAPH_FINALLY(igraph_free, x);
+
+        v = igraph_Calloc(options->ldv * options->ncv, igraph_real_t); CHECKMEM(v);
+        workl = igraph_Calloc(options->lworkl, igraph_real_t); CHECKMEM(workl);
+        workd = igraph_Calloc(3 * options->n, igraph_real_t); CHECKMEM(workd);
+        d = igraph_Calloc(2 * options->ncv, igraph_real_t); CHECKMEM(d);
+        resid = igraph_Calloc(options->n, igraph_real_t); CHECKMEM(resid);
+        ax = igraph_Calloc(options->n, igraph_real_t); CHECKMEM(ax);
+        select = igraph_Calloc(options->ncv, int); CHECKMEM(select);
+
+#undef CHECKMEM
+
+    }
+
+    /* Set final bits */
+    options->bmat[0] = 'I';
+    options->iparam[0] = options->ishift;
+    options->iparam[1] = 0;   // not referenced
+    options->iparam[2] = options->mxiter;
+    options->iparam[3] = 1;   // currently dsaupd() works only for nb=1
+    options->iparam[4] = 0;
+    options->iparam[5] = 0;   // not referenced
+    options->iparam[6] = options->mode;
+    options->iparam[7] = 0;   // return value
+    options->iparam[8] = 0;   // return value
+    options->iparam[9] = 0;   // return value
+    options->iparam[10] = 0;  // return value
+    options->info = options->start;
+    if (options->start) {
+        if (!storage && !vectors) {
+            IGRAPH_ERROR("Starting vector not given", IGRAPH_EINVAL);
+        }
+        if (vectors && (igraph_matrix_nrow(vectors) != options->n ||
+                        igraph_matrix_ncol(vectors) != 1)) {
+            IGRAPH_ERROR("Invalid starting vector size", IGRAPH_EINVAL);
+        }
+        if (vectors) {
+            for (i = 0; i < options->n; i++) {
+                resid[i] = MATRIX(*vectors, i, 0);
+            }
+        }
+    }
+
+    /* Ok, we have everything */
+    while (1) {
+#ifdef HAVE_GFORTRAN
+        igraphdsaupd_(&ido, options->bmat, &options->n, options->which,
+                      &options->nev, &options->tol,
+                      resid, &options->ncv, v, &options->ldv,
+                      options->iparam, options->ipntr,
+                      workd, workl, &options->lworkl, &options->info,
+                      /*bmat_len=*/ 1, /*which_len=*/ 2);
+#else
+        igraphdsaupd_(&ido, options->bmat, &options->n, options->which,
+                      &options->nev, &options->tol,
+                      resid, &options->ncv, v, &options->ldv,
+                      options->iparam, options->ipntr,
+                      workd, workl, &options->lworkl, &options->info);
+#endif
+
+        if (ido == -1 || ido == 1) {
+            igraph_real_t *from = workd + options->ipntr[0] - 1;
+            igraph_real_t *to = workd + options->ipntr[1] - 1;
+            if (fun(to, from, options->n, extra) != 0) {
+                IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product",
+                             IGRAPH_ARPACK_PROD);
+            }
+
+        } else {
+            break;
+        }
+    }
+
+    if (options->info == 1) {
+        igraph_i_arpack_report_no_convergence(options);
+    }
+    if (options->info != 0) {
+        IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dsaupd(options->info));
+    }
+
+    options->ierr = 0;
+#ifdef HAVE_GFORTRAN
+    igraphdseupd_(&rvec, all, select, d, v, &options->ldv,
+                  &options->sigma, options->bmat, &options->n,
+                  options->which, &options->nev, &options->tol,
+                  resid, &options->ncv, v, &options->ldv, options->iparam,
+                  options->ipntr, workd, workl, &options->lworkl,
+                  &options->ierr, /*howmny_len=*/ 1, /*bmat_len=*/ 1,
+                  /*which_len=*/ 2);
+#else
+    igraphdseupd_(&rvec, all, select, d, v, &options->ldv,
+                  &options->sigma, options->bmat, &options->n,
+                  options->which, &options->nev, &options->tol,
+                  resid, &options->ncv, v, &options->ldv, options->iparam,
+                  options->ipntr, workd, workl, &options->lworkl,
+                  &options->ierr);
+#endif
+
+    if (options->ierr != 0) {
+        IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dseupd(options->ierr));
+    }
+
+    /* Save the result */
+
+    options->noiter = options->iparam[2];
+    options->nconv = options->iparam[4];
+    options->numop = options->iparam[8];
+    options->numopb = options->iparam[9];
+    options->numreo = options->iparam[10];
+
+    if (options->nconv < options->nev) {
+        IGRAPH_WARNING("Not enough eigenvalues/vectors in symmetric ARPACK "
+                       "solver");
+    }
+
+    if (values || vectors) {
+        IGRAPH_CHECK(igraph_arpack_rssort(values, vectors, options, d, v));
+    }
+
+    options->ldv = origldv;
+    options->ncv = origncv;
+    options->lworkl = origlworkl;
+    options->which[0] = origwhich[0]; options->which[1] = origwhich[1];
+    options->tol = origtol;
+    options->nev = orignev;
+
+    /* Clean up if needed */
+    if (free_them) {
+        igraph_Free(select);
+        igraph_Free(ax);
+        igraph_Free(resid);
+        igraph_Free(d);
+        igraph_Free(workd);
+        igraph_Free(workl);
+        igraph_Free(v);
+        IGRAPH_FINALLY_CLEAN(7);
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_arpack_rnsolve
+ * \brief ARPACK solver for non-symmetric matrices
+ *
+ * Please always consider calling \ref igraph_arpack_rssolve() if your
+ * matrix is symmetric, it is much faster.
+ * \ref igraph_arpack_rnsolve() for non-symmetric matrices.
+ * </para><para>
+ * Note that ARPACK is not called for 2x2 matrices as an exact algebraic
+ * solution exists in these cases.
+ *
+ * \param fun Pointer to an \ref igraph_arpack_function_t object,
+ *     the function that performs the matrix-vector multiplication.
+ * \param extra An extra argument to be passed to \c fun.
+ * \param options An \ref igraph_arpack_options_t object.
+ * \param storage An \ref igraph_arpack_storage_t object, or a null
+ *     pointer. In the latter case memory allocation and deallocation
+ *     is performed automatically.
+ * \param values If not a null pointer, then it should be a pointer to an
+ *     initialized matrix. The (possibly complex) eigenvalues will be
+ *     stored here. The matrix will have two columns, the first column
+ *     contains the real, the second the imaginary parts of the
+ *     eigenvalues.
+ *     The matrix will be resized as needed.
+ * \param vectors If not a null pointer, then it must be a pointer to
+ *     an initialized matrix. The eigenvectors will be stored in the
+ *     columns of the matrix. The matrix will be resized as needed.
+ *     Note that real eigenvalues will have real eigenvectors in a single
+ *     column in this matrix; however, complex eigenvalues come in conjugate
+ *     pairs and the result matrix will store the eigenvector corresponding to
+ *     the eigenvalue with \em positive imaginary part only. Since in this case
+ *     the eigenvector is also complex, it will occupy \em two columns in the
+ *     eigenvector matrix (the real and the imaginary parts, in this order).
+ *     Caveat: if the eigenvalue vector returns only the eigenvalue with the
+ *     \em negative imaginary part for a complex conjugate eigenvalue pair, the
+ *     result vector will \em still store the eigenvector corresponding to the
+ *     eigenvalue with the positive imaginary part (since this is how ARPACK
+ *     works).
+ * \return Error code.
+ *
+ * Time complexity: depends on the matrix-vector
+ * multiplication. Usually a small number of iterations is enough, so
+ * if the matrix is sparse and the matrix-vector multiplication can be
+ * done in O(n) time (the number of vertices), then the eigenvalues
+ * are found in O(n) time as well.
+ */
+
+int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra,
+                          igraph_arpack_options_t *options,
+                          igraph_arpack_storage_t *storage,
+                          igraph_matrix_t *values, igraph_matrix_t *vectors) {
+
+    igraph_real_t *v, *workl, *workd, *dr, *di, *resid, *workev;
+    igraph_bool_t free_them = 0;
+    int *select, i;
+
+    int ido = 0;
+    int rvec = vectors || storage ? 1 : 0;
+    char *all = "All";
+
+    int origldv = options->ldv, origlworkl = options->lworkl,
+        orignev = options->nev, origncv = options->ncv;
+    char origwhich[2] = { options->which[0], options->which[1] };
+    igraph_real_t origtol = options->tol;
+    int d_size;
+
+    /* Special case for 1x1 and 2x2 matrices in mode 1 */
+    if (options->mode == 1 && options->n == 1) {
+        return igraph_i_arpack_rnsolve_1x1(fun, extra, options, values, vectors);
+    } else if (options->mode == 1 && options->n == 2) {
+        return igraph_i_arpack_rnsolve_2x2(fun, extra, options, values, vectors);
+    }
+
+    /* Brush up options if needed */
+    if (options->ldv == 0) {
+        options->ldv = options->n;
+    }
+    if (options->ncv == 0) {
+        igraph_i_arpack_auto_ncv(options);
+    }
+    if (options->lworkl == 0) {
+        options->lworkl = 3 * options->ncv * (options->ncv + 2);
+    }
+    if (options->which[0] == 'X') {
+        options->which[0] = 'L';
+        options->which[1] = 'M';
+    }
+
+    if (storage) {
+        /* Storage provided */
+        if (storage->maxn < options->n) {
+            IGRAPH_ERROR("Not enough storage for ARPACK (`n')", IGRAPH_EINVAL);
+        }
+        if (storage->maxncv < options->ncv) {
+            IGRAPH_ERROR("Not enough storage for ARPACK (`ncv')", IGRAPH_EINVAL);
+        }
+        if (storage->maxldv < options->ldv) {
+            IGRAPH_ERROR("Not enough storage for ARPACK (`ldv')", IGRAPH_EINVAL);
+        }
+
+        v      = storage->v;
+        workl  = storage->workl;
+        workd  = storage->workd;
+        workev = storage->workev;
+        dr     = storage->d;
+        di     = storage->di;
+        d_size = options->n;
+        resid  = storage->resid;
+        select = storage->select;
+
+    } else {
+        /* Storage not provided */
+        free_them = 1;
+
+#define CHECKMEM(x) \
+    if (!x) { \
+        IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \
+    } \
+    IGRAPH_FINALLY(igraph_free, x);
+
+        v = igraph_Calloc(options->n * options->ncv, igraph_real_t); CHECKMEM(v);
+        workl = igraph_Calloc(options->lworkl, igraph_real_t); CHECKMEM(workl);
+        workd = igraph_Calloc(3 * options->n, igraph_real_t); CHECKMEM(workd);
+        d_size = 2 * options->nev + 1 > options->ncv ? 2 * options->nev + 1 : options->ncv;
+        dr = igraph_Calloc(d_size, igraph_real_t); CHECKMEM(dr);
+        di = igraph_Calloc(d_size, igraph_real_t); CHECKMEM(di);
+        resid = igraph_Calloc(options->n, igraph_real_t); CHECKMEM(resid);
+        select = igraph_Calloc(options->ncv, int); CHECKMEM(select);
+        workev = igraph_Calloc(3 * options->ncv, igraph_real_t); CHECKMEM(workev);
+
+#undef CHECKMEM
+
+    }
+
+    /* Set final bits */
+    options->bmat[0] = 'I';
+    options->iparam[0] = options->ishift;
+    options->iparam[1] = 0;   // not referenced
+    options->iparam[2] = options->mxiter;
+    options->iparam[3] = 1;   // currently dnaupd() works only for nb=1
+    options->iparam[4] = 0;
+    options->iparam[5] = 0;   // not referenced
+    options->iparam[6] = options->mode;
+    options->iparam[7] = 0;   // return value
+    options->iparam[8] = 0;   // return value
+    options->iparam[9] = 0;   // return value
+    options->iparam[10] = 0;  // return value
+    options->info = options->start;
+    if (options->start) {
+        if (igraph_matrix_nrow(vectors) != options->n || igraph_matrix_ncol(vectors) != 1) {
+            IGRAPH_ERROR("Invalid starting vector size", IGRAPH_EINVAL);
+        }
+        for (i = 0; i < options->n; i++) {
+            resid[i] = MATRIX(*vectors, i, 0);
+        }
+    }
+
+    /* Ok, we have everything */
+    while (1) {
+#ifdef HAVE_GFORTRAN
+        igraphdnaupd_(&ido, options->bmat, &options->n, options->which,
+                      &options->nev, &options->tol,
+                      resid, &options->ncv, v, &options->ldv,
+                      options->iparam, options->ipntr,
+                      workd, workl, &options->lworkl, &options->info,
+                      /*bmat_len=*/ 1, /*which_len=*/ 2);
+#else
+        igraphdnaupd_(&ido, options->bmat, &options->n, options->which,
+                      &options->nev, &options->tol,
+                      resid, &options->ncv, v, &options->ldv,
+                      options->iparam, options->ipntr,
+                      workd, workl, &options->lworkl, &options->info);
+#endif
+
+        if (ido == -1 || ido == 1) {
+            igraph_real_t *from = workd + options->ipntr[0] - 1;
+            igraph_real_t *to = workd + options->ipntr[1] - 1;
+            if (fun(to, from, options->n, extra) != 0) {
+                IGRAPH_ERROR("ARPACK error while evaluating matrix-vector product",
+                             IGRAPH_ARPACK_PROD);
+            }
+        } else {
+            break;
+        }
+    }
+
+    if (options->info == 1) {
+        igraph_i_arpack_report_no_convergence(options);
+    }
+    if (options->info != 0 && options->info != -9999) {
+        IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dnaupd(options->info));
+    }
+
+    options->ierr = 0;
+#ifdef HAVE_GFORTRAN
+    igraphdneupd_(&rvec, all, select, dr, di, v, &options->ldv,
+                  &options->sigma, &options->sigmai, workev, options->bmat,
+                  &options->n, options->which, &options->nev, &options->tol,
+                  resid, &options->ncv, v, &options->ldv, options->iparam,
+                  options->ipntr, workd, workl, &options->lworkl,
+                  &options->ierr, /*howmny_len=*/ 1, /*bmat_len=*/ 1,
+                  /*which_len=*/ 2);
+#else
+    igraphdneupd_(&rvec, all, select, dr, di, v, &options->ldv,
+                  &options->sigma, &options->sigmai, workev, options->bmat,
+                  &options->n, options->which, &options->nev, &options->tol,
+                  resid, &options->ncv, v, &options->ldv, options->iparam,
+                  options->ipntr, workd, workl, &options->lworkl,
+                  &options->ierr);
+#endif
+
+    if (options->ierr != 0) {
+        IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dneupd(options->info));
+    }
+
+    /* Save the result */
+
+    options->noiter = options->iparam[2];
+    options->nconv = options->iparam[4];
+    options->numop = options->iparam[8];
+    options->numopb = options->iparam[9];
+    options->numreo = options->iparam[10];
+
+    if (options->nconv < options->nev) {
+        IGRAPH_WARNING("Not enough eigenvalues/vectors in ARPACK "
+                       "solver");
+    }
+
+    /* ARPACK might modify stuff in 'options' so reset everything that could
+     * potentially get modified */
+    options->ldv = origldv;
+    options->ncv = origncv;
+    options->lworkl = origlworkl;
+    options->which[0] = origwhich[0]; options->which[1] = origwhich[1];
+    options->tol = origtol;
+    options->nev = orignev;
+
+    if (values || vectors) {
+        IGRAPH_CHECK(igraph_arpack_rnsort(values, vectors, options,
+                                          dr, di, v));
+    }
+
+    /* Clean up if needed */
+    if (free_them) {
+        igraph_Free(workev);
+        igraph_Free(select);
+        igraph_Free(resid);
+        igraph_Free(di);
+        igraph_Free(dr);
+        igraph_Free(workd);
+        igraph_Free(workl);
+        igraph_Free(v);
+        IGRAPH_FINALLY_CLEAN(8);
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_arpack_unpack_complex
+ * \brief Make the result of the non-symmetric ARPACK solver more readable
+ *
+ * This function works on the output of \ref igraph_arpack_rnsolve and
+ * brushes it up a bit: it only keeps \p nev eigenvalues/vectors and
+ * every eigenvector is stored in two columns of the \p vectors
+ * matrix.
+ *
+ * </para><para>
+ * The output of the non-symmetric ARPACK solver is somewhat hard to
+ * parse, as real eigenvectors occupy only one column in the matrix,
+ * and the complex conjugate eigenvectors are not stored at all
+ * (usually). The other problem is that the solver might return more
+ * eigenvalues than requested. The common use of this function is to
+ * call it directly after \ref igraph_arpack_rnsolve with its \p
+ * vectors and \p values argument and \c options->nev as \p nev.
+ * \param vectors The eigenvector matrix, as returned by \ref
+ *   igraph_arpack_rnsolve. It will be resized, typically it will be
+ *   larger.
+ * \param values The eigenvalue matrix, as returned by \ref
+ *   igraph_arpack_rnsolve. It will be resized, typically extra,
+ *   unneeded rows (=eigenvalues) will be removed.
+ * \param nev The number of eigenvalues/vectors to keep. Can be less
+ *   or equal than the number originally requested from ARPACK.
+ * \return Error code.
+ *
+ * Time complexity: linear in the number of elements in the \p vectors
+ * matrix.
+ */
+
+int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values,
+                                 long int nev) {
+
+    long int nodes = igraph_matrix_nrow(vectors);
+    long int no_evs = igraph_matrix_nrow(values);
+    long int i, j, k, wh;
+    size_t colsize = (unsigned) nodes * sizeof(igraph_real_t);
+
+    /* Error checks */
+    if (nev < 0) {
+        IGRAPH_ERROR("`nev' cannot be negative", IGRAPH_EINVAL);
+    }
+    if (nev > no_evs) {
+        IGRAPH_ERROR("`nev' too large, we don't have that many in `values'",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(vectors, nodes, nev * 2));
+    for (i = nev; i < igraph_matrix_nrow(values); i++) {
+        IGRAPH_CHECK(igraph_matrix_remove_row(values, i));
+    }
+
+    /* Calculate where to start copying */
+    for (i = 0, j = 0, wh = 0; i < nev; i++) {
+        if (MATRIX(*values, i, 1) == 0) { /* TODO: == 0.0 ???? */
+            /* real */
+            j++;
+        } else {
+            /* complex */
+            if (wh == 0) {
+                j += 2;
+                wh = 1 - wh;
+            }
+        }
+    }
+    j--;
+
+    /* if (j>=origcol) { */
+    /*   IGRAPH_WARNING("Too few columns in `vectors', ARPACK results are likely wrong"); */
+    /* } */
+
+    /* We copy the j-th eigenvector to the (k-1)-th and k-th column */
+    k = nev * 2 - 1;
+
+    for (i = nev - 1; i >= 0; i--) {
+        if (MATRIX(*values, i, 1) == 0) {
+
+            /* real */
+            memset( &MATRIX(*vectors, 0, k), 0, colsize);
+            if (k - 1 != j) {
+                memcpy( &MATRIX(*vectors, 0, k - 1), &MATRIX(*vectors, 0, j), colsize);
+            }
+            k -= 2;
+            j -= 1;
+        } else {
+            /* complex */
+            if (k != j) {
+                /* Separate copy required, otherwise 'from' and 'to' might
+                   overlap */
+                memcpy( &MATRIX(*vectors, 0, k), &MATRIX(*vectors, 0, j), colsize);
+                memcpy( &MATRIX(*vectors, 0, k - 1), &MATRIX(*vectors, 0, j - 1), colsize);
+            }
+            if (i > 1 && MATRIX(*values, i, 1) != -MATRIX(*values, i - 1, 1)) {
+                /* The next one is not a conjugate of this one */
+                j -= 2;
+            } else {
+                /* Conjugate */
+                int l;
+                for (l = 0; l < nodes; l++) {
+                    MATRIX(*vectors, l, k) = - MATRIX(*vectors, l, k);
+                }
+            }
+            k -= 2;
+        }
+    }
+
+    return 0;
+}
diff --git a/igraph/src/array.c b/igraph/src/array.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/array.c
@@ -0,0 +1,50 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_array.h"
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "array.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "array.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "array.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "array.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
diff --git a/igraph/src/atlas.c b/igraph/src/atlas.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/atlas.c
@@ -0,0 +1,82 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph R package.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_constructors.h"
+#include "atlas-edges.h"
+#include "config.h"
+
+/**
+ * \function igraph_atlas
+ * \brief Create a small graph from the \quote Graph Atlas \endquote.
+ *
+ * </para><para>
+ * The number of the graph is given as a parameter.
+ * The graphs are listed: \olist
+ *      \oli in increasing order of number of nodes;
+ *      \oli for a fixed number of nodes, in increasing order of the
+ *           number of edges;
+ *      \oli for fixed numbers of nodes and edges, in increasing
+ *           order of the degree sequence, for example 111223 &lt; 112222;
+ *      \oli for fixed degree sequence, in increasing number of
+ *           automorphisms.
+ *      \endolist
+ *
+ * </para><para>
+ * The data was converted from the NetworkX software package,
+ * see http://networkx.github.io .
+ *
+ * </para><para>
+ * See \emb An Atlas of Graphs \eme by Ronald C. Read and Robin J. Wilson,
+ * Oxford University Press, 1998.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param number The number of the graph to generate.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number of
+ * edges.
+ *
+ * \example examples/simple/igraph_atlas.c
+ */
+int igraph_atlas(igraph_t *graph, int number) {
+
+    igraph_integer_t pos, n, e;
+    igraph_vector_t v = IGRAPH_VECTOR_NULL;
+
+    if (number < 0 ||
+        number >= (int) (sizeof(igraph_i_atlas_edges_pos) / sizeof(long int))) {
+        IGRAPH_ERROR("No such graph in atlas", IGRAPH_EINVAL);
+    }
+
+    pos = (igraph_integer_t) igraph_i_atlas_edges_pos[number];
+    n = (igraph_integer_t) igraph_i_atlas_edges[pos];
+    e = (igraph_integer_t) igraph_i_atlas_edges[pos + 1];
+
+    IGRAPH_CHECK(igraph_create(graph,
+                               igraph_vector_view(&v, igraph_i_atlas_edges + pos + 2,
+                                       e * 2),
+                               n, IGRAPH_UNDIRECTED));
+
+    return 0;
+}
diff --git a/igraph/src/attributes.c b/igraph/src/attributes.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/attributes.c
@@ -0,0 +1,442 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_attributes.h"
+#include "igraph_memory.h"
+#include "config.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+/* Should you ever want to have a thread-local attribute handler table, prepend
+ * IGRAPH_THREAD_LOCAL to the following declaration */
+igraph_attribute_table_t *igraph_i_attribute_table = 0;
+
+int igraph_i_attribute_init(igraph_t *graph, void *attr) {
+    graph->attr = 0;
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->init(graph, attr);
+    } else {
+        return 0;
+    }
+}
+
+void igraph_i_attribute_destroy(igraph_t *graph) {
+    if (igraph_i_attribute_table) {
+        igraph_i_attribute_table->destroy(graph);
+    }
+}
+
+int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga,
+                            igraph_bool_t va, igraph_bool_t ea) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->copy(to, from, ga, va, ea);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->add_vertices(graph, nv, attr);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_permute_vertices(const igraph_t *graph,
+                                        igraph_t *newgraph,
+                                        const igraph_vector_t *idx) {
+
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_combine_vertices(const igraph_t *graph,
+                                        igraph_t *newgraph,
+                                        const igraph_vector_ptr_t *merges,
+                                        const igraph_attribute_combination_t *comb) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->combine_vertices(graph, newgraph,
+                merges,
+                comb);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_add_edges(igraph_t *graph,
+                                 const igraph_vector_t *edges, void *attr) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->add_edges(graph, edges, attr);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_permute_edges(const igraph_t *graph,
+                                     igraph_t *newgraph,
+                                     const igraph_vector_t *idx) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->permute_edges(graph, newgraph, idx);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_combine_edges(const igraph_t *graph,
+                                     igraph_t *newgraph,
+                                     const igraph_vector_ptr_t *merges,
+                                     const igraph_attribute_combination_t *comb) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->combine_edges(graph, newgraph,
+                merges,
+                comb);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_info(const igraph_t *graph,
+                                igraph_strvector_t *gnames,
+                                igraph_vector_t *gtypes,
+                                igraph_strvector_t *vnames,
+                                igraph_vector_t *vtypes,
+                                igraph_strvector_t *enames,
+                                igraph_vector_t *etypes) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_info(graph, gnames, gtypes,
+                vnames, vtypes,
+                enames, etypes);
+    } else {
+        return 0;
+    }
+}
+
+igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph,
+        igraph_attribute_elemtype_t type,
+        const char *name) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->has_attr(graph, type, name);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_gettype(const igraph_t *graph,
+                               igraph_attribute_type_t *type,
+                               igraph_attribute_elemtype_t elemtype,
+                               const char *name) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->gettype(graph, type, elemtype, name);
+    } else {
+        return 0;
+    }
+
+}
+
+int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vector_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_numeric_graph_attr(graph, name, value);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_vector_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_numeric_vertex_attr(graph, name, vs, value);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_vector_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_numeric_edge_attr(graph, name, es, value);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_strvector_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_string_graph_attr(graph, name, value);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_strvector_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_string_vertex_attr(graph, name, vs, value);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_strvector_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_string_edge_attr(graph, name, es, value);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vector_bool_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_bool_graph_attr(graph, name, value);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_vector_bool_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_bool_vertex_attr(graph, name, vs, value);
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_vector_bool_t *value) {
+    if (igraph_i_attribute_table) {
+        return igraph_i_attribute_table->get_bool_edge_attr(graph, name, es, value);
+    } else {
+        return 0;
+    }
+}
+
+/**
+ * \function igraph_i_set_attribute_table
+ * \brief Attach an attribute table.
+ *
+ * This function attaches attribute handling code to the igraph library.
+ * Note that the attribute handler table is \em not thread-local even if
+ * igraph is compiled in thread-local mode. In the vast majority of cases,
+ * this is not a significant restriction.
+ *
+ * \param table Pointer to an \ref igraph_attribute_table_t object
+ *    containing the functions for attribute manipulation. Supply \c
+ *    NULL here if you don't want attributes.
+ * \return Pointer to the old attribute handling table.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_attribute_table_t *
+igraph_i_set_attribute_table(const igraph_attribute_table_t * table) {
+    igraph_attribute_table_t *old = igraph_i_attribute_table;
+    igraph_i_attribute_table = (igraph_attribute_table_t*) table;
+    return old;
+}
+
+igraph_bool_t igraph_has_attribute_table() {
+    return igraph_i_attribute_table != 0;
+}
+
+int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) {
+    IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0));
+    return 0;
+}
+
+void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) {
+    long int i, n = igraph_vector_ptr_size(&comb->list);
+    for (i = 0; i < n; i++) {
+        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
+        if (rec->name) {
+            igraph_Free(rec->name);
+        }
+        igraph_Free(rec);
+    }
+    igraph_vector_ptr_destroy(&comb->list);
+}
+
+int igraph_attribute_combination_add(igraph_attribute_combination_t *comb,
+                                     const char *name,
+                                     igraph_attribute_combination_type_t type,
+                                     igraph_function_pointer_t func) {
+    long int i, n = igraph_vector_ptr_size(&comb->list);
+
+    /* Search, in case it is already there */
+    for (i = 0; i < n; i++) {
+        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
+        const char *n = r->name;
+        if ( (!name && !n) ||
+             (name && n && !strcmp(n, name)) ) {
+            r->type = type;
+            r->func = func;
+            break;
+        }
+    }
+
+    if (i == n) {
+        /* This is a new attribute name */
+        igraph_attribute_combination_record_t *rec =
+            igraph_Calloc(1, igraph_attribute_combination_record_t);
+
+        if (!rec) {
+            IGRAPH_ERROR("Cannot create attribute combination data",
+                         IGRAPH_ENOMEM);
+        }
+        if (!name) {
+            rec->name = 0;
+        } else {
+            rec->name = strdup(name);
+        }
+        rec->type = type;
+        rec->func = func;
+
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec));
+
+    }
+
+    return 0;
+}
+
+int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb,
+                                        const char *name) {
+    long int i, n = igraph_vector_ptr_size(&comb->list);
+
+    /* Search, in case it is already there */
+    for (i = 0; i < n; i++) {
+        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
+        const char *n = r->name;
+        if ( (!name && !n) ||
+             (name && n && !strcmp(n, name)) ) {
+            break;
+        }
+    }
+
+    if (i != n) {
+        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
+        if (r->name) {
+            igraph_Free(r->name);
+        }
+        igraph_Free(r);
+        igraph_vector_ptr_remove(&comb->list, i);
+    } else {
+        /* It is not there, we don't do anything */
+    }
+
+    return 0;
+}
+
+int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb,
+                                       const char *name,
+                                       igraph_attribute_combination_type_t *type,
+                                       igraph_function_pointer_t *func) {
+    long int i, def = -1, len = igraph_vector_ptr_size(&comb->list);
+
+    for (i = 0; i < len; i++) {
+        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
+        const char *n = rec->name;
+        if ( (!name && !n) ||
+             (name && n && !strcmp(n, name)) ) {
+            *type = rec->type;
+            *func = rec->func;
+            return 0;
+        }
+        if (!n) {
+            def = i;
+        }
+    }
+
+    if (def == -1) {
+        /* Did not find anything */
+        *type = IGRAPH_ATTRIBUTE_COMBINE_DEFAULT;
+        *func = 0;
+    } else {
+        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[def];
+        *type = rec->type;
+        *func = rec->func;
+    }
+
+    return 0;
+}
+
+int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) {
+
+    va_list ap;
+
+    IGRAPH_CHECK(igraph_attribute_combination_init(comb));
+
+    va_start(ap, comb);
+    while (1) {
+        igraph_function_pointer_t func = 0;
+        igraph_attribute_combination_type_t type;
+        const char *name;
+
+        name = va_arg(ap, const char *);
+
+        if (name == IGRAPH_NO_MORE_ATTRIBUTES) {
+            break;
+        }
+
+        type = (igraph_attribute_combination_type_t)va_arg(ap, int);
+        if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) {
+#if defined(__GNUC__)
+            func = va_arg(ap, void (*)(void));
+#else
+            func = va_arg(ap, void*);
+#endif
+        }
+
+        if (strlen(name) == 0) {
+            name = 0;
+        }
+
+        IGRAPH_CHECK(igraph_attribute_combination_add(comb, name, type, func));
+    }
+
+    va_end(ap);
+
+    return 0;
+}
diff --git a/igraph/src/backspac.c b/igraph/src/backspac.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/backspac.c
@@ -0,0 +1,76 @@
+#include "f2c.h"
+#include "fio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef KR_headers
+integer f_back(a) alist *a;
+#else
+integer f_back(alist *a)
+#endif
+{	unit *b;
+	OFF_T v, w, x, y, z;
+	uiolen n;
+	FILE *f;
+
+	f__curunit = b = &f__units[a->aunit];	/* curunit for error messages */
+	if(a->aunit >= MXUNIT || a->aunit < 0)
+		err(a->aerr,101,"backspace")
+	if(b->useek==0) err(a->aerr,106,"backspace")
+	if(b->ufd == NULL) {
+		fk_open(1, 1, a->aunit);
+		return(0);
+		}
+	if(b->uend==1)
+	{	b->uend=0;
+		return(0);
+	}
+	if(b->uwrt) {
+		t_runc(a);
+		if (f__nowreading(b))
+			err(a->aerr,errno,"backspace")
+		}
+	f = b->ufd;	/* may have changed in t_runc() */
+	if(b->url>0)
+	{
+		x=FTELL(f);
+		y = x % b->url;
+		if(y == 0) x--;
+		x /= b->url;
+		x *= b->url;
+		(void) FSEEK(f,x,SEEK_SET);
+		return(0);
+	}
+
+	if(b->ufmt==0)
+	{	FSEEK(f,-(OFF_T)sizeof(uiolen),SEEK_CUR);
+		fread((char *)&n,sizeof(uiolen),1,f);
+		FSEEK(f,-(OFF_T)n-2*sizeof(uiolen),SEEK_CUR);
+		return(0);
+	}
+	w = x = FTELL(f);
+	z = 0;
+ loop:
+	while(x) {
+		x -= x < 64 ? x : 64;
+		FSEEK(f,x,SEEK_SET);
+		for(y = x; y < w; y++) {
+			if (getc(f) != '\n')
+				continue;
+			v = FTELL(f);
+			if (v == w) {
+				if (z)
+					goto break2;
+				goto loop;
+				}
+			z = v;
+			}
+		err(a->aerr,(EOF),"backspace")
+		}
+ break2:
+	FSEEK(f, z, SEEK_SET);
+	return 0;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/basic_query.c b/igraph/src/basic_query.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/basic_query.c
@@ -0,0 +1,64 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_datatype.h"
+#include "igraph_types.h"
+#include "igraph_interface.h"
+#include "igraph_structural.h"
+#include "config.h"
+
+/**
+ * \ingroup structural
+ * \function igraph_are_connected
+ * \brief Decides whether two vertices are connected
+ *
+ * \param graph The graph object.
+ * \param v1 The first vertex.
+ * \param v2 The second vertex.
+ * \param res Boolean, \c TRUE if there is an edge from
+ *         \p v1 to \p v2, \c FALSE otherwise.
+ * \return The error code \c IGRAPH_EINVVID is returned if an invalid
+ *         vertex ID is given.
+ *
+ * The function is of course symmetric for undirected graphs.
+ *
+ * </para><para>
+ * Time complexity: O( min(log(d1), log(d2)) ),
+ * d1 is the (out-)degree of \p v1 and d2 is the (in-)degree of \p v2.
+ */
+int igraph_are_connected(const igraph_t *graph,
+                         igraph_integer_t v1, igraph_integer_t v2,
+                         igraph_bool_t *res) {
+
+    long int nov = igraph_vcount(graph);
+    igraph_integer_t eid = -1;
+
+    if (v1 < 0 || v2 < 0 || v1 > nov - 1 || v2 > nov - 1) {
+        IGRAPH_ERROR("are connected", IGRAPH_EINVVID);
+    }
+
+    igraph_get_eid(graph, &eid, v1, v2, /*directed=*/1, /*error=*/ 0);
+    *res = (eid >= 0);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/bfgs.c b/igraph/src/bfgs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/bfgs.c
@@ -0,0 +1,223 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_nongraph.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_statusbar.h"
+#include "memory.h"
+#include "config.h"
+
+#include <math.h>
+
+/* This is from GNU R's optim.c, slightly adapted to igraph */
+
+#define stepredn    0.2
+#define acctol      0.0001
+#define reltest     10.0
+#define FALSE           0
+#define TRUE            1
+
+/*  BFGS variable-metric method, based on Pascal code
+in J.C. Nash, `Compact Numerical Methods for Computers', 2nd edition,
+converted by p2c then re-crafted by B.D. Ripley */
+
+int
+igraph_bfgs(igraph_vector_t *b, igraph_real_t *Fmin,
+            igraph_scalar_function_t fminfn, igraph_vector_function_t fmingr,
+            int maxit, int trace,
+            igraph_real_t abstol, igraph_real_t reltol, int nREPORT, void *ex,
+            igraph_integer_t *fncount, igraph_integer_t *grcount) {
+    int n = (int) igraph_vector_size(b);
+    igraph_bool_t accpoint, enough;
+    igraph_vector_t g, t, X, c;
+    igraph_matrix_t B;        /* Lmatrix really */
+    int   count, funcount, gradcount;
+    igraph_real_t f, gradproj;
+    int   i, j, ilast, iter = 0;
+    igraph_real_t s, steplength;
+    igraph_real_t D1, D2;
+
+    if (maxit <= 0) {
+        *Fmin = fminfn(b, 0, ex);
+        *fncount = 1;
+        *grcount = 0;
+        return 0;
+    }
+
+    if (nREPORT <= 0) {
+        IGRAPH_ERROR("REPORT must be > 0 (method = \"BFGS\")", IGRAPH_EINVAL);
+    }
+    IGRAPH_VECTOR_INIT_FINALLY(&g, n);
+    IGRAPH_VECTOR_INIT_FINALLY(&t, n);
+    IGRAPH_VECTOR_INIT_FINALLY(&X, n);
+    IGRAPH_VECTOR_INIT_FINALLY(&c, n);
+    IGRAPH_MATRIX_INIT_FINALLY(&B, n, n);
+    f = fminfn(b, 0, ex);
+    if (!IGRAPH_FINITE(f)) {
+        IGRAPH_ERROR("initial value in 'BFGS' is not finite", IGRAPH_DIVERGED);
+    }
+    if (trace) {
+        igraph_statusf("initial  value %f ", 0, f);
+    }
+    *Fmin = f;
+    funcount = gradcount = 1;
+    fmingr(b, 0, &g, ex);
+    iter++;
+    ilast = gradcount;
+
+    do {
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (ilast == gradcount) {
+            for (i = 0; i < n; i++) {
+                for (j = 0; j < i; j++) {
+                    MATRIX(B, i, j) = 0.0;
+                }
+                MATRIX(B, i, i) = 1.0;
+            }
+        }
+        for (i = 0; i < n; i++) {
+            VECTOR(X)[i] = VECTOR(*b)[i];
+            VECTOR(c)[i] = VECTOR(g)[i];
+        }
+        gradproj = 0.0;
+        for (i = 0; i < n; i++) {
+            s = 0.0;
+            for (j = 0; j <= i; j++) {
+                s -= MATRIX(B, i, j) * VECTOR(g)[j];
+            }
+            for (j = i + 1; j < n; j++) {
+                s -= MATRIX(B, j, i) * VECTOR(g)[j];
+            }
+            VECTOR(t)[i] = s;
+            gradproj += s * VECTOR(g)[i];
+        }
+
+        if (gradproj < 0.0) {   /* search direction is downhill */
+            steplength = 1.0;
+            accpoint = FALSE;
+            do {
+                count = 0;
+                for (i = 0; i < n; i++) {
+                    VECTOR(*b)[i] = VECTOR(X)[i] + steplength * VECTOR(t)[i];
+                    if (reltest + VECTOR(X)[i] == reltest + VECTOR(*b)[i]) { /* no change */
+                        count++;
+                    }
+                }
+                if (count < n) {
+                    f = fminfn(b, 0, ex);
+                    funcount++;
+                    accpoint = IGRAPH_FINITE(f) &&
+                               (f <= *Fmin + gradproj * steplength * acctol);
+                    if (!accpoint) {
+                        steplength *= stepredn;
+                    }
+                }
+            } while (!(count == n || accpoint));
+            enough = (f > abstol) &&
+                     fabs(f - *Fmin) > reltol * (fabs(*Fmin) + reltol);
+            /* stop if value if small or if relative change is low */
+            if (!enough) {
+                count = n;
+                *Fmin = f;
+            }
+            if (count < n) {/* making progress */
+                *Fmin = f;
+                fmingr(b, 0, &g, ex);
+                gradcount++;
+                iter++;
+                D1 = 0.0;
+                for (i = 0; i < n; i++) {
+                    VECTOR(t)[i] = steplength * VECTOR(t)[i];
+                    VECTOR(c)[i] = VECTOR(g)[i] - VECTOR(c)[i];
+                    D1 += VECTOR(t)[i] * VECTOR(c)[i];
+                }
+                if (D1 > 0) {
+                    D2 = 0.0;
+                    for (i = 0; i < n; i++) {
+                        s = 0.0;
+                        for (j = 0; j <= i; j++) {
+                            s += MATRIX(B, i, j) * VECTOR(c)[j];
+                        }
+                        for (j = i + 1; j < n; j++) {
+                            s += MATRIX(B, j, i) * VECTOR(c)[j];
+                        }
+                        VECTOR(X)[i] = s;
+                        D2 += s * VECTOR(c)[i];
+                    }
+                    D2 = 1.0 + D2 / D1;
+                    for (i = 0; i < n; i++) {
+                        for (j = 0; j <= i; j++)
+                            MATRIX(B, i, j) += (D2 * VECTOR(t)[i] * VECTOR(t)[j]
+                                                - VECTOR(X)[i] * VECTOR(t)[j]
+                                                - VECTOR(t)[i] * VECTOR(X)[j]) / D1;
+                    }
+                } else {    /* D1 < 0 */
+                    ilast = gradcount;
+                }
+            } else {  /* no progress */
+                if (ilast < gradcount) {
+                    count = 0;
+                    ilast = gradcount;
+                }
+            }
+        } else {        /* uphill search */
+            count = 0;
+            if (ilast == gradcount) {
+                count = n;
+            } else {
+                ilast = gradcount;
+            }
+            /* Resets unless has just been reset */
+        }
+        if (trace && (iter % nREPORT == 0)) {
+            igraph_statusf("iter%4d value %f", 0, iter, f);
+        }
+        if (iter >= maxit) {
+            break;
+        }
+        if (gradcount - ilast > 2 * n) {
+            ilast = gradcount;    /* periodic restart */
+        }
+    } while (count != n || ilast != gradcount);
+    if (trace) {
+        igraph_statusf("final  value %f ", 0, *Fmin);
+        if (iter < maxit) {
+            igraph_status("converged", 0);
+        } else {
+            igraph_statusf("stopped after %i iterations", 0, iter);
+        }
+    }
+    *fncount = funcount;
+    *grcount = gradcount;
+
+    igraph_matrix_destroy(&B);
+    igraph_vector_destroy(&c);
+    igraph_vector_destroy(&X);
+    igraph_vector_destroy(&t);
+    igraph_vector_destroy(&g);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return (iter < maxit) ? 0 : IGRAPH_DIVERGED;
+}
diff --git a/igraph/src/bigint.c b/igraph/src/bigint.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/bigint.c
@@ -0,0 +1,329 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "bigint.h"
+#include "igraph_error.h"
+#include "igraph_memory.h"
+
+int igraph_biguint_init(igraph_biguint_t *b) {
+    IGRAPH_CHECK(igraph_vector_limb_init(&b->v, IGRAPH_BIGUINT_DEFAULT_SIZE));
+    igraph_vector_limb_clear(&b->v);
+    return 0;
+}
+
+void igraph_biguint_destroy(igraph_biguint_t *b) {
+    igraph_vector_limb_destroy(&b->v);
+}
+
+int igraph_biguint_copy(igraph_biguint_t *to, igraph_biguint_t *from) {
+    return igraph_vector_limb_copy(&to->v, &from->v);
+}
+
+int igraph_biguint_extend(igraph_biguint_t *b, limb_t l) {
+    return igraph_vector_limb_push_back(&b->v, l);
+}
+
+int igraph_biguint_size(igraph_biguint_t *b) {
+    return (int) igraph_vector_limb_size(&b->v);
+}
+
+int igraph_biguint_resize(igraph_biguint_t *b, int newlength) {
+    int origlen = igraph_biguint_size(b);
+    IGRAPH_CHECK(igraph_vector_limb_resize(&b->v, newlength));
+    if (newlength > origlen) {
+        memset(VECTOR(b->v) + origlen, 0,
+               (size_t) (newlength - origlen) * sizeof(limb_t));
+    }
+    return 0;
+}
+
+int igraph_biguint_reserve(igraph_biguint_t *b, int length) {
+    return igraph_vector_limb_reserve(&b->v, length);
+}
+
+int igraph_biguint_zero(igraph_biguint_t *b) {
+    igraph_vector_limb_clear(&b->v);
+    return 0;
+}
+
+int igraph_biguint_set_limb(igraph_biguint_t *b, int value) {
+    IGRAPH_CHECK(igraph_vector_limb_resize(&b->v, 1));
+    VECTOR(b->v)[0] = (limb_t) value;
+    return 0;
+}
+
+igraph_real_t igraph_biguint_get(igraph_biguint_t *b) {
+    int size = igraph_biguint_size(b);
+    int i;
+    double val = VECTOR(b->v)[size - 1];
+    if (size == 0) {
+        return 0.0;
+    }
+    for (i = size - 2; i >= 0; i--) {
+        val = val * LIMBMASK + VECTOR(b->v)[i];
+        if (!IGRAPH_FINITE(val)) {
+            break;
+        }
+    }
+    return val;
+}
+
+int igraph_biguint_compare_limb(igraph_biguint_t *b, limb_t l) {
+    int n = igraph_biguint_size(b);
+    return bn_cmp_limb(VECTOR(b->v), l, (count_t) n);
+}
+
+int igraph_biguint_compare(igraph_biguint_t *left, igraph_biguint_t *right) {
+    /* bn_cmp requires the two numbers to have the same number of limbs,
+       so we do this partially by hand here */
+    int size_left = igraph_biguint_size(left);
+    int size_right = igraph_biguint_size(right);
+    while (size_left > size_right) {
+        if (VECTOR(left->v)[--size_left] > 0) {
+            return +1;
+        }
+    }
+    while (size_right > size_left) {
+        if (VECTOR(right->v)[--size_right] > 0) {
+            return -1;
+        }
+    }
+    return bn_cmp( VECTOR(left->v), VECTOR(right->v), (count_t) size_right );
+}
+
+
+igraph_bool_t igraph_biguint_equal(igraph_biguint_t *left, igraph_biguint_t *right) {
+    return 0 == igraph_biguint_compare(left, right);
+}
+
+
+igraph_bool_t igraph_biguint_bigger(igraph_biguint_t *left,
+                                    igraph_biguint_t *right) {
+    return 0 < igraph_biguint_compare(left, right);
+}
+
+
+igraph_bool_t igraph_biguint_biggerorequal(igraph_biguint_t *left,
+        igraph_biguint_t *right) {
+    return 0 <= igraph_biguint_compare(left, right);
+}
+
+int igraph_biguint_inc(igraph_biguint_t *res, igraph_biguint_t *b) {
+    return igraph_biguint_add_limb(res, b, 1);
+}
+
+int igraph_biguint_dec(igraph_biguint_t *res, igraph_biguint_t *b) {
+    return igraph_biguint_sub_limb(res, b, 1);
+}
+
+
+int igraph_biguint_add_limb(igraph_biguint_t *res, igraph_biguint_t *b,
+                            limb_t l) {
+    int nlimb = igraph_biguint_size(b);
+    limb_t carry;
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_biguint_resize(res, nlimb));
+    }
+
+    carry = bn_add_limb( VECTOR(res->v), VECTOR(b->v), l, (count_t) nlimb);
+    if (carry) {
+        IGRAPH_CHECK(igraph_biguint_extend(res, carry));
+    }
+    return 0;
+}
+
+int igraph_biguint_sub_limb(igraph_biguint_t *res, igraph_biguint_t *b,
+                            limb_t l) {
+    int nlimb = igraph_biguint_size(b);
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_biguint_resize(res, nlimb));
+    }
+
+    /* We don't check the return value here */
+    bn_sub_limb( VECTOR(res->v), VECTOR(b->v), l, (count_t) nlimb);
+
+    return 0;
+}
+
+int igraph_biguint_mul_limb(igraph_biguint_t *res, igraph_biguint_t *b,
+                            limb_t l) {
+    int nlimb = igraph_biguint_size(b);
+    limb_t carry;
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_biguint_resize(res, nlimb));
+    }
+
+    carry = bn_mul_limb( VECTOR(res->v), VECTOR(b->v), l, (count_t) nlimb);
+    if (carry) {
+        IGRAPH_CHECK(igraph_biguint_extend(res, carry));
+    }
+    return 0;
+}
+
+int igraph_biguint_add(igraph_biguint_t *res, igraph_biguint_t *left,
+                       igraph_biguint_t *right) {
+
+    int size_left = igraph_biguint_size(left);
+    int size_right = igraph_biguint_size(right);
+    limb_t carry;
+
+    if (size_left > size_right) {
+        IGRAPH_CHECK(igraph_biguint_resize(right, size_left));
+        size_right = size_left;
+    } else if (size_left < size_right) {
+        IGRAPH_CHECK(igraph_biguint_resize(left, size_right));
+        size_left = size_right;
+    }
+    IGRAPH_CHECK(igraph_biguint_resize(res, size_left));
+
+    carry = bn_add( VECTOR(res->v), VECTOR(left->v), VECTOR(right->v),
+                    (count_t) size_left);
+    if (carry) {
+        IGRAPH_CHECK(igraph_biguint_extend(res, carry));
+    }
+    return 0;
+}
+
+int igraph_biguint_sub(igraph_biguint_t *res, igraph_biguint_t *left,
+                       igraph_biguint_t *right) {
+
+    int size_left = igraph_biguint_size(left);
+    int size_right = igraph_biguint_size(right);
+
+    if (size_left > size_right) {
+        IGRAPH_CHECK(igraph_biguint_resize(right, size_left));
+        size_right = size_left;
+    } else if (size_left < size_right) {
+        IGRAPH_CHECK(igraph_biguint_resize(left, size_right));
+        size_left = size_right;
+    }
+    IGRAPH_CHECK(igraph_biguint_resize(res, size_left));
+
+    /* We don't check return value, left should not be smaller than right! */
+    bn_sub( VECTOR(res->v), VECTOR(left->v), VECTOR(right->v),
+            (count_t) size_left);
+
+    return 0;
+}
+
+int igraph_biguint_mul(igraph_biguint_t *res, igraph_biguint_t *left,
+                       igraph_biguint_t *right) {
+
+    int size_left = igraph_biguint_size(left);
+    int size_right = igraph_biguint_size(right);
+
+    if (size_left > size_right) {
+        IGRAPH_CHECK(igraph_biguint_resize(right, size_left));
+        size_right = size_left;
+    } else if (size_left < size_right) {
+        IGRAPH_CHECK(igraph_biguint_resize(left, size_right));
+        size_left = size_right;
+    }
+    IGRAPH_CHECK(igraph_biguint_resize(res, 2 * size_left));
+
+    bn_mul( VECTOR(res->v), VECTOR(left->v), VECTOR(right->v),
+            (count_t) size_left );
+    return 0;
+}
+
+int igraph_biguint_div(igraph_biguint_t *q, igraph_biguint_t *r,
+                       igraph_biguint_t *u, igraph_biguint_t *v) {
+
+    int ret;
+    int size_q = igraph_biguint_size(q);
+    int size_r = igraph_biguint_size(r);
+    int size_u = igraph_biguint_size(u);
+    int size_v = igraph_biguint_size(v);
+    int size_qru = size_q > size_r ? size_q : size_r;
+    size_qru = size_u > size_qru ? size_u : size_qru;
+
+    if (size_q < size_qru) {
+        IGRAPH_CHECK(igraph_biguint_resize(q, size_qru));
+    }
+    if (size_r < size_qru) {
+        IGRAPH_CHECK(igraph_biguint_resize(r, size_qru));
+    }
+    if (size_u < size_qru) {
+        IGRAPH_CHECK(igraph_biguint_resize(u, size_qru));
+    }
+
+    ret = bn_div( VECTOR(q->v), VECTOR(r->v), VECTOR(u->v), VECTOR(v->v),
+                  (count_t) size_qru, (count_t) size_v );
+
+    if (ret) {
+        IGRAPH_ERROR("Bigint division by zero", IGRAPH_EDIVZERO);
+    }
+
+    return 0;
+}
+
+#ifndef USING_R
+int igraph_biguint_print(igraph_biguint_t *b) {
+    return igraph_biguint_fprint(b, stdout);
+}
+#endif
+
+int igraph_biguint_fprint(igraph_biguint_t *b, FILE *file) {
+
+    /* It is hard to control memory allocation for the bn2d function,
+       so we do our own version */
+
+    int n = igraph_biguint_size(b);
+    long int size = 12 * n + 1;
+    igraph_biguint_t tmp;
+    char *dst;
+    limb_t r;
+
+    /* Zero? */
+    if (!bn_cmp_limb(VECTOR(b->v), 0, (count_t) n)) {
+        fputs("0", file);
+        return 0;
+    }
+
+    IGRAPH_CHECK(igraph_biguint_copy(&tmp, b));
+    IGRAPH_FINALLY(igraph_biguint_destroy, &tmp);
+    dst = igraph_Calloc(size, char);
+    if (!dst) {
+        IGRAPH_ERROR("Cannot print big number", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, dst);
+
+    size--;
+    dst[size] = '\0';
+    while (0 != bn_cmp_limb(VECTOR(tmp.v), 0, (count_t) n)) {
+        r = bn_div_limb(VECTOR(tmp.v), VECTOR(tmp.v), 10, (count_t) n);
+        dst[--size] = '0' + (char) r;
+    }
+
+    fputs(&dst[size], file);
+
+    igraph_Free(dst);
+    igraph_biguint_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
diff --git a/igraph/src/bignum.c b/igraph/src/bignum.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/bignum.c
@@ -0,0 +1,1984 @@
+/******************************************************************************
+ * bn.c - big number math implementation
+ *
+ * Copyright (c) 2004 by Juergen Buchmueller <pullmoll@stop1984.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ *
+ *  $Id: bignum.c,v 1.17 2005/07/23 02:55:53 pullmoll Exp $
+ ******************************************************************************/
+#include <math.h>
+#include "bignum.h"
+#include "config.h"
+#include "math.h"
+#include "igraph_error.h"
+
+#ifndef ASM_X86
+    #ifdef  X86
+        #define ASM_X86 1
+    #endif
+#endif
+
+/**
+ * @brief Return hex representation of a big number
+ *
+ * Returns the hex representation of a[],
+ * where a is a big number integer with nlimb limbs.
+ *
+ * @param a pointer to an array of limbs
+ * @param nlimb number of limbs in the array
+ *
+ * @result string containing the hex representation of a
+ */
+const char *bn2x(limb_t *a, count_t nlimb) {
+    static IGRAPH_THREAD_LOCAL count_t which = 0;
+    static IGRAPH_THREAD_LOCAL char *xbuff[8] = {
+        NULL, NULL, NULL, NULL,
+        NULL, NULL, NULL, NULL
+    };
+    char *dst;
+    count_t size;
+    count_t n = nlimb;
+
+    if (0 == n) {
+        return "0";
+    }
+
+    which = (which + 1) % 8;
+    size = 8 * n + 1;
+    if (NULL != xbuff[which]) {
+        free(xbuff[which]);
+    }
+    dst = xbuff[which] = calloc(size, sizeof(char));
+    if (NULL == dst) {
+        return "memory error";
+    }
+    while (n-- > 0) {
+        dst += snprintf(dst, size, "%08x", a[n]);
+        size -= 8;
+    }
+    return xbuff[which];
+}
+
+/**
+ * @brief Return decimal representation of a big number
+ *
+ * Returns the decimal representation of a[],
+ * where a is a big number integer with nlimb limbs.
+ *
+ * @param a pointer to an array of limbs
+ * @param nlimb number of limbs in the array
+ *
+ * @result string containing the decimal representation of a
+ */
+const char *bn2d(limb_t *a, count_t nlimb) {
+    static IGRAPH_THREAD_LOCAL count_t which = 0;
+    static IGRAPH_THREAD_LOCAL char *dbuff[8] = {
+        NULL, NULL, NULL, NULL,
+        NULL, NULL, NULL, NULL
+    };
+    static IGRAPH_THREAD_LOCAL limb_t v[BN_MAXSIZE];
+    limb_t r;
+    char *dst;
+    count_t size;
+    count_t n = bn_sizeof(a, nlimb);
+
+    if (0 == n) {
+        return "0";
+    }
+
+    bn_copy(v, a, n);
+    which = (which + 1) % 8;
+    size = 12 * n + 1;
+    if (NULL != dbuff[which]) {
+        free(dbuff[which]);
+    }
+    dst = dbuff[which] = calloc(size, sizeof(char));
+    if (NULL == dst) {
+        return "memory error";
+    }
+    size--;
+    while (0 != bn_cmp_limb(v, 0, n)) {
+        r = bn_div_limb(v, v, 10, n);
+        dst[--size] = '0' + (char) r;
+    }
+    return &dst[size];
+}
+
+/**
+ * @brief Return decimal representation of a big number pair
+ *
+ * Returns the decimal representation of a[].b[],
+ * where a is a big number integer with alimb limbs,
+ * and b is a multiprecision fixed fraction with blimb limbs.
+ *
+ * @param a pointer to an array of limbs
+ * @param alimb number of limbs in the a array
+ * @param b pointer to an array of limbs
+ * @param blimb number of limbs in the b array
+ *
+ * @result string containing the decimal representation of a.b
+ */
+const char *bn2f(limb_t *a, count_t alimb, limb_t *b, count_t blimb) {
+    static IGRAPH_THREAD_LOCAL count_t which = 0;
+    static IGRAPH_THREAD_LOCAL char *dbuff[8] = {
+        NULL, NULL, NULL, NULL,
+        NULL, NULL, NULL, NULL
+    };
+    static IGRAPH_THREAD_LOCAL limb_t v[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t w[BN_MAXSIZE];
+    limb_t r;
+    char *dst;
+    count_t size;
+
+    bn_copy(v, a, alimb);
+    bn_copy(w, b, blimb);
+
+    which = (which + 1) % 8;
+    size = 12 * (alimb + blimb) + 1 + 1;
+    if (NULL != dbuff[which]) {
+        free(dbuff[which]);
+    }
+    dst = dbuff[which] = calloc(size, sizeof(char));
+    if (NULL == dst) {
+        return "memory error";
+    }
+    size = 12 * alimb;
+    while (0 != bn_cmp_limb(w, 0, blimb) && size < 12 * (alimb + blimb)) {
+        r = bn_mul_limb(w, w, 10, blimb);
+        dst[size++] = '0' + (char) r;
+    }
+
+    size = 12 * alimb;
+    dst[size] = '.';
+    while (0 != bn_cmp_limb(v, 0, alimb) && size > 0) {
+        r = bn_div_limb(v, v, 10, alimb);
+        dst[--size] = '0' + (char) r;
+    }
+
+    return &dst[size];
+}
+
+/**
+ * @brief Return binary representation of a big number
+ *
+ * Returns the binary representation of a[],
+ * where a is a big number integer with nlimb limbs.
+ *
+ * @param a pointer to an array of limbs
+ * @param nlimb number of limbs in the array
+ *
+ * @result string containing the binary representation of a
+ */
+const char *bn2b(limb_t *a, count_t nlimb) {
+    static IGRAPH_THREAD_LOCAL count_t which = 0;
+    static IGRAPH_THREAD_LOCAL char *bbuff[8] = {
+        NULL, NULL, NULL, NULL,
+        NULL, NULL, NULL, NULL
+    };
+    limb_t r;
+    char *dst;
+    count_t size;
+    count_t n = bn_sizeof(a, nlimb);
+
+    if (0 == n) {
+        return "0";
+    }
+
+    which = (which + 1) % 8;
+    size = LIMBBITS * n + 1;
+    if (NULL != bbuff[which]) {
+        free(bbuff[which]);
+    }
+    dst = bbuff[which] = calloc(size, sizeof(char));
+    if (NULL == dst) {
+        return "memory error";
+    }
+    n = 0;
+    size--;
+    while (size-- > 0) {
+        r = (a[n / LIMBBITS] >> (n % LIMBBITS)) & 1;
+        n++;
+        dst[size] = '0' + (char) r;
+    }
+    return &dst[size];
+}
+
+/**
+ * @brief Zero an array of limbs
+ *
+ * Sets a[] = 0
+ * where a is a big number integer of nlimb limbs.
+ *
+ * @param a pointer to an array of limbs
+ * @param nlimb number of limbs in the array
+ *
+ */
+void bn_zero(limb_t a[], count_t nlimb) {
+    memset(a, 0, nlimb * sizeof(limb_t));
+}
+
+/**
+ * @brief Set an array of limbs to a single limb value
+ *
+ * Sets a[] = d
+ * where a is a big number integer of nlimb limbs,
+ * and d is a single limb
+ *
+ * @param a pointer to an array of limbs to set
+ * @param d limb value to set a to
+ * @param nlimb number of limbs in the array
+ *
+ */
+void bn_limb(limb_t a[], limb_t d, count_t nlimb) {
+    memset(a, 0, nlimb * sizeof(limb_t));
+    a[0] = d;
+}
+
+/**
+ * @brief Copy an array of limbs
+ *
+ * Sets a[] = b[]
+ * where a and b are a big number integers of nlimb limbs
+ *
+ * @param a pointer to an array of limbs (destination)
+ * @param b pointer to an array of limbs (source)
+ * @param nlimb number of limbs in the arrays
+ */
+void bn_copy(limb_t a[], limb_t b[], count_t nlimb) {
+    memcpy(a, b, nlimb * sizeof(limb_t));
+}
+
+/**
+ * @brief Return significant size of a big number
+ *
+ * Returns size of significant limbs in a[]
+ * i.e. searches for the first non-zero limb from
+ * nlimb-1 downto 0.
+ *
+ * @param a pointer to an array of limbs (candidate)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result number of significant limbs in a
+ */
+count_t bn_sizeof(limb_t a[], count_t nlimb) {
+    while (nlimb-- > 0)
+        if (0 != a[nlimb]) {
+            return ++nlimb;
+        }
+    return 0;
+}
+
+
+/**
+ * @brief Return sign of a bignum minus a limb
+ *
+ * Returns the sign of (a[] - b)
+ * where a is a big number integer of nlimb limbs,
+ * and b is a single limb
+ +
+ * @param a pointer to an array of limbs (minuend)
+ * @param b a single limb (subtrahend)
+ * @param nlimb number of limbs in the array a
+ *
+ * @result sign of the comparison: -1 a<b, 0 a=b, +1 a>b
+ */
+int bn_cmp_limb(limb_t a[], limb_t b, count_t nlimb) {
+    if (0 == nlimb) {
+        return 0;
+    }
+
+    while (nlimb-- > 1)
+        if (0 != a[nlimb]) {
+            return +1;
+        }
+    if (a[0] < b) {
+        return -1;
+    }
+    if (a[0] > b) {
+        return +1;
+    }
+    return 0;
+}
+
+/**
+ * @brief Return sign of bignum a minus bignum b
+ *
+ * Returns the sign of (a[] - b[])
+ * where a and b are a big number integers of nlimb limbs
+ *
+ * @param a pointer to an array of limbs (minuend)
+ * @param b pointer to an array of limbs (subtrahend)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result sign of the comparison: -1 a<b, 0 a=b, +1 a>b
+ */
+int bn_cmp(limb_t a[], limb_t b[], count_t nlimb) {
+    if (0 == nlimb) {
+        return 0;
+    }
+
+    while (nlimb-- > 0) {
+        if (a[nlimb] > b[nlimb]) {
+            return +1;    /* GT */
+        }
+        if (a[nlimb] < b[nlimb]) {
+            return -1;    /* LT */
+        }
+    }
+
+    return 0;   /* EQ */
+}
+
+/**
+ * @brief Single limb is even test
+ *
+ * Returns 1 if a is even, else 0
+ * where a is a single limb
+ *
+ * @param a a single limb
+ *
+ * @result zero if a is odd, 1 if a is even
+ */
+int sl_iseven(limb_t a) {
+    return (a & 1) ? 0 : 1;
+}
+
+/**
+ * @brief bignum is even test
+ *
+ * Returns 1 if a[] is even, else 0
+ * where a is a big number integer of nlimb limbs
+ * Note: a zero limb big number integer is even!
+ *
+ * @param a pointer to an array of limbs
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result zero if a is odd, 1 if a is even
+ */
+int bn_iseven(limb_t *a, count_t nlimb) {
+    if (0 == nlimb) {
+        return 1;
+    }
+    return (a[0] & 1) ? 0 : 1;
+}
+
+/**
+ * @brief Add a single limb to a bignum
+ *
+ * Computes w[] = u[] + v
+ * where w, u are big number integers of nlimb lims each,
+ * and v is a single limb.
+ * Returns carry if the addition overflows.
+ *
+ * Ref: Derived from Knuth Algorithm A.
+ *
+ * @param w pointer to an array of limbs receiving result
+ * @param u pointer to an array of limbs (addend 1)
+ * @param v a single limb
+ * @param nlimb number of limbs in the arrays w and u
+ *
+ * @result The carry status of the addition
+ */
+limb_t bn_add_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb) {
+    limb_t carry;
+    count_t j;
+
+    /* Copy u to w, so we can bail out if no borrow is left */
+    if (w != u) {
+        bn_copy(w, u, nlimb);
+    }
+
+    /* Add v to first limb of u */
+    w[0] += v;
+    carry = (w[0] < v ? 1 : 0);
+
+    /* Add carry to subsequent limbs */
+    for (j = 1; 0 != carry && j < nlimb; j++) {
+        w[j] += carry;
+        carry = (w[j] < carry ? 1 : 0);
+    }
+    return carry;
+}
+
+
+/**
+ * @brief Subtract a single limb from a bignum
+ *
+ * Computes w[] = u[] - v
+ * where w, u are big number integers of nlimb limbs each,
+ * and v is a single limb.
+ * Returns borrow (0 if u >= v, or 1 if v > u).
+ *
+ * Ref: Derived from Knuth Algorithm S.
+ *
+ * @param w pointer to an array of limbs receiving the result
+ * @param u pointer to an array of limbs (minuend)
+ * @param v single limb (subtrahend)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result borrow of the subtraction (0 if u >= v, 1 if u < v)
+ */
+limb_t bn_sub_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb) {
+    limb_t borrow;
+    count_t j;
+
+    /* Copy u to w, so we can bail out if no borrow is left */
+    if (w != u) {
+        bn_copy(w, u, nlimb);
+    }
+
+    /* Subtract v from first limb of u */
+    w[0] -= v;
+    borrow = (w[0] > ~v ? 1 : 0);
+
+    /* Subtract borrow from subsequent limbs */
+    for (j = 1; 0 != borrow && j < nlimb; j++) {
+        w[j] -= borrow;
+        borrow = (w[j] > ~borrow ? 1 : 0);
+    }
+
+    return borrow;
+}
+
+/**
+ * @brief Divide a bignum by a single limb
+ *
+ * Computes quotient q[] = u[] / v
+ * and returns remainder r = u[] % v
+ * where q, u are big number integers of nlimb limbs each,
+ * and v is a single limb.
+ *
+ * Makes no assumptions about normalisation.
+ *
+ * Ref: Knuth Vol 2 Ch 4.3.1 Exercise 16 p625
+ *
+ * @param q pointer to an array of limbs receiving the quotient
+ * @param u pointer to an array of limbs (dividend)
+ * @param v single limb (divisor)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result single limb remainder of the division (modulo)
+ */
+limb_t bn_div_limb(limb_t q[], limb_t u[], limb_t v, count_t nlimb) {
+    count_t j;
+    limb_t t[2], r;
+    count_t shift;
+
+    if (0 == nlimb) {
+        return 0;
+    }
+    if (0 == v) {
+        return LIMBMASK;    /* Divide by zero error */
+    }
+
+    /*
+     * Normalize first:
+     * qequires high bit of V to be set,
+     * so find most significant by shifting
+     * until DIGMSB is set.
+     */
+    for (shift = 0; 0 == (v & DIGMSB); shift++) {
+        v <<= 1;
+    }
+    r = bn_shl(q, u, shift, nlimb);
+
+    j = nlimb;
+    while (j-- > 0) {
+        t[0] = q[j];
+        t[1] = r;
+        sl_div(&q[j], &r, t, v);
+    }
+
+    /* Unnormalize */
+    r >>= shift;
+    return r;
+}
+
+/**
+ * @brief Modulo a bignum by a single limb
+ *
+ * Computes remainder (modulo) r = u[] mod v
+ * Computes r = u[] mod v
+ * where u is a big number integer of nlimb
+ * and r, v are single precision limbs
+ *
+ * Use remainder from divide function.
+ *
+ * @param u pointer to an array of limbs (dividend)
+ * @param v single limb (divisor)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result single limb remainder of the division (modulo)
+ */
+limb_t bn_mod_limb(limb_t u[], limb_t v, count_t nlimb) {
+    static IGRAPH_THREAD_LOCAL limb_t q[2 * BN_MAXSIZE];
+    limb_t r;
+
+    r = bn_div_limb(q, u, v, nlimb);
+
+    bn_zero(q, nlimb);
+    return r;
+}
+
+/**
+ * @brief Multiply a bignum by a single limb
+ *
+ * Computes product w[] = u[] * v
+ * Returns overflow k
+ * where w, u are big number integers of nlimb each
+ * and v is a single limb
+ *
+ * @param w pointer to an array of limbs to receive the result
+ * @param u pointer to an array of limbs (factor)
+ * @param v single limb (other factor)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result zero if no overflow, else overflow (value of w[nlimb])
+ */
+limb_t bn_mul_limb(limb_t w[], limb_t u[], limb_t v, count_t nlimb) {
+    limb_t t[2];
+    limb_t carry;
+    count_t j;
+
+    if (0 == v) {
+        bn_zero(w, nlimb);
+        return 0;
+    }
+
+    for (j = 0, carry = 0; j < nlimb; j++) {
+        sl_mul(t, u[j], v);
+        w[j] = t[0] + carry;
+        carry = t[1] + (w[j] < carry ? 1 : 0);
+    }
+
+    return carry;
+}
+
+#if HAVE_U64
+/**
+ * @brief Computes quotient and remainder of 64 bit / 32 bit
+ *
+ * Computes quotient q = u[] / v, remainder r = u[] mod v
+ * where u[] is a double limb.
+ *
+ * With native support for double limb division
+ *
+ * @param q pointer to the limb to receive the quotient
+ * @param r pointer to the limb to receive the remainder
+ * @param u pointer to an array of two limbs
+ * @param v single limb divisor
+ *
+ * @result zero on success
+ */
+limb_t sl_div(limb_t *q, limb_t *r, limb_t u[2], limb_t v) {
+#if ASM_X86
+    limb_t qq;
+    limb_t rr;
+
+    if (0 == v)
+        /* division by zero */
+    {
+        return LIMBMASK;
+    }
+    asm volatile(
+        "divl	%4"
+        : "=a"(qq), "=d"(rr)
+        : "a"(u[0]), "d"(u[1]), "g"(v));
+    *q = qq;
+    *r = rr;
+#else
+    dlimb_t dd;
+
+    if (0 == v)
+        /* division by zero */
+    {
+        return LIMBMASK;
+    }
+    dd = ((dlimb_t)u[1] << LIMBBITS) | u[0];
+    *q = (limb_t) (dd / v);
+    *r = dd % v;
+#endif
+    return 0;
+}
+
+#else
+
+#define B (HALFMASK + 1)
+
+/**
+ * @brief Computes quotient and remainder of 64 bit / 32 bit
+ *
+ * Computes quotient q = u / v, remainder r = u mod v
+ * where u is a double limb
+ * and q, v, r are single precision limbs.
+ * Returns high limb of quotient (max value is 1)
+ * Assumes normalized such that v1 >= b/2
+ * where b is size of HALF_DIGIT
+ * i.e. the most significant bit of v should be one
+ *
+ * In terms of half-limbs in Knuth notation:
+ *   (q2q1q0) = (u4u3u2u1u0) / (v1v0)
+ *   (r1r0) = (u4u3u2u1u0) % (v1v0)
+ * for m = 2, n = 2 where u4 = 0
+ *
+ * We set q = (q1q0) and return q2 as "overflow'
+ * Returned q2 is either 0 or 1.
+ *
+ * @param q pointer to the limb to receive the quotient
+ * @param r pointer to the limb to receive the remainder
+ * @param u pointer to an array of two limbs
+ * @param v single limb divisor
+ *
+ * @result zero on success
+ */
+limb_t sl_div(limb_t *q, limb_t *r, limb_t u[2], limb_t v) {
+    limb_t quot;
+    limb_t rem;
+    limb_t ul;
+    limb_t uh;
+    limb_t p0;
+    limb_t p1;
+    limb_t v0;
+    limb_t v1;
+    limb_t u0;
+    limb_t u1;
+    limb_t u2;
+    limb_t u3;
+    limb_t borrow;
+    limb_t q1;
+    limb_t q2;
+    limb_t s;
+    limb_t t;
+
+    /* Check for normalisation */
+    if (0 == (v & DIGMSB)) {
+        *q = *r = 0;
+        return LIMBMASK;
+    }
+
+    /* Split up into half-limbs */
+    v0 = LSH(v);
+    v1 = MSH(v);
+    u0 = LSH(u[0]);
+    u1 = MSH(u[0]);
+    u2 = LSH(u[1]);
+    u3 = MSH(u[1]);
+
+    /* Do three rounds of Knuth Algorithm D Vol 2 p272 */
+
+    /*
+     * ROUND 1 calculate q2:
+     * estimate quot = (u4u3)/v1 = 0 or 1,
+     * then set (u4u3u2) -= quot*(v1v0) where u4 = 0.
+     */
+    quot = u3 / v1;
+    if (quot > 0) {
+        rem = u3 - quot * v1;
+        t = SHL(rem) | u2;
+        if (quot * v0 > t) {
+            quot--;
+        }
+    }
+    uh = 0;     /* (u4) */
+    ul = u[1];  /* (u3u2) */
+    if (quot > 0) {
+        /* (u4u3u2) -= quot*(v1v0) where u4 = 0 */
+        p0 = quot * v0;
+        p1 = quot * v1;
+        s = p0 + SHL(p1);
+        ul -= s;
+        borrow = (ul > ~s ? 1 : 0);
+        uh -= MSH(p1) - borrow;
+
+        if (0 != MSH(uh)) {
+            /* add back */
+            quot--;
+            ul += v;
+            uh = 0;
+        }
+    }
+    q2 = quot;
+
+    /*
+     * ROUND 2 calculate q1:
+     * estimate quot = (u3u2) / v1,
+     * then set (u3u2u1) -= quot*(v1v0)
+     */
+    t = ul;
+    quot = t / v1;
+    rem = t - quot * v1;
+    /* Test on v0 */
+    t = SHL(rem) | u1;
+    if (B == quot || (quot * v0) > t) {
+        quot--;
+        rem += v1;
+        t = SHL(rem) | u1;
+        if (rem < B && (quot * v0) > t) {
+            quot--;
+        }
+    }
+
+    /*
+     * multiply and subtract:
+     * (u3u2u1)' = (u3u2u1) - quot*(v1v0)
+     */
+    uh = MSH(ul);   /* (0u3) */
+    ul = SHL(ul) | u1;  /* (u2u1) */
+    p0 = quot * v0;
+    p1 = quot * v1;
+    s = p0 + SHL(p1);
+    ul -= s;
+    borrow = (ul > ~s ? 1 : 0);
+    uh -= MSH(p1) - borrow;
+
+    if (0 != MSH(uh)) {
+        /* add back v */
+        quot--;
+        ul += v;
+        uh = 0;
+    }
+
+    /* quotient q1 */
+    q1 = quot;
+
+    /*
+     * ROUND 3:
+     * calculate q0; estimate quot = (u2u1) / v1,
+     * then set (u2u1u0) -= quot(v1v0)
+     */
+    t = ul;
+    quot = t / v1;
+    rem = t - quot * v1;
+    /* Test on v0 */
+    t = SHL(rem) | u0;
+    if (B == quot || (quot * v0) > t) {
+        quot--;
+        rem += v1;
+        t = SHL(rem) | u0;
+        if (rem < B && (quot * v0) > t) {
+            quot--;
+        }
+    }
+
+    /*
+     * multiply and subtract:
+     * (u2u1u0)" = (u2u1u0)' - quot(v1v0)
+     */
+    uh = MSH(ul);           /* (0u2) */
+    ul = SHL(ul) | u0;  /* (u1u0) */
+
+    p0 = quot * v0;
+    p1 = quot * v1;
+    s = p0 + SHL(p1);
+    ul -= s;
+    borrow = (ul > ~s ? 1 : 0);
+    uh -= MSH(p1) - borrow;
+    if (0 != MSH(uh)) {
+        /* add back v */
+        quot--;
+        ul += v;
+        uh = 0;
+    }
+
+    /* quotient q1q0 */
+    *q = SHL(q1) | LSH(quot);
+
+    /* Remainder is in (u1u0) i.e. ul */
+    *r = ul;
+
+    /* quotient q2 (overflow) is returned */
+    return q2;
+}
+
+#endif  /* HAVE_U64 */
+
+/**
+ * @brief Return greatest common divisor of two single limbs
+ *
+ * Returns gcd(x, y)
+ *
+ * Ref: Schneier 2nd ed, p245
+ *
+ * @param x single limb candidate #1
+ * @param y single limb candidate #2
+ *
+ * @result return zero if x and y are zero, else gcd(x,y)
+ */
+limb_t sl_gcd(limb_t x, limb_t y) {
+    limb_t g;
+
+    if (x + y == 0) {
+        return 0;    /* Error */
+    }
+
+    g = y;
+    while (x > 0) {
+        g = x;
+        x = y % x;
+        y = g;
+    }
+    return g;
+}
+
+/**
+ * @brief Compute single limb exp = x^e mod m
+ *
+ * Computes exp = x^e mod m
+ * Binary left-to-right method
+ *
+ * @param exp pointer to limb to receive result
+ * @param x single limb x (base)
+ * @param e single limb e (exponent)
+ * @param m single limb m (modulus)
+ *
+ * @result zero on success (always!?)
+ */
+int sl_modexp(limb_t *exp, limb_t x, limb_t e, limb_t m) {
+    limb_t mask;
+    limb_t y;   /* Temp variable */
+
+    /* Find most significant bit in e */
+    for (mask = DIGMSB; mask > 0; mask >>= 1) {
+        if (e & mask) {
+            break;
+        }
+    }
+
+    y = x;
+
+    for (mask >>= 1; mask > 0; mask >>= 1) {
+        sl_modmul(&y, y, y, m);     /* y = (y^2) % m */
+        if (e & mask) {
+            sl_modmul(&y, y, x, m);    /* y = (y*x) % m*/
+        }
+    }
+
+    *exp = y;
+    return 0;
+}
+
+/**
+ * @brief Compute single limb inverse inv = u^(-1) % v
+ *
+ * Computes inv = u^(-1) % v
+ * Ref: Knuth Algorithm X Vol 2 p 342
+ * ignoring u2, v2, t2 and avoiding negative numbers
+ *
+ * @param inv pointer to limb to receive result
+ * @param u single limb to inverse
+ * @param v single limb modulus
+ *
+ * @result zero on success (always!?)
+ */
+int sl_modinv(limb_t *inv, limb_t u, limb_t v) {
+    limb_t u1, u3, v1, v3, t1, t3, q, w;
+    int iter = 1;
+
+    /* Step X1. Initialize */
+    u1 = 1;
+    u3 = u;
+    v1 = 0;
+    v3 = v;
+
+    /* Step X2. */
+    while (v3 != 0) {
+        /* Step X3. */
+        q = u3 / v3;    /* Divide and */
+        t3 = u3 % v3;
+        w = q * v1; /* "Subtract" */
+        t1 = u1 + w;
+        /* Swap */
+        u1 = v1;
+        v1 = t1;
+        u3 = v3;
+        v3 = t3;
+        iter = -iter;
+    }
+
+    if (iter < 0) {
+        *inv = v - u1;
+    } else {
+        *inv = u1;
+    }
+
+    return 0;
+}
+
+/**
+ * @brief Compute single limb a = (x * y) % mod
+ *
+ * Computes a = (x * y) % m
+ *
+ * @param a pointer to single limb to receive result
+ * @param x single limb factor 1
+ * @param y single limb factor 2
+ * @param m single limb modulus
+ *
+ * @result zero on success (always!?)
+ */
+int sl_modmul(limb_t *a, limb_t x, limb_t y, limb_t m) {
+    static IGRAPH_THREAD_LOCAL limb_t pp[2];
+
+    /* pp[] = x * y */
+    sl_mul(pp, x, y);
+
+    /* *a = pp[] % m */
+    *a = bn_mod_limb(pp, m, 2);
+
+    /* Clean temp */
+    pp[0] = pp[1] = 0;
+    return 0;
+}
+
+#if HAVE_U64
+/**
+ * @brief Compute double limb product of two single limbs
+ *
+ * Computes p[] = x * y
+ * where p is two limbs (double precision) and x, y are single
+ * limbs. Use double precision natively supported on this machine.
+ *
+ * @param p pointer to an array of two limbs receiving the result
+ * @param x single limb factor #1
+ * @param y single limb factor #2
+ *
+ * @result zero on success (always)
+ */
+int sl_mul(limb_t p[2], limb_t x, limb_t y) {
+    dlimb_t dd;
+
+    dd = (dlimb_t)x * y;
+    p[0] = (limb_t)dd;
+    p[1] = (limb_t)(dd >> 32);
+    return 0;
+}
+
+#else
+
+/**
+ * @brief Compute double limb product of two single limbs
+ *
+ * Computes p[] = x * y
+ * Source: Arbitrary Precision Computation
+ * http://numbers.computation.free.fr/Constants/constants.html
+ *
+ * The limbs x and y are split in halves and the four products
+ * x1*y1, x0*y1, x1*y0 and x0*y0 are added shifting them to
+ * their respective least significant bit position:
+ * p[1] = x1*y1 + high(x0*y1 + x1*y0) + ch << 16 + cl
+ * p[0] = x0*y0 + low(x0*y1 + x1*y0) << 16
+ * ch = carry from adding x0*y1 + x1*y0
+ * cl = carry from adding low(x0*y1 + x1*y0) << 16 to p[0]
+ *
+ * @param p pointer to an array of two limbs receiving the result
+ * @param x single limb factor #1
+ * @param y single limb factor #2
+ *
+ * @result zero on success (always)
+ */
+int sl_mul(limb_t p[2], limb_t x, limb_t y) {
+    limb_t x0, y0, x1, y1;
+    limb_t t, u, carry;
+
+    /*
+     * Split each x,y into two halves
+     *   x = x0 + B*x1
+     *   y = y0 + B*y1
+     * where B = 2^16, half the limb size
+     * Product is
+     *   xy = x0y0 + B(x0y1 + x1y0) + B^2(x1y1)
+     */
+    x0 = LSH(x);
+    x1 = MSH(x);
+    y0 = LSH(y);
+    y1 = MSH(y);
+
+    /* Compute low part (w/o carry) */
+    p[0] = x0 * y0;
+
+    /* middle part */
+    t = x0 * y1;
+    u = x1 * y0;
+    t += u;
+    carry = (t < u ? 1 : 0);
+
+    /*
+     * The carry will go to high half of p[1],
+     * and the high half of t will go into the
+     * into low half of p[1]
+     */
+    carry = SHL(carry) + MSH(t);
+
+    /* add low half of t to high half of p[0] */
+    t = SHL(t);
+    p[0] += t;
+    if (p[0] < t) {
+        carry++;
+    }
+
+    p[1] = x1 * y1 + carry;
+
+    return 0;
+}
+
+#endif  /* HAVE_U64 */
+
+/**
+ * @brief Compute division of big number by a "half digit"
+ *
+ * Computes q[] = u[] / v, also returns r = u[] % v
+ * where q, a are big number integers of nlimb limbs each,
+ * and d, r are single limbs
+ *
+ * Using bit-by-bit method from MSB to LSB,
+ * so v must be <= HALFMASK
+ *
+ * According to "Principles in PGP by Phil Zimmermann"
+ *
+ * @param q pointer to an array of limbs to receive the result
+ * @param u pointer to an array of limbs (dividend)
+ * @param v single limb (actually half limb) divisor
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result returns remainder of the division
+ */
+limb_t bn_div_hdig(limb_t q[], limb_t u[], limb_t v, count_t nlimb) {
+    limb_t mask = DIGMSB;
+    limb_t r = 0;
+    if (v > HALFMASK) {
+        igraph_errorf("bn_div_hdig called with v:%x", __FILE__,
+                      __LINE__, (int) v);
+    }
+
+    if (0 == nlimb) {
+        return 0;
+    }
+    if (0 == v) {
+        return 0;    /* Divide by zero error */
+    }
+
+    /* Initialize quotient */
+    bn_zero(q, nlimb);
+
+    /* Work from MSB to LSB */
+    while (nlimb > 0) {
+        /* Multiply remainder by 2 */
+        r <<= 1;
+
+        /* Look at current bit */
+        if (u[nlimb - 1] & mask) {
+            r++;
+        }
+        if (r >= v) {
+            /* Remainder became greater than divisor */
+            r -= v;
+            q[nlimb - 1] |= mask;
+        }
+
+        /* next bit */
+        mask >>= 1;
+        if (0 != mask) {
+            continue;
+        }
+
+        /* next limb */
+        --nlimb;
+        mask = DIGMSB;
+    }
+    return r;
+}
+
+/**
+ * @brief Compute single limb remainder of bignum % single limb
+ *
+ * Computes r = u[] % v
+ * where a is a big number integer of nlimb
+ * and r, v are single limbs, using bit-by-bit
+ * method from MSB to LSB.
+ *
+ * Ref:
+ *   Derived from principles in PGP by Phil Zimmermann
+ * Note:
+ *   This method will only work until r <<= 1 overflows.
+ *   i.e. for d < DIGMSB, but we keep HALF_DIGIT
+ *   limit for safety, and also because we don't
+ *   have a 32nd bit.
+ *
+ * @param u pointer to big number to divide
+ * @param v single limb (actually half limb) modulus
+ * @param nlimb number of limbs in the array
+ *
+ * @result returns remainder of the division
+ */
+limb_t bn_mod_hdig(limb_t u[], limb_t v, count_t nlimb) {
+    limb_t mask;
+    limb_t r;
+
+    if (0 == nlimb) {
+        return 0;
+    }
+    if (0 == v) {
+        return 0;    /* Divide by zero error */
+    }
+
+    if (v > HALFMASK) {
+        igraph_errorf("bn_mod_hdig called with v:%x", __FILE__,
+                      __LINE__, (int) v);
+    }
+
+    /* Work from left to right */
+    mask = DIGMSB;
+    r = 0;
+    while (nlimb > 0) {
+        /* Multiply remainder by 2 */
+        r <<= 1;
+
+        /* Look at current bit */
+        if (u[nlimb - 1] & mask) {
+            r++;
+        }
+
+        if (r >= v)
+            /* Remainder became greater than divisor */
+        {
+            r -= v;
+        }
+
+        /* next bit */
+        mask >>= 1;
+        if (0 != mask) {
+            continue;
+        }
+
+        /* next limb */
+        --nlimb;
+        mask = DIGMSB;
+    }
+    return r;
+}
+
+/**
+ * @brief Addition of two bignum arrays
+ *
+ * Computes w[] = u[] + v[]
+ * where w, u, v are big number integers of nlimb limbs each.
+ * Returns carry, i.e. w[nlimb], as 0 or 1.
+ *
+ * Ref: Knuth Vol 2 Ch 4.3.1 p 266 Algorithm A.
+ *
+ * @param w pointer to array of limbs to receive the result
+ * @param u pointer to array of limbs (addend #1)
+ * @param v pointer to array of limbs (addend #2)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result returns the carry, i.e. w[nlimb], as 0 or 1
+ */
+limb_t bn_add(limb_t w[], limb_t u[], limb_t v[], count_t nlimb) {
+    limb_t carry;
+    count_t j;
+
+    for (j = 0, carry = 0; j < nlimb; j++) {
+        /*
+         * add limbs w[j] = u[j] + v[j] + carry;
+         * set carry = 1 if carry (overflow) occurs
+         */
+        w[j] = u[j] + carry;
+        carry = (w[j] < carry ? 1 : 0);
+
+        w[j] = w[j] + v[j];
+        if (w[j] < v[j]) {
+            carry++;
+        }
+    }
+
+    /* w[n] = carry */
+    return carry;
+}
+
+/**
+ * @brief Subtraction of two bignum arrays
+ *
+ * Calculates w[] = u[] - v[] where u[] >= v[]
+ * w, u, v are big number integers of nlimb limbs each
+ * Returns 0 if ok, or 1 if v was greater than u.
+ *
+ * Ref: Knuth Vol 2 Ch 4.3.1 p 267 Algorithm S.
+ *
+ * @param w pointer to array of limbs to receive the result
+ * @param u pointer to array of limbs (minuend)
+ * @param v pointer to array of limbs (subtrahend)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result zero on success, 1 if v was greater than u
+ */
+limb_t bn_sub(limb_t w[], limb_t u[], limb_t v[], count_t nlimb) {
+    limb_t borrow;
+    count_t j;
+
+    for (j = 0, borrow = 0; j < nlimb; j++) {
+        /*
+         * Subtract limbs w[j] = u[j] - v[j] - borrow;
+         * set borrow = 1 if borrow occurs
+         */
+        w[j] = u[j] - borrow;
+        borrow = (w[j] > ~borrow ? 1 : 0);
+
+        w[j] = w[j] - v[j];
+        if (w[j] > ~v[j]) {
+            borrow++;
+        }
+    }
+
+    /* borrow should be 0, if u >= v */
+    return borrow;
+}
+
+/**
+ * @brief Product of two bignum arrays
+ *
+ * Computes product w[] = u[] * v[]
+ * where u, v are big number integers of nlimb each
+ * and w is a big number integer of 2*nlimb limbs.
+ *
+ * Ref: Knuth Vol 2 Ch 4.3.1 p 268 Algorithm M.
+ *
+ * @param w pointer to array of limbs to receive the result
+ * @param u pointer to array of limbs (factor #1)
+ * @param v pointer to array of limbs (factor #2)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result zero on success (always!?)
+ */
+int bn_mul(limb_t w[], limb_t u[], limb_t v[], count_t nlimb) {
+    limb_t t[2];
+    limb_t carry;
+    count_t i, j, m, n;
+
+    m = n = nlimb;
+
+    /* zero result */
+    bn_zero(w, 2 * nlimb);
+
+    for (j = 0; j < n; j++) {
+        /* zero multiplier? */
+        if (0 == v[j]) {
+            w[j + m] = 0;
+            continue;
+        }
+        /* Initialize i */
+        carry = 0;
+        for (i = 0; i < m; i++) {
+            /*
+             * Multiply and add:
+             * t = u[i] * v[j] + w[i+j] + carry
+             */
+            sl_mul(t, u[i], v[j]);
+
+            t[0] += carry;
+            if (t[0] < carry) {
+                t[1]++;
+            }
+            t[0] += w[i + j];
+            if (t[0] < w[i + j]) {
+                t[1]++;
+            }
+
+            w[i + j] = t[0];
+            carry = t[1];
+        }
+        w[j + m] = carry;
+    }
+
+    return 0;
+}
+
+/**
+ * @brief Shift left a bignum by a number of bits (less than LIMBBITS)
+ *
+ * Computes a[] = b[] << x
+ * Where a and b are big number integers of nlimb each.
+ * The shift count must be less than LIMBBITS
+ *
+ * @param a pointer to array of limbs to receive the result
+ * @param b pointer to array of limbs to shift left
+ * @param x number of bits to shift (must be less than LIMBBITS)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result returns a single limb "carry", i.e. bits that came out left
+ */
+limb_t bn_shl(limb_t a[], limb_t b[], count_t x, count_t nlimb) {
+    count_t i, y;
+    limb_t carry, temp;
+
+    if (0 == nlimb) {
+        return 0;
+    }
+
+    if (0 == x) {
+        /* no shift at all */
+        if (a != b) {
+            bn_copy(a, b, nlimb);
+        }
+        return 0;
+    }
+
+    /* check shift amount */
+    if (x >= LIMBBITS) {
+        igraph_errorf("bn_shl() called with x >= %d", __FILE__,
+                      __LINE__, LIMBBITS);
+        return 0;
+    }
+
+    y = LIMBBITS - x;
+    carry = 0;
+    for (i = 0; i < nlimb; i++) {
+        temp = b[i] >> y;
+        a[i] = (b[i] << x) | carry;
+        carry = temp;
+    }
+
+    return carry;
+}
+
+/**
+ * @brief Shift right a bignum by a number of bits (less than LIMBBITS)
+ *
+ * Computes a[] = b[] >> x
+ * Where a and b are big number integers of nlimb each.
+ * The shift count must be less than LIMBBITS
+ *
+ * @param a pointer to array of limbs to receive the result
+ * @param b pointer to array of limbs to shift right
+ * @param x number of bits to shift (must be less than LIMBBITS)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result returns a single limb "carry", i.e. bits that came out right
+ */
+limb_t bn_shr(limb_t a[], limb_t b[], count_t x, count_t nlimb) {
+    count_t i, y;
+    limb_t carry, temp;
+
+    if (0 == nlimb) {
+        return 0;
+    }
+
+    if (0 == x) {
+        /* no shift at all */
+        if (a != b) {
+            bn_copy(a, b, nlimb);
+        }
+        return 0;
+    }
+
+    /* check shift amount */
+    if (x >= LIMBBITS) {
+        igraph_errorf("bn_shr() called with x >= %d", __FILE__,
+                      __LINE__, LIMBBITS);
+    }
+
+    y = LIMBBITS - x;
+    carry = 0;
+    i = nlimb;
+    while (i-- > 0) {
+        temp = b[i] << y;
+        a[i] = (b[i] >> x) | carry;
+        carry = temp;
+    }
+
+    return carry;
+}
+
+/**
+ * @brief Check a quotient for overflow
+ *
+ * Returns 1 if quot is too big,
+ * i.e. if (quot * Vn-2) > (b.rem + Uj+n-2)
+ * Returns 0 if ok
+ *
+ * @param quot quotient under test
+ * @param rem remainder
+ * @param
+ *
+ * @result zero on success
+ */
+static int quot_overflow(limb_t quot, limb_t rem, limb_t v, limb_t u) {
+    limb_t t[2];
+
+    sl_mul(t, quot, v);
+    if (t[1] < rem) {
+        return 0;
+    }
+    if (t[1] > rem) {
+        return 1;
+    }
+    if (t[0] > u) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/**
+ * @brief Compute quotient and remainder of bignum division
+ *
+ * Computes quotient q[] = u[] / v[]
+ * and remainder r[] = u[] % v[]
+ * where q, r, u are big number integers of ulimb limbs,
+ * and the divisor v of vlimb limbs.
+ *
+ * Ref: Knuth Vol 2 Ch 4.3.1 p 272 Algorithm D.
+ *
+ * @param q pointer to array of limbs to receive quotient
+ * @param r pointer to array of limbs to receive remainder
+ * @param u pointer to array of limbs (dividend)
+ * @param ulimb number of limbs in the q, r, u arrays
+ * @param v pointer to array of limbs (divisor)
+ * @param vlimb number of limbs in the v array
+ *
+ * @result zero on success, LIMBASK on division by zero
+ */
+int bn_div(limb_t q[], limb_t r[], limb_t u[], limb_t v[],
+           count_t ulimb, count_t vlimb) {
+    static IGRAPH_THREAD_LOCAL limb_t qq[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t uu[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t vv[BN_MAXSIZE];
+    limb_t mask;
+    limb_t overflow;
+    limb_t quot;
+    limb_t rem;
+    limb_t t[2];
+    limb_t *ww;
+    count_t n, m, i, j, shift;
+    int ok, cmp;
+
+    /* find size of v */
+    n = bn_sizeof(v, vlimb);
+
+    /* Catch special cases */
+    if (0 == n) {
+        return (int) LIMBMASK;    /* Error: divide by zero */
+    }
+
+    if (1 == n) {
+        /* Use short division instead */
+        r[0] = bn_div_limb(q, u, v[0], ulimb);
+        return 0;
+    }
+
+    /* find size of u */
+    m = bn_sizeof(u, ulimb);
+
+    if (m < n) {
+        /* v > u: just set q = 0 and r = u */
+        bn_zero(q, ulimb);
+        bn_copy(r, u, ulimb);
+        return 0;
+    }
+
+    if (m == n) {
+        /* u and v are the same length: compare them */
+        cmp = bn_cmp(u, v, (unsigned int)n);
+        if (0 == cmp) {
+            /* v == u: set q = 1 and r = 0 */
+            bn_limb(q, 1, ulimb);
+            bn_zero(r, ulimb);
+            return 0;
+        }
+        if (cmp < 0) {
+            /* v > u: set q = 0 and r = u */
+            bn_zero(q, ulimb);
+            bn_copy(r, u, ulimb);
+            return 0;
+        }
+    }
+
+    /* m greater than or equal to n */
+    m -= n;
+
+    /* clear quotient qq */
+    bn_zero(qq, ulimb);
+
+    /*
+     * Normalize v: requires high bit of v[n-1] to be set,
+     * so find most significant bit, then shift left
+     */
+    mask = DIGMSB;
+    for (shift = 0; shift < LIMBBITS; shift++) {
+        if (v[n - 1] & mask) {
+            break;
+        }
+        mask >>= 1;
+    }
+
+    /* normalize vv from v */
+    overflow = bn_shl(vv, v, shift, n);
+
+    /* copy normalized dividend u into remainder uu */
+    overflow = bn_shl(uu, u, shift, n + m);
+
+    /* new limb u[m+n] */
+    t[0] = overflow;
+
+    j = m + 1;
+    while (j-- > 0) {
+        /* quot = (b * u[j+n] + u[j+n-1]) / v[n-1] */
+        ok = 0;
+
+        /* This is Uj+n */
+        t[1] = t[0];
+        t[0] = uu[j + n - 1];
+
+        overflow = sl_div(&quot, &rem, t, vv[n - 1]);
+
+        if (overflow) {
+            /* quot = b */
+            quot = LIMBMASK;
+            rem = uu[j + n - 1] + vv[n - 1];
+            if (rem < vv[n - 1]) {
+                ok = 1;
+            }
+        }
+        if (0 == ok && quot_overflow(quot, rem, vv[n - 2], uu[j + n - 2])) {
+            /* quot * v[n-2] > b * rem + u[j+n-2] */
+            quot--;
+            rem += vv[n - 1];
+            if (rem >= vv[n - 1])
+                if (quot_overflow(quot, rem, vv[n - 2], uu[j + n - 2])) {
+                    quot--;
+                }
+        }
+
+        /* multiply and subtract vv[] * quot */
+        ww = &uu[j];
+
+        if (0 == quot) {
+            overflow = 0;
+        } else {
+            /* quot is non zero */
+            limb_t tt[2];
+            limb_t borrow;
+
+            for (i = 0, borrow = 0; i < n; i++) {
+                sl_mul(tt, quot, vv[i]);
+                ww[i] -= borrow;
+                borrow = (ww[i] > ~borrow ? 1 : 0);
+
+                ww[i] -= tt[0];
+                if (ww[i] > ~tt[0]) {
+                    borrow++;
+                }
+                borrow += tt[1];
+            }
+
+            /*
+             * w[n] is not in array w[0..n-1]:
+             * subtract final borrow
+             */
+            overflow = t[1] - borrow;
+        }
+
+        /* test for remainder */
+        if (overflow) {
+            quot--;
+            /* add back if mul/sub was negative */
+            overflow = bn_add(ww, ww, vv, n);
+        }
+
+        qq[j] = quot;
+
+        /* u[j+n] for next round */
+        t[0] = uu[j + n - 1];
+    }
+
+    /* clear uu[] limbs from n to n+m */
+    for (j = n; j < m + n; j++) {
+        uu[j] = 0;
+    }
+
+    /* denormalize remainder */
+    bn_shr(r, uu, shift, n);
+
+    /* copy quotient */
+    bn_copy(q, qq, n + m);
+
+    /* clear temps */
+    bn_zero(qq, n);
+    bn_zero(uu, n);
+    bn_zero(vv, n);
+    return 0;
+}
+
+/**
+ * @brief Compute remainder of bignum division (modulo)
+ *
+ * Calculates r[] = u[] % v[]
+ * where r, v are big number integers of length vlimb
+ * and u is a big number integer of length ulimb.
+ * r may overlap v.
+ *
+ * Note that r here is only vlimb long,
+ * whereas in bn_div it is ulimb long.
+ *
+ * Use remainder from bn_div function.
+ *
+ * @param r pointer to array of limbs to receive remainder
+ * @param u pointer to array of limbs (dividend)
+ * @param ulimb number of limbs in the u array
+ * @param v pointer to array of limbs (divisor)
+ * @param vlimb number of limbs in the r and v array
+ *
+ * @result zero on success, LIMBASK on division by zero
+ */
+limb_t bn_mod(limb_t r[], limb_t u[], count_t ulimb, limb_t v[], count_t vlimb) {
+    static IGRAPH_THREAD_LOCAL limb_t qq[2 * BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t rr[2 * BN_MAXSIZE];
+    limb_t d0;
+
+    /* rr[] = u[] % v[n] */
+    d0 = (limb_t) bn_div(qq, rr, u, v, ulimb, vlimb);
+
+    /* copy vlimb limbs of remainder */
+    bn_copy(r, rr, vlimb);
+
+    /* zero temps */
+    bn_zero(rr, ulimb);
+    bn_zero(qq, ulimb);
+
+    return d0;
+}
+
+/**
+ * @brief Compute greatest common divisor
+ *
+ * Computes g = gcd(x, y)
+ * Reference: Schneier
+ *
+ * @param g pointer to array of limbs to receive the gcd
+ * @param x pointer to array of limbs (candidate #1)
+ * @param y pointer to array of limbs (candidate #2)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result zero on succes (always)
+ */
+int bn_gcd(limb_t g[], limb_t x[], limb_t y[], count_t nlimb) {
+    static IGRAPH_THREAD_LOCAL limb_t yy[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t xx[BN_MAXSIZE];
+
+    bn_copy(xx, x, nlimb);
+    bn_copy(yy, y, nlimb);
+
+    /* g = y */
+    bn_copy(g, yy, nlimb);
+
+    /* while (x > 0) { */
+    while (0 != bn_cmp_limb(xx, 0, nlimb)) {
+        /* g = x */
+        bn_copy(g, xx, nlimb);
+        /* x = y % x */
+        bn_mod(xx, yy, nlimb, xx, nlimb);
+        /* y = g */
+        bn_copy(yy, g, nlimb);
+    }
+
+    bn_zero(xx, nlimb);
+    bn_zero(yy, nlimb);
+
+    /* gcd is left in g */
+    return 0;
+}
+
+/**
+ * @brief Compute modular exponentiation of bignums
+ *
+ * Computes y[] = (x[]^e[]) % m[]
+ * Binary MSB to LSB method
+ *
+ * @param y pointer to array of limbs to receive the result
+ * @param x pointer to array of limbs (base)
+ * @param e pointer to array of limbs (exponent)
+ * @param m pointer to array of limbs (modulus)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result zero on success, -1 on error (nlimb is zero)
+ */
+int bn_modexp(limb_t y[], limb_t x[], limb_t e[], limb_t m[], count_t nlimb) {
+    limb_t mask;
+    count_t n;
+
+    if (nlimb == 0) {
+        return -1;
+    }
+
+    /* Find second-most significant bit in e */
+    n = bn_sizeof(e, nlimb);
+    for (mask = DIGMSB; 0 != mask; mask >>= 1) {
+        if (e[n - 1] & mask) {
+            break;
+        }
+    }
+    /* next bit, because we start off with y[] == x[] */
+    mask >>= 1;
+    if (0 == mask) {
+        mask = DIGMSB;
+        n--;
+    }
+
+    /* y[] = x[] */
+    bn_copy(y, x, nlimb);
+
+    while (n > 0) {
+        /* y[] = (y[] ^ 2) % m[] */
+        bn_modmul(y, y, y, m, nlimb);
+
+        if (e[n - 1] & mask)
+            /* y[] = (y[] * x[]) % m[] */
+        {
+            bn_modmul(y, y, x, m, nlimb);
+        }
+
+        /* next bit */
+        mask >>= 1;
+        if (0 == mask) {
+            mask = DIGMSB;
+            n--;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * @brief Compute modular product of two bignums
+ *
+ * Computes a[] = (x[] * y[]) % m[]
+ * where a, x, y and m are big numbers of nlimb length
+ *
+ * @param a pointer to array of limbs to receive the result
+ * @param x pointer to array of limbs (factor #1)
+ * @param y pointer to array of limbs (factor #2)
+ * @param m pointer to array of limbs (modulus)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result zero on success, LIMBMASK if m was zero (division by zero)
+ */
+limb_t bn_modmul(limb_t a[], limb_t x[], limb_t y[], limb_t m[], count_t nlimb) {
+    static IGRAPH_THREAD_LOCAL limb_t pp[2 * BN_MAXSIZE];
+    limb_t d0;
+
+    /* pp[] = x[] * y[] (NB: double size pp[]) */
+    bn_mul(pp, x, y, nlimb);
+
+    /* a[] = pp[] % m[] */
+    d0 = bn_mod(a, pp, 2 * nlimb, m, nlimb);
+
+    /* zero temp */
+    bn_zero(pp, 2 * nlimb);
+
+    return d0;
+}
+
+/**
+ * @brief Compute modular inverse
+ *
+ * Computes inv[] = u[]^(-1) % v[]
+ * Ref: Knuth Algorithm X Vol 2 p 342
+ * ignoring u2, v2, t2 and avoiding negative numbers.
+ *
+ * @param inv pointer to array of limbs receiving the result
+ * @param u pointer to array of limbs (candidate)
+ * @param v pointer to array of limbs (modulus)
+ * @param nlimb number of limbs in the arrays
+ *
+ * @result zero on success
+ */
+int bn_modinv(limb_t inv[], limb_t u[], limb_t v[], count_t nlimb) {
+    /* Allocate temp variables */
+    static IGRAPH_THREAD_LOCAL limb_t u1[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t u3[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t v1[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t v3[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t t1[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t t3[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t q[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t w[2 * BN_MAXSIZE];
+    int iter;
+
+    /* Step X1. Initialize */
+    bn_limb(u1, 1, nlimb);  /* u1 = 1 */
+    bn_limb(v1, 0, nlimb);  /* v1 = 0 */
+    bn_copy(u3, u, nlimb);  /* u3 = u */
+    bn_copy(v3, v, nlimb);  /* v3 = v */
+
+    /* remember odd/even iterations */
+    iter = 1;
+
+    /* Step X2. Loop while v3 != 0 */
+    while (0 != bn_cmp_limb(v3, 0, nlimb)) {
+        /* Step X3. Divide and "Subtract" */
+        /* q = u3 / v3, t3 = u3 % v3 */
+        bn_div(q, t3, u3, v3, nlimb, nlimb);
+        /* w = q * v1 */
+        bn_mul(w, q, v1, nlimb);
+        /* t1 = u1 + w */
+        bn_add(t1, u1, w, nlimb);
+
+        /* Swap u1 <= v1 <= t1 */
+        bn_copy(u1, v1, nlimb);
+        bn_copy(v1, t1, nlimb);
+
+        /* Swap u3 <= v3 <= t3 */
+        bn_copy(u3, v3, nlimb);
+        bn_copy(v3, t3, nlimb);
+
+        iter ^= 1;
+    }
+
+    if (iter) {
+        bn_copy(inv, u1, nlimb);    /* inv = u1 */
+    } else {
+        bn_sub(inv, v, u1, nlimb);    /* inv = v - u1 */
+    }
+
+    /* clear temp vars */
+    bn_zero(u1, nlimb);
+    bn_zero(v1, nlimb);
+    bn_zero(t1, nlimb);
+    bn_zero(u3, nlimb);
+    bn_zero(v3, nlimb);
+    bn_zero(t3, nlimb);
+    bn_zero(q, nlimb);
+    bn_zero(w, 2 * nlimb);
+
+    return 0;
+}
+
+/**
+ * @brief Compute square root (and fraction) of a bignum
+ *
+ * Compute q[] = sqrt(u[]),
+ * where q and u are big number integers of nlimb limbs
+ *
+ * Method according to sqrt.html of 2001-08-15:
+ * Act on bytes from MSB to LSB, counting the number of times
+ * that we can subtract consecutive odd numbers starting with
+ * 1, 3, 5. Just uses add, subtract, shift and comparisons.
+ *
+ * The pointer r can be NULL if caller is not interested in
+ * the (partial) fraction.
+ *
+ * @param q pointer to array of limbs to receive the result (integer)
+ * @param r pointer to array of limbs to receive the result (fraction)
+ * @param u pointer to array of limbs (square)
+ * @param rlimb number of limbs in the q and r arrays
+ * @param ulimb number of limbs in the u array
+ *
+ * @result zero on success
+ */
+int bn_sqrt(limb_t q[], limb_t r[], limb_t u[], count_t rlimb, count_t ulimb) {
+    static IGRAPH_THREAD_LOCAL limb_t step[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t accu[BN_MAXSIZE];
+    static IGRAPH_THREAD_LOCAL limb_t w[2 * BN_MAXSIZE];
+    limb_t d;
+    count_t m, n;
+    count_t shift;
+
+    bn_zero(q, ulimb);
+    bn_limb(step, 1, BN_MAXSIZE);
+    bn_limb(accu, 0, BN_MAXSIZE);
+    n = bn_sizeof(u, ulimb);
+
+    /* determine first non-zero byte from MSB to LSB */
+    if (0 != (u[n - 1] >> 24)) {
+        shift = 32;
+    } else if (0 != (u[n - 1] >> 16)) {
+        shift = 24;
+    } else if (0 != (u[n - 1] >> 8)) {
+        shift = 16;
+    } else {
+        shift = 8;
+    }
+
+    m = 1;
+    while (n-- > 0) {
+        while (shift > 0) {
+            /* shift accu one byte left */
+            bn_shl(accu, accu, 8, m + 1);
+
+            /* shift for next byte from u[] */
+            shift -= 8;
+            accu[0] |= (u[n] >> shift) & 0xff;
+
+            /* digit = 0 */
+            d = 0;
+            /* subtract consecutive odd numbers step[] until overflow */
+            for (d = 0; bn_cmp(step, accu, m + 1) <= 0; d++) {
+                bn_sub(accu, accu, step, m + 1);
+                bn_add_limb(step, step, 2, m + 1);
+            }
+
+            /* put digit into result */
+            bn_shl(q, q, 4, m);
+            q[0] |= d;
+
+            /* step[] = 2 * q[] * 16 + 1 */
+            bn_shl(step, q, 5, m + 1);
+            bn_add_limb(step, step, 1, m + 1);
+        }
+        shift = 32;
+        if (0 == (n & 1)) {
+            m++;
+        }
+    }
+
+    /* Caller does not want to know the fraction? */
+    if (NULL == r) {
+        return 0;
+    }
+
+    /* nothing left to do if remainder is zero */
+    if (0 == bn_cmp_limb(accu, 0, ulimb)) {
+        bn_zero(r, rlimb);
+        return 0;
+    }
+
+    /* Start off with the integer part */
+    bn_zero(w, 2 * BN_MAXSIZE);
+    bn_copy(w, q, ulimb);
+
+    n = rlimb * (LIMBBITS / 4);
+    while (n-- > 0) {
+        /* shift accu one byte left */
+        bn_shl(accu, accu, 8, rlimb);
+
+        /* subtract consecutive odd numbers step[] until overflow */
+        for (d = 0; bn_cmp(step, accu, rlimb) <= 0; d++) {
+            bn_sub(accu, accu, step, rlimb);
+            bn_add_limb(step, step, 2, rlimb);
+        }
+
+        /* put digit into result */
+        bn_shl(w, w, 4, rlimb);
+        w[0] |= d;
+
+        /* step[] = 2 * w[] * 16 + 1 */
+        bn_shl(step, w, 5, rlimb);
+        bn_add_limb(step, step, 1, rlimb);
+    }
+
+    /* copy remainder */
+    bn_copy(r, w, rlimb);
+    return 0;
+}
diff --git a/igraph/src/bipartite.c b/igraph/src/bipartite.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/bipartite.c
@@ -0,0 +1,1147 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2008-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_bipartite.h"
+#include "igraph_attributes.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+#include "igraph_constructors.h"
+#include "igraph_dqueue.h"
+#include "igraph_random.h"
+#include "igraph_nongraph.h"
+
+/**
+ * \section about_bipartite Bipartite networks in igraph
+ *
+ * <para>
+ * A bipartite network contains two kinds of vertices and connections
+ * are only possible between two vertices of different kind. There are
+ * many natural examples, e.g. movies and actors as vertices and a
+ * movie is connected to all participating actors, etc.
+ *
+ * </para><para>
+ * igraph does not have direct support for bipartite networks, at
+ * least not at the C language level. In other words the igraph_t
+ * structure does not contain information about the vertex types.
+ * The C functions for bipartite networks usually have an additional
+ * input argument to graph, called \c types, a boolean vector giving
+ * the vertex types.
+ *
+ * </para><para>
+ * Most functions creating bipartite networks are able to create this
+ * extra vector, you just need to supply an initialized boolean vector
+ * to them.</para>
+ */
+
+/**
+ * \function igraph_bipartite_projection_size
+ * Calculate the number of vertices and edges in the bipartite projections
+ *
+ * This function calculates the number of vertices and edges in the
+ * two projections of a bipartite network. This is useful if you have
+ * a big bipartite network and you want to estimate the amount of
+ * memory you would need to calculate the projections themselves.
+ *
+ * \param graph The input graph.
+ * \param types Boolean vector giving the vertex types of the graph.
+ * \param vcount1 Pointer to an \c igraph_integer_t, the number of
+ *     vertices in the first projection is stored here.
+ * \param ecount1 Pointer to an \c igraph_integer_t, the number of
+ *     edges in the first projection is stored here.
+ * \param vcount2 Pointer to an \c igraph_integer_t, the number of
+ *     vertices in the second projection is stored here.
+ * \param ecount2 Pointer to an \c igraph_integer_t, the number of
+ *     edges in the second projection is stored here.
+ * \return Error code.
+ *
+ * \sa \ref igraph_bipartite_projection() to calculate the actual
+ * projection.
+ *
+ * Time complexity: O(|V|*d^2+|E|), |V| is the number of vertices, |E|
+ * is the number of edges, d is the average (total) degree of the
+ * graphs.
+ *
+ * \example examples/simple/igraph_bipartite_projection.c
+ */
+
+int igraph_bipartite_projection_size(const igraph_t *graph,
+                                     const igraph_vector_bool_t *types,
+                                     igraph_integer_t *vcount1,
+                                     igraph_integer_t *ecount1,
+                                     igraph_integer_t *vcount2,
+                                     igraph_integer_t *ecount2) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int vc1 = 0, ec1 = 0, vc2 = 0, ec2 = 0;
+    igraph_adjlist_t adjlist;
+    igraph_vector_long_t added;
+    long int i;
+
+    IGRAPH_CHECK(igraph_vector_long_init(&added, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &added);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_vector_int_t *neis1;
+        long int neilen1, j;
+        long int *ecptr;
+        if (VECTOR(*types)[i]) {
+            vc2++;
+            ecptr = &ec2;
+        } else {
+            vc1++;
+            ecptr = &ec1;
+        }
+        neis1 = igraph_adjlist_get(&adjlist, i);
+        neilen1 = igraph_vector_int_size(neis1);
+        for (j = 0; j < neilen1; j++) {
+            long int k, neilen2, nei = (long int) VECTOR(*neis1)[j];
+            igraph_vector_int_t *neis2 = igraph_adjlist_get(&adjlist, nei);
+            if (IGRAPH_UNLIKELY(VECTOR(*types)[i] == VECTOR(*types)[nei])) {
+                IGRAPH_ERROR("Non-bipartite edge found in bipartite projection",
+                             IGRAPH_EINVAL);
+            }
+            neilen2 = igraph_vector_int_size(neis2);
+            for (k = 0; k < neilen2; k++) {
+                long int nei2 = (long int) VECTOR(*neis2)[k];
+                if (nei2 <= i) {
+                    continue;
+                }
+                if (VECTOR(added)[nei2] == i + 1) {
+                    continue;
+                }
+                VECTOR(added)[nei2] = i + 1;
+                (*ecptr)++;
+            }
+        }
+    }
+
+    *vcount1 = (igraph_integer_t) vc1;
+    *ecount1 = (igraph_integer_t) ec1;
+    *vcount2 = (igraph_integer_t) vc2;
+    *ecount2 = (igraph_integer_t) ec2;
+
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_long_destroy(&added);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_bipartite_projection(const igraph_t *graph,
+                                  const igraph_vector_bool_t *types,
+                                  igraph_t *proj,
+                                  int which,
+                                  igraph_vector_t *multiplicity) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, j, k;
+    igraph_integer_t remaining_nodes = 0;
+    igraph_vector_t vertex_perm, vertex_index;
+    igraph_vector_t edges;
+    igraph_adjlist_t adjlist;
+    igraph_vector_int_t *neis1, *neis2;
+    long int neilen1, neilen2;
+    igraph_vector_long_t added;
+    igraph_vector_t mult;
+
+    if (which < 0) {
+        return 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&vertex_perm, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&vertex_perm, no_of_nodes));
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&vertex_index, no_of_nodes);
+    IGRAPH_CHECK(igraph_vector_long_init(&added, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &added);
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+    if (multiplicity) {
+        IGRAPH_VECTOR_INIT_FINALLY(&mult, no_of_nodes);
+        igraph_vector_clear(multiplicity);
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*types)[i] == which) {
+            VECTOR(vertex_index)[i] = ++remaining_nodes;
+            igraph_vector_push_back(&vertex_perm, i);
+        }
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*types)[i] == which) {
+            long int new_i = (long int) VECTOR(vertex_index)[i] - 1;
+            long int iedges = 0;
+            neis1 = igraph_adjlist_get(&adjlist, i);
+            neilen1 = igraph_vector_int_size(neis1);
+            for (j = 0; j < neilen1; j++) {
+                long int nei = (long int) VECTOR(*neis1)[j];
+                if (IGRAPH_UNLIKELY(VECTOR(*types)[i] == VECTOR(*types)[nei])) {
+                    IGRAPH_ERROR("Non-bipartite edge found in bipartite projection",
+                                 IGRAPH_EINVAL);
+                }
+                neis2 = igraph_adjlist_get(&adjlist, nei);
+                neilen2 = igraph_vector_int_size(neis2);
+                for (k = 0; k < neilen2; k++) {
+                    long int nei2 = (long int) VECTOR(*neis2)[k], new_nei2;
+                    if (nei2 <= i) {
+                        continue;
+                    }
+                    if (VECTOR(added)[nei2] == i + 1) {
+                        if (multiplicity) {
+                            VECTOR(mult)[nei2] += 1;
+                        }
+                        continue;
+                    }
+                    VECTOR(added)[nei2] = i + 1;
+                    if (multiplicity) {
+                        VECTOR(mult)[nei2] = 1;
+                    }
+                    iedges++;
+
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, new_i));
+                    if (multiplicity) {
+                        /* If we need the multiplicity as well, then we put in the
+                           old vertex ids here and rewrite it later */
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, nei2));
+                    } else {
+                        new_nei2 = (long int) VECTOR(vertex_index)[nei2] - 1;
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, new_nei2));
+                    }
+                }
+            }
+            if (multiplicity) {
+                /* OK, we need to go through all the edges added for vertex new_i
+                   and check their multiplicity */
+                long int now = igraph_vector_size(&edges);
+                long int from = now - iedges * 2;
+                for (j = from; j < now; j += 2) {
+                    long int nei2 = (long int) VECTOR(edges)[j + 1];
+                    long int new_nei2 = (long int) VECTOR(vertex_index)[nei2] - 1;
+                    long int m = (long int) VECTOR(mult)[nei2];
+                    VECTOR(edges)[j + 1] = new_nei2;
+                    IGRAPH_CHECK(igraph_vector_push_back(multiplicity, m));
+                }
+            }
+        } /* if VECTOR(*type)[i] == which */
+    }
+
+    if (multiplicity) {
+        igraph_vector_destroy(&mult);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_long_destroy(&added);
+    igraph_vector_destroy(&vertex_index);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    IGRAPH_CHECK(igraph_create(proj, &edges, remaining_nodes,
+                               /*directed=*/ 0));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_destroy, proj);
+
+    IGRAPH_I_ATTRIBUTE_DESTROY(proj);
+    IGRAPH_I_ATTRIBUTE_COPY(proj, graph, 1, 0, 0);
+    IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, proj, &vertex_perm));
+    igraph_vector_destroy(&vertex_perm);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_bipartite_projection
+ * Create one or both projections of a bipartite (two-mode) network
+ *
+ * Creates one or both projections of a bipartite graph.
+ * \param graph The bipartite input graph. Directedness of the edges
+ *   is ignored.
+ * \param types Boolean vector giving the vertex types of the graph.
+ * \param proj1 Pointer to an uninitialized graph object, the first
+ *   projection will be created here. It a null pointer, then it is
+ *   ignored, see also the \p probe1 argument.
+ * \param proj2 Pointer to an uninitialized graph object, the second
+ *   projection is created here, if it is not a null pointer. See also
+ *   the \p probe1 argument.
+ * \param multiplicity1 Pointer to a vector, or a null pointer. If not
+ *   the latter, then the multiplicity of the edges is stored
+ *   here. E.g. if there is an A-C-B and also an A-D-B triple in the
+ *   bipartite graph (but no more X, such that A-X-B is also in the
+ *   graph), then the multiplicity of the A-B edge in the projection
+ *   will be 2.
+ * \param multiplicity2 The same as \c multiplicity1, but for the
+ *   other projection.
+ * \param probe1 This argument can be used to specify the order of the
+ *   projections in the resulting list. When it is non-negative, then
+ *   it is considered as a vertex ID and the projection containing
+ *   this vertex will be the first one in the result. Setting this
+ *   argument to a non-negative value implies that \c proj1 must be
+ *   a non-null pointer. If you don't care about the ordering of the
+ *   projections, pass -1 here.
+ * \return Error code.
+ *
+ * \sa \ref igraph_bipartite_projection_size() to calculate the number
+ * of vertices and edges in the projections, without creating the
+ * projection graphs themselves.
+ *
+ * Time complexity: O(|V|*d^2+|E|), |V| is the number of vertices, |E|
+ * is the number of edges, d is the average (total) degree of the
+ * graphs.
+ *
+ * \example examples/simple/igraph_bipartite_projection.c
+ */
+
+int igraph_bipartite_projection(const igraph_t *graph,
+                                const igraph_vector_bool_t *types,
+                                igraph_t *proj1,
+                                igraph_t *proj2,
+                                igraph_vector_t *multiplicity1,
+                                igraph_vector_t *multiplicity2,
+                                igraph_integer_t probe1) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+
+    /* t1 is -1 if proj1 is omitted, it is 0 if it belongs to type zero,
+       it is 1 if it belongs to type one. The same for t2 */
+    int t1, t2;
+
+    if (igraph_vector_bool_size(types) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid bipartite type vector size", IGRAPH_EINVAL);
+    }
+
+    if (probe1 >= no_of_nodes) {
+        IGRAPH_ERROR("No such vertex to probe", IGRAPH_EINVAL);
+    }
+
+    if (probe1 >= 0 && !proj1) {
+        IGRAPH_ERROR("`probe1' given, but `proj1' is a null pointer", IGRAPH_EINVAL);
+    }
+
+    if (probe1 >= 0) {
+        t1 = VECTOR(*types)[(long int)probe1];
+        if (proj2) {
+            t2 = 1 - t1;
+        } else {
+            t2 = -1;
+        }
+    } else {
+        t1 = proj1 ? 0 : -1;
+        t2 = proj2 ? 1 : -1;
+    }
+
+    IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj1, t1, multiplicity1));
+    IGRAPH_FINALLY(igraph_destroy, proj1);
+    IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj2, t2, multiplicity2));
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+
+/**
+ * \function igraph_full_bipartite
+ * Create a full bipartite network
+ *
+ * A bipartite network contains two kinds of vertices and connections
+ * are only possible between two vertices of different kind. There are
+ * many natural examples, e.g. movies and actors as vertices and a
+ * movie is connected to all participating actors, etc.
+ *
+ * </para><para>
+ * igraph does not have direct support for bipartite networks, at
+ * least not at the C language level. In other words the igraph_t
+ * structure does not contain information about the vertex types.
+ * The C functions for bipartite networks usually have an additional
+ * input argument to graph, called \c types, a boolean vector giving
+ * the vertex types.
+ *
+ * </para><para>
+ * Most functions creating bipartite networks are able to create this
+ * extra vector, you just need to supply an initialized boolean vector
+ * to them.
+ *
+ * \param graph Pointer to an igraph_t object, the graph will be
+ *   created here.
+ * \param types Pointer to a boolean vector. If not a null pointer,
+ *   then the vertex types will be stored here.
+ * \param n1 Integer, the number of vertices of the first kind.
+ * \param n2 Integer, the number of vertices of the second kind.
+ * \param directed Boolean, whether to create a directed graph.
+ * \param mode A constant that gives the type of connections for
+ *   directed graphs. If \c IGRAPH_OUT, then edges point from vertices
+ *   of the first kind to vertices of the second kind; if \c
+ *   IGRAPH_IN, then the opposite direction is realized; if \c
+ *   IGRAPH_ALL, then mutual edges will be created.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ *
+ * \sa \ref igraph_full() for non-bipartite full graphs.
+ */
+
+int igraph_full_bipartite(igraph_t *graph,
+                          igraph_vector_bool_t *types,
+                          igraph_integer_t n1, igraph_integer_t n2,
+                          igraph_bool_t directed,
+                          igraph_neimode_t mode) {
+
+    igraph_integer_t nn1 = n1, nn2 = n2;
+    igraph_integer_t no_of_nodes = nn1 + nn2;
+    igraph_vector_t edges;
+    long int no_of_edges;
+    long int ptr = 0;
+    long int i, j;
+
+    if (!directed) {
+        no_of_edges = nn1 * nn2;
+    } else if (mode == IGRAPH_OUT || mode == IGRAPH_IN) {
+        no_of_edges = nn1 * nn2;
+    } else { /* mode==IGRAPH_ALL */
+        no_of_edges = nn1 * nn2 * 2;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+
+    if (!directed || mode == IGRAPH_OUT) {
+
+        for (i = 0; i < nn1; i++) {
+            for (j = 0; j < nn2; j++) {
+                VECTOR(edges)[ptr++] = i;
+                VECTOR(edges)[ptr++] = nn1 + j;
+            }
+        }
+
+    } else if (mode == IGRAPH_IN) {
+
+        for (i = 0; i < nn1; i++) {
+            for (j = 0; j < nn2; j++) {
+                VECTOR(edges)[ptr++] = nn1 + j;
+                VECTOR(edges)[ptr++] = i;
+            }
+        }
+
+    } else {
+
+        for (i = 0; i < nn1; i++) {
+            for (j = 0; j < nn2; j++) {
+                VECTOR(edges)[ptr++] = i;
+                VECTOR(edges)[ptr++] = nn1 + j;
+                VECTOR(edges)[ptr++] = nn1 + j;
+                VECTOR(edges)[ptr++] = i;
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_destroy, graph);
+
+    if (types) {
+        IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes));
+        igraph_vector_bool_null(types);
+        for (i = nn1; i < no_of_nodes; i++) {
+            VECTOR(*types)[i] = 1;
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_create_bipartite
+ * Create a bipartite graph
+ *
+ * This is a simple wrapper function to create a bipartite graph. It
+ * does a little more than \ref igraph_create(), e.g. it checks that
+ * the graph is indeed bipartite with respect to the given \p types
+ * vector. If there is an edge connecting two vertices of the same
+ * kind, then an error is reported.
+ * \param graph Pointer to an uninitialized graph object, the result is
+ *   created here.
+ * \param types Boolean vector giving the vertex types. The length of
+ *   the vector defines the number of vertices in the graph.
+ * \param edges Vector giving the edges of the graph. The highest
+ *   vertex id in this vector must be smaller than the length of the
+ *   \p types vector.
+ * \param directed Boolean scalar, whether to create a directed
+ *   graph.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ *
+ * \example examples/simple/igraph_bipartite_create.c
+ */
+
+int igraph_create_bipartite(igraph_t *graph, const igraph_vector_bool_t *types,
+                            const igraph_vector_t *edges,
+                            igraph_bool_t directed) {
+
+    igraph_integer_t no_of_nodes =
+        (igraph_integer_t) igraph_vector_bool_size(types);
+    long int no_of_edges = igraph_vector_size(edges);
+    igraph_real_t min_edge = 0, max_edge = 0;
+    igraph_bool_t min_type = 0, max_type = 0;
+    long int i;
+
+    if (no_of_edges % 2 != 0) {
+        IGRAPH_ERROR("Invalid (odd) edges vector", IGRAPH_EINVEVECTOR);
+    }
+    no_of_edges /= 2;
+
+    if (no_of_edges != 0) {
+        igraph_vector_minmax(edges, &min_edge, &max_edge);
+    }
+    if (min_edge < 0 || max_edge >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid (negative) vertex id", IGRAPH_EINVVID);
+    }
+
+    /* Check types vector */
+    if (no_of_nodes != 0) {
+        igraph_vector_bool_minmax(types, &min_type, &max_type);
+        if (min_type < 0 || max_type > 1) {
+            IGRAPH_WARNING("Non-binary type vector when creating a bipartite graph");
+        }
+    }
+
+    /* Check bipartiteness */
+    for (i = 0; i < no_of_edges * 2; i += 2) {
+        long int from = (long int) VECTOR(*edges)[i];
+        long int to = (long int) VECTOR(*edges)[i + 1];
+        long int t1 = VECTOR(*types)[from];
+        long int t2 = VECTOR(*types)[to];
+        if ( (t1 && t2) || (!t1 && !t2) ) {
+            IGRAPH_ERROR("Invalid edges, not a bipartite graph", IGRAPH_EINVAL);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_empty(graph, no_of_nodes, directed));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+    IGRAPH_CHECK(igraph_add_edges(graph, edges, 0));
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_incidence
+ * Create a bipartite graph from an incidence matrix
+ *
+ * A bipartite (or two-mode) graph contains two types of vertices and
+ * edges always connect vertices of different types. An incidence
+ * matrix is an nxm matrix, n and m are the number of vertices of the
+ * two types, respectively. Nonzero elements in the matrix denote
+ * edges between the two corresponding vertices.
+ *
+ * </para><para>
+ * Note that this function can operate in two modes, depending on the
+ * \p multiple argument. If it is FALSE (i.e. 0), then a single edge is
+ * created for every non-zero element in the incidence matrix. If \p
+ * multiple is TRUE (i.e. 1), then the matrix elements are rounded up
+ * to the closest non-negative integer to get the number of edges to
+ * create between a pair of vertices.
+ *
+ * </para><para>
+ * This function does not create multiple edges if \p multiple is
+ * FALSE, but might create some if it is TRUE.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param types Pointer to an initialized boolean vector, or a null
+ *   pointer. If not a null pointer, then the vertex types are stored
+ *   here. It is resized as needed.
+ * \param incidence The incidence matrix.
+ * \param directed Gives whether to create an undirected or a directed
+ *   graph.
+ * \param mode Specifies the direction of the edges in a directed
+ *   graph. If \c IGRAPH_OUT, then edges point from vertices
+ *   of the first kind (corresponding to rows) to vertices of the
+ *   second kind (corresponding to columns); if \c
+ *   IGRAPH_IN, then the opposite direction is realized; if \c
+ *   IGRAPH_ALL, then mutual edges will be created.
+ * \param multiple How to interpret the incidence matrix elements. See
+ *   details below.
+ * \return Error code.
+ *
+ * Time complexity: O(n*m), the size of the incidence matrix.
+ */
+
+int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types,
+                     const igraph_matrix_t *incidence,
+                     igraph_bool_t directed,
+                     igraph_neimode_t mode, igraph_bool_t multiple) {
+
+    igraph_integer_t n1 = (igraph_integer_t) igraph_matrix_nrow(incidence);
+    igraph_integer_t n2 = (igraph_integer_t) igraph_matrix_ncol(incidence);
+    igraph_integer_t no_of_nodes = n1 + n2;
+    igraph_vector_t edges;
+    long int i, j, k;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    if (multiple) {
+
+        for (i = 0; i < n1; i++) {
+            for (j = 0; j < n2; j++) {
+                long int elem = (long int) MATRIX(*incidence, i, j);
+                long int from, to;
+
+                if (!elem) {
+                    continue;
+                }
+
+                if (mode == IGRAPH_IN) {
+                    from = n1 + j;
+                    to = i;
+                } else {
+                    from = i;
+                    to = n1 + j;
+                }
+
+                if (mode != IGRAPH_ALL || !directed) {
+                    for (k = 0; k < elem; k++) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, from));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+                    }
+                } else {
+                    for (k = 0; k < elem; k++) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, from));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, from));
+                    }
+                }
+            }
+        }
+
+    } else {
+
+        for (i = 0; i < n1; i++) {
+            for (j = 0; j < n2; j++) {
+                long int from, to;
+
+                if (MATRIX(*incidence, i, j) != 0) {
+                    if (mode == IGRAPH_IN) {
+                        from = n1 + j;
+                        to = i;
+                    } else {
+                        from = i;
+                        to = n1 + j;
+                    }
+                    if (mode != IGRAPH_ALL || !directed) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, from));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+                    } else {
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, from));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, from));
+                    }
+                }
+            }
+        }
+
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_destroy, graph);
+
+    if (types) {
+        IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes));
+        igraph_vector_bool_null(types);
+        for (i = n1; i < no_of_nodes; i++) {
+            VECTOR(*types)[i] = 1;
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_get_incidence
+ * Convert a bipartite graph into an incidence matrix
+ *
+ * \param graph The input graph, edge directions are ignored.
+ * \param types Boolean vector containing the vertex types.
+ * \param res Pointer to an initialized matrix, the result is stored
+ *   here. An element of the matrix gives the number of edges
+ *   (irrespectively of their direction) between the two corresponding
+ *   vertices.
+ * \param row_ids Pointer to an initialized vector or a null
+ *   pointer. If not a null pointer, then the vertex ids (in the
+ *   graph) corresponding to the rows of the result matrix are stored
+ *   here.
+ * \param col_ids Pointer to an initialized vector or a null
+ *   pointer. If not a null pointer, then the vertex ids corresponding
+ *   to the columns of the result matrix are stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(n*m), n and m are number of vertices of the two
+ * different kind.
+ *
+ * \sa \ref igraph_incidence() for the opposite operation.
+ */
+
+int igraph_get_incidence(const igraph_t *graph,
+                         const igraph_vector_bool_t *types,
+                         igraph_matrix_t *res,
+                         igraph_vector_t *row_ids,
+                         igraph_vector_t *col_ids) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int n1 = 0, n2 = 0, i;
+    igraph_vector_t perm;
+    long int p1, p2;
+
+    if (igraph_vector_bool_size(types) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid vertex type vector for bipartite graph",
+                     IGRAPH_EINVAL);
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        n1 += VECTOR(*types)[i] == 0 ? 1 : 0;
+    }
+    n2 = no_of_nodes - n1;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&perm, no_of_nodes);
+
+    for (i = 0, p1 = 0, p2 = n1; i < no_of_nodes; i++) {
+        VECTOR(perm)[i] = VECTOR(*types)[i] ? p2++ : p1++;
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, n1, n2));
+    igraph_matrix_null(res);
+    for (i = 0; i < no_of_edges; i++) {
+        long int from = IGRAPH_FROM(graph, i);
+        long int to = IGRAPH_TO(graph, i);
+        long int from2 = (long int) VECTOR(perm)[from];
+        long int to2 = (long int) VECTOR(perm)[to];
+        if (! VECTOR(*types)[from]) {
+            MATRIX(*res, from2, to2 - n1) += 1;
+        } else {
+            MATRIX(*res, to2, from2 - n1) += 1;
+        }
+    }
+
+    if (row_ids) {
+        IGRAPH_CHECK(igraph_vector_resize(row_ids, n1));
+    }
+    if (col_ids) {
+        IGRAPH_CHECK(igraph_vector_resize(col_ids, n2));
+    }
+    if (row_ids || col_ids) {
+        for (i = 0; i < no_of_nodes; i++) {
+            if (! VECTOR(*types)[i]) {
+                if (row_ids) {
+                    long int i2 = (long int) VECTOR(perm)[i];
+                    VECTOR(*row_ids)[i2] = i;
+                }
+            } else {
+                if (col_ids) {
+                    long int i2 = (long int) VECTOR(perm)[i];
+                    VECTOR(*col_ids)[i2 - n1] = i;
+                }
+            }
+        }
+    }
+
+    igraph_vector_destroy(&perm);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_is_bipartite
+ * Check whether a graph is bipartite
+ *
+ * </para><para>
+ * This function simply checks whether a graph \emph{could} be
+ * bipartite. It tries to find a mapping that gives a possible division
+ * of the vertices into two classes, such that no two vertices of the
+ * same class are connected by an edge.
+ *
+ * </para><para>
+ * The existence of such a mapping is equivalent of having no circuits of
+ * odd length in the graph. A graph with loop edges cannot bipartite.
+ *
+ * </para><para>
+ * Note that the mapping is not necessarily unique, e.g. if the graph has
+ * at least two components, then the vertices in the separate components
+ * can be mapped independently.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a boolean, the result is stored here.
+ * \param type Pointer to an initialized boolean vector, or a null
+ *   pointer. If not a null pointer and a mapping was found, then it
+ *   is stored here. If not a null pointer, but no mapping was found,
+ *   the contents of this vector is invalid.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ */
+
+int igraph_is_bipartite(const igraph_t *graph,
+                        igraph_bool_t *res,
+                        igraph_vector_bool_t *type) {
+
+    /* We basically do a breadth first search and label the
+       vertices along the way. We stop as soon as we can find a
+       contradiction.
+
+       In the 'seen' vector 0 means 'not seen yet', 1 means type 1,
+       2 means type 2.
+    */
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_char_t seen;
+    igraph_dqueue_t Q;
+    igraph_vector_t neis;
+    igraph_bool_t bi = 1;
+    long int i;
+
+    IGRAPH_CHECK(igraph_vector_char_init(&seen, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &seen);
+    IGRAPH_DQUEUE_INIT_FINALLY(&Q, 100);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    for (i = 0; bi && i < no_of_nodes; i++) {
+
+        if (VECTOR(seen)[i]) {
+            continue;
+        }
+
+        IGRAPH_CHECK(igraph_dqueue_push(&Q, i));
+        VECTOR(seen)[i] = 1;
+
+        while (bi && !igraph_dqueue_empty(&Q)) {
+            long int n, j;
+            igraph_integer_t actnode = (igraph_integer_t) igraph_dqueue_pop(&Q);
+            char acttype = VECTOR(seen)[actnode];
+
+            IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_ALL));
+            n = igraph_vector_size(&neis);
+            for (j = 0; j < n; j++) {
+                long int nei = (long int) VECTOR(neis)[j];
+                if (VECTOR(seen)[nei]) {
+                    long int neitype = VECTOR(seen)[nei];
+                    if (neitype == acttype) {
+                        bi = 0;
+                        break;
+                    }
+                } else {
+                    VECTOR(seen)[nei] = 3 - acttype;
+                    IGRAPH_CHECK(igraph_dqueue_push(&Q, nei));
+                }
+            }
+        }
+    }
+
+    igraph_vector_destroy(&neis);
+    igraph_dqueue_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    if (res) {
+        *res = bi;
+    }
+
+    if (type && bi) {
+        IGRAPH_CHECK(igraph_vector_bool_resize(type, no_of_nodes));
+        for (i = 0; i < no_of_nodes; i++) {
+            VECTOR(*type)[i] = VECTOR(seen)[i] - 1;
+        }
+    }
+
+    igraph_vector_char_destroy(&seen);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types,
+                              igraph_integer_t n1, igraph_integer_t n2,
+                              igraph_real_t p, igraph_bool_t directed,
+                              igraph_neimode_t mode) {
+
+    int retval = 0;
+    igraph_vector_t edges, s;
+    int i;
+
+    if (p < 0.0 || p > 1.0) {
+        IGRAPH_ERROR("Invalid connection probability", IGRAPH_EINVAL);
+    }
+
+    if (types) {
+        IGRAPH_CHECK(igraph_vector_bool_resize(types, n1 + n2));
+        igraph_vector_bool_null(types);
+        for (i = n1; i < n1 + n2; i++) {
+            VECTOR(*types)[i] = 1;
+        }
+    }
+
+    if (p == 0 || n1 * n2 < 1) {
+        IGRAPH_CHECK(retval = igraph_empty(graph, n1 + n2, directed));
+    } else if (p == 1.0) {
+        IGRAPH_CHECK(retval = igraph_full_bipartite(graph, types, n1, n2, directed,
+                              mode));
+    } else {
+
+        long int to, from, slen;
+        double maxedges, last;
+        if (!directed || mode != IGRAPH_ALL) {
+            maxedges = (double) n1 * (double) n2;
+        } else {
+            maxedges = 2.0 * (double) n1 * (double) n2;
+        }
+
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&s, 0);
+        IGRAPH_CHECK(igraph_vector_reserve(&s, (long) (maxedges * p * 1.1)));
+
+        RNG_BEGIN();
+
+        last = RNG_GEOM(p);
+        while (last < maxedges) {
+            IGRAPH_CHECK(igraph_vector_push_back(&s, last));
+            last += RNG_GEOM(p);
+            last += 1;
+        }
+
+        RNG_END();
+
+        slen = igraph_vector_size(&s);
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, slen * 2));
+
+        for (i = 0; i < slen; i++) {
+            if (!directed || mode != IGRAPH_ALL) {
+                to = (long) floor(VECTOR(s)[i] / n1);
+                from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1);
+                to += n1;
+            } else {
+                long int n1n2 = n1 * n2;
+                if (VECTOR(s)[i] < n1n2) {
+                    to = (long) floor(VECTOR(s)[i] / n1);
+                    from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1);
+                    to += n1;
+                } else {
+                    to = (long) floor( (VECTOR(s)[i] - n1n2) / n2);
+                    from = (long) (VECTOR(s)[i] - n1n2 - ((igraph_real_t) to) * n2);
+                    from += n1;
+                }
+            }
+
+            if (mode != IGRAPH_IN) {
+                igraph_vector_push_back(&edges, from);
+                igraph_vector_push_back(&edges, to);
+            } else {
+                igraph_vector_push_back(&edges, to);
+                igraph_vector_push_back(&edges, from);
+            }
+        }
+
+        igraph_vector_destroy(&s);
+        IGRAPH_FINALLY_CLEAN(1);
+        IGRAPH_CHECK(retval = igraph_create(graph, &edges, n1 + n2, directed));
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return retval;
+}
+
+int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types,
+                              igraph_integer_t n1, igraph_integer_t n2,
+                              igraph_integer_t m, igraph_bool_t directed,
+                              igraph_neimode_t mode) {
+    igraph_vector_t edges;
+    igraph_vector_t s;
+    int retval = 0;
+
+    if (n1 < 0 || n2 < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    }
+    if (m < 0) {
+        IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL);
+    }
+
+    if (types) {
+        long int i;
+        IGRAPH_CHECK(igraph_vector_bool_resize(types, n1 + n2));
+        igraph_vector_bool_null(types);
+        for (i = n1; i < n1 + n2; i++) {
+            VECTOR(*types)[i] = 1;
+        }
+    }
+
+    if (m == 0 || n1 * n2 == 0) {
+        if (m > 0) {
+            IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL);
+        }
+        IGRAPH_CHECK(retval = igraph_empty(graph, n1 + n2, directed));
+    } else {
+
+
+        long int i;
+        double maxedges;
+        if (!directed || mode != IGRAPH_ALL) {
+            maxedges = (double) n1 * (double) n2;
+        } else {
+            maxedges = 2.0 * (double) n1 * (double) n2;
+        }
+
+        if (m > maxedges) {
+            IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL);
+        }
+
+        if (maxedges == m) {
+            IGRAPH_CHECK(retval = igraph_full_bipartite(graph, types, n1, n2,
+                                  directed, mode));
+        } else {
+
+            long int to, from;
+            IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+            IGRAPH_VECTOR_INIT_FINALLY(&s, 0);
+            IGRAPH_CHECK(igraph_random_sample(&s, 0, maxedges - 1, m));
+            IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2));
+
+            for (i = 0; i < m; i++) {
+                if (!directed || mode != IGRAPH_ALL) {
+                    to = (long) floor(VECTOR(s)[i] / n1);
+                    from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1);
+                    to += n1;
+                } else {
+                    long int n1n2 = n1 * n2;
+                    if (VECTOR(s)[i] < n1n2) {
+                        to = (long) floor(VECTOR(s)[i] / n1);
+                        from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1);
+                        to += n1;
+                    } else {
+                        to = (long) floor( (VECTOR(s)[i] - n1n2) / n2);
+                        from = (long) (VECTOR(s)[i] - n1n2 - ((igraph_real_t) to) * n2);
+                        from += n1;
+                    }
+                }
+
+                if (mode != IGRAPH_IN) {
+                    igraph_vector_push_back(&edges, from);
+                    igraph_vector_push_back(&edges, to);
+                } else {
+                    igraph_vector_push_back(&edges, to);
+                    igraph_vector_push_back(&edges, from);
+                }
+            }
+
+            igraph_vector_destroy(&s);
+            IGRAPH_FINALLY_CLEAN(1);
+            IGRAPH_CHECK(retval = igraph_create(graph, &edges, n1 + n2, directed));
+            igraph_vector_destroy(&edges);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    return retval;
+}
+
+/**
+ * \function igraph_bipartite_game
+ * Generate a bipartite random graph (similar to Erdos-Renyi)
+ *
+ * Similarly to unipartite (one-mode) networks, we can define the
+ * G(n,p), and G(n,m) graph classes for bipartite graphs, via their
+ * generating process. In G(n,p) every possible edge between top and
+ * bottom vertices is realized with probablity p, independently of the
+ * rest of the edges. In G(n,m), we uniformly choose m edges to
+ * realize.
+ * \param graph Pointer to an uninitialized igraph graph, the result
+ *    is stored here.
+ * \param types Pointer to an initialized boolean vector, or a null
+ *    pointer. If not a null pointer, then the vertex types are stored
+ *    here. Bottom vertices come first, n1 of them, then n2 top
+ *    vertices.
+ * \param type The type of the random graph, possible values:
+ *        \clist
+ *        \cli IGRAPH_ERDOS_RENYI_GNM
+ *          G(n,m) graph,
+ *          m edges are
+ *          selected uniformly randomly in a graph with
+ *          n vertices.
+ *        \cli IGRAPH_ERDOS_RENYI_GNP
+ *          G(n,p) graph,
+ *          every possible edge is included in the graph with
+ *          probability p.
+ *        \endclist
+ * \param n1 The number of bottom vertices.
+ * \param n2 The number of top verices.
+ * \param p The connection probability for G(n,p) graphs. It is
+ *     ignored for G(n,m) graphs.
+ * \param m The number of edges for G(n,m) graphs. It is ignored for
+ *     G(n,p) graphs.
+ * \param directed Boolean, whether to generate a directed graph. See
+ *     also the \p mode argument.
+ * \param mode Specifies how to direct the edges in directed
+ *     graphs. If it is \c IGRAPH_OUT, then directed edges point from
+ *     bottom vertices to top vertices. If it is \c IGRAPH_IN, edges
+ *     point from top vertices to bottom vertices. \c IGRAPH_OUT and
+ *     \c IGRAPH_IN do not generate mutual edges. If this argument is
+ *     \c IGRAPH_ALL, then each edge direction is considered
+ *     independently and mutual edges might be generated. This
+ *     argument is ignored for undirected graphs.
+ * \return Error code.
+ *
+ * \sa \ref igraph_erdos_renyi_game.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ */
+
+int igraph_bipartite_game(igraph_t *graph, igraph_vector_bool_t *types,
+                          igraph_erdos_renyi_t type,
+                          igraph_integer_t n1, igraph_integer_t n2,
+                          igraph_real_t p, igraph_integer_t m,
+                          igraph_bool_t directed, igraph_neimode_t mode) {
+    int retval = 0;
+
+    if (n1 < 0 || n2 < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    }
+
+    if (type == IGRAPH_ERDOS_RENYI_GNP) {
+        retval = igraph_bipartite_game_gnp(graph, types, n1, n2, p, directed, mode);
+    } else if (type == IGRAPH_ERDOS_RENYI_GNM) {
+        retval = igraph_bipartite_game_gnm(graph, types, n1, n2, m, directed, mode);
+    } else {
+        IGRAPH_ERROR("Invalid type", IGRAPH_EINVAL);
+    }
+    return retval;
+}
diff --git a/igraph/src/blas.c b/igraph/src/blas.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/blas.c
@@ -0,0 +1,110 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_blas.h"
+#include "igraph_blas_internal.h"
+
+#include <assert.h>
+
+/**
+ * \function igraph_blas_dgemv
+ * \brief Matrix-vector multiplication using BLAS, vector version.
+ *
+ * This function is a somewhat more user-friendly interface to
+ * the \c dgemv function in BLAS. \c dgemv performs the operation
+ * y = alpha*A*x + beta*y, where x and y are vectors and A is an
+ * appropriately sized matrix (symmetric or unsymmetric).
+ *
+ * \param transpose whether to transpose the matrix \p A
+ * \param alpha     the constant \p alpha
+ * \param a         the matrix \p A
+ * \param x         the vector \p x
+ * \param beta      the constant \p beta
+ * \param y         the vector \p y (which will be modified in-place)
+ *
+ * Time complexity: O(nk) if the matrix is of size n x k
+ *
+ * \sa \ref igraph_blas_dgemv_array if you have arrays instead of
+ *     vectors.
+ *
+ * \example examples/simple/blas.c
+ */
+void igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha,
+                       const igraph_matrix_t* a, const igraph_vector_t* x,
+                       igraph_real_t beta, igraph_vector_t* y) {
+    char trans = transpose ? 'T' : 'N';
+    int m, n;
+    int inc = 1;
+
+    m = (int) igraph_matrix_nrow(a);
+    n = (int) igraph_matrix_ncol(a);
+
+    assert(igraph_vector_size(x) == transpose ? m : n);
+    assert(igraph_vector_size(y) == transpose ? n : m);
+
+    igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m,
+                 VECTOR(*x), &inc, &beta, VECTOR(*y), &inc);
+}
+
+/**
+ * \function igraph_blas_dgemv_array
+ * \brief Matrix-vector multiplication using BLAS, array version.
+ *
+ * This function is a somewhat more user-friendly interface to
+ * the \c dgemv function in BLAS. \c dgemv performs the operation
+ * y = alpha*A*x + beta*y, where x and y are vectors and A is an
+ * appropriately sized matrix (symmetric or unsymmetric).
+ *
+ * \param transpose whether to transpose the matrix \p A
+ * \param alpha     the constant \p alpha
+ * \param a         the matrix \p A
+ * \param x         the vector \p x as a regular C array
+ * \param beta      the constant \p beta
+ * \param y         the vector \p y as a regular C array
+ *                  (which will be modified in-place)
+ *
+ * Time complexity: O(nk) if the matrix is of size n x k
+ *
+ * \sa \ref igraph_blas_dgemv if you have vectors instead of
+ *     arrays.
+ */
+void igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha,
+                             const igraph_matrix_t* a, const igraph_real_t* x,
+                             igraph_real_t beta, igraph_real_t* y) {
+    char trans = transpose ? 'T' : 'N';
+    int m, n;
+    int inc = 1;
+
+    m = (int) igraph_matrix_nrow(a);
+    n = (int) igraph_matrix_ncol(a);
+
+    igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m,
+                 (igraph_real_t*)x, &inc, &beta, y, &inc);
+}
+
+igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v) {
+    int n = igraph_vector_size(v);
+    int one = 1;
+    return igraphdnrm2_(&n, VECTOR(*v), &one);
+}
diff --git a/igraph/src/bliss.cc b/igraph/src/bliss.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/bliss.cc
@@ -0,0 +1,262 @@
+/*
+ Copyright (C) 2003-2006 Tommi Junttila
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */
+
+#include "bliss/graph.hh"
+
+#include "igraph_types.h"
+#include "igraph_topology.h"
+
+#include "igraph_datatype.h"
+#include "igraph_interface.h"
+
+
+using namespace bliss;
+using namespace std;
+
+
+namespace { // unnamed namespace
+
+inline AbstractGraph *bliss_from_igraph(const igraph_t *graph) {
+    unsigned int nof_vertices = (unsigned int)igraph_vcount(graph);
+    unsigned int nof_edges = (unsigned int)igraph_ecount(graph);
+
+    AbstractGraph *g;
+
+    if (igraph_is_directed(graph)) {
+        g = new Digraph(nof_vertices);
+    } else {
+        g = new Graph(nof_vertices);
+    }
+
+    g->set_verbose_level(0);
+
+    for (unsigned int i = 0; i < nof_edges; i++) {
+        g->add_edge((unsigned int)IGRAPH_FROM(graph, i), (unsigned int)IGRAPH_TO(graph, i));
+    }
+    return g;
+}
+
+
+void bliss_free_graph(AbstractGraph *g) {
+    delete g;
+}
+
+
+inline int bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) {
+    if (directed) {
+        Digraph::SplittingHeuristic gsh = Digraph::shs_fsm;
+        switch (sh) {
+        case IGRAPH_BLISS_F:    gsh = Digraph::shs_f;   break;
+        case IGRAPH_BLISS_FL:   gsh = Digraph::shs_fl;  break;
+        case IGRAPH_BLISS_FS:   gsh = Digraph::shs_fs;  break;
+        case IGRAPH_BLISS_FM:   gsh = Digraph::shs_fm;  break;
+        case IGRAPH_BLISS_FLM:  gsh = Digraph::shs_flm; break;
+        case IGRAPH_BLISS_FSM:  gsh = Digraph::shs_fsm; break;
+        default: IGRAPH_ERROR("Invalid splitting heuristic", IGRAPH_EINVAL);
+        }
+        static_cast<Digraph *>(g)->set_splitting_heuristic(gsh);
+    } else {
+        Graph::SplittingHeuristic gsh = Graph::shs_fsm;
+        switch (sh) {
+        case IGRAPH_BLISS_F:    gsh = Graph::shs_f;   break;
+        case IGRAPH_BLISS_FL:   gsh = Graph::shs_fl;  break;
+        case IGRAPH_BLISS_FS:   gsh = Graph::shs_fs;  break;
+        case IGRAPH_BLISS_FM:   gsh = Graph::shs_fm;  break;
+        case IGRAPH_BLISS_FLM:  gsh = Graph::shs_flm; break;
+        case IGRAPH_BLISS_FSM:  gsh = Graph::shs_fsm; break;
+        default: IGRAPH_ERROR("Invalid splitting heuristic", IGRAPH_EINVAL);
+        }
+        static_cast<Graph *>(g)->set_splitting_heuristic(gsh);
+    }
+    return IGRAPH_SUCCESS;
+}
+
+
+inline int bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) {
+    if (colors == NULL) {
+        return IGRAPH_SUCCESS;
+    }
+    const int n = g->get_nof_vertices();
+    if (n != igraph_vector_int_size(colors)) {
+        IGRAPH_ERROR("Invalid vertex color vector length", IGRAPH_EINVAL);
+    }
+    for (int i = 0; i < n; ++i) {
+        g->change_color(i, VECTOR(*colors)[i]);
+    }
+    return IGRAPH_SUCCESS;
+}
+
+
+inline void bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) {
+    if (info) {
+        info->max_level      = stats.get_max_level();
+        info->nof_nodes      = stats.get_nof_nodes();
+        info->nof_leaf_nodes = stats.get_nof_leaf_nodes();
+        info->nof_bad_nodes  = stats.get_nof_bad_nodes();
+        info->nof_canupdates = stats.get_nof_canupdates();
+        info->nof_generators = stats.get_nof_generators();
+        stats.group_size.tostring(&info->group_size);
+    }
+}
+
+
+// this is the callback function used with AbstractGraph::find_automorphisms()
+// it collects the group generators into a pointer vector
+void collect_generators(void *generators, unsigned int n, const unsigned int *aut) {
+    igraph_vector_ptr_t *gen = static_cast<igraph_vector_ptr_t *>(generators);
+    igraph_vector_t *newvector = igraph_Calloc(1, igraph_vector_t);
+    igraph_vector_init(newvector, n);
+    copy(aut, aut + n, newvector->stor_begin); // takes care of unsigned int -> double conversion
+    igraph_vector_ptr_push_back(gen, newvector);
+}
+
+} // end unnamed namespace
+
+/**
+ * \function igraph_canonical_permutation
+ * Canonical permutation using BLISS
+ *
+ * This function computes the canonical permutation which transforms
+ * the graph into a canonical form by using the BLISS algorithm.
+ *
+ * \param graph The input graph. Multiple edges between the same nodes
+ *   are not supported and will cause an incorrect result to be returned.
+ * \param colors An optional vertex color vector for the graph. Supply a
+ *   null pointer is the graph is not colored.
+ * \param labeling Pointer to a vector, the result is stored here. The
+ *    permutation takes vertex 0 to the first element of the vector,
+ *    vertex 1 to the second, etc. The vector will be resized as
+ *    needed.
+ * \param sh The splitting heuristics to be used in BLISS. See \ref
+ *    igraph_bliss_sh_t.
+ * \param info If not \c NULL then information on BLISS internals is
+ *    stored here. See \ref igraph_bliss_info_t.
+ * \return Error code.
+ *
+ * Time complexity: exponential, in practice it is fast for many graphs.
+ */
+int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors,
+                                 igraph_vector_t *labeling, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) {
+    AbstractGraph *g = bliss_from_igraph(graph);
+    IGRAPH_FINALLY(bliss_free_graph, g);
+    const unsigned int N = g->get_nof_vertices();
+
+    IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph)));
+    IGRAPH_CHECK(bliss_set_colors(g, colors));
+
+    Stats stats;
+    const unsigned int *cl = g->canonical_form(stats, NULL, NULL);
+    IGRAPH_CHECK(igraph_vector_resize(labeling, N));
+    for (unsigned int i = 0; i < N; i++) {
+        VECTOR(*labeling)[i] = cl[i];
+    }
+
+    bliss_info_to_igraph(info, stats);
+
+    delete g;
+    IGRAPH_FINALLY_CLEAN(1);
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_automorphisms
+ * Number of automorphisms using BLISS
+ *
+ * The number of automorphisms of a graph is computed using BLISS. The
+ * result is returned as part of the \p info structure, in tag \c
+ * group_size. It is returned as a string, as it can be very high even
+ * for relatively small graphs. If the GNU MP library is used then
+ * this number is exact, otherwise a <type>long double</type> is used
+ * and it is only approximate. See also \ref igraph_bliss_info_t.
+ *
+ * \param graph The input graph. Multiple edges between the same nodes
+ *   are not supported and will cause an incorrect result to be returned.
+ * \param colors An optional vertex color vector for the graph. Supply a
+ *   null pointer is the graph is not colored.
+ * \param sh The splitting heuristics to be used in BLISS. See \ref
+ *    igraph_bliss_sh_t.
+ * \param info The result is stored here, in particular in the \c
+ *    group_size tag of \p info.
+ * \return Error code.
+ *
+ * Time complexity: exponential, in practice it is fast for many graphs.
+ */
+int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors,
+                         igraph_bliss_sh_t sh, igraph_bliss_info_t *info) {
+    AbstractGraph *g = bliss_from_igraph(graph);
+    IGRAPH_FINALLY(bliss_free_graph, g);
+
+    IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph)));
+    IGRAPH_CHECK(bliss_set_colors(g, colors));
+
+    Stats stats;
+    g->find_automorphisms(stats, NULL, NULL);
+
+    bliss_info_to_igraph(info, stats);
+
+    delete g;
+    IGRAPH_FINALLY_CLEAN(1);
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_automorphism_group
+ * Automorphism group generators using BLISS
+ *
+ * The generators of the automorphism group of a graph are computed
+ * using BLISS. The generator set may not be minimal and may depend on
+ * the splitting heuristics.
+ *
+ * \param graph The input graph. Multiple edges between the same nodes
+ *   are not supported and will cause an incorrect result to be returned.
+ * \param colors An optional vertex color vector for the graph. Supply a
+ *   null pointer is the graph is not colored.
+ * \param generators Must be an initialized pointer vector. It will
+ *    contain pointers to \ref igraph_vector_t objects
+ *    representing generators of the automorphism group.
+ * \param sh The splitting heuristics to be used in BLISS. See \ref
+ *    igraph_bliss_sh_t.
+ * \param info If not \c NULL then information on BLISS internals is
+ *    stored here. See \ref igraph_bliss_info_t.
+ * \return Error code.
+ *
+ * Time complexity: exponential, in practice it is fast for many graphs.
+ */
+int igraph_automorphism_group(
+    const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_ptr_t *generators,
+    igraph_bliss_sh_t sh, igraph_bliss_info_t *info) {
+    AbstractGraph *g = bliss_from_igraph(graph);
+    IGRAPH_FINALLY(bliss_free_graph, g);
+
+    IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph)));
+    IGRAPH_CHECK(bliss_set_colors(g, colors));
+
+    Stats stats;
+    igraph_vector_ptr_resize(generators, 0);
+    g->find_automorphisms(stats, collect_generators, generators);
+
+    bliss_info_to_igraph(info, stats);
+
+    delete g;
+    IGRAPH_FINALLY_CLEAN(1);
+    return IGRAPH_SUCCESS;
+}
+
+
+
diff --git a/igraph/src/bliss_heap.cc b/igraph/src/bliss_heap.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/bliss_heap.cc
@@ -0,0 +1,99 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include "defs.hh"
+#include "heap.hh"
+
+/* use 'and' instead of '&&' */
+#if _MSC_VER
+#include <ciso646>
+#endif
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+Heap::~Heap()
+{
+  if(array)
+    {
+      free(array);
+      array = 0;
+      n = 0;
+      N = 0;
+    }
+}
+
+void Heap::upheap(unsigned int index)
+{
+  const unsigned int v = array[index];
+  array[0] = 0;
+  while(array[index/2] > v)
+    {
+      array[index] = array[index/2];
+      index = index/2;
+    }
+  array[index] = v;
+}
+
+void Heap::downheap(unsigned int index)
+{
+  const unsigned int v = array[index];
+  const unsigned int lim = n/2;
+  while(index <= lim)
+    {
+      unsigned int new_index = index + index;
+      if((new_index < n) and (array[new_index] > array[new_index+1]))
+	new_index++;
+      if(v <= array[new_index])
+	break;
+      array[index] = array[new_index];
+      index = new_index;
+    }
+  array[index] = v;
+}
+
+void Heap::init(const unsigned int size)
+{
+  if(size > N)
+    {
+      if(array)
+	free(array);
+      array = (unsigned int*)malloc((size + 1) * sizeof(unsigned int));
+      N = size;
+    }
+  n = 0;
+}
+
+void Heap::insert(const unsigned int v)
+{
+  array[++n] = v;
+  upheap(n);
+}
+
+unsigned int Heap::remove()
+{
+  const unsigned int v = array[1];
+  array[1] = array[n--];
+  downheap(1);
+  return v;
+}
+
+} // namespace bliss
diff --git a/igraph/src/c_abs.c b/igraph/src/c_abs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/c_abs.c
@@ -0,0 +1,20 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern double f__cabs();
+
+double c_abs(z) f2c_complex *z;
+#else
+extern double f__cabs(double, double);
+
+double c_abs(f2c_complex *z)
+#endif
+{
+return( f__cabs( z->r, z->i ) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/c_cos.c b/igraph/src/c_cos.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/c_cos.c
@@ -0,0 +1,23 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+extern double sin(), cos(), sinh(), cosh();
+
+VOID c_cos(r, z) f2c_complex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void c_cos(f2c_complex *r, f2c_complex *z)
+#endif
+{
+	double zi = z->i, zr = z->r;
+	r->r =   cos(zr) * cosh(zi);
+	r->i = - sin(zr) * sinh(zi);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/c_div.c b/igraph/src/c_div.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/c_div.c
@@ -0,0 +1,53 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern VOID sig_die();
+VOID c_div(c, a, b)
+f2c_complex *a, *b, *c;
+#else
+extern void sig_die(const char*,int);
+void c_div(f2c_complex *c, f2c_complex *a, f2c_complex *b)
+#endif
+{
+	double ratio, den;
+	double abr, abi, cr;
+
+	if( (abr = b->r) < 0.)
+		abr = - abr;
+	if( (abi = b->i) < 0.)
+		abi = - abi;
+	if( abr <= abi )
+		{
+		if(abi == 0) {
+#ifdef IEEE_COMPLEX_DIVIDE
+			float af, bf;
+			af = bf = abr;
+			if (a->i != 0 || a->r != 0)
+				af = 1.;
+			c->i = c->r = af / bf;
+			return;
+#else
+			sig_die("complex division by zero", 1);
+#endif
+			}
+		ratio = (double)b->r / b->i ;
+		den = b->i * (1 + ratio*ratio);
+		cr = (a->r*ratio + a->i) / den;
+		c->i = (a->i*ratio - a->r) / den;
+		}
+
+	else
+		{
+		ratio = (double)b->i / b->r ;
+		den = b->r * (1 + ratio*ratio);
+		cr = (a->r + a->i*ratio) / den;
+		c->i = (a->i - a->r*ratio) / den;
+		}
+	c->r = cr;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/c_exp.c b/igraph/src/c_exp.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/c_exp.c
@@ -0,0 +1,25 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+extern double exp(), cos(), sin();
+
+ VOID c_exp(r, z) f2c_complex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void c_exp(f2c_complex *r, f2c_complex *z)
+#endif
+{
+	double expx, zi = z->i;
+
+	expx = exp(z->r);
+	r->r = expx * cos(zi);
+	r->i = expx * sin(zi);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/c_log.c b/igraph/src/c_log.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/c_log.c
@@ -0,0 +1,23 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+extern double log(), f__cabs(), atan2();
+VOID c_log(r, z) f2c_complex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern double f__cabs(double, double);
+
+void c_log(f2c_complex *r, f2c_complex *z)
+#endif
+{
+	double zi, zr;
+	r->i = atan2(zi = z->i, zr = z->r);
+	r->r = log( f__cabs(zr, zi) );
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/c_sin.c b/igraph/src/c_sin.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/c_sin.c
@@ -0,0 +1,23 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+extern double sin(), cos(), sinh(), cosh();
+
+VOID c_sin(r, z) f2c_complex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void c_sin(f2c_complex *r, f2c_complex *z)
+#endif
+{
+	double zi = z->i, zr = z->r;
+	r->r = sin(zr) * cosh(zi);
+	r->i = cos(zr) * sinh(zi);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/c_sqrt.c b/igraph/src/c_sqrt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/c_sqrt.c
@@ -0,0 +1,41 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+extern double sqrt(), f__cabs();
+
+VOID c_sqrt(r, z) f2c_complex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern double f__cabs(double, double);
+
+void c_sqrt(f2c_complex *r, f2c_complex *z)
+#endif
+{
+	double mag, t;
+	double zi = z->i, zr = z->r;
+
+	if( (mag = f__cabs(zr, zi)) == 0.)
+		r->r = r->i = 0.;
+	else if(zr > 0)
+		{
+		r->r = t = sqrt(0.5 * (mag + zr) );
+		t = zi / t;
+		r->i = 0.5 * t;
+		}
+	else
+		{
+		t = sqrt(0.5 * (mag - zr) );
+		if(zi < 0)
+			t = -t;
+		r->i = t;
+		t = zi / t;
+		r->r = 0.5 * t;
+		}
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/cabs.c b/igraph/src/cabs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cabs.c
@@ -0,0 +1,33 @@
+#ifdef KR_headers
+extern double sqrt();
+double f__cabs(real, imag) double real, imag;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double f__cabs(double real, double imag)
+#endif
+{
+double temp;
+
+if(real < 0)
+	real = -real;
+if(imag < 0)
+	imag = -imag;
+if(imag > real){
+	temp = real;
+	real = imag;
+	imag = temp;
+}
+if((real+imag) == real)
+	return(real);
+
+temp = imag/real;
+temp = real*sqrt(1.0 + temp*temp);  /*overflow!!*/
+return(temp);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/cattributes.c b/igraph/src/cattributes.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cattributes.c
@@ -0,0 +1,4211 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_attributes.h"
+#include "igraph_memory.h"
+#include "config.h"
+#include "igraph_math.h"
+#include "igraph_interface.h"
+#include "igraph_random.h"
+
+#include <string.h>
+
+/* An attribute is either a numeric vector (vector_t) or a string
+   vector (strvector_t). The attribute itself is stored in a
+   struct igraph_attribute_record_t, there is one such object for each
+   attribute. The igraph_t has a pointer to an array of three
+   vector_ptr_t's which contains pointers to
+   igraph_i_cattribute_t's. Graph attributes are first, then vertex
+   and edge attributes. */
+
+igraph_bool_t igraph_i_cattribute_find(const igraph_vector_ptr_t *ptrvec,
+                                       const char *name, long int *idx) {
+    long int i, n = igraph_vector_ptr_size(ptrvec);
+    igraph_bool_t l = 0;
+    for (i = 0; !l && i < n; i++) {
+        igraph_attribute_record_t *rec = VECTOR(*ptrvec)[i];
+        l = !strcmp(rec->name, name);
+    }
+    if (idx) {
+        *idx = i - 1;
+    }
+    return l;
+}
+
+typedef struct igraph_i_cattributes_t {
+    igraph_vector_ptr_t gal;
+    igraph_vector_ptr_t val;
+    igraph_vector_ptr_t eal;
+} igraph_i_cattributes_t;
+
+int igraph_i_cattributes_copy_attribute_record(igraph_attribute_record_t **newrec,
+        const igraph_attribute_record_t *rec) {
+    igraph_vector_t *num, *newnum;
+    igraph_strvector_t *str, *newstr;
+
+    *newrec = igraph_Calloc(1, igraph_attribute_record_t);
+    if (!(*newrec)) {
+        IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, *newrec);
+    (*newrec)->type = rec->type;
+    (*newrec)->name = strdup(rec->name);
+    if (!(*newrec)->name) {
+        IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (void*)(*newrec)->name);
+    if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+        num = (igraph_vector_t *)rec->value;
+        newnum = igraph_Calloc(1, igraph_vector_t);
+        if (!newnum) {
+            IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, newnum);
+        IGRAPH_CHECK(igraph_vector_copy(newnum, num));
+        IGRAPH_FINALLY(igraph_vector_destroy, newnum);
+        (*newrec)->value = newnum;
+    } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+        str = (igraph_strvector_t*)rec->value;
+        newstr = igraph_Calloc(1, igraph_strvector_t);
+        if (!newstr) {
+            IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, newstr);
+        IGRAPH_CHECK(igraph_strvector_copy(newstr, str));
+        IGRAPH_FINALLY(igraph_strvector_destroy, newstr);
+        (*newrec)->value = newstr;
+    } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+        igraph_vector_bool_t *log = (igraph_vector_bool_t*) rec->value;
+        igraph_vector_bool_t *newlog = igraph_Calloc(1, igraph_vector_bool_t);
+        if (!newlog) {
+            IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, newlog);
+        IGRAPH_CHECK(igraph_vector_bool_copy(newlog, log));
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, newlog);
+        (*newrec)->value = newlog;
+    }
+
+    IGRAPH_FINALLY_CLEAN(4);
+    return 0;
+}
+
+
+int igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) {
+    igraph_attribute_record_t *attr_rec;
+    long int i, n;
+    igraph_i_cattributes_t *nattr;
+
+    n = attr ? igraph_vector_ptr_size(attr) : 0;
+
+    nattr = igraph_Calloc(1, igraph_i_cattributes_t);
+    if (!nattr) {
+        IGRAPH_ERROR("Can't init attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, nattr);
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->gal, n));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->gal);
+    IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->val, 0));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->val);
+    IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->eal, 0));
+    IGRAPH_FINALLY_CLEAN(3);
+
+    for (i = 0; i < n; i++) {
+        IGRAPH_CHECK(igraph_i_cattributes_copy_attribute_record(
+                         &attr_rec, VECTOR(*attr)[i]));
+        VECTOR(nattr->gal)[i] = attr_rec;
+    }
+
+    graph->attr = nattr;
+
+    return 0;
+}
+
+void igraph_i_cattribute_destroy(igraph_t *graph) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *als[3] = { &attr->gal, &attr->val, &attr->eal };
+    long int i, n, a;
+    igraph_vector_t *num;
+    igraph_strvector_t *str;
+    igraph_vector_bool_t *boolvec;
+    igraph_attribute_record_t *rec;
+    for (a = 0; a < 3; a++) {
+        n = igraph_vector_ptr_size(als[a]);
+        for (i = 0; i < n; i++) {
+            rec = VECTOR(*als[a])[i];
+            if (rec) {
+                if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                    num = (igraph_vector_t*)rec->value;
+                    igraph_vector_destroy(num);
+                    igraph_free(num);
+                } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+                    str = (igraph_strvector_t*)rec->value;
+                    igraph_strvector_destroy(str);
+                    igraph_free(str);
+                } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                    boolvec = (igraph_vector_bool_t*)rec->value;
+                    igraph_vector_bool_destroy(boolvec);
+                    igraph_free(boolvec);
+                }
+                igraph_free((char*)rec->name);
+                igraph_free(rec);
+            }
+        }
+    }
+    igraph_vector_ptr_destroy(&attr->gal);
+    igraph_vector_ptr_destroy(&attr->val);
+    igraph_vector_ptr_destroy(&attr->eal);
+    igraph_free(graph->attr);
+    graph->attr = 0;
+}
+
+/* Almost the same as destroy, but we might have null pointers */
+
+void igraph_i_cattribute_copy_free(igraph_i_cattributes_t *attr) {
+    igraph_vector_ptr_t *als[3] = { &attr->gal, &attr->val, &attr->eal };
+    long int i, n, a;
+    igraph_vector_t *num;
+    igraph_strvector_t *str;
+    igraph_vector_bool_t *boolvec;
+    igraph_attribute_record_t *rec;
+    for (a = 0; a < 3; a++) {
+        n = igraph_vector_ptr_size(als[a]);
+        for (i = 0; i < n; i++) {
+            rec = VECTOR(*als[a])[i];
+            if (!rec) {
+                continue;
+            }
+            if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                num = (igraph_vector_t*)rec->value;
+                igraph_vector_destroy(num);
+                igraph_free(num);
+            } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                boolvec = (igraph_vector_bool_t*)rec->value;
+                igraph_vector_bool_destroy(boolvec);
+                igraph_free(boolvec);
+            } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+                str = (igraph_strvector_t*)rec->value;
+                igraph_strvector_destroy(str);
+                igraph_free(str);
+            }
+            igraph_free((char*)rec->name);
+            igraph_free(rec);
+        }
+    }
+}
+
+/* No reference counting here. If you use attributes in C you should
+   know what you're doing. */
+
+int igraph_i_cattribute_copy(igraph_t *to, const igraph_t *from,
+                             igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) {
+    igraph_i_cattributes_t *attrfrom = from->attr, *attrto;
+    igraph_vector_ptr_t *alto[3], *alfrom[3] = { &attrfrom->gal, &attrfrom->val,
+                                                 &attrfrom->eal
+                                               };
+    long int i, n, a;
+    igraph_bool_t copy[3] = { ga, va, ea };
+    to->attr = attrto = igraph_Calloc(1, igraph_i_cattributes_t);
+    if (!attrto) {
+        IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, attrto);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->gal, 0);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->val, 0);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->eal, 0);
+    IGRAPH_FINALLY_CLEAN(3);
+    IGRAPH_FINALLY(igraph_i_cattribute_copy_free, attrto);
+
+    alto[0] = &attrto->gal; alto[1] = &attrto->val; alto[2] = &attrto->eal;
+    for (a = 0; a < 3; a++) {
+        if (copy[a]) {
+            n = igraph_vector_ptr_size(alfrom[a]);
+            IGRAPH_CHECK(igraph_vector_ptr_resize(alto[a], n));
+            igraph_vector_ptr_null(alto[a]);
+            for (i = 0; i < n; i++) {
+                igraph_attribute_record_t *newrec;
+                IGRAPH_CHECK(igraph_i_cattributes_copy_attribute_record(&newrec,
+                             VECTOR(*alfrom[a])[i]));
+                VECTOR(*alto[a])[i] = newrec;
+            }
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv,
+                                     igraph_vector_ptr_t *nattr) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int length = igraph_vector_ptr_size(val);
+    long int nattrno = nattr == NULL ? 0 : igraph_vector_ptr_size(nattr);
+    long int origlen = igraph_vcount(graph) - nv;
+    long int newattrs = 0, i;
+    igraph_vector_t news;
+
+    /* First add the new attributes if any */
+    newattrs = 0;
+    IGRAPH_VECTOR_INIT_FINALLY(&news, 0);
+    for (i = 0; i < nattrno; i++) {
+        igraph_attribute_record_t *nattr_entry = VECTOR(*nattr)[i];
+        const char *nname = nattr_entry->name;
+        long int j;
+        igraph_bool_t l = igraph_i_cattribute_find(val, nname, &j);
+        if (!l) {
+            newattrs++;
+            IGRAPH_CHECK(igraph_vector_push_back(&news, i));
+        } else {
+            /* check types */
+            if (nattr_entry->type !=
+                ((igraph_attribute_record_t*)VECTOR(*val)[j])->type) {
+                IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL);
+            }
+        }
+    }
+
+    /* Add NA/empty string vectors for the existing vertices */
+    if (newattrs != 0) {
+        for (i = 0; i < newattrs; i++) {
+            igraph_attribute_record_t *tmp = VECTOR(*nattr)[(long int)VECTOR(news)[i]];
+            igraph_attribute_record_t *newrec = igraph_Calloc(1, igraph_attribute_record_t);
+            igraph_attribute_type_t type = tmp->type;
+            if (!newrec) {
+                IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, newrec);
+            newrec->type = type;
+            newrec->name = strdup(tmp->name);
+            if (!newrec->name) {
+                IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, (char*)newrec->name);
+            if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_vector_t *newnum = igraph_Calloc(1, igraph_vector_t);
+                if (!newnum) {
+                    IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(igraph_free, newnum);
+                IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen);
+                newrec->value = newnum;
+                igraph_vector_fill(newnum, IGRAPH_NAN);
+            } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+                igraph_strvector_t *newstr = igraph_Calloc(1, igraph_strvector_t);
+                if (!newstr) {
+                    IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(igraph_free, newstr);
+                IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen);
+                newrec->value = newstr;
+            } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                igraph_vector_bool_t *newbool = igraph_Calloc(1, igraph_vector_bool_t);
+                if (!newbool) {
+                    IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(igraph_free, newbool);
+                IGRAPH_CHECK(igraph_vector_bool_init(newbool, origlen));
+                IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool);
+                newrec->value = newbool;
+                igraph_vector_bool_fill(newbool, 0);
+            }
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(val, newrec));
+            IGRAPH_FINALLY_CLEAN(4);
+        }
+        length = igraph_vector_ptr_size(val);
+    }
+
+    /* Now append the new values */
+    for (i = 0; i < length; i++) {
+        igraph_attribute_record_t *oldrec = VECTOR(*val)[i];
+        igraph_attribute_record_t *newrec = 0;
+        const char *name = oldrec->name;
+        long int j;
+        igraph_bool_t l = 0;
+        if (nattr) {
+            l = igraph_i_cattribute_find(nattr, name, &j);
+        }
+        if (l) {
+            /* This attribute is present in nattr */
+            igraph_vector_t *oldnum, *newnum;
+            igraph_strvector_t *oldstr, *newstr;
+            igraph_vector_bool_t *oldbool, *newbool;
+            newrec = VECTOR(*nattr)[j];
+            oldnum = (igraph_vector_t*)oldrec->value;
+            newnum = (igraph_vector_t*)newrec->value;
+            oldstr = (igraph_strvector_t*)oldrec->value;
+            newstr = (igraph_strvector_t*)newrec->value;
+            oldbool = (igraph_vector_bool_t*)oldrec->value;
+            newbool = (igraph_vector_bool_t*)newrec->value;
+            if (oldrec->type != newrec->type) {
+                IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL);
+            }
+            switch (oldrec->type) {
+            case IGRAPH_ATTRIBUTE_NUMERIC:
+                if (nv != igraph_vector_size(newnum)) {
+                    IGRAPH_ERROR("Invalid numeric attribute length", IGRAPH_EINVAL);
+                }
+                IGRAPH_CHECK(igraph_vector_append(oldnum, newnum));
+                break;
+            case IGRAPH_ATTRIBUTE_STRING:
+                if (nv != igraph_strvector_size(newstr)) {
+                    IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL);
+                }
+                IGRAPH_CHECK(igraph_strvector_append(oldstr, newstr));
+                break;
+            case IGRAPH_ATTRIBUTE_BOOLEAN:
+                if (nv != igraph_vector_bool_size(newbool)) {
+                    IGRAPH_ERROR("Invalid Boolean attribute length", IGRAPH_EINVAL);
+                }
+                IGRAPH_CHECK(igraph_vector_bool_append(oldbool, newbool));
+                break;
+            default:
+                IGRAPH_WARNING("Invalid attribute type");
+                break;
+            }
+        } else {
+            /* No such attribute, append NA's */
+            igraph_vector_t *oldnum = (igraph_vector_t *)oldrec->value;
+            igraph_strvector_t *oldstr = (igraph_strvector_t*)oldrec->value;
+            igraph_vector_bool_t *oldbool = (igraph_vector_bool_t*)oldrec->value;
+            switch (oldrec->type) {
+            case IGRAPH_ATTRIBUTE_NUMERIC:
+                IGRAPH_CHECK(igraph_vector_resize(oldnum, origlen + nv));
+                for (j = origlen; j < origlen + nv; j++) {
+                    VECTOR(*oldnum)[j] = IGRAPH_NAN;
+                }
+                break;
+            case IGRAPH_ATTRIBUTE_STRING:
+                IGRAPH_CHECK(igraph_strvector_resize(oldstr, origlen + nv));
+                break;
+            case IGRAPH_ATTRIBUTE_BOOLEAN:
+                IGRAPH_CHECK(igraph_vector_bool_resize(oldbool, origlen + nv));
+                for (j = origlen; j < origlen + nv; j++) {
+                    VECTOR(*oldbool)[j] = 0;
+                }
+                break;
+            default:
+                IGRAPH_WARNING("Invalid attribute type");
+                break;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&news);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+void igraph_i_cattribute_permute_free(igraph_vector_ptr_t *v) {
+    long int i, n = igraph_vector_ptr_size(v);
+    for (i = 0; i < n; i++) {
+        igraph_attribute_record_t *rec = VECTOR(*v)[i];
+        igraph_Free(rec->name);
+        if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+            igraph_vector_t *numv = (igraph_vector_t*) rec->value;
+            igraph_vector_destroy(numv);
+            igraph_Free(numv);
+        } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+            igraph_strvector_t *strv = (igraph_strvector_t*) rec->value;
+            igraph_strvector_destroy(strv);
+            igraph_Free(strv);
+        } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            igraph_vector_bool_t *boolv = (igraph_vector_bool_t*) rec->value;
+            igraph_vector_bool_destroy(boolv);
+            igraph_Free(boolv);
+        }
+        igraph_Free(rec);
+    }
+    igraph_vector_ptr_clear(v);
+}
+
+int igraph_i_cattribute_permute_vertices(const igraph_t *graph,
+        igraph_t *newgraph,
+        const igraph_vector_t *idx) {
+
+    if (graph == newgraph) {
+
+        igraph_i_cattributes_t *attr = graph->attr;
+        igraph_vector_ptr_t *val = &attr->val;
+        long int valno = igraph_vector_ptr_size(val);
+        long int i;
+
+        for (i = 0; i < valno; i++) {
+            igraph_attribute_record_t *oldrec = VECTOR(*val)[i];
+            igraph_attribute_type_t type = oldrec->type;
+            igraph_vector_t *num, *newnum;
+            igraph_strvector_t *str, *newstr;
+            igraph_vector_bool_t *oldbool, *newbool;
+            switch (type) {
+            case IGRAPH_ATTRIBUTE_NUMERIC:
+                num = (igraph_vector_t*) oldrec->value;
+                newnum = igraph_Calloc(1, igraph_vector_t);
+                if (!newnum) {
+                    IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_VECTOR_INIT_FINALLY(newnum, 0);
+                igraph_vector_index(num, newnum, idx);
+                oldrec->value = newnum;
+                igraph_vector_destroy(num);
+                igraph_Free(num);
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            case IGRAPH_ATTRIBUTE_BOOLEAN:
+                oldbool = (igraph_vector_bool_t*) oldrec->value;
+                newbool = igraph_Calloc(1, igraph_vector_bool_t);
+                if (!newbool) {
+                    IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_vector_bool_init(newbool, 0));
+                IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool);
+                igraph_vector_bool_index(oldbool, newbool, idx);
+                oldrec->value = newbool;
+                igraph_vector_bool_destroy(oldbool);
+                igraph_Free(oldbool);
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            case IGRAPH_ATTRIBUTE_STRING:
+                str = (igraph_strvector_t*)oldrec->value;
+                newstr = igraph_Calloc(1, igraph_strvector_t);
+                if (!newstr) {
+                    IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_strvector_init(newstr, 0));
+                IGRAPH_FINALLY(igraph_strvector_destroy, newstr);
+                igraph_strvector_index(str, newstr, idx);
+                oldrec->value = newstr;
+                igraph_strvector_destroy(str);
+                igraph_Free(str);
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            default:
+                IGRAPH_WARNING("Unknown edge attribute ignored");
+            }
+        }
+
+    } else {
+        igraph_i_cattributes_t *attr = graph->attr;
+        igraph_vector_ptr_t *val = &attr->val;
+        long int valno = igraph_vector_ptr_size(val);
+        long int i;
+
+        /* New vertex attributes */
+        igraph_i_cattributes_t *new_attr = newgraph->attr;
+        igraph_vector_ptr_t *new_val = &new_attr->val;
+        if (igraph_vector_ptr_size(new_val) != 0) {
+            IGRAPH_ERROR("Vertex attributes were already copied",
+                         IGRAPH_EATTRIBUTES);
+        }
+        IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno));
+
+        IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val);
+
+        for (i = 0; i < valno; i++) {
+            igraph_attribute_record_t *oldrec = VECTOR(*val)[i];
+            igraph_attribute_type_t type = oldrec->type;
+            igraph_vector_t *num, *newnum;
+            igraph_strvector_t *str, *newstr;
+            igraph_vector_bool_t *oldbool, *newbool;
+
+            /* The record itself */
+            igraph_attribute_record_t *new_rec =
+                igraph_Calloc(1, igraph_attribute_record_t);
+            if (!new_rec) {
+                IGRAPH_ERROR("Cannot create vertex attributes", IGRAPH_ENOMEM);
+            }
+            new_rec->name = strdup(oldrec->name);
+            new_rec->type = oldrec->type;
+            VECTOR(*new_val)[i] = new_rec;
+
+            /* The data */
+            switch (type) {
+            case IGRAPH_ATTRIBUTE_NUMERIC:
+                num = (igraph_vector_t*)oldrec->value;
+                newnum = igraph_Calloc(1, igraph_vector_t);
+                if (!newnum) {
+                    IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_VECTOR_INIT_FINALLY(newnum, 0);
+                igraph_vector_index(num, newnum, idx);
+                new_rec->value = newnum;
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            case IGRAPH_ATTRIBUTE_BOOLEAN:
+                oldbool = (igraph_vector_bool_t*)oldrec->value;
+                newbool = igraph_Calloc(1, igraph_vector_bool_t);
+                if (!newbool) {
+                    IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_vector_bool_init(newbool, 0));
+                IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool);
+                igraph_vector_bool_index(oldbool, newbool, idx);
+                new_rec->value = newbool;
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            case IGRAPH_ATTRIBUTE_STRING:
+                str = (igraph_strvector_t*)oldrec->value;
+                newstr = igraph_Calloc(1, igraph_strvector_t);
+                if (!newstr) {
+                    IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_strvector_init(newstr, 0));
+                IGRAPH_FINALLY(igraph_strvector_destroy, newstr);
+                igraph_strvector_index(str, newstr, idx);
+                new_rec->value = newstr;
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            default:
+                IGRAPH_WARNING("Unknown vertex attribute ignored");
+            }
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+typedef int igraph_cattributes_combine_num_t(const igraph_vector_t *input,
+        igraph_real_t *output);
+
+typedef int igraph_cattributes_combine_str_t(const igraph_strvector_t *input,
+        char **output);
+
+typedef int igraph_cattributes_combine_bool_t(const igraph_vector_bool_t *input,
+        igraph_bool_t *output);
+
+int igraph_i_cattributes_cn_sum(const igraph_attribute_record_t *oldrec,
+                                igraph_attribute_record_t * newrec,
+                                const igraph_vector_ptr_t *merges) {
+    const igraph_vector_t *oldv = oldrec->value;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_real_t s = 0.0;
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        for (j = 0; j < n; j++) {
+            long int x = (long int) VECTOR(*idx)[j];
+            s += VECTOR(*oldv)[x];
+        }
+        VECTOR(*newv)[i] = s;
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec,
+                                 igraph_attribute_record_t * newrec,
+                                 const igraph_vector_ptr_t *merges) {
+    const igraph_vector_t *oldv = oldrec->value;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_real_t s = 1.0;
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        for (j = 0; j < n; j++) {
+            long int x = (long int) VECTOR(*idx)[j];
+            s *= VECTOR(*oldv)[x];
+        }
+        VECTOR(*newv)[i] = s;
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec,
+                                igraph_attribute_record_t * newrec,
+                                const igraph_vector_ptr_t *merges) {
+    const igraph_vector_t *oldv = oldrec->value;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_real_t nan = IGRAPH_NAN;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        igraph_real_t m = n > 0 ? VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ] : nan;
+        for (j = 1; j < n; j++) {
+            long int x = (long int) VECTOR(*idx)[j];
+            igraph_real_t val = VECTOR(*oldv)[x];
+            if (val < m) {
+                m = val;
+            }
+        }
+        VECTOR(*newv)[i] = m;
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec,
+                                igraph_attribute_record_t * newrec,
+                                const igraph_vector_ptr_t *merges) {
+    const igraph_vector_t *oldv = oldrec->value;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_real_t nan = IGRAPH_NAN;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        igraph_real_t m = n > 0 ? VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ] : nan;
+        for (j = 1; j < n; j++) {
+            long int x = (long int) VECTOR(*idx)[j];
+            igraph_real_t val = VECTOR(*oldv)[x];
+            if (val > m) {
+                m = val;
+            }
+        }
+        VECTOR(*newv)[i] = m;
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldrec,
+                                   igraph_attribute_record_t * newrec,
+                                   const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_t *oldv = oldrec->value;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_real_t nan = IGRAPH_NAN;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        if (n == 0) {
+            VECTOR(*newv)[i] = nan;
+        } else if (n == 1) {
+            VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ];
+        } else {
+            long int r = RNG_INTEGER(0, n - 1);
+            VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[r] ];
+        }
+    }
+
+    RNG_END();
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cn_first(const igraph_attribute_record_t *oldrec,
+                                  igraph_attribute_record_t * newrec,
+                                  const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_t *oldv = oldrec->value;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_real_t nan = IGRAPH_NAN;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        if (n == 0) {
+            VECTOR(*newv)[i] = nan;
+        } else {
+            VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ];
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cn_last(const igraph_attribute_record_t *oldrec,
+                                 igraph_attribute_record_t * newrec,
+                                 const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_t *oldv = oldrec->value;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_real_t nan = IGRAPH_NAN;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        if (n == 0) {
+            VECTOR(*newv)[i] = nan;
+        } else {
+            VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[n - 1] ];
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec,
+                                 igraph_attribute_record_t * newrec,
+                                 const igraph_vector_ptr_t *merges) {
+    const igraph_vector_t *oldv = oldrec->value;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_real_t nan = IGRAPH_NAN;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        igraph_real_t s = n > 0 ? 0.0 : nan;
+        for (j = 0; j < n; j++) {
+            long int x = (long int) VECTOR(*idx)[j];
+            s += VECTOR(*oldv)[x];
+        }
+        if (n > 0) {
+            s = s / n;
+        }
+        VECTOR(*newv)[i] = s;
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec,
+                                 igraph_attribute_record_t *newrec,
+                                 const igraph_vector_ptr_t *merges,
+                                 igraph_cattributes_combine_num_t *func) {
+
+    const igraph_vector_t *oldv = oldrec->value;
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_vector_t *newv = igraph_Calloc(1, igraph_vector_t);
+    igraph_vector_t values;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_VECTOR_INIT_FINALLY(newv, newlen);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&values, 0);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        igraph_real_t res;
+        IGRAPH_CHECK(igraph_vector_resize(&values, n));
+        for (j = 0; j < n; j++) {
+            long int x = (long int) VECTOR(*idx)[j];
+            VECTOR(values)[j] = VECTOR(*oldv)[x];
+        }
+        IGRAPH_CHECK(func(&values, &res));
+        VECTOR(*newv)[i] = res;
+    }
+
+    igraph_vector_destroy(&values);
+    IGRAPH_FINALLY_CLEAN(3);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cb_random(const igraph_attribute_record_t *oldrec,
+                                   igraph_attribute_record_t * newrec,
+                                   const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_bool_t *oldv = oldrec->value;
+    igraph_vector_bool_t *newv = igraph_Calloc(1, igraph_vector_bool_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, newv);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        if (n == 0) {
+            VECTOR(*newv)[i] = 0;
+        } else if (n == 1) {
+            VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ];
+        } else {
+            long int r = RNG_INTEGER(0, n - 1);
+            VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[r] ];
+        }
+    }
+
+    RNG_END();
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cb_first(const igraph_attribute_record_t *oldrec,
+                                  igraph_attribute_record_t * newrec,
+                                  const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_bool_t *oldv = oldrec->value;
+    igraph_vector_bool_t *newv = igraph_Calloc(1, igraph_vector_bool_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, newv);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        if (n == 0) {
+            VECTOR(*newv)[i] = 0;
+        } else {
+            VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ];
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cb_last(const igraph_attribute_record_t *oldrec,
+                                 igraph_attribute_record_t * newrec,
+                                 const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_bool_t *oldv = oldrec->value;
+    igraph_vector_bool_t *newv = igraph_Calloc(1, igraph_vector_bool_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, newv);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        if (n == 0) {
+            VECTOR(*newv)[i] = 0;
+        } else {
+            VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[n - 1] ];
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cb_all_is_true(const igraph_attribute_record_t *oldrec,
+                                        igraph_attribute_record_t * newrec,
+                                        const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_bool_t *oldv = oldrec->value;
+    igraph_vector_bool_t *newv = igraph_Calloc(1, igraph_vector_bool_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i, j, n, x;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, newv);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        n = igraph_vector_size(idx);
+        VECTOR(*newv)[i] = 1;
+        for (j = 0; j < n; j++) {
+            x = (long int) VECTOR(*idx)[j];
+            if (!VECTOR(*oldv)[x]) {
+                VECTOR(*newv)[i] = 0;
+                break;
+            }
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cb_any_is_true(const igraph_attribute_record_t *oldrec,
+                                        igraph_attribute_record_t * newrec,
+                                        const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_bool_t *oldv = oldrec->value;
+    igraph_vector_bool_t *newv = igraph_Calloc(1, igraph_vector_bool_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i, j, n, x;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, newv);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        n = igraph_vector_size(idx);
+        VECTOR(*newv)[i] = 0;
+        for (j = 0; j < n; j++) {
+            x = (long int) VECTOR(*idx)[j];
+            if (VECTOR(*oldv)[x]) {
+                VECTOR(*newv)[i] = 1;
+                break;
+            }
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cb_majority(const igraph_attribute_record_t *oldrec,
+                                     igraph_attribute_record_t * newrec,
+                                     const igraph_vector_ptr_t *merges) {
+
+    const igraph_vector_bool_t *oldv = oldrec->value;
+    igraph_vector_bool_t *newv = igraph_Calloc(1, igraph_vector_bool_t);
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i, j, n, x, num_trues;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, newv);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+
+        n = igraph_vector_size(idx);
+
+        num_trues = 0;
+        for (j = 0; j < n; j++) {
+            x = (long int) VECTOR(*idx)[j];
+            if (VECTOR(*oldv)[x]) {
+                num_trues++;
+            }
+        }
+
+        if (n % 2 != 0) {
+            VECTOR(*newv)[i] = (num_trues > n / 2);
+        } else {
+            if (num_trues == n / 2) {
+                VECTOR(*newv)[i] = (RNG_UNIF01() < 0.5);
+            } else {
+                VECTOR(*newv)[i] = (num_trues > n / 2);
+            }
+        }
+    }
+
+    RNG_END();
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec,
+                                 igraph_attribute_record_t *newrec,
+                                 const igraph_vector_ptr_t *merges,
+                                 igraph_cattributes_combine_bool_t *func) {
+
+    const igraph_vector_bool_t *oldv = oldrec->value;
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_vector_bool_t *newv = igraph_Calloc(1, igraph_vector_bool_t);
+    igraph_vector_bool_t values;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, newv);
+
+    IGRAPH_CHECK(igraph_vector_bool_init(&values, 0));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, newv);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        igraph_bool_t res;
+
+        IGRAPH_CHECK(igraph_vector_bool_resize(&values, n));
+        for (j = 0; j < n; j++) {
+            long int x = (long int) VECTOR(*idx)[j];
+            VECTOR(values)[j] = VECTOR(*oldv)[x];
+        }
+
+        IGRAPH_CHECK(func(&values, &res));
+        VECTOR(*newv)[i] = res;
+    }
+
+    igraph_vector_bool_destroy(&values);
+    IGRAPH_FINALLY_CLEAN(3);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec,
+                                   igraph_attribute_record_t *newrec,
+                                   const igraph_vector_ptr_t *merges) {
+
+    const igraph_strvector_t *oldv = oldrec->value;
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_strvector_t *newv = igraph_Calloc(1, igraph_strvector_t);
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_strvector_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_strvector_destroy, newv);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        char *tmp;
+        if (n == 0) {
+            IGRAPH_CHECK(igraph_strvector_set(newv, i, ""));
+        } else if (n == 1) {
+            igraph_strvector_get(oldv, 0, &tmp);
+            IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp));
+        } else {
+            long int r = RNG_INTEGER(0, n - 1);
+            igraph_strvector_get(oldv, r, &tmp);
+            IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp));
+        }
+    }
+
+    RNG_END();
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec,
+                                  igraph_attribute_record_t *newrec,
+                                  const igraph_vector_ptr_t *merges) {
+
+    const igraph_strvector_t *oldv = oldrec->value;
+    long int i, newlen = igraph_vector_ptr_size(merges);
+    igraph_strvector_t *newv = igraph_Calloc(1, igraph_strvector_t);
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_strvector_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_strvector_destroy, newv);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        if (n == 0) {
+            IGRAPH_CHECK(igraph_strvector_set(newv, i, ""));
+        } else {
+            char *tmp;
+            igraph_strvector_get(oldv, (long int) VECTOR(*idx)[0], &tmp);
+            IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp));
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec,
+                                 igraph_attribute_record_t *newrec,
+                                 const igraph_vector_ptr_t *merges) {
+
+    const igraph_strvector_t *oldv = oldrec->value;
+    long int i, newlen = igraph_vector_ptr_size(merges);
+    igraph_strvector_t *newv = igraph_Calloc(1, igraph_strvector_t);
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_strvector_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_strvector_destroy, newv);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int n = igraph_vector_size(idx);
+        if (n == 0) {
+            IGRAPH_CHECK(igraph_strvector_set(newv, i, ""));
+        } else {
+            char *tmp;
+            igraph_strvector_get(oldv, (long int) VECTOR(*idx)[n - 1], &tmp);
+            IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp));
+        }
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldrec,
+                                   igraph_attribute_record_t *newrec,
+                                   const igraph_vector_ptr_t *merges) {
+
+    const igraph_strvector_t *oldv = oldrec->value;
+    long int i, newlen = igraph_vector_ptr_size(merges);
+    igraph_strvector_t *newv = igraph_Calloc(1, igraph_strvector_t);
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_strvector_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_strvector_destroy, newv);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        size_t len = 0;
+        char *tmp, *tmp2;
+        for (j = 0; j < n; j++) {
+            igraph_strvector_get(oldv, j, &tmp);
+            len += strlen(tmp);
+        }
+        tmp2 = igraph_Calloc(len + 1, char);
+        if (!tmp2) {
+            IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, tmp2);
+        len = 0;
+        for (j = 0; j < n; j++) {
+            igraph_strvector_get(oldv, j, &tmp);
+            strcpy(tmp2 + len, tmp);
+            len += strlen(tmp);
+        }
+
+        IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp2));
+        igraph_Free(tmp2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    newrec->value = newv;
+
+    return 0;
+}
+
+int igraph_i_cattributes_sn_func(const igraph_attribute_record_t *oldrec,
+                                 igraph_attribute_record_t *newrec,
+                                 const igraph_vector_ptr_t *merges,
+                                 igraph_cattributes_combine_str_t *func) {
+
+    const igraph_strvector_t *oldv = oldrec->value;
+    long int newlen = igraph_vector_ptr_size(merges);
+    long int i;
+    igraph_strvector_t *newv = igraph_Calloc(1, igraph_strvector_t);
+    igraph_strvector_t values;
+
+    if (!newv) {
+        IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newv);
+    IGRAPH_CHECK(igraph_strvector_init(newv, newlen));
+    IGRAPH_FINALLY(igraph_strvector_destroy, newv);
+
+    IGRAPH_CHECK(igraph_strvector_init(newv, 0));
+    IGRAPH_FINALLY(igraph_strvector_destroy, &values);
+
+    for (i = 0; i < newlen; i++) {
+        igraph_vector_t *idx = VECTOR(*merges)[i];
+        long int j, n = igraph_vector_size(idx);
+        char *res;
+        IGRAPH_CHECK(igraph_strvector_resize(&values, n));
+        for (j = 0; j < n; j++) {
+            long int x = (long int) VECTOR(*idx)[j];
+            char *elem;
+            igraph_strvector_get(oldv, x, &elem);
+            IGRAPH_CHECK(igraph_strvector_set(newv, j, elem));
+        }
+        IGRAPH_CHECK(func(&values, &res));
+        IGRAPH_FINALLY(igraph_free, res);
+        IGRAPH_CHECK(igraph_strvector_set(newv, i, res));
+        IGRAPH_FINALLY_CLEAN(1);
+        igraph_Free(res);
+    }
+
+    igraph_strvector_destroy(&values);
+    IGRAPH_FINALLY_CLEAN(3);
+    newrec->value = newv;
+
+    return 0;
+}
+
+
+int igraph_i_cattribute_combine_vertices(const igraph_t *graph,
+        igraph_t *newgraph,
+        const igraph_vector_ptr_t *merges,
+        const igraph_attribute_combination_t *comb) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_i_cattributes_t *toattr = newgraph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    igraph_vector_ptr_t *new_val = &toattr->val;
+    long int valno = igraph_vector_ptr_size(val);
+    long int i, j, keepno = 0;
+    int *TODO;
+    igraph_function_pointer_t *funcs;
+
+    TODO = igraph_Calloc(valno, int);
+    if (!TODO) {
+        IGRAPH_ERROR("Cannot combine vertex attributes",
+                     IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, TODO);
+    funcs = igraph_Calloc(valno, igraph_function_pointer_t);
+    if (!funcs) {
+        IGRAPH_ERROR("Cannot combine vertex attributes",
+                     IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, funcs);
+
+    for (i = 0; i < valno; i++) {
+        igraph_attribute_record_t *oldrec = VECTOR(*val)[i];
+        const char *name = oldrec->name;
+        igraph_attribute_combination_type_t todo;
+        igraph_function_pointer_t voidfunc;
+        igraph_attribute_combination_query(comb, name, &todo, &voidfunc);
+        TODO[i] = todo;
+        funcs[i] = voidfunc;
+        if (todo != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) {
+            keepno++;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, keepno));
+    IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val);
+
+    for (i = 0, j = 0; i < valno; i++) {
+        igraph_attribute_record_t *newrec, *oldrec = VECTOR(*val)[i];
+        const char *name = oldrec->name;
+        igraph_attribute_combination_type_t todo =
+            (igraph_attribute_combination_type_t) (TODO[i]);
+        igraph_attribute_type_t type = oldrec->type;
+        igraph_cattributes_combine_num_t *numfunc =
+            (igraph_cattributes_combine_num_t*) funcs[i];
+        igraph_cattributes_combine_str_t *strfunc =
+            (igraph_cattributes_combine_str_t*) funcs[i];
+        igraph_cattributes_combine_bool_t *boolfunc =
+            (igraph_cattributes_combine_bool_t*) funcs[i];
+
+        if (todo == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT ||
+            todo == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) {
+            continue;
+        }
+
+        newrec = igraph_Calloc(1, igraph_attribute_record_t);
+        if (!newrec) {
+            IGRAPH_ERROR("Cannot combine vertex attributes",
+                         IGRAPH_ENOMEM);
+        }
+        newrec->name = strdup(name);
+        newrec->type = type;
+        VECTOR(*new_val)[j] = newrec;
+
+        if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+            switch (todo) {
+            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges,
+                             numfunc));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_prod(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_min(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_max(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_random(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_first(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_last(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_mean(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
+                IGRAPH_ERROR("Median calculation not implemented",
+                             IGRAPH_UNIMPLEMENTED);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
+                IGRAPH_ERROR("Cannot concatenate numeric attributes",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            default:
+                IGRAPH_ERROR("Unknown attribute_combination",
+                             IGRAPH_UNIMPLEMENTED);
+                break;
+            }
+        } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            switch (todo) {
+            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges,
+                             boolfunc));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
+            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_any_is_true(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
+            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_all_is_true(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
+            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_majority(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_random(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_first(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_last(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
+                IGRAPH_ERROR("Cannot calculate concatenation of Booleans",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            default:
+                IGRAPH_ERROR("Unknown attribute_combination",
+                             IGRAPH_UNIMPLEMENTED);
+                break;
+            }
+        } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+            switch (todo) {
+            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges,
+                             strfunc));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
+                IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
+                IGRAPH_ERROR("Cannot multiply strings", IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
+                IGRAPH_ERROR("Cannot find minimum of strings",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
+                IGRAPH_ERROR("Cannot find maximum of strings",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
+                IGRAPH_ERROR("Cannot calculate mean of strings",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
+                IGRAPH_ERROR("Cannot calculate median of strings",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_random(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_first(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_last(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_concat(oldrec, newrec, merges));
+                break;
+            default:
+                IGRAPH_ERROR("Unknown attribute_combination",
+                             IGRAPH_UNIMPLEMENTED);
+                break;
+            }
+        } else {
+            IGRAPH_ERROR("Unknown attribute type, this should not happen",
+                         IGRAPH_UNIMPLEMENTED);
+        }
+
+        j++;
+    }
+
+    igraph_free(funcs);
+    igraph_free(TODO);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/* void igraph_i_cattribute_delete_vertices(igraph_t *graph, */
+/*                     const igraph_vector_t *eidx, */
+/*                     const igraph_vector_t *vidx) { */
+
+/*   igraph_i_cattributes_t *attr=graph->attr; */
+/*   igraph_vector_ptr_t *val=&attr->val; */
+/*   igraph_vector_ptr_t *eal=&attr->eal; */
+/*   long int valno=igraph_vector_ptr_size(val); */
+/*   long int ealno=igraph_vector_ptr_size(eal); */
+/*   long int i; */
+/*   long int origlen, newlen; */
+
+/*   /\* Vertices *\/ */
+/*   origlen=igraph_vector_size(vidx); */
+/*   newlen=0; */
+/*   for (i=0; i<origlen; i++) { */
+/*     if (VECTOR(*vidx)[i]>0) { */
+/*       newlen++; */
+/*     } */
+/*   } */
+/*   for (i=0; i<valno; i++) { */
+/*     igraph_attribute_record_t *oldrec=VECTOR(*val)[i]; */
+/*     igraph_attribute_type_t type=oldrec->type; */
+/*     igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */
+/*     igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */
+/*     switch (type) { */
+/*     case IGRAPH_ATTRIBUTE_NUMERIC: */
+/*       igraph_vector_permdelete(num, vidx, origlen-newlen); */
+/*       break; */
+/*     case IGRAPH_ATTRIBUTE_STRING: */
+/*       igraph_strvector_permdelete(str, vidx, origlen-newlen); */
+/*       break; */
+/*     default: */
+/*       IGRAPH_WARNING("Unknown vertex attribute ignored"); */
+/*     } */
+/*   } */
+
+/*   /\* Edges *\/ */
+/*   origlen=igraph_vector_size(eidx); */
+/*   newlen=0; */
+/*   for (i=0; i<origlen; i++) { */
+/*     if (VECTOR(*eidx)[i]>0) { */
+/*       newlen++; */
+/*     } */
+/*   } */
+/*   for (i=0; i<ealno; i++) { */
+/*     igraph_attribute_record_t *oldrec=VECTOR(*eal)[i]; */
+/*     igraph_attribute_type_t type=oldrec->type; */
+/*     igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */
+/*     igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */
+/*     switch (type) { */
+/*     case IGRAPH_ATTRIBUTE_NUMERIC: */
+/*       igraph_vector_permdelete(num, eidx, origlen-newlen); */
+/*       break; */
+/*     case IGRAPH_ATTRIBUTE_STRING: */
+/*       igraph_strvector_permdelete(str, eidx, origlen-newlen); */
+/*       break; */
+/*     default: */
+/*       IGRAPH_WARNING("Unknown edge attribute ignored"); */
+/*     } */
+/*   } */
+/* } */
+
+int igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_t *edges,
+                                  igraph_vector_ptr_t *nattr) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int ealno = igraph_vector_ptr_size(eal);
+    long int ne = igraph_vector_size(edges) / 2;
+    long int origlen = igraph_ecount(graph) - ne;
+    long int nattrno = nattr == 0 ? 0 : igraph_vector_ptr_size(nattr);
+    igraph_vector_t news;
+    long int newattrs, i;
+
+    /* First add the new attributes if any */
+    newattrs = 0;
+    IGRAPH_VECTOR_INIT_FINALLY(&news, 0);
+    for (i = 0; i < nattrno; i++) {
+        igraph_attribute_record_t *nattr_entry = VECTOR(*nattr)[i];
+        const char *nname = nattr_entry->name;
+        long int j;
+        igraph_bool_t l = igraph_i_cattribute_find(eal, nname, &j);
+        if (!l) {
+            newattrs++;
+            IGRAPH_CHECK(igraph_vector_push_back(&news, i));
+        } else {
+            /* check types */
+            if (nattr_entry->type !=
+                ((igraph_attribute_record_t*)VECTOR(*eal)[j])->type) {
+                IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL);
+            }
+        }
+    }
+
+    /* Add NA/empty string vectors for the existing vertices */
+    if (newattrs != 0) {
+        for (i = 0; i < newattrs; i++) {
+            igraph_attribute_record_t *tmp = VECTOR(*nattr)[(long int)VECTOR(news)[i]];
+            igraph_attribute_record_t *newrec = igraph_Calloc(1, igraph_attribute_record_t);
+            igraph_attribute_type_t type = tmp->type;
+            if (!newrec) {
+                IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, newrec);
+            newrec->type = type;
+            newrec->name = strdup(tmp->name);
+            if (!newrec->name) {
+                IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, (char*)newrec->name);
+            if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_vector_t *newnum = igraph_Calloc(1, igraph_vector_t);
+                if (!newnum) {
+                    IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(igraph_free, newnum);
+                IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen);
+                newrec->value = newnum;
+                igraph_vector_fill(newnum, IGRAPH_NAN);
+            } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                igraph_vector_bool_t *newbool = igraph_Calloc(1, igraph_vector_bool_t);
+                if (!newbool) {
+                    IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(igraph_free, newbool);
+                IGRAPH_CHECK(igraph_vector_bool_init(newbool, origlen));
+                IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool);
+                newrec->value = newbool;
+                igraph_vector_bool_fill(newbool, 0);
+            } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+                igraph_strvector_t *newstr = igraph_Calloc(1, igraph_strvector_t);
+                if (!newstr) {
+                    IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(igraph_free, newstr);
+                IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen);
+                newrec->value = newstr;
+            }
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, newrec));
+            IGRAPH_FINALLY_CLEAN(4);
+        }
+        ealno = igraph_vector_ptr_size(eal);
+    }
+
+    /* Now append the new values */
+    for (i = 0; i < ealno; i++) {
+        igraph_attribute_record_t *oldrec = VECTOR(*eal)[i];
+        igraph_attribute_record_t *newrec = 0;
+        const char *name = oldrec->name;
+        long int j;
+        igraph_bool_t l = 0;
+        if (nattr) {
+            l = igraph_i_cattribute_find(nattr, name, &j);
+        }
+        if (l) {
+            /* This attribute is present in nattr */
+            igraph_vector_t *oldnum, *newnum;
+            igraph_strvector_t *oldstr, *newstr;
+            igraph_vector_bool_t *oldbool, *newbool;
+            newrec = VECTOR(*nattr)[j];
+            oldnum = (igraph_vector_t*)oldrec->value;
+            newnum = (igraph_vector_t*)newrec->value;
+            oldstr = (igraph_strvector_t*)oldrec->value;
+            newstr = (igraph_strvector_t*)newrec->value;
+            oldbool = (igraph_vector_bool_t*)oldrec->value;
+            newbool = (igraph_vector_bool_t*)newrec->value;
+            if (oldrec->type != newrec->type) {
+                IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL);
+            }
+            switch (oldrec->type) {
+            case IGRAPH_ATTRIBUTE_NUMERIC:
+                if (ne != igraph_vector_size(newnum)) {
+                    IGRAPH_ERROR("Invalid numeric attribute length", IGRAPH_EINVAL);
+                }
+                IGRAPH_CHECK(igraph_vector_append(oldnum, newnum));
+                break;
+            case IGRAPH_ATTRIBUTE_STRING:
+                if (ne != igraph_strvector_size(newstr)) {
+                    IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL);
+                }
+                IGRAPH_CHECK(igraph_strvector_append(oldstr, newstr));
+                break;
+            case IGRAPH_ATTRIBUTE_BOOLEAN:
+                if (ne != igraph_vector_bool_size(newbool)) {
+                    IGRAPH_ERROR("Invalid Boolean attribute length", IGRAPH_EINVAL);
+                }
+                IGRAPH_CHECK(igraph_vector_bool_append(oldbool, newbool));
+                break;
+            default:
+                IGRAPH_WARNING("Invalid attribute type");
+                break;
+            }
+        } else {
+            /* No such attribute, append NA's */
+            igraph_vector_t *oldnum = (igraph_vector_t *)oldrec->value;
+            igraph_strvector_t *oldstr = (igraph_strvector_t*)oldrec->value;
+            igraph_vector_bool_t *oldbool = (igraph_vector_bool_t *)oldrec->value;
+            switch (oldrec->type) {
+            case IGRAPH_ATTRIBUTE_NUMERIC:
+                IGRAPH_CHECK(igraph_vector_resize(oldnum, origlen + ne));
+                for (j = origlen; j < origlen + ne; j++) {
+                    VECTOR(*oldnum)[j] = IGRAPH_NAN;
+                }
+                break;
+            case IGRAPH_ATTRIBUTE_STRING:
+                IGRAPH_CHECK(igraph_strvector_resize(oldstr, origlen + ne));
+                break;
+            case IGRAPH_ATTRIBUTE_BOOLEAN:
+                IGRAPH_CHECK(igraph_vector_bool_resize(oldbool, origlen + ne));
+                for (j = origlen; j < origlen + ne; j++) {
+                    VECTOR(*oldbool)[j] = 0;
+                }
+                break;
+            default:
+                IGRAPH_WARNING("Invalid attribute type");
+                break;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&news);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/* void igraph_i_cattribute_delete_edges(igraph_t *graph, const igraph_vector_t *idx) { */
+
+/*   igraph_i_cattributes_t *attr=graph->attr; */
+/*   igraph_vector_ptr_t *eal=&attr->eal; */
+/*   long int ealno=igraph_vector_ptr_size(eal); */
+/*   long int i; */
+/*   long int origlen=igraph_vector_size(idx), newlen; */
+
+/*   newlen=0; */
+/*   for (i=0; i<origlen; i++) { */
+/*     if (VECTOR(*idx)[i]>0) { */
+/*       newlen++; */
+/*     } */
+/*   } */
+/*   for (i=0; i<ealno; i++) { */
+/*     igraph_attribute_record_t *oldrec=VECTOR(*eal)[i]; */
+/*     igraph_attribute_type_t type=oldrec->type; */
+/*     igraph_vector_t *num=(igraph_vector_t*)oldrec->value; */
+/*     igraph_strvector_t *str=(igraph_strvector_t*)oldrec->value; */
+/*     switch (type) { */
+/*     case IGRAPH_ATTRIBUTE_NUMERIC: */
+/*       igraph_vector_permdelete(num, idx, origlen-newlen); */
+/*       break; */
+/*     case IGRAPH_ATTRIBUTE_STRING: */
+/*       igraph_strvector_permdelete(str, idx, origlen-newlen); */
+/*       break; */
+/*     default: */
+/*       IGRAPH_WARNING("Unknown edge attribute ignored"); */
+/*     } */
+/*   } */
+
+/* } */
+
+int igraph_i_cattribute_permute_edges(const igraph_t *graph,
+                                      igraph_t *newgraph,
+                                      const igraph_vector_t *idx) {
+
+    if (graph == newgraph) {
+
+        igraph_i_cattributes_t *attr = graph->attr;
+        igraph_vector_ptr_t *eal = &attr->eal;
+        long int ealno = igraph_vector_ptr_size(eal);
+        long int i;
+
+        for (i = 0; i < ealno; i++) {
+            igraph_attribute_record_t *oldrec = VECTOR(*eal)[i];
+            igraph_attribute_type_t type = oldrec->type;
+            igraph_vector_t *num, *newnum;
+            igraph_strvector_t *str, *newstr;
+            igraph_vector_bool_t *oldbool, *newbool;
+            switch (type) {
+            case IGRAPH_ATTRIBUTE_NUMERIC:
+                num = (igraph_vector_t*) oldrec->value;
+                newnum = igraph_Calloc(1, igraph_vector_t);
+                if (!newnum) {
+                    IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_VECTOR_INIT_FINALLY(newnum, 0);
+                igraph_vector_index(num, newnum, idx);
+                oldrec->value = newnum;
+                igraph_vector_destroy(num);
+                igraph_Free(num);
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            case IGRAPH_ATTRIBUTE_BOOLEAN:
+                oldbool = (igraph_vector_bool_t*) oldrec->value;
+                newbool = igraph_Calloc(1, igraph_vector_bool_t);
+                if (!newbool) {
+                    IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_vector_bool_init(newbool, 0));
+                IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool);
+                igraph_vector_bool_index(oldbool, newbool, idx);
+                oldrec->value = newbool;
+                igraph_vector_bool_destroy(oldbool);
+                igraph_Free(oldbool);
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            case IGRAPH_ATTRIBUTE_STRING:
+                str = (igraph_strvector_t*)oldrec->value;
+                newstr = igraph_Calloc(1, igraph_strvector_t);
+                if (!newstr) {
+                    IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_strvector_init(newstr, 0));
+                IGRAPH_FINALLY(igraph_strvector_destroy, newstr);
+                igraph_strvector_index(str, newstr, idx);
+                oldrec->value = newstr;
+                igraph_strvector_destroy(str);
+                igraph_Free(str);
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            default:
+                IGRAPH_WARNING("Unknown edge attribute ignored");
+            }
+        }
+
+    } else {
+
+        igraph_i_cattributes_t *attr = graph->attr;
+        igraph_vector_ptr_t *eal = &attr->eal;
+        long int ealno = igraph_vector_ptr_size(eal);
+        long int i;
+
+        /* New edge attributes */
+        igraph_i_cattributes_t *new_attr = newgraph->attr;
+        igraph_vector_ptr_t *new_eal = &new_attr->eal;
+        IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, ealno));
+
+        IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_eal);
+
+        for (i = 0; i < ealno; i++) {
+            igraph_attribute_record_t *oldrec = VECTOR(*eal)[i];
+            igraph_attribute_type_t type = oldrec->type;
+            igraph_vector_t *num, *newnum;
+            igraph_strvector_t *str, *newstr;
+            igraph_vector_bool_t *oldbool, *newbool;
+
+            /* The record itself */
+            igraph_attribute_record_t *new_rec =
+                igraph_Calloc(1, igraph_attribute_record_t);
+            if (!new_rec) {
+                IGRAPH_ERROR("Cannot create edge attributes", IGRAPH_ENOMEM);
+            }
+            new_rec->name = strdup(oldrec->name);
+            new_rec->type = oldrec->type;
+            VECTOR(*new_eal)[i] = new_rec;
+
+            switch (type) {
+            case IGRAPH_ATTRIBUTE_NUMERIC:
+                num = (igraph_vector_t*) oldrec->value;
+                newnum = igraph_Calloc(1, igraph_vector_t);
+                if (!newnum) {
+                    IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_VECTOR_INIT_FINALLY(newnum, 0);
+                igraph_vector_index(num, newnum, idx);
+                new_rec->value = newnum;
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            case IGRAPH_ATTRIBUTE_STRING:
+                str = (igraph_strvector_t*)oldrec->value;
+                newstr = igraph_Calloc(1, igraph_strvector_t);
+                if (!newstr) {
+                    IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_strvector_init(newstr, 0));
+                IGRAPH_FINALLY(igraph_strvector_destroy, newstr);
+                igraph_strvector_index(str, newstr, idx);
+                new_rec->value = newstr;
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            case IGRAPH_ATTRIBUTE_BOOLEAN:
+                oldbool = (igraph_vector_bool_t*) oldrec->value;
+                newbool = igraph_Calloc(1, igraph_vector_bool_t);
+                if (!newbool) {
+                    IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_vector_bool_init(newbool, 0));
+                IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool);
+                igraph_vector_bool_index(oldbool, newbool, idx);
+                new_rec->value = newbool;
+                IGRAPH_FINALLY_CLEAN(1);
+                break;
+            default:
+                IGRAPH_WARNING("Unknown edge attribute ignored");
+            }
+        }
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+int igraph_i_cattribute_combine_edges(const igraph_t *graph,
+                                      igraph_t *newgraph,
+                                      const igraph_vector_ptr_t *merges,
+                                      const igraph_attribute_combination_t *comb) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_i_cattributes_t *toattr = newgraph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    igraph_vector_ptr_t *new_eal = &toattr->eal;
+    long int ealno = igraph_vector_ptr_size(eal);
+    long int i, j, keepno = 0;
+    int *TODO;
+    igraph_function_pointer_t *funcs;
+
+    TODO = igraph_Calloc(ealno, int);
+    if (!TODO) {
+        IGRAPH_ERROR("Cannot combine edge attributes",
+                     IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, TODO);
+    funcs = igraph_Calloc(ealno, igraph_function_pointer_t);
+    if (!funcs) {
+        IGRAPH_ERROR("Cannot combine edge attributes",
+                     IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, funcs);
+
+    for (i = 0; i < ealno; i++) {
+        igraph_attribute_record_t *oldrec = VECTOR(*eal)[i];
+        const char *name = oldrec->name;
+        igraph_attribute_combination_type_t todo;
+        igraph_function_pointer_t voidfunc;
+        igraph_attribute_combination_query(comb, name, &todo, &voidfunc);
+        TODO[i] = todo;
+        funcs[i] = voidfunc;
+        if (todo != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) {
+            keepno++;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, keepno));
+    IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_eal);
+
+    for (i = 0, j = 0; i < ealno; i++) {
+        igraph_attribute_record_t *newrec, *oldrec = VECTOR(*eal)[i];
+        const char *name = oldrec->name;
+        igraph_attribute_combination_type_t todo =
+            (igraph_attribute_combination_type_t) (TODO[i]);
+        igraph_attribute_type_t type = oldrec->type;
+        igraph_cattributes_combine_num_t *numfunc =
+            (igraph_cattributes_combine_num_t*) funcs[i];
+        igraph_cattributes_combine_str_t *strfunc =
+            (igraph_cattributes_combine_str_t*) funcs[i];
+        igraph_cattributes_combine_bool_t *boolfunc =
+            (igraph_cattributes_combine_bool_t*) funcs[i];
+
+        if (todo == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT ||
+            todo == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) {
+            continue;
+        }
+
+        newrec = igraph_Calloc(1, igraph_attribute_record_t);
+        if (!newrec) {
+            IGRAPH_ERROR("Cannot combine edge attributes",
+                         IGRAPH_ENOMEM);
+        }
+        newrec->name = strdup(name);
+        newrec->type = type;
+        VECTOR(*new_eal)[j] = newrec;
+
+        if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+            switch (todo) {
+            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges,
+                             numfunc));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_prod(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_min(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_max(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_random(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_first(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_last(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
+                IGRAPH_CHECK(igraph_i_cattributes_cn_mean(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
+                IGRAPH_ERROR("Median calculation not implemented",
+                             IGRAPH_UNIMPLEMENTED);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
+                IGRAPH_ERROR("Cannot concatenate numeric attributes",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            default:
+                IGRAPH_ERROR("Unknown attribute_combination",
+                             IGRAPH_UNIMPLEMENTED);
+                break;
+            }
+        } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            switch (todo) {
+            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges,
+                             boolfunc));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
+            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_any_is_true(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
+            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_all_is_true(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
+            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_majority(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_random(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_first(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
+                IGRAPH_CHECK(igraph_i_cattributes_cb_last(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
+                IGRAPH_ERROR("Cannot calculate concatenation of Booleans",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            default:
+                IGRAPH_ERROR("Unknown attribute_combination",
+                             IGRAPH_UNIMPLEMENTED);
+                break;
+            }
+        } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+            switch (todo) {
+            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges,
+                             strfunc));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
+                IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
+                IGRAPH_ERROR("Cannot multiply strings", IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
+                IGRAPH_ERROR("Cannot find minimum of strings",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
+                IGRAPH_ERROR("Cannot find maximum of strings",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
+                IGRAPH_ERROR("Cannot calculate mean of strings",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
+                IGRAPH_ERROR("Cannot calculate median of strings",
+                             IGRAPH_EATTRCOMBINE);
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_random(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_first(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_last(oldrec, newrec, merges));
+                break;
+            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
+                IGRAPH_CHECK(igraph_i_cattributes_sn_concat(oldrec, newrec, merges));
+                break;
+            default:
+                IGRAPH_ERROR("Unknown attribute_combination",
+                             IGRAPH_UNIMPLEMENTED);
+                break;
+            }
+        } else {
+            IGRAPH_ERROR("Unknown attribute type, this should not happen",
+                         IGRAPH_UNIMPLEMENTED);
+        }
+
+        j++;
+    }
+
+    igraph_free(funcs);
+    igraph_free(TODO);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_info(const igraph_t *graph,
+                                 igraph_strvector_t *gnames,
+                                 igraph_vector_t *gtypes,
+                                 igraph_strvector_t *vnames,
+                                 igraph_vector_t *vtypes,
+                                 igraph_strvector_t *enames,
+                                 igraph_vector_t *etypes) {
+
+    igraph_strvector_t *names[3] = { gnames, vnames, enames };
+    igraph_vector_t *types[3] = { gtypes, vtypes, etypes };
+    igraph_i_cattributes_t *at = graph->attr;
+    igraph_vector_ptr_t *attr[3] = { &at->gal, &at->val, &at->eal };
+    long int i, j;
+
+    for (i = 0; i < 3; i++) {
+        igraph_strvector_t *n = names[i];
+        igraph_vector_t *t = types[i];
+        igraph_vector_ptr_t *al = attr[i];
+        long int len = igraph_vector_ptr_size(al);
+
+        if (n) {
+            IGRAPH_CHECK(igraph_strvector_resize(n, len));
+        }
+        if (t) {
+            IGRAPH_CHECK(igraph_vector_resize(t, len));
+        }
+
+        for (j = 0; j < len; j++) {
+            igraph_attribute_record_t *rec = VECTOR(*al)[j];
+            const char *name = rec->name;
+            igraph_attribute_type_t type = rec->type;
+            if (n) {
+                IGRAPH_CHECK(igraph_strvector_set(n, j, name));
+            }
+            if (t) {
+                VECTOR(*t)[j] = type;
+            }
+        }
+    }
+
+    return 0;
+}
+
+igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph,
+        igraph_attribute_elemtype_t type,
+        const char *name) {
+    igraph_i_cattributes_t *at = graph->attr;
+    igraph_vector_ptr_t *attr[3] = { &at->gal, &at->val, &at->eal };
+    long int attrnum;
+
+    switch (type) {
+    case IGRAPH_ATTRIBUTE_GRAPH:
+        attrnum = 0;
+        break;
+    case IGRAPH_ATTRIBUTE_VERTEX:
+        attrnum = 1;
+        break;
+    case IGRAPH_ATTRIBUTE_EDGE:
+        attrnum = 2;
+        break;
+    default:
+        IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL);
+        break;
+    }
+
+    return igraph_i_cattribute_find(attr[attrnum], name, 0);
+}
+
+int igraph_i_cattribute_gettype(const igraph_t *graph,
+                                igraph_attribute_type_t *type,
+                                igraph_attribute_elemtype_t elemtype,
+                                const char *name) {
+    long int attrnum;
+    igraph_attribute_record_t *rec;
+    igraph_i_cattributes_t *at = graph->attr;
+    igraph_vector_ptr_t *attr[3] = { &at->gal, &at->val, &at->eal };
+    igraph_vector_ptr_t *al;
+    long int j;
+    igraph_bool_t l = 0;
+
+    switch (elemtype) {
+    case IGRAPH_ATTRIBUTE_GRAPH:
+        attrnum = 0;
+        break;
+    case IGRAPH_ATTRIBUTE_VERTEX:
+        attrnum = 1;
+        break;
+    case IGRAPH_ATTRIBUTE_EDGE:
+        attrnum = 2;
+        break;
+    default:
+        IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL);
+        break;
+    }
+
+    al = attr[attrnum];
+    l = igraph_i_cattribute_find(al, name, &j);
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+    rec = VECTOR(*al)[j];
+    *type = rec->type;
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_numeric_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vector_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_t *num;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*gal)[j];
+    num = (igraph_vector_t*)rec->value;
+    IGRAPH_CHECK(igraph_vector_resize(value, 1));
+    VECTOR(*value)[0] = VECTOR(*num)[0];
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_bool_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vector_bool_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_bool_t *log;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*gal)[j];
+    log = (igraph_vector_bool_t*)rec->value;
+    IGRAPH_CHECK(igraph_vector_bool_resize(value, 1));
+    VECTOR(*value)[0] = VECTOR(*log)[0];
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_string_graph_attr(const igraph_t *graph,
+        const char *name,
+        igraph_strvector_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_strvector_t *str;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*gal)[j];
+    str = (igraph_strvector_t*)rec->value;
+    IGRAPH_CHECK(igraph_strvector_resize(value, 1));
+    IGRAPH_CHECK(igraph_strvector_set(value, 0, STR(*str, 0)));
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_numeric_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_vector_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_t *num;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*val)[j];
+    num = (igraph_vector_t*)rec->value;
+    if (igraph_vs_is_all(&vs)) {
+        igraph_vector_clear(value);
+        IGRAPH_CHECK(igraph_vector_append(value, num));
+    } else {
+        igraph_vit_t it;
+        long int i = 0;
+        IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
+        IGRAPH_FINALLY(igraph_vit_destroy, &it);
+        IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_VIT_SIZE(it)));
+        for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) {
+            long int v = IGRAPH_VIT_GET(it);
+            VECTOR(*value)[i] = VECTOR(*num)[v];
+        }
+        igraph_vit_destroy(&it);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_bool_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_vector_bool_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    igraph_vit_t it;
+    long int i, j, v;
+    igraph_attribute_record_t *rec;
+    igraph_vector_bool_t *log;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*val)[j];
+    log = (igraph_vector_bool_t*)rec->value;
+    if (igraph_vs_is_all(&vs)) {
+        igraph_vector_bool_clear(value);
+        IGRAPH_CHECK(igraph_vector_bool_append(value, log));
+    } else {
+        IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
+        IGRAPH_FINALLY(igraph_vit_destroy, &it);
+        IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_VIT_SIZE(it)));
+        for (i = 0; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) {
+            v = IGRAPH_VIT_GET(it);
+            VECTOR(*value)[i] = VECTOR(*log)[v];
+        }
+        igraph_vit_destroy(&it);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_string_vertex_attr(const igraph_t *graph,
+        const char *name,
+        igraph_vs_t vs,
+        igraph_strvector_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_strvector_t *str;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*val)[j];
+    str = (igraph_strvector_t*)rec->value;
+    if (igraph_vs_is_all(&vs)) {
+        igraph_strvector_resize(value, 0);
+        IGRAPH_CHECK(igraph_strvector_append(value, str));
+    } else {
+        igraph_vit_t it;
+        long int i = 0;
+        IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
+        IGRAPH_FINALLY(igraph_vit_destroy, &it);
+        IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_VIT_SIZE(it)));
+        for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) {
+            long int v = IGRAPH_VIT_GET(it);
+            char *s;
+            igraph_strvector_get(str, v, &s);
+            IGRAPH_CHECK(igraph_strvector_set(value, i, s));
+        }
+        igraph_vit_destroy(&it);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_numeric_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_vector_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_t *num;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*eal)[j];
+    num = (igraph_vector_t*)rec->value;
+    if (igraph_es_is_all(&es)) {
+        igraph_vector_clear(value);
+        IGRAPH_CHECK(igraph_vector_append(value, num));
+    } else {
+        igraph_eit_t it;
+        long int i = 0;
+        IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
+        IGRAPH_FINALLY(igraph_eit_destroy, &it);
+        IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_EIT_SIZE(it)));
+        for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) {
+            long int e = IGRAPH_EIT_GET(it);
+            VECTOR(*value)[i] = VECTOR(*num)[e];
+        }
+        igraph_eit_destroy(&it);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_string_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_strvector_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_strvector_t *str;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*eal)[j];
+    str = (igraph_strvector_t*)rec->value;
+    if (igraph_es_is_all(&es)) {
+        igraph_strvector_resize(value, 0);
+        IGRAPH_CHECK(igraph_strvector_append(value, str));
+    } else {
+        igraph_eit_t it;
+        long int i = 0;
+        IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
+        IGRAPH_FINALLY(igraph_eit_destroy, &it);
+        IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_EIT_SIZE(it)));
+        for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) {
+            long int e = IGRAPH_EIT_GET(it);
+            char *s;
+            igraph_strvector_get(str, e, &s);
+            IGRAPH_CHECK(igraph_strvector_set(value, i, s));
+        }
+        igraph_eit_destroy(&it);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+int igraph_i_cattribute_get_bool_edge_attr(const igraph_t *graph,
+        const char *name,
+        igraph_es_t es,
+        igraph_vector_bool_t *value) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_bool_t *log;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (!l) {
+        IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+    }
+
+    rec = VECTOR(*eal)[j];
+    log = (igraph_vector_bool_t*)rec->value;
+    if (igraph_es_is_all(&es)) {
+        igraph_vector_bool_clear(value);
+        IGRAPH_CHECK(igraph_vector_bool_append(value, log));
+    } else {
+        igraph_eit_t it;
+        long int i = 0;
+        IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
+        IGRAPH_FINALLY(igraph_eit_destroy, &it);
+        IGRAPH_CHECK(igraph_vector_bool_resize(value, IGRAPH_EIT_SIZE(it)));
+        for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) {
+            long int e = IGRAPH_EIT_GET(it);
+            VECTOR(*value)[i] = VECTOR(*log)[e];
+        }
+        igraph_eit_destroy(&it);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/* -------------------------------------- */
+
+const igraph_attribute_table_t igraph_cattribute_table = {
+    &igraph_i_cattribute_init, &igraph_i_cattribute_destroy,
+    &igraph_i_cattribute_copy, &igraph_i_cattribute_add_vertices,
+    &igraph_i_cattribute_permute_vertices,
+    &igraph_i_cattribute_combine_vertices, &igraph_i_cattribute_add_edges,
+    &igraph_i_cattribute_permute_edges,
+    &igraph_i_cattribute_combine_edges,
+    &igraph_i_cattribute_get_info,
+    &igraph_i_cattribute_has_attr, &igraph_i_cattribute_gettype,
+    &igraph_i_cattribute_get_numeric_graph_attr,
+    &igraph_i_cattribute_get_string_graph_attr,
+    &igraph_i_cattribute_get_bool_graph_attr,
+    &igraph_i_cattribute_get_numeric_vertex_attr,
+    &igraph_i_cattribute_get_string_vertex_attr,
+    &igraph_i_cattribute_get_bool_vertex_attr,
+    &igraph_i_cattribute_get_numeric_edge_attr,
+    &igraph_i_cattribute_get_string_edge_attr,
+    &igraph_i_cattribute_get_bool_edge_attr
+};
+
+/* -------------------------------------- */
+
+/**
+ * \section cattributes
+ * <para>There is an experimental attribute handler that can be used
+ * from C code. In this section we show how this works. This attribute
+ * handler is by default not attached (the default is no attribute
+ * handler), so we first need to attach it:
+ * <programlisting>
+ * igraph_i_set_attribute_table(&amp;igraph_cattribute_table);
+ * </programlisting>
+ * </para>
+ * <para>Now the attribute functions are available. Please note that
+ * the attribute handler must be attached before you call any other
+ * igraph functions, otherwise you might end up with graphs without
+ * attributes and an active attribute handler, which might cause
+ * unexpected program behaviour. The rule is that you attach the
+ * attribute handler in the beginning of your
+ * <function>main()</function> and never touch it again. (Detaching
+ * the attribute handler might lead to memory leaks.)</para>
+ *
+ * <para>It is not currently possible to have attribute handlers on a
+ * per-graph basis. All graphs in an application must be managed with
+ * the same attribute handler. (Including the default case when there
+ * is no attribute handler at all.</para>
+ *
+ * <para>The C attribute handler supports attaching real numbers and
+ * character strings as attributes. No vectors are allowed, ie. every
+ * vertex might have an attribute called <code>name</code>, but it is
+ * not possible to have a <code>coords</code> graph (or other)
+ * attribute which is a vector of numbers.</para>
+ *
+ * \example examples/simple/cattributes.c
+ * \example examples/simple/cattributes2.c
+ * \example examples/simple/cattributes3.c
+ * \example examples/simple/cattributes4.c
+ */
+
+/**
+ * \function igraph_cattribute_GAN
+ * Query a numeric graph attribute.
+ *
+ * Returns the value of the given numeric graph attribute.
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute to query.
+ * \return The value of the attribute.
+ *
+ * \sa \ref GAN for a simpler interface.
+ *
+ * Time complexity: O(Ag), the number of graph attributes.
+ */
+igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_t *num;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*gal)[j];
+    num = (igraph_vector_t*)rec->value;
+    return VECTOR(*num)[0];
+}
+
+/**
+ * \function igraph_cattribute_GAB
+ * Query a boolean graph attribute.
+ *
+ * Returns the value of the given numeric graph attribute.
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute to query.
+ * \return The value of the attribute.
+ *
+ * \sa \ref GAB for a simpler interface.
+ *
+ * Time complexity: O(Ag), the number of graph attributes.
+ */
+igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_bool_t *log;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*gal)[j];
+    log = (igraph_vector_bool_t*)rec->value;
+    return VECTOR(*log)[0];
+}
+
+/**
+ * \function igraph_cattribute_GAS
+ * Query a string graph attribute.
+ *
+ * Returns a <type>const</type> pointer to the string graph attribute
+ * specified in \p name.
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute to query.
+ * \return The value of the attribute.
+ *
+ * \sa \ref GAS for a simpler interface.
+ *
+ * Time complexity: O(Ag), the number of graph attributes.
+ */
+const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_strvector_t *str;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*gal)[j];
+    str = (igraph_strvector_t*)rec->value;
+    return STR(*str, 0);
+}
+
+/**
+ * \function igraph_cattribute_VAN
+ * Query a numeric vertex attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vid The id of the queried vertex.
+ * \return The value of the attribute.
+ *
+ * \sa \ref VAN macro for a simpler interface.
+ *
+ * Time complexity: O(Av), the number of vertex attributes.
+ */
+igraph_real_t igraph_cattribute_VAN(const igraph_t *graph, const char *name,
+                                    igraph_integer_t vid) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_t *num;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*val)[j];
+    num = (igraph_vector_t*)rec->value;
+    return VECTOR(*num)[(long int)vid];
+}
+
+/**
+ * \function igraph_cattribute_VAB
+ * Query a boolean vertex attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vid The id of the queried vertex.
+ * \return The value of the attribute.
+ *
+ * \sa \ref VAB macro for a simpler interface.
+ *
+ * Time complexity: O(Av), the number of vertex attributes.
+ */
+igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name,
+                                    igraph_integer_t vid) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_bool_t *log;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*val)[j];
+    log = (igraph_vector_bool_t*)rec->value;
+    return VECTOR(*log)[(long int)vid];
+}
+
+/**
+ * \function igraph_cattribute_VAS
+ * Query a string vertex attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vid The id of the queried vertex.
+ * \return The value of the attribute.
+ *
+ * \sa The macro \ref VAS for a simpler interface.
+ *
+ * Time complexity: O(Av), the number of vertex attributes.
+ */
+const char* igraph_cattribute_VAS(const igraph_t *graph, const char *name,
+                                  igraph_integer_t vid) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_strvector_t *str;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*val)[j];
+    str = (igraph_strvector_t*)rec->value;
+    return STR(*str, (long int)vid);
+}
+
+/**
+ * \function igraph_cattribute_EAN
+ * Query a numeric edge attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eid The id of the queried edge.
+ * \return The value of the attribute.
+ *
+ * \sa \ref EAN for an easier interface.
+ *
+ * Time complexity: O(Ae), the number of edge attributes.
+ */
+igraph_real_t igraph_cattribute_EAN(const igraph_t *graph, const char *name,
+                                    igraph_integer_t eid) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_t *num;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*eal)[j];
+    num = (igraph_vector_t*)rec->value;
+    return VECTOR(*num)[(long int)eid];
+}
+
+/**
+ * \function igraph_cattribute_EAB
+ * Query a boolean edge attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eid The id of the queried edge.
+ * \return The value of the attribute.
+ *
+ * \sa \ref EAB for an easier interface.
+ *
+ * Time complexity: O(Ae), the number of edge attributes.
+ */
+igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name,
+                                    igraph_integer_t eid) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_vector_bool_t *log;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*eal)[j];
+    log = (igraph_vector_bool_t*)rec->value;
+    return VECTOR(*log)[(long int)eid];
+}
+
+/**
+ * \function igraph_cattribute_EAS
+ * Query a string edge attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eid The id of the queried edge.
+ * \return The value of the attribute.
+ *
+ * \se \ref EAS if you want to type less.
+ *
+ * Time complexity: O(Ae), the number of edge attributes.
+ */
+const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name,
+                                  igraph_integer_t eid) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_attribute_record_t *rec;
+    igraph_strvector_t *str;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (!l) {
+        igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+        return 0;
+    }
+
+    rec = VECTOR(*eal)[j];
+    str = (igraph_strvector_t*)rec->value;
+    return STR(*str, (long int)eid);
+}
+
+/**
+ * \function igraph_cattribute_VANV
+ * Query a numeric vertex attribute for many vertices
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vids The vertices to query.
+ * \param result Pointer to an initialized vector, the result is
+ *    stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(v), where v is the number of vertices in 'vids'.
+ */
+
+int igraph_cattribute_VANV(const igraph_t *graph, const char *name,
+                           igraph_vs_t vids, igraph_vector_t *result) {
+
+    return igraph_i_cattribute_get_numeric_vertex_attr(graph, name, vids,
+            result);
+}
+
+/**
+ * \function igraph_cattribute_VABV
+ * Query a boolean vertex attribute for many vertices
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vids The vertices to query.
+ * \param result Pointer to an initialized boolean vector, the result is
+ *    stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(v), where v is the number of vertices in 'vids'.
+ */
+
+int igraph_cattribute_VABV(const igraph_t *graph, const char *name,
+                           igraph_vs_t vids, igraph_vector_bool_t *result) {
+
+    return igraph_i_cattribute_get_bool_vertex_attr(graph, name, vids,
+            result);
+}
+
+/**
+ * \function igraph_cattribute_EANV
+ * Query a numeric edge attribute for many edges
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eids The edges to query.
+ * \param result Pointer to an initialized vector, the result is
+ *    stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(e), where e is the number of edges in 'eids'.
+ */
+
+int igraph_cattribute_EANV(const igraph_t *graph, const char *name,
+                           igraph_es_t eids, igraph_vector_t *result) {
+
+    return igraph_i_cattribute_get_numeric_edge_attr(graph, name, eids,
+            result);
+}
+
+/**
+ * \function igraph_cattribute_EABV
+ * Query a boolean edge attribute for many edges
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eids The edges to query.
+ * \param result Pointer to an initialized boolean vector, the result is
+ *    stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(e), where e is the number of edges in 'eids'.
+ */
+
+int igraph_cattribute_EABV(const igraph_t *graph, const char *name,
+                           igraph_es_t eids, igraph_vector_bool_t *result) {
+
+    return igraph_i_cattribute_get_bool_edge_attr(graph, name, eids,
+            result);
+}
+
+/**
+ * \function igraph_cattribute_VASV
+ * Query a string vertex attribute for many vertices
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vids The vertices to query.
+ * \param result Pointer to an initialized string vector, the result
+ *     is stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(v), where v is the number of vertices in 'vids'.
+ * (We assume that the string attributes have a bounded length.)
+ */
+
+int igraph_cattribute_VASV(const igraph_t *graph, const char *name,
+                           igraph_vs_t vids, igraph_strvector_t *result) {
+
+    return igraph_i_cattribute_get_string_vertex_attr(graph, name, vids,
+            result);
+}
+
+/**
+ * \function igraph_cattribute_EASV
+ * Query a string edge attribute for many edges
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vids The edges to query.
+ * \param result Pointer to an initialized string vector, the result
+ *     is stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(e), where e is the number of edges in
+ * 'eids'. (We assume that the string attributes have a bounded length.)
+ */
+
+int igraph_cattribute_EASV(const igraph_t *graph, const char *name,
+                           igraph_es_t eids, igraph_strvector_t *result) {
+
+    return igraph_i_cattribute_get_string_edge_attr(graph, name, eids,
+            result);
+}
+
+/**
+ * \function igraph_cattribute_list
+ * List all attributes
+ *
+ * See \ref igraph_attribute_type_t for the various attribute types.
+ * \param graph The input graph.
+ * \param gnames String vector, the names of the graph attributes.
+ * \param gtypes Numeric vector, the types of the graph attributes.
+ * \param vnames String vector, the names of the vertex attributes.
+ * \param vtypes Numeric vector, the types of the vertex attributes.
+ * \param enames String vector, the names of the edge attributes.
+ * \param etypes Numeric vector, the types of the edge attributes.
+ * \return Error code.
+ *
+ * Naturally, the string vector with the attribute names and the
+ * numeric vector with the attribute types are in the right order,
+ * i.e. the first name corresponds to the first type, etc.
+ *
+ * Time complexity: O(Ag+Av+Ae), the number of all attributes.
+ */
+int igraph_cattribute_list(const igraph_t *graph,
+                           igraph_strvector_t *gnames, igraph_vector_t *gtypes,
+                           igraph_strvector_t *vnames, igraph_vector_t *vtypes,
+                           igraph_strvector_t *enames, igraph_vector_t *etypes) {
+    return igraph_i_cattribute_get_info(graph, gnames, gtypes, vnames, vtypes,
+                                        enames, etypes);
+}
+
+/**
+ * \function igraph_cattribute_has_attr
+ * Checks whether a (graph, vertex or edge) attribute exists
+ *
+ * \param graph The graph.
+ * \param type The type of the attribute, \c IGRAPH_ATTRIBUTE_GRAPH,
+ *        \c IGRAPH_ATTRIBUTE_VERTEX or \c IGRAPH_ATTRIBUTE_EDGE.
+ * \param name Character constant, the name of the attribute.
+ * \return Logical value, TRUE if the attribute exists, FALSE otherwise.
+ *
+ * Time complexity: O(A), the number of (graph, vertex or edge)
+ * attributes, assuming attribute names are not too long.
+ */
+igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph,
+        igraph_attribute_elemtype_t type,
+        const char *name) {
+    return igraph_i_cattribute_has_attr(graph, type, name);
+}
+
+/**
+ * \function igraph_cattribute_GAN_set
+ * Set a numeric graph attribute
+ *
+ * \param graph The graph.
+ * \param name Name of the graph attribute. If there is no such
+ *   attribute yet, then it will be added.
+ * \param value The (new) value of the graph attribute.
+ * \return Error code.
+ *
+ * \se \ref SETGAN if you want to type less.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_cattribute_GAN_set(igraph_t *graph, const char *name,
+                              igraph_real_t value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*gal)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_vector_t *num = (igraph_vector_t *)rec->value;
+            VECTOR(*num)[0] = value;
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_t *num;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_NUMERIC;
+        num = igraph_Calloc(1, igraph_vector_t);
+        if (!num) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, num);
+        IGRAPH_VECTOR_INIT_FINALLY(num, 1);
+        VECTOR(*num)[0] = value;
+        rec->value = num;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_GAB_set
+ * Set a boolean graph attribute
+ *
+ * \param graph The graph.
+ * \param name Name of the graph attribute. If there is no such
+ *   attribute yet, then it will be added.
+ * \param value The (new) value of the graph attribute.
+ * \return Error code.
+ *
+ * \se \ref SETGAN if you want to type less.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_cattribute_GAB_set(igraph_t *graph, const char *name,
+                              igraph_bool_t value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*gal)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_vector_bool_t *log = (igraph_vector_bool_t *)rec->value;
+            VECTOR(*log)[0] = value;
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_bool_t *log;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_BOOLEAN;
+        log = igraph_Calloc(1, igraph_vector_bool_t);
+        if (!log) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, log);
+        IGRAPH_CHECK(igraph_vector_bool_init(log, 1));
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, log);
+        VECTOR(*log)[0] = value;
+        rec->value = log;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_GAS_set
+ * Set a string graph attribute.
+ *
+ * \param graph The graph.
+ * \param name Name of the graph attribute. If there is no such
+ *   attribute yet, then it will be added.
+ * \param value The (new) value of the graph attribute. It will be
+ *   copied.
+ * \return Error code.
+ *
+ * \se \ref SETGAS if you want to type less.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_cattribute_GAS_set(igraph_t *graph, const char *name,
+                              const char *value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*gal)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_strvector_t *str = (igraph_strvector_t*)rec->value;
+            IGRAPH_CHECK(igraph_strvector_set(str, 0, value));
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_strvector_t *str;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_STRING;
+        str = igraph_Calloc(1, igraph_strvector_t);
+        if (!str) {
+            IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, str);
+        IGRAPH_STRVECTOR_INIT_FINALLY(str, 1);
+        IGRAPH_CHECK(igraph_strvector_set(str, 0, value));
+        rec->value = str;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_VAN_set
+ * Set a numeric vertex attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all vertices
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param vid Vertices for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETVAN for a simpler way.
+ *
+ * Time complexity: O(n), the number of vertices if the attribute is
+ * new, O(|vid|) otherwise.
+ */
+int igraph_cattribute_VAN_set(igraph_t *graph, const char *name,
+                              igraph_integer_t vid, igraph_real_t value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*val)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_vector_t *num = (igraph_vector_t*)rec->value;
+            VECTOR(*num)[(long int)vid] = value;
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_t *num;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_NUMERIC;
+        num = igraph_Calloc(1, igraph_vector_t);
+        if (!num) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, num);
+        IGRAPH_VECTOR_INIT_FINALLY(num, igraph_vcount(graph));
+        igraph_vector_fill(num, IGRAPH_NAN);
+        VECTOR(*num)[(long int)vid] = value;
+        rec->value = num;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_VAB_set
+ * Set a boolean vertex attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all vertices
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param vid Vertices for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETVAB for a simpler way.
+ *
+ * Time complexity: O(n), the number of vertices if the attribute is
+ * new, O(|vid|) otherwise.
+ */
+int igraph_cattribute_VAB_set(igraph_t *graph, const char *name,
+                              igraph_integer_t vid, igraph_bool_t value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*val)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_vector_bool_t *log = (igraph_vector_bool_t*)rec->value;
+            VECTOR(*log)[(long int)vid] = value;
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_bool_t *log;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_BOOLEAN;
+        log = igraph_Calloc(1, igraph_vector_bool_t);
+        if (!log) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, log);
+        IGRAPH_CHECK(igraph_vector_bool_init(log, igraph_vcount(graph)));
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, log);
+        igraph_vector_bool_fill(log, 0);
+        VECTOR(*log)[(long int)vid] = value;
+        rec->value = log;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_VAS_set
+ * Set a string vertex attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all vertices
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param vid Vertices for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETVAS for a simpler way.
+ *
+ * Time complexity: O(n*l), n is the number of vertices, l is the
+ * length of the string to set. If the attribute if not new then only
+ * O(|vid|*l).
+ */
+int igraph_cattribute_VAS_set(igraph_t *graph, const char *name,
+                              igraph_integer_t vid, const char *value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*val)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_strvector_t *str = (igraph_strvector_t*)rec->value;
+            IGRAPH_CHECK(igraph_strvector_set(str, vid, value));
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_strvector_t *str;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_STRING;
+        str = igraph_Calloc(1, igraph_strvector_t);
+        if (!str) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, str);
+        IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_vcount(graph));
+        IGRAPH_CHECK(igraph_strvector_set(str, vid, value));
+        rec->value = str;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_EAN_set
+ * Set a numeric edge attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all edges
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param eid Edges for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETEAN for a simpler way.
+ *
+ * Time complexity: O(e), the number of edges if the attribute is
+ * new, O(|eid|) otherwise.
+ */
+int igraph_cattribute_EAN_set(igraph_t *graph, const char *name,
+                              igraph_integer_t eid, igraph_real_t value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*eal)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_vector_t *num = (igraph_vector_t*)rec->value;
+            VECTOR(*num)[(long int)eid] = value;
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_t *num;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_NUMERIC;
+        num = igraph_Calloc(1, igraph_vector_t);
+        if (!num) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, num);
+        IGRAPH_VECTOR_INIT_FINALLY(num, igraph_ecount(graph));
+        igraph_vector_fill(num, IGRAPH_NAN);
+        VECTOR(*num)[(long int)eid] = value;
+        rec->value = num;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_EAB_set
+ * Set a boolean edge attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all edges
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param eid Edges for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETEAB for a simpler way.
+ *
+ * Time complexity: O(e), the number of edges if the attribute is
+ * new, O(|eid|) otherwise.
+ */
+int igraph_cattribute_EAB_set(igraph_t *graph, const char *name,
+                              igraph_integer_t eid, igraph_bool_t value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*eal)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_vector_bool_t *log = (igraph_vector_bool_t*)rec->value;
+            VECTOR(*log)[(long int)eid] = value;
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_bool_t *log;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_BOOLEAN;
+        log = igraph_Calloc(1, igraph_vector_bool_t);
+        if (!log) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, log);
+        IGRAPH_CHECK(igraph_vector_bool_init(log, igraph_ecount(graph)));
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, log);
+        igraph_vector_bool_fill(log, 0);
+        VECTOR(*log)[(long int)eid] = value;
+        rec->value = log;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_EAS_set
+ * Set a string edge attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all edges
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param eid Edges for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETEAS for a simpler way.
+ *
+ * Time complexity: O(e*l), n is the number of edges, l is the
+ * length of the string to set. If the attribute if not new then only
+ * O(|eid|*l).
+ */
+int igraph_cattribute_EAS_set(igraph_t *graph, const char *name,
+                              igraph_integer_t eid, const char *value) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (l) {
+        igraph_attribute_record_t *rec = VECTOR(*eal)[j];
+        if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+            IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+        } else {
+            igraph_strvector_t *str = (igraph_strvector_t*)rec->value;
+            IGRAPH_CHECK(igraph_strvector_set(str, eid, value));
+        }
+    } else {
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_strvector_t *str;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        rec->type = IGRAPH_ATTRIBUTE_STRING;
+        str = igraph_Calloc(1, igraph_strvector_t);
+        if (!str) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, str);
+        IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_ecount(graph));
+        IGRAPH_CHECK(igraph_strvector_set(str, eid, value));
+        rec->value = str;
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_VAN_setv
+ * Set a numeric vertex attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param v The new attribute values. The length of this vector must
+ *   match the number of vertices.
+ * \return Error code.
+ *
+ * \sa \ref SETVANV for a simpler way.
+ *
+ * Time complexity: O(n), the number of vertices.
+ */
+
+int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name,
+                               const igraph_vector_t *v) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    /* Check length first */
+    if (igraph_vector_size(v) != igraph_vcount(graph)) {
+        IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL);
+    }
+
+    if (l) {
+        /* Already present, check type */
+        igraph_attribute_record_t *rec = VECTOR(*val)[j];
+        igraph_vector_t *num = (igraph_vector_t *)rec->value;
+        if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) {
+            IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL);
+        }
+        igraph_vector_clear(num);
+        IGRAPH_CHECK(igraph_vector_append(num, v));
+    } else {
+        /* Add it */
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_t *num;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->type = IGRAPH_ATTRIBUTE_NUMERIC;
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        num = igraph_Calloc(1, igraph_vector_t);
+        if (!num) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, num);
+        rec->value = num;
+        IGRAPH_CHECK(igraph_vector_copy(num, v));
+        IGRAPH_FINALLY(igraph_vector_destroy, num);
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+/**
+ * \function igraph_cattribute_VAB_setv
+ * Set a boolean vertex attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param v The new attribute values. The length of this boolean vector must
+ *   match the number of vertices.
+ * \return Error code.
+ *
+ * \sa \ref SETVANV for a simpler way.
+ *
+ * Time complexity: O(n), the number of vertices.
+ */
+
+int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name,
+                               const igraph_vector_bool_t *v) {
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    /* Check length first */
+    if (igraph_vector_bool_size(v) != igraph_vcount(graph)) {
+        IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL);
+    }
+
+    if (l) {
+        /* Already present, check type */
+        igraph_attribute_record_t *rec = VECTOR(*val)[j];
+        igraph_vector_bool_t *log = (igraph_vector_bool_t *)rec->value;
+        if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) {
+            IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL);
+        }
+        igraph_vector_bool_clear(log);
+        IGRAPH_CHECK(igraph_vector_bool_append(log, v));
+    } else {
+        /* Add it */
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_bool_t *log;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->type = IGRAPH_ATTRIBUTE_BOOLEAN;
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        log = igraph_Calloc(1, igraph_vector_bool_t);
+        if (!log) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, log);
+        rec->value = log;
+        IGRAPH_CHECK(igraph_vector_bool_copy(log, v));
+        IGRAPH_FINALLY(igraph_vector_destroy, log);
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_VAS_setv
+ * Set a string vertex attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param sv String vector, the new attribute values. The length of this vector must
+ *   match the number of vertices.
+ * \return Error code.
+ *
+ * \sa \ref SETVASV for a simpler way.
+ *
+ * Time complexity: O(n+l), n is the number of vertices, l is the
+ * total length of the strings.
+ */
+int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name,
+                               const igraph_strvector_t *sv) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    /* Check length first */
+    if (igraph_strvector_size(sv) != igraph_vcount(graph)) {
+        IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL);
+    }
+
+    if (l) {
+        /* Already present, check type */
+        igraph_attribute_record_t *rec = VECTOR(*val)[j];
+        igraph_strvector_t *str = (igraph_strvector_t *)rec->value;
+        if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+            IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL);
+        }
+        igraph_strvector_clear(str);
+        IGRAPH_CHECK(igraph_strvector_append(str, sv));
+    } else {
+        /* Add it */
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_strvector_t *str;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->type = IGRAPH_ATTRIBUTE_STRING;
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        str = igraph_Calloc(1, igraph_strvector_t);
+        if (!str) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, str);
+        rec->value = str;
+        IGRAPH_CHECK(igraph_strvector_copy(str, sv));
+        IGRAPH_FINALLY(igraph_strvector_destroy, str);
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_EAN_setv
+ * Set a numeric edge attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param v The new attribute values. The length of this vector must
+ *   match the number of edges.
+ * \return Error code.
+ *
+ * \sa \ref SETEANV for a simpler way.
+ *
+ * Time complexity: O(e), the number of edges.
+ */
+int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name,
+                               const igraph_vector_t *v) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    /* Check length first */
+    if (igraph_vector_size(v) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL);
+    }
+
+    if (l) {
+        /* Already present, check type */
+        igraph_attribute_record_t *rec = VECTOR(*eal)[j];
+        igraph_vector_t *num = (igraph_vector_t *)rec->value;
+        if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) {
+            IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL);
+        }
+        igraph_vector_clear(num);
+        IGRAPH_CHECK(igraph_vector_append(num, v));
+    } else {
+        /* Add it */
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_t *num;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->type = IGRAPH_ATTRIBUTE_NUMERIC;
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        num = igraph_Calloc(1, igraph_vector_t);
+        if (!num) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, num);
+        rec->value = num;
+        IGRAPH_CHECK(igraph_vector_copy(num, v));
+        IGRAPH_FINALLY(igraph_vector_destroy, num);
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_EAB_setv
+ * Set a boolean edge attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param v The new attribute values. The length of this vector must
+ *   match the number of edges.
+ * \return Error code.
+ *
+ * \sa \ref SETEABV for a simpler way.
+ *
+ * Time complexity: O(e), the number of edges.
+ */
+int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name,
+                               const igraph_vector_bool_t *v) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    /* Check length first */
+    if (igraph_vector_bool_size(v) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL);
+    }
+
+    if (l) {
+        /* Already present, check type */
+        igraph_attribute_record_t *rec = VECTOR(*eal)[j];
+        igraph_vector_bool_t *log = (igraph_vector_bool_t *)rec->value;
+        if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) {
+            IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL);
+        }
+        igraph_vector_bool_clear(log);
+        IGRAPH_CHECK(igraph_vector_bool_append(log, v));
+    } else {
+        /* Add it */
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_vector_bool_t *log;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->type = IGRAPH_ATTRIBUTE_BOOLEAN;
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        log = igraph_Calloc(1, igraph_vector_bool_t);
+        if (!log) {
+            IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, log);
+        rec->value = log;
+        IGRAPH_CHECK(igraph_vector_bool_copy(log, v));
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, log);
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_cattribute_EAS_setv
+ * Set a string edge attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param sv String vector, the new attribute values. The length of this vector must
+ *   match the number of edges.
+ * \return Error code.
+ *
+ * \sa \ref SETEASV for a simpler way.
+ *
+ * Time complexity: O(e+l), e is the number of edges, l is the
+ * total length of the strings.
+ */
+int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name,
+                               const igraph_strvector_t *sv) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    /* Check length first */
+    if (igraph_strvector_size(sv) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL);
+    }
+
+    if (l) {
+        /* Already present, check type */
+        igraph_attribute_record_t *rec = VECTOR(*eal)[j];
+        igraph_strvector_t *str = (igraph_strvector_t *)rec->value;
+        if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+            IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL);
+        }
+        igraph_strvector_clear(str);
+        IGRAPH_CHECK(igraph_strvector_append(str, sv));
+    } else {
+        /* Add it */
+        igraph_attribute_record_t *rec = igraph_Calloc(1, igraph_attribute_record_t);
+        igraph_strvector_t *str;
+        if (!rec) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, rec);
+        rec->type = IGRAPH_ATTRIBUTE_STRING;
+        rec->name = strdup(name);
+        if (!rec->name) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+        str = igraph_Calloc(1, igraph_strvector_t);
+        if (!str) {
+            IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, str);
+        rec->value = str;
+        IGRAPH_CHECK(igraph_strvector_copy(str, sv));
+        IGRAPH_FINALLY(igraph_strvector_destroy, str);
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec));
+        IGRAPH_FINALLY_CLEAN(4);
+    }
+
+    return 0;
+}
+
+void igraph_i_cattribute_free_rec(igraph_attribute_record_t *rec) {
+
+    if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+        igraph_vector_t *num = (igraph_vector_t*)rec->value;
+        igraph_vector_destroy(num);
+    } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+        igraph_strvector_t *str = (igraph_strvector_t*)rec->value;
+        igraph_strvector_destroy(str);
+    } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+        igraph_vector_bool_t *boolvec = (igraph_vector_bool_t*)rec->value;
+        igraph_vector_bool_destroy(boolvec);
+    }
+    igraph_Free(rec->name);
+    igraph_Free(rec->value);
+    igraph_Free(rec);
+}
+
+/**
+ * \function igraph_cattribute_remove_g
+ * Remove a graph attribute
+ *
+ * \param graph The graph object.
+ * \param name Name of the graph attribute to remove.
+ *
+ * \sa \ref DELGA for a simpler way.
+ *
+ */
+void igraph_cattribute_remove_g(igraph_t *graph, const char *name) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *gal = &attr->gal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j);
+
+    if (l) {
+        igraph_i_cattribute_free_rec(VECTOR(*gal)[j]);
+        igraph_vector_ptr_remove(gal, j);
+    } else {
+        IGRAPH_WARNING("Cannot remove non-existent graph attribute");
+    }
+}
+
+/**
+ * \function igraph_cattribute_remove_v
+ * Remove a vertex attribute
+ *
+ * \param graph The graph object.
+ * \param name Name of the vertex attribute to remove.
+ *
+ * \sa \ref DELVA for a simpler way.
+ *
+ */
+void igraph_cattribute_remove_v(igraph_t *graph, const char *name) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *val = &attr->val;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(val, name, &j);
+
+    if (l) {
+        igraph_i_cattribute_free_rec(VECTOR(*val)[j]);
+        igraph_vector_ptr_remove(val, j);
+    } else {
+        IGRAPH_WARNING("Cannot remove non-existent graph attribute");
+    }
+}
+
+/**
+ * \function igraph_cattribute_remove_e
+ * Remove an edge attribute
+ *
+ * \param graph The graph object.
+ * \param name Name of the edge attribute to remove.
+ *
+ * \sa \ref DELEA for a simpler way.
+ *
+ */
+void igraph_cattribute_remove_e(igraph_t *graph, const char *name) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+    igraph_vector_ptr_t *eal = &attr->eal;
+    long int j;
+    igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j);
+
+    if (l) {
+        igraph_i_cattribute_free_rec(VECTOR(*eal)[j]);
+        igraph_vector_ptr_remove(eal, j);
+    } else {
+        IGRAPH_WARNING("Cannot remove non-existent graph attribute");
+    }
+}
+
+/**
+ * \function igraph_cattribute_remove_all
+ * Remove all graph/vertex/edge attributes
+ *
+ * \param graph The graph object.
+ * \param g Boolean, whether to remove graph attributes.
+ * \param v Boolean, whether to remove vertex attributes.
+ * \param e Boolean, whether to remove edge attributes.
+ *
+ * \sa \ref DELGAS, \ref DELVAS, \ref DELEAS, \ref DELALL for simpler
+ * ways.
+ */
+void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g,
+                                  igraph_bool_t v, igraph_bool_t e) {
+
+    igraph_i_cattributes_t *attr = graph->attr;
+
+    if (g) {
+        igraph_vector_ptr_t *gal = &attr->gal;
+        long int i, n = igraph_vector_ptr_size(gal);
+        for (i = 0; i < n; i++) {
+            igraph_i_cattribute_free_rec(VECTOR(*gal)[i]);
+        }
+        igraph_vector_ptr_clear(gal);
+    }
+    if (v) {
+        igraph_vector_ptr_t *val = &attr->val;
+        long int i, n = igraph_vector_ptr_size(val);
+        for (i = 0; i < n; i++) {
+            igraph_i_cattribute_free_rec(VECTOR(*val)[i]);
+        }
+        igraph_vector_ptr_clear(val);
+    }
+    if (e) {
+        igraph_vector_ptr_t *eal = &attr->eal;
+        long int i, n = igraph_vector_ptr_size(eal);
+        for (i = 0; i < n; i++) {
+            igraph_i_cattribute_free_rec(VECTOR(*eal)[i]);
+        }
+        igraph_vector_ptr_clear(eal);
+    }
+}
diff --git a/igraph/src/centrality.c b/igraph/src/centrality.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/centrality.c
@@ -0,0 +1,3516 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include <math.h>
+#include <string.h>    /* memset */
+#include <assert.h>
+#include "igraph_centrality.h"
+#include "igraph_math.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+#include "igraph_progress.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_topology.h"
+#include "igraph_types_internal.h"
+#include "igraph_stack.h"
+#include "igraph_dqueue.h"
+#include "config.h"
+
+#include "bigint.h"
+#include "prpack.h"
+
+int igraph_personalized_pagerank_arpack(const igraph_t *graph,
+                                        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,
+                                        igraph_arpack_options_t *options);
+
+igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vector) {
+    /* Many of the centrality measures correspond to the eigenvector of some
+     * matrix. When v is an eigenvector, c*v is also an eigenvector, therefore
+     * it may happen that all the scores in the eigenvector are negative, in which
+     * case we want to negate them since the centrality scores should be positive.
+     * However, since ARPACK is not always stable, sometimes it happens that
+     * *some* of the centrality scores are small negative numbers. This function
+     * helps distinguish between the two cases; it should return true if most of
+     * the values are relatively large negative numbers, in which case we should
+     * negate the eigenvector.
+     */
+    long int i, n = igraph_vector_size(vector);
+    igraph_real_t mi, ma;
+
+    if (n == 0) {
+        return 0;
+    }
+
+    mi = ma = VECTOR(*vector)[0];
+    for (i = 1; i < n; i++) {
+        if (VECTOR(*vector)[i] < mi) {
+            mi = VECTOR(*vector)[i];
+        }
+        if (VECTOR(*vector)[i] > ma) {
+            ma = VECTOR(*vector)[i];
+        }
+    }
+
+    if (mi >= 0) {
+        return 0;
+    }
+    if (ma <= 0) {
+        return 1;
+    }
+
+    mi /= ma;
+    return (mi < 1e-5) ? 1 : 0;
+}
+
+int igraph_i_eigenvector_centrality(igraph_real_t *to, const igraph_real_t *from,
+                                    int n, void *extra) {
+    igraph_adjlist_t *adjlist = extra;
+    igraph_vector_int_t *neis;
+    long int i, j, nlen;
+
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(adjlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            to[i] += from[nei];
+        }
+    }
+
+
+    return 0;
+}
+
+typedef struct igraph_i_eigenvector_centrality_t {
+    const igraph_t *graph;
+    const igraph_inclist_t *inclist;
+    const igraph_vector_t *weights;
+} igraph_i_eigenvector_centrality_t;
+
+int igraph_i_eigenvector_centrality2(igraph_real_t *to, const igraph_real_t *from,
+                                     int n, void *extra) {
+
+    igraph_i_eigenvector_centrality_t *data = extra;
+    const igraph_t *graph = data->graph;
+    const igraph_inclist_t *inclist = data->inclist;
+    const igraph_vector_t *weights = data->weights;
+    igraph_vector_int_t *edges;
+    long int i, j, nlen;
+
+    for (i = 0; i < n; i++) {
+        edges = igraph_inclist_get(inclist, i);
+        nlen = igraph_vector_int_size(edges);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = VECTOR(*edges)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            to[i] += w * from[nei];
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_eigenvector_centrality_loop(igraph_adjlist_t *adjlist) {
+
+    long int i, j, k, nlen, n = igraph_adjlist_size(adjlist);
+    igraph_vector_int_t *neis;
+
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(adjlist, i);
+        nlen = igraph_vector_int_size(neis);
+        for (j = 0; j < nlen && VECTOR(*neis)[j] < i; j++) ;
+        for (k = j; k < nlen && VECTOR(*neis)[k] == i; k++) ;
+        if (k != j) {
+            /* First loop edge is 'j', first non-loop edge is 'k' */
+            igraph_vector_int_remove_section(neis, j + (k - j) / 2, k);
+        }
+    }
+
+    return 0;
+}
+
+int igraph_eigenvector_centrality_undirected(const igraph_t *graph, igraph_vector_t *vector,
+        igraph_real_t *value, igraph_bool_t scale,
+        const igraph_vector_t *weights,
+        igraph_arpack_options_t *options) {
+
+    igraph_vector_t values;
+    igraph_matrix_t vectors;
+    igraph_vector_t degree;
+    long int i;
+
+    options->n = igraph_vcount(graph);
+    options->start = 1;   /* no random start vector */
+
+    if (igraph_ecount(graph) == 0) {
+        /* special case: empty graph */
+        if (value) {
+            *value = 0;
+        }
+        if (vector) {
+            igraph_vector_resize(vector, igraph_vcount(graph));
+            igraph_vector_fill(vector, 1);
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    if (weights) {
+        igraph_real_t min, max;
+
+        if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+            IGRAPH_ERROR("Invalid length of weights vector when calculating "
+                         "eigenvector centrality", IGRAPH_EINVAL);
+        }
+        IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max));
+        if (min == 0 && max == 0) {
+            /* special case: all weights are zeros */
+            if (value) {
+                *value = 0;
+            }
+            if (vector) {
+                igraph_vector_resize(vector, igraph_vcount(graph));
+                igraph_vector_fill(vector, 1);
+            }
+            return IGRAPH_SUCCESS;
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&values, 0);
+    IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, options->n);
+    IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(),
+                               IGRAPH_ALL, /*loops=*/ 0));
+    RNG_BEGIN();
+    for (i = 0; i < options->n; i++) {
+        if (VECTOR(degree)[i]) {
+            MATRIX(vectors, i, 0) = VECTOR(degree)[i] + RNG_UNIF(-1e-4, 1e-4);
+        } else {
+            MATRIX(vectors, i, 0) = 1.0;
+        }
+    }
+    RNG_END();
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    options->n = igraph_vcount(graph);
+    options->nev = 1;
+    options->ncv = 0;   /* 0 means "automatic" in igraph_arpack_rssolve */
+    options->which[0] = 'L'; options->which[1] = 'A';
+    options->start = 1;   /* no random start vector */
+
+    if (!weights) {
+
+        igraph_adjlist_t adjlist;
+
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+        IGRAPH_CHECK(igraph_i_eigenvector_centrality_loop(&adjlist));
+
+        IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigenvector_centrality,
+                                           &adjlist, options, 0, &values, &vectors));
+
+        igraph_adjlist_destroy(&adjlist);
+        IGRAPH_FINALLY_CLEAN(1);
+
+    } else {
+
+        igraph_inclist_t inclist;
+        igraph_i_eigenvector_centrality_t data = { graph, &inclist, weights };
+
+        IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+
+        IGRAPH_CHECK(igraph_inclist_remove_duplicate(graph, &inclist));
+
+        IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigenvector_centrality2,
+                                           &data, options, 0, &values, &vectors));
+
+        igraph_inclist_destroy(&inclist);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (value) {
+        *value = VECTOR(values)[0];
+    }
+
+    if (vector) {
+        igraph_real_t amax = 0;
+        long int which = 0;
+        long int i;
+        IGRAPH_CHECK(igraph_vector_resize(vector, options->n));
+
+        if (VECTOR(values)[0] <= 0) {
+            /* Pathological case: largest eigenvalue is zero, therefore all the
+             * scores can also be zeros, this will be a valid eigenvector.
+             * This usually happens with graphs that have lots of sinks and
+             * sources only. */
+            igraph_vector_fill(vector, 0);
+        } else {
+            for (i = 0; i < options->n; i++) {
+                igraph_real_t tmp;
+                VECTOR(*vector)[i] = MATRIX(vectors, i, 0);
+                tmp = fabs(VECTOR(*vector)[i]);
+                if (tmp > amax) {
+                    amax = tmp;
+                    which = i;
+                }
+            }
+            if (scale && amax != 0) {
+                igraph_vector_scale(vector, 1 / VECTOR(*vector)[which]);
+            } else if (igraph_i_vector_mostly_negative(vector)) {
+                igraph_vector_scale(vector, -1.0);
+            }
+
+            /* Correction for numeric inaccuracies (eliminating -0.0) */
+            for (i = 0; i < options->n; i++) {
+                if (VECTOR(*vector)[i] < 0) {
+                    VECTOR(*vector)[i] = 0;
+                }
+            }
+        }
+    }
+
+    if (options->info) {
+        IGRAPH_WARNING("Non-zero return code from ARPACK routine!");
+    }
+
+    igraph_matrix_destroy(&vectors);
+    igraph_vector_destroy(&values);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/* int igraph_i_evcent_dir(igraph_real_t *to, const igraph_real_t *from, */
+/*          long int n, void *extra) { */
+/*   /\* TODO *\/ */
+/*   return 0; */
+/* } */
+
+/* int igraph_i_evcent_dir2(igraph_real_t *to, const igraph_real_t *from, */
+/*           long int n, void *extra) { */
+/*   /\* TODO *\/ */
+/*   return 0; */
+/* } */
+
+int igraph_eigenvector_centrality_directed(const igraph_t *graph, igraph_vector_t *vector,
+        igraph_real_t *value, igraph_bool_t scale,
+        const igraph_vector_t *weights,
+        igraph_arpack_options_t *options) {
+
+    igraph_matrix_t values;
+    igraph_matrix_t vectors;
+    igraph_vector_t indegree;
+    igraph_bool_t dag;
+    long int i;
+
+    if (igraph_ecount(graph) == 0) {
+        /* special case: empty graph */
+        if (value) {
+            *value = 0;
+        }
+        if (vector) {
+            igraph_vector_resize(vector, igraph_vcount(graph));
+            igraph_vector_fill(vector, 1);
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    /* Quick check: if the graph is a DAG, all the eigenvector centralities are
+     * zeros, and so is the eigenvalue */
+    IGRAPH_CHECK(igraph_is_dag(graph, &dag));
+    if (dag) {
+        /* special case: graph is a DAG */
+        IGRAPH_WARNING("graph is directed and acyclic; eigenvector centralities "
+                       "will be zeros");
+        if (value) {
+            *value = 0;
+        }
+        if (vector) {
+            igraph_vector_resize(vector, igraph_vcount(graph));
+            igraph_vector_fill(vector, 0);
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    if (weights) {
+        igraph_real_t min, max;
+
+        if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+            IGRAPH_ERROR("Invalid length of weights vector when calculating "
+                         "eigenvector centrality", IGRAPH_EINVAL);
+        }
+        if (igraph_is_directed(graph)) {
+            IGRAPH_WARNING("Weighted directed graph in eigenvector centrality");
+        }
+
+        IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max));
+
+        if (min < 0.0) {
+            IGRAPH_WARNING("Negative weights, eigenpair might be complex");
+        }
+        if (min == 0.0 && max == 0.0) {
+            /* special case: all weights are zeros */
+            if (value) {
+                *value = 0;
+            }
+            if (vector) {
+                igraph_vector_resize(vector, igraph_vcount(graph));
+                igraph_vector_fill(vector, 1);
+            }
+            return IGRAPH_SUCCESS;
+        }
+    }
+
+    options->n = igraph_vcount(graph);
+    options->start = 1;
+    options->nev = 1;
+    options->ncv = 0;   /* 0 means "automatic" in igraph_arpack_rnsolve */
+    /* LM mode is not OK here because +1 and -1 can be eigenvalues at the
+     * same time, e.g.: a -> b -> a, c -> a */
+    options->which[0] = 'L' ; options->which[1] = 'R';
+
+    IGRAPH_MATRIX_INIT_FINALLY(&values, 0, 0);
+    IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&indegree, options->n);
+    IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(),
+                                 IGRAPH_IN, /*loops=*/ 1, weights));
+    RNG_BEGIN();
+    for (i = 0; i < options->n; i++) {
+        if (VECTOR(indegree)[i]) {
+            MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4);
+        } else {
+            MATRIX(vectors, i, 0) = 1.0;
+        }
+    }
+    RNG_END();
+    igraph_vector_destroy(&indegree);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    if (!weights) {
+        igraph_adjlist_t adjlist;
+
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+        IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_eigenvector_centrality,
+                                           &adjlist, options, 0, &values,
+                                           &vectors));
+
+        igraph_adjlist_destroy(&adjlist);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        igraph_inclist_t inclist;
+        igraph_i_eigenvector_centrality_t data = { graph, &inclist, weights };
+
+        IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+
+        IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_eigenvector_centrality2,
+                                           &data, options, 0, &values, &vectors));
+
+        igraph_inclist_destroy(&inclist);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (value) {
+        *value = MATRIX(values, 0, 0);
+    }
+
+    if (vector) {
+        igraph_real_t amax = 0;
+        long int which = 0;
+        long int i;
+        IGRAPH_CHECK(igraph_vector_resize(vector, options->n));
+
+        if (MATRIX(values, 0, 0) <= 0) {
+            /* Pathological case: largest eigenvalue is zero, therefore all the
+             * scores can also be zeros, this will be a valid eigenvector.
+             * This usually happens with graphs that have lots of sinks and
+             * sources only. */
+            igraph_vector_fill(vector, 0);
+            MATRIX(values, 0, 0) = 0;
+        } else {
+            for (i = 0; i < options->n; i++) {
+                igraph_real_t tmp;
+                VECTOR(*vector)[i] = MATRIX(vectors, i, 0);
+                tmp = fabs(VECTOR(*vector)[i]);
+                if (tmp > amax) {
+                    amax = tmp;
+                    which = i;
+                }
+            }
+            if (scale && amax != 0) {
+                igraph_vector_scale(vector, 1 / VECTOR(*vector)[which]);
+            } else if (igraph_i_vector_mostly_negative(vector)) {
+                igraph_vector_scale(vector, -1.0);
+            }
+        }
+
+        /* Correction for numeric inaccuracies (eliminating -0.0) */
+        for (i = 0; i < options->n; i++) {
+            if (VECTOR(*vector)[i] < 0) {
+                VECTOR(*vector)[i] = 0;
+            }
+        }
+    }
+
+    if (options->info) {
+        IGRAPH_WARNING("Non-zero return code from ARPACK routine!");
+    }
+
+    igraph_matrix_destroy(&vectors);
+    igraph_matrix_destroy(&values);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_eigenvector_centrality
+ * Eigenvector centrality of the vertices
+ *
+ * Eigenvector centrality is a measure of the importance of a node in a
+ * network. It assigns relative scores to all nodes in the network based
+ * on the principle that connections to high-scoring nodes contribute
+ * more to the score of the node in question than equal connections to
+ * low-scoring nodes. In practice, this is determined by calculating the
+ * eigenvector corresponding to the largest positive eigenvalue of the
+ * adjacency matrix. The centrality scores returned by igraph are always
+ * normalized such that the largest eigenvector centrality score is one
+ * (with one exception, see below).
+ *
+ * </para><para>
+ * Since the eigenvector centrality scores of nodes in different components
+ * do not affect each other, it may be beneficial for large graphs to
+ * decompose it first into weakly connected components and calculate the
+ * centrality scores individually for each component.
+ *
+ * </para><para>
+ * Also note that the adjacency matrix of a directed acyclic graph or the
+ * adjacency matrix of an empty graph does not possess positive eigenvalues,
+ * therefore the eigenvector centrality is not defined for these graphs.
+ * igraph will return an eigenvalue of zero in such cases. The eigenvector
+ * centralities will all be equal for an empty graph and will all be zeros
+ * for a directed acyclic graph. Such pathological cases can be detected
+ * by asking igraph to calculate the eigenvalue as well (using the \p value
+ * parameter, see below) and checking whether the eigenvalue is very close
+ * to zero.
+ *
+ * \param graph The input graph. It might be directed.
+ * \param vector Pointer to an initialized vector, it will be resized
+ *     as needed. The result of the computation is stored here. It can
+ *     be a null pointer, then it is ignored.
+ * \param value If not a null pointer, then the eigenvalue
+ *     corresponding to the found eigenvector is stored here.
+ * \param directed Boolean scalar, whether to consider edge directions
+ *     in a directed graph. It is ignored for undirected graphs.
+ * \param scale If not zero then the result will be scaled such that
+ *     the absolute value of the maximum centrality is one.
+ * \param weights A null pointer (=no edge weights), or a vector
+ *     giving the weights of the edges. The algorithm might result
+ *     complex numbers is some weights are negative. In this case only
+ *     the real part is reported.
+ * \param options Options to ARPACK. See \ref igraph_arpack_options_t
+ *    for details. Note that the function overwrites the
+ *    <code>n</code> (number of vertices) parameter and
+ *    it always starts the calculation from a non-random vector
+ *    calculated based on the degree of the vertices.
+ * \return Error code.
+ *
+ * Time complexity: depends on the input graph, usually it is O(|V|+|E|).
+ *
+ * \sa \ref igraph_pagerank and \ref igraph_personalized_pagerank for
+ *   modifications of eigenvector centrality.
+ *
+ * \example examples/simple/eigenvector_centrality.c
+ */
+
+int igraph_eigenvector_centrality(const igraph_t *graph,
+                                  igraph_vector_t *vector,
+                                  igraph_real_t *value,
+                                  igraph_bool_t directed, igraph_bool_t scale,
+                                  const igraph_vector_t *weights,
+                                  igraph_arpack_options_t *options) {
+
+    if (directed && igraph_is_directed(graph)) {
+        return igraph_eigenvector_centrality_directed(graph, vector, value,
+                scale, weights, options);
+    } else {
+        return igraph_eigenvector_centrality_undirected(graph, vector, value,
+                scale, weights, options);
+    }
+}
+
+/* struct for the unweighted variant of the HITS algorithm */
+typedef struct igraph_i_kleinberg_data_t {
+    igraph_adjlist_t *in;
+    igraph_adjlist_t *out;
+    igraph_vector_t *tmp;
+} igraph_i_kleinberg_data_t;
+
+/* struct for the weighted variant of the HITS algorithm */
+typedef struct igraph_i_kleinberg_data2_t {
+    const igraph_t *graph;
+    igraph_inclist_t *in;
+    igraph_inclist_t *out;
+    igraph_vector_t *tmp;
+    const igraph_vector_t *weights;
+} igraph_i_kleinberg_data2_t;
+
+/* ARPACK auxiliary routine for the unweighted HITS algorithm */
+int igraph_i_kleinberg_unweighted(igraph_real_t *to,
+                                  const igraph_real_t *from,
+                                  int n, void *extra) {
+    igraph_i_kleinberg_data_t *data = (igraph_i_kleinberg_data_t*)extra;
+    igraph_adjlist_t *in = data->in;
+    igraph_adjlist_t *out = data->out;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *neis;
+    long int i, j, nlen;
+
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(in, i);
+        nlen = igraph_vector_int_size(neis);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            VECTOR(*tmp)[i] += from[nei];
+        }
+    }
+
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(out, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            to[i] += VECTOR(*tmp)[nei];
+        }
+    }
+
+    return 0;
+}
+
+/* ARPACK auxiliary routine for the weighted HITS algorithm */
+int igraph_i_kleinberg_weighted(igraph_real_t *to,
+                                const igraph_real_t *from,
+                                int n, void *extra) {
+
+    igraph_i_kleinberg_data2_t *data = (igraph_i_kleinberg_data2_t*)extra;
+    igraph_inclist_t *in = data->in;
+    igraph_inclist_t *out = data->out;
+    igraph_vector_t *tmp = data->tmp;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *g = data->graph;
+    igraph_vector_int_t *neis;
+    long int i, j, nlen;
+
+    for (i = 0; i < n; i++) {
+        neis = igraph_inclist_get(in, i);
+        nlen = igraph_vector_int_size(neis);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei_edge = (long int) VECTOR(*neis)[j];
+            long int nei = IGRAPH_OTHER(g, nei_edge, i);
+            VECTOR(*tmp)[i] += from[nei] * VECTOR(*weights)[nei_edge];
+        }
+    }
+
+    for (i = 0; i < n; i++) {
+        neis = igraph_inclist_get(out, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei_edge = (long int) VECTOR(*neis)[j];
+            long int nei = IGRAPH_OTHER(g, nei_edge, i);
+            to[i] += VECTOR(*tmp)[nei] * VECTOR(*weights)[nei_edge];
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_kleinberg(const igraph_t *graph, igraph_vector_t *vector,
+                       igraph_real_t *value, igraph_bool_t scale,
+                       const igraph_vector_t *weights,
+                       igraph_arpack_options_t *options, int inout) {
+
+    igraph_adjlist_t myinadjlist, myoutadjlist;
+    igraph_inclist_t myininclist, myoutinclist;
+    igraph_adjlist_t *inadjlist, *outadjlist;
+    igraph_inclist_t *ininclist, *outinclist;
+    igraph_vector_t tmp;
+    igraph_vector_t values;
+    igraph_matrix_t vectors;
+    igraph_i_kleinberg_data_t extra;
+    igraph_i_kleinberg_data2_t extra2;
+    long int i;
+
+    if (igraph_ecount(graph) == 0 || igraph_vcount(graph) == 1) {
+        /* special case: empty graph or single vertex */
+        if (value) {
+            *value = igraph_ecount(graph) ? 1.0 : IGRAPH_NAN;
+        }
+        if (vector) {
+            igraph_vector_resize(vector, igraph_vcount(graph));
+            igraph_vector_fill(vector, 1);
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    if (weights) {
+        igraph_real_t min, max;
+
+        if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+            IGRAPH_ERROR("Invalid length of weights vector when calculating "
+                         "hub or authority scores", IGRAPH_EINVAL);
+        }
+        IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max));
+        if (min == 0 && max == 0) {
+            /* special case: all weights are zeros */
+            if (value) {
+                *value = IGRAPH_NAN;
+            }
+            if (vector) {
+                igraph_vector_resize(vector, igraph_vcount(graph));
+                igraph_vector_fill(vector, 1);
+            }
+            return IGRAPH_SUCCESS;
+        }
+    }
+
+    options->n = igraph_vcount(graph);
+    options->start = 1;   /* no random start vector */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&values, 0);
+    IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, options->n);
+
+    if (inout == 0) {
+        inadjlist = &myinadjlist;
+        outadjlist = &myoutadjlist;
+        ininclist = &myininclist;
+        outinclist = &myoutinclist;
+    } else if (inout == 1) {
+        inadjlist = &myoutadjlist;
+        outadjlist = &myinadjlist;
+        ininclist = &myoutinclist;
+        outinclist = &myininclist;
+    } else {
+        /* This should not happen */
+        IGRAPH_ERROR("Invalid 'inout' argument, please do not call "
+                     "this function directly", IGRAPH_FAILURE);
+    }
+
+    if (weights == 0) {
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &myinadjlist, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &myinadjlist);
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &myoutadjlist, IGRAPH_OUT));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &myoutadjlist);
+    } else {
+        IGRAPH_CHECK(igraph_inclist_init(graph, &myininclist, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &myininclist);
+        IGRAPH_CHECK(igraph_inclist_init(graph, &myoutinclist, IGRAPH_OUT));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &myoutinclist);
+    }
+
+    IGRAPH_CHECK(igraph_degree(graph, &tmp, igraph_vss_all(), IGRAPH_ALL, 0));
+    for (i = 0; i < options->n; i++) {
+        if (VECTOR(tmp)[i] != 0) {
+            MATRIX(vectors, i, 0) = VECTOR(tmp)[i];
+        } else {
+            MATRIX(vectors, i, 0) = 1.0;
+        }
+    }
+
+    extra.in = inadjlist; extra.out = outadjlist; extra.tmp = &tmp;
+    extra2.in = ininclist; extra2.out = outinclist; extra2.tmp = &tmp;
+    extra2.graph = graph; extra2.weights = weights;
+
+    options->nev = 1;
+    options->ncv = 0;   /* 0 means "automatic" in igraph_arpack_rssolve */
+    options->which[0] = 'L'; options->which[1] = 'M';
+
+    if (weights == 0) {
+        IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_unweighted, &extra,
+                                           options, 0, &values, &vectors));
+        igraph_adjlist_destroy(&myoutadjlist);
+        igraph_adjlist_destroy(&myinadjlist);
+        IGRAPH_FINALLY_CLEAN(2);
+    } else {
+        IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_weighted, &extra2,
+                                           options, 0, &values, &vectors));
+        igraph_inclist_destroy(&myoutinclist);
+        igraph_inclist_destroy(&myininclist);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    if (value) {
+        *value = VECTOR(values)[0];
+    }
+
+    if (vector) {
+        igraph_real_t amax = 0;
+        long int which = 0;
+        long int i;
+        IGRAPH_CHECK(igraph_vector_resize(vector, options->n));
+        for (i = 0; i < options->n; i++) {
+            igraph_real_t tmp;
+            VECTOR(*vector)[i] = MATRIX(vectors, i, 0);
+            tmp = fabs(VECTOR(*vector)[i]);
+            if (tmp > amax) {
+                amax = tmp;
+                which = i;
+            }
+        }
+        if (scale && amax != 0) {
+            igraph_vector_scale(vector, 1 / VECTOR(*vector)[which]);
+        } else if (igraph_i_vector_mostly_negative(vector)) {
+            igraph_vector_scale(vector, -1.0);
+        }
+
+        /* Correction for numeric inaccuracies (eliminating -0.0) */
+        for (i = 0; i < options->n; i++) {
+            if (VECTOR(*vector)[i] < 0) {
+                VECTOR(*vector)[i] = 0;
+            }
+        }
+    }
+
+    if (options->info) {
+        IGRAPH_WARNING("Non-zero return code from ARPACK routine!");
+    }
+    igraph_matrix_destroy(&vectors);
+    igraph_vector_destroy(&values);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_hub_score
+ * Kleinberg's hub scores
+ *
+ * The hub scores of the vertices are defined as the principal
+ * eigenvector of <code>A*A^T</code>, where <code>A</code> is the adjacency
+ * matrix of the graph, <code>A^T</code> is its transposed.
+ * </para><para>
+ * See the following reference on the meaning of this score:
+ * J. Kleinberg. Authoritative sources in a hyperlinked
+ * environment. \emb Proc. 9th ACM-SIAM Symposium on Discrete
+ * Algorithms, \eme 1998. Extended version in \emb Journal of the
+ * ACM \eme 46(1999). Also appears as IBM Research Report RJ 10076, May
+ * 1997.
+ * \param graph The input graph. Can be directed and undirected.
+ * \param vector Pointer to an initialized vector, the result is
+ *    stored here. If a null pointer then it is ignored.
+ * \param value If not a null pointer then the eigenvalue
+ *    corresponding to the calculated eigenvector is stored here.
+ * \param scale If not zero then the result will be scaled such that
+ *     the absolute value of the maximum centrality is one.
+ * \param weights A null pointer (=no edge weights), or a vector
+ *     giving the weights of the edges.
+ * \param options Options to ARPACK. See \ref igraph_arpack_options_t
+ *    for details. Note that the function overwrites the
+ *    <code>n</code> (number of vertices) parameter and
+ *    it always starts the calculation from a non-random vector
+ *    calculated based on the degree of the vertices.
+ * \return Error code.
+ *
+ * Time complexity: depends on the input graph, usually it is O(|V|),
+ * the number of vertices.
+ *
+ * \sa \ref igraph_authority_score() for the companion measure,
+ * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(),
+ * \ref igraph_eigenvector_centrality() for similar measures.
+ */
+
+int igraph_hub_score(const igraph_t *graph, igraph_vector_t *vector,
+                     igraph_real_t *value, igraph_bool_t scale,
+                     const igraph_vector_t *weights,
+                     igraph_arpack_options_t *options) {
+
+    return igraph_i_kleinberg(graph, vector, value, scale, weights, options, 0);
+}
+
+/**
+ * \function igraph_authority_score
+ * Kleinerg's authority scores
+ *
+ * The authority scores of the vertices are defined as the principal
+ * eigenvector of <code>A^T*A</code>, where <code>A</code> is the adjacency
+ * matrix of the graph, <code>A^T</code> is its transposed.
+ * </para><para>
+ * See the following reference on the meaning of this score:
+ * J. Kleinberg. Authoritative sources in a hyperlinked
+ * environment. \emb Proc. 9th ACM-SIAM Symposium on Discrete
+ * Algorithms, \eme 1998. Extended version in \emb Journal of the
+ * ACM \eme 46(1999). Also appears as IBM Research Report RJ 10076, May
+ * 1997.
+ * \param graph The input graph. Can be directed and undirected.
+ * \param vector Pointer to an initialized vector, the result is
+ *    stored here. If a null pointer then it is ignored.
+ * \param value If not a null pointer then the eigenvalue
+ *    corresponding to the calculated eigenvector is stored here.
+ * \param scale If not zero then the result will be scaled such that
+ *     the absolute value of the maximum centrality is one.
+ * \param weights A null pointer (=no edge weights), or a vector
+ *     giving the weights of the edges.
+ * \param options Options to ARPACK. See \ref igraph_arpack_options_t
+ *    for details. Note that the function overwrites the
+ *    <code>n</code> (number of vertices) parameter and
+ *    it always starts the calculation from a non-random vector
+ *    calculated based on the degree of the vertices.
+ * \return Error code.
+ *
+ * Time complexity: depends on the input graph, usually it is O(|V|),
+ * the number of vertices.
+ *
+ * \sa \ref igraph_hub_score() for the companion measure,
+ * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(),
+ * \ref igraph_eigenvector_centrality() for similar measures.
+ */
+
+int igraph_authority_score(const igraph_t *graph, igraph_vector_t *vector,
+                           igraph_real_t *value, igraph_bool_t scale,
+                           const igraph_vector_t *weights,
+                           igraph_arpack_options_t *options) {
+
+    return igraph_i_kleinberg(graph, vector, value, scale, weights, options, 1);
+}
+
+typedef struct igraph_i_pagerank_data_t {
+    const igraph_t *graph;
+    igraph_adjlist_t *adjlist;
+    igraph_real_t damping;
+    igraph_vector_t *outdegree;
+    igraph_vector_t *tmp;
+    igraph_vector_t *reset;
+} igraph_i_pagerank_data_t;
+
+typedef struct igraph_i_pagerank_data2_t {
+    const igraph_t *graph;
+    igraph_inclist_t *inclist;
+    const igraph_vector_t *weights;
+    igraph_real_t damping;
+    igraph_vector_t *outdegree;
+    igraph_vector_t *tmp;
+    igraph_vector_t *reset;
+} igraph_i_pagerank_data2_t;
+
+int igraph_i_pagerank(igraph_real_t *to, const igraph_real_t *from,
+                      int n, void *extra) {
+
+    igraph_i_pagerank_data_t *data = extra;
+    igraph_adjlist_t *adjlist = data->adjlist;
+    igraph_vector_t *outdegree = data->outdegree;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_t *reset = data->reset;
+    igraph_vector_int_t *neis;
+    long int i, j, nlen;
+    igraph_real_t sumfrom = 0.0;
+    igraph_real_t fact = 1 - data->damping;
+
+    /* Calculate p(x) / outdegree(x) in advance for all the vertices.
+     * Note that we may divide by zero here; this is intentional since
+     * we won't use those values and we save a comparison this way.
+     * At the same time, we calculate the global probability of a
+     * random jump in `sumfrom`. For vertices with no outgoing edges,
+     * we will surely jump from there if we are there, hence those
+     * vertices contribute p(x) to the teleportation probability.
+     * For vertices with some outgoing edges, we jump from there with
+     * probability `fact` if we are there, hence they contribute
+     * p(x)*fact */
+    for (i = 0; i < n; i++) {
+        sumfrom += VECTOR(*outdegree)[i] != 0 ? from[i] * fact : from[i];
+        VECTOR(*tmp)[i] = from[i] / VECTOR(*outdegree)[i];
+    }
+
+    /* Here we calculate the part of the `to` vector that results from
+     * moving along links (and not from teleportation) */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(adjlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            to[i] += VECTOR(*tmp)[nei];
+        }
+        to[i] *= data->damping;
+    }
+
+    /* Now we add the contribution from random jumps. `reset` is a vector
+     * that defines the probability of ending up in vertex i after a jump.
+     * `sumfrom` is the global probability of jumping as mentioned above. */
+    /* printf("sumfrom = %.6f\n", (float)sumfrom); */
+
+    if (reset) {
+        /* Running personalized PageRank */
+        for (i = 0; i < n; i++) {
+            to[i] += sumfrom * VECTOR(*reset)[i];
+        }
+    } else {
+        /* Traditional PageRank with uniform reset vector */
+        sumfrom /= n;
+        for (i = 0; i < n; i++) {
+            to[i] += sumfrom;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_pagerank2(igraph_real_t *to, const igraph_real_t *from,
+                       int n, void *extra) {
+
+    igraph_i_pagerank_data2_t *data = extra;
+    const igraph_t *graph = data->graph;
+    igraph_inclist_t *inclist = data->inclist;
+    const igraph_vector_t *weights = data->weights;
+    igraph_vector_t *outdegree = data->outdegree;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_t *reset = data->reset;
+    long int i, j, nlen;
+    igraph_real_t sumfrom = 0.0;
+    igraph_vector_int_t *neis;
+    igraph_real_t fact = 1 - data->damping;
+
+    /*
+    printf("PageRank weighted: multiplying vector: ");
+    for (i=0; i<n; i++) { printf(" %.4f", from[i]); }
+    printf("\n");
+    */
+
+    for (i = 0; i < n; i++) {
+        sumfrom += VECTOR(*outdegree)[i] != 0 ? from[i] * fact : from[i];
+        VECTOR(*tmp)[i] = from[i] / VECTOR(*outdegree)[i];
+    }
+
+    for (i = 0; i < n; i++) {
+        neis = igraph_inclist_get(inclist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = (long int) VECTOR(*neis)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            to[i] += VECTOR(*weights)[edge] * VECTOR(*tmp)[nei];
+        }
+        to[i] *= data->damping;
+    }
+
+    /* printf("sumfrom = %.6f\n", (float)sumfrom); */
+
+    if (reset) {
+        /* Running personalized PageRank */
+        for (i = 0; i < n; i++) {
+            to[i] += sumfrom * VECTOR(*reset)[i];
+        }
+    } else {
+        /* Traditional PageRank with uniform reset vector */
+        sumfrom /= n;
+        for (i = 0; i < n; i++) {
+            to[i] += sumfrom;
+        }
+    }
+
+    /*
+    printf("PageRank weighted: multiplied vector: ");
+    for (i=0; i<n; i++) { printf(" %.4f", to[i]); }
+    printf("\n");
+    */
+
+    return 0;
+}
+
+/**
+ * \function igraph_pagerank
+ * \brief Calculates the Google PageRank for the specified vertices.
+ *
+ * Starting from version 0.7, igraph has three PageRank implementations,
+ * and the user can choose between them. The first implementation is
+ * \c IGRAPH_PAGERANK_ALGO_POWER, also available as the (now
+ * deprecated) function \ref igraph_pagerank_old(). The second
+ * implementation is based on the ARPACK library, this was the default
+ * before igraph version 0.7: \c IGRAPH_PAGERANK_ALGO_ARPACK.
+ *
+ * The third and recommmended implementation is \c
+ * IGRAPH_PAGERANK_ALGO_PRPACK. This is using the the PRPACK package,
+ * see https://github.com/dgleich/prpack .
+ *
+ * </para><para>
+ * Please note that the PageRank of a given vertex depends on the PageRank
+ * of all other vertices, so even if you want to calculate the PageRank for
+ * only some of the vertices, all of them must be calculated. Requesting
+ * the PageRank for only some of the vertices does not result in any
+ * performance increase at all.
+ * </para>
+ *
+ * <para>
+ * For the explanation of the PageRank algorithm, see the following
+ * webpage:
+ * http://infolab.stanford.edu/~backrub/google.html , or the
+ * following reference:
+ * </para>
+ *
+ * <para>
+ * Sergey Brin and Larry Page: The Anatomy of a Large-Scale Hypertextual
+ * Web Search Engine. Proceedings of the 7th World-Wide Web Conference,
+ * Brisbane, Australia, April 1998.
+ * </para>
+ * <para>
+ * \param graph The graph object.
+ * \param algo The PageRank implementation to use. Possible values:
+ *    \c IGRAPH_PAGERANK_ALGO_POWER, \c IGRAPH_PAGERANK_ALGO_ARPACK,
+ *    \c IGRAPH_PAGERANK_ALGO_PRPACK.
+ * \param vector Pointer to an initialized vector, the result is
+ *    stored here. It is resized as needed.
+ * \param value Pointer to a real variable, the eigenvalue
+ *    corresponding to the PageRank vector is stored here. It should
+ *    be always exactly one.
+ * \param vids The vertex ids for which the PageRank is returned.
+ * \param directed Boolean, whether to consider the directedness of
+ *    the edges. This is ignored for undirected graphs.
+ * \param damping The damping factor ("d" in the original paper)
+ * \param weights Optional edge weights, it is either a null pointer,
+ *    then the edges are not weighted, or a vector of the same length
+ *    as the number of edges.
+ * \param options Options to the power method or ARPACK. For the power
+ *    method, \c IGRAPH_PAGERANK_ALGO_POWER it must be a pointer to
+ *    a \ref igraph_pagerank_power_options_t object.
+ *    For \c IGRAPH_PAGERANK_ALGO_ARPACK it must be a pointer to an
+ *    \ref igraph_arpack_options_t object. See \ref igraph_arpack_options_t
+ *    for details. Note that the function overwrites the
+ *    <code>n</code> (number of vertices), <code>nev</code> (1),
+ *    <code>ncv</code> (3) and <code>which</code> (LM) parameters and
+ *    it always starts the calculation from a non-random vector
+ *    calculated based on the degree of the vertices.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *         \c IGRAPH_EINVVID, invalid vertex id in
+ *         \p vids.
+ *
+ * Time complexity: depends on the input graph, usually it is O(|E|),
+ * the number of edges.
+ *
+ * \sa \ref igraph_pagerank_old() for the old implementation,
+ * \ref igraph_personalized_pagerank() and \ref igraph_personalized_pagerank_vs()
+ * for the personalized PageRank measure, \ref igraph_arpack_rssolve() and
+ * \ref igraph_arpack_rnsolve() for the underlying machinery.
+ *
+ * \example examples/simple/igraph_pagerank.c
+ */
+
+int 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_personalized_pagerank(graph, algo, vector, value, vids,
+                                        directed, damping, 0, weights,
+                                        options);
+}
+
+/**
+ * \function igraph_personalized_pagerank_vs
+ * \brief Calculates the personalized Google PageRank for the specified vertices.
+ *
+ * The personalized PageRank is similar to the original PageRank measure, but the
+ * random walk is reset in every step with probability 1-damping to a non-uniform
+ * distribution (instead of the uniform distribution in the original PageRank measure.
+ *
+ * </para><para>
+ * This simplified interface takes a vertex sequence and resets the random walk to
+ * one of the vertices in the specified vertex sequence, chosen uniformly. A typical
+ * application of personalized PageRank is when the random walk is reset to the same
+ * vertex every time - this can easily be achieved using \ref igraph_vss_1() which
+ * generates a vertex sequence containing only a single vertex.
+ *
+ * </para><para>
+ * Please note that the personalized PageRank of a given vertex depends on the
+ * personalized PageRank of all other vertices, so even if you want to calculate
+ * the personalized PageRank for only some of the vertices, all of them must be
+ * calculated. Requesting the personalized PageRank for only some of the vertices
+ * does not result in any performance increase at all.
+ * </para>
+ *
+ * <para>
+ * \param graph The graph object.
+ * \param algo The PageRank implementation to use. Possible values:
+ *    \c IGRAPH_PAGERANK_ALGO_POWER, \c IGRAPH_PAGERANK_ALGO_ARPACK,
+ *    \c IGRAPH_PAGERANK_ALGO_PRPACK.
+ * \param vector Pointer to an initialized vector, the result is
+ *    stored here. It is resized as needed.
+ * \param value Pointer to a real variable, the eigenvalue
+ *    corresponding to the PageRank vector is stored here. It should
+ *    be always exactly one.
+ * \param vids The vertex ids for which the PageRank is returned.
+ * \param directed Boolean, whether to consider the directedness of
+ *    the edges. This is ignored for undirected graphs.
+ * \param damping The damping factor ("d" in the original paper)
+ * \param reset_vids IDs of the vertices used when resetting the random walk.
+ * \param weights Optional edge weights, it is either a null pointer,
+ *    then the edges are not weighted, or a vector of the same length
+ *    as the number of edges.
+ * \param options Options to the power method or ARPACK. For the power
+ *    method, \c IGRAPH_PAGERANK_ALGO_POWER it must be a pointer to
+ *    a \ref igraph_pagerank_power_options_t object.
+ *    For \c IGRAPH_PAGERANK_ALGO_ARPACK it must be a pointer to an
+ *    \ref igraph_arpack_options_t object. See \ref igraph_arpack_options_t
+ *    for details. Note that the function overwrites the
+ *    <code>n</code> (number of vertices), <code>nev</code> (1),
+ *    <code>ncv</code> (3) and <code>which</code> (LM) parameters and
+ *    it always starts the calculation from a non-random vector
+ *    calculated based on the degree of the vertices.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *         \c IGRAPH_EINVVID, invalid vertex id in
+ *         \p vids or an empty reset vertex sequence in
+ *         \p vids_reset.
+ *
+ * Time complexity: depends on the input graph, usually it is O(|E|),
+ * the number of edges.
+ *
+ * \sa \ref igraph_pagerank() for the non-personalized implementation,
+ * \ref igraph_arpack_rssolve() and \ref igraph_arpack_rnsolve() for
+ * the underlying machinery.
+ */
+
+int igraph_personalized_pagerank_vs(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_vs_t reset_vids,
+                                    const igraph_vector_t *weights,
+                                    void *options) {
+    igraph_vector_t reset;
+    igraph_vit_t vit;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&reset, igraph_vcount(graph));
+    IGRAPH_CHECK(igraph_vit_create(graph, reset_vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    while (!IGRAPH_VIT_END(vit)) {
+        VECTOR(reset)[(long int)IGRAPH_VIT_GET(vit)]++;
+        IGRAPH_VIT_NEXT(vit);
+    }
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_CHECK(igraph_personalized_pagerank(graph, algo, vector,
+                 value, vids, directed,
+                 damping, &reset, weights,
+                 options));
+
+    igraph_vector_destroy(&reset);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_personalized_pagerank
+ * \brief Calculates the personalized Google PageRank for the specified vertices.
+ *
+ * The personalized PageRank is similar to the original PageRank measure, but the
+ * random walk is reset in every step with probability 1-damping to a non-uniform
+ * distribution (instead of the uniform distribution in the original PageRank measure.
+ *
+ * </para><para>
+ * Please note that the personalized PageRank of a given vertex depends on the
+ * personalized PageRank of all other vertices, so even if you want to calculate
+ * the personalized PageRank for only some of the vertices, all of them must be
+ * calculated. Requesting the personalized PageRank for only some of the vertices
+ * does not result in any performance increase at all.
+ * </para>
+ *
+ * <para>
+ * \param graph The graph object.
+ * \param algo The PageRank implementation to use. Possible values:
+ *    \c IGRAPH_PAGERANK_ALGO_POWER, \c IGRAPH_PAGERANK_ALGO_ARPACK,
+ *    \c IGRAPH_PAGERANK_ALGO_PRPACK.
+ * \param vector Pointer to an initialized vector, the result is
+ *    stored here. It is resized as needed.
+ * \param value Pointer to a real variable, the eigenvalue
+ *    corresponding to the PageRank vector is stored here. It should
+ *    be always exactly one.
+ * \param vids The vertex ids for which the PageRank is returned.
+ * \param directed Boolean, whether to consider the directedness of
+ *    the edges. This is ignored for undirected graphs.
+ * \param damping The damping factor ("d" in the original paper)
+ * \param reset The probability distribution over the vertices used when
+ *    resetting the random walk. It is either a null pointer (denoting
+ *    a uniform choice that results in the original PageRank measure)
+ *    or a vector of the same length as the number of vertices.
+ * \param weights Optional edge weights, it is either a null pointer,
+ *    then the edges are not weighted, or a vector of the same length
+ *    as the number of edges.
+ * \param options Options to the power method or ARPACK. For the power
+ *    method, \c IGRAPH_PAGERANK_ALGO_POWER it must be a pointer to
+ *    a \ref igraph_pagerank_power_options_t object.
+ *    For \c IGRAPH_PAGERANK_ALGO_ARPACK it must be a pointer to an
+ *    \ref igraph_arpack_options_t object. See \ref igraph_arpack_options_t
+ *    for details. Note that the function overwrites the
+ *    <code>n</code> (number of vertices), <code>nev</code> (1),
+ *    <code>ncv</code> (3) and <code>which</code> (LM) parameters and
+ *    it always starts the calculation from a non-random vector
+ *    calculated based on the degree of the vertices.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *         \c IGRAPH_EINVVID, invalid vertex id in
+ *         \p vids or an invalid reset vector in \p reset.
+ *
+ * Time complexity: depends on the input graph, usually it is O(|E|),
+ * the number of edges.
+ *
+ * \sa \ref igraph_pagerank() for the non-personalized implementation,
+ * \ref igraph_arpack_rssolve() and \ref igraph_arpack_rnsolve() for
+ * the underlying machinery.
+ */
+int 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) {
+
+    if (algo == IGRAPH_PAGERANK_ALGO_POWER) {
+        igraph_pagerank_power_options_t *o =
+            (igraph_pagerank_power_options_t *) options;
+        if (reset) {
+            IGRAPH_WARNING("Cannot use weights with power method, "
+                           "weights will be ignored");
+        }
+        return igraph_pagerank_old(graph, vector, vids, directed,
+                                   o->niter, o->eps, damping,
+                                   /*old=*/ 0);
+    } else if (algo == IGRAPH_PAGERANK_ALGO_ARPACK) {
+        igraph_arpack_options_t *o = (igraph_arpack_options_t*) options;
+        return igraph_personalized_pagerank_arpack(graph, vector, value, vids,
+                directed, damping, reset,
+                weights, o);
+    } else if (algo == IGRAPH_PAGERANK_ALGO_PRPACK) {
+        return igraph_personalized_pagerank_prpack(graph, vector, value, vids,
+                directed, damping, reset,
+                weights);
+    } else {
+        IGRAPH_ERROR("Unknown PageRank algorithm", IGRAPH_EINVAL);
+    }
+
+    return 0;
+}
+
+/*
+ * ARPACK-based implementation of \c igraph_personalized_pagerank.
+ *
+ * See \c igraph_personalized_pagerank for the documentation of the parameters.
+ */
+int igraph_personalized_pagerank_arpack(const igraph_t *graph, 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,
+                                        igraph_arpack_options_t *options) {
+    igraph_matrix_t values;
+    igraph_matrix_t vectors;
+    igraph_neimode_t dirmode;
+    igraph_vector_t outdegree;
+    igraph_vector_t indegree;
+    igraph_vector_t tmp;
+
+    long int i;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+
+    if (no_of_edges == 0) {
+        /* special case: empty graph */
+        if (value) {
+            *value = 1.0;
+        }
+        if (vector) {
+            igraph_vector_resize(vector, no_of_nodes);
+            igraph_vector_fill(vector, 1.0 / no_of_nodes);
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    options->n = (int) no_of_nodes;
+    options->nev = 1;
+    options->ncv = 0;   /* 0 means "automatic" in igraph_arpack_rnsolve */
+    options->which[0] = 'L'; options->which[1] = 'M';
+    options->start = 1;       /* no random start vector */
+
+    directed = directed && igraph_is_directed(graph);
+
+    if (weights) {
+        igraph_real_t min, max;
+
+        if (igraph_vector_size(weights) != no_of_edges) {
+            IGRAPH_ERROR("Invalid length of weights vector when calculating "
+                         "PageRank scores", IGRAPH_EINVAL);
+        }
+
+        IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max));
+        if (min == 0 && max == 0) {
+            /* special case: all weights are zeros */
+            if (value) {
+                *value = 1.0;
+            }
+            if (vector) {
+                igraph_vector_resize(vector, igraph_vcount(graph));
+                igraph_vector_fill(vector, 1.0 / no_of_nodes);
+            }
+            return IGRAPH_SUCCESS;
+        }
+    }
+
+    if (reset && igraph_vector_size(reset) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid length of reset vector when calculating "
+                     "personalized PageRank scores", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_MATRIX_INIT_FINALLY(&values, 0, 0);
+    IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1);
+
+    if (directed) {
+        dirmode = IGRAPH_IN;
+    } else {
+        dirmode = IGRAPH_ALL;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&indegree, options->n);
+    IGRAPH_VECTOR_INIT_FINALLY(&outdegree, options->n);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, options->n);
+
+    RNG_BEGIN();
+
+    if (reset) {
+        /* Normalize reset vector so the sum is 1 */
+        double reset_sum;
+        if (igraph_vector_min(reset) < 0) {
+            IGRAPH_ERROR("the reset vector must not contain negative elements", IGRAPH_EINVAL);
+        }
+        reset_sum = igraph_vector_sum(reset);
+        if (reset_sum == 0) {
+            IGRAPH_ERROR("the sum of the elements in the reset vector must not be zero", IGRAPH_EINVAL);
+        }
+        igraph_vector_scale(reset, 1.0 / reset_sum);
+    }
+
+    if (!weights) {
+
+        igraph_adjlist_t adjlist;
+        igraph_i_pagerank_data_t data = { graph, &adjlist, damping,
+                                          &outdegree, &tmp, reset
+                                        };
+
+        IGRAPH_CHECK(igraph_degree(graph, &outdegree, igraph_vss_all(),
+                                   directed ? IGRAPH_OUT : IGRAPH_ALL, /*loops=*/ 0));
+        IGRAPH_CHECK(igraph_degree(graph, &indegree, igraph_vss_all(),
+                                   directed ? IGRAPH_IN : IGRAPH_ALL, /*loops=*/ 0));
+        /* Set up an appropriate starting vector. We start from the in-degrees
+         * plus some small random noise to avoid convergence problems */
+        for (i = 0; i < options->n; i++) {
+            if (VECTOR(indegree)[i]) {
+                MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4);
+            } else {
+                MATRIX(vectors, i, 0) = 1;
+            }
+        }
+
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, dirmode));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+        IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_pagerank,
+                                           &data, options, 0, &values, &vectors));
+
+        igraph_adjlist_destroy(&adjlist);
+        IGRAPH_FINALLY_CLEAN(1);
+
+    } else {
+
+        igraph_inclist_t inclist;
+        igraph_bool_t negative_weight_warned = 0;
+        igraph_i_pagerank_data2_t data = { graph, &inclist, weights,
+                                           damping, &outdegree, &tmp, reset
+                                         };
+
+        IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, dirmode));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+
+        /* Weighted degree */
+        for (i = 0; i < no_of_edges; i++) {
+            long int from = IGRAPH_FROM(graph, i);
+            long int to = IGRAPH_TO(graph, i);
+            igraph_real_t weight = VECTOR(*weights)[i];
+            if (weight < 0 && !negative_weight_warned) {
+                IGRAPH_WARNING("replacing negative weights with zeros");
+                weight = 0;
+                negative_weight_warned = 1;
+            }
+            VECTOR(outdegree)[from] += weight;
+            VECTOR(indegree) [to]   += weight;
+            if (!directed) {
+                VECTOR(outdegree)[to]   += weight;
+                VECTOR(indegree) [from] += weight;
+            }
+        }
+        /* Set up an appropriate starting vector. We start from the in-degrees
+         * plus some small random noise to avoid convergence problems */
+        for (i = 0; i < options->n; i++) {
+            if (VECTOR(indegree)[i]) {
+                MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4);
+            } else {
+                MATRIX(vectors, i, 0) = 1;
+            }
+        }
+
+        IGRAPH_CHECK(igraph_arpack_rnsolve(igraph_i_pagerank2,
+                                           &data, options, 0, &values, &vectors));
+
+        igraph_inclist_destroy(&inclist);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    RNG_END();
+
+    igraph_vector_destroy(&tmp);
+    igraph_vector_destroy(&outdegree);
+    igraph_vector_destroy(&indegree);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (value) {
+        *value = MATRIX(values, 0, 0);
+    }
+
+    if (vector) {
+        long int i;
+        igraph_vit_t vit;
+        long int nodes_to_calc;
+        igraph_real_t sum = 0;
+
+        for (i = 0; i < no_of_nodes; i++) {
+            sum += MATRIX(vectors, i, 0);
+        }
+
+        IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+        nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+
+        IGRAPH_CHECK(igraph_vector_resize(vector, nodes_to_calc));
+        for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit);
+             IGRAPH_VIT_NEXT(vit), i++) {
+            VECTOR(*vector)[i] = MATRIX(vectors, (long int)IGRAPH_VIT_GET(vit), 0);
+            VECTOR(*vector)[i] /= sum;
+        }
+
+        igraph_vit_destroy(&vit);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (options->info) {
+        IGRAPH_WARNING("Non-zero return code from ARPACK routine!");
+    }
+
+    igraph_matrix_destroy(&vectors);
+    igraph_matrix_destroy(&values);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_betweenness
+ * \brief Betweenness centrality of some vertices.
+ *
+ * </para><para>
+ * The betweenness centrality of a vertex is the number of geodesics
+ * going through it. If there are more than one geodesic between two
+ * vertices, the value of these geodesics are weighted by one over the
+ * number of geodesics.
+ * \param graph The graph object.
+ * \param res The result of the computation, a vector containing the
+ *        betweenness scores for the specified vertices.
+ * \param vids The vertices of which the betweenness centrality scores
+ *        will be calculated.
+ * \param directed Logical, if true directed paths will be considered
+ *        for directed graphs. It is ignored for undirected graphs.
+ * \param weights An optional vector containing edge weights for
+ *        calculating weighted betweenness. Supply a null pointer here
+ *        for unweighted betweenness.
+ * \param nobigint Logical, if true, then we don't use big integers
+ *        for the calculation, setting this to 1 (=true) should
+ *        work for most graphs. It is currently ignored for weighted
+ *        graphs.
+ * \return Error code:
+ *        \c IGRAPH_ENOMEM, not enough memory for
+ *        temporary data.
+ *        \c IGRAPH_EINVVID, invalid vertex id passed in
+ *        \p vids.
+ *
+ * Time complexity: O(|V||E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the graph.
+ * Note that the time complexity is independent of the number of
+ * vertices for which the score is calculated.
+ *
+ * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness().
+ *     See \ref igraph_edge_betweenness() for calculating the betweenness score
+ *     of the edges in a graph. See \ref igraph_betweenness_estimate() to
+ *     estimate the betweenness score of the vertices in a graph.
+ *
+ * \example examples/simple/igraph_betweenness.c
+ */
+int 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_estimate(graph, res, vids, directed, -1, weights,
+                                       nobigint);
+}
+
+int igraph_i_betweenness_estimate_weighted(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_bool_t directed,
+        igraph_real_t cutoff,
+        const igraph_vector_t *weights,
+        igraph_bool_t nobigint) {
+
+    igraph_real_t minweight;
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph);
+    igraph_2wheap_t Q;
+    igraph_inclist_t inclist;
+    igraph_adjlist_t fathers;
+    long int source, j;
+    igraph_stack_t S;
+    igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL;
+    igraph_vector_t dist, nrgeo, tmpscore;
+    igraph_vector_t v_tmpres, *tmpres = &v_tmpres;
+    igraph_vit_t vit;
+    int cmp_result;
+    const double eps = IGRAPH_SHORTEST_PATH_EPSILON;
+
+    IGRAPH_UNUSED(nobigint);
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL);
+    }
+    minweight = igraph_vector_min(weights);
+    if (minweight <= 0) {
+        IGRAPH_ERROR("Weight vector must be positive", IGRAPH_EINVAL);
+    } else if (minweight <= eps) {
+        IGRAPH_WARNING("Some weights are smaller than epsilon, calculations may suffer from numerical precision.");
+    }
+
+    IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
+    IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
+    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+    IGRAPH_CHECK(igraph_adjlist_init_empty(&fathers, no_of_nodes));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &fathers);
+
+    IGRAPH_CHECK(igraph_stack_init(&S, no_of_nodes));
+    IGRAPH_FINALLY(igraph_stack_destroy, &S);
+    IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmpscore, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&nrgeo, no_of_nodes);
+
+    if (igraph_vs_is_all(&vids)) {
+        IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
+        igraph_vector_null(res);
+        tmpres = res;
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes);
+    }
+
+    for (source = 0; source < no_of_nodes; source++) {
+        IGRAPH_PROGRESS("Betweenness centrality: ", 100.0 * source / no_of_nodes, 0);
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        igraph_2wheap_push_with_index(&Q, source, -1.0);
+        VECTOR(dist)[source] = 1.0;
+        VECTOR(nrgeo)[source] = 1;
+
+        while (!igraph_2wheap_empty(&Q)) {
+            long int minnei = igraph_2wheap_max_index(&Q);
+            igraph_real_t mindist = -igraph_2wheap_delete_max(&Q);
+            igraph_vector_int_t *neis;
+            long int nlen;
+
+            igraph_stack_push(&S, minnei);
+            if (cutoff > 0 && VECTOR(dist)[minnei] >= cutoff + 1.0) {
+                continue;
+            }
+
+            /* Now check all neighbors of 'minnei' for a shorter path */
+            neis = igraph_inclist_get(&inclist, minnei);
+            nlen = igraph_vector_int_size(neis);
+            for (j = 0; j < nlen; j++) {
+                long int edge = (long int) VECTOR(*neis)[j];
+                long int to = IGRAPH_OTHER(graph, edge, minnei);
+                igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
+                igraph_real_t curdist = VECTOR(dist)[to];
+
+                if (curdist == 0) {
+                    /* this means curdist is infinity */
+                    cmp_result = -1;
+                } else {
+                    cmp_result = igraph_cmp_epsilon(altdist, curdist, eps);
+                }
+
+                if (curdist == 0) {
+                    /* This is the first non-infinite distance */
+                    igraph_vector_int_t *v = igraph_adjlist_get(&fathers, to);
+                    igraph_vector_int_resize(v, 1);
+                    VECTOR(*v)[0] = minnei;
+                    VECTOR(nrgeo)[to] = VECTOR(nrgeo)[minnei];
+
+                    VECTOR(dist)[to] = altdist;
+                    IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, to, -altdist));
+                } else if (cmp_result < 0) {
+                    /* This is a shorter path */
+                    igraph_vector_int_t *v = igraph_adjlist_get(&fathers, to);
+                    igraph_vector_int_resize(v, 1);
+                    VECTOR(*v)[0] = minnei;
+                    VECTOR(nrgeo)[to] = VECTOR(nrgeo)[minnei];
+
+                    VECTOR(dist)[to] = altdist;
+                    IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist));
+                } else if (cmp_result == 0) {
+                    igraph_vector_int_t *v = igraph_adjlist_get(&fathers, to);
+                    igraph_vector_int_push_back(v, minnei);
+                    VECTOR(nrgeo)[to] += VECTOR(nrgeo)[minnei];
+                }
+            }
+
+        } /* !igraph_2wheap_empty(&Q) */
+
+        while (!igraph_stack_empty(&S)) {
+            long int w = (long int) igraph_stack_pop(&S);
+            igraph_vector_int_t *fatv = igraph_adjlist_get(&fathers, w);
+            long int fatv_len = igraph_vector_int_size(fatv);
+            for (j = 0; j < fatv_len; j++) {
+                long int f = (long int) VECTOR(*fatv)[j];
+                VECTOR(tmpscore)[f] += VECTOR(nrgeo)[f] / VECTOR(nrgeo)[w] * (1 + VECTOR(tmpscore)[w]);
+            }
+            if (w != source) {
+                VECTOR(*tmpres)[w] += VECTOR(tmpscore)[w];
+            }
+
+            VECTOR(tmpscore)[w] = 0;
+            VECTOR(dist)[w] = 0;
+            VECTOR(nrgeo)[w] = 0;
+            igraph_vector_int_clear(igraph_adjlist_get(&fathers, w));
+        }
+
+    } /* source < no_of_nodes */
+
+    if (!igraph_vs_is_all(&vids)) {
+        IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+        IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit)));
+
+        for (j = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit);
+             IGRAPH_VIT_NEXT(vit), j++) {
+            long int node = IGRAPH_VIT_GET(vit);
+            VECTOR(*res)[j] = VECTOR(*tmpres)[node];
+        }
+
+        no_of_nodes = (igraph_integer_t) j;
+
+        igraph_vit_destroy(&vit);
+        igraph_vector_destroy(tmpres);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    if (!directed || !igraph_is_directed(graph)) {
+        for (j = 0; j < no_of_nodes; j++) {
+            VECTOR(*res)[j] /= 2.0;
+        }
+    }
+
+    IGRAPH_PROGRESS("Betweenness centrality: ", 100.0, 0);
+
+    igraph_vector_destroy(&nrgeo);
+    igraph_vector_destroy(&tmpscore);
+    igraph_vector_destroy(&dist);
+    igraph_stack_destroy(&S);
+    igraph_adjlist_destroy(&fathers);
+    igraph_inclist_destroy(&inclist);
+    igraph_2wheap_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(7);
+
+    return 0;
+}
+
+void igraph_i_destroy_biguints(igraph_biguint_t *p) {
+    igraph_biguint_t *p2 = p;
+    while ( *((long int*)(p)) ) {
+        igraph_biguint_destroy(p);
+        p++;
+    }
+    igraph_Free(p2);
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_betweenness_estimate
+ * \brief Estimated betweenness centrality of some vertices.
+ *
+ * </para><para>
+ * The betweenness centrality of a vertex is the number of geodesics
+ * going through it. If there are more than one geodesic between two
+ * vertices, the value of these geodesics are weighted by one over the
+ * number of geodesics. When estimating betweenness centrality, igraph
+ * takes into consideration only those paths that are shorter than or
+ * equal to a prescribed length. Note that the estimated centrality
+ * will always be less than the real one.
+ *
+ * \param graph The graph object.
+ * \param res The result of the computation, a vector containing the
+ *        estimated betweenness scores for the specified vertices.
+ * \param vids The vertices of which the betweenness centrality scores
+ *        will be estimated.
+ * \param directed Logical, if true directed paths will be considered
+ *        for directed graphs. It is ignored for undirected graphs.
+ * \param cutoff The maximal length of paths that will be considered.
+ *        If zero or negative, the exact betweenness will be calculated
+ *        (no upper limit on path lengths).
+ * \param weights An optional vector containing edge weights for
+ *        calculating weighted betweenness. Supply a null pointer here
+ *        for unweighted betweenness.
+ * \param nobigint Logical, if true, then we don't use big integers
+ *        for the calculation, setting this to 1 (=true) should
+ *        work for most graphs. It is currently ignored for weighted
+ *        graphs.
+ * \return Error code:
+ *        \c IGRAPH_ENOMEM, not enough memory for
+ *        temporary data.
+ *        \c IGRAPH_EINVVID, invalid vertex id passed in
+ *        \p vids.
+ *
+ * Time complexity: O(|V||E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the graph.
+ * Note that the time complexity is independent of the number of
+ * vertices for which the score is calculated.
+ *
+ * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness().
+ *     See \ref igraph_edge_betweenness() for calculating the betweenness score
+ *     of the edges in a graph.
+ */
+int igraph_betweenness_estimate(const igraph_t *graph, igraph_vector_t *res,
+                                const igraph_vs_t vids, igraph_bool_t directed,
+                                igraph_real_t cutoff,
+                                const igraph_vector_t *weights,
+                                igraph_bool_t nobigint) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    long int *distance;
+    unsigned long long int *nrgeo = 0;  /* must be long long; consider grid
+                       graphs for example */
+    igraph_biguint_t *big_nrgeo = 0;
+    double *tmpscore;
+    igraph_stack_t stack = IGRAPH_STACK_NULL;
+    long int source;
+    long int j, k, nneis;
+    igraph_vector_int_t *neis;
+    igraph_vector_t v_tmpres, *tmpres = &v_tmpres;
+    igraph_vit_t vit;
+
+    igraph_adjlist_t adjlist_out, adjlist_in;
+    igraph_adjlist_t *adjlist_out_p, *adjlist_in_p;
+
+    igraph_biguint_t D, R, T;
+
+    if (weights) {
+        return igraph_i_betweenness_estimate_weighted(graph, res, vids, directed,
+                cutoff, weights, nobigint);
+    }
+
+    if (!igraph_vs_is_all(&vids)) {
+        /* subset */
+        IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes);
+    } else {
+        /* only  */
+        IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
+        igraph_vector_null(res);
+        tmpres = res;
+    }
+
+    directed = directed && igraph_is_directed(graph);
+    if (directed) {
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_out, IGRAPH_OUT));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out);
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_in);
+        adjlist_out_p = &adjlist_out;
+        adjlist_in_p = &adjlist_in;
+    } else {
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_out, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out);
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_in);
+        adjlist_out_p = &adjlist_out;
+        adjlist_in_p = &adjlist_in;
+    }
+    for (j = 0; j < no_of_nodes; j++) {
+        igraph_vector_int_clear(igraph_adjlist_get(adjlist_in_p, j));
+    }
+
+    distance = igraph_Calloc(no_of_nodes, long int);
+    if (distance == 0) {
+        IGRAPH_ERROR("betweenness failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, distance);
+    if (nobigint) {
+        nrgeo = igraph_Calloc(no_of_nodes, unsigned long long int);
+        if (nrgeo == 0) {
+            IGRAPH_ERROR("betweenness failed", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, nrgeo);
+    } else {
+        /* +1 is to have one containing zeros, when we free it, we stop
+           at the zero */
+        big_nrgeo = igraph_Calloc(no_of_nodes + 1, igraph_biguint_t);
+        if (!big_nrgeo) {
+            IGRAPH_ERROR("betweenness failed", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_i_destroy_biguints, big_nrgeo);
+        for (j = 0; j < no_of_nodes; j++) {
+            IGRAPH_CHECK(igraph_biguint_init(&big_nrgeo[j]));
+        }
+        IGRAPH_CHECK(igraph_biguint_init(&D));
+        IGRAPH_FINALLY(igraph_biguint_destroy, &D);
+        IGRAPH_CHECK(igraph_biguint_init(&R));
+        IGRAPH_FINALLY(igraph_biguint_destroy, &R);
+        IGRAPH_CHECK(igraph_biguint_init(&T));
+        IGRAPH_FINALLY(igraph_biguint_destroy, &T);
+    }
+    tmpscore = igraph_Calloc(no_of_nodes, double);
+    if (tmpscore == 0) {
+        IGRAPH_ERROR("betweenness failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, tmpscore);
+
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    igraph_stack_init(&stack, no_of_nodes);
+    IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+
+    /* here we go */
+
+    for (source = 0; source < no_of_nodes; source++) {
+        IGRAPH_PROGRESS("Betweenness centrality: ", 100.0 * source / no_of_nodes, 0);
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        IGRAPH_CHECK(igraph_dqueue_push(&q, source));
+        if (nobigint) {
+            nrgeo[source] = 1;
+        } else {
+            igraph_biguint_set_limb(&big_nrgeo[source], 1);
+        }
+        distance[source] = 1;
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            IGRAPH_CHECK(igraph_stack_push(&stack, actnode));
+
+            if (cutoff > 0 && distance[actnode] >= cutoff + 1) {
+                continue;
+            }
+
+            neis = igraph_adjlist_get(adjlist_out_p, actnode);
+            nneis = igraph_vector_int_size(neis);
+            for (j = 0; j < nneis; j++) {
+                long int neighbor = (long int) VECTOR(*neis)[j];
+                if (distance[neighbor] == 0) {
+                    distance[neighbor] = distance[actnode] + 1;
+                    IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                }
+                if (distance[neighbor] == distance[actnode] + 1) {
+                    igraph_vector_int_t *v = igraph_adjlist_get(adjlist_in_p,
+                                             neighbor);
+                    igraph_vector_int_push_back(v, actnode);
+                    if (nobigint) {
+                        nrgeo[neighbor] += nrgeo[actnode];
+                    } else {
+                        IGRAPH_CHECK(igraph_biguint_add(&big_nrgeo[neighbor],
+                                                        &big_nrgeo[neighbor],
+                                                        &big_nrgeo[actnode]));
+                    }
+                }
+            }
+        } /* while !igraph_dqueue_empty */
+
+        /* Ok, we've the distance of each node and also the number of
+           shortest paths to them. Now we do an inverse search, starting
+           with the farthest nodes. */
+        while (!igraph_stack_empty(&stack)) {
+            long int actnode = (long int) igraph_stack_pop(&stack);
+            neis = igraph_adjlist_get(adjlist_in_p, actnode);
+            nneis = igraph_vector_int_size(neis);
+            for (j = 0; j < nneis; j++) {
+                long int neighbor = (long int) VECTOR(*neis)[j];
+                if (nobigint) {
+                    tmpscore[neighbor] +=  (tmpscore[actnode] + 1) *
+                                           ((double)(nrgeo[neighbor])) / nrgeo[actnode];
+                } else {
+                    if (!igraph_biguint_compare_limb(&big_nrgeo[actnode], 0)) {
+                        tmpscore[neighbor] = IGRAPH_INFINITY;
+                    } else {
+                        double div;
+                        limb_t shift = 1000000000L;
+                        IGRAPH_CHECK(igraph_biguint_mul_limb(&T, &big_nrgeo[neighbor],
+                                                             shift));
+                        igraph_biguint_div(&D, &R, &T, &big_nrgeo[actnode]);
+                        div = igraph_biguint_get(&D) / shift;
+                        tmpscore[neighbor] += (tmpscore[actnode] + 1) * div;
+                    }
+                }
+            }
+
+            if (actnode != source) {
+                VECTOR(*tmpres)[actnode] += tmpscore[actnode];
+            }
+
+            distance[actnode] = 0;
+            if (nobigint) {
+                nrgeo[actnode] = 0;
+            } else {
+                igraph_biguint_set_limb(&big_nrgeo[actnode], 0);
+            }
+            tmpscore[actnode] = 0;
+            igraph_vector_int_clear(igraph_adjlist_get(adjlist_in_p, actnode));
+        }
+
+    } /* for source < no_of_nodes */
+
+    IGRAPH_PROGRESS("Betweenness centrality: ", 100.0, 0);
+
+    /* clean  */
+    igraph_Free(distance);
+    if (nobigint) {
+        igraph_Free(nrgeo);
+    } else {
+        igraph_biguint_destroy(&T);
+        igraph_biguint_destroy(&R);
+        igraph_biguint_destroy(&D);
+        IGRAPH_FINALLY_CLEAN(3);
+        igraph_i_destroy_biguints(big_nrgeo);
+    }
+    igraph_Free(tmpscore);
+
+    igraph_dqueue_destroy(&q);
+    igraph_stack_destroy(&stack);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    /* Keep only the requested vertices */
+    if (!igraph_vs_is_all(&vids)) {
+        IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+        IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit)));
+
+        for (k = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit);
+             IGRAPH_VIT_NEXT(vit), k++) {
+            long int node = IGRAPH_VIT_GET(vit);
+            VECTOR(*res)[k] = VECTOR(*tmpres)[node];
+        }
+
+        igraph_vit_destroy(&vit);
+        igraph_vector_destroy(tmpres);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    /* divide by 2 for undirected graph */
+    if (!directed) {
+        nneis = igraph_vector_size(res);
+        for (j = 0; j < nneis; j++) {
+            VECTOR(*res)[j] /= 2.0;
+        }
+    }
+
+    igraph_adjlist_destroy(&adjlist_out);
+    igraph_adjlist_destroy(&adjlist_in);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_edge_betweenness_estimate_weighted(const igraph_t *graph,
+        igraph_vector_t *result,
+        igraph_bool_t directed,
+        igraph_real_t cutoff,
+        const igraph_vector_t *weights) {
+
+    igraph_real_t minweight;
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph);
+    igraph_2wheap_t Q;
+    igraph_inclist_t inclist;
+    igraph_inclist_t fathers;
+    igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL;
+    igraph_vector_t distance, tmpscore;
+    igraph_vector_long_t nrgeo;
+    long int source, j;
+    int cmp_result;
+    const double eps = IGRAPH_SHORTEST_PATH_EPSILON;
+    igraph_stack_t S;
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL);
+    }
+    minweight = igraph_vector_min(weights);
+    if (minweight <= 0) {
+        IGRAPH_ERROR("Weight vector must be positive", IGRAPH_EINVAL);
+    } else if (minweight <= eps) {
+        IGRAPH_WARNING("Some weights are smaller than epsilon, calculations may suffer from numerical precision.");
+    }
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+    IGRAPH_CHECK(igraph_inclist_init_empty(&fathers, no_of_nodes));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &fathers);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&distance, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmpscore, no_of_nodes);
+    IGRAPH_CHECK(igraph_vector_long_init(&nrgeo, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &nrgeo);
+
+    IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
+    IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
+    IGRAPH_CHECK(igraph_stack_init(&S, no_of_nodes));
+    IGRAPH_FINALLY(igraph_stack_destroy, &S);
+
+    IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges));
+    igraph_vector_null(result);
+
+    for (source = 0; source < no_of_nodes; source++) {
+        IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0 * source / no_of_nodes, 0);
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /*     printf("source: %li\n", source); */
+
+        igraph_vector_null(&distance);
+        igraph_vector_null(&tmpscore);
+        igraph_vector_long_null(&nrgeo);
+
+        igraph_2wheap_push_with_index(&Q, source, -1.0);
+        VECTOR(distance)[source] = 1.0;
+        VECTOR(nrgeo)[source] = 1;
+
+        while (!igraph_2wheap_empty(&Q)) {
+            long int minnei = igraph_2wheap_max_index(&Q);
+            igraph_real_t mindist = -igraph_2wheap_delete_max(&Q);
+            igraph_vector_int_t *neis;
+            long int nlen;
+
+            /* printf("SP to %li is final, dist: %g, nrgeo: %li\n", minnei, */
+            /* VECTOR(distance)[minnei]-1.0, VECTOR(nrgeo)[minnei]); */
+
+            igraph_stack_push(&S, minnei);
+
+            if (cutoff > 0 && VECTOR(distance)[minnei] >= cutoff + 1.0) {
+                continue;
+            }
+
+            neis = igraph_inclist_get(&inclist, minnei);
+            nlen = igraph_vector_int_size(neis);
+            for (j = 0; j < nlen; j++) {
+                long int edge = (long int) VECTOR(*neis)[j];
+                long int to = IGRAPH_OTHER(graph, edge, minnei);
+                igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
+                igraph_real_t curdist = VECTOR(distance)[to];
+
+                if (curdist == 0) {
+                    /* this means curdist is infinity */
+                    cmp_result = -1;
+                } else {
+                    cmp_result = igraph_cmp_epsilon(altdist, curdist, eps);
+                }
+
+                /* printf("to=%ld, altdist = %lg, curdist = %lg, cmp = %d\n",
+                  to, altdist, curdist-1, cmp_result); */
+                if (curdist == 0) {
+                    /* This is the first finite distance to 'to' */
+                    igraph_vector_int_t *v = igraph_inclist_get(&fathers, to);
+                    /* printf("Found first path to %li (from %li)\n", to, minnei); */
+                    igraph_vector_int_resize(v, 1);
+                    VECTOR(*v)[0] = edge;
+                    VECTOR(nrgeo)[to] = VECTOR(nrgeo)[minnei];
+                    VECTOR(distance)[to] = altdist;
+                    IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, to, -altdist));
+                } else if (cmp_result < 0) {
+                    /* This is a shorter path */
+                    igraph_vector_int_t *v = igraph_inclist_get(&fathers, to);
+                    /* printf("Found a shorter path to %li (from %li)\n", to, minnei); */
+                    igraph_vector_int_resize(v, 1);
+                    VECTOR(*v)[0] = edge;
+                    VECTOR(nrgeo)[to] = VECTOR(nrgeo)[minnei];
+                    VECTOR(distance)[to] = altdist;
+                    IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist));
+                } else if (cmp_result == 0) {
+                    igraph_vector_int_t *v = igraph_inclist_get(&fathers, to);
+                    /* printf("Found a second SP to %li (from %li)\n", to, minnei); */
+                    igraph_vector_int_push_back(v, edge);
+                    VECTOR(nrgeo)[to] += VECTOR(nrgeo)[minnei];
+                }
+            }
+
+        } /* igraph_2wheap_empty(&Q) */
+
+        while (!igraph_stack_empty(&S)) {
+            long int w = (long int) igraph_stack_pop(&S);
+            igraph_vector_int_t *fatv = igraph_inclist_get(&fathers, w);
+            long int fatv_len = igraph_vector_int_size(fatv);
+            /* printf("Popping %li.\n", w); */
+            for (j = 0; j < fatv_len; j++) {
+                long int fedge = (long int) VECTOR(*fatv)[j];
+                long int neighbor = IGRAPH_OTHER(graph, fedge, w);
+                VECTOR(tmpscore)[neighbor] += ((double)VECTOR(nrgeo)[neighbor]) /
+                                              VECTOR(nrgeo)[w] * (1.0 + VECTOR(tmpscore)[w]);
+                /* printf("Scoring %li (edge %li)\n", neighbor, fedge); */
+                VECTOR(*result)[fedge] +=
+                    ((VECTOR(tmpscore)[w] + 1) * VECTOR(nrgeo)[neighbor]) /
+                    VECTOR(nrgeo)[w];
+            }
+
+            VECTOR(tmpscore)[w] = 0;
+            VECTOR(distance)[w] = 0;
+            VECTOR(nrgeo)[w] = 0;
+            igraph_vector_int_clear(fatv);
+        }
+
+    } /* source < no_of_nodes */
+
+    if (!directed || !igraph_is_directed(graph)) {
+        for (j = 0; j < no_of_edges; j++) {
+            VECTOR(*result)[j] /= 2.0;
+        }
+    }
+
+    IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0);
+
+    igraph_stack_destroy(&S);
+    igraph_2wheap_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    igraph_inclist_destroy(&inclist);
+    igraph_inclist_destroy(&fathers);
+    igraph_vector_destroy(&distance);
+    igraph_vector_destroy(&tmpscore);
+    igraph_vector_long_destroy(&nrgeo);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_edge_betweenness
+ * \brief Betweenness centrality of the edges.
+ *
+ * </para><para>
+ * The betweenness centrality of an edge is the number of geodesics
+ * going through it. If there are more than one geodesics between two
+ * vertices, the value of these geodesics are weighted by one over the
+ * number of geodesics.
+ * \param graph The graph object.
+ * \param result The result of the computation, vector containing the
+ *        betweenness scores for the edges.
+ * \param directed Logical, if true directed paths will be considered
+ *        for directed graphs. It is ignored for undirected graphs.
+ * \param weights An optional weight vector for weighted edge
+ *        betweenness. Supply a null pointer here for the unweighted
+ *        version.
+ * \return Error code:
+ *        \c IGRAPH_ENOMEM, not enough memory for
+ *        temporary data.
+ *
+ * Time complexity: O(|V||E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the graph.
+ *
+ * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness().
+ *     See \ref igraph_edge_betweenness() for calculating the betweenness score
+ *     of the edges in a graph. See \ref igraph_edge_betweenness_estimate() to
+ *     estimate the betweenness score of the edges in a graph.
+ *
+ * \example examples/simple/igraph_edge_betweenness.c
+ */
+int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result,
+                            igraph_bool_t directed,
+                            const igraph_vector_t *weights) {
+    return igraph_edge_betweenness_estimate(graph, result, directed, -1,
+                                            weights);
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_edge_betweenness_estimate
+ * \brief Estimated betweenness centrality of the edges.
+ *
+ * </para><para>
+ * The betweenness centrality of an edge is the number of geodesics
+ * going through it. If there are more than one geodesics between two
+ * vertices, the value of these geodesics are weighted by one over the
+ * number of geodesics. When estimating betweenness centrality, igraph
+ * takes into consideration only those paths that are shorter than or
+ * equal to a prescribed length. Note that the estimated centrality
+ * will always be less than the real one.
+ * \param graph The graph object.
+ * \param result The result of the computation, vector containing the
+ *        betweenness scores for the edges.
+ * \param directed Logical, if true directed paths will be considered
+ *        for directed graphs. It is ignored for undirected graphs.
+ * \param cutoff The maximal length of paths that will be considered.
+ *        If zero or negative, the exact betweenness will be calculated
+ *        (no upper limit on path lengths).
+ * \param weights An optional weight vector for weighted
+ *        betweenness. Supply a null pointer here for unweighted
+ *        betweenness.
+ * \return Error code:
+ *        \c IGRAPH_ENOMEM, not enough memory for
+ *        temporary data.
+ *
+ * Time complexity: O(|V||E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the graph.
+ *
+ * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness().
+ *     See \ref igraph_betweenness() for calculating the betweenness score
+ *     of the vertices in a graph.
+ */
+int igraph_edge_betweenness_estimate(const igraph_t *graph, igraph_vector_t *result,
+                                     igraph_bool_t directed, igraph_real_t cutoff,
+                                     const igraph_vector_t *weights) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    long int *distance;
+    unsigned long long int *nrgeo;
+    double *tmpscore;
+    igraph_stack_t stack = IGRAPH_STACK_NULL;
+    long int source;
+    long int j;
+
+    igraph_inclist_t elist_out, elist_in;
+    igraph_inclist_t *elist_out_p, *elist_in_p;
+    igraph_vector_int_t *neip;
+    long int neino;
+    long int i;
+
+    if (weights) {
+        return igraph_i_edge_betweenness_estimate_weighted(graph, result,
+                directed, cutoff, weights);
+    }
+
+    directed = directed && igraph_is_directed(graph);
+    if (directed) {
+        IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_OUT));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out);
+        IGRAPH_CHECK(igraph_inclist_init(graph, &elist_in, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &elist_in);
+        elist_out_p = &elist_out;
+        elist_in_p = &elist_in;
+    } else {
+        IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out);
+        elist_out_p = elist_in_p = &elist_out;
+    }
+
+    distance = igraph_Calloc(no_of_nodes, long int);
+    if (distance == 0) {
+        IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, distance);
+    nrgeo = igraph_Calloc(no_of_nodes, unsigned long long int);
+    if (nrgeo == 0) {
+        IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, nrgeo);
+    tmpscore = igraph_Calloc(no_of_nodes, double);
+    if (tmpscore == 0) {
+        IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, tmpscore);
+
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes));
+    IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+
+    IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges));
+
+    igraph_vector_null(result);
+
+    /* here we go */
+
+    for (source = 0; source < no_of_nodes; source++) {
+        IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0 * source / no_of_nodes, 0);
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        memset(distance, 0, (size_t) no_of_nodes * sizeof(long int));
+        memset(nrgeo, 0, (size_t) no_of_nodes * sizeof(unsigned long long int));
+        memset(tmpscore, 0, (size_t) no_of_nodes * sizeof(double));
+        igraph_stack_clear(&stack); /* it should be empty anyway... */
+
+        IGRAPH_CHECK(igraph_dqueue_push(&q, source));
+
+        nrgeo[source] = 1;
+        distance[source] = 0;
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+
+            if (cutoff > 0 && distance[actnode] >= cutoff ) {
+                continue;
+            }
+
+            /* check the neighbors and add to them to the queue if unseen before */
+            neip = igraph_inclist_get(elist_out_p, actnode);
+            neino = igraph_vector_int_size(neip);
+            for (i = 0; i < neino; i++) {
+                igraph_integer_t edge = (igraph_integer_t) VECTOR(*neip)[i], from, to;
+                long int neighbor;
+                igraph_edge(graph, edge, &from, &to);
+                neighbor = actnode != from ? from : to;
+                if (nrgeo[neighbor] != 0) {
+                    /* we've already seen this node, another shortest path? */
+                    if (distance[neighbor] == distance[actnode] + 1) {
+                        nrgeo[neighbor] += nrgeo[actnode];
+                    }
+                } else {
+                    /* we haven't seen this node yet */
+                    nrgeo[neighbor] += nrgeo[actnode];
+                    distance[neighbor] = distance[actnode] + 1;
+                    IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                    IGRAPH_CHECK(igraph_stack_push(&stack, neighbor));
+                }
+            }
+        } /* while !igraph_dqueue_empty */
+
+        /* Ok, we've the distance of each node and also the number of
+           shortest paths to them. Now we do an inverse search, starting
+           with the farthest nodes. */
+        while (!igraph_stack_empty(&stack)) {
+            long int actnode = (long int) igraph_stack_pop(&stack);
+            if (distance[actnode] < 1) {
+                continue;    /* skip source node */
+            }
+
+            /* set the temporary score of the friends */
+            neip = igraph_inclist_get(elist_in_p, actnode);
+            neino = igraph_vector_int_size(neip);
+            for (i = 0; i < neino; i++) {
+                igraph_integer_t from, to;
+                long int neighbor;
+                igraph_integer_t edgeno = (igraph_integer_t) VECTOR(*neip)[i];
+                igraph_edge(graph, edgeno, &from, &to);
+                neighbor = actnode != from ? from : to;
+                if (distance[neighbor] == distance[actnode] - 1 &&
+                    nrgeo[neighbor] != 0) {
+                    tmpscore[neighbor] +=
+                        (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode];
+                    VECTOR(*result)[edgeno] +=
+                        (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode];
+                }
+            }
+        }
+        /* Ok, we've the scores for this source */
+    } /* for source <= no_of_nodes */
+    IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0);
+
+    /* clean and return */
+    igraph_Free(distance);
+    igraph_Free(nrgeo);
+    igraph_Free(tmpscore);
+    igraph_dqueue_destroy(&q);
+    igraph_stack_destroy(&stack);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    if (directed) {
+        igraph_inclist_destroy(&elist_out);
+        igraph_inclist_destroy(&elist_in);
+        IGRAPH_FINALLY_CLEAN(2);
+    } else {
+        igraph_inclist_destroy(&elist_out);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* divide by 2 for undirected graph */
+    if (!directed || !igraph_is_directed(graph)) {
+        for (j = 0; j < igraph_vector_size(result); j++) {
+            VECTOR(*result)[j] /= 2.0;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_closeness
+ * \brief Closeness centrality calculations for some vertices.
+ *
+ * </para><para>
+ * The closeness centrality of a vertex measures how easily other
+ * vertices can be reached from it (or the other way: how easily it
+ * can be reached from the other vertices). It is defined as
+ * the number of vertices minus one divided by the sum of the
+ * lengths of all geodesics from/to the given vertex.
+ *
+ * </para><para>
+ * If the graph is not connected, and there is no path between two
+ * vertices, the number of vertices is used instead the length of the
+ * geodesic. This is longer than the longest possible geodesic in case
+ * of unweighted graphs, but may not be so in weighted graphs, so it is
+ * best not to use this function on weighted graphs.
+ *
+ * </para><para>
+ * If the graph has a single vertex only, the closeness centrality of
+ * that single vertex will be NaN (because we are essentially dividing
+ * zero with zero).
+ *
+ * \param graph The graph object.
+ * \param res The result of the computation, a vector containing the
+ *        closeness centrality scores for the given vertices.
+ * \param vids Vector giving the vertices for which the closeness
+ *        centrality scores will be computed.
+ * \param mode The type of shortest paths to be used for the
+ *        calculation in directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the lengths of the outgoing paths are calculated.
+ *        \cli IGRAPH_IN
+ *          the lengths of the incoming paths are calculated.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an
+ *          undirected one for the computation.
+ *        \endclist
+ * \param weights An optional vector containing edge weights for
+ *        weighted closeness. Supply a null pointer here for
+ *        traditional, unweighted closeness.
+ * \param normalized Boolean, whether to normalize results by multiplying
+ *        by the number of vertices minus one.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(n|E|),
+ * n is the number
+ * of vertices for which the calculation is done and
+ * |E| is the number
+ * of edges in the graph.
+ *
+ * \sa Other centrality types: \ref igraph_degree(), \ref igraph_betweenness().
+ *   See \ref igraph_closeness_estimate() to estimate closeness values.
+ */
+int 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_estimate(graph, res, vids, mode, -1, weights,
+                                     normalized);
+}
+
+int igraph_i_closeness_estimate_weighted(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_neimode_t mode,
+        igraph_real_t cutoff,
+        const igraph_vector_t *weights,
+        igraph_bool_t normalized) {
+
+    /* See igraph_shortest_paths_dijkstra() for the implementation
+       details and the dirty tricks. */
+
+    igraph_real_t minweight;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+
+    igraph_2wheap_t Q;
+    igraph_vit_t vit;
+    long int nodes_to_calc;
+
+    igraph_lazy_inclist_t inclist;
+    long int i, j;
+
+    igraph_vector_t dist;
+    igraph_vector_long_t which;
+    long int nodes_reached;
+
+    int cmp_result;
+    const double eps = IGRAPH_SHORTEST_PATH_EPSILON;
+    igraph_real_t mindist;
+
+    igraph_bool_t warning_shown = 0;
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    minweight = igraph_vector_min(weights);
+    if (minweight <= 0) {
+        IGRAPH_ERROR("Weight vector must be positive", IGRAPH_EINVAL);
+    } else if (minweight <= eps) {
+        IGRAPH_WARNING("Some weights are smaller than epsilon, calculations may suffer from numerical precision.");
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+
+    IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
+    IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
+    IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode));
+    IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes);
+    IGRAPH_CHECK(igraph_vector_long_init(&which, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &which);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
+    igraph_vector_null(res);
+
+    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+
+        long int source = IGRAPH_VIT_GET(vit);
+        igraph_2wheap_clear(&Q);
+        igraph_2wheap_push_with_index(&Q, source, -1.0);
+        VECTOR(which)[source] = i + 1;
+        VECTOR(dist)[source] = 1.0;     /* actual distance is zero but we need to store distance + 1 */
+        nodes_reached = 0;
+
+        while (!igraph_2wheap_empty(&Q)) {
+            igraph_integer_t minnei = (igraph_integer_t) igraph_2wheap_max_index(&Q);
+            /* Now check all neighbors of minnei for a shorter path */
+            igraph_vector_t *neis = igraph_lazy_inclist_get(&inclist, minnei);
+            long int nlen = igraph_vector_size(neis);
+
+            mindist = -igraph_2wheap_delete_max(&Q);
+
+            VECTOR(*res)[i] += (mindist - 1.0);
+            nodes_reached++;
+
+            if (cutoff > 0 && mindist >= cutoff + 1.0) {
+                continue;    /* NOT break!!! */
+            }
+
+            for (j = 0; j < nlen; j++) {
+                long int edge = (long int) VECTOR(*neis)[j];
+                long int to = IGRAPH_OTHER(graph, edge, minnei);
+                igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
+                igraph_real_t curdist = VECTOR(dist)[to];
+                if (curdist == 0) {
+                    /* this means curdist is infinity */
+                    cmp_result = -1;
+                } else {
+                    cmp_result = igraph_cmp_epsilon(altdist, curdist, eps);
+                }
+
+                if (VECTOR(which)[to] != i + 1) {
+                    /* First non-infinite distance */
+                    VECTOR(which)[to] = i + 1;
+                    VECTOR(dist)[to] = altdist;
+                    IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, to, -altdist));
+                } else if (cmp_result < 0) {
+                    /* This is a shorter path */
+                    VECTOR(dist)[to] = altdist;
+                    IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist));
+                }
+            }
+
+        } /* !igraph_2wheap_empty(&Q) */
+
+        /* using igraph_real_t here instead of igraph_integer_t to avoid overflow */
+        VECTOR(*res)[i] += ((igraph_real_t)no_of_nodes * (no_of_nodes - nodes_reached));
+        VECTOR(*res)[i] = (no_of_nodes - 1) / VECTOR(*res)[i];
+
+        if (((cutoff > 0 && mindist < cutoff + 1.0) || (cutoff <= 0)) &&
+            nodes_reached < no_of_nodes && !warning_shown) {
+            IGRAPH_WARNING("closeness centrality is not well-defined for disconnected graphs");
+            warning_shown = 1;
+        }
+    } /* !IGRAPH_VIT_END(vit) */
+
+    if (!normalized) {
+        for (i = 0; i < nodes_to_calc; i++) {
+            VECTOR(*res)[i] /= (no_of_nodes - 1);
+        }
+    }
+
+    igraph_vector_long_destroy(&which);
+    igraph_vector_destroy(&dist);
+    igraph_lazy_inclist_destroy(&inclist);
+    igraph_2wheap_destroy(&Q);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_closeness_estimate
+ * \brief Closeness centrality estimations for some vertices.
+ *
+ * </para><para>
+ * The closeness centrality of a vertex measures how easily other
+ * vertices can be reached from it (or the other way: how easily it
+ * can be reached from the other vertices). It is defined as
+ * the number of vertices minus one divided by the sum of the
+ * lengths of all geodesics from/to the given vertex. When estimating
+ * closeness centrality, igraph considers paths having a length less than
+ * or equal to a prescribed cutoff value.
+ *
+ * </para><para>
+ * If the graph is not connected, and there is no such path between two
+ * vertices, the number of vertices is used instead the length of the
+ * geodesic. This is always longer than the longest possible geodesic.
+ *
+ * </para><para>
+ * Since the estimation considers vertex pairs with a distance greater than
+ * the given value as disconnected, the resulting estimation will always be
+ * lower than the actual closeness centrality.
+ *
+ * \param graph The graph object.
+ * \param res The result of the computation, a vector containing the
+ *        closeness centrality scores for the given vertices.
+ * \param vids Vector giving the vertices for which the closeness
+ *        centrality scores will be computed.
+ * \param mode The type of shortest paths to be used for the
+ *        calculation in directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the lengths of the outgoing paths are calculated.
+ *        \cli IGRAPH_IN
+ *          the lengths of the incoming paths are calculated.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an
+ *          undirected one for the computation.
+ *        \endclist
+ * \param cutoff The maximal length of paths that will be considered.
+ *        If zero or negative, the exact closeness will be calculated
+ *        (no upper limit on path lengths).
+ * \param weights An optional vector containing edge weights for
+ *        weighted closeness. Supply a null pointer here for
+ *        traditional, unweighted closeness.
+ * \param normalized Boolean, whether to normalize results by multiplying
+ *        by the number of vertices minus one.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(n|E|),
+ * n is the number
+ * of vertices for which the calculation is done and
+ * |E| is the number
+ * of edges in the graph.
+ *
+ * \sa Other centrality types: \ref igraph_degree(), \ref igraph_betweenness().
+ */
+int igraph_closeness_estimate(const igraph_t *graph, igraph_vector_t *res,
+                              const igraph_vs_t vids, igraph_neimode_t mode,
+                              igraph_real_t cutoff,
+                              const igraph_vector_t *weights,
+                              igraph_bool_t normalized) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t already_counted;
+    igraph_vector_int_t *neis;
+    long int i, j;
+    long int nodes_reached;
+    long int actdist;
+    igraph_adjlist_t allneis;
+
+    igraph_dqueue_t q;
+
+    long int nodes_to_calc;
+    igraph_vit_t vit;
+
+    igraph_bool_t warning_shown = 0;
+
+    if (weights) {
+        return igraph_i_closeness_estimate_weighted(graph, res, vids, mode, cutoff,
+                weights, normalized);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("calculating closeness", IGRAPH_EINVMODE);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&already_counted, no_of_nodes);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
+    igraph_vector_null(res);
+
+    for (IGRAPH_VIT_RESET(vit), i = 0;
+         !IGRAPH_VIT_END(vit);
+         IGRAPH_VIT_NEXT(vit), i++) {
+        igraph_dqueue_clear(&q);
+        IGRAPH_CHECK(igraph_dqueue_push(&q, IGRAPH_VIT_GET(vit)));
+        IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
+        nodes_reached = 1;
+        VECTOR(already_counted)[(long int)IGRAPH_VIT_GET(vit)] = i + 1;
+
+        IGRAPH_PROGRESS("Closeness: ", 100.0 * i / no_of_nodes, NULL);
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int act = (long int) igraph_dqueue_pop(&q);
+            actdist = (long int) igraph_dqueue_pop(&q);
+
+            VECTOR(*res)[i] += actdist;
+
+            if (cutoff > 0 && actdist >= cutoff) {
+                continue;    /* NOT break!!! */
+            }
+
+            /* check the neighbors */
+            neis = igraph_adjlist_get(&allneis, act);
+            for (j = 0; j < igraph_vector_int_size(neis); j++) {
+                long int neighbor = (long int) VECTOR(*neis)[j];
+                if (VECTOR(already_counted)[neighbor] == i + 1) {
+                    continue;
+                }
+                VECTOR(already_counted)[neighbor] = i + 1;
+                nodes_reached++;
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+            }
+        }
+
+        /* using igraph_real_t here instead of igraph_integer_t to avoid overflow */
+        VECTOR(*res)[i] += ((igraph_real_t)no_of_nodes * (no_of_nodes - nodes_reached));
+        VECTOR(*res)[i] = (no_of_nodes - 1) / VECTOR(*res)[i];
+
+        if (((cutoff > 0 && actdist < cutoff) || cutoff <= 0) &&
+            no_of_nodes > nodes_reached && !warning_shown) {
+            IGRAPH_WARNING("closeness centrality is not well-defined for disconnected graphs");
+            warning_shown = 1;
+        }
+    }
+
+    if (!normalized) {
+        for (i = 0; i < nodes_to_calc; i++) {
+            VECTOR(*res)[i] /= (no_of_nodes - 1);
+        }
+    }
+
+    IGRAPH_PROGRESS("Closeness: ", 100.0, NULL);
+
+    /* Clean */
+    igraph_dqueue_destroy(&q);
+    igraph_vector_destroy(&already_counted);
+    igraph_vit_destroy(&vit);
+    igraph_adjlist_destroy(&allneis);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/**
+ * \function igraph_centralization
+ * Calculate the centralization score from the node level scores
+ *
+ * For a centrality score defined on the vertices of a graph, it is
+ * possible to define a graph level centralization index, by
+ * calculating the sum of the deviation from the maximum centrality
+ * score. Consequently, the higher the centralization index of the
+ * graph, the more centralized the structure is.
+ *
+ * </para><para>In order to make graphs of different sizes comparable,
+ * the centralization index is usually normalized to a number between
+ * zero and one, by dividing the (unnormalized) centralization score
+ * of the most centralized structure with the same number of vertices.
+ *
+ * </para><para>For most centrality indices the most centralized
+ * structure is the star graph, a single center connected to all other
+ * nodes in the network. There are some variation depending on whether
+ * the graph is directed or not, whether loop edges are allowed, etc.
+ *
+ * </para><para>
+ * This function simply calculates the graph level index, if the node
+ * level scores and the theoretical maximum are given. It is called by
+ * all the measure-specific centralization functions.
+ *
+ * \param scores A vector containing the node-level centrality
+ *     scores.
+ * \param theoretical_max The graph level centrality score of the most
+ *     centralized graph with the same number of vertices. Only used
+ *     if \c normalized set to true.
+ * \param normalized Boolean, whether to normalize the centralization
+ *     by dividing the supplied theoretical maximum.
+ * \return The graph level index.
+ *
+ * \sa \ref igraph_centralization_degree(), \ref
+ * igraph_centralization_betweenness(), \ref
+ * igraph_centralization_closeness(), and \ref
+ * igraph_centralization_eigenvector_centrality() for specific
+ * centralization functions.
+ *
+ * Time complexity: O(n), the length of the score vector.
+ *
+ * \example examples/simple/centralization.c
+ */
+
+igraph_real_t igraph_centralization(const igraph_vector_t *scores,
+                                    igraph_real_t theoretical_max,
+                                    igraph_bool_t normalized) {
+
+    long int no_of_nodes = igraph_vector_size(scores);
+    igraph_real_t maxscore = 0.0;
+    igraph_real_t cent = 0.0;
+
+    if (no_of_nodes != 0) {
+        maxscore = igraph_vector_max(scores);
+        cent = no_of_nodes * maxscore - igraph_vector_sum(scores);
+        if (normalized) {
+            cent = cent / theoretical_max;
+        }
+    } else {
+        cent = IGRAPH_NAN;
+    }
+
+    return cent;
+}
+
+/**
+ * \function igraph_centralization_degree
+ * Calculate vertex degree and graph centralization
+ *
+ * This function calculates the degree of the vertices by passing its
+ * arguments to \ref igraph_degree(); and it calculates the graph
+ * level centralization index based on the results by calling \ref
+ * igraph_centralization().
+ * \param graph The input graph.
+ * \param res A vector if you need the node-level degree scores, or a
+ *     null pointer otherwise.
+ * \param mode Constant the specifies the type of degree for directed
+ *     graphs. Possible values: \c IGRAPH_IN, \c IGRAPH_OUT and \c
+ *     IGRAPH_ALL. This argument is ignored for undirected graphs.
+ * \param loops Boolean, whether to consider loop edges when
+ *     calculating the degree (and the centralization).
+ * \param centralization Pointer to a real number, the centralization
+ *     score is placed here.
+ * \param theoretical_max Pointer to real number or a null pointer. If
+ *     not a null pointer, then the theoretical maximum graph
+ *     centrality score for a graph with the same number vertices is
+ *     stored here.
+ * \param normalized Boolean, whether to calculate a normalized
+ *     centralization score. See \ref igraph_centralization() for how
+ *     the normalization is done.
+ * \return Error code.
+ *
+ * \sa \ref igraph_centralization(), \ref igraph_degree().
+ *
+ * Time complexity: the complexity of \ref igraph_degree() plus O(n),
+ * the number of vertices queried, for calculating the centralization
+ * score.
+ */
+
+int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res,
+                                 igraph_neimode_t mode, igraph_bool_t loops,
+                                 igraph_real_t *centralization,
+                                 igraph_real_t *theoretical_max,
+                                 igraph_bool_t normalized) {
+
+    igraph_vector_t myscores;
+    igraph_vector_t *scores = res;
+    igraph_real_t *tmax = theoretical_max, mytmax;
+
+    if (!tmax) {
+        tmax = &mytmax;
+    }
+
+    if (!res) {
+        scores = &myscores;
+        IGRAPH_VECTOR_INIT_FINALLY(scores, 0);
+    }
+
+    IGRAPH_CHECK(igraph_degree(graph, scores, igraph_vss_all(), mode, loops));
+
+    IGRAPH_CHECK(igraph_centralization_degree_tmax(graph, 0, mode, loops,
+                 tmax));
+
+    *centralization = igraph_centralization(scores, *tmax, normalized);
+
+    if (!res) {
+        igraph_vector_destroy(scores);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_centralization_degree_tmax
+ * Theoretical maximum for graph centralization based on degree
+ *
+ * This function returns the theoretical maximum graph centrality
+ * based on vertex degree.
+ *
+ * </para><para>
+ * There are two ways to call this function, the first is to supply a
+ * graph as the <code>graph</code> argument, and then the number of
+ * vertices is taken from this object, and its directedness is
+ * considered as well. The <code>nodes</code> argument is ignored in
+ * this case. The <code>mode</code> argument is also ignored if the
+ * supplied graph is undirected.
+ *
+ * </para><para>
+ * The other way is to supply a null pointer as the <code>graph</code>
+ * argument. In this case the <code>nodes</code> and <code>mode</code>
+ * arguments are considered.
+ *
+ * </para><para>
+ * The most centralized structure is the star. More specifically, for
+ * undirected graphs it is the star, for directed graphs it is the
+ * in-star or the out-star.
+ * \param graph A graph object or a null pointer, see the description
+ *     above.
+ * \param nodes The number of nodes. This is ignored if the
+ *     <code>graph</code> argument is not a null pointer.
+ * \param mode Constant, whether the calculation is based on in-degree
+ *     (<code>IGRAPH_IN</code>), out-degree (<code>IGRAPH_OUT</code>)
+ *     or total degree (<code>IGRAPH_ALL</code>). This is ignored if
+ *     the <code>graph</code> argument is not a null pointer and the
+ *     given graph is undirected.
+ * \param loops Boolean scalar, whether to consider loop edges in the
+ *     calculation.
+ * \param res Pointer to a real variable, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ *
+ * \sa \ref igraph_centralization_degree() and \ref
+ * igraph_centralization().
+ */
+
+int igraph_centralization_degree_tmax(const igraph_t *graph,
+                                      igraph_integer_t nodes,
+                                      igraph_neimode_t mode,
+                                      igraph_bool_t loops,
+                                      igraph_real_t *res) {
+
+    igraph_bool_t directed = mode != IGRAPH_ALL;
+    igraph_real_t real_nodes;
+
+    if (graph) {
+        directed = igraph_is_directed(graph);
+        nodes = igraph_vcount(graph);
+    }
+
+    real_nodes = nodes;    /* implicit cast to igraph_real_t */
+
+    if (directed) {
+        switch (mode) {
+        case IGRAPH_IN:
+        case IGRAPH_OUT:
+            if (!loops) {
+                *res = (real_nodes - 1) * (real_nodes - 1);
+            } else {
+                *res = (real_nodes - 1) * real_nodes;
+            }
+            break;
+        case IGRAPH_ALL:
+            if (!loops) {
+                *res = 2 * (real_nodes - 1) * (real_nodes - 2);
+            } else {
+                *res = 2 * (real_nodes - 1) * (real_nodes - 1);
+            }
+            break;
+        }
+    } else {
+        if (!loops) {
+            *res = (real_nodes - 1) * (real_nodes - 2);
+        } else {
+            *res = (real_nodes - 1) * real_nodes;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_centralization_betweenness
+ * Calculate vertex betweenness and graph centralization
+ *
+ * This function calculates the betweenness centrality of the vertices
+ * by passing its arguments to \ref igraph_betweenness(); and it
+ * calculates the graph level centralization index based on the
+ * results by calling \ref igraph_centralization().
+ * \param graph The input graph.
+ * \param res A vector if you need the node-level betweenness scores, or a
+ *     null pointer otherwise.
+ * \param directed Boolean, whether to consider directed paths when
+ *     calculating betweenness.
+ * \param nobigint Logical, if true, then we don't use big integers
+ *        for the calculation, setting this to zero (=false) should
+ *        work for most graphs. It is currently ignored for weighted
+ *        graphs.
+ * \param centralization Pointer to a real number, the centralization
+ *     score is placed here.
+ * \param theoretical_max Pointer to real number or a null pointer. If
+ *     not a null pointer, then the theoretical maximum graph
+ *     centrality score for a graph with the same number vertices is
+ *     stored here.
+ * \param normalized Boolean, whether to calculate a normalized
+ *     centralization score. See \ref igraph_centralization() for how
+ *     the normalization is done.
+ * \return Error code.
+ *
+ * \sa \ref igraph_centralization(), \ref igraph_betweenness().
+ *
+ * Time complexity: the complexity of \ref igraph_betweenness() plus
+ * O(n), the number of vertices queried, for calculating the
+ * centralization score.
+ */
+
+int igraph_centralization_betweenness(const igraph_t *graph,
+                                      igraph_vector_t *res,
+                                      igraph_bool_t directed,
+                                      igraph_bool_t nobigint,
+                                      igraph_real_t *centralization,
+                                      igraph_real_t *theoretical_max,
+                                      igraph_bool_t normalized) {
+
+    igraph_vector_t myscores;
+    igraph_vector_t *scores = res;
+    igraph_real_t *tmax = theoretical_max, mytmax;
+
+    if (!tmax) {
+        tmax = &mytmax;
+    }
+
+    if (!res) {
+        scores = &myscores;
+        IGRAPH_VECTOR_INIT_FINALLY(scores, 0);
+    }
+
+    IGRAPH_CHECK(igraph_betweenness(graph, scores, igraph_vss_all(), directed,
+                                    /*weights=*/ 0, nobigint));
+
+    IGRAPH_CHECK(igraph_centralization_betweenness_tmax(graph, 0, directed,
+                 tmax));
+
+    *centralization = igraph_centralization(scores, *tmax, normalized);
+
+    if (!res) {
+        igraph_vector_destroy(scores);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_centralization_betweenness_tmax
+ * Theoretical maximum for graph centralization based on betweenness
+ *
+ * This function returns the theoretical maximum graph centrality
+ * based on vertex betweenness.
+ *
+ * </para><para>
+ * There are two ways to call this function, the first is to supply a
+ * graph as the <code>graph</code> argument, and then the number of
+ * vertices is taken from this object, and its directedness is
+ * considered as well. The <code>nodes</code> argument is ignored in
+ * this case. The <code>directed</code> argument is also ignored if the
+ * supplied graph is undirected.
+ *
+ * </para><para>
+ * The other way is to supply a null pointer as the <code>graph</code>
+ * argument. In this case the <code>nodes</code> and <code>directed</code>
+ * arguments are considered.
+ *
+ * </para><para>
+ * The most centralized structure is the star.
+ * \param graph A graph object or a null pointer, see the description
+ *     above.
+ * \param nodes The number of nodes. This is ignored if the
+ *     <code>graph</code> argument is not a null pointer.
+ * \param directed Boolean scalar, whether to use directed paths in
+ *     the betweenness calculation. This argument is ignored if
+ *     <code>graph</code> is not a null pointer and it is undirected.
+ * \param res Pointer to a real variable, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ *
+ * \sa \ref igraph_centralization_betweenness() and \ref
+ * igraph_centralization().
+ */
+
+int igraph_centralization_betweenness_tmax(const igraph_t *graph,
+        igraph_integer_t nodes,
+        igraph_bool_t directed,
+        igraph_real_t *res) {
+    igraph_real_t real_nodes;
+
+    if (graph) {
+        directed = directed && igraph_is_directed(graph);
+        nodes = igraph_vcount(graph);
+    }
+
+    real_nodes = nodes;    /* implicit cast to igraph_real_t */
+
+    if (directed) {
+        *res = (real_nodes - 1) * (real_nodes - 1) * (real_nodes - 2);
+    } else {
+        *res = (real_nodes - 1) * (real_nodes - 1) * (real_nodes - 2) / 2.0;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_centralization_closeness
+ * Calculate vertex closeness and graph centralization
+ *
+ * This function calculates the closeness centrality of the vertices
+ * by passing its arguments to \ref igraph_closeness(); and it
+ * calculates the graph level centralization index based on the
+ * results by calling \ref igraph_centralization().
+ * \param graph The input graph.
+ * \param res A vector if you need the node-level closeness scores, or a
+ *     null pointer otherwise.
+ * \param mode Constant the specifies the type of closeness for directed
+ *     graphs. Possible values: \c IGRAPH_IN, \c IGRAPH_OUT and \c
+ *     IGRAPH_ALL. This argument is ignored for undirected graphs. See
+ *     \ref igraph_closeness() argument with the same name for more.
+ * \param centralization Pointer to a real number, the centralization
+ *     score is placed here.
+ * \param theoretical_max Pointer to real number or a null pointer. If
+ *     not a null pointer, then the theoretical maximum graph
+ *     centrality score for a graph with the same number vertices is
+ *     stored here.
+ * \param normalized Boolean, whether to calculate a normalized
+ *     centralization score. See \ref igraph_centralization() for how
+ *     the normalization is done.
+ * \return Error code.
+ *
+ * \sa \ref igraph_centralization(), \ref igraph_closeness().
+ *
+ * Time complexity: the complexity of \ref igraph_closeness() plus
+ * O(n), the number of vertices queried, for calculating the
+ * centralization score.
+ */
+
+int igraph_centralization_closeness(const igraph_t *graph,
+                                    igraph_vector_t *res,
+                                    igraph_neimode_t mode,
+                                    igraph_real_t *centralization,
+                                    igraph_real_t *theoretical_max,
+                                    igraph_bool_t normalized) {
+
+    igraph_vector_t myscores;
+    igraph_vector_t *scores = res;
+    igraph_real_t *tmax = theoretical_max, mytmax;
+
+    if (!tmax) {
+        tmax = &mytmax;
+    }
+
+    if (!res) {
+        scores = &myscores;
+        IGRAPH_VECTOR_INIT_FINALLY(scores, 0);
+    }
+
+    IGRAPH_CHECK(igraph_closeness(graph, scores, igraph_vss_all(), mode,
+                                  /*weights=*/ 0, /*normalize=*/ 1));
+
+    IGRAPH_CHECK(igraph_centralization_closeness_tmax(graph, 0, mode,
+                 tmax));
+
+    *centralization = igraph_centralization(scores, *tmax, normalized);
+
+    if (!res) {
+        igraph_vector_destroy(scores);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_centralization_closeness_tmax
+ * Theoretical maximum for graph centralization based on closeness
+ *
+ * This function returns the theoretical maximum graph centrality
+ * based on vertex closeness.
+ *
+ * </para><para>
+ * There are two ways to call this function, the first is to supply a
+ * graph as the <code>graph</code> argument, and then the number of
+ * vertices is taken from this object, and its directedness is
+ * considered as well. The <code>nodes</code> argument is ignored in
+ * this case. The <code>mode</code> argument is also ignored if the
+ * supplied graph is undirected.
+ *
+ * </para><para>
+ * The other way is to supply a null pointer as the <code>graph</code>
+ * argument. In this case the <code>nodes</code> and <code>mode</code>
+ * arguments are considered.
+ *
+ * </para><para>
+ * The most centralized structure is the star.
+ * \param graph A graph object or a null pointer, see the description
+ *     above.
+ * \param nodes The number of nodes. This is ignored if the
+ *     <code>graph</code> argument is not a null pointer.
+ * \param mode Constant, specifies what kinf of distances to consider
+ *     to calculate closeness. See the <code>mode</code> argument of
+ *     \ref igraph_closeness() for details. This argument is ignored
+ *     if <code>graph</code> is not a null pointer and it is
+ *     undirected.
+ * \param res Pointer to a real variable, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ *
+ * \sa \ref igraph_centralization_closeness() and \ref
+ * igraph_centralization().
+ */
+
+int igraph_centralization_closeness_tmax(const igraph_t *graph,
+        igraph_integer_t nodes,
+        igraph_neimode_t mode,
+        igraph_real_t *res) {
+    igraph_real_t real_nodes;
+
+    if (graph) {
+        nodes = igraph_vcount(graph);
+        if (!igraph_is_directed(graph)) {
+            mode = IGRAPH_ALL;
+        }
+    }
+
+    real_nodes = nodes;    /* implicit cast to igraph_real_t */
+
+    if (mode != IGRAPH_ALL) {
+        *res = (real_nodes - 1) * (1.0 - 1.0 / real_nodes);
+    } else {
+        *res = (real_nodes - 1) * (real_nodes - 2) / (2.0 * real_nodes - 3);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_centralization_eigenvector_centrality
+ * Calculate eigenvector centrality scores and graph centralization
+ *
+ * This function calculates the eigenvector centrality of the vertices
+ * by passing its arguments to \ref igraph_eigenvector_centrality);
+ * and it calculates the graph level centralization index based on the
+ * results by calling \ref igraph_centralization().
+ * \param graph The input graph.
+ * \param vector A vector if you need the node-level eigenvector
+ *      centrality scores, or a null pointer otherwise.
+ * \param value If not a null pointer, then the leading eigenvalue is
+ *      stored here.
+ * \param scale If not zero then the result will be scaled, such that
+ *     the absolute value of the maximum centrality is one.
+ * \param options Options to ARPACK. See \ref igraph_arpack_options_t
+ *    for details. Note that the function overwrites the
+ *    <code>n</code> (number of vertices) parameter and
+ *    it always starts the calculation from a non-random vector
+ *    calculated based on the degree of the vertices.
+ * \param centralization Pointer to a real number, the centralization
+ *     score is placed here.
+ * \param theoretical_max Pointer to real number or a null pointer. If
+ *     not a null pointer, then the theoretical maximum graph
+ *     centrality score for a graph with the same number vertices is
+ *     stored here.
+ * \param normalized Boolean, whether to calculate a normalized
+ *     centralization score. See \ref igraph_centralization() for how
+ *     the normalization is done.
+ * \return Error code.
+ *
+ * \sa \ref igraph_centralization(), \ref igraph_eigenvector_centrality().
+ *
+ * Time complexity: the complexity of \ref
+ * igraph_eigenvector_centrality() plus O(|V|), the number of vertices
+ * for the calculating the centralization.
+ */
+
+int igraph_centralization_eigenvector_centrality(
+    const igraph_t *graph,
+    igraph_vector_t *vector,
+    igraph_real_t *value,
+    igraph_bool_t directed,
+    igraph_bool_t scale,
+    igraph_arpack_options_t *options,
+    igraph_real_t *centralization,
+    igraph_real_t *theoretical_max,
+    igraph_bool_t normalized) {
+
+    igraph_vector_t myscores;
+    igraph_vector_t *scores = vector;
+    igraph_real_t realvalue, *myvalue = value;
+    igraph_real_t *tmax = theoretical_max, mytmax;
+
+    if (!tmax) {
+        tmax = &mytmax;
+    }
+
+    if (!vector) {
+        scores = &myscores;
+        IGRAPH_VECTOR_INIT_FINALLY(scores, 0);
+    }
+    if (!value) {
+        myvalue = &realvalue;
+    }
+
+    IGRAPH_CHECK(igraph_eigenvector_centrality(graph, scores, myvalue, directed,
+                 scale, /*weights=*/ 0,
+                 options));
+
+    IGRAPH_CHECK(igraph_centralization_eigenvector_centrality_tmax(
+                     graph, 0, directed,
+                     scale,
+                     tmax));
+
+    *centralization = igraph_centralization(scores, *tmax, normalized);
+
+    if (!vector) {
+        igraph_vector_destroy(scores);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_centralization_eigenvector_centrality_tmax
+ * Theoretical maximum centralization for eigenvector centrality
+ *
+ * This function returns the theoretical maximum graph centrality
+ * based on vertex eigenvector centrality.
+ *
+ * </para><para>
+ * There are two ways to call this function, the first is to supply a
+ * graph as the <code>graph</code> argument, and then the number of
+ * vertices is taken from this object, and its directedness is
+ * considered as well. The <code>nodes</code> argument is ignored in
+ * this case. The <code>directed</code> argument is also ignored if the
+ * supplied graph is undirected.
+ *
+ * </para><para>
+ * The other way is to supply a null pointer as the <code>graph</code>
+ * argument. In this case the <code>nodes</code> and <code>directed</code>
+ * arguments are considered.
+ *
+ * </para><para>
+ * The most centralized directed structure is the in-star. The most
+ * centralized undirected structure is the graph with a single edge.
+ * \param graph A graph object or a null pointer, see the description
+ *     above.
+ * \param nodes The number of nodes. This is ignored if the
+ *     <code>graph</code> argument is not a null pointer.
+ * \param directed Boolean scalar, whether to consider edge
+ *     directions. This argument is ignored if
+ *     <code>graph</code> is not a null pointer and it is undirected.
+ * \param scale Whether to rescale the node-level centrality scores to
+ *     have a maximum of one.
+ * \param res Pointer to a real variable, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ *
+ * \sa \ref igraph_centralization_closeness() and \ref
+ * igraph_centralization().
+ */
+
+int igraph_centralization_eigenvector_centrality_tmax(
+    const igraph_t *graph,
+    igraph_integer_t nodes,
+    igraph_bool_t directed,
+    igraph_bool_t scale,
+    igraph_real_t *res) {
+
+    if (graph) {
+        nodes = igraph_vcount(graph);
+        directed = directed && igraph_is_directed(graph);
+    }
+
+    if (directed) {
+        *res = nodes - 1;
+    } else {
+        if (scale) {
+            *res = nodes - 2;
+        } else {
+            *res = (nodes - 2.0) / M_SQRT2;
+        }
+    }
+
+    return 0;
+}
diff --git a/igraph/src/cliquer.c b/igraph/src/cliquer.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cliquer.c
@@ -0,0 +1,1778 @@
+
+/*
+ * This file contains the clique searching routines.
+ *
+ * Copyright (C) 2002 Sampo Niskanen, Patric Östergård.
+ * Licensed under the GNU GPL, read the file LICENSE for details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+/*
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/times.h>
+*/
+
+#include "cliquer.h"
+
+#include "config.h"
+
+#ifdef USING_R
+#include <R.h>
+#endif
+
+/* Default cliquer options */
+IGRAPH_THREAD_LOCAL clique_options clique_default_options = {
+    reorder_by_default, NULL, /*clique_print_time*/ NULL, NULL, NULL, NULL, NULL, 0
+};
+
+
+/* Calculate d/q, rounding result upwards/downwards. */
+#define DIV_UP(d,q) (((d)+(q)-1)/(q))
+#define DIV_DOWN(d,q) ((int)((d)/(q)))
+
+
+/* Global variables used: */
+/* These must be saved and restored in re-entrance. */
+static IGRAPH_THREAD_LOCAL int *clique_size;      /* c[i] == max. clique size in {0,1,...,i-1} */
+static IGRAPH_THREAD_LOCAL set_t current_clique;  /* Current clique being searched. */
+static IGRAPH_THREAD_LOCAL set_t best_clique;     /* Largest/heaviest clique found so far. */
+/*static struct tms cputimer;*/      /* Timer for opts->time_function() */
+/*static struct timeval realtimer;*/ /* Timer for opts->time_function() */
+static IGRAPH_THREAD_LOCAL int clique_list_count=0;  /* No. of cliques in opts->clique_list[] */
+static IGRAPH_THREAD_LOCAL int weight_multiplier=1;  /* Weights multiplied by this when passing
+				  * to time_function(). */
+
+/* List cache (contains memory blocks of size g->n * sizeof(int)) */
+static IGRAPH_THREAD_LOCAL int **temp_list=NULL;
+static IGRAPH_THREAD_LOCAL int temp_count=0;
+
+
+/*
+ * Macros for re-entrance.  ENTRANCE_SAVE() must be called immediately
+ * after variable definitions, ENTRANCE_RESTORE() restores global
+ * variables to original values.  entrance_level should be increased
+ * and decreased accordingly.
+ */
+static IGRAPH_THREAD_LOCAL int entrance_level=0;  /* How many levels for entrance have occurred? */
+
+#define ENTRANCE_SAVE() \
+int *old_clique_size = clique_size;                     \
+set_t old_current_clique = current_clique;              \
+set_t old_best_clique = best_clique;                    \
+int old_clique_list_count = clique_list_count;          \
+int old_weight_multiplier = weight_multiplier;          \
+int **old_temp_list = temp_list;                        \
+int old_temp_count = temp_count;                        \
+/*struct tms old_cputimer;                                \
+struct timeval old_realtimer;                           \
+memcpy(&old_cputimer,&cputimer,sizeof(struct tms));       \
+memcpy(&old_realtimer,&realtimer,sizeof(struct timeval));*/
+
+#define ENTRANCE_RESTORE() \
+clique_size = old_clique_size;                          \
+current_clique = old_current_clique;                    \
+best_clique = old_best_clique;                          \
+clique_list_count = old_clique_list_count;              \
+weight_multiplier = old_weight_multiplier;              \
+temp_list = old_temp_list;                              \
+temp_count = old_temp_count;                            \
+/*memcpy(&cputimer,&old_cputimer,sizeof(struct tms));       \
+memcpy(&realtimer,&old_realtimer,sizeof(struct timeval));*/
+
+
+/* Number of clock ticks per second (as returned by sysconf(_SC_CLK_TCK)) */
+/*static int clocks_per_sec=0;*/
+
+
+
+
+/* Recursion and helper functions */
+static boolean sub_unweighted_single(int *table, int size, int min_size,
+				     graph_t *g);
+static int sub_unweighted_all(int *table, int size, int min_size, int max_size,
+			      boolean maximal, graph_t *g,
+			      clique_options *opts);
+static int sub_weighted_all(int *table, int size, int weight,
+			    int current_weight, int prune_low, int prune_high,
+			    int min_weight, int max_weight, boolean maximal,
+			    graph_t *g, clique_options *opts);
+
+
+static boolean store_clique(set_t clique, graph_t *g, clique_options *opts);
+static boolean is_maximal(set_t clique, graph_t *g);
+static boolean false_function(set_t clique,graph_t *g,clique_options *opts);
+
+
+
+
+
+/*****  Unweighted searches  *****/
+/*
+ * Unweighted searches are done separately from weighted searches because
+ * some effective pruning methods can be used when the vertex weights
+ * are all 1.  Single and all clique finding routines are separated,
+ * because the single clique finding routine can store the found clique
+ * while it is returning from the recursion, thus requiring no implicit
+ * storing of the current clique.  When searching for all cliques the
+ * current clique must be stored.
+ */
+
+
+/*
+ * unweighted_clique_search_single()
+ *
+ * Searches for a single clique of size min_size.  Stores maximum clique
+ * sizes into clique_size[].
+ *
+ *   table    - the order of the vertices in g to use
+ *   min_size - minimum size of clique to search for.  If min_size==0,
+ *              searches for a maximum clique.
+ *   g        - the graph
+ *   opts     - time printing options
+ *
+ * opts->time_function is called after each base-level recursion, if
+ * non-NULL.
+ *
+ * Returns the size of the clique found, or 0 if min_size>0 and a clique
+ * of that size was not found (or if time_function aborted the search).
+ * The largest clique found is stored in current_clique.
+ *
+ * Note: Does NOT use opts->user_function of opts->clique_list.
+ */
+static int unweighted_clique_search_single(int *table, int min_size,
+					   graph_t *g, clique_options *opts) {
+    /*
+    struct tms tms;
+	struct timeval timeval;
+    */
+	int i,j;
+	int v,w;
+	int *newtable;
+	int newsize;
+
+	v=table[0];
+	clique_size[v]=1;
+	set_empty(current_clique);
+	SET_ADD_ELEMENT(current_clique,v);
+	if (min_size==1)
+		return 1;
+
+	if (temp_count) {
+		temp_count--;
+		newtable=temp_list[temp_count];
+	} else {
+		newtable=malloc(g->n * sizeof(int));
+	}
+	for (i=1; i < g->n; i++) {
+		w=v;
+		v=table[i];
+
+		newsize=0;
+		for (j=0; j<i; j++) {
+			if (GRAPH_IS_EDGE(g, v, table[j])) {
+				newtable[newsize]=table[j];
+				newsize++;
+			}
+		}
+
+		if (sub_unweighted_single(newtable,newsize,clique_size[w],g)) {
+			SET_ADD_ELEMENT(current_clique,v);
+			clique_size[v]=clique_size[w]+1;
+		} else {
+			clique_size[v]=clique_size[w];
+		}
+
+        /*
+		if (opts && opts->time_function) {
+			gettimeofday(&timeval,NULL);
+			times(&tms);
+			if (!opts->time_function(entrance_level,
+						 i+1,g->n,clique_size[v] *
+						 weight_multiplier,
+						 (double)(tms.tms_utime-
+							  cputimer.tms_utime)/
+						 clocks_per_sec,
+						 timeval.tv_sec-
+						 realtimer.tv_sec+
+						 (double)(timeval.tv_usec-
+							  realtimer.tv_usec)/
+						 1000000,opts)) {
+				temp_list[temp_count++]=newtable;
+				return 0;
+			}
+		}
+        */
+
+		if (min_size) {
+			if (clique_size[v]>=min_size) {
+				temp_list[temp_count++]=newtable;
+				return clique_size[v];
+			}
+			if (clique_size[v]+g->n-i-1 < min_size) {
+				temp_list[temp_count++]=newtable;
+				return 0;
+			}
+		}
+	}
+
+	temp_list[temp_count++]=newtable;
+
+	if (min_size)
+		return 0;
+	return clique_size[v];
+}
+
+/*
+ * sub_unweighted_single()
+ *
+ * Recursion function for searching for a single clique of size min_size.
+ *
+ *    table    - subset of the vertices in graph
+ *    size     - size of table
+ *    min_size - size of clique to look for within the subgraph
+ *               (decreased with every recursion)
+ *    g        - the graph
+ *
+ * Returns TRUE if a clique of size min_size is found, FALSE otherwise.
+ * If a clique of size min_size is found, it is stored in current_clique.
+ *
+ * clique_size[] for all values in table must be defined and correct,
+ * otherwise inaccurate results may occur.
+ */
+static boolean sub_unweighted_single(int *table, int size, int min_size,
+				     graph_t *g) {
+	int i;
+	int v;
+	int *newtable;
+	int *p1, *p2;
+
+	/* Zero or one vertices needed anymore. */
+	if (min_size <= 1) {
+		if (size>0 && min_size==1) {
+			set_empty(current_clique);
+			SET_ADD_ELEMENT(current_clique,table[0]);
+			return TRUE;
+		}
+		if (min_size==0) {
+			set_empty(current_clique);
+			return TRUE;
+		}
+		return FALSE;
+	}
+	if (size < min_size)
+		return FALSE;
+
+	/* Dynamic memory allocation with cache */
+	if (temp_count) {
+		temp_count--;
+		newtable=temp_list[temp_count];
+	} else {
+		newtable=malloc(g->n * sizeof(int));
+	}
+
+	for (i = size-1; i >= 0; i--) {
+		v = table[i];
+
+		if (clique_size[v] < min_size)
+			break;
+		/* This is faster when compiling with gcc than placing
+		 * this in the for-loop condition. */
+		if (i+1 < min_size)
+			break;
+
+		/* Very ugly code, but works faster than "for (i=...)" */
+		p1 = newtable;
+		for (p2=table; p2 < table+i; p2++) {
+			int w = *p2;
+			if (GRAPH_IS_EDGE(g, v, w)) {
+				*p1 = w;
+				p1++;
+			}
+		}
+
+		/* Avoid unneccessary loops (next size == p1-newtable) */
+		if (p1-newtable < min_size-1)
+			continue;
+		/* Now p1-newtable >= min_size-1 >= 2-1 == 1, so we can use
+		 * p1-newtable-1 safely. */
+		if (clique_size[newtable[p1-newtable-1]] < min_size-1)
+			continue;
+
+		if (sub_unweighted_single(newtable,p1-newtable,
+					  min_size-1,g)) {
+			/* Clique found. */
+			SET_ADD_ELEMENT(current_clique,v);
+			temp_list[temp_count++]=newtable;
+			return TRUE;
+		}
+	}
+	temp_list[temp_count++]=newtable;
+	return FALSE;
+}
+
+
+/*
+ * unweighted_clique_search_all()
+ *
+ * Searches for all cliques with size at least min_size and at most
+ * max_size.  Stores the cliques as opts declares.
+ *
+ *   table    - the order of the vertices in g to search
+ *   start    - first index where the subgraph table[0], ..., table[start]
+ *              might include a requested kind of clique
+ *   min_size - minimum size of clique to search for.  min_size > 0 !
+ *   max_size - maximum size of clique to search for.  If no upper limit
+ *              is desired, use eg. INT_MAX
+ *   maximal  - requires cliques to be maximal
+ *   g        - the graph
+ *   opts     - time printing and clique storage options
+ *
+ * Cliques found are stored as defined by opts->user_function and
+ * opts->clique_list.  opts->time_function is called after each
+ * base-level recursion, if non-NULL.
+ *
+ * clique_size[] must be defined and correct for all values of
+ * table[0], ..., table[start-1].
+ *
+ * Returns the number of cliques stored (not neccessarily number of cliques
+ * in graph, if user/time_function aborts).
+ */
+static int unweighted_clique_search_all(int *table, int start,
+					int min_size, int max_size,
+					boolean maximal, graph_t *g,
+					clique_options *opts) {
+    /*
+	struct timeval timeval;
+	struct tms tms;
+    */
+	int i,j;
+	int v;
+	int *newtable;
+	int newsize;
+	int count=0;
+
+	if (temp_count) {
+		temp_count--;
+		newtable=temp_list[temp_count];
+	} else {
+		newtable=malloc(g->n * sizeof(int));
+	}
+
+	clique_list_count=0;
+	set_empty(current_clique);
+	for (i=start; i < g->n; i++) {
+		v=table[i];
+		clique_size[v]=min_size;  /* Do not prune here. */
+
+		newsize=0;
+		for (j=0; j<i; j++) {
+			if (GRAPH_IS_EDGE(g,v,table[j])) {
+				newtable[newsize]=table[j];
+				newsize++;
+			}
+		}
+
+		SET_ADD_ELEMENT(current_clique,v);
+		j=sub_unweighted_all(newtable,newsize,min_size-1,max_size-1,
+				     maximal,g,opts);
+		SET_DEL_ELEMENT(current_clique,v);
+		if (j<0) {
+			/* Abort. */
+			count-=j;
+			break;
+		}
+		count+=j;
+
+#if 0
+		if (opts->time_function) {
+			gettimeofday(&timeval,NULL);
+			times(&tms);
+			if (!opts->time_function(entrance_level,
+						 i+1,g->n,min_size *
+						 weight_multiplier,
+						 (double)(tms.tms_utime-
+							  cputimer.tms_utime)/
+						 clocks_per_sec,
+						 timeval.tv_sec-
+						 realtimer.tv_sec+
+						 (double)(timeval.tv_usec-
+							  realtimer.tv_usec)/
+						 1000000,opts)) {
+				/* Abort. */
+				break;
+			}
+		}
+#endif
+	}
+	temp_list[temp_count++]=newtable;
+	return count;
+}
+
+/*
+ * sub_unweighted_all()
+ *
+ * Recursion function for searching for all cliques of given size.
+ *
+ *   table    - subset of vertices of graph g
+ *   size     - size of table
+ *   min_size - minimum size of cliques to search for (decreased with
+ *              every recursion)
+ *   max_size - maximum size of cliques to search for (decreased with
+ *              every recursion).  If no upper limit is desired, use
+ *              eg. INT_MAX
+ *   maximal  - require cliques to be maximal (passed through)
+ *   g        - the graph
+ *   opts     - storage options
+ *
+ * All cliques of suitable size found are stored according to opts.
+ *
+ * Returns the number of cliques found.  If user_function returns FALSE,
+ * then the number of cliques is returned negative.
+ *
+ * Uses current_clique to store the currently-being-searched clique.
+ * clique_size[] for all values in table must be defined and correct,
+ * otherwise inaccurate results may occur.
+ */
+static int sub_unweighted_all(int *table, int size, int min_size, int max_size,
+			      boolean maximal, graph_t *g,
+			      clique_options *opts) {
+	int i;
+	int v;
+	int n;
+	int *newtable;
+	int *p1, *p2;
+	int count=0;     /* Amount of cliques found */
+
+	if (min_size <= 0) {
+		if ((!maximal) || is_maximal(current_clique,g)) {
+			/* We've found one.  Store it. */
+			count++;
+			if (!store_clique(current_clique,g,opts)) {
+				return -count;
+			}
+		}
+		if (max_size <= 0) {
+			/* If we add another element, size will be too big. */
+			return count;
+		}
+	}
+
+	if (size < min_size) {
+		return count;
+	}
+
+	/* Dynamic memory allocation with cache */
+	if (temp_count) {
+		temp_count--;
+		newtable=temp_list[temp_count];
+	} else {
+		newtable=malloc(g->n * sizeof(int));
+	}
+
+	for (i=size-1; i>=0; i--) {
+		v = table[i];
+		if (clique_size[v] < min_size) {
+			break;
+		}
+		if (i+1 < min_size) {
+			break;
+		}
+
+		/* Very ugly code, but works faster than "for (i=...)" */
+		p1 = newtable;
+		for (p2=table; p2 < table+i; p2++) {
+			int w = *p2;
+			if (GRAPH_IS_EDGE(g, v, w)) {
+				*p1 = w;
+				p1++;
+			}
+		}
+
+		/* Avoid unneccessary loops (next size == p1-newtable) */
+		if (p1-newtable < min_size-1) {
+			continue;
+		}
+
+		SET_ADD_ELEMENT(current_clique,v);
+		n=sub_unweighted_all(newtable,p1-newtable,
+				     min_size-1,max_size-1,maximal,g,opts);
+		SET_DEL_ELEMENT(current_clique,v);
+		if (n < 0) {
+			/* Abort. */
+			count -= n;
+			count = -count;
+			break;
+		}
+		count+=n;
+	}
+	temp_list[temp_count++]=newtable;
+	return count;
+}
+
+
+
+
+/***** Weighted clique searches *****/
+/*
+ * Weighted clique searches can use the same recursive routine, because
+ * in both cases (single/all) they have to search through all potential
+ * permutations searching for heavier cliques.
+ */
+
+
+/*
+ * weighted_clique_search_single()
+ *
+ * Searches for a single clique of weight at least min_weight, and at
+ * most max_weight.  Stores maximum clique sizes into clique_size[]
+ * (or min_weight-1, whichever is smaller).
+ *
+ *   table      - the order of the vertices in g to use
+ *   min_weight - minimum weight of clique to search for.  If min_weight==0,
+ *                then searches for a maximum weight clique
+ *   max_weight - maximum weight of clique to search for.  If no upper limit
+ *                is desired, use eg. INT_MAX
+ *   g          - the graph
+ *   opts       - time printing options
+ *
+ * opts->time_function is called after each base-level recursion, if
+ * non-NULL.
+ *
+ * Returns 0 if a clique of requested weight was not found (also if
+ * time_function requested an abort), otherwise returns >= 1.
+ * If min_weight==0 (search for maximum-weight clique), then the return
+ * value is the weight of the clique found.  The found clique is stored
+ * in best_clique.
+ *
+ * Note: Does NOT use opts->user_function of opts->clique_list.
+ */
+static int weighted_clique_search_single(int *table, int min_weight,
+					 int max_weight, graph_t *g,
+					 clique_options *opts) {
+    /*
+	struct timeval timeval;
+	struct tms tms;
+    */
+	int i,j;
+	int v;
+	int *newtable;
+	int newsize;
+	int newweight;
+	int search_weight;
+	int min_w;
+	clique_options localopts;
+
+	if (min_weight==0)
+		min_w=INT_MAX;
+	else
+		min_w=min_weight;
+
+
+	if (min_weight==1) {
+		/* min_weight==1 may cause trouble in the routine, and
+		 * it's trivial to check as it's own case.
+		 * We write nothing to clique_size[]. */
+		for (i=0; i < g->n; i++) {
+			if (g->weights[table[i]] <= max_weight) {
+				set_empty(best_clique);
+				SET_ADD_ELEMENT(best_clique,table[i]);
+				return g->weights[table[i]];
+			}
+		}
+		return 0;
+	}
+	
+	localopts.time_function=NULL;
+	localopts.reorder_function=NULL;
+	localopts.reorder_map=NULL;
+	localopts.user_function=false_function;
+	localopts.user_data=NULL;
+	localopts.clique_list=&best_clique;
+	localopts.clique_list_length=1;
+	clique_list_count=0;
+
+	v=table[0];
+	set_empty(best_clique);
+	SET_ADD_ELEMENT(best_clique,v);
+	search_weight=g->weights[v];
+	if (min_weight && (search_weight >= min_weight)) {
+		if (search_weight <= max_weight) {
+			/* Found suitable clique. */
+			return search_weight;
+		}
+		search_weight=min_weight-1;
+	}
+	clique_size[v]=search_weight;
+	set_empty(current_clique);
+
+	if (temp_count) {
+		temp_count--;
+		newtable=temp_list[temp_count];
+	} else {
+		newtable=malloc(g->n * sizeof(int));
+	}
+
+	for (i = 1; i < g->n; i++) {
+		v=table[i];
+
+		newsize=0;
+		newweight=0;
+		for (j=0; j<i; j++) {
+			if (GRAPH_IS_EDGE(g,v,table[j])) {
+				newweight += g->weights[table[j]];
+				newtable[newsize]=table[j];
+				newsize++;
+			}
+		}
+
+
+		SET_ADD_ELEMENT(current_clique,v);
+		search_weight=sub_weighted_all(newtable,newsize,newweight,
+					       g->weights[v],search_weight,
+					       clique_size[table[i-1]] +
+					       g->weights[v],
+					       min_w,max_weight,FALSE,
+					       g,&localopts);
+		SET_DEL_ELEMENT(current_clique,v);
+		if (search_weight < 0) {
+			break;
+		}
+
+		clique_size[v]=search_weight;
+
+        /*
+		if (opts->time_function) {
+			gettimeofday(&timeval,NULL);
+			times(&tms);
+			if (!opts->time_function(entrance_level,
+						 i+1,g->n,clique_size[v] *
+						 weight_multiplier,
+						 (double)(tms.tms_utime-
+							  cputimer.tms_utime)/
+						 clocks_per_sec,
+						 timeval.tv_sec-
+						 realtimer.tv_sec+
+						 (double)(timeval.tv_usec-
+							  realtimer.tv_usec)/
+						 1000000,opts)) {
+				set_free(current_clique);
+				current_clique=NULL;
+				break;
+			}
+		}
+        */
+	}
+	temp_list[temp_count++]=newtable;
+	if (min_weight && (search_weight > 0)) {
+		/* Requested clique has not been found. */
+		return 0;
+	}
+	return clique_size[table[i-1]];
+}
+
+
+/*
+ * weighted_clique_search_all()
+ *
+ * Searches for all cliques with weight at least min_weight and at most
+ * max_weight.  Stores the cliques as opts declares.
+ *
+ *   table      - the order of the vertices in g to search
+ *   start      - first index where the subgraph table[0], ..., table[start]
+ *                might include a requested kind of clique
+ *   min_weight - minimum weight of clique to search for.  min_weight > 0 !
+ *   max_weight - maximum weight of clique to search for.  If no upper limit
+ *                is desired, use eg. INT_MAX
+ *   maximal    - search only for maximal cliques
+ *   g          - the graph
+ *   opts       - time printing and clique storage options
+ *
+ * Cliques found are stored as defined by opts->user_function and
+ * opts->clique_list.  opts->time_function is called after each
+ * base-level recursion, if non-NULL.
+ *
+ * clique_size[] must be defined and correct for all values of
+ * table[0], ..., table[start-1].
+ *
+ * Returns the number of cliques stored (not neccessarily number of cliques
+ * in graph, if user/time_function aborts).
+ */
+static int weighted_clique_search_all(int *table, int start,
+				      int min_weight, int max_weight,
+				      boolean maximal, graph_t *g,
+				      clique_options *opts) {
+    /*
+	struct timeval timeval;
+	struct tms tms;
+    */
+	int i,j;
+	int v;
+	int *newtable;
+	int newsize;
+	int newweight;
+
+	if (temp_count) {
+		temp_count--;
+		newtable=temp_list[temp_count];
+	} else {
+		newtable=malloc(g->n * sizeof(int));
+	}
+
+	clique_list_count=0;
+	set_empty(current_clique);
+	for (i=start; i < g->n; i++) {
+		v=table[i];
+		clique_size[v]=min_weight;   /* Do not prune here. */
+
+		newsize=0;
+		newweight=0;
+		for (j=0; j<i; j++) {
+			if (GRAPH_IS_EDGE(g,v,table[j])) {
+				newtable[newsize]=table[j];
+				newweight+=g->weights[table[j]];
+				newsize++;
+			}
+		}
+
+		SET_ADD_ELEMENT(current_clique,v);
+		j=sub_weighted_all(newtable,newsize,newweight,
+				   g->weights[v],min_weight-1,INT_MAX,
+				   min_weight,max_weight,maximal,g,opts);
+		SET_DEL_ELEMENT(current_clique,v);
+
+		if (j<0) {
+			/* Abort. */
+			break;
+		}
+
+        /*
+		if (opts->time_function) {
+			gettimeofday(&timeval,NULL);
+			times(&tms);
+			if (!opts->time_function(entrance_level,
+						 i+1,g->n,clique_size[v] *
+						 weight_multiplier,
+						 (double)(tms.tms_utime-
+							  cputimer.tms_utime)/
+						 clocks_per_sec,
+						 timeval.tv_sec-
+						 realtimer.tv_sec+
+						 (double)(timeval.tv_usec-
+							  realtimer.tv_usec)/
+						 1000000,opts)) {
+				set_free(current_clique);
+				current_clique=NULL;
+				break;
+			}
+		}
+        */
+	}
+	temp_list[temp_count++]=newtable;
+
+	return clique_list_count;
+}
+
+/*
+ * sub_weighted_all()
+ *
+ * Recursion function for searching for all cliques of given weight.
+ *
+ *   table      - subset of vertices of graph g
+ *   size       - size of table
+ *   weight     - total weight of vertices in table
+ *   current_weight - weight of clique found so far
+ *   prune_low  - ignore all cliques with weight less or equal to this value
+ *                (often heaviest clique found so far)  (passed through)
+ *   prune_high - maximum weight possible for clique in this subgraph
+ *                (passed through)
+ *   min_size   - minimum weight of cliques to search for (passed through)
+ *                Must be greater than 0.
+ *   max_size   - maximum weight of cliques to search for (passed through)
+ *                If no upper limit is desired, use eg. INT_MAX
+ *   maximal    - search only for maximal cliques
+ *   g          - the graph
+ *   opts       - storage options
+ *
+ * All cliques of suitable weight found are stored according to opts.
+ *
+ * Returns weight of heaviest clique found (prune_low if a heavier clique
+ * hasn't been found);  if a clique with weight at least min_size is found
+ * then min_size-1 is returned.  If clique storage failed, -1 is returned.
+ *
+ * The largest clique found smaller than max_weight is stored in
+ * best_clique, if non-NULL.
+ *
+ * Uses current_clique to store the currently-being-searched clique.
+ * clique_size[] for all values in table must be defined and correct,
+ * otherwise inaccurate results may occur.
+ *
+ * To search for a single maximum clique, use min_weight==max_weight==INT_MAX,
+ * with best_clique non-NULL.  To search for a single given-weight clique,
+ * use opts->clique_list and opts->user_function=false_function.  When
+ * searching for all cliques, min_weight should be given the minimum weight
+ * desired.
+ */
+static int sub_weighted_all(int *table, int size, int weight,
+			    int current_weight, int prune_low, int prune_high,
+			    int min_weight, int max_weight, boolean maximal,
+			    graph_t *g, clique_options *opts) {
+	int i;
+	int v,w;
+	int *newtable;
+	int *p1, *p2;
+	int newweight;
+
+	if (current_weight >= min_weight) {
+		if ((current_weight <= max_weight) &&
+		    ((!maximal) || is_maximal(current_clique,g))) {
+			/* We've found one.  Store it. */
+			if (!store_clique(current_clique,g,opts)) {
+				return -1;
+			}
+		}
+		if (current_weight >= max_weight) {
+			/* Clique too heavy. */
+			return min_weight-1;
+		} 
+	}
+	if (size <= 0) {
+		/* current_weight < min_weight, prune_low < min_weight,
+		 * so return value is always < min_weight. */
+		if (current_weight>prune_low) {
+			if (best_clique) {
+				best_clique = set_copy(best_clique,current_clique);
+			}
+			if (current_weight < min_weight)
+				return current_weight;
+			else
+				return min_weight-1;
+		} else {
+			return prune_low;
+		}
+	}
+
+	/* Dynamic memory allocation with cache */
+	if (temp_count) {
+		temp_count--;
+		newtable=temp_list[temp_count];
+	} else {
+		newtable=malloc(g->n * sizeof(int));
+	}
+
+	for (i = size-1; i >= 0; i--) {
+		v = table[i];
+		if (current_weight+clique_size[v] <= prune_low) {
+			/* Dealing with subset without heavy enough clique. */
+			break;
+		}
+		if (current_weight+weight <= prune_low) {
+			/* Even if all elements are added, won't do. */
+			break;
+		}
+
+		/* Very ugly code, but works faster than "for (i=...)" */
+		p1 = newtable;
+		newweight = 0;
+		for (p2=table; p2 < table+i; p2++) {
+			w = *p2;
+			if (GRAPH_IS_EDGE(g, v, w)) {
+				*p1 = w;
+				newweight += g->weights[w];
+				p1++;
+			}
+		}
+
+		w=g->weights[v];
+		weight-=w;
+		/* Avoid a few unneccessary loops */
+		if (current_weight+w+newweight <= prune_low) {
+			continue;
+		}
+
+		SET_ADD_ELEMENT(current_clique,v);
+		prune_low=sub_weighted_all(newtable,p1-newtable,
+					   newweight,
+					   current_weight+w,
+					   prune_low,prune_high,
+					   min_weight,max_weight,maximal,
+					   g,opts);
+		SET_DEL_ELEMENT(current_clique,v);
+		if ((prune_low<0) || (prune_low>=prune_high)) {
+			/* Impossible to find larger clique. */
+			break;
+		}
+	}
+	temp_list[temp_count++]=newtable;
+	return prune_low;
+}
+
+
+
+
+/***** Helper functions *****/
+
+
+/*
+ * store_clique()
+ *
+ * Stores a clique according to given user options.
+ *
+ *   clique - the clique to store
+ *   opts   - storage options
+ *
+ * Returns FALSE if opts->user_function() returned FALSE; otherwise
+ * returns TRUE.
+ */
+static boolean store_clique(set_t clique, graph_t *g, clique_options *opts) {
+
+	clique_list_count++;
+
+	/* clique_list[] */
+	if (opts->clique_list) {
+		/*
+		 * This has been a major source of bugs:
+		 * Has clique_list_count been set to 0 before calling
+		 * the recursions? 
+		 */
+		if (clique_list_count <= 0) {
+#ifdef USING_R
+		        error("CLIQUER INTERNAL ERROR: ",
+			      "clique_list_count has negative value!");
+#else
+			fprintf(stderr,"CLIQUER INTERNAL ERROR: "
+				"clique_list_count has negative value!\n");
+			fprintf(stderr,"Please report as a bug.\n");
+			abort();
+#endif
+		}
+		if (clique_list_count <= opts->clique_list_length)
+			opts->clique_list[clique_list_count-1] =
+				set_copy(opts->clique_list[clique_list_count-1], clique);
+	}
+
+	/* user_function() */
+	if (opts->user_function) {
+		if (!opts->user_function(clique,g,opts)) {
+			/* User function requested abort. */
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+/*
+ * maximalize_clique()
+ *
+ * Adds greedily all possible vertices in g to set s to make it a maximal
+ * clique.
+ *
+ *   s - clique of vertices to make maximal
+ *   g - graph
+ *
+ * Note: Not very optimized (uses a simple O(n^2) routine), but is called
+ *       at maximum once per clique_xxx() call, so it shouldn't matter.
+ */
+static void maximalize_clique(set_t s,graph_t *g) {
+	int i,j;
+	boolean add;
+
+	for (i=0; i < g->n; i++) {
+		add=TRUE;
+		for (j=0; j < g->n; j++) {
+			if (SET_CONTAINS_FAST(s,j) && !GRAPH_IS_EDGE(g,i,j)) {
+				add=FALSE;
+				break;
+			}
+		}
+		if (add) {
+			SET_ADD_ELEMENT(s,i);
+		}
+	}
+	return;
+}
+
+
+/*
+ * is_maximal()
+ *
+ * Check whether a clique is maximal or not.
+ *
+ *   clique - set of vertices in clique
+ *   g      - graph
+ *
+ * Returns TRUE is clique is a maximal clique of g, otherwise FALSE.
+ */
+static boolean is_maximal(set_t clique, graph_t *g) {
+	int i,j;
+	int *table;
+	int len;
+	boolean addable;
+
+	if (temp_count) {
+		temp_count--;
+		table=temp_list[temp_count];
+	} else {
+		table=malloc(g->n * sizeof(int));
+	}
+
+	len=0;
+	for (i=0; i < g->n; i++)
+		if (SET_CONTAINS_FAST(clique,i))
+			table[len++]=i;
+
+	for (i=0; i < g->n; i++) {
+		addable=TRUE;
+		for (j=0; j<len; j++) {
+			if (!GRAPH_IS_EDGE(g,i,table[j])) {
+				addable=FALSE;
+				break;
+			}
+		}
+		if (addable) {
+			temp_list[temp_count++]=table;
+			return FALSE;
+		}
+	}
+	temp_list[temp_count++]=table;
+	return TRUE;
+}
+
+
+/*
+ * false_function()
+ *
+ * Returns FALSE.  Can be used as user_function.
+ */
+static boolean false_function(set_t clique,graph_t *g,clique_options *opts) {
+	return FALSE;
+}
+
+
+
+
+/***** API-functions *****/
+
+/*
+ * clique_unweighted_max_weight()
+ *
+ * Returns the size of the maximum (sized) clique in g (or 0 if search
+ * was aborted).
+ *
+ *   g    - the graph
+ *   opts - time printing options
+ *
+ * Note: As we don't have an algorithm faster than actually finding
+ *       a maximum clique, we use clique_unweighted_find_single().
+ *       This incurs only very small overhead.
+ */
+int clique_unweighted_max_weight(graph_t *g, clique_options *opts) {
+	set_t s;
+	int size;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(g!=NULL);
+
+	s=clique_unweighted_find_single(g,0,0,FALSE,opts);
+	if (s==NULL) {
+		/* Search was aborted. */
+		return 0;
+	}
+	size=set_size(s);
+	set_free(s);
+	return size;
+}
+
+
+/*
+ * clique_unweighted_find_single()
+ *
+ * Returns a clique with size at least min_size and at most max_size.
+ *
+ *   g        - the graph
+ *   min_size - minimum size of clique to search for.  If min_size==0,
+ *              searches for maximum clique.
+ *   max_size - maximum size of clique to search for.  If max_size==0, no
+ *              upper limit is used.  If min_size==0, this must also be 0.
+ *   maximal  - require returned clique to be maximal
+ *   opts     - time printing options
+ *
+ * Returns the set of vertices forming the clique, or NULL if a clique
+ * of requested size/maximality does not exist in the graph  (or if
+ * opts->time_function() requests abort).
+ *
+ * The returned clique is newly allocated and can be freed by set_free().
+ *
+ * Note: Does NOT use opts->user_function() or opts->clique_list[].
+ */
+set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size,
+				    boolean maximal, clique_options *opts) {
+	int i;
+	int *table;
+	set_t s;
+
+	ENTRANCE_SAVE();
+	entrance_level++;
+
+	if (opts==NULL)
+		opts=&clique_default_options;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(g!=NULL);
+	ASSERT(min_size>=0);
+	ASSERT(max_size>=0);
+	ASSERT((max_size==0) || (min_size <= max_size));
+	ASSERT(!((min_size==0) && (max_size>0)));
+	ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL));
+
+	if ((max_size>0) && (min_size>max_size)) {
+		/* state was not changed */
+		entrance_level--;
+		return NULL;
+	}
+
+    /*
+	if (clocks_per_sec==0)
+		clocks_per_sec=sysconf(_SC_CLK_TCK);
+	ASSERT(clocks_per_sec>0);
+    */
+
+	/* Dynamic allocation */
+	current_clique=set_new(g->n);
+	clique_size=malloc(g->n * sizeof(int));
+	/* table allocated later */
+	temp_list=malloc((g->n+2)*sizeof(int *));
+	temp_count=0;
+
+	/* "start clock" */
+    /*
+	gettimeofday(&realtimer,NULL);
+	times(&cputimer);
+    */
+
+	/* reorder */
+	if (opts->reorder_function) {
+		table=opts->reorder_function(g,FALSE);
+	} else if (opts->reorder_map) {
+		table=reorder_duplicate(opts->reorder_map,g->n);
+	} else {
+		table=reorder_ident(g->n);
+	}
+	ASSERT(reorder_is_bijection(table,g->n));
+
+
+	if (unweighted_clique_search_single(table,min_size,g,opts)==0) {
+		set_free(current_clique);
+		current_clique=NULL;
+		goto cleanreturn;
+	}
+	if (maximal && (min_size>0)) {
+		maximalize_clique(current_clique,g);
+
+		if ((max_size > 0) && (set_size(current_clique) > max_size)) {
+			clique_options localopts;
+
+			s = set_new(g->n);
+			localopts.time_function = opts->time_function;
+			localopts.output = opts->output;
+			localopts.user_function = false_function;
+			localopts.clique_list = &s;
+			localopts.clique_list_length = 1;
+
+			for (i=0; i < g->n-1; i++)
+				if (clique_size[table[i]]>=min_size)
+					break;
+			if (unweighted_clique_search_all(table,i,min_size,
+							 max_size,maximal,
+							 g,&localopts)) {
+				set_free(current_clique);
+				current_clique=s;
+			} else {
+				set_free(current_clique);
+				current_clique=NULL;
+			}
+		}
+	}
+	
+    cleanreturn:
+	s=current_clique;
+
+	/* Free resources */
+	for (i=0; i < temp_count; i++)
+		free(temp_list[i]);
+	free(temp_list);
+	free(table);
+	free(clique_size);
+
+	ENTRANCE_RESTORE();
+	entrance_level--;
+
+	return s;
+}
+
+
+/*
+ * clique_unweighted_find_all()
+ *
+ * Find all cliques with size at least min_size and at most max_size.
+ *
+ *   g        - the graph
+ *   min_size - minimum size of cliques to search for.  If min_size==0,
+ *              searches for maximum cliques.
+ *   max_size - maximum size of cliques to search for.  If max_size==0, no
+ *              upper limit is used.  If min_size==0, this must also be 0.
+ *   maximal  - require cliques to be maximal cliques
+ *   opts     - time printing and clique storage options
+ *
+ * Returns the number of cliques found.  This can be less than the number
+ * of cliques in the graph iff opts->time_function() or opts->user_function()
+ * returns FALSE (request abort).
+ *
+ * The cliques found are stored in opts->clique_list[] and
+ * opts->user_function() is called with them (if non-NULL).  The cliques
+ * stored in opts->clique_list[] are newly allocated, and can be freed
+ * by set_free().
+ */
+int clique_unweighted_find_all(graph_t *g, int min_size, int max_size,
+			       boolean maximal, clique_options *opts) {
+	int i;
+	int *table;
+	int count;
+
+	ENTRANCE_SAVE();
+	entrance_level++;
+
+	if (opts==NULL)
+		opts=&clique_default_options;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(g!=NULL);
+	ASSERT(min_size>=0);
+	ASSERT(max_size>=0);
+	ASSERT((max_size==0) || (min_size <= max_size));
+	ASSERT(!((min_size==0) && (max_size>0)));
+	ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL));
+
+	if ((max_size>0) && (min_size>max_size)) {
+		/* state was not changed */
+		entrance_level--;
+		return 0;
+	}
+
+    /*
+	if (clocks_per_sec==0)
+		clocks_per_sec=sysconf(_SC_CLK_TCK);
+	ASSERT(clocks_per_sec>0);
+    */
+
+	/* Dynamic allocation */
+	current_clique=set_new(g->n);
+	clique_size=malloc(g->n * sizeof(int));
+	/* table allocated later */
+	temp_list=malloc((g->n+2)*sizeof(int *));
+	temp_count=0;
+
+	clique_list_count=0;
+	memset(clique_size,0,g->n * sizeof(int));
+
+	/* "start clock" */
+    /*
+	gettimeofday(&realtimer,NULL);
+	times(&cputimer);
+    */
+
+	/* reorder */
+	if (opts->reorder_function) {
+		table=opts->reorder_function(g,FALSE);
+	} else if (opts->reorder_map) {
+		table=reorder_duplicate(opts->reorder_map,g->n);
+	} else {
+		table=reorder_ident(g->n);
+	}
+	ASSERT(reorder_is_bijection(table,g->n));
+
+
+	/* Search as normal until there is a chance to find a suitable
+	 * clique. */
+	if (unweighted_clique_search_single(table,min_size,g,opts)==0) {
+		count=0;
+		goto cleanreturn;
+	}
+
+	if (min_size==0 && max_size==0) {
+		min_size=max_size=clique_size[table[g->n-1]];
+		maximal=FALSE;  /* No need to test, since we're searching
+				 * for maximum cliques. */
+	}
+	if (max_size==0) {
+		max_size=INT_MAX;
+	}
+
+	for (i=0; i < g->n-1; i++)
+		if (clique_size[table[i]] >= min_size)
+			break;
+	count=unweighted_clique_search_all(table,i,min_size,max_size,
+					   maximal,g,opts);
+
+  cleanreturn:
+	/* Free resources */
+	for (i=0; i<temp_count; i++)
+		free(temp_list[i]);
+	free(temp_list);
+	free(table);
+	free(clique_size);
+	set_free(current_clique);
+
+	ENTRANCE_RESTORE();
+	entrance_level--;
+
+	return count;
+}
+
+
+
+
+/*
+ * clique_max_weight()
+ *
+ * Returns the weight of the maximum weight clique in the graph (or 0 if
+ * the search was aborted).
+ *
+ *   g    - the graph
+ *   opts - time printing options
+ *
+ * Note: As we don't have an algorithm faster than actually finding
+ *       a maximum weight clique, we use clique_find_single().
+ *       This incurs only very small overhead.
+ */
+int clique_max_weight(graph_t *g,clique_options *opts) {
+	set_t s;
+	int weight;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(g!=NULL);
+
+	s=clique_find_single(g,0,0,FALSE,opts);
+	if (s==NULL) {
+		/* Search was aborted. */
+		return 0;
+	}
+	weight=graph_subgraph_weight(g,s);
+	set_free(s);
+	return weight;
+}
+
+
+/*
+ * clique_find_single()
+ *
+ * Returns a clique with weight at least min_weight and at most max_weight.
+ *
+ *   g          - the graph
+ *   min_weight - minimum weight of clique to search for.  If min_weight==0,
+ *                searches for a maximum weight clique.
+ *   max_weight - maximum weight of clique to search for.  If max_weight==0,
+ *                no upper limit is used.  If min_weight==0, max_weight must
+ *                also be 0.
+ *   maximal    - require returned clique to be maximal
+ *   opts       - time printing options
+ *
+ * Returns the set of vertices forming the clique, or NULL if a clique
+ * of requested weight/maximality does not exist in the graph  (or if
+ * opts->time_function() requests abort).
+ *
+ * The returned clique is newly allocated and can be freed by set_free().
+ *
+ * Note: Does NOT use opts->user_function() or opts->clique_list[].
+ * Note: Automatically uses clique_unweighted_find_single if all vertex
+ *       weights are the same.
+ */
+set_t clique_find_single(graph_t *g,int min_weight,int max_weight,
+			 boolean maximal, clique_options *opts) {
+	int i;
+	int *table;
+	set_t s;
+
+	ENTRANCE_SAVE();
+	entrance_level++;
+
+	if (opts==NULL)
+		opts=&clique_default_options;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(g!=NULL);
+	ASSERT(min_weight>=0);
+	ASSERT(max_weight>=0);
+	ASSERT((max_weight==0) || (min_weight <= max_weight));
+	ASSERT(!((min_weight==0) && (max_weight>0)));
+	ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL));
+
+	if ((max_weight>0) && (min_weight>max_weight)) {
+		/* state was not changed */
+		entrance_level--;
+		return NULL;
+	}
+
+    /*
+	if (clocks_per_sec==0)
+		clocks_per_sec=sysconf(_SC_CLK_TCK);
+	ASSERT(clocks_per_sec>0);
+    */
+
+	/* Check whether we can use unweighted routines. */
+	if (!graph_weighted(g)) {
+		min_weight=DIV_UP(min_weight,g->weights[0]);
+		if (max_weight) {
+			max_weight=DIV_DOWN(max_weight,g->weights[0]);
+			if (max_weight < min_weight) {
+				/* state was not changed */
+				entrance_level--;
+				return NULL;
+			}
+		}
+
+		weight_multiplier = g->weights[0];
+		entrance_level--;
+		s=clique_unweighted_find_single(g,min_weight,max_weight,
+						maximal,opts);
+		ENTRANCE_RESTORE();
+		return s;
+	}
+
+	/* Dynamic allocation */
+	current_clique=set_new(g->n);
+	best_clique=set_new(g->n);
+	clique_size=malloc(g->n * sizeof(int));
+	memset(clique_size, 0, g->n * sizeof(int));
+	/* table allocated later */
+	temp_list=malloc((g->n+2)*sizeof(int *));
+	temp_count=0;
+
+	clique_list_count=0;
+
+	/* "start clock" */
+    /*
+	gettimeofday(&realtimer,NULL);
+	times(&cputimer);
+    */
+
+	/* reorder */
+	if (opts->reorder_function) {
+		table=opts->reorder_function(g,TRUE);
+	} else if (opts->reorder_map) {
+		table=reorder_duplicate(opts->reorder_map,g->n);
+	} else {
+		table=reorder_ident(g->n);
+	}
+	ASSERT(reorder_is_bijection(table,g->n));
+
+	if (max_weight==0)
+		max_weight=INT_MAX;
+
+	if (weighted_clique_search_single(table,min_weight,max_weight,
+					  g,opts)==0) {
+		/* Requested clique has not been found. */
+		set_free(best_clique);
+		best_clique=NULL;
+		goto cleanreturn;
+	}
+	if (maximal && (min_weight>0)) {
+		maximalize_clique(best_clique,g);
+		if (graph_subgraph_weight(g,best_clique) > max_weight) {
+			clique_options localopts;
+
+			localopts.time_function = opts->time_function;
+			localopts.output = opts->output;
+			localopts.user_function = false_function;
+			localopts.clique_list = &best_clique;
+			localopts.clique_list_length = 1;
+
+			for (i=0; i < g->n-1; i++)
+				if ((clique_size[table[i]] >= min_weight) ||
+				    (clique_size[table[i]] == 0))
+					break;
+			if (!weighted_clique_search_all(table,i,min_weight,
+							max_weight,maximal,
+							g,&localopts)) {
+				set_free(best_clique);
+				best_clique=NULL;
+			}
+		}
+	}
+
+ cleanreturn:
+	s=best_clique;
+
+	/* Free resources */
+	for (i=0; i < temp_count; i++)
+		free(temp_list[i]);
+	free(temp_list);
+	temp_list=NULL;
+	temp_count=0;
+	free(table);
+	set_free(current_clique);
+	current_clique=NULL;
+	free(clique_size);
+	clique_size=NULL;
+
+	ENTRANCE_RESTORE();
+	entrance_level--;
+
+	return s;
+}
+
+
+
+
+
+/*
+ * clique_find_all()
+ *
+ * Find all cliques with weight at least min_weight and at most max_weight.
+ *
+ *   g          - the graph
+ *   min_weight - minimum weight of cliques to search for.  If min_weight==0,
+ *                searches for maximum weight cliques.
+ *   max_weight - maximum weight of cliques to search for.  If max_weight==0,
+ *                no upper limit is used.  If min_weight==0, max_weight must
+ *                also be 0.
+ *   maximal    - require cliques to be maximal cliques
+ *   opts       - time printing and clique storage options
+ *
+ * Returns the number of cliques found.  This can be less than the number
+ * of cliques in the graph iff opts->time_function() or opts->user_function()
+ * returns FALSE (request abort).
+ *
+ * The cliques found are stored in opts->clique_list[] and
+ * opts->user_function() is called with them (if non-NULL).  The cliques
+ * stored in opts->clique_list[] are newly allocated, and can be freed
+ * by set_free().
+ *
+ * Note: Automatically uses clique_unweighted_find_all if all vertex
+ *       weights are the same.
+ */
+int clique_find_all(graph_t *g, int min_weight, int max_weight,
+		    boolean maximal, clique_options *opts) {
+	int i,n;
+	int *table;
+
+	ENTRANCE_SAVE();
+	entrance_level++;
+
+	if (opts==NULL)
+		opts=&clique_default_options;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(g!=NULL);
+	ASSERT(min_weight>=0);
+	ASSERT(max_weight>=0);
+	ASSERT((max_weight==0) || (min_weight <= max_weight));
+	ASSERT(!((min_weight==0) && (max_weight>0)));
+	ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL));
+
+	if ((max_weight>0) && (min_weight>max_weight)) {
+		/* state was not changed */
+		entrance_level--;
+		return 0;
+	}
+
+    /*
+	if (clocks_per_sec==0)
+		clocks_per_sec=sysconf(_SC_CLK_TCK);
+	ASSERT(clocks_per_sec>0);
+    */
+
+	if (!graph_weighted(g)) {
+		min_weight=DIV_UP(min_weight,g->weights[0]);
+		if (max_weight) {
+			max_weight=DIV_DOWN(max_weight,g->weights[0]);
+			if (max_weight < min_weight) {
+				/* state was not changed */
+				entrance_level--;
+				return 0;
+			}
+		}
+		
+		weight_multiplier = g->weights[0];
+		entrance_level--;
+		i=clique_unweighted_find_all(g,min_weight,max_weight,maximal,
+					     opts);
+		ENTRANCE_RESTORE();
+		return i;
+	}
+
+	/* Dynamic allocation */
+	current_clique=set_new(g->n);
+	best_clique=set_new(g->n);
+	clique_size=malloc(g->n * sizeof(int));
+	memset(clique_size, 0, g->n * sizeof(int));
+	/* table allocated later */
+	temp_list=malloc((g->n+2)*sizeof(int *));
+	temp_count=0;
+
+	/* "start clock" */
+    /*
+	gettimeofday(&realtimer,NULL);
+	times(&cputimer);
+    */
+
+	/* reorder */
+	if (opts->reorder_function) {
+		table=opts->reorder_function(g,TRUE);
+	} else if (opts->reorder_map) {
+		table=reorder_duplicate(opts->reorder_map,g->n);
+	} else {
+		table=reorder_ident(g->n);
+	}
+	ASSERT(reorder_is_bijection(table,g->n));
+
+	/* First phase */
+	n=weighted_clique_search_single(table,min_weight,INT_MAX,g,opts);
+	if (n==0) {
+		/* Requested clique has not been found. */
+		goto cleanreturn;
+	}
+
+	if (min_weight==0) {
+		min_weight=n;
+		max_weight=n;
+		maximal=FALSE;  /* They're maximum cliques already. */
+	}
+	if (max_weight==0)
+		max_weight=INT_MAX;
+
+	for (i=0; i < g->n; i++)
+		if ((clique_size[table[i]] >= min_weight) ||
+		    (clique_size[table[i]] == 0))
+			break;
+
+	/* Second phase */
+	n=weighted_clique_search_all(table,i,min_weight,max_weight,maximal,
+				     g,opts);
+
+      cleanreturn:
+	/* Free resources */
+	for (i=0; i < temp_count; i++)
+		free(temp_list[i]);
+	free(temp_list);
+	free(table);
+	set_free(current_clique);
+	set_free(best_clique);
+	free(clique_size);
+
+	ENTRANCE_RESTORE();
+	entrance_level--;
+
+	return n;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#if 0
+/*
+ * clique_print_time()
+ *
+ * Reports current running information every 0.1 seconds or when values
+ * change.
+ *
+ *   level    - re-entrance level
+ *   i        - current recursion level
+ *   n        - maximum recursion level
+ *   max      - weight of heaviest clique found
+ *   cputime  - CPU time used in algorithm so far
+ *   realtime - real time used in algorithm so far
+ *   opts     - prints information to (FILE *)opts->output (or stdout if NULL)
+ *
+ * Returns always TRUE  (ie. never requests abort).
+ */
+boolean clique_print_time(int level, int i, int n, int max,
+			  double cputime, double realtime,
+			  clique_options *opts) {
+	static float prev_time=100;
+	static int prev_i=100;
+	static int prev_max=100;
+	static int prev_level=0;
+	FILE *fp=opts->output;
+	int j;
+
+	if (fp==NULL)
+		fp=stdout;
+
+	if (ABS(prev_time-realtime)>0.1 || i==n || i<prev_i || max!=prev_max ||
+	    level!=prev_level) {
+		for (j=1; j<level; j++)
+			fprintf(fp,"  ");
+		if (realtime-prev_time < 0.01 || i<=prev_i)
+			fprintf(fp,"%3d/%d (max %2d)  %2.2f s  "
+				"(0.00 s/round)\n",i,n,max,
+				realtime);
+		else
+			fprintf(fp,"%3d/%d (max %2d)  %2.2f s  "
+				"(%2.2f s/round)\n",
+				i,n,max,realtime,
+				(realtime-prev_time)/(i-prev_i));
+		prev_time=realtime;
+		prev_i=i;
+		prev_max=max;
+		prev_level=level;
+	}
+	return TRUE;
+}
+
+/*
+ * clique_print_time_always()
+ *
+ * Reports current running information.
+ *
+ *   level    - re-entrance level
+ *   i        - current recursion level
+ *   n        - maximum recursion level
+ *   max      - largest clique found
+ *   cputime  - CPU time used in algorithm so far
+ *   realtime - real time used in algorithm so far
+ *   opts     - prints information to (FILE *)opts->output (or stdout if NULL)
+ *
+ * Returns always TRUE  (ie. never requests abort).
+ */
+boolean clique_print_time_always(int level, int i, int n, int max,
+				 double cputime, double realtime,
+				 clique_options *opts) {
+	static float prev_time=100;
+	static int prev_i=100;
+	FILE *fp=opts->output;
+	int j;
+
+	if (fp==NULL)
+		fp=stdout;
+
+	for (j=1; j<level; j++)
+		fprintf(fp,"  ");
+
+	if (realtime-prev_time < 0.01 || i<=prev_i)
+		fprintf(fp,"%3d/%d (max %2d)  %2.2f s  (0.00 s/round)\n",
+			i,n,max,realtime);
+	else
+		fprintf(fp,"%3d/%d (max %2d)  %2.2f s  (%2.2f s/round)\n",
+			i,n,max,realtime,(realtime-prev_time)/(i-prev_i));
+	prev_time=realtime;
+	prev_i=i;
+
+	return TRUE;
+}
+#endif
diff --git a/igraph/src/cliquer_graph.c b/igraph/src/cliquer_graph.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cliquer_graph.c
@@ -0,0 +1,768 @@
+
+/*
+ * This file contains the graph handling routines.
+ *
+ * Copyright (C) 2002 Sampo Niskanen, Patric Östergård.
+ * Licensed under the GNU GPL, read the file LICENSE for details.
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "graph.h"
+
+#ifdef USING_R
+#include <R.h>
+#endif
+
+/*
+static graph_t *graph_read_dimacs_binary(FILE *fp,char *firstline);
+static graph_t *graph_read_dimacs_ascii(FILE *fp,char *firstline);
+*/
+
+
+/*
+ * graph_new()
+ *
+ * Returns a newly allocated graph with n vertices all with weight 1,
+ * and no edges.
+ */
+graph_t *graph_new(int n) {
+	graph_t *g;
+	int i;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(n>0);
+
+	g=malloc(sizeof(graph_t));
+	g->n=n;
+	g->edges=malloc(g->n * sizeof(set_t));
+	g->weights=malloc(g->n * sizeof(int));
+	for (i=0; i < g->n; i++) {
+		g->edges[i]=set_new(n);
+		g->weights[i]=1;
+	}
+	return g;
+}
+
+/*
+ * graph_free()
+ *
+ * Frees the memory associated with the graph g.
+ */
+void graph_free(graph_t *g) {
+	int i;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(g!=NULL);
+	ASSERT(g->n > 0);
+
+	for (i=0; i < g->n; i++) {
+		set_free(g->edges[i]);
+	}
+	free(g->weights);
+	free(g->edges);
+	free(g);
+	return;
+}
+
+
+/*
+ * graph_resize()
+ *
+ * Resizes graph g to given size.  If size > g->n, the new vertices are
+ * not connected to any others and their weights are set to 1.
+ * If size < g->n, the last g->n - size vertices are removed.
+ */
+void graph_resize(graph_t *g, int size) {
+	int i;
+
+	ASSERT(g!=NULL);
+	ASSERT(g->n > 0);
+	ASSERT(size > 0);
+
+	if (g->n == size)
+		return;
+
+	/* Free/alloc extra edge-sets */
+	for (i=size; i < g->n; i++)
+		set_free(g->edges[i]);
+	g->edges=realloc(g->edges, size * sizeof(set_t));
+	for (i=g->n; i < size; i++)
+		g->edges[i]=set_new(size);
+
+	/* Resize original sets */
+	for (i=0; i < MIN(g->n,size); i++) {
+		g->edges[i]=set_resize(g->edges[i],size);
+	}
+
+	/* Weights */
+	g->weights=realloc(g->weights,size * sizeof(int));
+	for (i=g->n; i<size; i++)
+		g->weights[i]=1;
+	
+	g->n=size;
+	return;
+}
+
+/*
+ * graph_crop()
+ *
+ * Resizes the graph so as to remove all highest-valued isolated vertices.
+ */
+void graph_crop(graph_t *g) {
+	int i;
+	
+	for (i=g->n-1; i>=1; i--)
+		if (set_size(g->edges[i])>0)
+			break;
+	graph_resize(g,i+1);
+	return;
+}
+
+
+/*
+ * graph_weighted()
+ *
+ * Returns TRUE if all vertex weights of graph g are all the same.
+ *
+ * Note: Does NOT require weights to be 1.
+ */
+boolean graph_weighted(graph_t *g) {
+	int i,w;
+
+	w=g->weights[0];
+	for (i=1; i < g->n; i++)
+		if (g->weights[i] != w)
+			return TRUE;
+	return FALSE;
+}
+
+/*
+ * graph_edge_count()
+ *
+ * Returns the number of edges in graph g.
+ */
+int graph_edge_count(graph_t *g) {
+	int i;
+	int count=0;
+
+	for (i=0; i < g->n; i++) {
+		count += set_size(g->edges[i]);
+	}
+	return count/2;
+}
+
+
+#if 0
+/*
+ * graph_write_dimacs_ascii_file()
+ *
+ * Writes an ASCII dimacs-format file of graph g, with comment, to
+ * given file.
+ *
+ * Returns TRUE if successful, FALSE if an error occurred.
+ */
+boolean graph_write_dimacs_ascii_file(graph_t *g, char *comment, char *file) {
+	FILE *fp;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(file!=NULL);
+
+	if ((fp=fopen(file,"wb"))==NULL)
+		return FALSE;
+	if (!graph_write_dimacs_ascii(g,comment,fp)) {
+		fclose(fp);
+		return FALSE;
+	}
+	fclose(fp);
+	return TRUE;
+}
+
+/*
+ * graph_write_dimacs_ascii()
+ *
+ * Writes an ASCII dimacs-format file of graph g, with comment, to the
+ * file stream fp.
+ *
+ * Returns TRUE if successful, FALSE if an error occurred.
+ */
+boolean graph_write_dimacs_ascii(graph_t *g, char *comment, FILE *fp) {
+	int i,j;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(graph_test(g,NULL));
+	ASSERT(fp!=NULL);
+
+	if (comment)
+		fprintf(fp,"c %s\n",comment);
+	fprintf(fp,"p edge %d %d\n",g->n,graph_edge_count(g));
+	for (i=0; i < g->n; i++)
+		if (g->weights[i]!=1)
+			fprintf(fp,"n %d %d\n",i+1,g->weights[i]);
+	for (i=0; i < g->n; i++)
+		for (j=0; j<i; j++)
+			if (GRAPH_IS_EDGE_FAST(g,i,j))
+				fprintf(fp,"e %d %d\n",i+1,j+1);
+	return TRUE;
+}
+
+/*
+ * graph_write_dimacs_binary_file()
+ *
+ * Writes a binary dimacs-format file of graph g, with comment, to
+ * given file.
+ *
+ * Returns TRUE if successful, FALSE if an error occurred.
+ */
+boolean graph_write_dimacs_binary_file(graph_t *g, char *comment, char *file) {
+	FILE *fp;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(file!=NULL);
+
+	if ((fp=fopen(file,"wb"))==NULL)
+		return FALSE;
+	if (!graph_write_dimacs_binary(g,comment,fp)) {
+		fclose(fp);
+		return FALSE;
+	}
+	fclose(fp);
+	return TRUE;
+}
+
+/*
+ * graph_write_dimacs_binary()
+ *
+ * Writes a binary dimacs-format file of graph g, with comment, to the
+ * file stream fp.
+ *
+ * Returns TRUE if successful, FALSE if an error occurred.
+ */
+
+#define STR_APPEND(s) \
+if (headerlength+strlen(s) >= headersize) {  \
+	headersize+=1024;                    \
+	header=realloc(header,headersize);   \
+}                                            \
+strncat(header,s,1000);                      \
+headerlength+=strlen(s);
+
+boolean graph_write_dimacs_binary(graph_t *g, char *comment,FILE *fp) {
+	char *buf;
+	char *header=NULL;
+	int headersize=0;
+	int headerlength=0;
+	int i,j;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(graph_test(g,NULL));
+	ASSERT(fp!=NULL);
+
+	buf=malloc(MAX(1024,g->n/8+1));
+	header=malloc(1024);
+	header[0]=0;
+	headersize=1024;
+	if (comment) {
+		strcpy(buf,"c ");
+		strncat(buf,comment,1000);
+		strcat(buf,"\n");
+		STR_APPEND(buf);
+	}
+	sprintf(buf,"p edge %d %d\n",g->n,graph_edge_count(g));
+	STR_APPEND(buf);
+	for (i=0; i < g->n; i++) {
+		if (g->weights[i]!=1) {
+			sprintf(buf,"n %d %d\n",i+1,g->weights[i]);
+			STR_APPEND(buf);
+		}
+	}
+
+	fprintf(fp,"%d\n",(int)strlen(header));
+	fprintf(fp,"%s",header);
+	free(header);
+
+	for (i=0; i < g->n; i++) {
+		memset(buf,0,i/8+1);
+		for (j=0; j<i; j++) {
+			if (GRAPH_IS_EDGE_FAST(g,i,j)) {
+				buf[j/8] |= SET_BIT_MASK(7-j%8);
+			}
+		}
+		fwrite(buf,1,i/8+1,fp);
+	}
+	free(buf);
+	return TRUE;
+}
+
+
+
+/*
+ * graph_read_dimacs_file()
+ *
+ * Reads a dimacs-format (ASCII or binary) file from the given file.
+ *
+ * Returns a newly allocated graph, or NULL if an error occurred, and an
+ * error message is printed to stderr.
+ */
+graph_t *graph_read_dimacs_file(char *file) {
+	FILE *fp;
+	graph_t *g;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(file!=NULL);
+
+	if ((fp=fopen(file,"rb"))==NULL) {
+		perror(file);
+		return NULL;
+	}
+	g=graph_read_dimacs(fp);
+	fclose(fp);
+	return g;
+}
+
+
+/*
+ * graph_read_dimacs()
+ *
+ * Reads a dimacs-format (ASCII or binary) file from the file stream fp.
+ *
+ * Returns a newly allocated graph, or NULL if an error occurred, and an
+ * error message is printed to stderr.
+ */
+graph_t *graph_read_dimacs(FILE *fp) {
+	char buffer[1024];
+	graph_t *g;
+	char tmp[10];
+	int n;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+	ASSERT(fp!=NULL);
+
+	if (fgets(buffer,1023,fp)==NULL) {
+		fprintf(stderr,"Input does not contain any data.\n");
+		return NULL;
+	}
+	if (sscanf(buffer," %d %2s",&n,tmp)!=1) {
+		g=graph_read_dimacs_ascii(fp,buffer);
+	} else {
+		g=graph_read_dimacs_binary(fp,buffer);
+	}
+	return g;
+}	
+
+
+/*
+ * parse_input()
+ *
+ * Parses the string str for ASCII-format dimacs commands, and modifies
+ * the graph g accordingly.
+ *
+ * Returns TRUE if successful, FALSE if a bad command was encountered.
+ *
+ * Note: Ignores all unknown commands.  The 'd', 'v' and 'x' commands
+ *       (mainly generator-specific information) are ignored silently,
+ *       for all others a warning message is printed to stderr.
+ */
+static boolean parse_input(char *str,graph_t *g) {
+	int i,j,w;
+	char tmp[16];
+
+	for (i=0; i<strlen(str); i++) {
+		if (!isspace((int)str[i]))
+			break;
+	}
+	if (i>=strlen(str))  /* blank line */
+		return TRUE;
+	if (str[i+1]!=0 && !isspace(str[i+1]))  /* not 1-char field */
+		return FALSE;
+
+	switch (str[i]) {
+	case 'c':
+		return TRUE;
+	case 'p':
+		if (g->n != 0)
+			return FALSE;
+		if (sscanf(str," p %15s %d %d %2s",tmp,&(g->n),&i,tmp)!=3)
+			return FALSE;
+		if (g->n <= 0)
+			return FALSE;
+		g->edges=calloc(g->n,sizeof(set_t));
+		for (i=0; i<g->n; i++)
+			g->edges[i]=set_new(g->n);
+		g->weights=calloc(g->n,sizeof(int));
+		for (i=0; i<g->n; i++)
+			g->weights[i]=1;
+		return TRUE;
+	case 'n':
+		if ((g->n <= 0) || (g->weights == NULL))
+			return FALSE;
+		if (sscanf(str," n %d %d %2s",&i,&w,tmp)!=2)
+			return FALSE;
+		if (i<1 || i>g->n)
+			return FALSE;
+		if (w<=0)
+			return FALSE;
+		g->weights[i-1]=w;
+		return TRUE;
+	case 'e':
+		if ((g->n <= 0) || (g->edges == NULL))
+			return FALSE;
+		if (sscanf(str," e %d %d %2s",&i,&j,tmp)!=2)
+			return FALSE;
+		if (i<1 || j<1 || i>g->n || j>g->n)
+			return FALSE;
+		if (i==j)   /* We want antireflexive graphs. */
+			return TRUE;
+		GRAPH_ADD_EDGE(g,i-1,j-1);
+		return TRUE;
+	case 'd':
+	case 'v':
+	case 'x':
+		return TRUE;
+	default:
+		fprintf(stderr,"Warning: ignoring field '%c' in "
+			"input.\n",str[i]);
+		return TRUE;
+	}
+}
+
+
+/*
+ * graph_read_dimacs_binary()
+ *
+ * Reads a dimacs-format binary file from file stream fp with the first
+ * line being firstline.
+ *
+ * Returns the newly-allocated graph or NULL if an error occurred.
+ *
+ * TODO: This function leaks memory when reading erroneous files.
+ */
+static graph_t *graph_read_dimacs_binary(FILE *fp,char *firstline) {
+	int length=0;
+	graph_t *g;
+	int i,j;
+	char *buffer;
+	char *start;
+	char *end;
+	char **buf;
+	char tmp[10];
+
+	if (sscanf(firstline," %d %2s",&length,tmp)!=1)
+		return NULL;
+	if (length<=0) {
+		fprintf(stderr,"Malformed preamble: preamble size < 0.\n");
+		return NULL;
+	}
+	buffer=malloc(length+2);
+	if (fread(buffer,1,length,fp)<length) {
+		fprintf(stderr,"Malformed preamble: unexpected "
+			"end of file.\n");
+		free(buffer);
+		return NULL;
+	}
+
+	g=calloc(1,sizeof(graph_t));
+	start=buffer;
+	while (start < buffer+length) {
+		end=strchr(start,'\n');
+		if (end==NULL)
+			end=buffer+length;
+		end[0]=0;
+		if (!parse_input(start,g)) {
+			fprintf(stderr,"Malformed preamble: %s\n",start);
+			free (buffer);
+			return NULL;
+		}
+		start=end+1;
+	}
+
+	free(buffer);
+	if (g->n <= 0) {
+		fprintf(stderr,"Malformed preamble: number of "
+			"vertices <= 0\n");
+		free(g);
+		return NULL;
+	}
+
+	/* Binary part. */
+	buf=calloc(g->n,sizeof(char*));
+	for (i=0; i < g->n; i++) {
+		buf[i]=calloc(g->n,1);
+		if (fread(buf[i],1,i/8+1,fp) < (i/8+1)) {
+			fprintf(stderr,"Unexpected end of file when "
+				"reading graph.\n");
+			return NULL;
+		}
+	}
+
+	for (i=0; i < g->n; i++) {
+		for (j=0; j<i; j++) {
+			if (buf[i][j/8]&(1<<(7-(j%8)))) {
+				GRAPH_ADD_EDGE(g,i,j);
+			}
+		}
+		free(buf[i]);
+	}
+	free(buf);
+
+	return g;
+}
+
+
+/*
+ * graph_read_dimacs_ascii()
+ *
+ * Reads a dimacs-format ASCII file from file stream fp with the first
+ * line being firstline.
+ *
+ * Returns the newly-allocated graph or NULL if an error occurred.
+ *
+ * TODO:  This function leaks memory when reading erroneous files.
+ */
+static graph_t *graph_read_dimacs_ascii(FILE *fp, char *firstline) {
+	graph_t *g;
+	char buffer[1024];
+
+	g=calloc(1,sizeof(graph_t));
+
+	if (!parse_input(firstline,g)) {
+		fprintf(stderr,"Malformed input: %s",firstline);
+		free(g);
+		return NULL;
+	}
+	while (fgets(buffer,1023,fp)) {
+		if (!parse_input(buffer,g)) {
+			fprintf(stderr,"Malformed input: %s",buffer);
+			return NULL;
+		}
+	}
+	if (g->n <= 0) {
+		free(g);
+		fprintf(stderr,"Unexpected end of file when reading graph.\n");
+		return NULL;
+	}
+
+	return g;
+}
+#endif
+
+
+#ifndef USING_R
+/*
+ * graph_print()
+ *
+ * Prints a representation of the graph g to stdout (along with any errors
+ * noticed).  Mainly useful for debugging purposes and trivial output.
+ *
+ * The output consists of a first line describing the dimensions and then
+ * one line per vertex containing the vertex number (numbered 0,...,n-1),
+ * the vertex weight (if the graph is weighted), "->" and then a list
+ * of all vertices it is adjacent to.
+ */
+void graph_print(graph_t *g) {
+	int i,j;
+	int asymm=0;
+	int refl=0;
+	int nonpos=0;
+	int extra=0;
+	unsigned int weight=0;
+	boolean weighted;
+	
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+
+	if (g==NULL) {
+		printf("   WARNING: Graph pointer is NULL!\n");
+		return;
+	}
+	if (g->n <= 0) {
+		printf("   WARNING: Graph has %d vertices "
+		       "(should be positive)!\n",g->n);
+		return;
+	}
+	
+	weighted=graph_weighted(g);
+
+	printf("%s graph has %d vertices, %d edges (density %.2f).\n",
+	       weighted?"Weighted":((g->weights[0]==1)?
+				    "Unweighted":"Semi-weighted"),
+	       g->n,graph_edge_count(g),
+	       (float)graph_edge_count(g)/((float)(g->n - 1)*(g->n)/2));
+
+	for (i=0; i < g->n; i++) {
+		printf("%2d",i);
+		if (weighted) {
+			printf(" w=%d",g->weights[i]);
+			if (g->weights[i] <= 0) {
+				printf("*NON-POSITIVE*");
+				nonpos++;
+			}
+		}
+		if (weight < INT_MAX)
+			weight+=g->weights[i];
+		printf(" ->");
+		for (j=0; j < g->n; j++) {
+			if (SET_CONTAINS_FAST(g->edges[i],j)) {
+				printf(" %d",j);
+				if (i==j) {
+					printf("*REFLEXIVE*");
+					refl++;
+				}
+				if (!SET_CONTAINS_FAST(g->edges[j],i)) {
+					printf("*ASYMMERTIC*");
+					asymm++;
+				}
+			}
+		}
+		for (j=g->n; j < SET_ARRAY_LENGTH(g->edges[i])*ELEMENTSIZE;
+		     j++) {
+			if (SET_CONTAINS_FAST(g->edges[i],j)) {
+				printf(" %d*NON-EXISTENT*",j);
+				extra++;
+			}
+		}
+		printf("\n");
+	}
+
+	if (asymm)
+		printf("   WARNING: Graph contained %d asymmetric edges!\n",
+		       asymm);
+	if (refl)
+		printf("   WARNING: Graph contained %d reflexive edges!\n",
+		       refl);
+	if (nonpos)
+		printf("   WARNING: Graph contained %d non-positive vertex "
+		       "weights!\n",nonpos);
+	if (extra)
+		printf("   WARNING: Graph contained %d edges to "
+		       "non-existent vertices!\n",extra);
+	if (weight>=INT_MAX)
+		printf("   WARNING: Total graph weight >= INT_MAX!\n");
+	return;
+}
+
+#endif
+
+/*
+ * graph_test()
+ *
+ * Tests graph g to be valid.  Checks that g is non-NULL, the edges are
+ * symmetric and anti-reflexive, and that all vertex weights are positive.
+ * If output is non-NULL, prints a few lines telling the status of the graph
+ * to file descriptor output.
+ * 
+ * Returns TRUE if the graph is valid, FALSE otherwise.
+ */
+boolean graph_test(graph_t *g,FILE *output) {
+	int i,j;
+	int edges=0;
+	int asymm=0;
+	int nonpos=0;
+	int refl=0;
+	int extra=0;
+	unsigned int weight=0;
+	boolean weighted;
+
+	ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+
+	if (g==NULL) {
+		if (output)
+			fprintf(output,"   WARNING: Graph pointer is NULL!\n");
+		return FALSE;
+	}
+
+	weighted=graph_weighted(g);
+	
+	for (i=0; i < g->n; i++) {
+		if (g->edges[i]==NULL) {
+			if (output)
+				fprintf(output,"   WARNING: Graph edge set "
+					"NULL!\n"
+					"   (further warning suppressed)\n");
+			return FALSE;
+		}
+		if (SET_MAX_SIZE(g->edges[i]) < g->n) {
+			if (output)
+				fprintf(output,"   WARNING: Graph edge set "
+					"too small!\n"
+					"   (further warnings suppressed)\n");
+			return FALSE;
+		}
+		for (j=0; j < g->n; j++) {
+			if (SET_CONTAINS_FAST(g->edges[i],j)) {
+				edges++;
+				if (i==j) {
+					refl++;
+				}
+				if (!SET_CONTAINS_FAST(g->edges[j],i)) {
+					asymm++;
+				}
+			}
+		}
+		for (j=g->n; j < SET_ARRAY_LENGTH(g->edges[i])*ELEMENTSIZE;
+		     j++) {
+			if (SET_CONTAINS_FAST(g->edges[i],j))
+				extra++;
+		}
+		if (g->weights[i] <= 0)
+			nonpos++;
+		if (weight<INT_MAX)
+			weight += g->weights[i];
+	}
+	
+	edges/=2;  /* Each is counted twice. */
+	
+	if (output) {
+		/* Semi-weighted means all weights are equal, but not 1. */
+		fprintf(output,"%s graph has %d vertices, %d edges "
+			"(density %.2f).\n",
+			weighted?"Weighted":
+			((g->weights[0]==1)?"Unweighted":"Semi-weighted"),
+			g->n,edges,(float)edges/((float)(g->n - 1)*(g->n)/2));
+		
+		if (asymm)
+			fprintf(output,"   WARNING: Graph contained %d "
+				"asymmetric edges!\n",asymm);
+		if (refl)
+			fprintf(output,"   WARNING: Graph contained %d "
+				"reflexive edges!\n",refl);
+		if (nonpos)
+			fprintf(output,"   WARNING: Graph contained %d "
+				"non-positive vertex weights!\n",nonpos);
+		if (extra)
+			fprintf(output,"   WARNING: Graph contained %d edges "
+				"to non-existent vertices!\n",extra);
+		if (weight>=INT_MAX)
+			fprintf(output,"   WARNING: Total graph weight >= "
+				"INT_MAX!\n");
+		if (asymm==0 && refl==0 && nonpos==0 && extra==0 &&
+		    weight<INT_MAX)
+			fprintf(output,"Graph OK.\n");
+	}
+	
+	if (asymm || refl || nonpos || extra || weight>=INT_MAX)
+		return FALSE;
+
+	return TRUE;
+}
+
+
+/*
+ * graph_test_regular()
+ *
+ * Returns the vertex degree for regular graphs, or -1 if the graph is
+ * not regular.
+ */
+int graph_test_regular(graph_t *g) {
+	int i,n;
+
+	n=set_size(g->edges[0]);
+
+	for (i=1; i < g->n; i++) {
+		if (set_size(g->edges[i]) != n)
+			return -1;
+	}
+	return n;
+}
+
diff --git a/igraph/src/cliques.c b/igraph/src/cliques.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cliques.c
@@ -0,0 +1,1399 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_cliques.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_constants.h"
+#include "igraph_adjlist.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_interface.h"
+#include "igraph_progress.h"
+#include "igraph_stack.h"
+#include "igraph_types_internal.h"
+#include "igraph_cliquer.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>    /* memset */
+
+void igraph_i_cliques_free_res(igraph_vector_ptr_t *res) {
+    long i, n;
+
+    n = igraph_vector_ptr_size(res);
+    for (i = 0; i < n; i++) {
+        if (VECTOR(*res)[i] != 0) {
+            igraph_vector_destroy(VECTOR(*res)[i]);
+            igraph_free(VECTOR(*res)[i]);
+        }
+    }
+    igraph_vector_ptr_clear(res);
+}
+
+int igraph_i_find_k_cliques(const igraph_t *graph,
+                            long int size,
+                            const igraph_real_t *member_storage,
+                            igraph_real_t **new_member_storage,
+                            long int old_clique_count,
+                            long int *clique_count,
+                            igraph_vector_t *neis,
+                            igraph_bool_t independent_vertices) {
+
+    long int j, k, l, m, n, new_member_storage_size;
+    const igraph_real_t *c1, *c2;
+    igraph_real_t v1, v2;
+    igraph_bool_t ok;
+
+    /* Allocate the storage */
+    *new_member_storage = igraph_Realloc(*new_member_storage,
+                                         (size_t) (size * old_clique_count),
+                                         igraph_real_t);
+    if (*new_member_storage == 0) {
+        IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM);
+    }
+    new_member_storage_size = size * old_clique_count;
+    IGRAPH_FINALLY(igraph_free, *new_member_storage);
+
+    m = n = 0;
+
+    /* Now consider all pairs of i-1-cliques and see if they can be merged */
+    for (j = 0; j < old_clique_count; j++) {
+        for (k = j + 1; k < old_clique_count; k++) {
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            /* Since cliques are represented by their vertex indices in increasing
+             * order, two cliques can be merged iff they have exactly the same
+             * indices excluding one AND there is an edge between the two different
+             * vertices */
+            c1 = member_storage + j * (size - 1);
+            c2 = member_storage + k * (size - 1);
+            /* Find the longest prefixes of c1 and c2 that are equal */
+            for (l = 0; l < size - 1 && c1[l] == c2[l]; l++) {
+                (*new_member_storage)[m++] = c1[l];
+            }
+            /* Now, if l == size-1, the two vectors are totally equal.
+            This is a bug */
+            if (l == size - 1) {
+                IGRAPH_WARNING("possible bug in igraph_cliques");
+                m = n;
+            } else {
+                /* Assuming that j<k, c1[l] is always less than c2[l], since cliques
+                 * are ordered alphabetically. Now add c1[l] and store c2[l] in a
+                 * dummy variable */
+                (*new_member_storage)[m++] = c1[l];
+                v1 = c1[l];
+                v2 = c2[l];
+                l++;
+                /* Copy the remaining part of the two vectors. Every member pair
+                 * found in the remaining parts satisfies the following:
+                 * 1. If they are equal, they should be added.
+                 * 2. If they are not equal, the smaller must be equal to the
+                 *    one stored in the dummy variable. If not, the two vectors
+                 *    differ in more than one place. The larger will be stored in
+                 *    the dummy variable again.
+                 */
+                ok = 1;
+                for (; l < size - 1; l++) {
+                    if (c1[l] == c2[l]) {
+                        (*new_member_storage)[m++] = c1[l];
+                        ok = 0;
+                    } else if (ok) {
+                        if (c1[l] < c2[l]) {
+                            if (c1[l] == v1) {
+                                (*new_member_storage)[m++] = c1[l];
+                                v2 = c2[l];
+                            } else {
+                                break;
+                            }
+                        } else {
+                            if (ok && c2[l] == v1) {
+                                (*new_member_storage)[m++] = c2[l];
+                                v2 = c1[l];
+                            } else {
+                                break;
+                            }
+                        }
+                    } else {
+                        break;
+                    }
+                }
+                /* Now, if l != size-1, the two vectors had a difference in more than
+                 * one place, so the whole clique is invalid. */
+                if (l != size - 1) {
+                    /* Step back in new_member_storage */
+                    m = n;
+                } else {
+                    /* v1 and v2 are the two different vertices. Check for an edge
+                     * if we are looking for cliques and check for the absence of an
+                     * edge if we are looking for independent vertex sets */
+                    IGRAPH_CHECK(igraph_neighbors(graph, neis, (igraph_integer_t) v1,
+                                                  IGRAPH_ALL));
+                    l = igraph_vector_search(neis, 0, v2, 0);
+                    if ((l && !independent_vertices) || (!l && independent_vertices)) {
+                        /* Found a new clique, step forward in new_member_storage */
+                        if (m == n || v2 > (*new_member_storage)[m - 1]) {
+                            (*new_member_storage)[m++] = v2;
+                            n = m;
+                        } else {
+                            m = n;
+                        }
+                    } else {
+                        m = n;
+                    }
+                }
+                /* See if new_member_storage is full. If so, reallocate */
+                if (m == new_member_storage_size) {
+                    IGRAPH_FINALLY_CLEAN(1);
+                    *new_member_storage = igraph_Realloc(*new_member_storage,
+                                                         (size_t) new_member_storage_size * 2,
+                                                         igraph_real_t);
+                    if (*new_member_storage == 0) {
+                        IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM);
+                    }
+                    new_member_storage_size *= 2;
+                    IGRAPH_FINALLY(igraph_free, *new_member_storage);
+                }
+            }
+        }
+    }
+
+    /* Calculate how many cliques have we found */
+    *clique_count = n / size;
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/* Internal function for calculating cliques or independent vertex sets.
+ * They are practically the same except that the complementer of the graph
+ * should be used in the latter case.
+ */
+int igraph_i_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
+                     igraph_integer_t min_size, igraph_integer_t max_size,
+                     igraph_bool_t independent_vertices) {
+
+    igraph_integer_t no_of_nodes;
+    igraph_vector_t neis;
+    igraph_real_t *member_storage = 0, *new_member_storage, *c1;
+    long int i, j, k, clique_count, old_clique_count;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_WARNING("directionality of edges is ignored for directed graphs");
+    }
+
+    no_of_nodes = igraph_vcount(graph);
+
+    if (min_size < 0) {
+        min_size = 0;
+    }
+    if (max_size > no_of_nodes || max_size <= 0) {
+        max_size = no_of_nodes;
+    }
+
+    igraph_vector_ptr_clear(res);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_FINALLY(igraph_i_cliques_free_res, res);
+
+    /* Will be resized later, if needed. */
+    member_storage = igraph_Calloc(1, igraph_real_t);
+    if (member_storage == 0) {
+        IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, member_storage);
+
+    /* Find all 1-cliques: every vertex will be a clique */
+    new_member_storage = igraph_Calloc(no_of_nodes, igraph_real_t);
+    if (new_member_storage == 0) {
+        IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, new_member_storage);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        new_member_storage[i] = i;
+    }
+    clique_count = no_of_nodes;
+    old_clique_count = 0;
+
+    /* Add size 1 cliques if requested */
+    if (min_size <= 1) {
+        IGRAPH_CHECK(igraph_vector_ptr_resize(res, no_of_nodes));
+        igraph_vector_ptr_null(res);
+        for (i = 0; i < no_of_nodes; i++) {
+            igraph_vector_t *p = igraph_Calloc(1, igraph_vector_t);
+            if (p == 0) {
+                IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, p);
+            IGRAPH_CHECK(igraph_vector_init(p, 1));
+            VECTOR(*p)[0] = i;
+            VECTOR(*res)[i] = p;
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    for (i = 2; i <= max_size && clique_count > 1; i++) {
+
+        /* Here new_member_storage contains the cliques found in the previous
+           iteration. Save this into member_storage, might be needed later  */
+
+        c1 = member_storage;
+        member_storage = new_member_storage;
+        new_member_storage = c1;
+        old_clique_count = clique_count;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /* Calculate the cliques */
+
+        IGRAPH_FINALLY_CLEAN(2);
+        IGRAPH_CHECK(igraph_i_find_k_cliques(graph, i, member_storage,
+                                             &new_member_storage,
+                                             old_clique_count,
+                                             &clique_count,
+                                             &neis,
+                                             independent_vertices));
+        IGRAPH_FINALLY(igraph_free, member_storage);
+        IGRAPH_FINALLY(igraph_free, new_member_storage);
+
+        /* Add the cliques just found to the result if requested */
+        if (i >= min_size && i <= max_size) {
+            for (j = 0, k = 0; j < clique_count; j++, k += i) {
+                igraph_vector_t *p = igraph_Calloc(1, igraph_vector_t);
+                if (p == 0) {
+                    IGRAPH_ERROR("cliques failed", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(igraph_free, p);
+                IGRAPH_CHECK(igraph_vector_init_copy(p, &new_member_storage[k], i));
+                IGRAPH_FINALLY(igraph_vector_destroy, p);
+                IGRAPH_CHECK(igraph_vector_ptr_push_back(res, p));
+                IGRAPH_FINALLY_CLEAN(2);
+            }
+        }
+
+    } /* i <= max_size && clique_count != 0 */
+
+    igraph_free(member_storage);
+    igraph_free(new_member_storage);
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(4); /* 3 here, +1 is igraph_i_cliques_free_res */
+
+    return 0;
+}
+
+/**
+ * \function igraph_cliques
+ * \brief Find all or some cliques in a graph
+ *
+ * </para><para>
+ * Cliques are fully connected subgraphs of a graph.
+ *
+ * </para><para>
+ * If you are only interested in the size of the largest clique in the graph,
+ * use \ref igraph_clique_number() instead.
+ *
+ * </para><para>The current implementation of this function searches
+ * for maximal independent vertex sets (see \ref
+ * igraph_maximal_independent_vertex_sets()) in the complementer graph
+ * using the algorithm published in:
+ * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm
+ * for generating all the maximal independent sets. SIAM J Computing,
+ * 6:505--517, 1977.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a pointer vector, the result will be stored
+ *   here, ie. \c res will contain pointers to \c igraph_vector_t
+ *   objects which contain the indices of vertices involved in a clique.
+ *   The pointer vector will be resized if needed but note that the
+ *   objects in the pointer vector will not be freed.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_largest_cliques() and \ref igraph_clique_number().
+ *
+ * Time complexity: TODO
+ *
+ * \example examples/simple/igraph_cliques.c
+ */
+int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
+                   igraph_integer_t min_size, igraph_integer_t max_size) {
+    return igraph_i_cliquer_cliques(graph, res, min_size, max_size);
+}
+
+
+/**
+ * \function igraph_clique_size_hist
+ * \brief Count cliques of each size in the graph
+ *
+ * </para><para>
+ * Cliques are fully connected subgraphs of a graph.
+ *
+ * </para><para>The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * \param graph The input graph.
+ * \param hist Pointer to an initialized vector. The result will be stored
+ * here. The first element will store the number of size-1 cliques, the second
+ * element the number of size-2 cliques, etc.  For cliques smaller than \c min_size,
+ * zero counts will be returned.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_cliques() and \ref igraph_cliques_callback()
+ *
+ * Time complexity: Exponential
+ *
+ */
+int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist,
+                            igraph_integer_t min_size, igraph_integer_t max_size) {
+    return igraph_i_cliquer_histogram(graph, hist, min_size, max_size);
+}
+
+
+/**
+ * \function igraph_cliques_callback
+ * \brief Calls a function for each clique in the graph.
+ *
+ * </para><para>
+ * Cliques are fully connected subgraphs of a graph. This function
+ * enumerates all cliques within the given size range and calls
+ * \p cliquehandler_fn for each of them. The cliques are passed to the
+ * callback function as an <type>igraph_vector_t *</type>.  Destroying and
+ * freeing this vector is left up to the user.  Use \ref igraph_vector_destroy()
+ * to destroy it first, then free it using \ref igraph_free().
+ *
+ * </para><para>The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * \param graph The input graph.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \param cliquehandler_fn Callback function to be called for each clique.
+ * See also igraph_clique_handler_t.
+ * \param arg Extra argument to supply to \p cliquehandler_fn.
+ * \return Error code.
+ *
+ * \sa \ref igraph_cliques()
+ *
+ * Time complexity: Exponential
+ *
+ */
+int igraph_cliques_callback(const igraph_t *graph,
+                            igraph_integer_t min_size, igraph_integer_t max_size,
+                            igraph_clique_handler_t *cliquehandler_fn, void *arg) {
+    return igraph_i_cliquer_callback(graph, min_size, max_size, cliquehandler_fn, arg);
+}
+
+
+/**
+ * \function igraph_weighted_cliques
+ * \brief Find all cliques in a given weight range in a vertex weighted graph
+ *
+ * </para><para>
+ * Cliques are fully connected subgraphs of a graph.
+ * The weight of a clique is the sum of the weights
+ * of individual vertices within the clique.
+ *
+ * </para><para>The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * Only positive integer vertex weights are supported.
+ *
+ * \param graph The input graph.
+ * \param vertex_weights A vector of vertex weights. The current implementation
+ *   will truncate all weights to their integer parts.
+ * \param res Pointer to a pointer vector, the result will be stored
+ *   here, ie. \c res will contain pointers to \c igraph_vector_t
+ *   objects which contain the indices of vertices involved in a clique.
+ *   The pointer vector will be resized if needed but note that the
+ *   objects in the pointer vector will not be freed.
+ * \param min_weight Integer giving the minimum weight of the cliques to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_weight Integer giving the maximum weight of the cliques to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \param maximal If true, only maximal cliques will be returned
+ * \return Error code.
+ *
+ * \sa \ref igraph_cliques(), \ref igraph_maximal_cliques()
+ *
+ * Time complexity: Exponential
+ *
+ */
+int igraph_weighted_cliques(const igraph_t *graph,
+                            const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res,
+                            igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal) {
+    return igraph_i_weighted_cliques(graph, vertex_weights, res, min_weight, max_weight, maximal);
+}
+
+
+/**
+ * \function igraph_largest_weighted_cliques
+ * \brief Finds the largest weight clique(s) in a graph.
+ *
+ * </para><para>
+ * Finds the clique(s) having the largest weight in the graph.
+ *
+ * </para><para>The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * Only positive integer vertex weights are supported.
+ *
+ * \param graph The input graph.
+ * \param vertex_weights A vector of vertex weights. The current implementation
+ *   will truncate all weights to their integer parts.
+ * \param res Pointer to a pointer vector, the result will be stored
+ *   here, ie. \c res will contain pointers to \c igraph_vector_t
+ *   objects which contain the indices of vertices involved in a clique.
+ *   The pointer vector will be resized if needed but note that the
+ *   objects in the pointer vector will not be freed.
+ * \return Error code.
+ *
+ * \sa \ref igraph_weighted_cliques(), \ref igraph_weighted_clique_number(), \ref igraph_largest_cliques()
+ *
+ * Time complexity: TODO
+ */
+int igraph_largest_weighted_cliques(const igraph_t *graph,
+                                    const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res) {
+    return igraph_i_largest_weighted_cliques(graph, vertex_weights, res);
+}
+
+
+/**
+ * \function igraph_weighted_clique_number
+ * \brief Find the weight of the largest weight clique in the graph
+ *
+ * </para><para>The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * Only positive integer vertex weights are supported.
+ *
+ * \param graph The input graph.
+ * \param vertex_weights A vector of vertex weights. The current implementation
+ *   will truncate all weights to their integer parts.
+ * \param res The largest weight will be returned to the \c igraph_real_t
+ *   pointed to by this variable.
+ * \return Error code.
+ *
+ * \sa \ref igraph_weighted_cliques(), \ref igraph_largest_weighted_cliques(), \ref igraph_clique_number()
+ *
+ * Time complexity: TODO
+ *
+ */
+int igraph_weighted_clique_number(const igraph_t *graph,
+                                  const igraph_vector_t *vertex_weights, igraph_real_t *res) {
+    return igraph_i_weighted_clique_number(graph, vertex_weights, res);
+}
+
+typedef int(*igraph_i_maximal_clique_func_t)(const igraph_vector_t*, void*, igraph_bool_t*);
+typedef struct {
+    igraph_vector_ptr_t* result;
+    igraph_integer_t min_size;
+    igraph_integer_t max_size;
+} igraph_i_maximal_clique_data_t;
+
+int igraph_i_maximal_cliques(const igraph_t *graph, igraph_i_maximal_clique_func_t func, void* data);
+
+int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph,
+        igraph_vector_ptr_t *res,
+        igraph_integer_t *clique_number,
+        igraph_bool_t keep_only_largest,
+        igraph_bool_t complementer);
+
+/**
+ * \function igraph_independent_vertex_sets
+ * \brief Find all independent vertex sets in a graph
+ *
+ * </para><para>
+ * A vertex set is considered independent if there are no edges between
+ * them.
+ *
+ * </para><para>
+ * If you are interested in the size of the largest independent vertex set,
+ * use \ref igraph_independence_number() instead.
+ *
+ * </para><para>
+ * The current implementation was ported to igraph from the Very Nauty Graph
+ * Library by Keith Briggs and uses the algorithm from the paper
+ * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm
+ * for generating all the maximal independent sets. SIAM J Computing,
+ * 6:505--517, 1977.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a pointer vector, the result will be stored
+ *   here, ie. \c res will contain pointers to \c igraph_vector_t
+ *   objects which contain the indices of vertices involved in an independent
+ *   vertex set. The pointer vector will be resized if needed but note that the
+ *   objects in the pointer vector will not be freed.
+ * \param min_size Integer giving the minimum size of the sets to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the sets to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_largest_independent_vertex_sets(),
+ * \ref igraph_independence_number().
+ *
+ * Time complexity: TODO
+ *
+ * \example examples/simple/igraph_independent_sets.c
+ */
+int igraph_independent_vertex_sets(const igraph_t *graph,
+                                   igraph_vector_ptr_t *res,
+                                   igraph_integer_t min_size,
+                                   igraph_integer_t max_size) {
+    return igraph_i_cliques(graph, res, min_size, max_size, 1);
+}
+
+/**
+ * \function igraph_largest_independent_vertex_sets
+ * \brief Finds the largest independent vertex set(s) in a graph.
+ *
+ * </para><para>
+ * An independent vertex set is largest if there is no other
+ * independent vertex set with more vertices in the graph.
+ *
+ * </para><para>
+ * The current implementation was ported to igraph from the Very Nauty Graph
+ * Library by Keith Briggs and uses the algorithm from the paper
+ * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm
+ * for generating all the maximal independent sets. SIAM J Computing,
+ * 6:505--517, 1977.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a pointer vector, the result will be stored
+ *     here. It will be resized as needed.
+ * \return Error code.
+ *
+ * \sa \ref igraph_independent_vertex_sets(), \ref
+ * igraph_maximal_independent_vertex_sets().
+ *
+ * Time complexity: TODO
+ */
+
+int igraph_largest_independent_vertex_sets(const igraph_t *graph,
+        igraph_vector_ptr_t *res) {
+    return igraph_i_maximal_or_largest_cliques_or_indsets(graph, res, 0, 1, 0);
+}
+
+typedef struct igraph_i_max_ind_vsets_data_t {
+    igraph_integer_t matrix_size;
+    igraph_adjlist_t adj_list;         /* Adjacency list of the graph */
+    igraph_vector_t deg;                 /* Degrees of individual nodes */
+    igraph_set_t* buckets;               /* Bucket array */
+    /* The IS value for each node. Still to be explained :) */
+    igraph_integer_t* IS;
+    igraph_integer_t largest_set_size;   /* Size of the largest set encountered */
+    igraph_bool_t keep_only_largest;     /* True if we keep only the largest sets */
+} igraph_i_max_ind_vsets_data_t;
+
+int igraph_i_maximal_independent_vertex_sets_backtrack(const igraph_t *graph,
+        igraph_vector_ptr_t *res,
+        igraph_i_max_ind_vsets_data_t *clqdata,
+        igraph_integer_t level) {
+    long int v1, v2, v3, c, j, k;
+    igraph_vector_int_t *neis1, *neis2;
+    igraph_bool_t f;
+    igraph_integer_t j1;
+    long int it_state;
+
+    IGRAPH_ALLOW_INTERRUPTION();
+
+    if (level >= clqdata->matrix_size - 1) {
+        igraph_integer_t size = 0;
+        if (res) {
+            igraph_vector_t *vec;
+            vec = igraph_Calloc(1, igraph_vector_t);
+            if (vec == 0) {
+                IGRAPH_ERROR("igraph_i_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM);
+            }
+            IGRAPH_VECTOR_INIT_FINALLY(vec, 0);
+            for (v1 = 0; v1 < clqdata->matrix_size; v1++)
+                if (clqdata->IS[v1] == 0) {
+                    IGRAPH_CHECK(igraph_vector_push_back(vec, v1));
+                }
+            size = (igraph_integer_t) igraph_vector_size(vec);
+            if (!clqdata->keep_only_largest) {
+                IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec));
+            } else {
+                if (size > clqdata->largest_set_size) {
+                    /* We are keeping only the largest sets, and we've found one that's
+                     * larger than all previous sets, so we have to clear the list */
+                    j = igraph_vector_ptr_size(res);
+                    for (v1 = 0; v1 < j; v1++) {
+                        igraph_vector_destroy(VECTOR(*res)[v1]);
+                        free(VECTOR(*res)[v1]);
+                    }
+                    igraph_vector_ptr_clear(res);
+                    IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec));
+                } else if (size == clqdata->largest_set_size) {
+                    IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec));
+                } else {
+                    igraph_vector_destroy(vec);
+                    free(vec);
+                }
+            }
+            IGRAPH_FINALLY_CLEAN(1);
+        } else {
+            for (v1 = 0, size = 0; v1 < clqdata->matrix_size; v1++)
+                if (clqdata->IS[v1] == 0) {
+                    size++;
+                }
+        }
+        if (size > clqdata->largest_set_size) {
+            clqdata->largest_set_size = size;
+        }
+    } else {
+        v1 = level + 1;
+        /* Count the number of vertices with an index less than v1 that have
+         * an IS value of zero */
+        neis1 = igraph_adjlist_get(&clqdata->adj_list, v1);
+        c = 0;
+        j = 0;
+        while (j < VECTOR(clqdata->deg)[v1] &&
+               (v2 = (long int) VECTOR(*neis1)[j]) <= level) {
+            if (clqdata->IS[v2] == 0) {
+                c++;
+            }
+            j++;
+        }
+
+        if (c == 0) {
+            /* If there are no such nodes... */
+            j = 0;
+            while (j < VECTOR(clqdata->deg)[v1] &&
+                   (v2 = (long int) VECTOR(*neis1)[j]) <= level) {
+                clqdata->IS[v2]++;
+                j++;
+            }
+            IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, (igraph_integer_t) v1));
+            j = 0;
+            while (j < VECTOR(clqdata->deg)[v1] &&
+                   (v2 = (long int) VECTOR(*neis1)[j]) <= level) {
+                clqdata->IS[v2]--;
+                j++;
+            }
+        } else {
+            /* If there are such nodes, store the count in the IS value of v1 */
+            clqdata->IS[v1] = (igraph_integer_t) c;
+            IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, (igraph_integer_t) v1));
+            clqdata->IS[v1] = 0;
+
+            f = 1;
+            j = 0;
+            while (j < VECTOR(clqdata->deg)[v1] &&
+                   (v2 = (long int) VECTOR(*neis1)[j]) <= level) {
+                if (clqdata->IS[v2] == 0) {
+                    IGRAPH_CHECK(igraph_set_add(&clqdata->buckets[v1],
+                                                (igraph_integer_t) j));
+                    neis2 = igraph_adjlist_get(&clqdata->adj_list, v2);
+                    k = 0;
+                    while (k < VECTOR(clqdata->deg)[v2] &&
+                           (v3 = (long int) VECTOR(*neis2)[k]) <= level) {
+                        clqdata->IS[v3]--;
+                        if (clqdata->IS[v3] == 0) {
+                            f = 0;
+                        }
+                        k++;
+                    }
+                }
+                clqdata->IS[v2]++;
+                j++;
+            }
+
+            if (f) {
+                IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, (igraph_integer_t) v1));
+            }
+
+            j = 0;
+            while (j < VECTOR(clqdata->deg)[v1] &&
+                   (v2 = (long int) VECTOR(*neis1)[j]) <= level) {
+                clqdata->IS[v2]--;
+                j++;
+            }
+
+            it_state = 0;
+            while (igraph_set_iterate(&clqdata->buckets[v1], &it_state, &j1)) {
+                j = (long)j1;
+                v2 = (long int) VECTOR(*neis1)[j];
+                neis2 = igraph_adjlist_get(&clqdata->adj_list, v2);
+                k = 0;
+                while (k < VECTOR(clqdata->deg)[v2] &&
+                       (v3 = (long int) VECTOR(*neis2)[k]) <= level) {
+                    clqdata->IS[v3]++;
+                    k++;
+                }
+            }
+            igraph_set_clear(&clqdata->buckets[v1]);
+        }
+    }
+
+    return 0;
+}
+
+void igraph_i_free_set_array(igraph_set_t* array) {
+    long int i = 0;
+    while (igraph_set_inited(array + i)) {
+        igraph_set_destroy(array + i);
+        i++;
+    }
+    igraph_Free(array);
+}
+
+/**
+ * \function igraph_maximal_independent_vertex_sets
+ * \brief Find all maximal independent vertex sets of a graph
+ *
+ * </para><para>
+ * A maximal independent vertex set is an independent vertex set which
+ * can't be extended any more by adding a new vertex to it.
+ *
+ * </para><para>
+ * The algorithm used here is based on the following paper:
+ * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm for
+ * generating all the maximal independent sets. SIAM J Computing,
+ * 6:505--517, 1977.
+ *
+ * </para><para>
+ * The implementation was originally written by Kevin O'Neill and modified
+ * by K M Briggs in the Very Nauty Graph Library. I simply re-wrote it to
+ * use igraph's data structures.
+ *
+ * </para><para>
+ * If you are interested in the size of the largest independent vertex set,
+ * use \ref igraph_independence_number() instead.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a pointer vector, the result will be stored
+ *   here, ie. \c res will contain pointers to \c igraph_vector_t
+ *   objects which contain the indices of vertices involved in an independent
+ *   vertex set. The pointer vector will be resized if needed but note that the
+ *   objects in the pointer vector will not be freed.
+ * \return Error code.
+ *
+ * \sa \ref igraph_maximal_cliques(), \ref
+ * igraph_independence_number()
+ *
+ * Time complexity: TODO.
+ */
+int igraph_maximal_independent_vertex_sets(const igraph_t *graph,
+        igraph_vector_ptr_t *res) {
+    igraph_i_max_ind_vsets_data_t clqdata;
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_WARNING("directionality of edges is ignored for directed graphs");
+    }
+
+    clqdata.matrix_size = no_of_nodes;
+    clqdata.keep_only_largest = 0;
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &clqdata.adj_list, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list);
+
+    clqdata.IS = igraph_Calloc(no_of_nodes, igraph_integer_t);
+    if (clqdata.IS == 0) {
+        IGRAPH_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, clqdata.IS);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(clqdata.deg)[i] = igraph_vector_int_size(igraph_adjlist_get(&clqdata.adj_list, i));
+    }
+
+    clqdata.buckets = igraph_Calloc(no_of_nodes + 1, igraph_set_t);
+    if (clqdata.buckets == 0) {
+        IGRAPH_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0));
+    }
+
+    igraph_vector_ptr_clear(res);
+
+    /* Do the show */
+    clqdata.largest_set_size = 0;
+    IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, &clqdata, 0));
+
+    /* Cleanup */
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_set_destroy(&clqdata.buckets[i]);
+    }
+    igraph_adjlist_destroy(&clqdata.adj_list);
+    igraph_vector_destroy(&clqdata.deg);
+    igraph_free(clqdata.IS);
+    igraph_free(clqdata.buckets);
+    IGRAPH_FINALLY_CLEAN(4);
+    return 0;
+}
+
+/**
+ * \function igraph_independence_number
+ * \brief Find the independence number of the graph
+ *
+ * </para><para>
+ * The independence number of a graph is the cardinality of the largest
+ * independent vertex set.
+ *
+ * </para><para>
+ * The current implementation was ported to igraph from the Very Nauty Graph
+ * Library by Keith Briggs and uses the algorithm from the paper
+ * S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka. A new algorithm
+ * for generating all the maximal independent sets. SIAM J Computing,
+ * 6:505--517, 1977.
+ *
+ * \param graph The input graph.
+ * \param no The independence number will be returned to the \c
+ *   igraph_integer_t pointed by this variable.
+ * \return Error code.
+ *
+ * \sa \ref igraph_independent_vertex_sets().
+ *
+ * Time complexity: TODO.
+ */
+int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no) {
+    igraph_i_max_ind_vsets_data_t clqdata;
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_WARNING("directionality of edges is ignored for directed graphs");
+    }
+
+    clqdata.matrix_size = no_of_nodes;
+    clqdata.keep_only_largest = 0;
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &clqdata.adj_list, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list);
+
+    clqdata.IS = igraph_Calloc(no_of_nodes, igraph_integer_t);
+    if (clqdata.IS == 0) {
+        IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, clqdata.IS);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(clqdata.deg)[i] = igraph_vector_int_size(igraph_adjlist_get(&clqdata.adj_list, i));
+    }
+
+    clqdata.buckets = igraph_Calloc(no_of_nodes + 1, igraph_set_t);
+    if (clqdata.buckets == 0) {
+        IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0));
+    }
+
+    /* Do the show */
+    clqdata.largest_set_size = 0;
+    IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, 0, &clqdata, 0));
+    *no = clqdata.largest_set_size;
+
+    /* Cleanup */
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_set_destroy(&clqdata.buckets[i]);
+    }
+    igraph_adjlist_destroy(&clqdata.adj_list);
+    igraph_vector_destroy(&clqdata.deg);
+    igraph_free(clqdata.IS);
+    igraph_free(clqdata.buckets);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/*************************************************************************/
+/* MAXIMAL CLIQUES, LARGEST CLIQUES                                      */
+/*************************************************************************/
+
+int igraph_i_maximal_cliques_store_max_size(const igraph_vector_t* clique, void* data,
+        igraph_bool_t* cont) {
+    igraph_integer_t* result = (igraph_integer_t*)data;
+    IGRAPH_UNUSED(cont);
+    if (*result < igraph_vector_size(clique)) {
+        *result = (igraph_integer_t) igraph_vector_size(clique);
+    }
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_i_maximal_cliques_store(const igraph_vector_t* clique, void* data, igraph_bool_t* cont) {
+    igraph_vector_ptr_t* result = (igraph_vector_ptr_t*)data;
+    igraph_vector_t* vec;
+
+    IGRAPH_UNUSED(cont);
+    vec = igraph_Calloc(1, igraph_vector_t);
+    if (vec == 0) {
+        IGRAPH_ERROR("cannot allocate memory for storing next clique", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_CHECK(igraph_vector_copy(vec, clique));
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(result, vec));
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_i_maximal_cliques_store_size_check(const igraph_vector_t* clique, void* data_, igraph_bool_t* cont) {
+    igraph_i_maximal_clique_data_t* data = (igraph_i_maximal_clique_data_t*)data_;
+    igraph_vector_t* vec;
+    igraph_integer_t size = (igraph_integer_t) igraph_vector_size(clique);
+
+    IGRAPH_UNUSED(cont);
+    if (size < data->min_size || size > data->max_size) {
+        return IGRAPH_SUCCESS;
+    }
+
+    vec = igraph_Calloc(1, igraph_vector_t);
+    if (vec == 0) {
+        IGRAPH_ERROR("cannot allocate memory for storing next clique", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_CHECK(igraph_vector_copy(vec, clique));
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(data->result, vec));
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_i_largest_cliques_store(const igraph_vector_t* clique, void* data, igraph_bool_t* cont) {
+    igraph_vector_ptr_t* result = (igraph_vector_ptr_t*)data;
+    igraph_vector_t* vec;
+    long int i, n;
+
+    IGRAPH_UNUSED(cont);
+    /* Is the current clique at least as large as the others that we have found? */
+    if (!igraph_vector_ptr_empty(result)) {
+        n = igraph_vector_size(clique);
+        if (n < igraph_vector_size(VECTOR(*result)[0])) {
+            return IGRAPH_SUCCESS;
+        }
+
+        if (n > igraph_vector_size(VECTOR(*result)[0])) {
+            for (i = 0; i < igraph_vector_ptr_size(result); i++) {
+                igraph_vector_destroy(VECTOR(*result)[i]);
+            }
+            igraph_vector_ptr_free_all(result);
+            igraph_vector_ptr_resize(result, 0);
+        }
+    }
+
+    vec = igraph_Calloc(1, igraph_vector_t);
+    if (vec == 0) {
+        IGRAPH_ERROR("cannot allocate memory for storing next clique", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_CHECK(igraph_vector_copy(vec, clique));
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(result, vec));
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_largest_cliques
+ * \brief Finds the largest clique(s) in a graph.
+ *
+ * </para><para>
+ * A clique is largest (quite intuitively) if there is no other clique
+ * in the graph which contains more vertices.
+ *
+ * </para><para>
+ * Note that this is not necessarily the same as a maximal clique,
+ * ie. the largest cliques are always maximal but a maximal clique is
+ * not always largest.
+ *
+ * </para><para>The current implementation of this function searches
+ * for maximal cliques using \ref igraph_maximal_cliques() and drops
+ * those that are not the largest.
+ *
+ * </para><para>The implementation of this function changed between
+ * igraph 0.5 and 0.6, so the order of the cliques and the order of
+ * vertices within the cliques will almost surely be different between
+ * these two versions.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to an initialized pointer vector, the result
+ *        will be stored here. It will be resized as needed. Note that
+ *        vertices of a clique may be returned in arbitrary order.
+ * \return Error code.
+ *
+ * \sa \ref igraph_cliques(), \ref igraph_maximal_cliques()
+ *
+ * Time complexity: O(3^(|V|/3)) worst case.
+ */
+
+int igraph_largest_cliques(const igraph_t *graph, igraph_vector_ptr_t *res) {
+    igraph_vector_ptr_clear(res);
+    IGRAPH_FINALLY(igraph_i_cliques_free_res, res);
+    IGRAPH_CHECK(igraph_i_maximal_cliques(graph, &igraph_i_largest_cliques_store, (void*)res));
+    IGRAPH_FINALLY_CLEAN(1);
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_clique_number
+ * \brief Find the clique number of the graph
+ *
+ * </para><para>
+ * The clique number of a graph is the size of the largest clique.
+ *
+ * \param graph The input graph.
+ * \param no The clique number will be returned to the \c igraph_integer_t
+ *   pointed by this variable.
+ * \return Error code.
+ *
+ * \sa \ref igraph_cliques(), \ref igraph_largest_cliques().
+ *
+ * Time complexity: O(3^(|V|/3)) worst case.
+ */
+int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no) {
+    *no = 0;
+    return igraph_i_maximal_cliques(graph, &igraph_i_maximal_cliques_store_max_size, (void*)no);
+}
+
+typedef struct {
+    igraph_vector_int_t cand;
+    igraph_vector_int_t fini;
+    igraph_vector_int_t cand_filtered;
+} igraph_i_maximal_cliques_stack_frame;
+
+void igraph_i_maximal_cliques_stack_frame_destroy(igraph_i_maximal_cliques_stack_frame *frame) {
+    igraph_vector_int_destroy(&frame->cand);
+    igraph_vector_int_destroy(&frame->fini);
+    igraph_vector_int_destroy(&frame->cand_filtered);
+}
+
+void igraph_i_maximal_cliques_stack_destroy(igraph_stack_ptr_t *stack) {
+    igraph_i_maximal_cliques_stack_frame *frame;
+
+    while (!igraph_stack_ptr_empty(stack)) {
+        frame = (igraph_i_maximal_cliques_stack_frame*)igraph_stack_ptr_pop(stack);
+        igraph_i_maximal_cliques_stack_frame_destroy(frame);
+        free(frame);
+    }
+
+    igraph_stack_ptr_destroy(stack);
+}
+
+int igraph_i_maximal_cliques(const igraph_t *graph, igraph_i_maximal_clique_func_t func, void* data) {
+    int directed = igraph_is_directed(graph);
+    long int i, j, k, l;
+    igraph_integer_t no_of_nodes, nodes_to_check, nodes_done;
+    igraph_integer_t best_cand = 0, best_cand_degree = 0, best_fini_cand_degree;
+    igraph_adjlist_t adj_list;
+    igraph_stack_ptr_t stack;
+    igraph_i_maximal_cliques_stack_frame frame, *new_frame_ptr;
+    igraph_vector_t clique;
+    igraph_vector_int_t new_cand, new_fini, cn, best_cand_nbrs,
+                        best_fini_cand_nbrs;
+    igraph_bool_t cont = 1;
+    int assret;
+
+    if (directed) {
+        IGRAPH_WARNING("directionality of edges is ignored for directed graphs");
+    }
+
+    no_of_nodes = igraph_vcount(graph);
+    if (no_of_nodes == 0) {
+        return IGRAPH_SUCCESS;
+    }
+
+    /* Construct an adjacency list representation */
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adj_list, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adj_list);
+    IGRAPH_CHECK(igraph_adjlist_simplify(&adj_list));
+    igraph_adjlist_sort(&adj_list);
+
+    /* Initialize stack */
+    IGRAPH_CHECK(igraph_stack_ptr_init(&stack, 0));
+    IGRAPH_FINALLY(igraph_i_maximal_cliques_stack_destroy, &stack);
+
+    /* Create the initial (empty) clique */
+    IGRAPH_VECTOR_INIT_FINALLY(&clique, 0);
+
+    /* Initialize new_cand, new_fini, cn, best_cand_nbrs and best_fini_cand_nbrs (will be used later) */
+    igraph_vector_int_init(&new_cand, 0);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &new_cand);
+    igraph_vector_int_init(&new_fini, 0);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &new_fini);
+    igraph_vector_int_init(&cn, 0);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &cn);
+    igraph_vector_int_init(&best_cand_nbrs, 0);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &best_cand_nbrs);
+    igraph_vector_int_init(&best_fini_cand_nbrs, 0);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &best_fini_cand_nbrs);
+
+    /* Find the vertex with the highest degree */
+    best_cand = 0; best_cand_degree = (igraph_integer_t) igraph_vector_int_size(igraph_adjlist_get(&adj_list, 0));
+    for (i = 1; i < no_of_nodes; i++) {
+        j = igraph_vector_int_size(igraph_adjlist_get(&adj_list, i));
+        if (j > best_cand_degree) {
+            best_cand = (igraph_integer_t) i;
+            best_cand_degree = (igraph_integer_t) j;
+        }
+    }
+
+    /* Create the initial stack frame */
+    IGRAPH_CHECK(igraph_vector_int_init_seq(&frame.cand, 0, no_of_nodes - 1));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &frame.cand);
+    IGRAPH_CHECK(igraph_vector_int_init(&frame.fini, 0));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &frame.fini);
+    IGRAPH_CHECK(igraph_vector_int_init(&frame.cand_filtered, 0));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &frame.cand_filtered);
+    IGRAPH_CHECK(igraph_vector_int_difference_sorted(&frame.cand,
+                 igraph_adjlist_get(&adj_list, best_cand), &frame.cand_filtered));
+    IGRAPH_FINALLY_CLEAN(3);
+    IGRAPH_FINALLY(igraph_i_maximal_cliques_stack_frame_destroy, &frame);
+
+    /* TODO: frame.cand and frame.fini should be a set instead of a vector */
+
+    /* Main loop starts here */
+    nodes_to_check = (igraph_integer_t) igraph_vector_int_size(&frame.cand_filtered); nodes_done = 0;
+    while (!igraph_vector_int_empty(&frame.cand_filtered) || !igraph_stack_ptr_empty(&stack)) {
+        if (igraph_vector_int_empty(&frame.cand_filtered)) {
+            /* No candidates left to check in this stack frame, pop out the previous stack frame */
+            igraph_i_maximal_cliques_stack_frame *newframe = igraph_stack_ptr_pop(&stack);
+            igraph_i_maximal_cliques_stack_frame_destroy(&frame);
+            frame = *newframe;
+            free(newframe);
+
+            if (igraph_stack_ptr_size(&stack) == 1) {
+                /* We will be using the next candidate node in the next iteration, so we can increase
+                 * nodes_done by 1 */
+                nodes_done++;
+            }
+
+            /* For efficiency reasons, we only check for interruption and show progress here */
+            IGRAPH_PROGRESS("Maximal cliques: ", 100.0 * nodes_done / nodes_to_check, NULL);
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            igraph_vector_pop_back(&clique);
+            continue;
+        }
+
+        /* Try the next node in the clique */
+        i = (long int) igraph_vector_int_pop_back(&frame.cand_filtered);
+        IGRAPH_CHECK(igraph_vector_push_back(&clique, i));
+
+        /* Remove the node from the candidate list */
+        assret = igraph_vector_int_binsearch(&frame.cand, i, &j); assert(assret);
+        igraph_vector_int_remove(&frame.cand, j);
+
+        /* Add the node to the finished list */
+        assret = !igraph_vector_int_binsearch(&frame.fini, i, &j); assert(assret);
+        IGRAPH_CHECK(igraph_vector_int_insert(&frame.fini, j, i));
+
+        /* Create new_cand and new_fini */
+        IGRAPH_CHECK(igraph_vector_int_intersect_sorted(&frame.cand, igraph_adjlist_get(&adj_list, i), &new_cand));
+        IGRAPH_CHECK(igraph_vector_int_intersect_sorted(&frame.fini, igraph_adjlist_get(&adj_list, i), &new_fini));
+
+        /* Do we have anything more to search? */
+        if (igraph_vector_int_empty(&new_cand)) {
+            if (igraph_vector_int_empty(&new_fini)) {
+                /* We have a maximal clique here */
+                IGRAPH_CHECK(func(&clique, data, &cont));
+                if (!cont) {
+                    /* The callback function requested to stop the search */
+                    break;
+                }
+            }
+            igraph_vector_pop_back(&clique);
+            continue;
+        }
+        if (igraph_vector_int_empty(&new_fini) &&
+            igraph_vector_int_size(&new_cand) == 1) {
+            /* Shortcut: only one node left */
+            IGRAPH_CHECK(igraph_vector_push_back(&clique, VECTOR(new_cand)[0]));
+            IGRAPH_CHECK(func(&clique, data, &cont));
+            if (!cont) {
+                /* The callback function requested to stop the search */
+                break;
+            }
+            igraph_vector_pop_back(&clique);
+            igraph_vector_pop_back(&clique);
+            continue;
+        }
+
+        /* Find the next best candidate node in new_fini */
+        l = igraph_vector_int_size(&new_cand);
+        best_cand_degree = -1;
+        j = igraph_vector_int_size(&new_fini);
+        for (i = 0; i < j; i++) {
+            k = (long int)VECTOR(new_fini)[i];
+            IGRAPH_CHECK(igraph_vector_int_intersect_sorted(&new_cand, igraph_adjlist_get(&adj_list, k), &cn));
+            if (igraph_vector_int_size(&cn) > best_cand_degree) {
+                best_cand_degree = (igraph_integer_t) igraph_vector_int_size(&cn);
+                IGRAPH_CHECK(igraph_vector_int_update(&best_fini_cand_nbrs, &cn));
+                if (best_cand_degree == l) {
+                    /* Cool, we surely have the best candidate node here as best_cand_degree can't get any better */
+                    break;
+                }
+            }
+        }
+        /* Shortcut here: we don't have to examine new_cand */
+        if (best_cand_degree == l) {
+            igraph_vector_pop_back(&clique);
+            continue;
+        }
+        /* Still finding best candidate node */
+        best_fini_cand_degree = best_cand_degree;
+        best_cand_degree = -1;
+        j = igraph_vector_int_size(&new_cand);
+        l = l - 1;
+        for (i = 0; i < j; i++) {
+            k = (long int)VECTOR(new_cand)[i];
+            IGRAPH_CHECK(igraph_vector_int_intersect_sorted(&new_cand, igraph_adjlist_get(&adj_list, k), &cn));
+            if (igraph_vector_int_size(&cn) > best_cand_degree) {
+                best_cand_degree = (igraph_integer_t) igraph_vector_int_size(&cn);
+                IGRAPH_CHECK(igraph_vector_int_update(&best_cand_nbrs, &cn));
+                if (best_cand_degree == l) {
+                    /* Cool, we surely have the best candidate node here as best_cand_degree can't get any better */
+                    break;
+                }
+            }
+        }
+
+        /* Create a new stack frame in case we back out later */
+        new_frame_ptr = igraph_Calloc(1, igraph_i_maximal_cliques_stack_frame);
+        if (new_frame_ptr == 0) {
+            IGRAPH_ERROR("cannot allocate new stack frame", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, new_frame_ptr);
+        *new_frame_ptr = frame;
+        memset(&frame, 0, sizeof(frame));
+        IGRAPH_CHECK(igraph_stack_ptr_push(&stack, new_frame_ptr));
+        IGRAPH_FINALLY_CLEAN(1);  /* ownership of new_frame_ptr taken by the stack */
+        /* Ownership of the current frame and its vectors (frame.cand, frame.done, frame.cand_filtered)
+         * is taken by the stack from now on. Vectors in frame must be re-initialized with new_cand,
+         * new_fini and stuff. The old frame.cand and frame.fini won't be leaked because they are
+         * managed by the stack now. */
+        frame.cand = new_cand;
+        frame.fini = new_fini;
+        IGRAPH_CHECK(igraph_vector_int_init(&new_cand, 0));
+        IGRAPH_CHECK(igraph_vector_int_init(&new_fini, 0));
+        IGRAPH_CHECK(igraph_vector_int_init(&frame.cand_filtered, 0));
+
+        /* Adjust frame.cand_filtered */
+        if (best_cand_degree < best_fini_cand_degree) {
+            IGRAPH_CHECK(igraph_vector_int_difference_sorted(&frame.cand, &best_fini_cand_nbrs, &frame.cand_filtered));
+        } else {
+            IGRAPH_CHECK(igraph_vector_int_difference_sorted(&frame.cand, &best_cand_nbrs, &frame.cand_filtered));
+        }
+    }
+
+    IGRAPH_PROGRESS("Maximal cliques: ", 100.0, NULL);
+
+    igraph_adjlist_destroy(&adj_list);
+    igraph_vector_destroy(&clique);
+    igraph_vector_int_destroy(&new_cand);
+    igraph_vector_int_destroy(&new_fini);
+    igraph_vector_int_destroy(&cn);
+    igraph_vector_int_destroy(&best_cand_nbrs);
+    igraph_vector_int_destroy(&best_fini_cand_nbrs);
+    igraph_i_maximal_cliques_stack_frame_destroy(&frame);
+    igraph_i_maximal_cliques_stack_destroy(&stack);
+    IGRAPH_FINALLY_CLEAN(9);
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph,
+        igraph_vector_ptr_t *res,
+        igraph_integer_t *clique_number,
+        igraph_bool_t keep_only_largest,
+        igraph_bool_t complementer) {
+    igraph_i_max_ind_vsets_data_t clqdata;
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_WARNING("directionality of edges is ignored for directed graphs");
+    }
+
+    clqdata.matrix_size = no_of_nodes;
+    clqdata.keep_only_largest = keep_only_largest;
+
+    if (complementer) {
+        IGRAPH_CHECK(igraph_adjlist_init_complementer(graph, &clqdata.adj_list, IGRAPH_ALL, 0));
+    } else {
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &clqdata.adj_list, IGRAPH_ALL));
+    }
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list);
+
+    clqdata.IS = igraph_Calloc(no_of_nodes, igraph_integer_t);
+    if (clqdata.IS == 0) {
+        IGRAPH_ERROR("igraph_i_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, clqdata.IS);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(clqdata.deg)[i] = igraph_vector_int_size(igraph_adjlist_get(&clqdata.adj_list, i));
+    }
+
+    clqdata.buckets = igraph_Calloc(no_of_nodes + 1, igraph_set_t);
+    if (clqdata.buckets == 0) {
+        IGRAPH_ERROR("igraph_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0));
+    }
+
+    if (res) {
+        igraph_vector_ptr_clear(res);
+    }
+
+    /* Do the show */
+    clqdata.largest_set_size = 0;
+    IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, &clqdata, 0));
+
+    /* Cleanup */
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_set_destroy(&clqdata.buckets[i]);
+    }
+    igraph_adjlist_destroy(&clqdata.adj_list);
+    igraph_vector_destroy(&clqdata.deg);
+    igraph_free(clqdata.IS);
+    igraph_free(clqdata.buckets);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    if (clique_number) {
+        *clique_number = clqdata.largest_set_size;
+    }
+    return 0;
+}
diff --git a/igraph/src/close.c b/igraph/src/close.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/close.c
@@ -0,0 +1,101 @@
+#include "f2c.h"
+#include "fio.h"
+#ifdef KR_headers
+integer f_clos(a) cllist *a;
+#else
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#ifdef NON_UNIX_STDIO
+#ifndef unlink
+#define unlink remove
+#endif
+#else
+#ifdef MSDOS
+#include "io.h"
+#else
+#ifdef __cplusplus
+extern "C" int unlink(const char*);
+#else
+extern int unlink(const char*);
+#endif
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+integer f_clos(cllist *a)
+#endif
+{	unit *b;
+
+	if(a->cunit >= MXUNIT) return(0);
+	b= &f__units[a->cunit];
+	if(b->ufd==NULL)
+		goto done;
+	if (b->uscrtch == 1)
+		goto Delete;
+	if (!a->csta)
+		goto Keep;
+	switch(*a->csta) {
+		default:
+	 	Keep:
+		case 'k':
+		case 'K':
+			if(b->uwrt == 1)
+				t_runc((alist *)a);
+			if(b->ufnm) {
+				fclose(b->ufd);
+				free(b->ufnm);
+				}
+			break;
+		case 'd':
+		case 'D':
+		Delete:
+			fclose(b->ufd);
+			if(b->ufnm) {
+				unlink(b->ufnm); /*SYSDEP*/
+				free(b->ufnm);
+				}
+		}
+	b->ufd=NULL;
+ done:
+	b->uend=0;
+	b->ufnm=NULL;
+	return(0);
+	}
+ void
+#ifdef KR_headers
+f_exit()
+#else
+f_exit(void)
+#endif
+{	int i;
+	static cllist xx;
+	if (!xx.cerr) {
+		xx.cerr=1;
+		xx.csta=NULL;
+		for(i=0;i<MXUNIT;i++)
+		{
+			xx.cunit=i;
+			(void) f_clos(&xx);
+		}
+	}
+}
+ int
+#ifdef KR_headers
+flush_()
+#else
+flush_(void)
+#endif
+{	int i;
+	for(i=0;i<MXUNIT;i++)
+		if(f__units[i].ufd != NULL && f__units[i].uwrt)
+			fflush(f__units[i].ufd);
+return 0;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/clustertool.cpp b/igraph/src/clustertool.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/clustertool.cpp
@@ -0,0 +1,693 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Joerg Reichardt
+   The original copyright notice follows here */
+
+/***************************************************************************
+                          main.cpp  -  description
+                             -------------------
+    begin                : Tue Jul 13 11:26:47 CEST 2004
+    copyright            : (C) 2004 by
+    email                :
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <iostream>
+#include <cstdlib>
+#include <cstdio>
+#include <ctime>
+
+#include "NetDataTypes.h"
+#include "NetRoutines.h"
+#include "pottsmodel_2.h"
+
+#include "igraph_community.h"
+#include "igraph_error.h"
+#include "igraph_random.h"
+#include "igraph_math.h"
+#include "igraph_interface.h"
+#include "igraph_components.h"
+#include "igraph_interrupt_internal.h"
+
+int igraph_i_community_spinglass_orig(const igraph_t *graph,
+                                      const igraph_vector_t *weights,
+                                      igraph_real_t *modularity,
+                                      igraph_real_t *temperature,
+                                      igraph_vector_t *membership,
+                                      igraph_vector_t *csize,
+                                      igraph_integer_t spins,
+                                      igraph_bool_t parupdate,
+                                      igraph_real_t starttemp,
+                                      igraph_real_t stoptemp,
+                                      igraph_real_t coolfact,
+                                      igraph_spincomm_update_t update_rule,
+                                      igraph_real_t gamma);
+
+int igraph_i_community_spinglass_negative(const igraph_t *graph,
+        const igraph_vector_t *weights,
+        igraph_real_t *modularity,
+        igraph_real_t *temperature,
+        igraph_vector_t *membership,
+        igraph_vector_t *csize,
+        igraph_integer_t spins,
+        igraph_bool_t parupdate,
+        igraph_real_t starttemp,
+        igraph_real_t stoptemp,
+        igraph_real_t coolfact,
+        igraph_spincomm_update_t update_rule,
+        igraph_real_t gamma,
+        /*                    igraph_matrix_t *adhesion, */
+        /*                    igraph_matrix_t *normalised_adhesion, */
+        /*                    igraph_real_t *polarization, */
+        igraph_real_t gamma_minus);
+
+/**
+ * \function igraph_community_spinglass
+ * \brief Community detection based on statistical mechanics
+ *
+ * This function implements the community structure detection
+ * algorithm proposed by Joerg Reichardt and Stefan Bornholdt.
+ * The algorithm is described in their paper: Statistical Mechanics of
+ * Community Detection, http://arxiv.org/abs/cond-mat/0603718 .
+ *
+ * </para><para> From version 0.6 igraph also supports an extension to
+ * the algorithm that allows negative edge weights. This is described
+ * in  V.A. Traag and Jeroen Bruggeman: Community detection in networks
+ * with positive and negative links, http://arxiv.org/abs/0811.2329 .
+ * \param graph The input graph, it may be directed but the direction
+ *     of the edge is not used in the algorithm.
+ * \param weights The vector giving the edge weights, it may be \c NULL,
+ *     in which case all edges are weighted equally. Edge weights
+ *     should be positive, altough this is not tested.
+ * \param modularity Pointer to a real number, if not \c NULL then the
+ *     modularity score of the solution will be stored here. This is the
+ *     gereralized modularity that simplifies to the one defined in
+ *     M. E. J. Newman and M. Girvan, Phys. Rev. E 69, 026113 (2004),
+ *     if the gamma parameter is one.
+ * \param temperature Pointer to a real number, if not \c NULL then
+ *     the temperature at the end of the algorithm will be stored
+ *     here.
+ * \param membership Pointer to an initialized vector or \c NULL. If
+ *     not \c NULL then the result of the clustering will be stored
+ *     here, for each vertex the number of its cluster is given, the
+ *     first cluster is numbered zero. The vector will be resized as
+ *     needed.
+ * \param csize Pointer to an initialized vector or \c NULL. If not \c
+ *     NULL then the sizes of the clusters will stored here in cluster
+ *     number order. The vector will be resized as needed.
+ * \param spins Integer giving the number of spins, ie. the maximum
+ *     number of clusters. Usually it is not a program to give a high
+ *     number here, the default was 25 in the original code. Even if
+ *     the number of spins is high the number of clusters in the
+ *     result might small.
+ * \param parupdate A logical constant, whether to update all spins in
+ *     parallel. The default for this argument was \c FALSE (ie. 0) in
+ *     the original code. It is not implemented in the \c
+ *     IGRAPH_SPINCOMM_INP_NEG implementation.
+ * \param starttemp Real number, the temperature at the start. The
+ *     value of this argument was 1.0 in the original code.
+ * \param stoptemp Real number, the algorithm stops at this
+ *     temperature. The default was 0.01 in the original code.
+ * \param coolfact Real number, the coolinf factor for the simulated
+ *     annealing. The default was 0.99 in the original code.
+ * \param update_rule The type of the update rule. Possible values: \c
+ *     IGRAPH_SPINCOMM_UPDATE_SIMPLE and \c
+ *     IGRAPH_SPINCOMM_UPDATE_CONFIG. Basically this parameter defined
+ *     the null model based on which the actual clustering is done. If
+ *     this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph
+ *     (ie. G(n,p)), if it is \c IGRAPH_SPINCOMM_UPDATE then the
+ *     configuration model is used. The configuration means that the
+ *     baseline for the clustering is a random graph with the same
+ *     degree distribution as the input graph.
+ * \param gamma Real number. The gamma parameter of the
+ *     algorithm. This defined the weight of the missing and existing
+ *     links in the quality function for the clustering. The default
+ *     value in the original code was 1.0, which is equal weight to
+ *     missing and existing edges. Smaller values make the existing
+ *     links contibute more to the energy function which is minimized
+ *     in the algorithm. Bigger values make the missing links more
+ *     important. (If my understanding is correct.)
+ * \param implementation Constant, chooses between the two
+ *     implementations of the spin-glass algorithm that are included
+ *     in igraph. \c IGRAPH_SPINCOMM_IMP_ORIG selects the original
+ *     implementation, this is faster, \c IGRAPH_SPINCOMM_INP_NEG selects
+ *     a new implementation by Vincent Traag that allows negative edge
+ *     weights.
+ * \param gamma_minus Real number. Parameter for the \c
+ *     IGRAPH_SPINCOMM_IMP_NEG implementation. This
+ *     specifies the balance between the importance of present and
+ *     non-present negative weighted edges in a community. Smaller values of
+ *     \p gamma_minus lead to communities with lesser
+ *     negative intra-connectivity.
+ *     If this argument is set to zero, the algorithm reduces to a graph
+ *     coloring algorithm, using the number of spins as the number of
+ *     colors.
+ * \return Error code.
+ *
+ * \sa igraph_community_spinglass_single() for calculating the community
+ * of a single vertex.
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/spinglass.c
+ */
+
+int igraph_community_spinglass(const igraph_t *graph,
+                               const igraph_vector_t *weights,
+                               igraph_real_t *modularity,
+                               igraph_real_t *temperature,
+                               igraph_vector_t *membership,
+                               igraph_vector_t *csize,
+                               igraph_integer_t spins,
+                               igraph_bool_t parupdate,
+                               igraph_real_t starttemp,
+                               igraph_real_t stoptemp,
+                               igraph_real_t coolfact,
+                               igraph_spincomm_update_t update_rule,
+                               igraph_real_t gamma,
+                               /* the rest is for the NegSpin implementation */
+                               igraph_spinglass_implementation_t implementation,
+                               /*                 igraph_matrix_t *adhesion, */
+                               /*                 igraph_matrix_t *normalised_adhesion, */
+                               /*                 igraph_real_t *polarization, */
+                               igraph_real_t gamma_minus) {
+
+    switch (implementation) {
+    case IGRAPH_SPINCOMM_IMP_ORIG:
+        return igraph_i_community_spinglass_orig(graph, weights, modularity,
+                temperature, membership, csize,
+                spins, parupdate, starttemp,
+                stoptemp, coolfact, update_rule,
+                gamma);
+        break;
+    case IGRAPH_SPINCOMM_IMP_NEG:
+        return igraph_i_community_spinglass_negative(graph, weights, modularity,
+                temperature, membership, csize,
+                spins, parupdate, starttemp,
+                stoptemp, coolfact,
+                update_rule, gamma,
+                /*                       adhesion, normalised_adhesion, */
+                /*                       polarization, */
+                gamma_minus);
+        break;
+    default:
+        IGRAPH_ERROR("Unknown `implementation' in spinglass community finding",
+                     IGRAPH_EINVAL);
+    }
+
+    return 0;
+}
+
+int igraph_i_community_spinglass_orig(const igraph_t *graph,
+                                      const igraph_vector_t *weights,
+                                      igraph_real_t *modularity,
+                                      igraph_real_t *temperature,
+                                      igraph_vector_t *membership,
+                                      igraph_vector_t *csize,
+                                      igraph_integer_t spins,
+                                      igraph_bool_t parupdate,
+                                      igraph_real_t starttemp,
+                                      igraph_real_t stoptemp,
+                                      igraph_real_t coolfact,
+                                      igraph_spincomm_update_t update_rule,
+                                      igraph_real_t gamma) {
+
+    unsigned long changes, runs;
+    igraph_bool_t use_weights = 0;
+    bool zeroT;
+    double kT, acc, prob;
+    ClusterList<NNode*> *cl_cur;
+    network *net;
+    PottsModel *pm;
+
+    /* Check arguments */
+
+    if (spins < 2 || spins > 500) {
+        IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL);
+    }
+    if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE &&
+        update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) {
+        IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL);
+    }
+    if (weights) {
+        if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+            IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+        }
+        use_weights = 1;
+    }
+    if (coolfact < 0 || coolfact >= 1.0) {
+        IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL);
+    }
+    if (gamma < 0.0) {
+        IGRAPH_ERROR("Invalid gamma value", IGRAPH_EINVAL);
+    }
+    if (starttemp / stoptemp < 1.0) {
+        IGRAPH_ERROR("starttemp should be larger in absolute value than stoptemp",
+                     IGRAPH_EINVAL);
+    }
+
+    /* Check whether we have a single component */
+    igraph_bool_t conn;
+    IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
+    if (!conn) {
+        IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL);
+    }
+
+    net = new network;
+    net->node_list   = new DL_Indexed_List<NNode*>();
+    net->link_list   = new DL_Indexed_List<NLink*>();
+    net->cluster_list = new DL_Indexed_List<ClusterList<NNode*>*>();
+
+    /* Transform the igraph_t */
+    IGRAPH_CHECK(igraph_i_read_network(graph, weights,
+                                       net, use_weights, 0));
+
+    prob = 2.0 * net->sum_weights / double(net->node_list->Size())
+           / double(net->node_list->Size() - 1);
+
+    pm = new PottsModel(net, (unsigned int)spins, update_rule);
+
+    /* initialize the random number generator */
+    RNG_BEGIN();
+
+    if ((stoptemp == 0.0) && (starttemp == 0.0)) {
+        zeroT = true;
+    } else {
+        zeroT = false;
+    }
+    if (!zeroT) {
+        kT = pm->FindStartTemp(gamma, prob, starttemp);
+    } else {
+        kT = stoptemp;
+    }
+    /* assign random initial configuration */
+    pm->assign_initial_conf(-1);
+    runs = 0;
+    changes = 1;
+
+    while (changes > 0 && (kT / stoptemp > 1.0 || (zeroT && runs < 150))) {
+
+        IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */
+
+        runs++;
+        if (!zeroT) {
+            kT *= coolfact;
+            if (parupdate) {
+                changes = pm->HeatBathParallelLookup(gamma, prob, kT, 50);
+            } else {
+                acc = pm->HeatBathLookup(gamma, prob, kT, 50);
+                if (acc < (1.0 - 1.0 / double(spins)) * 0.01) {
+                    changes = 0;
+                } else {
+                    changes = 1;
+                }
+            }
+        } else {
+            if (parupdate) {
+                changes = pm->HeatBathParallelLookupZeroTemp(gamma, prob, 50);
+            } else {
+                acc = pm->HeatBathLookupZeroTemp(gamma, prob, 50);
+                /* less than 1 percent acceptance ratio */
+                if (acc < (1.0 - 1.0 / double(spins)) * 0.01) {
+                    changes = 0;
+                } else {
+                    changes = 1;
+                }
+            }
+        }
+    } /* while loop */
+
+    pm->WriteClusters(modularity, temperature, csize, membership, kT, gamma);
+
+    while (net->link_list->Size()) {
+        delete net->link_list->Pop();
+    }
+    while (net->node_list->Size()) {
+        delete net->node_list->Pop();
+    }
+    while (net->cluster_list->Size()) {
+        cl_cur = net->cluster_list->Pop();
+        while (cl_cur->Size()) {
+            cl_cur->Pop();
+        }
+        delete cl_cur;
+    }
+    delete net->link_list;
+    delete net->node_list;
+    delete net->cluster_list;
+
+    RNG_END();
+
+    delete net;
+    delete pm;
+
+    return 0;
+}
+
+/**
+ * \function igraph_community_spinglass_single
+ * \brief Community of a single node based on statistical mechanics
+ *
+ * This function implements the community structure detection
+ * algorithm proposed by Joerg Reichardt and Stefan Bornholdt. It is
+ * described in their paper: Statistical Mechanics of
+ * Community Detection, http://arxiv.org/abs/cond-mat/0603718 .
+ *
+ * </para><para>
+ * This function calculates the community of a single vertex without
+ * calculating all the communities in the graph.
+ *
+ * \param graph The input graph, it may be directed but the direction
+ *    of the edges is not used in the algorithm.
+ * \param weights Pointer to a vector with the weights of the edges.
+ *    Alternatively \c NULL can be supplied to have the same weight
+ *    for every edge.
+ * \param vertex The vertex id of the vertex of which ths community is
+ *    calculated.
+ * \param community Pointer to an initialized vector, the result, the
+ *    ids of the vertices in the community of the input vertex will be
+ *    stored here. The vector will be resized as needed.
+ * \param cohesion Pointer to a real variable, if not \c NULL the
+ *     cohesion index of the community will be stored here.
+ * \param adhesion Pointer to a real variable, if not \c NULL the
+ *     adhesion index of the community will be stored here.
+ * \param inner_links Pointer to an integer, if not \c NULL the
+ *     number of edges within the community is stored here.
+ * \param outer_links Pointer to an integer, if not \c NULL the
+ *     number of edges between the community and the rest of the graph
+ *     will be stored here.
+ * \param spins The number of spins to use, this can be higher than
+ *    the actual number of clusters in the network, in which case some
+ *    clusters will contain zero vertices.
+ * \param update_rule The type of the update rule. Possible values: \c
+ *     IGRAPH_SPINCOMM_UPDATE_SIMPLE and \c
+ *     IGRAPH_SPINCOMM_UPDATE_CONFIG. Basically this parameter defined
+ *     the null model based on which the actual clustering is done. If
+ *     this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph
+ *     (ie. G(n,p)), if it is \c IGRAPH_SPINCOMM_UPDATE then the
+ *     configuration model is used. The configuration means that the
+ *     baseline for the clustering is a random graph with the same
+ *     degree distribution as the input graph.
+ * \param gamma Real number. The gamma parameter of the
+ *     algorithm. This defined the weight of the missing and existing
+ *     links in the quality function for the clustering. The default
+ *     value in the original code was 1.0, which is equal weight to
+ *     missing and existing edges. Smaller values make the existing
+ *     links contibute more to the energy function which is minimized
+ *     in the algorithm. Bigger values make the missing links more
+ *     important. (If my understanding is correct.)
+ * \return Error code.
+ *
+ * \sa igraph_community_spinglass() for the traditional version of the
+ * algorithm.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_community_spinglass_single(const igraph_t *graph,
+                                      const igraph_vector_t *weights,
+                                      igraph_integer_t vertex,
+                                      igraph_vector_t *community,
+                                      igraph_real_t *cohesion,
+                                      igraph_real_t *adhesion,
+                                      igraph_integer_t *inner_links,
+                                      igraph_integer_t *outer_links,
+                                      igraph_integer_t spins,
+                                      igraph_spincomm_update_t update_rule,
+                                      igraph_real_t gamma) {
+
+    igraph_bool_t use_weights = 0;
+    double prob;
+    ClusterList<NNode*> *cl_cur;
+    network *net;
+    PottsModel *pm;
+    char startnode[255];
+
+    /* Check arguments */
+
+    if (spins < 2 || spins > 500) {
+        IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL);
+    }
+    if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE &&
+        update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) {
+        IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL);
+    }
+    if (weights) {
+        if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+            IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+        }
+        use_weights = 1;
+    }
+    if (gamma < 0.0) {
+        IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL);
+    }
+    if (vertex < 0 || vertex > igraph_vcount(graph)) {
+        IGRAPH_ERROR("Invalid vertex id", IGRAPH_EINVAL);
+    }
+
+    /* Check whether we have a single component */
+    igraph_bool_t conn;
+    IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
+    if (!conn) {
+        IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL);
+    }
+
+    net = new network;
+    net->node_list   = new DL_Indexed_List<NNode*>();
+    net->link_list   = new DL_Indexed_List<NLink*>();
+    net->cluster_list = new DL_Indexed_List<ClusterList<NNode*>*>();
+
+    /* Transform the igraph_t */
+    IGRAPH_CHECK(igraph_i_read_network(graph, weights,
+                                       net, use_weights, 0));
+
+    prob = 2.0 * net->sum_weights / double(net->node_list->Size())
+           / double(net->node_list->Size() - 1);
+
+    pm = new PottsModel(net, (unsigned int)spins, update_rule);
+
+    /* initialize the random number generator */
+    RNG_BEGIN();
+
+    /* to be exected, if we want to find the community around a particular node*/
+    /* the initial conf is needed, because otherwise,
+       the degree of the nodes is not in the weight property, stupid!!! */
+    pm->assign_initial_conf(-1);
+    snprintf(startnode, 255, "%li", (long int)vertex + 1);
+    pm->FindCommunityFromStart(gamma, prob, startnode, community,
+                               cohesion, adhesion, inner_links, outer_links);
+
+    while (net->link_list->Size()) {
+        delete net->link_list->Pop();
+    }
+    while (net->node_list->Size()) {
+        delete net->node_list->Pop();
+    }
+    while (net->cluster_list->Size()) {
+        cl_cur = net->cluster_list->Pop();
+        while (cl_cur->Size()) {
+            cl_cur->Pop();
+        }
+        delete cl_cur;
+    }
+    delete net->link_list;
+    delete net->node_list;
+    delete net->cluster_list;
+
+    RNG_END();
+
+    delete net;
+    delete pm;
+
+    return 0;
+}
+
+int igraph_i_community_spinglass_negative(const igraph_t *graph,
+        const igraph_vector_t *weights,
+        igraph_real_t *modularity,
+        igraph_real_t *temperature,
+        igraph_vector_t *membership,
+        igraph_vector_t *csize,
+        igraph_integer_t spins,
+        igraph_bool_t parupdate,
+        igraph_real_t starttemp,
+        igraph_real_t stoptemp,
+        igraph_real_t coolfact,
+        igraph_spincomm_update_t update_rule,
+        igraph_real_t gamma,
+        /*                    igraph_matrix_t *adhesion, */
+        /*                    igraph_matrix_t *normalised_adhesion, */
+        /*                    igraph_real_t *polarization, */
+        igraph_real_t gamma_minus) {
+
+    unsigned long changes, runs;
+    igraph_bool_t use_weights = 0;
+    bool zeroT;
+    double kT, acc;
+    ClusterList<NNode*> *cl_cur;
+    network *net;
+    PottsModelN *pm;
+    igraph_real_t d_n;
+    igraph_real_t d_p;
+
+    /* Check arguments */
+
+    if (parupdate) {
+        IGRAPH_ERROR("Parallel spin update not implemented with "
+                     "negative gamma", IGRAPH_UNIMPLEMENTED);
+    }
+
+    if (spins < 2 || spins > 500) {
+        IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL);
+    }
+    if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE &&
+        update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) {
+        IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL);
+    }
+    if (weights) {
+        if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+            IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+        }
+        use_weights = 1;
+    }
+    if (coolfact < 0 || coolfact >= 1.0) {
+        IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL);
+    }
+    if (gamma < 0.0) {
+        IGRAPH_ERROR("Invalid gamma value", IGRAPH_EINVAL);
+    }
+    if (starttemp / stoptemp < 1.0) {
+        IGRAPH_ERROR("starttemp should be larger in absolute value than stoptemp",
+                     IGRAPH_EINVAL);
+    }
+
+    /* Check whether we have a single component */
+    igraph_bool_t conn;
+    IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
+    if (!conn) {
+        IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL);
+    }
+
+    if (weights) {
+        igraph_vector_minmax(weights, &d_n, &d_p);
+    } else {
+        d_n = d_p = 1;
+    }
+
+    if (d_n > 0) {
+        d_n = 0;
+    }
+    if (d_p < 0) {
+        d_p = 0;
+    }
+    d_n = -d_n;
+
+    net = new network;
+    net->node_list   = new DL_Indexed_List<NNode*>();
+    net->link_list   = new DL_Indexed_List<NLink*>();
+    net->cluster_list = new DL_Indexed_List<ClusterList<NNode*>*>();
+
+    /* Transform the igraph_t */
+    IGRAPH_CHECK(igraph_i_read_network(graph, weights,
+                                       net, use_weights, 0));
+
+    bool directed = igraph_is_directed(graph);
+
+    pm = new PottsModelN(net, (unsigned int)spins, directed);
+
+    /* initialize the random number generator */
+    RNG_BEGIN();
+
+    if ((stoptemp == 0.0) && (starttemp == 0.0)) {
+        zeroT = true;
+    } else {
+        zeroT = false;
+    }
+
+    //Begin at a high enough temperature
+    kT = pm->FindStartTemp(gamma, gamma_minus, starttemp);
+
+    /* assign random initial configuration */
+    pm->assign_initial_conf(true);
+
+    runs = 0;
+    changes = 1;
+    acc = 0;
+    while (changes > 0 && (kT / stoptemp > 1.0 || (zeroT && runs < 150))) {
+
+        IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */
+
+        runs++;
+        kT = kT * coolfact;
+        acc = pm->HeatBathLookup(gamma, gamma_minus, kT, 50);
+        if (acc < (1.0 - 1.0 / double(spins)) * 0.001) {
+            changes = 0;
+        } else {
+            changes = 1;
+        }
+
+    } /* while loop */
+
+    /* These are needed, otherwise 'modularity' is not calculated */
+    igraph_matrix_t adhesion, normalized_adhesion;
+    igraph_real_t polarization;
+    IGRAPH_MATRIX_INIT_FINALLY(&adhesion, 0, 0);
+    IGRAPH_MATRIX_INIT_FINALLY(&normalized_adhesion, 0, 0);
+    pm->WriteClusters(modularity, temperature, csize, membership,
+                      &adhesion, &normalized_adhesion, &polarization,
+                      kT, d_p, d_n, gamma, gamma_minus);
+    igraph_matrix_destroy(&normalized_adhesion);
+    igraph_matrix_destroy(&adhesion);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    while (net->link_list->Size()) {
+        delete net->link_list->Pop();
+    }
+    while (net->node_list->Size()) {
+        delete net->node_list->Pop();
+    }
+    while (net->cluster_list->Size()) {
+        cl_cur = net->cluster_list->Pop();
+        while (cl_cur->Size()) {
+            cl_cur->Pop();
+        }
+        delete cl_cur;
+    }
+
+    RNG_END();
+
+    return 0;
+}
diff --git a/igraph/src/cocitation.c b/igraph/src/cocitation.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cocitation.c
@@ -0,0 +1,780 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph R package.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_cocitation.h"
+#include "igraph_memory.h"
+#include "igraph_adjlist.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_interface.h"
+#include "config.h"
+#include <math.h>
+
+int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res,
+                           igraph_vs_t vids, igraph_neimode_t mode,
+                           igraph_vector_t *weights);
+
+/**
+ * \ingroup structural
+ * \function igraph_cocitation
+ * \brief Cocitation coupling.
+ *
+ * </para><para>
+ * Two vertices are cocited if there is another vertex citing both of
+ * them. \ref igraph_cocitation() simply counts how many times two vertices are
+ * cocited.
+ * The cocitation score for each given vertex and all other vertices
+ * in the graph will be calculated.
+ * \param graph The graph object to analyze.
+ * \param res Pointer to a matrix, the result of the calculation will
+ *        be stored here. The number of its rows is the same as the
+ *        number of vertex ids in \p vids, the number of
+ *        columns is the number of vertices in the graph.
+ * \param vids The vertex ids of the vertices for which the
+ *        calculation will be done.
+ * \return Error code:
+ *         \c IGRAPH_EINVVID: invalid vertex id.
+ *
+ * Time complexity: O(|V|d^2), |V| is
+ * the number of vertices in the graph,
+ * d is the (maximum) degree of
+ * the vertices in the graph.
+ *
+ * \sa \ref igraph_bibcoupling()
+ *
+ * \example examples/simple/igraph_cocitation.c
+ */
+
+int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res,
+                      const igraph_vs_t vids) {
+    return igraph_cocitation_real(graph, res, vids, IGRAPH_OUT, 0);
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_bibcoupling
+ * \brief Bibliographic coupling.
+ *
+ * </para><para>
+ * The bibliographic coupling of two vertices is the number
+ * of other vertices they both cite, \ref igraph_bibcoupling() calculates
+ * this.
+ * The bibliographic coupling  score for each given vertex and all
+ * other vertices in the graph will be calculated.
+ * \param graph The graph object to analyze.
+ * \param res Pointer to a matrix, the result of the calculation will
+ *        be stored here. The number of its rows is the same as the
+ *        number of vertex ids in \p vids, the number of
+ *        columns is the number of vertices in the graph.
+ * \param vids The vertex ids of the vertices for which the
+ *        calculation will be done.
+ * \return Error code:
+ *         \c IGRAPH_EINVVID: invalid vertex id.
+ *
+ * Time complexity: O(|V|d^2),
+ * |V| is the number of vertices in
+ * the graph, d is the (maximum)
+ * degree of the vertices in the graph.
+ *
+ * \sa \ref igraph_cocitation()
+ */
+
+int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res,
+                       const igraph_vs_t vids) {
+    return igraph_cocitation_real(graph, res, vids, IGRAPH_IN, 0);
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_similarity_inverse_log_weighted
+ * \brief Vertex similarity based on the inverse logarithm of vertex degrees.
+ *
+ * </para><para>
+ * The inverse log-weighted similarity of two vertices is the number of
+ * their common neighbors, weighted by the inverse logarithm of their degrees.
+ * It is based on the assumption that two vertices should be considered
+ * more similar if they share a low-degree common neighbor, since high-degree
+ * common neighbors are more likely to appear even by pure chance.
+ *
+ * </para><para>
+ * Isolated vertices will have zero similarity to any other vertex.
+ * Self-similarities are not calculated.
+ *
+ * </para><para>
+ * See the following paper for more details: Lada A. Adamic and Eytan Adar:
+ * Friends and neighbors on the Web. Social Networks, 25(3):211-230, 2003.
+ *
+ * \param graph The graph object to analyze.
+ * \param res Pointer to a matrix, the result of the calculation will
+ *        be stored here. The number of its rows is the same as the
+ *        number of vertex ids in \p vids, the number of
+ *        columns is the number of vertices in the graph.
+ * \param vids The vertex ids of the vertices for which the
+ *        calculation will be done.
+ * \param mode The type of neighbors to be used for the calculation in
+ *        directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing edges will be considered for each node. Nodes
+ *          will be weighted according to their in-degree.
+ *        \cli IGRAPH_IN
+ *          the incoming edges will be considered for each node. Nodes
+ *          will be weighted according to their out-degree.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an undirected one for the
+ *          computation. Every node is weighted according to its undirected
+ *          degree.
+ *        \endclist
+ * \return Error code:
+ *         \c IGRAPH_EINVVID: invalid vertex id.
+ *
+ * Time complexity: O(|V|d^2),
+ * |V| is the number of vertices in
+ * the graph, d is the (maximum)
+ * degree of the vertices in the graph.
+ *
+ * \example examples/simple/igraph_similarity.c
+ */
+
+int igraph_similarity_inverse_log_weighted(const igraph_t *graph,
+        igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode) {
+    igraph_vector_t weights;
+    igraph_neimode_t mode0;
+    long int i, no_of_nodes;
+
+    switch (mode) {
+    case IGRAPH_OUT: mode0 = IGRAPH_IN; break;
+    case IGRAPH_IN: mode0 = IGRAPH_OUT; break;
+    default: mode0 = IGRAPH_ALL;
+    }
+
+    no_of_nodes = igraph_vcount(graph);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_nodes);
+    IGRAPH_CHECK(igraph_degree(graph, &weights, igraph_vss_all(), mode0, 1));
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(weights)[i] > 1) {
+            VECTOR(weights)[i] = 1.0 / log(VECTOR(weights)[i]);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_cocitation_real(graph, res, vids, mode0, &weights));
+    igraph_vector_destroy(&weights);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res,
+                           igraph_vs_t vids,
+                           igraph_neimode_t mode,
+                           igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_vids;
+    long int from, i, j, k, l, u, v;
+    igraph_vector_t neis = IGRAPH_VECTOR_NULL;
+    igraph_vector_t vid_reverse_index;
+    igraph_vit_t vit;
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    no_of_vids = IGRAPH_VIT_SIZE(vit);
+
+    /* Create a mapping from vertex IDs to the row of the matrix where
+     * the result for this vertex will appear */
+    IGRAPH_VECTOR_INIT_FINALLY(&vid_reverse_index, no_of_nodes);
+    igraph_vector_fill(&vid_reverse_index, -1);
+    for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        v = IGRAPH_VIT_GET(vit);
+        if (v < 0 || v >= no_of_nodes) {
+            IGRAPH_ERROR("invalid vertex ID in vertex selector", IGRAPH_EINVAL);
+        }
+        VECTOR(vid_reverse_index)[v] = i;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_vids, no_of_nodes));
+    igraph_matrix_null(res);
+
+    /* The result */
+
+    for (from = 0; from < no_of_nodes; from++) {
+        igraph_real_t weight = 1;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis,
+                                      (igraph_integer_t) from, mode));
+        if (weights) {
+            weight = VECTOR(*weights)[from];
+        }
+
+        for (i = 0; i < igraph_vector_size(&neis) - 1; i++) {
+            u = (long int) VECTOR(neis)[i];
+            k = (long int) VECTOR(vid_reverse_index)[u];
+            for (j = i + 1; j < igraph_vector_size(&neis); j++) {
+                v = (long int) VECTOR(neis)[j];
+                l = (long int) VECTOR(vid_reverse_index)[v];
+                if (k != -1) {
+                    MATRIX(*res, k, v) += weight;
+                }
+                if (l != -1) {
+                    MATRIX(*res, l, u) += weight;
+                }
+            }
+        }
+    }
+
+    /* Clean up */
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&vid_reverse_index);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+int igraph_i_neisets_intersect(const igraph_vector_t *v1,
+                               const igraph_vector_t *v2, long int *len_union,
+                               long int *len_intersection);
+
+int igraph_i_neisets_intersect(const igraph_vector_t *v1,
+                               const igraph_vector_t *v2, long int *len_union,
+                               long int *len_intersection) {
+    /* ASSERT: v1 and v2 are sorted */
+    long int i, j, i0, jj0;
+    i0 = igraph_vector_size(v1); jj0 = igraph_vector_size(v2);
+    *len_union = i0 + jj0; *len_intersection = 0;
+    i = 0; j = 0;
+    while (i < i0 && j < jj0) {
+        if (VECTOR(*v1)[i] == VECTOR(*v2)[j]) {
+            (*len_intersection)++; (*len_union)--;
+            i++; j++;
+        } else if (VECTOR(*v1)[i] < VECTOR(*v2)[j]) {
+            i++;
+        } else {
+            j++;
+        }
+    }
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_similarity_jaccard
+ * \brief Jaccard similarity coefficient for the given vertices.
+ *
+ * </para><para>
+ * The Jaccard similarity coefficient of two vertices is the number of common
+ * neighbors divided by the number of vertices that are neighbors of at
+ * least one of the two vertices being considered. This function calculates
+ * the pairwise Jaccard similarities for some (or all) of the vertices.
+ *
+ * \param graph The graph object to analyze
+ * \param res Pointer to a matrix, the result of the calculation will
+ *        be stored here. The number of its rows and columns is the same
+ *        as the number of vertex ids in \p vids.
+ * \param vids The vertex ids of the vertices for which the
+ *        calculation will be done.
+ * \param mode The type of neighbors to be used for the calculation in
+ *        directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing edges will be considered for each node.
+ *        \cli IGRAPH_IN
+ *          the incoming edges will be considered for each node.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an undirected one for the
+ *          computation.
+ *        \endclist
+ * \param loops Whether to include the vertices themselves in the neighbor
+ *        sets.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(|V|^2 d),
+ * |V| is the number of vertices in the vertex iterator given, d is the
+ * (maximum) degree of the vertices in the graph.
+ *
+ * \sa \ref igraph_similarity_dice(), a measure very similar to the Jaccard
+ *   coefficient
+ *
+ * \example examples/simple/igraph_similarity.c
+ */
+int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res,
+                              const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) {
+    igraph_lazy_adjlist_t al;
+    igraph_vit_t vit, vit2;
+    long int i, j, k;
+    long int len_union, len_intersection;
+    igraph_vector_t *v1, *v2;
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit2));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit2);
+
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, mode, IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al);
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, IGRAPH_VIT_SIZE(vit), IGRAPH_VIT_SIZE(vit)));
+
+    if (loops) {
+        for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+            i = IGRAPH_VIT_GET(vit);
+            v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) i);
+            if (!igraph_vector_binsearch(v1, i, &k)) {
+                igraph_vector_insert(v1, k, i);
+            }
+        }
+    }
+
+    for (IGRAPH_VIT_RESET(vit), i = 0;
+         !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        MATRIX(*res, i, i) = 1.0;
+        for (IGRAPH_VIT_RESET(vit2), j = 0;
+             !IGRAPH_VIT_END(vit2); IGRAPH_VIT_NEXT(vit2), j++) {
+            if (j <= i) {
+                continue;
+            }
+            v1 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit));
+            v2 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit2));
+            igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection);
+            if (len_union > 0) {
+                MATRIX(*res, i, j) = ((igraph_real_t)len_intersection) / len_union;
+            } else {
+                MATRIX(*res, i, j) = 0.0;
+            }
+            MATRIX(*res, j, i) = MATRIX(*res, i, j);
+        }
+    }
+
+    igraph_lazy_adjlist_destroy(&al);
+    igraph_vit_destroy(&vit);
+    igraph_vit_destroy(&vit2);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_similarity_jaccard_pairs
+ * \brief Jaccard similarity coefficient for given vertex pairs.
+ *
+ * </para><para>
+ * The Jaccard similarity coefficient of two vertices is the number of common
+ * neighbors divided by the number of vertices that are neighbors of at
+ * least one of the two vertices being considered. This function calculates
+ * the pairwise Jaccard similarities for a list of vertex pairs.
+ *
+ * \param graph The graph object to analyze
+ * \param res Pointer to a vector, the result of the calculation will
+ *        be stored here. The number of elements is the same as the number
+ *        of pairs in \p pairs.
+ * \param pairs A vector that contains the pairs for which the similarity
+ *        will be calculated. Each pair is defined by two consecutive elements,
+ *        i.e. the first and second element of the vector specifies the first
+ *        pair, the third and fourth element specifies the second pair and so on.
+ * \param mode The type of neighbors to be used for the calculation in
+ *        directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing edges will be considered for each node.
+ *        \cli IGRAPH_IN
+ *          the incoming edges will be considered for each node.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an undirected one for the
+ *          computation.
+ *        \endclist
+ * \param loops Whether to include the vertices themselves in the neighbor
+ *        sets.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(nd), n is the number of pairs in the given vector, d is
+ * the (maximum) degree of the vertices in the graph.
+ *
+ * \sa \ref igraph_similarity_jaccard() to calculate the Jaccard similarity
+ *   between all pairs of a vertex set, or \ref igraph_similarity_dice() and
+ *   \ref igraph_similarity_dice_pairs() for a measure very similar to the
+ *   Jaccard coefficient
+ *
+ * \example examples/simple/igraph_similarity.c
+ */
+int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res,
+                                    const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) {
+    igraph_lazy_adjlist_t al;
+    long int i, j, k, u, v;
+    long int len_union, len_intersection;
+    igraph_vector_t *v1, *v2;
+    igraph_bool_t *seen;
+
+    k = igraph_vector_size(pairs);
+    if (k % 2 != 0) {
+        IGRAPH_ERROR("number of elements in `pairs' must be even", IGRAPH_EINVAL);
+    }
+    IGRAPH_CHECK(igraph_vector_resize(res, k / 2));
+
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, mode, IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al);
+
+    if (loops) {
+        /* Add the loop edges */
+        i = igraph_vcount(graph);
+        seen = igraph_Calloc(i, igraph_bool_t);
+        if (seen == 0) {
+            IGRAPH_ERROR("cannot calculate Jaccard similarity", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(free, seen);
+
+        for (i = 0; i < k; i++) {
+            j = (long int) VECTOR(*pairs)[i];
+            if (seen[j]) {
+                continue;
+            }
+            seen[j] = 1;
+            v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) j);
+            if (!igraph_vector_binsearch(v1, j, &u)) {
+                igraph_vector_insert(v1, u, j);
+            }
+        }
+
+        free(seen);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    for (i = 0, j = 0; i < k; i += 2, j++) {
+        u = (long int) VECTOR(*pairs)[i];
+        v = (long int) VECTOR(*pairs)[i + 1];
+
+        if (u == v) {
+            VECTOR(*res)[j] = 1.0;
+            continue;
+        }
+
+        v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) u);
+        v2 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) v);
+        igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection);
+        if (len_union > 0) {
+            VECTOR(*res)[j] = ((igraph_real_t)len_intersection) / len_union;
+        } else {
+            VECTOR(*res)[j] = 0.0;
+        }
+    }
+
+    igraph_lazy_adjlist_destroy(&al);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_similarity_jaccard_es
+ * \brief Jaccard similarity coefficient for a given edge selector.
+ *
+ * </para><para>
+ * The Jaccard similarity coefficient of two vertices is the number of common
+ * neighbors divided by the number of vertices that are neighbors of at
+ * least one of the two vertices being considered. This function calculates
+ * the pairwise Jaccard similarities for the endpoints of edges in a given edge
+ * selector.
+ *
+ * \param graph The graph object to analyze
+ * \param res Pointer to a vector, the result of the calculation will
+ *        be stored here. The number of elements is the same as the number
+ *        of edges in \p es.
+ * \param es An edge selector that specifies the edges to be included in the
+ *        result.
+ * \param mode The type of neighbors to be used for the calculation in
+ *        directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing edges will be considered for each node.
+ *        \cli IGRAPH_IN
+ *          the incoming edges will be considered for each node.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an undirected one for the
+ *          computation.
+ *        \endclist
+ * \param loops Whether to include the vertices themselves in the neighbor
+ *        sets.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(nd), n is the number of edges in the edge selector, d is
+ * the (maximum) degree of the vertices in the graph.
+ *
+ * \sa \ref igraph_similarity_jaccard() and \ref igraph_similarity_jaccard_pairs()
+ *   to calculate the Jaccard similarity between all pairs of a vertex set or
+ *   some selected vertex pairs, or \ref igraph_similarity_dice(),
+ *   \ref igraph_similarity_dice_pairs() and \ref igraph_similarity_dice_es() for a
+ *   measure very similar to the Jaccard coefficient
+ *
+ * \example examples/simple/igraph_similarity.c
+ */
+int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res,
+                                 const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops) {
+    igraph_vector_t v;
+    igraph_eit_t eit;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&v, 0);
+
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+    while (!IGRAPH_EIT_END(eit)) {
+        long int eid = IGRAPH_EIT_GET(eit);
+        igraph_vector_push_back(&v, IGRAPH_FROM(graph, eid));
+        igraph_vector_push_back(&v, IGRAPH_TO(graph, eid));
+        IGRAPH_EIT_NEXT(eit);
+    }
+
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, &v, mode, loops));
+    igraph_vector_destroy(&v);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_similarity_dice
+ * \brief Dice similarity coefficient.
+ *
+ * </para><para>
+ * The Dice similarity coefficient of two vertices is twice the number of common
+ * neighbors divided by the sum of the degrees of the vertices. This function
+ * calculates the pairwise Dice similarities for some (or all) of the vertices.
+ *
+ * \param graph The graph object to analyze
+ * \param res Pointer to a matrix, the result of the calculation will
+ *        be stored here. The number of its rows and columns is the same
+ *        as the number of vertex ids in \p vids.
+ * \param vids The vertex ids of the vertices for which the
+ *        calculation will be done.
+ * \param mode The type of neighbors to be used for the calculation in
+ *        directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing edges will be considered for each node.
+ *        \cli IGRAPH_IN
+ *          the incoming edges will be considered for each node.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an undirected one for the
+ *          computation.
+ *        \endclist
+ * \param loops Whether to include the vertices themselves as their own
+ *        neighbors.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(|V|^2 d),
+ * |V| is the number of vertices in the vertex iterator given, d is the
+ * (maximum) degree of the vertices in the graph.
+ *
+ * \sa \ref igraph_similarity_jaccard(), a measure very similar to the Dice
+ *   coefficient
+ *
+ * \example examples/simple/igraph_similarity.c
+ */
+int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res,
+                           const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) {
+    long int i, j, nr, nc;
+
+    IGRAPH_CHECK(igraph_similarity_jaccard(graph, res, vids, mode, loops));
+
+    nr = igraph_matrix_nrow(res);
+    nc = igraph_matrix_ncol(res);
+    for (i = 0; i < nr; i++) {
+        for (j = 0; j < nc; j++) {
+            igraph_real_t x = MATRIX(*res, i, j);
+            MATRIX(*res, i, j) = 2 * x / (1 + x);
+        }
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_similarity_dice_pairs
+ * \brief Dice similarity coefficient for given vertex pairs.
+ *
+ * </para><para>
+ * The Dice similarity coefficient of two vertices is twice the number of common
+ * neighbors divided by the sum of the degrees of the vertices. This function
+ * calculates the pairwise Dice similarities for a list of vertex pairs.
+ *
+ * \param graph The graph object to analyze
+ * \param res Pointer to a vector, the result of the calculation will
+ *        be stored here. The number of elements is the same as the number
+ *        of pairs in \p pairs.
+ * \param pairs A vector that contains the pairs for which the similarity
+ *        will be calculated. Each pair is defined by two consecutive elements,
+ *        i.e. the first and second element of the vector specifies the first
+ *        pair, the third and fourth element specifies the second pair and so on.
+ * \param mode The type of neighbors to be used for the calculation in
+ *        directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing edges will be considered for each node.
+ *        \cli IGRAPH_IN
+ *          the incoming edges will be considered for each node.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an undirected one for the
+ *          computation.
+ *        \endclist
+ * \param loops Whether to include the vertices themselves as their own
+ *        neighbors.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(nd), n is the number of pairs in the given vector, d is
+ * the (maximum) degree of the vertices in the graph.
+ *
+ * \sa \ref igraph_similarity_dice() to calculate the Dice similarity
+ *   between all pairs of a vertex set, or \ref igraph_similarity_jaccard(),
+ *   \ref igraph_similarity_jaccard_pairs() and \ref igraph_similarity_jaccard_es()
+ *   for a measure very similar to the Dice coefficient
+ *
+ * \example examples/simple/igraph_similarity.c
+ */
+int igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res,
+                                 const igraph_vector_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) {
+    long int i, n;
+
+    IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, pairs, mode, loops));
+    n = igraph_vector_size(res);
+    for (i = 0; i < n; i++) {
+        igraph_real_t x = VECTOR(*res)[i];
+        VECTOR(*res)[i] = 2 * x / (1 + x);
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_similarity_dice_es
+ * \brief Dice similarity coefficient for a given edge selector.
+ *
+ * </para><para>
+ * The Dice similarity coefficient of two vertices is twice the number of common
+ * neighbors divided by the sum of the degrees of the vertices. This function
+ * calculates the pairwise Dice similarities for the endpoints of edges in a given
+ * edge selector.
+ *
+ * \param graph The graph object to analyze
+ * \param res Pointer to a vector, the result of the calculation will
+ *        be stored here. The number of elements is the same as the number
+ *        of edges in \p es.
+ * \param es An edge selector that specifies the edges to be included in the
+ *        result.
+ * \param mode The type of neighbors to be used for the calculation in
+ *        directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing edges will be considered for each node.
+ *        \cli IGRAPH_IN
+ *          the incoming edges will be considered for each node.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an undirected one for the
+ *          computation.
+ *        \endclist
+ * \param loops Whether to include the vertices themselves as their own
+ *        neighbors.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(nd), n is the number of pairs in the given vector, d is
+ * the (maximum) degree of the vertices in the graph.
+ *
+ * \sa \ref igraph_similarity_dice() and \ref igraph_similarity_dice_pairs()
+ *   to calculate the Dice similarity between all pairs of a vertex set or
+ *   some selected vertex pairs, or \ref igraph_similarity_jaccard(),
+ *   \ref igraph_similarity_jaccard_pairs() and \ref igraph_similarity_jaccard_es()
+ *   for a measure very similar to the Dice coefficient
+ *
+ * \example examples/simple/igraph_similarity.c
+ */
+int igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res,
+                              const igraph_es_t es, igraph_neimode_t mode, igraph_bool_t loops) {
+    long int i, n;
+
+    IGRAPH_CHECK(igraph_similarity_jaccard_es(graph, res, es, mode, loops));
+    n = igraph_vector_size(res);
+    for (i = 0; i < n; i++) {
+        igraph_real_t x = VECTOR(*res)[i];
+        VECTOR(*res)[i] = 2 * x / (1 + x);
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
diff --git a/igraph/src/cohesive_blocks.c b/igraph/src/cohesive_blocks.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cohesive_blocks.c
@@ -0,0 +1,612 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_cohesive_blocks.h"
+#include "igraph_interface.h"
+#include "igraph_memory.h"
+#include "igraph_flow.h"
+#include "igraph_separators.h"
+#include "igraph_structural.h"
+#include "igraph_components.h"
+#include "igraph_dqueue.h"
+#include "igraph_constructors.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_statusbar.h"
+
+void igraph_i_cohesive_blocks_free(igraph_vector_ptr_t *ptr) {
+    long int i, n = igraph_vector_ptr_size(ptr);
+
+    for (i = 0; i < n; i++) {
+        igraph_t *g = VECTOR(*ptr)[i];
+        if (g) {
+            igraph_destroy(g);
+            igraph_free(g);
+        }
+    }
+}
+
+void igraph_i_cohesive_blocks_free2(igraph_vector_ptr_t *ptr) {
+    long int i, n = igraph_vector_ptr_size(ptr);
+
+    for (i = 0; i < n; i++) {
+        igraph_vector_long_t *v = VECTOR(*ptr)[i];
+        if (v) {
+            igraph_vector_long_destroy(v);
+            igraph_free(v);
+        }
+    }
+}
+
+void igraph_i_cohesive_blocks_free3(igraph_vector_ptr_t *ptr) {
+    long int i, n = igraph_vector_ptr_size(ptr);
+
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *v = VECTOR(*ptr)[i];
+        if (v) {
+            igraph_vector_destroy(v);
+            igraph_free(v);
+        }
+    }
+}
+
+/* This is kind of a BFS to find the components of the graph, after
+ * deleting the vertices marked in 'excluded'.
+ * These vertices are not put in the BFS queue, but they are added to
+ * all neighboring components.
+ */
+
+int igraph_i_cb_components(igraph_t *graph,
+                           const igraph_vector_bool_t *excluded,
+                           igraph_vector_long_t *components,
+                           long int *no,
+                           /* working area follows */
+                           igraph_vector_long_t *compid,
+                           igraph_dqueue_t *Q,
+                           igraph_vector_t *neis) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+    long int cno = 0;
+
+    igraph_vector_long_clear(components);
+    igraph_dqueue_clear(Q);
+    IGRAPH_CHECK(igraph_vector_long_resize(compid, no_of_nodes));
+    igraph_vector_long_null(compid);
+
+    for (i = 0; i < no_of_nodes; i++) {
+
+        if (VECTOR(*compid)[i])   {
+            continue;
+        }
+        if (VECTOR(*excluded)[i]) {
+            continue;
+        }
+
+        IGRAPH_CHECK(igraph_dqueue_push(Q, i));
+        IGRAPH_CHECK(igraph_vector_long_push_back(components, i));
+        VECTOR(*compid)[i] = ++cno;
+
+        while (!igraph_dqueue_empty(Q)) {
+            igraph_integer_t node = (igraph_integer_t) igraph_dqueue_pop(Q);
+            long int j, n;
+            IGRAPH_CHECK(igraph_neighbors(graph, neis, node, IGRAPH_ALL));
+            n = igraph_vector_size(neis);
+            for (j = 0; j < n; j++) {
+                long int v = (long int) VECTOR(*neis)[j];
+                if (VECTOR(*excluded)[v]) {
+                    if (VECTOR(*compid)[v] != cno) {
+                        VECTOR(*compid)[v] = cno;
+                        IGRAPH_CHECK(igraph_vector_long_push_back(components, v));
+                    }
+                } else {
+                    if (!VECTOR(*compid)[v]) {
+                        VECTOR(*compid)[v] = cno; /* could be anything positive */
+                        IGRAPH_CHECK(igraph_vector_long_push_back(components, v));
+                        IGRAPH_CHECK(igraph_dqueue_push(Q, v));
+                    }
+                }
+            }
+        } /* while !igraph_dqueue_empty */
+
+        IGRAPH_CHECK(igraph_vector_long_push_back(components, -1));
+
+    } /* for i<no_of_nodes */
+
+    *no = cno;
+
+    return 0;
+}
+
+igraph_bool_t igraph_i_cb_isin(const igraph_vector_t *needle,
+                               const igraph_vector_t *haystack) {
+    long int nlen = igraph_vector_size(needle);
+    long int hlen = igraph_vector_size(haystack);
+    long int np = 0, hp = 0;
+
+    if (hlen < nlen) {
+        return 0;
+    }
+
+    while (np < nlen && hp < hlen) {
+        if (VECTOR(*needle)[np] == VECTOR(*haystack)[hp]) {
+            np++; hp++;
+        } else if (VECTOR(*needle)[np] < VECTOR(*haystack)[hp]) {
+            return 0;
+        } else {
+            hp++;
+        }
+    }
+
+    return np == nlen;
+}
+
+/**
+ * \function igraph_cohesive_blocks
+ * Identifies the hierarchical cohesive block structure of a graph
+ *
+ * Cohesive blocking is a method of determining hierarchical subsets of
+ * graph vertices based on their structural cohesion (or vertex
+ * connectivity). For a given graph G, a subset of its vertices
+ * S is said to be maximally k-cohesive if there is
+ * no superset of S with vertex connectivity greater than or equal to k.
+ * Cohesive blocking is a process through which, given a
+ * k-cohesive set of vertices, maximally l-cohesive subsets are
+ * recursively identified with l>k. Thus a hiearchy of vertex subsets
+ * is found, whith the entire graph G at its root. See the following
+ * reference for details: J. Moody and D. R. White. Structural
+ * cohesion and embeddedness: A hierarchical concept of social
+ * groups. American Sociological Review, 68(1):103--127, Feb 2003.
+ *
+ * </para><para>This function implements cohesive blocking and
+ * calculates the complete cohesive block hierarchy of a graph.
+ *
+ * \param graph The input graph. It must be undirected and simple. See
+ *    \ref igraph_is_simple().
+ * \param blocks If not a null pointer, then it must be an initialized
+ *    vector of pointers and the cohesive blocks are stored here.
+ *    Each block is encoded with a numeric vector, that contains the
+ *    vertex ids of the block.
+ * \param cohesion If not a null pointer, then it must be an initialized
+ *    vector and the cohesion of the blocks is stored here, in the same
+ *    order as the blocks in the \p blocks pointer vector.
+ * \param parent If not a null pointer, then it must be an initialized
+ *    vector and the block hierarchy is stored here. For each block, the
+ *    id (i.e. the position in the \p blocks pointer vector) of its
+ *    parent block is stored. For the top block in the hierarchy,
+ *    -1 is stored.
+ * \param block_tree If not a null pointer, then it must be a pointer
+ *    to an uninitialized graph, and the block hierarchy is stored
+ *    here as an igraph graph. The vertex ids correspond to the order
+ *    of the blocks in the \p blocks vector.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/cohesive_blocks.c
+ */
+
+int igraph_cohesive_blocks(const igraph_t *graph,
+                           igraph_vector_ptr_t *blocks,
+                           igraph_vector_t *cohesion,
+                           igraph_vector_t *parent,
+                           igraph_t *block_tree) {
+
+    /* Some implementation comments. Everything is relatively
+       straightforward, except, that we need to follow the vertex ids
+       of the various subgraphs, without having to store two-way
+       mappings at each level. The subgraphs can overlap, this
+       complicates things a bit.
+
+       The 'Q' vector is used as a double ended queue and it contains
+       the subgraphs to work on in the future. Some other vectors are
+       associated with it. 'Qparent' gives the parent graph of a graph
+       in Q. Qmapping gives the mapping of the vertices from the graph
+       to the parent graph. Qcohesion is the vertex connectivity of the
+       graph.
+
+       Qptr is an integer and points to the next graph to work on.
+    */
+
+    igraph_vector_ptr_t Q;
+    igraph_vector_ptr_t Qmapping;
+    igraph_vector_long_t Qparent;
+    igraph_vector_long_t Qcohesion;
+    igraph_vector_bool_t Qcheck;
+    long int Qptr = 0;
+    igraph_integer_t conn;
+    igraph_bool_t is_simple;
+
+    igraph_t *graph_copy;
+
+    igraph_vector_ptr_t separators;
+    igraph_vector_t compvertices;
+    igraph_vector_long_t components;
+    igraph_vector_bool_t marked;
+
+    igraph_vector_long_t compid;
+    igraph_dqueue_t bfsQ;
+    igraph_vector_t neis;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Cohesive blocking only works on undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_is_simple(graph, &is_simple));
+    if (!is_simple) {
+        IGRAPH_ERROR("Cohesive blocking only works on simple graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_STATUS("Starting cohesive block calculation.\n", 0);
+
+    if (blocks)   {
+        igraph_vector_ptr_clear(blocks);
+    }
+    if (cohesion) {
+        igraph_vector_clear(cohesion);
+    }
+    if (parent)   {
+        igraph_vector_clear(parent);
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&Q, 1));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &Q);
+    IGRAPH_FINALLY(igraph_i_cohesive_blocks_free, &Q);
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&Qmapping, 1));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &Qmapping);
+    IGRAPH_FINALLY(igraph_i_cohesive_blocks_free2, &Qmapping);
+
+    IGRAPH_CHECK(igraph_vector_long_init(&Qparent, 1));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &Qparent);
+
+    IGRAPH_CHECK(igraph_vector_long_init(&Qcohesion, 1));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &Qcohesion);
+
+    IGRAPH_CHECK(igraph_vector_bool_init(&Qcheck, 1));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &Qcheck);
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&separators, 0));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &separators);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&compvertices, 0);
+    IGRAPH_CHECK(igraph_vector_bool_init(&marked, 0));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &marked);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_dqueue_init(&bfsQ, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &bfsQ);
+    IGRAPH_CHECK(igraph_vector_long_init(&compid, 0));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &compid);
+    IGRAPH_CHECK(igraph_vector_long_init(&components, 0));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &components);
+
+    /* Put the input graph in the queue */
+    graph_copy = igraph_Calloc(1, igraph_t);
+    if (!graph_copy) {
+        IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM);
+    }
+    IGRAPH_CHECK(igraph_copy(graph_copy, graph));
+    VECTOR(Q)[0] = graph_copy;
+    VECTOR(Qmapping)[0] = 0;  /* Identity mapping */
+    VECTOR(Qparent)[0] = -1;  /* Has no parent */
+    IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /*checks=*/ 1));
+    VECTOR(Qcohesion)[0] = conn;
+    VECTOR(Qcheck)[0] = 0;
+
+    /* Then work until the queue is empty */
+    while (Qptr < igraph_vector_ptr_size(&Q)) {
+        igraph_t *mygraph = VECTOR(Q)[Qptr];
+        igraph_bool_t mycheck = VECTOR(Qcheck)[Qptr];
+        long int mynodes = igraph_vcount(mygraph);
+        long int i, nsep;
+        long int no, kept = 0;
+        long int cptr = 0;
+        long int nsepv = 0;
+        igraph_bool_t addedsep = 0;
+
+        IGRAPH_STATUSF(("Candidate %li: %li vertices,",
+                        0, Qptr, mynodes));
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /* Get the separators */
+        IGRAPH_CHECK(igraph_minimum_size_separators(mygraph, &separators));
+        IGRAPH_FINALLY(igraph_i_cohesive_blocks_free3, &separators);
+        nsep = igraph_vector_ptr_size(&separators);
+
+        IGRAPH_STATUSF((" %li separators,", 0, nsep));
+
+        /* Remove them from the graph, also mark them */
+        IGRAPH_CHECK(igraph_vector_bool_resize(&marked, mynodes));
+        igraph_vector_bool_null(&marked);
+        for (i = 0; i < nsep; i++) {
+            igraph_vector_t *v = VECTOR(separators)[i];
+            long int j, n = igraph_vector_size(v);
+            for (j = 0; j < n; j++) {
+                long int vv = (long int) VECTOR(*v)[j];
+                if (!VECTOR(marked)[vv]) {
+                    nsepv++;
+                    VECTOR(marked)[vv] = 1;
+                }
+            }
+        }
+
+        /* Find the connected components, omitting the separator vertices,
+           but including the neighboring separator vertices
+         */
+        IGRAPH_CHECK(igraph_i_cb_components(mygraph, &marked,
+                                            &components, &no,
+                                            &compid, &bfsQ, &neis));
+
+        /* Add the separator vertices themselves, as another component,
+           but only if there is at least one vertex not included in any
+           separator. */
+        if (nsepv != mynodes) {
+            addedsep = 1;
+            for (i = 0; i < mynodes; i++) {
+                if (VECTOR(marked)[i]) {
+                    IGRAPH_CHECK(igraph_vector_long_push_back(&components, i));
+                }
+            }
+            IGRAPH_CHECK(igraph_vector_long_push_back(&components, -1));
+            no++;
+        }
+
+        IGRAPH_STATUSF((" %li new candidates,", 0, no));
+
+        for (i = 0; i < no; i++) {
+            igraph_vector_t *newmapping;
+            igraph_t *newgraph;
+            igraph_integer_t maxdeg;
+
+            igraph_vector_clear(&compvertices);
+
+            while (1) {
+                long int v = VECTOR(components)[cptr++];
+                if (v < 0) {
+                    break;
+                }
+                IGRAPH_CHECK(igraph_vector_push_back(&compvertices, v));
+            }
+
+            newmapping = igraph_Calloc(1, igraph_vector_t);
+            if (!newmapping) {
+                IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, newmapping);
+            IGRAPH_VECTOR_INIT_FINALLY(newmapping, 0);
+            newgraph = igraph_Calloc(1, igraph_t);
+            if (!newgraph) {
+                IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, newgraph);
+            IGRAPH_CHECK(igraph_induced_subgraph_map(mygraph, newgraph,
+                         igraph_vss_vector(&compvertices),
+                         IGRAPH_SUBGRAPH_AUTO,
+                         /*map=*/ 0,
+                         /*invmap=*/ newmapping));
+            IGRAPH_FINALLY(igraph_destroy, newgraph);
+
+            IGRAPH_CHECK(igraph_maxdegree(newgraph, &maxdeg, igraph_vss_all(),
+                                          IGRAPH_ALL, IGRAPH_LOOPS));
+            if (maxdeg > VECTOR(Qcohesion)[Qptr]) {
+                igraph_integer_t newconn;
+                kept++;
+                IGRAPH_CHECK(igraph_vector_ptr_push_back(&Q, newgraph));
+                IGRAPH_FINALLY_CLEAN(2);
+                IGRAPH_CHECK(igraph_vector_ptr_push_back(&Qmapping, newmapping));
+                IGRAPH_FINALLY_CLEAN(2);
+                IGRAPH_CHECK(igraph_vertex_connectivity(newgraph, &newconn,
+                                                        /*checks=*/ 1));
+                IGRAPH_CHECK(igraph_vector_long_push_back(&Qcohesion, newconn));
+                IGRAPH_CHECK(igraph_vector_long_push_back(&Qparent, Qptr));
+                IGRAPH_CHECK(igraph_vector_bool_push_back(&Qcheck,
+                             mycheck || addedsep));
+            } else {
+                igraph_destroy(newgraph);
+                igraph_free(newgraph);
+                igraph_vector_destroy(newmapping);
+                igraph_free(newmapping);
+                IGRAPH_FINALLY_CLEAN(4);
+            }
+        }
+
+        IGRAPH_STATUSF((" keeping %li.\n", 0, kept));
+
+        igraph_destroy(mygraph);
+        igraph_free(mygraph);
+        VECTOR(Q)[Qptr] = 0;
+        igraph_i_cohesive_blocks_free3(&separators);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        Qptr++;
+    }
+
+    igraph_vector_long_destroy(&components);
+    igraph_vector_long_destroy(&compid);
+    igraph_dqueue_destroy(&bfsQ);
+    igraph_vector_destroy(&neis);
+    igraph_vector_bool_destroy(&marked);
+    igraph_vector_destroy(&compvertices);
+    igraph_vector_ptr_destroy(&separators);
+    IGRAPH_FINALLY_CLEAN(7);
+
+    if (blocks || cohesion || parent || block_tree) {
+        igraph_integer_t noblocks = (igraph_integer_t) Qptr, badblocks = 0;
+        igraph_vector_bool_t removed;
+        long int i, resptr = 0;
+        igraph_vector_long_t rewritemap;
+
+        IGRAPH_CHECK(igraph_vector_bool_init(&removed, noblocks));
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed);
+        IGRAPH_CHECK(igraph_vector_long_init(&rewritemap, noblocks));
+        IGRAPH_FINALLY(igraph_vector_long_destroy, &rewritemap);
+
+        for (i = 1; i < noblocks; i++) {
+            long int p = VECTOR(Qparent)[i];
+            while (VECTOR(removed)[p]) {
+                p = VECTOR(Qparent)[p];
+            }
+            if (VECTOR(Qcohesion)[p] >= VECTOR(Qcohesion)[i]) {
+                VECTOR(removed)[i] = 1;
+                badblocks++;
+            }
+        }
+
+        /* Rewrite the mappings */
+        for (i = 1; i < Qptr; i++) {
+            long int p = VECTOR(Qparent)[i];
+            igraph_vector_t *mapping = VECTOR(Qmapping)[i];
+            igraph_vector_t *pmapping = VECTOR(Qmapping)[p];
+            long int j, n = igraph_vector_size(mapping);
+
+            if (!pmapping) {
+                continue;
+            }
+            for (j = 0; j < n; j++) {
+                long int v = (long int) VECTOR(*mapping)[j];
+                VECTOR(*mapping)[j] = VECTOR(*pmapping)[v];
+            }
+        }
+
+        /* Because we also put the separator vertices in the queue, it is
+           not ensured that the found blocks are not subsets of each other.
+           We check this now. */
+        for (i = 1; i < noblocks; i++) {
+            long int j, ic;
+            igraph_vector_t *ivec;
+            if (!VECTOR(Qcheck)[i] || VECTOR(removed)[i]) {
+                continue;
+            }
+            ivec = VECTOR(Qmapping)[i];
+            ic = VECTOR(Qcohesion)[i];
+            for (j = 1; j < noblocks; j++) {
+                igraph_vector_t *jvec;
+                long int jc;
+                if (j == i || !VECTOR(Qcheck)[j] || VECTOR(removed)[j]) {
+                    continue;
+                }
+                jvec = VECTOR(Qmapping)[j];
+                jc = VECTOR(Qcohesion)[j];
+                if (igraph_i_cb_isin(ivec, jvec) && jc >= ic) {
+                    badblocks++;
+                    VECTOR(removed)[i] = 1;
+                    break;
+                }
+            }
+        }
+
+        noblocks -= badblocks;
+
+        if (blocks) {
+            IGRAPH_CHECK(igraph_vector_ptr_resize(blocks, noblocks));
+        }
+        if (cohesion) {
+            IGRAPH_CHECK(igraph_vector_resize(cohesion, noblocks));
+        }
+        if (parent) {
+            IGRAPH_CHECK(igraph_vector_resize(parent, noblocks));
+        }
+
+        for (i = 0; i < Qptr; i++) {
+            if (VECTOR(removed)[i]) {
+                IGRAPH_STATUSF(("Candidate %li ignored.\n", 0, i));
+                continue;
+            } else {
+                IGRAPH_STATUSF(("Candidate %li is a cohesive (sub)block\n", 0, i));
+            }
+            VECTOR(rewritemap)[i] = resptr;
+            if (cohesion) {
+                VECTOR(*cohesion)[resptr] = VECTOR(Qcohesion)[i];
+            }
+            if (parent || block_tree) {
+                long int p = VECTOR(Qparent)[i];
+                while (p >= 0 && VECTOR(removed)[p]) {
+                    p = VECTOR(Qparent)[p];
+                }
+                if (p >= 0) {
+                    p = VECTOR(rewritemap)[p];
+                }
+                VECTOR(Qparent)[i] = p;
+                if (parent) {
+                    VECTOR(*parent)[resptr] = p;
+                }
+            }
+            if (blocks) {
+                VECTOR(*blocks)[resptr] = VECTOR(Qmapping)[i];
+                VECTOR(Qmapping)[i] = 0;
+            }
+            resptr++;
+        }
+
+        /* Plus the original graph */
+        if (blocks) {
+            igraph_vector_t *orig = igraph_Calloc(1, igraph_vector_t);
+            if (!orig) {
+                IGRAPH_ERROR("Cannot do cohesive blocking", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, orig);
+            IGRAPH_CHECK(igraph_vector_init_seq(orig, 0, igraph_vcount(graph) - 1));
+            VECTOR(*blocks)[0] = orig;
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        if (block_tree) {
+            igraph_vector_t edges;
+            long int eptr = 0;
+            IGRAPH_VECTOR_INIT_FINALLY(&edges, noblocks * 2 - 2);
+            for (i = 1; i < Qptr; i++) {
+                if (VECTOR(removed)[i]) {
+                    continue;
+                }
+                VECTOR(edges)[eptr++] = VECTOR(Qparent)[i];
+                VECTOR(edges)[eptr++] = VECTOR(rewritemap)[i];
+            }
+
+            IGRAPH_CHECK(igraph_create(block_tree, &edges, noblocks,
+                                       IGRAPH_DIRECTED));
+            igraph_vector_destroy(&edges);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        igraph_vector_long_destroy(&rewritemap);
+        igraph_vector_bool_destroy(&removed);
+        IGRAPH_FINALLY_CLEAN(2);
+
+    }
+
+    igraph_vector_bool_destroy(&Qcheck);
+    igraph_vector_long_destroy(&Qcohesion);
+    igraph_vector_long_destroy(&Qparent);
+    igraph_i_cohesive_blocks_free2(&Qmapping);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    igraph_vector_ptr_destroy(&Qmapping);
+    igraph_vector_ptr_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(3);      /* + the elements of Q, they were
+                   already destroyed */
+
+    IGRAPH_STATUS("Cohesive blocking done.\n", 0);
+
+    return 0;
+}
diff --git a/igraph/src/coloring.c b/igraph/src/coloring.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/coloring.c
@@ -0,0 +1,142 @@
+
+#include "igraph_coloring.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_types_internal.h"
+
+
+int igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_t *colors) {
+    long i, vertex, maxdeg;
+    long vc = igraph_vcount(graph);
+    igraph_2wheap_t cn; /* indexed heap storing number of already coloured neighbours */
+    igraph_vector_int_t neigh_colors;
+    igraph_adjlist_t adjlist;
+
+    IGRAPH_CHECK(igraph_vector_int_resize(colors, vc));
+    igraph_vector_int_fill(colors, 0);
+
+    /* Nothing to do for 0 or 1 vertices.
+     * Remember that colours are integers starting from 0,
+     * and the 'colors' vector is already 0-initialized above.
+     */
+    if (vc <= 1) {
+        return IGRAPH_SUCCESS;
+    }
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    /* find maximum degree and a corresponding vertex */
+    {
+        igraph_vector_t degree;
+
+        IGRAPH_CHECK(igraph_vector_init(&degree, 0));
+        IGRAPH_FINALLY(igraph_vector_destroy, &degree);
+        IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL, 0));
+
+        vertex = igraph_vector_which_max(&degree);
+        maxdeg = VECTOR(degree)[vertex];
+
+        igraph_vector_destroy(&degree);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_init(&neigh_colors, 0));
+    IGRAPH_CHECK(igraph_vector_int_reserve(&neigh_colors, maxdeg));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &neigh_colors);
+
+    IGRAPH_CHECK(igraph_2wheap_init(&cn, vc));
+    IGRAPH_FINALLY(igraph_2wheap_destroy, &cn);
+    for (i = 0; i < vc; ++i)
+        if (i != vertex) {
+            igraph_2wheap_push_with_index(&cn, i, 0);    /* should not fail since memory was already reserved */
+        }
+
+    while (1) {
+        igraph_vector_int_t *neighbors = igraph_adjlist_get(&adjlist, vertex);
+        long neigh_count = igraph_vector_int_size(neighbors);
+
+        /* colour current vertex */
+        {
+            igraph_integer_t col;
+
+            IGRAPH_CHECK(igraph_vector_int_resize(&neigh_colors, neigh_count));
+            for (i = 0; i < neigh_count; ++i) {
+                VECTOR(neigh_colors)[i] = VECTOR(*colors)[ VECTOR(*neighbors)[i] ];
+            }
+            igraph_vector_int_sort(&neigh_colors);
+
+            i = 0;
+            col = 0;
+            do {
+                while (i < neigh_count && VECTOR(neigh_colors)[i] == col) {
+                    i++;
+                }
+                col++;
+            } while (i < neigh_count && VECTOR(neigh_colors)[i] == col);
+
+            VECTOR(*colors)[vertex] = col;
+        }
+
+        /* increment number of coloured neighbours for each neighbour of vertex */
+        for (i = 0; i < neigh_count; ++i) {
+            long idx = VECTOR(*neighbors)[i];
+            if (igraph_2wheap_has_elem(&cn, idx)) {
+                igraph_2wheap_modify(&cn, idx, igraph_2wheap_get(&cn, idx) + 1);
+            }
+        }
+
+        /* stop if no more vertices left to colour */
+        if (igraph_2wheap_empty(&cn)) {
+            break;
+        }
+
+        igraph_2wheap_delete_max_index(&cn, &vertex);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+    }
+
+    /* subtract 1 from each colour value, so that colours start at 0 */
+    igraph_vector_int_add_constant(colors, -1);
+
+    /* free data structures */
+    igraph_vector_int_destroy(&neigh_colors);
+    igraph_adjlist_destroy(&adjlist);
+    igraph_2wheap_destroy(&cn);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/**
+ * \function igraph_vertex_coloring_greedy
+ * \brief Computes a vertex coloring using a greedy algorithm.
+ *
+ * </para><para>
+ * This function assigns a "color"---represented as a non-negative integer---to
+ * each vertex of the graph in such a way that neighboring vertices never have
+ * the same color. The obtained coloring is not necessarily minimal.
+ *
+ * </para><para>
+ * Vertices are colored one by one, choosing the smallest color index that
+ * differs from that of already colored neighbors.
+ * Colors are represented with non-negative integers 0, 1, 2, ...
+ *
+ * \param graph The input graph.
+ * \param colors Pointer to an initialized integer vector. The vertex colors will be stored here.
+ * \param heuristic The vertex ordering heuristic to use during greedy coloring. See \ref igraph_coloring_greedy_t
+ *
+ * \return Error code.
+ *
+ * \example examples/simple/igraph_coloring.c
+ */
+int igraph_vertex_coloring_greedy(const igraph_t *graph, igraph_vector_int_t *colors, igraph_coloring_greedy_t heuristic) {
+    switch (heuristic) {
+    case IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS:
+        return igraph_i_vertex_coloring_greedy_cn(graph, colors);
+    default:
+        return IGRAPH_EINVAL;
+    }
+}
diff --git a/igraph/src/community.c b/igraph/src/community.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/community.c
@@ -0,0 +1,3840 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_community.h"
+#include "igraph_constructors.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_arpack.h"
+#include "igraph_arpack_internal.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_components.h"
+#include "igraph_dqueue.h"
+#include "igraph_progress.h"
+#include "igraph_stack.h"
+#include "igraph_spmatrix.h"
+#include "igraph_statusbar.h"
+#include "igraph_types_internal.h"
+#include "igraph_conversion.h"
+#include "igraph_centrality.h"
+#include "igraph_structural.h"
+#include "config.h"
+
+#include <string.h>
+#include <math.h>
+
+#ifdef USING_R
+    #include <R.h>
+#endif
+
+int igraph_i_rewrite_membership_vector(igraph_vector_t *membership) {
+    long int no = (long int) igraph_vector_max(membership) + 1;
+    igraph_vector_t idx;
+    long int realno = 0;
+    long int i;
+    long int len = igraph_vector_size(membership);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&idx, no);
+    for (i = 0; i < len; i++) {
+        long int t = (long int) VECTOR(*membership)[i];
+        if (VECTOR(idx)[t]) {
+            VECTOR(*membership)[i] = VECTOR(idx)[t] - 1;
+        } else {
+            VECTOR(idx)[t] = ++realno;
+            VECTOR(*membership)[i] = VECTOR(idx)[t] - 1;
+        }
+    }
+    igraph_vector_destroy(&idx);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_community_eb_get_merges2(const igraph_t *graph,
+                                      const igraph_vector_t *edges,
+                                      const igraph_vector_t *weights,
+                                      igraph_matrix_t *res,
+                                      igraph_vector_t *bridges,
+                                      igraph_vector_t *modularity,
+                                      igraph_vector_t *membership) {
+
+    igraph_vector_t mymembership;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+    igraph_real_t maxmod = -1;
+    long int midx = 0;
+    igraph_integer_t no_comps;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&mymembership, no_of_nodes);
+
+    if (membership) {
+        IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+    }
+
+    if (modularity || res || bridges) {
+        IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps,
+                                     IGRAPH_WEAK));
+
+        if (modularity) {
+            IGRAPH_CHECK(igraph_vector_resize(modularity,
+                                              no_of_nodes - no_comps + 1));
+        }
+        if (res) {
+            IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes - no_comps,
+                                              2));
+        }
+        if (bridges) {
+            IGRAPH_CHECK(igraph_vector_resize(bridges,
+                                              no_of_nodes - no_comps));
+        }
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(mymembership)[i] = i;
+    }
+    if (membership) {
+        igraph_vector_update(membership, &mymembership);
+    }
+
+    IGRAPH_CHECK(igraph_modularity(graph, &mymembership, &maxmod, weights));
+    if (modularity) {
+        VECTOR(*modularity)[0] = maxmod;
+    }
+
+    for (i = igraph_vector_size(edges) - 1; i >= 0; i--) {
+        long int edge = (long int) VECTOR(*edges)[i];
+        long int from = IGRAPH_FROM(graph, edge);
+        long int to = IGRAPH_TO(graph, edge);
+        long int c1 = (long int) VECTOR(mymembership)[from];
+        long int c2 = (long int) VECTOR(mymembership)[to];
+        igraph_real_t actmod;
+        long int j;
+        if (c1 != c2) {     /* this is a merge */
+            if (res) {
+                MATRIX(*res, midx, 0) = c1;
+                MATRIX(*res, midx, 1) = c2;
+            }
+            if (bridges) {
+                VECTOR(*bridges)[midx] = i + 1;
+            }
+
+            /* The new cluster has id no_of_nodes+midx+1 */
+            for (j = 0; j < no_of_nodes; j++) {
+                if (VECTOR(mymembership)[j] == c1 ||
+                    VECTOR(mymembership)[j] == c2) {
+                    VECTOR(mymembership)[j] = no_of_nodes + midx;
+                }
+            }
+
+            IGRAPH_CHECK(igraph_modularity(graph, &mymembership, &actmod, weights));
+            if (modularity) {
+                VECTOR(*modularity)[midx + 1] = actmod;
+                if (actmod > maxmod) {
+                    maxmod = actmod;
+                    if (membership) {
+                        igraph_vector_update(membership, &mymembership);
+                    }
+                }
+            }
+
+            midx++;
+        }
+    }
+
+    if (membership) {
+        IGRAPH_CHECK(igraph_i_rewrite_membership_vector(membership));
+    }
+
+    igraph_vector_destroy(&mymembership);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+
+/**
+ * \function igraph_community_eb_get_merges
+ * \brief Calculating the merges, ie. the dendrogram for an edge betweenness community structure
+ *
+ * </para><para>
+ * This function is handy if you have a sequence of edge which are
+ * gradually removed from the network and you would like to know how
+ * the network falls apart into separate components. The edge sequence
+ * may come from the \ref igraph_community_edge_betweenness()
+ * function, but this is not necessary. Note that \ref
+ * igraph_community_edge_betweenness can also calculate the
+ * dendrogram, via its \p merges argument.
+ *
+ * \param graph The input graph.
+ * \param edges Vector containing the edges to be removed from the
+ *    network, all edges are expected to appear exactly once in the
+ *    vector.
+ * \param weights An optional vector containing edge weights. If null,
+ *     the unweighted modularity scores will be calculated. If not null,
+ *     the weighted modularity scores will be calculated. Ignored if both
+ *     \p modularity and \p membership are nulls.
+ * \param res Pointer to an initialized matrix, if not NULL then the
+ *    dendrogram will be stored here, in the same form as for the \ref
+ *    igraph_community_walktrap() function: the matrix has two columns
+ *    and each line is a merge given by the ids of the merged
+ *    components. The component ids are number from zero and
+ *    component ids smaller than the number of vertices in the graph
+ *    belong to individual vertices. The non-trivial components
+ *    containing at least two vertices are numbered from \c n, \c n is
+ *    the number of vertices in the graph. So if the first line
+ *    contains \c a and \c b that means that components \c a and \c b
+ *    are merged into component \c n, the second line creates
+ *    component \c n+1, etc. The matrix will be resized as needed.
+ * \param bridges Pointer to an initialized vector or NULL. If not
+ *    null then the index of the edge removals which split the network
+ *    will be stored here. The vector will be resized as needed.
+ * \param modularity If not a null pointer, then the modularity values
+ *    for the different divisions, corresponding to the merges matrix,
+ *    will be stored here.
+ * \param membership If not a null pointer, then the membership vector
+ *    for the best division (in terms of modularity) will be stored
+ *    here.
+ * \return Error code.
+ *
+ * \sa \ref igraph_community_edge_betweenness().
+ *
+ * Time complexity: O(|E|+|V|log|V|), |V| is the number of vertices,
+ * |E| is the number of edges.
+ */
+
+int igraph_community_eb_get_merges(const igraph_t *graph,
+                                   const igraph_vector_t *edges,
+                                   const igraph_vector_t *weights,
+                                   igraph_matrix_t *res,
+                                   igraph_vector_t *bridges,
+                                   igraph_vector_t *modularity,
+                                   igraph_vector_t *membership) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t ptr;
+    long int i, midx = 0;
+    igraph_integer_t no_comps;
+
+    if (membership || modularity) {
+        return igraph_i_community_eb_get_merges2(graph, edges, weights, res,
+                bridges, modularity,
+                membership);
+    }
+
+    IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps, IGRAPH_WEAK));
+
+    IGRAPH_VECTOR_INIT_FINALLY(&ptr, no_of_nodes * 2 - 1);
+    if (res) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes - no_comps, 2));
+    }
+    if (bridges) {
+        IGRAPH_CHECK(igraph_vector_resize(bridges, no_of_nodes - no_comps));
+    }
+
+    for (i = igraph_vector_size(edges) - 1; i >= 0; i--) {
+        igraph_integer_t edge = (igraph_integer_t) VECTOR(*edges)[i];
+        igraph_integer_t from, to, c1, c2, idx;
+        igraph_edge(graph, edge, &from, &to);
+        idx = from + 1;
+        while (VECTOR(ptr)[idx - 1] != 0) {
+            idx = (igraph_integer_t) VECTOR(ptr)[idx - 1];
+        }
+        c1 = idx - 1;
+        idx = to + 1;
+        while (VECTOR(ptr)[idx - 1] != 0) {
+            idx = (igraph_integer_t) VECTOR(ptr)[idx - 1];
+        }
+        c2 = idx - 1;
+        if (c1 != c2) {     /* this is a merge */
+            if (res) {
+                MATRIX(*res, midx, 0) = c1;
+                MATRIX(*res, midx, 1) = c2;
+            }
+            if (bridges) {
+                VECTOR(*bridges)[midx] = i + 1;
+            }
+
+            VECTOR(ptr)[c1] = no_of_nodes + midx + 1;
+            VECTOR(ptr)[c2] = no_of_nodes + midx + 1;
+            VECTOR(ptr)[from] = no_of_nodes + midx + 1;
+            VECTOR(ptr)[to] = no_of_nodes + midx + 1;
+
+            midx++;
+        }
+    }
+
+    igraph_vector_destroy(&ptr);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/* Find the smallest active element in the vector */
+long int igraph_i_vector_which_max_not_null(const igraph_vector_t *v,
+        const char *passive) {
+    long int which, i = 0, size = igraph_vector_size(v);
+    igraph_real_t max;
+    while (passive[i]) {
+        i++;
+    }
+    which = i;
+    max = VECTOR(*v)[which];
+    for (i++; i < size; i++) {
+        igraph_real_t elem = VECTOR(*v)[i];
+        if (!passive[i] && elem > max) {
+            max = elem;
+            which = i;
+        }
+    }
+
+    return which;
+}
+
+/**
+ * \function igraph_community_edge_betweenness
+ * \brief Community finding based on edge betweenness
+ *
+ * Community structure detection based on the betweenness of the edges
+ * in the network. The algorithm was invented by M. Girvan and
+ * M. Newman, see: M. Girvan and M. E. J. Newman: Community structure in
+ * social and biological networks, Proc. Nat. Acad. Sci. USA 99, 7821-7826
+ * (2002).
+ *
+ * </para><para>
+ * The idea is that the betweenness of the edges connecting two
+ * communities is typically high, as many of the shortest paths
+ * between nodes in separate communities go through them. So we
+ * gradually remove the edge with highest betweenness from the
+ * network, and recalculate edge betweenness after every removal.
+ * This way sooner or later the network falls off to two components,
+ * then after a while one of these components falls off to two smaller
+ * components, etc. until all edges are removed. This is a divisive
+ * hierarchical approach, the result is a dendrogram.
+ * \param graph The input graph.
+ * \param result Pointer to an initialized vector, the result will be
+ *     stored here, the ids of the removed edges in the order of their
+ *     removal. It will be resized as needed. It may be NULL if
+ *     the edge IDs are not needed by the caller.
+ * \param edge_betweenness Pointer to an initialized vector or
+ *     NULL. In the former case the edge betweenness of the removed
+ *     edge is stored here. The vector will be resized as needed.
+ * \param merges Pointer to an initialized matrix or NULL. If not NULL
+ *     then merges performed by the algorithm are stored here. Even if
+ *     this is a divisive algorithm, we can replay it backwards and
+ *     note which two clusters were merged. Clusters are numbered from
+ *     zero, see the \p merges argument of \ref
+ *     igraph_community_walktrap() for details. The matrix will be
+ *     resized as needed.
+ * \param bridges Pointer to an initialized vector of NULL. If not
+ *     NULL then all edge removals which separated the network into
+ *     more components are marked here.
+ * \param modularity If not a null pointer, then the modularity values
+ *     of the different divisions are stored here, in the order
+ *     corresponding to the merge matrix. The modularity values will
+ *     take weights into account if \p weights is not null.
+ * \param membership If not a null pointer, then the membership vector,
+ *     corresponding to the highest modularity value, is stored here.
+ * \param directed Logical constant, whether to calculate directed
+ *    betweenness (ie. directed paths) for directed graphs. It is
+ *    ignored for undirected graphs.
+ * \param weights An optional vector containing edge weights. If null,
+ *     the unweighted edge betweenness scores will be calculated and
+ *     used. If not null, the weighted edge betweenness scores will be
+ *     calculated and used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_community_eb_get_merges(), \ref
+ * igraph_community_spinglass(), \ref igraph_community_walktrap().
+ *
+ * Time complexity: O(|V||E|^2), as the betweenness calculation requires
+ * O(|V||E|) and we do it |E|-1 times.
+ *
+ * \example examples/simple/igraph_community_edge_betweenness.c
+ */
+
+int igraph_community_edge_betweenness(const igraph_t *graph,
+                                      igraph_vector_t *result,
+                                      igraph_vector_t *edge_betweenness,
+                                      igraph_matrix_t *merges,
+                                      igraph_vector_t *bridges,
+                                      igraph_vector_t *modularity,
+                                      igraph_vector_t *membership,
+                                      igraph_bool_t directed,
+                                      const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    double *distance, *tmpscore;
+    unsigned long long int *nrgeo;
+    long int source, i, e;
+
+    igraph_inclist_t elist_out, elist_in, fathers;
+    igraph_inclist_t *elist_out_p, *elist_in_p;
+    igraph_vector_int_t *neip;
+    long int neino;
+    igraph_vector_t eb;
+    long int maxedge, pos;
+    igraph_integer_t from, to;
+    igraph_bool_t result_owned = 0;
+    igraph_stack_t stack = IGRAPH_STACK_NULL;
+    igraph_real_t steps, steps_done;
+
+    char *passive;
+
+    /* Needed only for the unweighted case */
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+
+    /* Needed only for the weighted case */
+    igraph_2wheap_t heap;
+
+    if (result == 0) {
+        result = igraph_Calloc(1, igraph_vector_t);
+        if (result == 0) {
+            IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, result);
+        IGRAPH_VECTOR_INIT_FINALLY(result, 0);
+        result_owned = 1;
+    }
+
+    directed = directed && igraph_is_directed(graph);
+    if (directed) {
+        IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_OUT));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out);
+        IGRAPH_CHECK(igraph_inclist_init(graph, &elist_in, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &elist_in);
+        elist_out_p = &elist_out;
+        elist_in_p = &elist_in;
+    } else {
+        IGRAPH_CHECK(igraph_inclist_init(graph, &elist_out, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &elist_out);
+        elist_out_p = elist_in_p = &elist_out;
+    }
+
+    distance = igraph_Calloc(no_of_nodes, double);
+    if (distance == 0) {
+        IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, distance);
+    nrgeo = igraph_Calloc(no_of_nodes, unsigned long long int);
+    if (nrgeo == 0) {
+        IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, nrgeo);
+    tmpscore = igraph_Calloc(no_of_nodes, double);
+    if (tmpscore == 0) {
+        IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, tmpscore);
+
+    if (weights == 0) {
+        IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    } else {
+        if (igraph_vector_min(weights) <= 0) {
+            IGRAPH_ERROR("weights must be strictly positive", IGRAPH_EINVAL);
+        }
+
+        if (membership != 0) {
+            IGRAPH_WARNING("Membership vector will be selected based on the lowest "\
+                           "modularity score.");
+        }
+
+        if (modularity != 0 || membership != 0) {
+            IGRAPH_WARNING("Modularity calculation with weighted edge betweenness "\
+                           "community detection might not make sense -- modularity treats edge "\
+                           "weights as similarities while edge betwenness treats them as "\
+                           "distances");
+        }
+
+        IGRAPH_CHECK(igraph_2wheap_init(&heap, no_of_nodes));
+        IGRAPH_FINALLY(igraph_2wheap_destroy, &heap);
+        IGRAPH_CHECK(igraph_inclist_init_empty(&fathers,
+                                               (igraph_integer_t) no_of_nodes));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &fathers);
+    }
+
+    IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes));
+    IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+
+    IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges));
+    if (edge_betweenness) {
+        IGRAPH_CHECK(igraph_vector_resize(edge_betweenness, no_of_edges));
+        VECTOR(*edge_betweenness)[no_of_edges - 1] = 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&eb, no_of_edges);
+
+    passive = igraph_Calloc(no_of_edges, char);
+    if (!passive) {
+        IGRAPH_ERROR("edge betweenness community structure failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, passive);
+
+    /* Estimate the number of steps to be taken.
+     * It is assumed that one iteration is O(|E||V|), but |V| is constant
+     * anyway, so we will have approximately |E|^2 / 2 steps, and one
+     * iteration of the outer loop advances the step counter by the number
+     * of remaining edges at that iteration.
+     */
+    steps = no_of_edges / 2.0 * (no_of_edges + 1);
+    steps_done = 0;
+
+    for (e = 0; e < no_of_edges; steps_done += no_of_edges - e, e++) {
+        IGRAPH_PROGRESS("Edge betweenness community detection: ",
+                        100.0 * steps_done / steps, NULL);
+
+        igraph_vector_null(&eb);
+
+        if (weights == 0) {
+            /* Unweighted variant follows */
+
+            /* The following for loop is copied almost intact from
+             * igraph_edge_betweenness_estimate */
+            for (source = 0; source < no_of_nodes; source++) {
+
+                IGRAPH_ALLOW_INTERRUPTION();
+
+                memset(distance, 0, (size_t) no_of_nodes * sizeof(double));
+                memset(nrgeo, 0, (size_t) no_of_nodes * sizeof(unsigned long long int));
+                memset(tmpscore, 0, (size_t) no_of_nodes * sizeof(double));
+                igraph_stack_clear(&stack); /* it should be empty anyway... */
+
+                IGRAPH_CHECK(igraph_dqueue_push(&q, source));
+
+                nrgeo[source] = 1;
+                distance[source] = 0;
+
+                while (!igraph_dqueue_empty(&q)) {
+                    long int actnode = (long int) igraph_dqueue_pop(&q);
+
+                    neip = igraph_inclist_get(elist_out_p, actnode);
+                    neino = igraph_vector_int_size(neip);
+                    for (i = 0; i < neino; i++) {
+                        igraph_integer_t edge = (igraph_integer_t) VECTOR(*neip)[i], from, to;
+                        long int neighbor;
+                        igraph_edge(graph, edge, &from, &to);
+                        neighbor = actnode != from ? from : to;
+                        if (nrgeo[neighbor] != 0) {
+                            /* we've already seen this node, another shortest path? */
+                            if (distance[neighbor] == distance[actnode] + 1) {
+                                nrgeo[neighbor] += nrgeo[actnode];
+                            }
+                        } else {
+                            /* we haven't seen this node yet */
+                            nrgeo[neighbor] += nrgeo[actnode];
+                            distance[neighbor] = distance[actnode] + 1;
+                            IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                            IGRAPH_CHECK(igraph_stack_push(&stack, neighbor));
+                        }
+                    }
+                } /* while !igraph_dqueue_empty */
+
+                /* Ok, we've the distance of each node and also the number of
+                   shortest paths to them. Now we do an inverse search, starting
+                   with the farthest nodes. */
+                while (!igraph_stack_empty(&stack)) {
+                    long int actnode = (long int) igraph_stack_pop(&stack);
+                    if (distance[actnode] < 1) {
+                        continue;    /* skip source node */
+                    }
+
+                    /* set the temporary score of the friends */
+                    neip = igraph_inclist_get(elist_in_p, actnode);
+                    neino = igraph_vector_int_size(neip);
+                    for (i = 0; i < neino; i++) {
+                        long int edge = (long int) VECTOR(*neip)[i];
+                        long int neighbor = IGRAPH_OTHER(graph, edge, actnode);
+                        if (distance[neighbor] == distance[actnode] - 1 &&
+                            nrgeo[neighbor] != 0) {
+                            tmpscore[neighbor] +=
+                                (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode];
+                            VECTOR(eb)[edge] +=
+                                (tmpscore[actnode] + 1) * nrgeo[neighbor] / nrgeo[actnode];
+                        }
+                    }
+                }
+                /* Ok, we've the scores for this source */
+            } /* for source <= no_of_nodes */
+        } else {
+            /* Weighted variant follows */
+
+            /* The following for loop is copied almost intact from
+             * igraph_i_edge_betweenness_estimate_weighted */
+            for (source = 0; source < no_of_nodes; source++) {
+                /* This will contain the edge betweenness in the current step */
+                IGRAPH_ALLOW_INTERRUPTION();
+
+                memset(distance, 0, (size_t) no_of_nodes * sizeof(double));
+                memset(nrgeo, 0, (size_t) no_of_nodes * sizeof(unsigned long long int));
+                memset(tmpscore, 0, (size_t) no_of_nodes * sizeof(double));
+
+                igraph_2wheap_push_with_index(&heap, source, 0);
+                distance[source] = 1.0;
+                nrgeo[source] = 1;
+
+                while (!igraph_2wheap_empty(&heap)) {
+                    long int minnei = igraph_2wheap_max_index(&heap);
+                    igraph_real_t mindist = -igraph_2wheap_delete_max(&heap);
+
+                    igraph_stack_push(&stack, minnei);
+
+                    neip = igraph_inclist_get(elist_out_p, minnei);
+                    neino = igraph_vector_int_size(neip);
+
+                    for (i = 0; i < neino; i++) {
+                        long int edge = VECTOR(*neip)[i];
+                        long int to = IGRAPH_OTHER(graph, edge, minnei);
+                        igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
+                        igraph_real_t curdist = distance[to];
+                        igraph_vector_int_t *v;
+
+                        if (curdist == 0) {
+                            /* This is the first finite distance to 'to' */
+                            v = igraph_inclist_get(&fathers, to);
+                            igraph_vector_int_resize(v, 1);
+                            VECTOR(*v)[0] = edge;
+                            nrgeo[to] = nrgeo[minnei];
+                            distance[to] = altdist + 1.0;
+                            IGRAPH_CHECK(igraph_2wheap_push_with_index(&heap, to, -altdist));
+                        } else if (altdist < curdist - 1) {
+                            /* This is a shorter path */
+                            v = igraph_inclist_get(&fathers, to);
+                            igraph_vector_int_resize(v, 1);
+                            VECTOR(*v)[0] = edge;
+                            nrgeo[to] = nrgeo[minnei];
+                            distance[to] = altdist + 1.0;
+                            IGRAPH_CHECK(igraph_2wheap_modify(&heap, to, -altdist));
+                        } else if (altdist == curdist - 1) {
+                            /* Another path with the same length */
+                            v = igraph_inclist_get(&fathers, to);
+                            igraph_vector_int_push_back(v, edge);
+                            nrgeo[to] += nrgeo[minnei];
+                        }
+                    }
+                } /* igraph_2wheap_empty(&Q) */
+
+                while (!igraph_stack_empty(&stack)) {
+                    long int w = (long int) igraph_stack_pop(&stack);
+                    igraph_vector_int_t *fatv = igraph_inclist_get(&fathers, w);
+                    long int fatv_len = igraph_vector_int_size(fatv);
+
+                    for (i = 0; i < fatv_len; i++) {
+                        long int fedge = (long int) VECTOR(*fatv)[i];
+                        long int neighbor = IGRAPH_OTHER(graph, fedge, w);
+                        tmpscore[neighbor] += (tmpscore[w] + 1) * nrgeo[neighbor] / nrgeo[w];
+                        VECTOR(eb)[fedge] += (tmpscore[w] + 1) * nrgeo[neighbor] / nrgeo[w];
+                    }
+
+                    tmpscore[w] = 0;
+                    distance[w] = 0;
+                    nrgeo[w] = 0;
+                    igraph_vector_int_clear(fatv);
+                }
+            } /* source < no_of_nodes */
+        }
+
+        /* Now look for the smallest edge betweenness */
+        /* and eliminate that edge from the network */
+        maxedge = igraph_i_vector_which_max_not_null(&eb, passive);
+        VECTOR(*result)[e] = maxedge;
+        if (edge_betweenness) {
+            VECTOR(*edge_betweenness)[e] = VECTOR(eb)[maxedge];
+            if (!directed) {
+                VECTOR(*edge_betweenness)[e] /= 2.0;
+            }
+        }
+        passive[maxedge] = 1;
+        igraph_edge(graph, (igraph_integer_t) maxedge, &from, &to);
+
+        neip = igraph_inclist_get(elist_in_p, to);
+        neino = igraph_vector_int_size(neip);
+        igraph_vector_int_search(neip, 0, maxedge, &pos);
+        VECTOR(*neip)[pos] = VECTOR(*neip)[neino - 1];
+        igraph_vector_int_pop_back(neip);
+
+        neip = igraph_inclist_get(elist_out_p, from);
+        neino = igraph_vector_int_size(neip);
+        igraph_vector_int_search(neip, 0, maxedge, &pos);
+        VECTOR(*neip)[pos] = VECTOR(*neip)[neino - 1];
+        igraph_vector_int_pop_back(neip);
+    }
+
+    IGRAPH_PROGRESS("Edge betweenness community detection: ", 100.0, NULL);
+
+    igraph_free(passive);
+    igraph_vector_destroy(&eb);
+    igraph_stack_destroy(&stack);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (weights == 0) {
+        igraph_dqueue_destroy(&q);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        igraph_2wheap_destroy(&heap);
+        igraph_inclist_destroy(&fathers);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+    igraph_free(tmpscore);
+    igraph_free(nrgeo);
+    igraph_free(distance);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (directed) {
+        igraph_inclist_destroy(&elist_out);
+        igraph_inclist_destroy(&elist_in);
+        IGRAPH_FINALLY_CLEAN(2);
+    } else {
+        igraph_inclist_destroy(&elist_out);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (merges || bridges || modularity || membership) {
+        IGRAPH_CHECK(igraph_community_eb_get_merges(graph, result, weights, merges,
+                     bridges, modularity,
+                     membership));
+    }
+
+    if (result_owned) {
+        igraph_vector_destroy(result);
+        free(result);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    return 0;
+}
+
+
+/**
+ * \function igraph_community_to_membership
+ * \brief Create membership vector from community structure dendrogram
+ *
+ * This function creates a membership vector from a community
+ * structure dendrogram. A membership vector contains for each vertex
+ * the id of its graph component, the graph components are numbered
+ * from zero, see the same argument of \ref igraph_clusters() for an
+ * example of a membership vector.
+ *
+ * </para><para>
+ * Many community detection algorithms return with a \em merges
+ * matrix, \ref igraph_community_walktrap() and \ref
+ * igraph_community_edge_betweenness() are two examples. The matrix
+ * contains the merge operations performed while mapping the
+ * hierarchical structure of a network. If the matrix has \c n-1 rows,
+ * where \c n is the number of vertices in the graph, then it contains
+ * the hierarchical structure of the whole network and it is called a
+ * dendrogram.
+ *
+ * </para><para>
+ * This function performs \p steps merge operations as prescribed by
+ * the \p merges matrix and returns the current state of the network.
+ *
+ * </para><para>
+ * If \p merges is not a complete dendrogram, it is possible to
+ * take \p steps steps if \p steps is not bigger than the number
+ * lines in \p merges.
+ * \param merges The two-column matrix containing the merge
+ *    operations. See \ref igraph_community_walktrap() for the
+ *    detailed syntax.
+ * \param nodes The number of leaf nodes in the dendrogram
+ * \param steps Integer constant, the number of steps to take.
+ * \param membership Pointer to an initialized vector, the membership
+ *    results will be stored here, if not NULL. The vector will be
+ *    resized as needed.
+ * \param csize Pointer to an initialized vector, or NULL. If not NULL
+ *    then the sizes of the components will be stored here, the vector
+ *    will be resized as needed.
+ *
+ * \sa \ref igraph_community_walktrap(), \ref
+ * igraph_community_edge_betweenness(), \ref
+ * igraph_community_fastgreedy() for community structure detection
+ * algorithms.
+ *
+ * Time complexity: O(|V|), the number of vertices in the graph.
+ */
+
+int igraph_community_to_membership(const igraph_matrix_t *merges,
+                                   igraph_integer_t nodes,
+                                   igraph_integer_t steps,
+                                   igraph_vector_t *membership,
+                                   igraph_vector_t *csize) {
+
+    long int no_of_nodes = nodes;
+    long int components = no_of_nodes - steps;
+    long int i, found = 0;
+    igraph_vector_t tmp;
+
+    if (steps > igraph_matrix_nrow(merges)) {
+        IGRAPH_ERROR("`steps' to big or `merges' matrix too short", IGRAPH_EINVAL);
+    }
+
+    if (membership) {
+        IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+        igraph_vector_null(membership);
+    }
+    if (csize) {
+        IGRAPH_CHECK(igraph_vector_resize(csize, components));
+        igraph_vector_null(csize);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, steps);
+
+    for (i = steps - 1; i >= 0; i--) {
+        long int c1 = (long int) MATRIX(*merges, i, 0);
+        long int c2 = (long int) MATRIX(*merges, i, 1);
+
+        /* new component? */
+        if (VECTOR(tmp)[i] == 0) {
+            found++;
+            VECTOR(tmp)[i] = found;
+        }
+
+        if (c1 < no_of_nodes) {
+            long int cid = (long int) VECTOR(tmp)[i] - 1;
+            if (membership) {
+                VECTOR(*membership)[c1] = cid + 1;
+            }
+            if (csize) {
+                VECTOR(*csize)[cid] += 1;
+            }
+        } else {
+            VECTOR(tmp)[c1 - no_of_nodes] = VECTOR(tmp)[i];
+        }
+
+        if (c2 < no_of_nodes) {
+            long int cid = (long int) VECTOR(tmp)[i] - 1;
+            if (membership) {
+                VECTOR(*membership)[c2] = cid + 1;
+            }
+            if (csize) {
+                VECTOR(*csize)[cid] += 1;
+            }
+        } else {
+            VECTOR(tmp)[c2 - no_of_nodes] = VECTOR(tmp)[i];
+        }
+
+    }
+
+    if (membership || csize) {
+        for (i = 0; i < no_of_nodes; i++) {
+            long int tmp = (long int) VECTOR(*membership)[i];
+            if (tmp != 0) {
+                if (membership) {
+                    VECTOR(*membership)[i] = tmp - 1;
+                }
+            } else {
+                if (csize) {
+                    VECTOR(*csize)[found] += 1;
+                }
+                if (membership) {
+                    VECTOR(*membership)[i] = found;
+                }
+                found++;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_modularity
+ * \brief Calculate the modularity of a graph with respect to some vertex types
+ *
+ * The modularity of a graph with respect to some division (or vertex
+ * types) measures how good the division is, or how separated are the
+ * different vertex types from each other. It is defined as
+ * Q=1/(2m) * sum((Aij - ki*kj / (2m)) delta(ci,cj), i, j), here `m' is the
+ * number of edges, `Aij' is the element of the `A' adjacency matrix
+ * in row `i' and column `j', `ki' is the degree of `i', `kj' is the
+ * degree of `j', `ci' is the type (or component) of `i', `cj' that of
+ * `j', the sum goes over all `i' and `j' pairs of vertices, and
+ * `delta(x,y)' is one if x=y and zero otherwise.
+ *
+ * </para><para>
+ * Modularity on weighted graphs is also meaningful. When taking edge
+ * weights into account, `Aij' becomes the weight of the corresponding
+ * edge (or 0 if there is no edge), `ki' is the total weight of edges
+ * incident on vertex `i', `kj' is the total weight of edges incident
+ * on vertex `j' and `m' is the total weight of all edges.
+ *
+ * </para><para>
+ * See also Clauset, A.; Newman, M. E. J.; Moore, C. Finding
+ * community structure in very large networks, Physical Review E,
+ * 2004, 70, 066111.
+ * \param graph The input graph. It must be undirected; directed graphs are
+ *     not supported yet.
+ * \param membership Numeric vector which gives the type of each
+ *     vertex, ie. the component to which it belongs.
+ *     It does not have to be consecutive, i.e. empty communities are
+ *     allowed.
+ * \param modularity Pointer to a real number, the result will be
+ *     stored here.
+ * \param weights Weight vector or NULL if no weights are specified.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges.
+ */
+
+int igraph_modularity(const igraph_t *graph,
+                      const igraph_vector_t *membership,
+                      igraph_real_t *modularity,
+                      const igraph_vector_t *weights) {
+
+    igraph_vector_t e, a;
+    long int types = (long int) igraph_vector_max(membership) + 1;
+    long int no_of_edges = igraph_ecount(graph);
+    long int i;
+    igraph_integer_t from, to;
+    igraph_real_t m;
+    long int c1, c2;
+
+    if (igraph_is_directed(graph)) {
+#ifndef USING_R
+        IGRAPH_ERROR("modularity is implemented for undirected graphs", IGRAPH_EINVAL);
+#else
+        REprintf("Modularity is implemented for undirected graphs only.\n");
+#endif
+    }
+
+    if (igraph_vector_size(membership) < igraph_vcount(graph)) {
+        IGRAPH_ERROR("cannot calculate modularity, membership vector too short",
+                     IGRAPH_EINVAL);
+    }
+    if (igraph_vector_min(membership) < 0) {
+        IGRAPH_ERROR("Invalid membership vector", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&e, types);
+    IGRAPH_VECTOR_INIT_FINALLY(&a, types);
+
+    if (weights) {
+        if (igraph_vector_size(weights) < no_of_edges)
+            IGRAPH_ERROR("cannot calculate modularity, weight vector too short",
+                         IGRAPH_EINVAL);
+        m = igraph_vector_sum(weights);
+        for (i = 0; i < no_of_edges; i++) {
+            igraph_real_t w = VECTOR(*weights)[i];
+            if (w < 0) {
+                IGRAPH_ERROR("negative weight in weight vector", IGRAPH_EINVAL);
+            }
+            igraph_edge(graph, (igraph_integer_t) i, &from, &to);
+            c1 = (long int) VECTOR(*membership)[from];
+            c2 = (long int) VECTOR(*membership)[to];
+            if (c1 == c2) {
+                VECTOR(e)[c1] += 2 * w;
+            }
+            VECTOR(a)[c1] += w;
+            VECTOR(a)[c2] += w;
+        }
+    } else {
+        m = no_of_edges;
+        for (i = 0; i < no_of_edges; i++) {
+            igraph_edge(graph, (igraph_integer_t) i, &from, &to);
+            c1 = (long int) VECTOR(*membership)[from];
+            c2 = (long int) VECTOR(*membership)[to];
+            if (c1 == c2) {
+                VECTOR(e)[c1] += 2;
+            }
+            VECTOR(a)[c1] += 1;
+            VECTOR(a)[c2] += 1;
+        }
+    }
+
+    *modularity = 0.0;
+    if (m > 0) {
+        for (i = 0; i < types; i++) {
+            igraph_real_t tmp = VECTOR(a)[i] / 2 / m;
+            *modularity += VECTOR(e)[i] / 2 / m;
+            *modularity -= tmp * tmp;
+        }
+    }
+
+    igraph_vector_destroy(&e);
+    igraph_vector_destroy(&a);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_modularity_matrix
+ * \brief Calculate the modularity matrix
+ *
+ * This function returns the modularity matrix defined as
+ * `B_ij = A_ij - k_i k_j * / 2 m`
+ * where `A_ij` denotes the adjacency matrix, `k_i` is the degree of node `i`
+ * and `m` is the total weight in the graph. Note that self-loops are multiplied
+ * by 2 in this implementation. If weights are specified, the weighted
+ * counterparts are used.
+ *
+ * \param graph   The input graph
+ * \param modmat  Pointer to an initialized matrix in which the modularity
+ *                matrix is stored.
+ * \param weights Edge weights, pointer to a vector. If this is a null pointer
+ *                then every edge is assumed to have a weight of 1.
+ */
+
+int igraph_modularity_matrix(const igraph_t *graph,
+                             igraph_matrix_t *modmat,
+                             const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_real_t sw = weights ? igraph_vector_sum(weights) : no_of_edges;
+    igraph_vector_t deg;
+    long int i, j;
+
+    if (weights && igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&deg, no_of_nodes);
+    if (!weights) {
+        IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(), IGRAPH_ALL,
+                                   IGRAPH_LOOPS));
+    } else {
+        IGRAPH_CHECK(igraph_strength(graph, &deg, igraph_vss_all(), IGRAPH_ALL,
+                                     IGRAPH_LOOPS, weights));
+    }
+    IGRAPH_CHECK(igraph_get_adjacency(graph, modmat, IGRAPH_GET_ADJACENCY_BOTH,
+                                      /*eids=*/ 0));
+
+    for (i = 0; i < no_of_nodes; i++) {
+        MATRIX(*modmat, i, i) *= 2;
+    }
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j < no_of_nodes; j++) {
+            MATRIX(*modmat, i, j) -= VECTOR(deg)[i] * VECTOR(deg)[j] / 2.0 / sw;
+        }
+    }
+
+    igraph_vector_destroy(&deg);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_reindex_membership
+ * \brief Makes the IDs in a membership vector continuous
+ *
+ * This function reindexes component IDs in a membership vector
+ * in a way that the new IDs start from zero and go up to C-1,
+ * where C is the number of unique component IDs in the original
+ * vector. The supplied membership is expected to fall in the
+ * range 0, ..., n - 1.
+ *
+ * \param  membership  Numeric vector which gives the type of each
+ *                     vertex, ie. the component to which it belongs.
+ *                     The vector will be altered in-place.
+ * \param  new_to_old  Pointer to a vector which will contain the
+ *                     old component ID for each new one, or NULL,
+ *                     in which case it is not returned. The vector
+ *                     will be resized as needed.
+ * \param  nb_clusters Pointer to an integer for the number of
+ *                     distinct clusters. If not NULL, this will be
+ *                     updated to reflect the number of distinct
+ *                     clusters found in membership.
+ *
+ * Time complexity: should be O(n) for n elements.
+ */
+int igraph_reindex_membership(igraph_vector_t *membership,
+                              igraph_vector_t *new_to_old,
+                              igraph_integer_t *nb_clusters) {
+
+    long int i, n = igraph_vector_size(membership);
+    igraph_vector_t new_cluster;
+    igraph_integer_t i_nb_clusters;
+
+    /* We allow original cluster indices in the range 0, ..., n - 1 */
+    IGRAPH_CHECK(igraph_vector_init(&new_cluster, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &new_cluster);
+
+    if (new_to_old) {
+        igraph_vector_clear(new_to_old);
+    }
+
+    /* Clean clusters. We will store the new cluster + 1 so that membership == 0
+     * indicates that no cluster was assigned yet. */
+    i_nb_clusters = 1;
+    for (i = 0; i < n; i++) {
+        long int c = (long int)VECTOR(*membership)[i];
+
+        if (c >= n) {
+            IGRAPH_ERROR("Cluster out of range", IGRAPH_EINVAL);
+        }
+
+        if (VECTOR(new_cluster)[c] == 0) {
+            VECTOR(new_cluster)[c] = (igraph_real_t)i_nb_clusters;
+            i_nb_clusters += 1;
+            if (new_to_old) {
+                IGRAPH_CHECK(igraph_vector_push_back(new_to_old, c));
+            }
+        }
+    }
+
+    /* Assign new membership */
+    for (i = 0; i < n; i++) {
+        long int c = (long int)VECTOR(*membership)[i];
+        VECTOR(*membership)[i] = VECTOR(new_cluster)[c] - 1;
+    }
+    if (nb_clusters) {
+        /* We used the cluster + 1, so correct */
+        *nb_clusters = i_nb_clusters - 1;
+    }
+
+    igraph_vector_destroy(&new_cluster);
+
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/********************************************************************/
+
+/**
+ * \section about_leading_eigenvector_methods
+ *
+ * <para>
+ * The function documented in these section implements the
+ * <quote>leading eigenvector</quote> method developed by Mark Newman and
+ * published in MEJ Newman: Finding community structure using the
+ * eigenvectors of matrices, Phys Rev E 74:036104 (2006).</para>
+ *
+ * <para>
+ * The heart of the method is the definition of the modularity matrix,
+ * B, which is B=A-P, A being the adjacency matrix of the (undirected)
+ * network, and P contains the probability that certain edges are
+ * present according to the <quote>configuration model</quote> In
+ * other words, a Pij element of P is the probability that there is an
+ * edge between vertices i and j in a random network in which the
+ * degrees of all vertices are the same as in the input graph.</para>
+ *
+ * <para>
+ * The leading eigenvector method works by calculating the eigenvector
+ * of the modularity matrix for the largest positive eigenvalue and
+ * then separating vertices into two community based on the sign of
+ * the corresponding element in the eigenvector. If all elements in
+ * the eigenvector are of the same sign that means that the network
+ * has no underlying community structure.
+ * Check Newman's paper to understand why this is a good method for
+ * detecting community structure. </para>
+ *
+ * <para>
+ * The leading eigenvector community structure detection method is
+ * implemented in \ref igraph_community_leading_eigenvector(). After
+ * the initial split, the following splits are done in a way to
+ * optimize modularity regarding to the original network. Note that
+ * any further refinement, for example using Kernighan-Lin, as
+ * proposed in Section V.A of Newman (2006), is not implemented here.
+ * </para>
+ *
+ * <para>
+ * \example examples/simple/igraph_community_leading_eigenvector.c
+ * </para>
+ */
+
+typedef struct igraph_i_community_leading_eigenvector_data_t {
+    igraph_vector_t *idx;
+    igraph_vector_t *idx2;
+    igraph_adjlist_t *adjlist;
+    igraph_inclist_t *inclist;
+    igraph_vector_t *tmp;
+    long int no_of_edges;
+    igraph_vector_t *mymembership;
+    long int comm;
+    const igraph_vector_t *weights;
+    const igraph_t *graph;
+    igraph_vector_t *strength;
+    igraph_real_t sumweights;
+} igraph_i_community_leading_eigenvector_data_t;
+
+int igraph_i_community_leading_eigenvector(igraph_real_t *to,
+        const igraph_real_t *from,
+        int n, void *extra) {
+
+    igraph_i_community_leading_eigenvector_data_t *data = extra;
+    long int j, k, nlen, size = n;
+    igraph_vector_t *idx = data->idx;
+    igraph_vector_t *idx2 = data->idx2;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_adjlist_t *adjlist = data->adjlist;
+    igraph_real_t ktx, ktx2;
+    long int no_of_edges = data->no_of_edges;
+    igraph_vector_t *mymembership = data->mymembership;
+    long int comm = data->comm;
+
+    /* Ax */
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid);
+        nlen = igraph_vector_int_size(neis);
+        to[j] = 0.0;
+        VECTOR(*tmp)[j] = 0.0;
+        for (k = 0; k < nlen; k++) {
+            long int nei = (long int) VECTOR(*neis)[k];
+            long int neimemb = (long int) VECTOR(*mymembership)[nei];
+            if (neimemb == comm) {
+                to[j] += from[ (long int) VECTOR(*idx2)[nei] ];
+                VECTOR(*tmp)[j] += 1;
+            }
+        }
+    }
+
+    /* Now calculate k^Tx/2m */
+    ktx = 0.0; ktx2 = 0.0;
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid);
+        long int degree = igraph_vector_int_size(neis);
+        ktx += from[j] * degree;
+        ktx2 += degree;
+    }
+    ktx = ktx / no_of_edges / 2.0;
+    ktx2 = ktx2 / no_of_edges / 2.0;
+
+    /* Now calculate Bx */
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid);
+        igraph_real_t degree = igraph_vector_int_size(neis);
+        to[j] = to[j] - ktx * degree;
+        VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * degree;
+    }
+
+    /* -d_ij summa l in G B_il */
+    for (j = 0; j < size; j++) {
+        to[j] -= VECTOR(*tmp)[j] * from[j];
+    }
+
+    return 0;
+}
+
+int igraph_i_community_leading_eigenvector2(igraph_real_t *to,
+        const igraph_real_t *from,
+        int n, void *extra) {
+
+    igraph_i_community_leading_eigenvector_data_t *data = extra;
+    long int j, k, nlen, size = n;
+    igraph_vector_t *idx = data->idx;
+    igraph_vector_t *idx2 = data->idx2;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_adjlist_t *adjlist = data->adjlist;
+    igraph_real_t ktx, ktx2;
+    long int no_of_edges = data->no_of_edges;
+    igraph_vector_t *mymembership = data->mymembership;
+    long int comm = data->comm;
+
+    /* Ax */
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid);
+        nlen = igraph_vector_int_size(neis);
+        to[j] = 0.0;
+        VECTOR(*tmp)[j] = 0.0;
+        for (k = 0; k < nlen; k++) {
+            long int nei = (long int) VECTOR(*neis)[k];
+            long int neimemb = (long int) VECTOR(*mymembership)[nei];
+            if (neimemb == comm) {
+                long int fi = (long int) VECTOR(*idx2)[nei];
+                if (fi < size) {
+                    to[j] += from[fi];
+                }
+                VECTOR(*tmp)[j] += 1;
+            }
+        }
+    }
+
+    /* Now calculate k^Tx/2m */
+    ktx = 0.0; ktx2 = 0.0;
+    for (j = 0; j < size + 1; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid);
+        long int degree = igraph_vector_int_size(neis);
+        if (j < size) {
+            ktx += from[j] * degree;
+        }
+        ktx2 += degree;
+    }
+    ktx = ktx / no_of_edges / 2.0;
+    ktx2 = ktx2 / no_of_edges / 2.0;
+
+    /* Now calculate Bx */
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid);
+        igraph_real_t degree = igraph_vector_int_size(neis);
+        to[j] = to[j] - ktx * degree;
+        VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * degree;
+    }
+
+    /* -d_ij summa l in G B_il */
+    for (j = 0; j < size; j++) {
+        to[j] -= VECTOR(*tmp)[j] * from[j];
+    }
+
+    return 0;
+}
+
+int igraph_i_community_leading_eigenvector_weighted(igraph_real_t *to,
+        const igraph_real_t *from,
+        int n, void *extra) {
+
+    igraph_i_community_leading_eigenvector_data_t *data = extra;
+    long int j, k, nlen, size = n;
+    igraph_vector_t *idx = data->idx;
+    igraph_vector_t *idx2 = data->idx2;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_inclist_t *inclist = data->inclist;
+    igraph_real_t ktx, ktx2;
+    igraph_vector_t *mymembership = data->mymembership;
+    long int comm = data->comm;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_t *strength = data->strength;
+    igraph_real_t sw = data->sumweights;
+
+    /* Ax */
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_vector_int_t *inc = igraph_inclist_get(inclist, oldid);
+        nlen = igraph_vector_int_size(inc);
+        to[j] = 0.0;
+        VECTOR(*tmp)[j] = 0.0;
+        for (k = 0; k < nlen; k++) {
+            long int edge = (long int) VECTOR(*inc)[k];
+            igraph_real_t w = VECTOR(*weights)[edge];
+            long int nei = IGRAPH_OTHER(graph, edge, oldid);
+            long int neimemb = (long int) VECTOR(*mymembership)[nei];
+            if (neimemb == comm) {
+                to[j] += from[ (long int) VECTOR(*idx2)[nei] ] * w;
+                VECTOR(*tmp)[j] += w;
+            }
+        }
+    }
+
+    /* k^Tx/2m */
+    ktx = 0.0; ktx2 = 0.0;
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_real_t str = VECTOR(*strength)[oldid];
+        ktx += from[j] * str;
+        ktx2 += str;
+    }
+    ktx = ktx / sw / 2.0;
+    ktx2 = ktx2 / sw / 2.0;
+
+    /* Bx */
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_real_t str = VECTOR(*strength)[oldid];
+        to[j] = to[j] - ktx * str;
+        VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * str;
+    }
+
+    /* -d_ij summa l in G B_il */
+    for (j = 0; j < size; j++) {
+        to[j] -= VECTOR(*tmp)[j] * from[j];
+    }
+
+    return 0;
+}
+
+int igraph_i_community_leading_eigenvector2_weighted(igraph_real_t *to,
+        const igraph_real_t *from,
+        int n, void *extra) {
+
+    igraph_i_community_leading_eigenvector_data_t *data = extra;
+    long int j, k, nlen, size = n;
+    igraph_vector_t *idx = data->idx;
+    igraph_vector_t *idx2 = data->idx2;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_inclist_t *inclist = data->inclist;
+    igraph_real_t ktx, ktx2;
+    igraph_vector_t *mymembership = data->mymembership;
+    long int comm = data->comm;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_t *strength = data->strength;
+    igraph_real_t sw = data->sumweights;
+
+    /* Ax */
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_vector_int_t *inc = igraph_inclist_get(inclist, oldid);
+        nlen = igraph_vector_int_size(inc);
+        to[j] = 0.0;
+        VECTOR(*tmp)[j] = 0.0;
+        for (k = 0; k < nlen; k++) {
+            long int edge = (long int) VECTOR(*inc)[k];
+            igraph_real_t w = VECTOR(*weights)[edge];
+            long int nei = IGRAPH_OTHER(graph, edge, oldid);
+            long int neimemb = (long int) VECTOR(*mymembership)[nei];
+            if (neimemb == comm) {
+                long int fi = (long int) VECTOR(*idx2)[nei];
+                if (fi < size) {
+                    to[j] += from[fi] * w;
+                }
+                VECTOR(*tmp)[j] += w;
+            }
+        }
+    }
+
+    /* k^Tx/2m */
+    ktx = 0.0; ktx2 = 0.0;
+    for (j = 0; j < size + 1; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_real_t str = VECTOR(*strength)[oldid];
+        if (j < size) {
+            ktx += from[j] * str;
+        }
+        ktx2 += str;
+    }
+    ktx = ktx / sw / 2.0;
+    ktx2 = ktx2 / sw / 2.0;
+
+    /* Bx */
+    for (j = 0; j < size; j++) {
+        long int oldid = (long int) VECTOR(*idx)[j];
+        igraph_real_t str = VECTOR(*strength)[oldid];
+        to[j] = to[j] - ktx * str;
+        VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * str;
+    }
+
+    /* -d_ij summa l in G B_il */
+    for (j = 0; j < size; j++) {
+        to[j] -= VECTOR(*tmp)[j] * from[j];
+    }
+
+    return 0;
+}
+
+void igraph_i_levc_free(igraph_vector_ptr_t *ptr) {
+    long int i, n = igraph_vector_ptr_size(ptr);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *v = VECTOR(*ptr)[i];
+        if (v) {
+            igraph_vector_destroy(v);
+            igraph_free(v);
+        }
+    }
+}
+
+void igraph_i_error_handler_none(const char *reason, const char *file,
+                                 int line, int igraph_errno) {
+    IGRAPH_UNUSED(reason);
+    IGRAPH_UNUSED(file);
+    IGRAPH_UNUSED(line);
+    IGRAPH_UNUSED(igraph_errno);
+    /* do nothing */
+}
+
+
+/**
+ * \ingroup communities
+ * \function igraph_community_leading_eigenvector
+ * \brief Leading eigenvector community finding (proper version).
+ *
+ * Newman's leading eigenvector method for detecting community
+ * structure. This is the proper implementation of the recursive,
+ * divisive algorithm: each split is done by maximizing the modularity
+ * regarding the original network, see MEJ Newman: Finding community
+ * structure in networks using the eigenvectors of matrices,
+ * Phys Rev E 74:036104 (2006).
+ *
+ * \param graph The undirected input graph.
+ * \param weights The weights of the edges, or a null pointer for
+ *    unweighted graphs.
+ * \param merges The result of the algorithm, a matrix containing the
+ *    information about the splits performed. The matrix is built in
+ *    the opposite way however, it is like the result of an
+ *    agglomerative algorithm. If at the end of the algorithm (after
+ *    \p steps steps was done) there are <quote>p</quote> communities,
+ *    then these are numbered from zero to <quote>p-1</quote>. The
+ *    first line of the matrix contains the first <quote>merge</quote>
+ *    (which is in reality the last split) of two communities into
+ *    community <quote>p</quote>, the merge in the second line forms
+ *    community <quote>p+1</quote>, etc. The matrix should be
+ *    initialized before calling and will be resized as needed.
+ *    This argument is ignored of it is \c NULL.
+ * \param membership The membership of the vertices after all the
+ *    splits were performed will be stored here. The vector must be
+ *    initialized  before calling and will be resized as needed.
+ *    This argument is ignored if it is \c NULL. This argument can
+ *    also be used to supply a starting configuration for the community
+ *    finding, in the format of a membership vector. In this case the
+ *    \p start argument must be set to 1.
+ * \param steps The maximum number of steps to perform. It might
+ *    happen that some component (or the whole network) has no
+ *    underlying community structure and no further steps can be
+ *    done. If you want as many steps as possible then supply the
+ *    number of vertices in the network here.
+ * \param options The options for ARPACK. \c n is always
+ *    overwritten. \c ncv is set to at least 4.
+ * \param modularity If not a null pointer, then it must be a pointer
+ *    to a real number and the modularity score of the final division
+ *    is stored here.
+ * \param start Boolean, whether to use the community structure given
+ *    in the \p membership argument as a starting point.
+ * \param eigenvalues Pointer to an initialized vector or a null
+ *    pointer. If not a null pointer, then the eigenvalues calculated
+ *    along the community structure detection are stored here. The
+ *    non-positive eigenvalues, that do not result a split, are stored
+ *    as well.
+ * \param eigenvectors If not a null pointer, then the eigenvectors
+ *    that are calculated in each step of the algorithm, are stored here,
+ *    in a pointer vector. Each eigenvector is stored in an
+ *    \ref igraph_vector_t object. The user is responsible of
+ *    deallocating the memory that belongs to the individual vectors,
+ *    by calling first \ref igraph_vector_destroy(), and then
+ *    <code>free()</code> on them.
+ * \param history Pointer to an initialized vector or a null pointer.
+ *    If not a null pointer, then a trace of the algorithm is stored
+ *    here, encoded numerically. The various operations:
+ *    \clist
+ *    \cli IGRAPH_LEVC_HIST_START_FULL
+ *      Start the algorithm from an initial state where each connected
+ *      component is a separate community.
+ *    \cli IGRAPH_LEVC_HIST_START_GIVEN
+ *      Start the algorithm from a given community structure. The next
+ *      value in the vector contains the initial number of
+ *      communities.
+ *    \cli IGRAPH_LEVC_HIST_SPLIT
+ *      Split a community into two communities. The id of the splitted
+ *      community is given in the next element of the history vector.
+ *      The id of the first new community is the same as the id of the
+ *      splitted community. The id of the second community equals to
+ *      the number of communities before the split.
+ *    \cli IGRAPH_LEVC_HIST_FAILED
+ *      Tried to split a community, but it was not worth it, as it
+ *      does not result in a bigger modularity value. The id of the
+ *      community is given in the next element of the vector.
+ *    \endclist
+ * \param callback A null pointer or a function of type \ref
+ *    igraph_community_leading_eigenvector_callback_t. If given, this
+ *    callback function is called after each eigenvector/eigenvalue
+ *    calculation. If the callback returns a non-zero value, then the
+ *    community finding algorithm stops. See the arguments passed to
+ *    the callback at the documentation of \ref
+ *    igraph_community_leading_eigenvector_callback_t.
+ * \param callback_extra Extra argument to pass to the callback
+ *    function.
+ * \return Error code.
+ *
+ * \sa \ref igraph_community_walktrap() and \ref
+ * igraph_community_spinglass() for other community structure
+ * detection methods.
+ *
+ * Time complexity: O(|E|+|V|^2*steps), |V| is the number of vertices,
+ * |E| the number of edges, <quote>steps</quote> the number of splits
+ * performed.
+ */
+
+int igraph_community_leading_eigenvector(const igraph_t *graph,
+        const igraph_vector_t *weights,
+        igraph_matrix_t *merges,
+        igraph_vector_t *membership,
+        igraph_integer_t steps,
+        igraph_arpack_options_t *options,
+        igraph_real_t *modularity,
+        igraph_bool_t start,
+        igraph_vector_t *eigenvalues,
+        igraph_vector_ptr_t *eigenvectors,
+        igraph_vector_t *history,
+        igraph_community_leading_eigenvector_callback_t *callback,
+        void *callback_extra) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_dqueue_t tosplit;
+    igraph_vector_t idx, idx2, mymerges;
+    igraph_vector_t strength, tmp;
+    long int staken = 0;
+    igraph_adjlist_t adjlist;
+    igraph_inclist_t inclist;
+    long int i, j, k, l;
+    long int communities;
+    igraph_vector_t vmembership, *mymembership = membership;
+    igraph_i_community_leading_eigenvector_data_t extra;
+    igraph_arpack_storage_t storage;
+    igraph_real_t mod = 0;
+    igraph_arpack_function_t *arpcb1 =
+        weights ? igraph_i_community_leading_eigenvector_weighted :
+        igraph_i_community_leading_eigenvector;
+    igraph_arpack_function_t *arpcb2 =
+        weights ? igraph_i_community_leading_eigenvector2_weighted :
+        igraph_i_community_leading_eigenvector2;
+    igraph_real_t sumweights = 0.0;
+
+    if (weights && no_of_edges != igraph_vector_size(weights)) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    if (start && !membership) {
+        IGRAPH_ERROR("Cannot start from given configuration if memberships "
+                     "missing", IGRAPH_EINVAL);
+    }
+
+    if (start && membership &&
+        igraph_vector_size(membership) != no_of_nodes) {
+        IGRAPH_ERROR("Wrong length for vector of predefined memberships",
+                     IGRAPH_EINVAL);
+    }
+
+    if (start && membership && igraph_vector_max(membership) >= no_of_nodes) {
+        IGRAPH_WARNING("Too many communities in membership start vector");
+    }
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_WARNING("This method was developed for undirected graphs");
+    }
+
+    if (steps < 0 || steps > no_of_nodes - 1) {
+        steps = (igraph_integer_t) no_of_nodes - 1;
+    }
+
+    if (!membership) {
+        mymembership = &vmembership;
+        IGRAPH_VECTOR_INIT_FINALLY(mymembership, 0);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&mymerges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&mymerges, steps * 2));
+    IGRAPH_VECTOR_INIT_FINALLY(&idx, 0);
+    if (eigenvalues)  {
+        igraph_vector_clear(eigenvalues);
+    }
+    if (eigenvectors) {
+        igraph_vector_ptr_clear(eigenvectors);
+        IGRAPH_FINALLY(igraph_i_levc_free, eigenvectors);
+    }
+
+    IGRAPH_STATUS("Starting leading eigenvector method.\n", 0);
+
+    if (!start) {
+        /* Calculate the weakly connected components in the graph and use them as
+         * an initial split */
+        IGRAPH_CHECK(igraph_clusters(graph, mymembership, &idx, 0, IGRAPH_WEAK));
+        communities = igraph_vector_size(&idx);
+        IGRAPH_STATUSF(("Starting from %li component(s).\n", 0, communities));
+        if (history) {
+            IGRAPH_CHECK(igraph_vector_push_back(history,
+                                                 IGRAPH_LEVC_HIST_START_FULL));
+        }
+    } else {
+        /* Just create the idx vector for the given membership vector */
+        communities = (long int) igraph_vector_max(mymembership) + 1;
+        IGRAPH_STATUSF(("Starting from given membership vector with %li "
+                        "communities.\n", 0, communities));
+        if (history) {
+            IGRAPH_CHECK(igraph_vector_push_back(history,
+                                                 IGRAPH_LEVC_HIST_START_GIVEN));
+            IGRAPH_CHECK(igraph_vector_push_back(history, communities));
+        }
+        IGRAPH_CHECK(igraph_vector_resize(&idx, communities));
+        igraph_vector_null(&idx);
+        for (i = 0; i < no_of_nodes; i++) {
+            int t = (int) VECTOR(*mymembership)[i];
+            VECTOR(idx)[t] += 1;
+        }
+    }
+
+    IGRAPH_DQUEUE_INIT_FINALLY(&tosplit, 100);
+    for (i = 0; i < communities; i++) {
+        if (VECTOR(idx)[i] > 2) {
+            igraph_dqueue_push(&tosplit, i);
+        }
+    }
+    for (i = 1; i < communities; i++) {
+        /* Record merge */
+        IGRAPH_CHECK(igraph_vector_push_back(&mymerges, i - 1));
+        IGRAPH_CHECK(igraph_vector_push_back(&mymerges, i));
+        if (eigenvalues) {
+            IGRAPH_CHECK(igraph_vector_push_back(eigenvalues, IGRAPH_NAN));
+        }
+        if (eigenvectors) {
+            igraph_vector_t *v = igraph_Calloc(1, igraph_vector_t);
+            if (!v) {
+                IGRAPH_ERROR("Cannot do leading eigenvector community detection",
+                             IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, v);
+            IGRAPH_VECTOR_INIT_FINALLY(v, 0);
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(eigenvectors, v));
+            IGRAPH_FINALLY_CLEAN(2);
+        }
+        if (history) {
+            IGRAPH_CHECK(igraph_vector_push_back(history, IGRAPH_LEVC_HIST_SPLIT));
+            IGRAPH_CHECK(igraph_vector_push_back(history, i - 1));
+        }
+    }
+    staken = communities - 1;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_of_nodes);
+    IGRAPH_CHECK(igraph_vector_resize(&idx, no_of_nodes));
+    igraph_vector_null(&idx);
+    IGRAPH_VECTOR_INIT_FINALLY(&idx2, no_of_nodes);
+    if (!weights) {
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+    } else {
+        IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+        IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes);
+        IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(),
+                                     IGRAPH_ALL, IGRAPH_LOOPS, weights));
+        sumweights = igraph_vector_sum(weights);
+    }
+
+    options->ncv = 0;   /* 0 means "automatic" in igraph_arpack_rssolve */
+    options->start = 0;
+    options->which[0] = 'L'; options->which[1] = 'A';
+
+    /* Memory for ARPACK */
+    /* We are allocating memory for 20 eigenvectors since options->ncv won't be
+     * larger than 20 when using automatic mode in igraph_arpack_rssolve */
+    IGRAPH_CHECK(igraph_arpack_storage_init(&storage, (int) no_of_nodes, 20,
+                                            (int) no_of_nodes, 1));
+    IGRAPH_FINALLY(igraph_arpack_storage_destroy, &storage);
+    extra.idx = &idx;
+    extra.idx2 = &idx2;
+    extra.tmp = &tmp;
+    extra.adjlist = &adjlist;
+    extra.inclist = &inclist;
+    extra.weights = weights;
+    extra.sumweights = sumweights;
+    extra.graph = graph;
+    extra.strength = &strength;
+    extra.no_of_edges = no_of_edges;
+    extra.mymembership = mymembership;
+
+    while (!igraph_dqueue_empty(&tosplit) && staken < steps) {
+        long int comm = (long int) igraph_dqueue_pop_back(&tosplit);
+        /* depth first search */
+        long int size = 0;
+        igraph_real_t tmpev;
+
+        IGRAPH_STATUSF(("Trying to split community %li... ", 0, comm));
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        for (i = 0; i < no_of_nodes; i++) {
+            if (VECTOR(*mymembership)[i] == comm) {
+                VECTOR(idx)[size] = i;
+                VECTOR(idx2)[i] = size++;
+            }
+        }
+
+        staken++;
+        if (size <= 2) {
+            continue;
+        }
+
+        /* We solve two eigenproblems, one for the original modularity
+           matrix, and one for the modularity matrix after deleting the
+           last row and last column from it. This is a trick to find
+           multiple leading eigenvalues, because ARPACK is sometimes
+           unstable when the first two eigenvalues are requested, but it
+           does much better for the single principal eigenvalue. */
+
+        /* We start with the smaller eigenproblem. */
+
+        options->n = (int) size - 1;
+        options->info = 0;
+        options->nev = 1;
+        options->ldv = 0;
+        options->ncv = 0;   /* 0 means "automatic" in igraph_arpack_rssolve */
+        options->nconv = 0;
+        options->lworkl = 0;        /* we surely have enough space */
+        extra.comm = comm;
+
+        /* We try calling the solver twice, once from a random starting
+           point, once from a fixed one. This is because for some hard
+           cases it tends to fail. We need to suppress error handling for
+           the first call. */
+        {
+            int i;
+            igraph_error_handler_t *errh =
+                igraph_set_error_handler(igraph_i_error_handler_none);
+            igraph_warning_handler_t *warnh =
+                igraph_set_warning_handler(igraph_warning_handler_ignore);
+            igraph_arpack_rssolve(arpcb2, &extra, options, &storage,
+                                  /*values=*/ 0, /*vectors=*/ 0);
+            igraph_set_error_handler(errh);
+            igraph_set_warning_handler(warnh);
+            if (options->nconv < 1) {
+                /* Call again from a fixed starting point. Note that we cannot use a
+                 * fixed all-1 starting vector as sometimes ARPACK would return a
+                 * 'starting vector is zero' error -- this is of course not true but
+                 * it's a result of ARPACK >= 3.6.3 trying to force the starting vector
+                 * into the range of OP (i.e. the matrix being solved). The initial
+                 * vector we use here seems to work, but I have no theoretical argument
+                 * for its usage; it just happens to work. */
+                options->start = 1;
+                options->info = 0;
+                options->ncv = 0;
+                options->lworkl = 0;    /* we surely have enough space */
+                for (i = 0; i < options->n ; i++) {
+                    storage.resid[i] = i % 2 ? 1 : -1;
+                }
+                IGRAPH_CHECK(igraph_arpack_rssolve(arpcb2, &extra, options, &storage,
+                                                   /*values=*/ 0, /*vectors=*/ 0));
+                options->start = 0;
+            }
+        }
+
+        if (options->nconv < 1) {
+            IGRAPH_ERROR("ARPACK did not converge", IGRAPH_ARPACK_FAILED);
+        }
+
+        tmpev = storage.d[0];
+
+        /* Now we do the original eigenproblem, again, twice if needed */
+
+        options->n = (int) size;
+        options->info = 0;
+        options->nev = 1;
+        options->ldv = 0;
+        options->nconv = 0;
+        options->lworkl = 0;    /* we surely have enough space */
+        options->ncv = 0;   /* 0 means "automatic" in igraph_arpack_rssolve */
+
+        {
+            int i;
+            igraph_error_handler_t *errh =
+                igraph_set_error_handler(igraph_i_error_handler_none);
+            igraph_arpack_rssolve(arpcb1, &extra, options, &storage,
+                                  /*values=*/ 0, /*vectors=*/ 0);
+            igraph_set_error_handler(errh);
+            if (options->nconv < 1) {
+                /* Call again from a fixed starting point. See the comment a few lines
+                 * above about the exact choice of this starting vector */
+                options->start = 1;
+                options->info = 0;
+                options->ncv = 0;
+                options->lworkl = 0;    /* we surely have enough space */
+                for (i = 0; i < options->n; i++) {
+                    storage.resid[i] = i % 2 ? 1 : -1;
+                }
+                IGRAPH_CHECK(igraph_arpack_rssolve(arpcb1, &extra, options, &storage,
+                                                   /*values=*/ 0, /*vectors=*/ 0));
+                options->start = 0;
+            }
+        }
+
+        if (options->nconv < 1) {
+            IGRAPH_ERROR("ARPACK did not converge", IGRAPH_ARPACK_FAILED);
+        }
+
+        /* Ok, we have the leading eigenvector of the modularity matrix*/
+
+        /* ---------------------------------------------------------------*/
+        /* To avoid numeric errors */
+        if (fabs(storage.d[0]) < 1e-8) {
+            storage.d[0] = 0;
+        }
+
+        /* We replace very small (in absolute value) elements of the
+           leading eigenvector with zero, to get the same result,
+           consistently.*/
+        for (i = 0; i < size; i++) {
+            if (fabs(storage.v[i]) < 1e-8) {
+                storage.v[i] = 0;
+            }
+        }
+
+        /* Just to have the always the same result, we multiply by -1
+           if the first (nonzero) element is not positive. */
+        for (i = 0; i < size; i++) {
+            if (storage.v[i] != 0) {
+                break;
+            }
+        }
+        if (i < size && storage.v[i] < 0) {
+            for (i = 0; i < size; i++) {
+                storage.v[i] = - storage.v[i];
+            }
+        }
+        /* ---------------------------------------------------------------*/
+
+        if (callback) {
+            igraph_vector_t vv;
+            int ret;
+            igraph_vector_view(&vv, storage.v, size);
+            ret = callback(mymembership, comm, storage.d[0], &vv,
+                           arpcb1, &extra, callback_extra);
+            if (ret) {
+                break;
+            }
+        }
+
+        if (eigenvalues) {
+            IGRAPH_CHECK(igraph_vector_push_back(eigenvalues, storage.d[0]));
+        }
+
+        if (eigenvectors) {
+            igraph_vector_t *v = igraph_Calloc(1, igraph_vector_t);
+            if (!v) {
+                IGRAPH_ERROR("Cannot do leading eigenvector community detection",
+                             IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, v);
+            IGRAPH_VECTOR_INIT_FINALLY(v, size);
+            for (i = 0; i < size; i++) {
+                VECTOR(*v)[i] = storage.v[i];
+            }
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(eigenvectors, v));
+            IGRAPH_FINALLY_CLEAN(2);
+        }
+
+        if (storage.d[0] <= 0) {
+            IGRAPH_STATUS("no split.\n", 0);
+            if (history) {
+                IGRAPH_CHECK(igraph_vector_push_back(history,
+                                                     IGRAPH_LEVC_HIST_FAILED));
+                IGRAPH_CHECK(igraph_vector_push_back(history, comm));
+            }
+            continue;
+        }
+
+        /* Check for multiple leading eigenvalues */
+
+        if (fabs(storage.d[0] - tmpev) < 1e-8) {
+            IGRAPH_STATUS("multiple principal eigenvalue, no split.\n", 0);
+            if (history) {
+                IGRAPH_CHECK(igraph_vector_push_back(history,
+                                                     IGRAPH_LEVC_HIST_FAILED));
+                IGRAPH_CHECK(igraph_vector_push_back(history, comm));
+            }
+            continue;
+        }
+
+        /* Count the number of vertices in each community after the split */
+        l = 0;
+        for (j = 0; j < size; j++) {
+            if (storage.v[j] < 0) {
+                storage.v[j] = -1;
+                l++;
+            } else {
+                storage.v[j] = 1;
+            }
+        }
+        if (l == 0 || l == size) {
+            IGRAPH_STATUS("no split.\n", 0);
+            if (history) {
+                IGRAPH_CHECK(igraph_vector_push_back(history,
+                                                     IGRAPH_LEVC_HIST_FAILED));
+                IGRAPH_CHECK(igraph_vector_push_back(history, comm));
+            }
+            continue;
+        }
+
+        /* Check that Q increases with our choice of split */
+        arpcb1(storage.v + size, storage.v, (int) size, &extra);
+        mod = 0;
+        for (i = 0; i < size; i++) {
+            mod += storage.v[size + i] * storage.v[i];
+        }
+        if (mod <= 1e-8) {
+            IGRAPH_STATUS("no modularity increase, no split.\n", 0);
+            if (history) {
+                IGRAPH_CHECK(igraph_vector_push_back(history,
+                                                     IGRAPH_LEVC_HIST_FAILED));
+                IGRAPH_CHECK(igraph_vector_push_back(history, comm));
+            }
+            continue;
+        }
+
+        communities++;
+        IGRAPH_STATUS("split.\n", 0);
+
+        /* Rewrite the mymembership vector */
+        for (j = 0; j < size; j++) {
+            if (storage.v[j] < 0) {
+                long int oldid = (long int) VECTOR(idx)[j];
+                VECTOR(*mymembership)[oldid] = communities - 1;
+            }
+        }
+
+        /* Record merge */
+        IGRAPH_CHECK(igraph_vector_push_back(&mymerges, comm));
+        IGRAPH_CHECK(igraph_vector_push_back(&mymerges, communities - 1));
+        if (history) {
+            IGRAPH_CHECK(igraph_vector_push_back(history, IGRAPH_LEVC_HIST_SPLIT));
+            IGRAPH_CHECK(igraph_vector_push_back(history, comm));
+        }
+
+        /* Store the resulting communities in the queue if needed */
+        if (l > 1) {
+            IGRAPH_CHECK(igraph_dqueue_push(&tosplit, communities - 1));
+        }
+        if (size - l > 1) {
+            IGRAPH_CHECK(igraph_dqueue_push(&tosplit, comm));
+        }
+
+    }
+
+    igraph_arpack_storage_destroy(&storage);
+    IGRAPH_FINALLY_CLEAN(1);
+    if (!weights) {
+        igraph_adjlist_destroy(&adjlist);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        igraph_inclist_destroy(&inclist);
+        igraph_vector_destroy(&strength);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+    igraph_dqueue_destroy(&tosplit);
+    igraph_vector_destroy(&tmp);
+    igraph_vector_destroy(&idx2);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    IGRAPH_STATUS("Done.\n", 0);
+
+    /* reform the mymerges vector */
+    if (merges) {
+        igraph_vector_null(&idx);
+        l = igraph_vector_size(&mymerges);
+        k = communities;
+        j = 0;
+        IGRAPH_CHECK(igraph_matrix_resize(merges, l / 2, 2));
+        for (i = l; i > 0; i -= 2) {
+            long int from = (long int) VECTOR(mymerges)[i - 1];
+            long int to = (long int) VECTOR(mymerges)[i - 2];
+            MATRIX(*merges, j, 0) = VECTOR(mymerges)[i - 2];
+            MATRIX(*merges, j, 1) = VECTOR(mymerges)[i - 1];
+            if (VECTOR(idx)[from] != 0) {
+                MATRIX(*merges, j, 1) = VECTOR(idx)[from] - 1;
+            }
+            if (VECTOR(idx)[to] != 0) {
+                MATRIX(*merges, j, 0) = VECTOR(idx)[to] - 1;
+            }
+            VECTOR(idx)[to] = ++k;
+            j++;
+        }
+    }
+
+    if (eigenvectors) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    igraph_vector_destroy(&idx);
+    igraph_vector_destroy(&mymerges);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    if (modularity) {
+        IGRAPH_CHECK(igraph_modularity(graph, mymembership, modularity,
+                                       weights));
+    }
+
+    if (!membership) {
+        igraph_vector_destroy(mymembership);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_le_community_to_membership
+ * Vertex membership from the leading eigenvector community structure
+ *
+ * This function creates a membership vector from the
+ * result of \ref igraph_community_leading_eigenvector(),
+ * It takes \c membership
+ * and performs \c steps merges, according to the supplied
+ * \c merges matrix.
+ * \param merges The matrix defining the merges to make.
+ *     This is usually from the output of the leading eigenvector community
+ *     structure detection routines.
+ * \param steps The number of steps to make according to \c merges.
+ * \param membership Initially the starting membership vector,
+ *     on output the resulting membership vector, after performing \c steps merges.
+ * \param csize Optionally the sizes of the communities is stored here,
+ *     if this is not a null pointer, but an initialized vector.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|), the number of vertices.
+ */
+
+int igraph_le_community_to_membership(const igraph_matrix_t *merges,
+                                      igraph_integer_t steps,
+                                      igraph_vector_t *membership,
+                                      igraph_vector_t *csize) {
+
+    long int no_of_nodes = igraph_vector_size(membership);
+    igraph_vector_t fake_memb;
+    long int components, i;
+
+    if (igraph_matrix_nrow(merges) < steps) {
+        IGRAPH_ERROR("`steps' to big or `merges' matrix too short", IGRAPH_EINVAL);
+    }
+
+    components = (long int) igraph_vector_max(membership) + 1;
+    if (components > no_of_nodes) {
+        IGRAPH_ERROR("Invalid membership vector, too many components", IGRAPH_EINVAL);
+    }
+    if (steps >= components) {
+        IGRAPH_ERROR("Cannot make `steps' steps from supplied membership vector",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&fake_memb, components);
+
+    /* Check membership vector */
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*membership)[i] < 0) {
+            IGRAPH_ERROR("Invalid membership vector, negative id", IGRAPH_EINVAL);
+        }
+        VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ] += 1;
+    }
+    for (i = 0; i < components; i++) {
+        if (VECTOR(fake_memb)[i] == 0) {
+            IGRAPH_ERROR("Invalid membership vector, empty cluster", IGRAPH_EINVAL);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_community_to_membership(merges, (igraph_integer_t)
+                 components, steps,
+                 &fake_memb, 0));
+
+    /* Ok, now we have the membership of the initial components,
+       rewrite the original membership vector. */
+
+    if (csize) {
+        IGRAPH_CHECK(igraph_vector_resize(csize, components - steps));
+        igraph_vector_null(csize);
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(*membership)[i] = VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ];
+        if (csize) {
+            VECTOR(*csize)[ (long int) VECTOR(*membership)[i] ] += 1;
+        }
+    }
+
+    igraph_vector_destroy(&fake_memb);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/********************************************************************/
+
+/**
+ * \ingroup communities
+ * \function igraph_community_fluid_communities
+ * \brief Community detection algorithm based on the simple idea of
+ * several fluids interacting in a non-homogeneous environment
+ * (the graph topology), expanding and contracting based on their
+ * interaction and density.
+ *
+ * This function implements the community detection method described in:
+ * Parés F, Gasulla DG, et. al. (2018) Fluid Communities: A Competitive,
+ * Scalable and Diverse Community Detection Algorithm. In: Complex Networks
+ * &amp; Their Applications VI: Proceedings of Complex Networks 2017 (The Sixth
+ * International Conference on Complex Networks and Their Applications),
+ * Springer, vol 689, p 229.
+ *
+ * \param graph The input graph. The graph must be simple and connected.
+ *   Empty graphs are not supported as well as single vertex graphs.
+ *   Edge directions are ignored. Weights are not considered.
+ * \param no_of_communities The number of communities to be found. Must be
+ *   greater than 0 and fewer than number of vertices in the graph.
+ * \param membership The result vector mapping vertices to the communities
+ * they are assigned to.
+ * \param modularity If not a null pointer, then it must be a pointer
+ *   to a real number. The modularity score of the detected community
+ *   structure is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|)
+ *
+ * \example examples/tests/igraph_community_fluid_communities.c
+ */
+int igraph_community_fluid_communities(const igraph_t *graph,
+                                       igraph_integer_t no_of_communities,
+                                       igraph_vector_t *membership,
+                                       igraph_real_t *modularity) {
+    /* Declaration of variables */
+    long int no_of_nodes, i, j, k, kv1;
+    igraph_adjlist_t al;
+    double max_density;
+    igraph_bool_t res, running;
+    igraph_vector_t node_order, density, label_counters, dominant_labels, nonzero_labels;
+    igraph_vector_int_t com_to_numvertices;
+
+    /* Initialization of variables needed for initial checking */
+    no_of_nodes = igraph_vcount(graph);
+
+    /* Checking input values */
+    if (no_of_nodes < 2) {
+        IGRAPH_ERROR("Empty and single vertex graphs are not supported.", IGRAPH_EINVAL);
+    }
+    if ((long int) no_of_communities < 1) {
+        IGRAPH_ERROR("'no_of_communities' must be greater than 0.", IGRAPH_EINVAL);
+    }
+    if ((long int) no_of_communities > no_of_nodes) {
+        IGRAPH_ERROR("'no_of_communities' can not be greater than number of nodes in "
+                     "the graph.", IGRAPH_EINVAL);
+    }
+    igraph_is_simple(graph, &res);
+    if (!res) {
+        IGRAPH_ERROR("Only simple graphs are supported.", IGRAPH_EINVAL);
+    }
+    igraph_is_connected(graph, &res, IGRAPH_WEAK);
+    if (!res) {
+        IGRAPH_ERROR("Disconnected graphs are not supported.", IGRAPH_EINVAL);
+    }
+    if (igraph_is_directed(graph)) {
+        IGRAPH_WARNING("Edge directions are ignored.");
+    }
+
+    /* Internal variables initialization */
+    max_density = 1.0;
+    running = 1;
+
+    /* Resize membership vector (number of nodes) */
+    IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+
+    /* Initialize density and com_to_numvertices vectors */
+    IGRAPH_CHECK(igraph_vector_init(&density, (long int) no_of_communities));
+    IGRAPH_FINALLY(igraph_vector_destroy, &density);
+    IGRAPH_CHECK(igraph_vector_int_init(&com_to_numvertices, (long int) no_of_communities));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &com_to_numvertices);
+
+    /* Initialize node ordering vector */
+    IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, no_of_nodes - 1));
+    IGRAPH_FINALLY(igraph_vector_destroy, &node_order);
+
+    /* Initialize the membership vector with 0 values */
+    igraph_vector_null(membership);
+    /* Initialize densities to max_density */
+    igraph_vector_fill(&density, max_density);
+
+    RNG_BEGIN();
+
+    /* Initialize com_to_numvertices and initialize communities into membership vector */
+    IGRAPH_CHECK(igraph_vector_shuffle(&node_order));
+    for (i = 0; i < no_of_communities; i++) {
+        /* Initialize membership at initial nodes for each community
+         * where 0 refers to have no label*/
+        VECTOR(*membership)[(long int)VECTOR(node_order)[i]] = i + 1.0;
+        /* Initialize com_to_numvertices list: Number of vertices for each community */
+        VECTOR(com_to_numvertices)[i] = 1;
+    }
+
+    /* Create an adjacency list representation for efficiency. */
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
+
+    /* Create storage space for counting distinct labels and dominant ones */
+    IGRAPH_VECTOR_INIT_FINALLY(&dominant_labels, (long int) no_of_communities);
+    IGRAPH_VECTOR_INIT_FINALLY(&nonzero_labels, (long int) no_of_communities);
+
+    IGRAPH_CHECK(igraph_vector_init(&label_counters, (long int) no_of_communities));
+    IGRAPH_FINALLY(igraph_vector_destroy, &label_counters);
+
+    /* running is the convergence boolean variable */
+    running = 1;
+    while (running) {
+        /* Declarations of varibales used inside main loop */
+        long int v1, size, rand_idx;
+        igraph_real_t max_count, label_counter_diff;
+        igraph_vector_int_t *neis;
+        igraph_bool_t same_label_in_dominant;
+
+        running = 0;
+
+        /* Shuffle the node ordering vector */
+        IGRAPH_CHECK(igraph_vector_shuffle(&node_order));
+        /* In the prescribed order, loop over the vertices and reassign labels */
+        for (i = 0; i < no_of_nodes; i++) {
+            /* Clear dominant_labels and nonzero_labels vectors */
+            igraph_vector_clear(&dominant_labels);
+            igraph_vector_null(&label_counters);
+
+            /* Obtain actual node index */
+            v1 = (long int) VECTOR(node_order)[i];
+            /* Take into account same label in updating rule */
+            kv1 = (long int) VECTOR(*membership)[v1];
+            max_count = 0.0;
+            if (kv1 != 0) {
+                VECTOR(label_counters)[kv1 - 1] += VECTOR(density)[kv1 - 1];
+                /* Set up max_count */
+                max_count = VECTOR(density)[kv1 - 1];
+                /* Initialize dominant_labels */
+                IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1));
+                VECTOR(dominant_labels)[0] = kv1;
+            }
+
+            /* Count the weights corresponding to different labels */
+            neis = igraph_adjlist_get(&al, v1);
+            size = igraph_vector_int_size(neis);
+            for (j = 0; j < size; j++) {
+                k = (long int) VECTOR(*membership)[(long)VECTOR(*neis)[j]];
+                /* skip if it has no label yet */
+                if (k == 0) {
+                    continue;
+                }
+                /* Update label counter and evaluate diff against max_count*/
+                VECTOR(label_counters)[k - 1] += VECTOR(density)[k - 1];
+                label_counter_diff = VECTOR(label_counters)[k - 1] - max_count;
+                /* Check if this label must be included in dominant_labels vector */
+                if (label_counter_diff > 0.0001) {
+                    max_count = VECTOR(label_counters)[k - 1];
+                    IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1));
+                    VECTOR(dominant_labels)[0] = k;
+                } else if (-0.0001 < label_counter_diff && label_counter_diff < 0.0001) {
+                    IGRAPH_CHECK(igraph_vector_push_back(&dominant_labels, k));
+                }
+            }
+
+            if (!igraph_vector_empty(&dominant_labels)) {
+                /* Maintain same label if it exists in dominant_labels */
+                same_label_in_dominant = igraph_vector_contains(&dominant_labels, kv1);
+
+                if (!same_label_in_dominant) {
+                    /* We need at least one more iteration */
+                    running = 1;
+
+                    /* Select randomly from the dominant labels */
+                    rand_idx = RNG_INTEGER(0, igraph_vector_size(&dominant_labels) - 1);
+                    k = (long int) VECTOR(dominant_labels)[rand_idx];
+
+                    if (kv1 != 0) {
+                        /* Subtract 1 vertex from corresponding community in com_to_numvertices */
+                        VECTOR(com_to_numvertices)[kv1 - 1] -= 1;
+                        /* Re-calculate density for community kv1 */
+                        VECTOR(density)[kv1 - 1] = max_density / VECTOR(com_to_numvertices)[kv1 - 1];
+                    }
+
+                    /* Update vertex new label */
+                    VECTOR(*membership)[v1] = k;
+
+                    /* Add 1 vertex to corresponding new community in com_to_numvertices */
+                    VECTOR(com_to_numvertices)[k - 1] += 1;
+                    /* Re-calculate density for new community k */
+                    VECTOR(density)[k - 1] = max_density / VECTOR(com_to_numvertices)[k - 1];
+                }
+            }
+        }
+    }
+
+    RNG_END();
+
+
+    /* Shift back the membership vector */
+    /* There must be no 0 labels in membership vector at this point */
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(*membership)[i] -= 1;
+        /* Something went wrong: At least one vertex has no community assigned */
+        if (VECTOR(*membership)[i] < 0) {
+            IGRAPH_ERROR("Something went wrong during execution. One or more vertices got "
+                         "no community assigned at algorithm convergence.", IGRAPH_EINTERNAL);
+        }
+    }
+
+    igraph_adjlist_destroy(&al);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    if (modularity) {
+        IGRAPH_CHECK(igraph_modularity(graph, membership, modularity,
+                                       NULL));
+    }
+
+    igraph_vector_destroy(&node_order);
+    igraph_vector_destroy(&density);
+    igraph_vector_int_destroy(&com_to_numvertices);
+    igraph_vector_destroy(&label_counters);
+    igraph_vector_destroy(&dominant_labels);
+    igraph_vector_destroy(&nonzero_labels);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return 0;
+}
+
+/********************************************************************/
+
+/**
+ * \ingroup communities
+ * \function igraph_community_label_propagation
+ * \brief Community detection based on label propagation
+ *
+ * This function implements the community detection method described in:
+ * Raghavan, U.N. and Albert, R. and Kumara, S.: Near linear time algorithm
+ * to detect community structures in large-scale networks. Phys Rev E
+ * 76, 036106. (2007). This version extends the original method by
+ * the ability to take edge weights into consideration and also
+ * by allowing some labels to be fixed.
+ *
+ * </para><para>
+ * Weights are taken into account as follows: when the new label of node
+ * i is determined, the algorithm iterates over all edges incident on
+ * node i and calculate the total weight of edges leading to other
+ * nodes with label 0, 1, 2, ..., k-1 (where k is the number of possible
+ * labels). The new label of node i will then be the label whose edges
+ * (among the ones incident on node i) have the highest total weight.
+ *
+ * \param graph The input graph, should be undirected to make sense.
+ * \param membership The membership vector, the result is returned here.
+ *    For each vertex it gives the ID of its community (label).
+ * \param weights The weight vector, it should contain a positive
+ *    weight for all the edges.
+ * \param initial The initial state. If NULL, every vertex will have
+ *   a different label at the beginning. Otherwise it must be a vector
+ *   with an entry for each vertex. Non-negative values denote different
+ *   labels, negative entries denote vertices without labels.
+ * \param fixed Boolean vector denoting which labels are fixed. Of course
+ *   this makes sense only if you provided an initial state, otherwise
+ *   this element will be ignored. Also note that vertices without labels
+ *   cannot be fixed.
+ * \param modularity If not a null pointer, then it must be a pointer
+ *   to a real number. The modularity score of the detected community
+ *   structure is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(m+n)
+ *
+ * \example examples/simple/igraph_community_label_propagation.c
+ */
+int igraph_community_label_propagation(const igraph_t *graph,
+                                       igraph_vector_t *membership,
+                                       const igraph_vector_t *weights,
+                                       const igraph_vector_t *initial,
+                                       igraph_vector_bool_t *fixed,
+                                       igraph_real_t *modularity) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int no_of_not_fixed_nodes = no_of_nodes;
+    long int i, j, k;
+    igraph_adjlist_t al;
+    igraph_inclist_t il;
+    igraph_bool_t running = 1;
+
+    igraph_vector_t label_counters, dominant_labels, nonzero_labels, node_order;
+
+    /* The implementation uses a trick to avoid negative array indexing:
+     * elements of the membership vector are increased by 1 at the start
+     * of the algorithm; this to allow us to denote unlabeled vertices
+     * (if any) by zeroes. The membership vector is shifted back in the end
+     */
+
+    /* Do some initial checks */
+    if (fixed && igraph_vector_bool_size(fixed) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid fixed labeling vector length", IGRAPH_EINVAL);
+    }
+    if (weights) {
+        if (igraph_vector_size(weights) != no_of_edges) {
+            IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+        } else if (igraph_vector_min(weights) < 0) {
+            IGRAPH_ERROR("Weights must be non-negative", IGRAPH_EINVAL);
+        }
+    }
+    if (fixed && !initial) {
+        IGRAPH_WARNING("Ignoring fixed vertices as no initial labeling given");
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+
+    if (initial) {
+        if (igraph_vector_size(initial) != no_of_nodes) {
+            IGRAPH_ERROR("Invalid initial labeling vector length", IGRAPH_EINVAL);
+        }
+        /* Check if the labels used are valid, initialize membership vector */
+        for (i = 0; i < no_of_nodes; i++) {
+            if (VECTOR(*initial)[i] < 0) {
+                VECTOR(*membership)[i] = 0;
+            } else {
+                VECTOR(*membership)[i] = floor(VECTOR(*initial)[i]) + 1;
+            }
+        }
+        if (fixed) {
+            for (i = 0; i < no_of_nodes; i++) {
+                if (VECTOR(*fixed)[i]) {
+                    if (VECTOR(*membership)[i] == 0) {
+                        IGRAPH_WARNING("Fixed nodes cannot be unlabeled, ignoring them");
+                        VECTOR(*fixed)[i] = 0;
+                    } else {
+                        no_of_not_fixed_nodes--;
+                    }
+                }
+            }
+        }
+
+        i = (long int) igraph_vector_max(membership);
+        if (i > no_of_nodes) {
+            IGRAPH_ERROR("elements of the initial labeling vector must be between 0 and |V|-1", IGRAPH_EINVAL);
+        }
+        if (i <= 0) {
+            IGRAPH_ERROR("at least one vertex must be labeled in the initial labeling", IGRAPH_EINVAL);
+        }
+    } else {
+        for (i = 0; i < no_of_nodes; i++) {
+            VECTOR(*membership)[i] = i + 1;
+        }
+    }
+
+    /* Create an adjacency/incidence list representation for efficiency.
+     * For the unweighted case, the adjacency list is enough. For the
+     * weighted case, we need the incidence list */
+    if (weights) {
+        IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &il);
+    } else {
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_IN));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
+    }
+
+    /* Create storage space for counting distinct labels and dominant ones */
+    IGRAPH_VECTOR_INIT_FINALLY(&label_counters, no_of_nodes + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&dominant_labels, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&nonzero_labels, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&dominant_labels, 2));
+
+    RNG_BEGIN();
+
+    /* Initialize node ordering vector with only the not fixed nodes */
+    if (fixed) {
+        IGRAPH_VECTOR_INIT_FINALLY(&node_order, no_of_not_fixed_nodes);
+        for (i = 0, j = 0; i < no_of_nodes; i++) {
+            if (!VECTOR(*fixed)[i]) {
+                VECTOR(node_order)[j] = i;
+                j++;
+            }
+        }
+    } else {
+        IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, no_of_nodes - 1));
+        IGRAPH_FINALLY(igraph_vector_destroy, &node_order);
+    }
+
+    running = 1;
+    while (running) {
+        long int v1, num_neis;
+        igraph_real_t max_count;
+        igraph_vector_int_t *neis;
+        igraph_vector_int_t *ineis;
+        igraph_bool_t was_zero;
+
+        running = 0;
+
+        /* Shuffle the node ordering vector */
+        IGRAPH_CHECK(igraph_vector_shuffle(&node_order));
+        /* In the prescribed order, loop over the vertices and reassign labels */
+        for (i = 0; i < no_of_not_fixed_nodes; i++) {
+            v1 = (long int) VECTOR(node_order)[i];
+
+            /* Count the weights corresponding to different labels */
+            igraph_vector_clear(&dominant_labels);
+            igraph_vector_clear(&nonzero_labels);
+            max_count = 0.0;
+            if (weights) {
+                ineis = igraph_inclist_get(&il, v1);
+                num_neis = igraph_vector_int_size(ineis);
+                for (j = 0; j < num_neis; j++) {
+                    k = (long int) VECTOR(*membership)[
+                    (long)IGRAPH_OTHER(graph, VECTOR(*ineis)[j], v1) ];
+                    if (k == 0) {
+                        continue;    /* skip if it has no label yet */
+                    }
+                    was_zero = (VECTOR(label_counters)[k] == 0);
+                    VECTOR(label_counters)[k] += VECTOR(*weights)[(long)VECTOR(*ineis)[j]];
+                    if (was_zero && VECTOR(label_counters)[k] != 0) {
+                        /* counter just became nonzero */
+                        IGRAPH_CHECK(igraph_vector_push_back(&nonzero_labels, k));
+                    }
+                    if (max_count < VECTOR(label_counters)[k]) {
+                        max_count = VECTOR(label_counters)[k];
+                        IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1));
+                        VECTOR(dominant_labels)[0] = k;
+                    } else if (max_count == VECTOR(label_counters)[k]) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&dominant_labels, k));
+                    }
+                }
+            } else {
+                neis = igraph_adjlist_get(&al, v1);
+                num_neis = igraph_vector_int_size(neis);
+                for (j = 0; j < num_neis; j++) {
+                    k = (long int) VECTOR(*membership)[(long)VECTOR(*neis)[j]];
+                    if (k == 0) {
+                        continue;    /* skip if it has no label yet */
+                    }
+                    VECTOR(label_counters)[k]++;
+                    if (VECTOR(label_counters)[k] == 1) {
+                        /* counter just became nonzero */
+                        IGRAPH_CHECK(igraph_vector_push_back(&nonzero_labels, k));
+                    }
+                    if (max_count < VECTOR(label_counters)[k]) {
+                        max_count = VECTOR(label_counters)[k];
+                        IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1));
+                        VECTOR(dominant_labels)[0] = k;
+                    } else if (max_count == VECTOR(label_counters)[k]) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&dominant_labels, k));
+                    }
+                }
+            }
+
+            if (igraph_vector_size(&dominant_labels) > 0) {
+                /* Select randomly from the dominant labels */
+                k = RNG_INTEGER(0, igraph_vector_size(&dominant_labels) - 1);
+                k = (long int) VECTOR(dominant_labels)[k];
+                /* Check if the _current_ label of the node is also dominant */
+                if (VECTOR(label_counters)[(long)VECTOR(*membership)[v1]] != max_count) {
+                    /* Nope, we need at least one more iteration */
+                    running = 1;
+                }
+                VECTOR(*membership)[v1] = k;
+            }
+
+            /* Clear the nonzero elements in label_counters */
+            num_neis = igraph_vector_size(&nonzero_labels);
+            for (j = 0; j < num_neis; j++) {
+                VECTOR(label_counters)[(long int)VECTOR(nonzero_labels)[j]] = 0;
+            }
+        }
+    }
+
+    RNG_END();
+
+    /* Shift back the membership vector, permute labels in increasing order */
+    /* We recycle label_counters here :) */
+    igraph_vector_fill(&label_counters, -1);
+    j = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        k = (long)VECTOR(*membership)[i] - 1;
+        if (k >= 0) {
+            if (VECTOR(label_counters)[k] == -1) {
+                /* We have seen this label for the first time */
+                VECTOR(label_counters)[k] = j;
+                k = j;
+                j++;
+            } else {
+                k = (long int) VECTOR(label_counters)[k];
+            }
+        } else {
+            /* This is an unlabeled vertex */
+        }
+        VECTOR(*membership)[i] = k;
+    }
+
+    if (weights) {
+        igraph_inclist_destroy(&il);
+    } else {
+        igraph_adjlist_destroy(&al);
+    }
+    IGRAPH_FINALLY_CLEAN(1);
+
+    if (modularity) {
+        IGRAPH_CHECK(igraph_modularity(graph, membership, modularity,
+                                       weights));
+    }
+
+    igraph_vector_destroy(&node_order);
+    igraph_vector_destroy(&label_counters);
+    igraph_vector_destroy(&dominant_labels);
+    igraph_vector_destroy(&nonzero_labels);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/********************************************************************/
+
+/* Structure storing a community */
+typedef struct {
+    igraph_integer_t size;           /* Size of the community */
+    igraph_real_t weight_inside;     /* Sum of edge weights inside community */
+    igraph_real_t weight_all;        /* Sum of edge weights starting/ending
+                                      in the community */
+} igraph_i_multilevel_community;
+
+/* Global community list structure */
+typedef struct {
+    long int communities_no, vertices_no;  /* Number of communities, number of vertices */
+    igraph_real_t weight_sum;              /* Sum of edges weight in the whole graph */
+    igraph_i_multilevel_community *item;   /* List of communities */
+    igraph_vector_t *membership;           /* Community IDs */
+    igraph_vector_t *weights;        /* Graph edge weights */
+} igraph_i_multilevel_community_list;
+
+/* Computes the modularity of a community partitioning */
+igraph_real_t igraph_i_multilevel_community_modularity(
+    const igraph_i_multilevel_community_list *communities) {
+    igraph_real_t result = 0;
+    long int i;
+    igraph_real_t m = communities->weight_sum;
+
+    for (i = 0; i < communities->vertices_no; i++) {
+        if (communities->item[i].size > 0) {
+            result += (communities->item[i].weight_inside - communities->item[i].weight_all * communities->item[i].weight_all / m) / m;
+        }
+    }
+
+    return result;
+}
+
+typedef struct {
+    long int from;
+    long int to;
+    long int id;
+} igraph_i_multilevel_link;
+
+int igraph_i_multilevel_link_cmp(const void *a, const void *b) {
+    long int r = (((igraph_i_multilevel_link*)a)->from -
+                  ((igraph_i_multilevel_link*)b)->from);
+    if (r != 0) {
+        return (int) r;
+    }
+
+    return (int) (((igraph_i_multilevel_link*)a)->to -
+                  ((igraph_i_multilevel_link*)b)->to);
+}
+
+/* removes multiple edges and returns new edge id's for each edge in |E|log|E| */
+int igraph_i_multilevel_simplify_multiple(igraph_t *graph, igraph_vector_t *eids) {
+    long int ecount = igraph_ecount(graph);
+    long int i, l = -1, last_from = -1, last_to = -1;
+    igraph_bool_t directed = igraph_is_directed(graph);
+    igraph_integer_t from, to;
+    igraph_vector_t edges;
+    igraph_i_multilevel_link *links;
+
+    /* Make sure there's enough space in eids to store the new edge IDs */
+    IGRAPH_CHECK(igraph_vector_resize(eids, ecount));
+
+    links = igraph_Calloc(ecount, igraph_i_multilevel_link);
+    if (links == 0) {
+        IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, links);
+
+    for (i = 0; i < ecount; i++) {
+        igraph_edge(graph, (igraph_integer_t) i, &from, &to);
+        links[i].from = from;
+        links[i].to = to;
+        links[i].id = i;
+    }
+
+    qsort((void*)links, (size_t) ecount, sizeof(igraph_i_multilevel_link),
+          igraph_i_multilevel_link_cmp);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    for (i = 0; i < ecount; i++) {
+        if (links[i].from == last_from && links[i].to == last_to) {
+            VECTOR(*eids)[links[i].id] = l;
+            continue;
+        }
+
+        last_from = links[i].from;
+        last_to = links[i].to;
+
+        igraph_vector_push_back(&edges, last_from);
+        igraph_vector_push_back(&edges, last_to);
+
+        l++;
+
+        VECTOR(*eids)[links[i].id] = l;
+    }
+
+    free(links);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_destroy(graph);
+    IGRAPH_CHECK(igraph_create(graph, &edges, igraph_vcount(graph), directed));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+typedef struct {
+    long int community;
+    igraph_real_t weight;
+} igraph_i_multilevel_community_link;
+
+int igraph_i_multilevel_community_link_cmp(const void *a, const void *b) {
+    return (int) (((igraph_i_multilevel_community_link*)a)->community -
+                  ((igraph_i_multilevel_community_link*)b)->community);
+}
+
+/**
+ * Given a graph, a community structure and a vertex ID, this method
+ * calculates:
+ *
+ * - edges: the list of edge IDs that are incident on the vertex
+ * - weight_all: the total weight of these edges
+ * - weight_inside: the total weight of edges that stay within the same
+ *   community where the given vertex is right now, excluding loop edges
+ * - weight_loop: the total weight of loop edges
+ * - links_community and links_weight: together these two vectors list the
+ *   communities incident on this vertex and the total weight of edges
+ *   pointing to these communities
+ */
+int igraph_i_multilevel_community_links(const igraph_t *graph,
+                                        const igraph_i_multilevel_community_list *communities,
+                                        igraph_integer_t vertex, igraph_vector_t *edges,
+                                        igraph_real_t *weight_all, igraph_real_t *weight_inside, igraph_real_t *weight_loop,
+                                        igraph_vector_t *links_community, igraph_vector_t *links_weight) {
+
+    long int i, n, last = -1, c = -1;
+    igraph_real_t weight = 1;
+    long int to, to_community;
+    long int community = (long int) VECTOR(*(communities->membership))[(long int)vertex];
+    igraph_i_multilevel_community_link *links;
+
+    *weight_all = *weight_inside = *weight_loop = 0;
+
+    igraph_vector_clear(links_community);
+    igraph_vector_clear(links_weight);
+
+    /* Get the list of incident edges */
+    igraph_incident(graph, edges, vertex, IGRAPH_ALL);
+
+    n = igraph_vector_size(edges);
+    links = igraph_Calloc(n, igraph_i_multilevel_community_link);
+    if (links == 0) {
+        IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, links);
+
+    for (i = 0; i < n; i++) {
+        long int eidx = (long int) VECTOR(*edges)[i];
+        weight = VECTOR(*communities->weights)[eidx];
+
+        to = IGRAPH_OTHER(graph, eidx, vertex);
+
+        *weight_all += weight;
+        if (to == vertex) {
+            *weight_loop += weight;
+
+            links[i].community = community;
+            links[i].weight = 0;
+            continue;
+        }
+
+        to_community = (long int)VECTOR(*(communities->membership))[to];
+        if (community == to_community) {
+            *weight_inside += weight;
+        }
+
+        /* debug("Link %ld (C: %ld) <-> %ld (C: %ld)\n", vertex, community, to, to_community); */
+
+        links[i].community = to_community;
+        links[i].weight = weight;
+    }
+
+    /* Sort links by community ID and merge the same */
+    qsort((void*)links, (size_t) n, sizeof(igraph_i_multilevel_community_link),
+          igraph_i_multilevel_community_link_cmp);
+    for (i = 0; i < n; i++) {
+        to_community = links[i].community;
+        if (to_community != last) {
+            igraph_vector_push_back(links_community, to_community);
+            igraph_vector_push_back(links_weight, links[i].weight);
+            last = to_community;
+            c++;
+        } else {
+            VECTOR(*links_weight)[c] += links[i].weight;
+        }
+    }
+
+    igraph_free(links);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+igraph_real_t igraph_i_multilevel_community_modularity_gain(
+    const igraph_i_multilevel_community_list *communities,
+    igraph_integer_t community, igraph_integer_t vertex,
+    igraph_real_t weight_all, igraph_real_t weight_inside) {
+    IGRAPH_UNUSED(vertex);
+    return weight_inside -
+           communities->item[(long int)community].weight_all * weight_all / communities->weight_sum;
+}
+
+/* Shrinks communities into single vertices, keeping all the edges.
+ * This method is internal because it destroys the graph in-place and
+ * creates a new one -- this is fine for the multilevel community
+ * detection where a copy of the original graph is used anyway.
+ * The membership vector will also be rewritten by the underlying
+ * igraph_membership_reindex call */
+int igraph_i_multilevel_shrink(igraph_t *graph, igraph_vector_t *membership) {
+    igraph_vector_t edges;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+
+    long int i;
+    igraph_eit_t eit;
+
+    if (no_of_nodes == 0) {
+        return 0;
+    }
+
+    if (igraph_vector_size(membership) < no_of_nodes) {
+        IGRAPH_ERROR("cannot shrink graph, membership vector too short",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+
+    IGRAPH_CHECK(igraph_reindex_membership(membership, 0, NULL));
+
+    /* Create the new edgelist */
+    igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit);
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+    i = 0;
+    while (!IGRAPH_EIT_END(eit)) {
+        igraph_integer_t from, to;
+        IGRAPH_CHECK(igraph_edge(graph, IGRAPH_EIT_GET(eit), &from, &to));
+        VECTOR(edges)[i++] = VECTOR(*membership)[(long int) from];
+        VECTOR(edges)[i++] = VECTOR(*membership)[(long int) to];
+        IGRAPH_EIT_NEXT(eit);
+    }
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Create the new graph */
+    igraph_destroy(graph);
+    no_of_nodes = (long int) igraph_vector_max(membership) + 1;
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \ingroup communities
+ * \function igraph_i_community_multilevel_step
+ * \brief Performs a single step of the multi-level modularity optimization method
+ *
+ * This function implements a single step of the multi-level modularity optimization
+ * algorithm for finding community structure, see VD Blondel, J-L Guillaume,
+ * R Lambiotte and E Lefebvre: Fast unfolding of community hierarchies in large
+ * networks, http://arxiv.org/abs/0803.0476 for the details.
+ *
+ * This function was contributed by Tom Gregorovic.
+ *
+ * \param graph   The input graph. It must be an undirected graph.
+ * \param weights Numeric vector containing edge weights. If \c NULL, every edge
+ *     has equal weight. The weights are expected to be non-negative.
+ * \param membership The membership vector, the result is returned here.
+ *     For each vertex it gives the ID of its community.
+ * \param modularity The modularity of the partition is returned here.
+ *     \c NULL means that the modularity is not needed.
+ * \return Error code.
+ *
+ * Time complexity: in average near linear on sparse graphs.
+ */
+int igraph_i_community_multilevel_step(igraph_t *graph,
+                                       igraph_vector_t *weights, igraph_vector_t *membership,
+                                       igraph_real_t *modularity) {
+
+    long int i, j;
+    long int vcount = igraph_vcount(graph);
+    long int ecount = igraph_ecount(graph);
+    igraph_integer_t ffrom, fto;
+    igraph_real_t q, pass_q;
+    int pass;
+    igraph_bool_t changed = 0;
+    igraph_vector_t links_community;
+    igraph_vector_t links_weight;
+    igraph_vector_t edges;
+    igraph_vector_t temp_membership;
+    igraph_i_multilevel_community_list communities;
+
+    /* Initial sanity checks on the input parameters */
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("multi-level community detection works for undirected graphs only",
+                     IGRAPH_UNIMPLEMENTED);
+    }
+    if (igraph_vector_size(weights) < igraph_ecount(graph)) {
+        IGRAPH_ERROR("multi-level community detection: weight vector too short", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_any_smaller(weights, 0)) {
+        IGRAPH_ERROR("weights must be positive", IGRAPH_EINVAL);
+    }
+
+    /* Initialize data structures */
+    IGRAPH_VECTOR_INIT_FINALLY(&links_community, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&links_weight, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&temp_membership, vcount);
+    IGRAPH_CHECK(igraph_vector_resize(membership, vcount));
+
+    /* Initialize list of communities from graph vertices */
+    communities.vertices_no = vcount;
+    communities.communities_no = vcount;
+    communities.weights = weights;
+    communities.weight_sum = 2 * igraph_vector_sum(weights);
+    communities.membership = membership;
+    communities.item = igraph_Calloc(vcount, igraph_i_multilevel_community);
+    if (communities.item == 0) {
+        IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, communities.item);
+
+    /* Still initializing the communities data structure */
+    for (i = 0; i < vcount; i++) {
+        VECTOR(*communities.membership)[i] = i;
+        communities.item[i].size = 1;
+        communities.item[i].weight_inside = 0;
+        communities.item[i].weight_all = 0;
+    }
+
+    /* Some more initialization :) */
+    for (i = 0; i < ecount; i++) {
+        igraph_real_t weight = 1;
+        igraph_edge(graph, (igraph_integer_t) i, &ffrom, &fto);
+
+        weight = VECTOR(*weights)[i];
+        communities.item[(long int) ffrom].weight_all += weight;
+        communities.item[(long int) fto].weight_all += weight;
+        if (ffrom == fto) {
+            communities.item[(long int) ffrom].weight_inside += 2 * weight;
+        }
+    }
+
+    q = igraph_i_multilevel_community_modularity(&communities);
+    pass = 1;
+
+    do { /* Pass begin */
+        long int temp_communities_no = communities.communities_no;
+
+        pass_q = q;
+        changed = 0;
+
+        /* Save the current membership, it will be restored in case of worse result */
+        IGRAPH_CHECK(igraph_vector_update(&temp_membership, communities.membership));
+
+        for (i = 0; i < vcount; i++) {
+            /* Exclude vertex from its current community */
+            igraph_real_t weight_all = 0;
+            igraph_real_t weight_inside = 0;
+            igraph_real_t weight_loop = 0;
+            igraph_real_t max_q_gain = 0;
+            igraph_real_t max_weight;
+            long int old_id, new_id, n;
+
+            igraph_i_multilevel_community_links(graph, &communities,
+                                                (igraph_integer_t) i, &edges,
+                                                &weight_all, &weight_inside,
+                                                &weight_loop, &links_community,
+                                                &links_weight);
+
+            old_id = (long int)VECTOR(*(communities.membership))[i];
+            new_id = old_id;
+
+            /* Update old community */
+            igraph_vector_set(communities.membership, i, -1);
+            communities.item[old_id].size--;
+            if (communities.item[old_id].size == 0) {
+                communities.communities_no--;
+            }
+            communities.item[old_id].weight_all -= weight_all;
+            communities.item[old_id].weight_inside -= 2 * weight_inside + weight_loop;
+
+            /* debug("Remove %ld all: %lf Inside: %lf\n", i, -weight_all, -2*weight_inside + weight_loop); */
+
+            /* Find new community to join with the best modification gain */
+            max_q_gain = 0;
+            max_weight = weight_inside;
+            n = igraph_vector_size(&links_community);
+
+            for (j = 0; j < n; j++) {
+                long int c = (long int) VECTOR(links_community)[j];
+                igraph_real_t w = VECTOR(links_weight)[j];
+
+                igraph_real_t q_gain =
+                    igraph_i_multilevel_community_modularity_gain(&communities,
+                            (igraph_integer_t) c,
+                            (igraph_integer_t) i,
+                            weight_all, w);
+                /* debug("Link %ld -> %ld weight: %lf gain: %lf\n", i, c, (double) w, (double) q_gain); */
+                if (q_gain > max_q_gain) {
+                    new_id = c;
+                    max_q_gain = q_gain;
+                    max_weight = w;
+                }
+            }
+
+            /* debug("Added vertex %ld to community %ld (gain %lf).\n", i, new_id, (double) max_q_gain); */
+
+            /* Add vertex to "new" community and update it */
+            igraph_vector_set(communities.membership, i, new_id);
+            if (communities.item[new_id].size == 0) {
+                communities.communities_no++;
+            }
+            communities.item[new_id].size++;
+            communities.item[new_id].weight_all += weight_all;
+            communities.item[new_id].weight_inside += 2 * max_weight + weight_loop;
+
+            if (new_id != old_id) {
+                changed++;
+            }
+        }
+
+        q = igraph_i_multilevel_community_modularity(&communities);
+
+        if (changed && (q > pass_q)) {
+            /* debug("Pass %d (changed: %d) Communities: %ld Modularity from %lf to %lf\n",
+              pass, changed, communities.communities_no, (double) pass_q, (double) q); */
+            pass++;
+        } else {
+            /* No changes or the modularity became worse, restore last membership */
+            IGRAPH_CHECK(igraph_vector_update(communities.membership, &temp_membership));
+            communities.communities_no = temp_communities_no;
+            break;
+        }
+
+        IGRAPH_ALLOW_INTERRUPTION();
+    } while (changed && (q > pass_q)); /* Pass end */
+
+    if (modularity) {
+        *modularity = q;
+    }
+
+    /* debug("Result Communities: %ld Modularity: %lf\n",
+      communities.communities_no, (double) q); */
+
+    IGRAPH_CHECK(igraph_reindex_membership(membership, 0, NULL));
+
+    /* Shrink the nodes of the graph according to the present community structure
+     * and simplify the resulting graph */
+
+    /* TODO: check if we really need to copy temp_membership */
+    IGRAPH_CHECK(igraph_vector_update(&temp_membership, membership));
+    IGRAPH_CHECK(igraph_i_multilevel_shrink(graph, &temp_membership));
+    igraph_vector_destroy(&temp_membership);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Update edge weights after shrinking and simplification */
+    /* Here we reuse the edges vector as we don't need the previous contents anymore */
+    /* TODO: can we use igraph_simplify here? */
+    IGRAPH_CHECK(igraph_i_multilevel_simplify_multiple(graph, &edges));
+
+    /* We reuse the links_weight vector to store the old edge weights */
+    IGRAPH_CHECK(igraph_vector_update(&links_weight, weights));
+    igraph_vector_fill(weights, 0);
+
+    for (i = 0; i < ecount; i++) {
+        VECTOR(*weights)[(long int)VECTOR(edges)[i]] += VECTOR(links_weight)[i];
+    }
+
+    igraph_free(communities.item);
+    igraph_vector_destroy(&links_community);
+    igraph_vector_destroy(&links_weight);
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/**
+ * \ingroup communities
+ * \function igraph_community_multilevel
+ * \brief Finding community structure by multi-level optimization of modularity
+ *
+ * This function implements the multi-level modularity optimization
+ * algorithm for finding community structure, see
+ * VD Blondel, J-L Guillaume, R Lambiotte and E Lefebvre: Fast unfolding of
+ * community hierarchies in large networks, J Stat Mech P10008 (2008)
+ * for the details (preprint: http://arxiv.org/abs/arXiv:0803.0476).
+ *
+ * It is based on the modularity measure and a hierarchical approach.
+ * Initially, each vertex is assigned to a community on its own. In every step,
+ * vertices are re-assigned to communities in a local, greedy way: each vertex
+ * is moved to the community with which it achieves the highest contribution to
+ * modularity. When no vertices can be reassigned, each community is considered
+ * a vertex on its own, and the process starts again with the merged communities.
+ * The process stops when there is only a single vertex left or when the modularity
+ * cannot be increased any more in a step.
+ *
+ * This function was contributed by Tom Gregorovic.
+ *
+ * \param graph The input graph. It must be an undirected graph.
+ * \param weights Numeric vector containing edge weights. If \c NULL, every edge
+ *    has equal weight. The weights are expected to be non-negative.
+ * \param membership The membership vector, the result is returned here.
+ *    For each vertex it gives the ID of its community. The vector
+ *    must be initialized and it will be resized accordingly.
+ * \param memberships Numeric matrix that will contain the membership
+ *     vector after each level, if not \c NULL. It must be initialized and
+ *     it will be resized accordingly.
+ * \param modularity Numeric vector that will contain the modularity score
+ *     after each level, if not \c NULL. It must be initialized and it
+ *     will be resized accordingly.
+ * \return Error code.
+ *
+ * Time complexity: in average near linear on sparse graphs.
+ *
+ * \example examples/simple/igraph_community_multilevel.c
+ */
+
+int igraph_community_multilevel(const igraph_t *graph,
+                                const igraph_vector_t *weights, igraph_vector_t *membership,
+                                igraph_matrix_t *memberships, igraph_vector_t *modularity) {
+
+    igraph_t g;
+    igraph_vector_t w, m, level_membership;
+    igraph_real_t prev_q = -1, q = -1;
+    int i, level = 1;
+    long int vcount = igraph_vcount(graph);
+
+    /* Make a copy of the original graph, we will do the merges on the copy */
+    IGRAPH_CHECK(igraph_copy(&g, graph));
+    IGRAPH_FINALLY(igraph_destroy, &g);
+
+    if (weights) {
+        IGRAPH_CHECK(igraph_vector_copy(&w, weights));
+        IGRAPH_FINALLY(igraph_vector_destroy, &w);
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&w, igraph_ecount(&g));
+        igraph_vector_fill(&w, 1);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&m, vcount);
+    IGRAPH_VECTOR_INIT_FINALLY(&level_membership, vcount);
+
+    if (memberships || membership) {
+        /* Put each vertex in its own community */
+        for (i = 0; i < vcount; i++) {
+            VECTOR(level_membership)[i] = i;
+        }
+    }
+    if (memberships) {
+        /* Resize the membership matrix to have vcount columns and no rows */
+        IGRAPH_CHECK(igraph_matrix_resize(memberships, 0, vcount));
+    }
+    if (modularity) {
+        /* Clear the modularity vector */
+        igraph_vector_clear(modularity);
+    }
+
+    while (1) {
+        /* Remember the previous modularity and vertex count, do a single step */
+        igraph_integer_t step_vcount = igraph_vcount(&g);
+
+        prev_q = q;
+        IGRAPH_CHECK(igraph_i_community_multilevel_step(&g, &w, &m, &q));
+
+        /* Were there any merges? If not, we have to stop the process */
+        if (igraph_vcount(&g) == step_vcount || q < prev_q) {
+            break;
+        }
+
+        if (memberships || membership) {
+            for (i = 0; i < vcount; i++) {
+                /* Readjust the membership vector */
+                VECTOR(level_membership)[i] = VECTOR(m)[(long int) VECTOR(level_membership)[i]];
+            }
+        }
+
+        if (modularity) {
+            /* If we have to return the modularity scores, add it to the modularity vector */
+            IGRAPH_CHECK(igraph_vector_push_back(modularity, q));
+        }
+
+        if (memberships) {
+            /* If we have to return the membership vectors at each level, store the new
+             * membership vector */
+            IGRAPH_CHECK(igraph_matrix_add_rows(memberships, 1));
+            IGRAPH_CHECK(igraph_matrix_set_row(memberships, &level_membership, level - 1));
+        }
+
+        /* debug("Level: %d Communities: %ld Modularity: %f\n", level, (long int) igraph_vcount(&g),
+          (double) q); */
+
+        /* Increase the level counter */
+        level++;
+    }
+
+    /* It might happen that there are no merges, so every vertex is in its
+       own community. We still might want the modularity score for that. */
+    if (modularity && igraph_vector_size(modularity) == 0) {
+        igraph_vector_t tmp;
+        igraph_real_t mod;
+        int i;
+        IGRAPH_VECTOR_INIT_FINALLY(&tmp, vcount);
+        for (i = 0; i < vcount; i++) {
+            VECTOR(tmp)[i] = i;
+        }
+        IGRAPH_CHECK(igraph_modularity(graph, &tmp, &mod, weights));
+        igraph_vector_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(1);
+        IGRAPH_CHECK(igraph_vector_resize(modularity, 1));
+        VECTOR(*modularity)[0] = mod;
+    }
+
+    /* If we need the final membership vector, copy it to the output */
+    if (membership) {
+        IGRAPH_CHECK(igraph_vector_resize(membership, vcount));
+        for (i = 0; i < vcount; i++) {
+            VECTOR(*membership)[i] = VECTOR(level_membership)[i];
+        }
+    }
+
+    /* Destroy the copy of the graph */
+    igraph_destroy(&g);
+
+    /* Destroy the temporary vectors */
+    igraph_vector_destroy(&m);
+    igraph_vector_destroy(&w);
+    igraph_vector_destroy(&level_membership);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+
+int igraph_i_compare_communities_vi(const igraph_vector_t *v1,
+                                    const igraph_vector_t *v2, igraph_real_t* result);
+int igraph_i_compare_communities_nmi(const igraph_vector_t *v1,
+                                     const igraph_vector_t *v2, igraph_real_t* result);
+int igraph_i_compare_communities_rand(const igraph_vector_t *v1,
+                                      const igraph_vector_t *v2, igraph_real_t* result, igraph_bool_t adjust);
+int igraph_i_split_join_distance(const igraph_vector_t *v1,
+                                 const igraph_vector_t *v2, igraph_integer_t* distance12,
+                                 igraph_integer_t* distance21);
+
+/**
+ * \ingroup communities
+ * \function igraph_compare_communities
+ * \brief Compares community structures using various metrics
+ *
+ * This function assesses the distance between two community structures
+ * using the variation of information (VI) metric of Meila (2003), the
+ * normalized mutual information (NMI) of Danon et al (2005), the
+ * split-join distance of van Dongen (2000), the Rand index of Rand (1971)
+ * or the adjusted Rand index of Hubert and Arabie (1985).
+ *
+ * </para><para>
+ * References:
+ *
+ * </para><para>
+ * Meila M: Comparing clusterings by the variation of information.
+ * In: Schölkopf B, Warmuth MK (eds.). Learning Theory and Kernel Machines:
+ * 16th Annual Conference on Computational Learning Theory and 7th Kernel
+ * Workshop, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in Computer
+ * Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1.
+ *
+ * </para><para>
+ * Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community structure
+ * identification. J Stat Mech P09008, 2005.
+ *
+ * </para><para>
+ * van Dongen S: Performance criteria for graph clustering and Markov cluster
+ * experiments. Technical Report INS-R0012, National Research Institute for
+ * Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000.
+ *
+ * </para><para>
+ * Rand WM: Objective criteria for the evaluation of clustering methods.
+ * J Am Stat Assoc 66(336):846-850, 1971.
+ *
+ * </para><para>
+ * Hubert L and Arabie P: Comparing partitions. Journal of Classification
+ * 2:193-218, 1985.
+ *
+ * \param  comm1   the membership vector of the first community structure
+ * \param  comm2   the membership vector of the second community structure
+ * \param  result  the result is stored here.
+ * \param  method  the comparison method to use. \c IGRAPH_COMMCMP_VI
+ *                 selects the variation of information (VI) metric of
+ *                 Meila (2003), \c IGRAPH_COMMCMP_NMI selects the
+ *                 normalized mutual information measure proposed by
+ *                 Danon et al (2005), \c IGRAPH_COMMCMP_SPLIT_JOIN
+ *                 selects the split-join distance of van Dongen (2000),
+ *                 \c IGRAPH_COMMCMP_RAND selects the unadjusted Rand
+ *                 index (1971) and \c IGRAPH_COMMCMP_ADJUSTED_RAND
+ *                 selects the adjusted Rand index.
+ *
+ * \return  Error code.
+ *
+ * Time complexity: O(n log(n)).
+ */
+int igraph_compare_communities(const igraph_vector_t *comm1,
+                               const igraph_vector_t *comm2, igraph_real_t* result,
+                               igraph_community_comparison_t method) {
+    igraph_vector_t c1, c2;
+
+    if (igraph_vector_size(comm1) != igraph_vector_size(comm2)) {
+        IGRAPH_ERROR("community membership vectors have different lengths", IGRAPH_EINVAL);
+    }
+
+    /* Copy and reindex membership vectors to make sure they are continuous */
+    IGRAPH_CHECK(igraph_vector_copy(&c1, comm1));
+    IGRAPH_FINALLY(igraph_vector_destroy, &c1);
+
+    IGRAPH_CHECK(igraph_vector_copy(&c2, comm2));
+    IGRAPH_FINALLY(igraph_vector_destroy, &c2);
+
+    IGRAPH_CHECK(igraph_reindex_membership(&c1, 0, NULL));
+    IGRAPH_CHECK(igraph_reindex_membership(&c2, 0, NULL));
+
+    switch (method) {
+    case IGRAPH_COMMCMP_VI:
+        IGRAPH_CHECK(igraph_i_compare_communities_vi(&c1, &c2, result));
+        break;
+
+    case IGRAPH_COMMCMP_NMI:
+        IGRAPH_CHECK(igraph_i_compare_communities_nmi(&c1, &c2, result));
+        break;
+
+    case IGRAPH_COMMCMP_SPLIT_JOIN: {
+        igraph_integer_t d12, d21;
+        IGRAPH_CHECK(igraph_i_split_join_distance(&c1, &c2, &d12, &d21));
+        *result = d12 + d21;
+    }
+    break;
+
+    case IGRAPH_COMMCMP_RAND:
+    case IGRAPH_COMMCMP_ADJUSTED_RAND:
+        IGRAPH_CHECK(igraph_i_compare_communities_rand(&c1, &c2, result,
+                     method == IGRAPH_COMMCMP_ADJUSTED_RAND));
+        break;
+
+    default:
+        IGRAPH_ERROR("unknown community comparison method", IGRAPH_EINVAL);
+    }
+
+    /* Clean up everything */
+    igraph_vector_destroy(&c1);
+    igraph_vector_destroy(&c2);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \ingroup communities
+ * \function igraph_split_join_distance
+ * \brief Calculates the split-join distance of two community structures
+ *
+ * The split-join distance between partitions A and B is the sum of the
+ * projection distance of A from B and the projection distance of B from
+ * A. The projection distance is an asymmetric measure and it is defined
+ * as follows:
+ *
+ * </para><para>
+ * First, each set in partition A is evaluated against all sets in partition
+ * B. For each set in partition A, the best matching set in partition B is
+ * found and the overlap size is calculated. (Matching is quantified by the
+ * size of the overlap between the two sets). Then, the maximal overlap sizes
+ * for each set in A are summed together and subtracted from the number of
+ * elements in A.
+ *
+ * </para><para>
+ * The split-join distance will be returned in two arguments, \c distance12
+ * will contain the projection distance of the first partition from the
+ * second, while \c distance21 will be the projection distance of the second
+ * partition from the first. This makes it easier to detect whether a
+ * partition is a subpartition of the other, since in this case, the
+ * corresponding distance will be zero.
+ *
+ * </para><para>
+ * Reference:
+ *
+ * </para><para>
+ * van Dongen S: Performance criteria for graph clustering and Markov cluster
+ * experiments. Technical Report INS-R0012, National Research Institute for
+ * Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000.
+ *
+ * \param  comm1       the membership vector of the first community structure
+ * \param  comm2       the membership vector of the second community structure
+ * \param  distance12  pointer to an \c igraph_integer_t, the projection distance
+ *                     of the first community structure from the second one will be
+ *                     returned here.
+ * \param  distance21  pointer to an \c igraph_integer_t, the projection distance
+ *                     of the second community structure from the first one will be
+ *                     returned here.
+ * \return  Error code.
+ *
+ * \see \ref igraph_compare_communities() with the \c IGRAPH_COMMCMP_SPLIT_JOIN
+ * method if you are not interested in the individual distances but only the sum
+ * of them.
+ *
+ * Time complexity: O(n log(n)).
+ */
+int igraph_split_join_distance(const igraph_vector_t *comm1,
+                               const igraph_vector_t *comm2, igraph_integer_t *distance12,
+                               igraph_integer_t *distance21) {
+    igraph_vector_t c1, c2;
+
+    if (igraph_vector_size(comm1) != igraph_vector_size(comm2)) {
+        IGRAPH_ERROR("community membership vectors have different lengths", IGRAPH_EINVAL);
+    }
+
+    /* Copy and reindex membership vectors to make sure they are continuous */
+    IGRAPH_CHECK(igraph_vector_copy(&c1, comm1));
+    IGRAPH_FINALLY(igraph_vector_destroy, &c1);
+
+    IGRAPH_CHECK(igraph_vector_copy(&c2, comm2));
+    IGRAPH_FINALLY(igraph_vector_destroy, &c2);
+
+    IGRAPH_CHECK(igraph_reindex_membership(&c1, 0, NULL));
+    IGRAPH_CHECK(igraph_reindex_membership(&c2, 0, NULL));
+
+    IGRAPH_CHECK(igraph_i_split_join_distance(&c1, &c2, distance12, distance21));
+
+    /* Clean up everything */
+    igraph_vector_destroy(&c1);
+    igraph_vector_destroy(&c2);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * Calculates the entropy and the mutual information for two reindexed community
+ * membership vectors v1 and v2. This is needed by both Meila's and Danon's
+ * community comparison measure.
+ */
+int igraph_i_entropy_and_mutual_information(const igraph_vector_t* v1,
+        const igraph_vector_t* v2, double* h1, double* h2, double* mut_inf) {
+    long int i, n = igraph_vector_size(v1);
+    long int k1 = (long int)igraph_vector_max(v1) + 1;
+    long int k2 = (long int)igraph_vector_max(v2) + 1;
+    double *p1, *p2;
+    igraph_spmatrix_t m;
+    igraph_spmatrix_iter_t mit;
+
+    p1 = igraph_Calloc(k1, double);
+    if (p1 == 0) {
+        IGRAPH_ERROR("igraph_i_entropy_and_mutual_information failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, p1);
+    p2 = igraph_Calloc(k2, double);
+    if (p2 == 0) {
+        IGRAPH_ERROR("igraph_i_entropy_and_mutual_information failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, p2);
+
+    /* Calculate the entropy of v1 */
+    *h1 = 0.0;
+    for (i = 0; i < n; i++) {
+        p1[(long int)VECTOR(*v1)[i]]++;
+    }
+    for (i = 0; i < k1; i++) {
+        p1[i] /= n;
+        *h1 -= p1[i] * log(p1[i]);
+    }
+
+    /* Calculate the entropy of v2 */
+    *h2 = 0.0;
+    for (i = 0; i < n; i++) {
+        p2[(long int)VECTOR(*v2)[i]]++;
+    }
+    for (i = 0; i < k2; i++) {
+        p2[i] /= n;
+        *h2 -= p2[i] * log(p2[i]);
+    }
+
+    /* We will only need the logs of p1 and p2 from now on */
+    for (i = 0; i < k1; i++) {
+        p1[i] = log(p1[i]);
+    }
+    for (i = 0; i < k2; i++) {
+        p2[i] = log(p2[i]);
+    }
+
+    /* Calculate the mutual information of v1 and v2 */
+    *mut_inf = 0.0;
+    IGRAPH_CHECK(igraph_spmatrix_init(&m, k1, k2));
+    IGRAPH_FINALLY(igraph_spmatrix_destroy, &m);
+    for (i = 0; i < n; i++) {
+        IGRAPH_CHECK(igraph_spmatrix_add_e(&m,
+                                           (int)VECTOR(*v1)[i], (int)VECTOR(*v2)[i], 1));
+    }
+    IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m));
+    IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit);
+    while (!igraph_spmatrix_iter_end(&mit)) {
+        double p = mit.value / n;
+        *mut_inf += p * (log(p) - p1[mit.ri] - p2[mit.ci]);
+        igraph_spmatrix_iter_next(&mit);
+    }
+
+    igraph_spmatrix_iter_destroy(&mit);
+    igraph_spmatrix_destroy(&m);
+    free(p1); free(p2);
+
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/**
+ * Implementation of the normalized mutual information (NMI) measure of
+ * Danon et al. This function assumes that the community membership
+ * vectors have already been normalized using igraph_reindex_communities().
+ *
+ * </para><para>
+ * Reference: Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community
+ * structure identification. J Stat Mech P09008, 2005.
+ *
+ * </para><para>
+ * Time complexity: O(n log(n))
+ */
+int igraph_i_compare_communities_nmi(const igraph_vector_t *v1, const igraph_vector_t *v2,
+                                     igraph_real_t* result) {
+    double h1, h2, mut_inf;
+
+    IGRAPH_CHECK(igraph_i_entropy_and_mutual_information(v1, v2, &h1, &h2, &mut_inf));
+
+    if (h1 == 0 && h2 == 0) {
+        *result = 1;
+    } else {
+        *result = 2 * mut_inf / (h1 + h2);
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * Implementation of the variation of information metric (VI) of
+ * Meila et al. This function assumes that the community membership
+ * vectors have already been normalized using igraph_reindex_communities().
+ *
+ * </para><para>
+ * Reference: Meila M: Comparing clusterings by the variation of information.
+ * In: Schölkopf B, Warmuth MK (eds.). Learning Theory and Kernel Machines:
+ * 16th Annual Conference on Computational Learning Theory and 7th Kernel
+ * Workshop, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in Computer
+ * Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1.
+ *
+ * </para><para>
+ * Time complexity: O(n log(n))
+ */
+int igraph_i_compare_communities_vi(const igraph_vector_t *v1, const igraph_vector_t *v2,
+                                    igraph_real_t* result) {
+    double h1, h2, mut_inf;
+
+    IGRAPH_CHECK(igraph_i_entropy_and_mutual_information(v1, v2, &h1, &h2, &mut_inf));
+    *result = h1 + h2 - 2 * mut_inf;
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \brief Calculates the confusion matrix for two clusterings.
+ *
+ * </para><para>
+ * This function assumes that the community membership vectors have already
+ * been normalized using igraph_reindex_communities().
+ *
+ * </para><para>
+ * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1
+ * and k2 are the number of clusters in each of the clusterings.
+ */
+int igraph_i_confusion_matrix(const igraph_vector_t *v1, const igraph_vector_t *v2,
+                              igraph_spmatrix_t *m) {
+    long int k1 = (long int)igraph_vector_max(v1) + 1;
+    long int k2 = (long int)igraph_vector_max(v2) + 1;
+    long int i, n = igraph_vector_size(v1);
+
+    IGRAPH_CHECK(igraph_spmatrix_resize(m, k1, k2));
+    for (i = 0; i < n; i++) {
+        IGRAPH_CHECK(igraph_spmatrix_add_e(m,
+                                           (int)VECTOR(*v1)[i], (int)VECTOR(*v2)[i], 1));
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * Implementation of the split-join distance of van Dongen.
+ *
+ * </para><para>
+ * This function assumes that the community membership vectors have already
+ * been normalized using igraph_reindex_communities().
+ *
+ * </para><para>
+ * Reference: van Dongen S: Performance criteria for graph clustering and Markov
+ * cluster experiments. Technical Report INS-R0012, National Research Institute
+ * for Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000.
+ *
+ * </para><para>
+ * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1
+ * and k2 are the number of clusters in each of the clusterings.
+ */
+int igraph_i_split_join_distance(const igraph_vector_t *v1, const igraph_vector_t *v2,
+                                 igraph_integer_t* distance12, igraph_integer_t* distance21) {
+    long int n = igraph_vector_size(v1);
+    igraph_vector_t rowmax, colmax;
+    igraph_spmatrix_t m;
+    igraph_spmatrix_iter_t mit;
+
+    /* Calculate the confusion matrix */
+    IGRAPH_CHECK(igraph_spmatrix_init(&m, 1, 1));
+    IGRAPH_FINALLY(igraph_spmatrix_destroy, &m);
+    IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &m));
+
+    /* Initialize vectors that will store the row/columnwise maxima */
+    IGRAPH_VECTOR_INIT_FINALLY(&rowmax, igraph_spmatrix_nrow(&m));
+    IGRAPH_VECTOR_INIT_FINALLY(&colmax, igraph_spmatrix_ncol(&m));
+
+    /* Find the row/columnwise maxima */
+    IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m));
+    IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit);
+    while (!igraph_spmatrix_iter_end(&mit)) {
+        if (mit.value > VECTOR(rowmax)[mit.ri]) {
+            VECTOR(rowmax)[mit.ri] = mit.value;
+        }
+        if (mit.value > VECTOR(colmax)[mit.ci]) {
+            VECTOR(colmax)[mit.ci] = mit.value;
+        }
+        igraph_spmatrix_iter_next(&mit);
+    }
+    igraph_spmatrix_iter_destroy(&mit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Calculate the distances */
+    *distance12 = (igraph_integer_t) (n - igraph_vector_sum(&rowmax));
+    *distance21 = (igraph_integer_t) (n - igraph_vector_sum(&colmax));
+
+    igraph_vector_destroy(&rowmax);
+    igraph_vector_destroy(&colmax);
+    igraph_spmatrix_destroy(&m);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * Implementation of the adjusted and unadjusted Rand indices.
+ *
+ * </para><para>
+ * This function assumes that the community membership vectors have already
+ * been normalized using igraph_reindex_communities().
+ *
+ * </para><para>
+ * References:
+ *
+ * </para><para>
+ * Rand WM: Objective criteria for the evaluation of clustering methods. J Am
+ * Stat Assoc 66(336):846-850, 1971.
+ *
+ * </para><para>
+ * Hubert L and Arabie P: Comparing partitions. Journal of Classification
+ * 2:193-218, 1985.
+ *
+ * </para><para>
+ * Time complexity: O(n log(max(k1, k2))), where n is the number of vertices, k1
+ * and k2 are the number of clusters in each of the clusterings.
+ */
+int igraph_i_compare_communities_rand(const igraph_vector_t *v1,
+                                      const igraph_vector_t *v2, igraph_real_t *result, igraph_bool_t adjust) {
+    igraph_spmatrix_t m;
+    igraph_spmatrix_iter_t mit;
+    igraph_vector_t rowsums, colsums;
+    long int i, nrow, ncol;
+    double rand, n;
+    double frac_pairs_in_1, frac_pairs_in_2;
+
+    /* Calculate the confusion matrix */
+    IGRAPH_CHECK(igraph_spmatrix_init(&m, 1, 1));
+    IGRAPH_FINALLY(igraph_spmatrix_destroy, &m);
+    IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &m));
+
+    /* The unadjusted Rand index is defined as (a+d) / (a+b+c+d), where:
+     *
+     * - a is the number of pairs in the same cluster both in v1 and v2. This
+     *   equals the sum of n(i,j) choose 2 for all i and j.
+     *
+     * - b is the number of pairs in the same cluster in v1 and in different
+     *   clusters in v2. This is sum n(i,*) choose 2 for all i minus a.
+     *   n(i,*) is the number of elements in cluster i in v1.
+     *
+     * - c is the number of pairs in the same cluster in v2 and in different
+     *   clusters in v1. This is sum n(*,j) choose 2 for all j minus a.
+     *   n(*,j) is the number of elements in cluster j in v2.
+     *
+     * - d is (n choose 2) - a - b - c.
+     *
+     * Therefore, a+d = (n choose 2) - b - c
+     *                = (n choose 2) - sum (n(i,*) choose 2)
+     *                               - sum (n(*,j) choose 2)
+     *                               + 2 * sum (n(i,j) choose 2).
+     *
+     * Since a+b+c+d = (n choose 2) and this goes in the denominator, we can
+     * just as well start dividing each term in a+d by (n choose 2), which
+     * yields:
+     *
+     * 1 - sum( n(i,*)/n * (n(i,*)-1)/(n-1) )
+     *   - sum( n(*,i)/n * (n(*,i)-1)/(n-1) )
+     *   + sum( n(i,j)/n * (n(i,j)-1)/(n-1) ) * 2
+     */
+
+    /* Calculate row and column sums */
+    nrow = igraph_spmatrix_nrow(&m);
+    ncol = igraph_spmatrix_ncol(&m);
+    n = igraph_vector_size(v1) + 0.0;
+    IGRAPH_VECTOR_INIT_FINALLY(&rowsums, nrow);
+    IGRAPH_VECTOR_INIT_FINALLY(&colsums, ncol);
+    IGRAPH_CHECK(igraph_spmatrix_rowsums(&m, &rowsums));
+    IGRAPH_CHECK(igraph_spmatrix_colsums(&m, &colsums));
+
+    /* Start calculating the unadjusted Rand index */
+    rand = 0.0;
+    IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, &m));
+    IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit);
+    while (!igraph_spmatrix_iter_end(&mit)) {
+        rand += (mit.value / n) * (mit.value - 1) / (n - 1);
+        igraph_spmatrix_iter_next(&mit);
+    }
+    igraph_spmatrix_iter_destroy(&mit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    frac_pairs_in_1 = frac_pairs_in_2 = 0.0;
+    for (i = 0; i < nrow; i++) {
+        frac_pairs_in_1 += (VECTOR(rowsums)[i] / n) * (VECTOR(rowsums)[i] - 1) / (n - 1);
+    }
+    for (i = 0; i < ncol; i++) {
+        frac_pairs_in_2 += (VECTOR(colsums)[i] / n) * (VECTOR(colsums)[i] - 1) / (n - 1);
+    }
+
+    rand = 1.0 + 2 * rand - frac_pairs_in_1 - frac_pairs_in_2;
+
+    if (adjust) {
+        double expected = frac_pairs_in_1 * frac_pairs_in_2 +
+                          (1 - frac_pairs_in_1) * (1 - frac_pairs_in_2);
+        rand = (rand - expected) / (1 - expected);
+    }
+
+    igraph_vector_destroy(&rowsums);
+    igraph_vector_destroy(&colsums);
+    igraph_spmatrix_destroy(&m);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    *result = rand;
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/community_leiden.c b/igraph/src/community_leiden.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/community_leiden.c
@@ -0,0 +1,1079 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_adjlist.h"
+#include "igraph_community.h"
+#include "igraph_dqueue.h"
+#include "igraph_interface.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_stack.h"
+#include "igraph_constructors.h"
+
+/* Move nodes in order to improve the quality of a partition.
+ *
+ * This function considers each node and greedily moves it to a neighboring
+ * community that maximizes the improvement in the quality of a partition.
+ *
+ * The nodes are examined in a queue, and initially all nodes are put in the
+ * queue in a random order. Nodes are popped from the queue when they are
+ * examined, and only neighbors of nodes that are moved (which are not part of
+ * the cluster the node was moved to) are pushed to the queue again.
+ *
+ * The \c membership vector is used as the starting point to move around nodes,
+ * and is updated in-place.
+ *
+ */
+int igraph_i_community_leiden_fastmovenodes(const igraph_t *graph,
+        const igraph_inclist_t *edges_per_node,
+        const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights,
+        const igraph_real_t resolution_parameter,
+        igraph_integer_t *nb_clusters,
+        igraph_vector_t *membership) {
+
+    igraph_dqueue_t unstable_nodes;
+    igraph_real_t max_diff = 0.0, diff = 0.0;
+    igraph_integer_t n = igraph_vcount(graph);
+    igraph_vector_bool_t neighbor_cluster_added, node_is_stable;
+    igraph_vector_t node_order, cluster_weights, edge_weights_per_cluster, neighbor_clusters;
+    igraph_vector_int_t nb_nodes_per_cluster;
+    igraph_stack_t empty_clusters;
+    long int i, j, c, nb_neigh_clusters;
+
+    /* Initialize queue of unstable nodes and whether node is stable. Only
+     * unstable nodes are in the queue. */
+    IGRAPH_CHECK(igraph_vector_bool_init(&node_is_stable, n));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &node_is_stable);
+
+    IGRAPH_CHECK(igraph_dqueue_init(&unstable_nodes, n));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &unstable_nodes);
+
+    /* Shuffle nodes */
+    IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, n - 1));
+    IGRAPH_FINALLY(igraph_vector_destroy, &node_order);
+    IGRAPH_CHECK(igraph_vector_shuffle(&node_order));
+
+    /* Add to the queue */
+    for (i = 0; i < n; i++) {
+        igraph_dqueue_push(&unstable_nodes, (long int)VECTOR(node_order)[i]);
+    }
+
+    /* Initialize cluster weights and nb nodes */
+    IGRAPH_CHECK(igraph_vector_init(&cluster_weights, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &cluster_weights);
+    IGRAPH_CHECK(igraph_vector_int_init(&nb_nodes_per_cluster, n));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &nb_nodes_per_cluster);
+    for (i = 0; i < n; i++) {
+        c = (long int)VECTOR(*membership)[i];
+        VECTOR(cluster_weights)[c] += VECTOR(*node_weights)[i];
+        VECTOR(nb_nodes_per_cluster)[c] += 1;
+    }
+
+    /* Initialize empty clusters */
+    IGRAPH_CHECK(igraph_stack_init(&empty_clusters, n));
+    IGRAPH_FINALLY(igraph_stack_destroy, &empty_clusters);
+    for (c = 0; c < n; c++)
+        if (VECTOR(nb_nodes_per_cluster)[c] == 0) {
+            igraph_stack_push(&empty_clusters, c);
+        }
+
+    /* Initialize vectors to be used in calculating differences */
+    IGRAPH_CHECK(igraph_vector_init(&edge_weights_per_cluster, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &edge_weights_per_cluster);
+
+    /* Initialize neighboring cluster */
+    IGRAPH_CHECK(igraph_vector_bool_init(&neighbor_cluster_added, n));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &neighbor_cluster_added);
+    IGRAPH_CHECK(igraph_vector_init(&neighbor_clusters, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &neighbor_clusters);
+
+    /* Iterate while the queue is not empty */
+    j = 0;
+    while (!igraph_dqueue_empty(&unstable_nodes)) {
+        long int v = (long int)igraph_dqueue_pop(&unstable_nodes);
+        long int best_cluster, current_cluster = VECTOR(*membership)[v];
+        long int degree, i;
+        igraph_vector_int_t *edges;
+
+        /* Remove node from current cluster */
+        VECTOR(cluster_weights)[current_cluster] -= VECTOR(*node_weights)[v];
+        VECTOR(nb_nodes_per_cluster)[current_cluster]--;
+        if (VECTOR(nb_nodes_per_cluster)[current_cluster] == 0) {
+            igraph_stack_push(&empty_clusters, current_cluster);
+        }
+
+        /* Find out neighboring clusters */
+        c = (long int)igraph_stack_top(&empty_clusters);
+        VECTOR(neighbor_clusters)[0] = c;
+        VECTOR(neighbor_cluster_added)[c] = 1;
+        nb_neigh_clusters = 1;
+
+        /* Determine the edge weight to each neighboring cluster */
+        edges = igraph_inclist_get(edges_per_node, v);
+        degree = igraph_vector_int_size(edges);
+        for (i = 0; i < degree; i++) {
+            long int e = VECTOR(*edges)[i];
+            long int u = (long int)IGRAPH_OTHER(graph, e, v);
+            c = VECTOR(*membership)[u];
+            if (!VECTOR(neighbor_cluster_added)[c]) {
+                VECTOR(neighbor_cluster_added)[c] = 1;
+                VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c;
+            }
+            VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e];
+        }
+
+        /* Calculate maximum diff */
+        best_cluster = current_cluster;
+        max_diff = VECTOR(edge_weights_per_cluster)[current_cluster] - VECTOR(*node_weights)[v] * VECTOR(cluster_weights)[current_cluster] * resolution_parameter;
+        for (i = 0; i < nb_neigh_clusters; i++) {
+            c = VECTOR(neighbor_clusters)[i];
+            diff = VECTOR(edge_weights_per_cluster)[c] - VECTOR(*node_weights)[v] * VECTOR(cluster_weights)[c] * resolution_parameter;
+            if (diff > max_diff) {
+                best_cluster = c;
+                max_diff = diff;
+            }
+            VECTOR(edge_weights_per_cluster)[c] = 0.0;
+            VECTOR(neighbor_cluster_added)[c] = 0;
+        }
+
+        /* Move node to best cluster */
+        VECTOR(cluster_weights)[best_cluster] += VECTOR(*node_weights)[v];
+        VECTOR(nb_nodes_per_cluster)[best_cluster]++;
+        if (best_cluster == igraph_stack_top(&empty_clusters)) {
+            igraph_stack_pop(&empty_clusters);
+        }
+
+        /* Mark node as stable */
+        VECTOR(node_is_stable)[v] = 1;
+
+        /* Add stable neighbours that are not part of the new cluster to the queue */
+        if (best_cluster != current_cluster) {
+            VECTOR(*membership)[v] = best_cluster;
+
+            for (i = 0; i < degree; i++) {
+                long int e = VECTOR(*edges)[i];
+                long int u = (long int)IGRAPH_OTHER(graph, e, v);
+                if (VECTOR(node_is_stable)[u] && VECTOR(*membership)[u] != best_cluster) {
+                    igraph_dqueue_push(&unstable_nodes, u);
+                    VECTOR(node_is_stable)[u] = 0;
+                }
+            }
+        }
+
+        j++;
+        if (j > 10000) {
+            IGRAPH_ALLOW_INTERRUPTION();
+            j = 0;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, nb_clusters));
+
+    igraph_vector_destroy(&neighbor_clusters);
+    igraph_vector_bool_destroy(&neighbor_cluster_added);
+    igraph_vector_destroy(&edge_weights_per_cluster);
+    igraph_stack_destroy(&empty_clusters);
+    igraph_vector_int_destroy(&nb_nodes_per_cluster);
+    igraph_vector_destroy(&cluster_weights);
+    igraph_vector_destroy(&node_order);
+    igraph_dqueue_destroy(&unstable_nodes);
+    igraph_vector_bool_destroy(&node_is_stable);
+
+    IGRAPH_FINALLY_CLEAN(9);
+
+    return IGRAPH_SUCCESS;
+}
+
+/* Clean a refined membership vector.
+ *
+ * This function examines all nodes in \c node_subset and updates \c
+ * refined_membership to ensure that the clusters are numbered consecutively,
+ * starting from \c nb_refined_clusters. The \c nb_refined_clusters is also
+ * updated itself. If C is the initial \c nb_refined_clusters and C' the
+ * resulting \c nb_refined_clusters, then nodes in \c node_subset are numbered
+ * C, C + 1, ..., C' - 1.
+ */
+int igraph_i_community_leiden_clean_refined_membership(const igraph_vector_t* node_subset, igraph_vector_t *refined_membership, igraph_integer_t* nb_refined_clusters) {
+    long int i, n = igraph_vector_size(node_subset);
+    igraph_vector_t new_cluster;
+
+    IGRAPH_CHECK(igraph_vector_init(&new_cluster, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &new_cluster);
+
+    /* Clean clusters. We will store the new cluster + 1 so that cluster == 0
+     * indicates that no membership was assigned yet. */
+    *nb_refined_clusters += 1;
+    for (i = 0; i < n; i++) {
+        long int v = (long int)VECTOR(*node_subset)[i];
+        long int c = (long int)VECTOR(*refined_membership)[v];
+        if (VECTOR(new_cluster)[c] == 0) {
+            VECTOR(new_cluster)[c] = (igraph_real_t)(*nb_refined_clusters);
+            *nb_refined_clusters += 1;
+        }
+    }
+
+    /* Assign new cluster */
+    for (i = 0; i < n; i++) {
+        long int v = (long int)VECTOR(*node_subset)[i];
+        long int c = (long int)VECTOR(*refined_membership)[v];
+        VECTOR(*refined_membership)[v] = VECTOR(new_cluster)[c] - 1;
+    }
+    /* We used the cluster + 1, so correct */
+    *nb_refined_clusters -= 1;
+
+    igraph_vector_destroy(&new_cluster);
+
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/* Merge nodes for a subset of the nodes. This is used to refine a partition.
+ *
+ * The nodes included in \c node_subset are assumed to be the nodes i for which
+ * membership[i] = cluster_subset.
+ *
+ * All nodes in \c node_subset are initialized to a singleton partition in \c
+ * refined_membership. Only singleton clusters can be merged if they are
+ * sufficiently well connected to the current subgraph induced by \c
+ * node_subset.
+ *
+ * We only examine each node once. Instead of greedily choosing the maximum
+ * possible cluster to merge with, the cluster is chosen randomly among all
+ * possibilities that do not decrease the quality of the partition. The
+ * probability of choosing a certain cluster is proportional to exp(diff/beta).
+ * For beta to 0 this converges to selecting a cluster with the maximum
+ * improvement. For beta to infinity this converges to a uniform distribution
+ * among all eligible clusters.
+ *
+ * The \c refined_membership is updated for node in \c node_subset. The number
+ * of refined clusters, \c nb_refined_clusters is used to set the actual refined
+ * cluster membership and is updated after this routine. Within each cluster
+ * (i.e. for a given \c node_subset), the refined membership is initially simply
+ * set to 0, ..., n - 1 (for n nodes in \c node_subset). However, for each \c
+ * node_subset the refined membership should of course be unique. Hence, after
+ * merging, the refined membership starts with \c nb_refined_clusters, which is
+ * also updated to ensure that the resulting \c nb_refined_clusters counts all
+ * refined clusters that have already been processed. See
+ * igraph_i_community_leiden_clean_refined_membership for more information about
+ * this aspect.
+ */
+int igraph_i_community_leiden_mergenodes(const igraph_t *graph,
+        const igraph_inclist_t *edges_per_node,
+        const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights,
+        const igraph_vector_t *node_subset,
+        const igraph_vector_t *membership,
+        const igraph_integer_t cluster_subset,
+        const igraph_real_t resolution_parameter,
+        const igraph_real_t beta,
+        igraph_integer_t *nb_refined_clusters,
+        igraph_vector_t *refined_membership) {
+    igraph_vector_t node_order;
+    igraph_vector_bool_t non_singleton_cluster, neighbor_cluster_added;
+    igraph_real_t max_diff, total_cum_trans_diff, diff = 0.0, total_node_weight = 0.0;
+    igraph_integer_t n = igraph_vector_size(node_subset);
+    igraph_vector_t cluster_weights, cum_trans_diff, edge_weights_per_cluster, external_edge_weight_per_cluster_in_subset, neighbor_clusters;
+    igraph_vector_int_t *edges, nb_nodes_per_cluster;
+    long int i, j, degree, nb_neigh_clusters;
+
+    /* Initialize cluster weights */
+    IGRAPH_CHECK(igraph_vector_init(&cluster_weights, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &cluster_weights);
+
+    /* Initialize number of nodes per cluster */
+    IGRAPH_CHECK(igraph_vector_int_init(&nb_nodes_per_cluster, n));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &nb_nodes_per_cluster);
+
+    /* Initialize external edge weight per cluster in subset */
+    IGRAPH_CHECK(igraph_vector_init(&external_edge_weight_per_cluster_in_subset, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &external_edge_weight_per_cluster_in_subset);
+
+    /* Initialize administration for a singleton partition */
+    for (i = 0; i < n; i++) {
+        long int v = (long int)VECTOR(*node_subset)[i];
+        VECTOR(*refined_membership)[v] = i;
+        VECTOR(cluster_weights)[i] += VECTOR(*node_weights)[v];
+        VECTOR(nb_nodes_per_cluster)[i] += 1;
+        total_node_weight += VECTOR(*node_weights)[v];
+
+        /* Find out neighboring clusters */
+        edges = igraph_inclist_get(edges_per_node, v);
+        degree = igraph_vector_int_size(edges);
+        for (j = 0; j < degree; j++) {
+            long int e = VECTOR(*edges)[j];
+            long int u = (long int)IGRAPH_OTHER(graph, e, v);
+            if (VECTOR(*membership)[u] == cluster_subset) {
+                VECTOR(external_edge_weight_per_cluster_in_subset)[i] += VECTOR(*edge_weights)[e];
+            }
+        }
+    }
+
+    /* Shuffle nodes */
+    IGRAPH_CHECK(igraph_vector_copy(&node_order, node_subset));
+    IGRAPH_FINALLY(igraph_vector_destroy, &node_order);
+    IGRAPH_CHECK(igraph_vector_shuffle(&node_order));
+
+    /* Initialize non singleton clusters */
+    IGRAPH_CHECK(igraph_vector_bool_init(&non_singleton_cluster, n));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &non_singleton_cluster);
+
+    /* Initialize vectors to be used in calculating differences */
+    IGRAPH_CHECK(igraph_vector_init(&edge_weights_per_cluster, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &edge_weights_per_cluster);
+
+    /* Initialize neighboring cluster */
+    IGRAPH_CHECK(igraph_vector_bool_init(&neighbor_cluster_added, n));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &neighbor_cluster_added);
+    IGRAPH_CHECK(igraph_vector_init(&neighbor_clusters, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &neighbor_clusters);
+
+    /* Initialize cumulative transformed difference */
+    IGRAPH_CHECK(igraph_vector_init(&cum_trans_diff, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &cum_trans_diff);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < n; i++) {
+        long int v = (long int)VECTOR(node_order)[i];
+        long int chosen_cluster, best_cluster, current_cluster = (long int)VECTOR(*refined_membership)[v];
+
+        if (!VECTOR(non_singleton_cluster)[current_cluster] &&
+            (VECTOR(external_edge_weight_per_cluster_in_subset)[current_cluster] >=
+             VECTOR(cluster_weights)[current_cluster] * (total_node_weight - VECTOR(cluster_weights)[current_cluster]) * resolution_parameter)) {
+            /* Remove node from current cluster, which is then a singleton by
+             * definition. */
+            VECTOR(cluster_weights)[current_cluster] = 0.0;
+            VECTOR(nb_nodes_per_cluster)[current_cluster] = 0;
+
+            /* Find out neighboring clusters */
+            edges = igraph_inclist_get(edges_per_node, v);
+            degree = igraph_vector_int_size(edges);
+
+            /* Also add current cluster to ensure it can be chosen. */
+            VECTOR(neighbor_clusters)[0] = current_cluster;
+            VECTOR(neighbor_cluster_added)[current_cluster] = 1;
+            nb_neigh_clusters = 1;
+            for (j = 0; j < degree; j++) {
+                long int e = (long int)VECTOR(*edges)[j];
+                long int u = (long int)IGRAPH_OTHER(graph, e, v);
+                if (VECTOR(*membership)[u] == cluster_subset) {
+                    long int c = VECTOR(*refined_membership)[u];
+                    if (!VECTOR(neighbor_cluster_added)[c]) {
+                        VECTOR(neighbor_cluster_added)[c] = 1;
+                        VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c;
+                    }
+                    VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e];
+                }
+            }
+
+            /* Calculate diffs */
+            best_cluster = current_cluster;
+            max_diff = 0.0;
+            total_cum_trans_diff = 0.0;
+            for (j = 0; j < nb_neigh_clusters; j++) {
+                long int c = (long int)VECTOR(neighbor_clusters)[j];
+                if (VECTOR(external_edge_weight_per_cluster_in_subset)[c] >= VECTOR(cluster_weights)[c] * (total_node_weight - VECTOR(cluster_weights)[c]) * resolution_parameter) {
+                    diff = VECTOR(edge_weights_per_cluster)[c] - VECTOR(*node_weights)[v] * VECTOR(cluster_weights)[c] * resolution_parameter;
+
+                    if (diff > max_diff) {
+                        best_cluster = c;
+                        max_diff = diff;
+                    }
+
+                    /* Calculate the transformed difference for sampling */
+                    if (diff >= 0) {
+                        total_cum_trans_diff += exp(diff / beta);
+                    }
+
+                }
+
+                VECTOR(cum_trans_diff)[j] = total_cum_trans_diff;
+                VECTOR(edge_weights_per_cluster)[c] = 0.0;
+                VECTOR(neighbor_cluster_added)[c] = 0;
+            }
+
+            /* Determine the neighboring cluster to which the currently selected node
+             * will be moved.
+             */
+            if (total_cum_trans_diff < IGRAPH_INFINITY) {
+                igraph_real_t r = igraph_rng_get_unif(igraph_rng_default(), 0, total_cum_trans_diff);
+                long int chosen_idx;
+                igraph_i_vector_binsearch_slice(&cum_trans_diff, r, &chosen_idx, 0, nb_neigh_clusters);
+                chosen_cluster = VECTOR(neighbor_clusters)[chosen_idx];
+            } else {
+                chosen_cluster = best_cluster;
+            }
+
+            /* Move node to randomly chosen cluster */
+            VECTOR(cluster_weights)[chosen_cluster] += VECTOR(*node_weights)[v];
+            VECTOR(nb_nodes_per_cluster)[chosen_cluster]++;
+
+            for (j = 0; j < degree; j++) {
+                long int e = (long int)VECTOR(*edges)[j];
+                long int u = (long int)IGRAPH_OTHER(graph, e, v);
+                if (VECTOR(*membership)[u] == cluster_subset) {
+                    if (VECTOR(*refined_membership)[u] == chosen_cluster) {
+                        VECTOR(external_edge_weight_per_cluster_in_subset)[chosen_cluster] -= VECTOR(*edge_weights)[e];
+                    } else {
+                        VECTOR(external_edge_weight_per_cluster_in_subset)[chosen_cluster] += VECTOR(*edge_weights)[e];
+                    }
+                }
+            }
+
+            /* Set cluster  */
+            if (chosen_cluster != current_cluster) {
+                VECTOR(*refined_membership)[v] = chosen_cluster;
+
+                VECTOR(non_singleton_cluster)[chosen_cluster] = 1;
+            }
+        } /* end if singleton and may be merged */
+    }
+
+    RNG_END();
+
+    IGRAPH_CHECK(igraph_i_community_leiden_clean_refined_membership(node_subset, refined_membership, nb_refined_clusters));
+
+    igraph_vector_destroy(&cum_trans_diff);
+    igraph_vector_destroy(&neighbor_clusters);
+    igraph_vector_bool_destroy(&neighbor_cluster_added);
+    igraph_vector_destroy(&edge_weights_per_cluster);
+    igraph_vector_bool_destroy(&non_singleton_cluster);
+    igraph_vector_destroy(&node_order);
+    igraph_vector_destroy(&external_edge_weight_per_cluster_in_subset);
+    igraph_vector_int_destroy(&nb_nodes_per_cluster);
+    igraph_vector_destroy(&cluster_weights);
+
+    IGRAPH_FINALLY_CLEAN(9);
+
+    return IGRAPH_SUCCESS;
+}
+
+/* Create clusters out of a membership vector.
+ *
+ * The cluster pointer vector should be initialized for all entries of the
+ * membership vector, no range checking is performed. If a vector for a cluster
+ * does not yet exist it will be created and initialized. If a vector for a
+ * cluster already does exist it will not be emptied on first use. Hence, it
+ * should be ensured that all clusters are always properly empty (or
+ * non-existing) before calling this function.
+ */
+int igraph_i_community_get_clusters(const igraph_vector_t *membership, igraph_vector_ptr_t *clusters) {
+    long int i, c, n = igraph_vector_size(membership);
+    igraph_vector_t *cluster;
+    for (i = 0; i < n; i++) {
+        /* Get cluster for node i */
+        c = VECTOR(*membership)[i];
+        cluster = (igraph_vector_t*)VECTOR(*clusters)[c];
+
+        /* No cluster vector exists yet, so we create a new one */
+        if (!cluster) {
+            cluster = igraph_Calloc(1, igraph_vector_t);
+            if (cluster == 0) {
+                IGRAPH_ERROR("Cannot allocate memory for assigning cluster", IGRAPH_ENOMEM);
+            }
+            IGRAPH_CHECK(igraph_vector_init(cluster, 0));
+            VECTOR(*clusters)[c] = cluster;
+        }
+
+        /* Add node i to cluster vector */
+        igraph_vector_push_back(cluster, i);
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/* Aggregate the graph based on the \c refined membership while setting the
+ * membership of each aggregated node according to the \c membership.
+ *
+ * Technically speaking we have that
+ * aggregated_membership[refined_membership[v]] = membership[v] for each node v.
+ *
+ * The new aggregated graph is returned in \c aggregated_graph. This graph
+ * object should not yet be initialized, `igraph_create` is called on it, and
+ * responsibility for destroying the object lies with the calling method
+ *
+ * The remaining results, aggregated_edge_weights, aggregate_node_weights and
+ * aggregated_membership are all expected to be initialized.
+ *
+ */
+int igraph_i_community_leiden_aggregate(
+    const igraph_t *graph, const igraph_inclist_t *edges_per_node, const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights,
+    const igraph_vector_t *membership, const igraph_vector_t *refined_membership, const igraph_integer_t nb_refined_clusters,
+    igraph_t *aggregated_graph, igraph_vector_t *aggregated_edge_weights, igraph_vector_t *aggregated_node_weights, igraph_vector_t *aggregated_membership) {
+    igraph_vector_t aggregated_edges, edge_weight_to_cluster;
+    igraph_vector_ptr_t refined_clusters;
+    igraph_vector_int_t *incident_edges;
+    igraph_vector_t neighbor_clusters;
+    igraph_vector_bool_t neighbor_cluster_added;
+    long int i, j, c, degree, nb_neigh_clusters;
+
+    /* Get refined clusters */
+    IGRAPH_CHECK(igraph_vector_ptr_init(&refined_clusters, nb_refined_clusters));
+    igraph_vector_ptr_set_item_destructor(&refined_clusters, igraph_vector_destroy);
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &refined_clusters);
+    IGRAPH_CHECK(igraph_i_community_get_clusters(refined_membership, &refined_clusters));
+
+    /* Initialize new edges */
+    IGRAPH_CHECK(igraph_vector_init(&aggregated_edges, 0));
+    IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_edges);
+
+    /* We clear the aggregated edge weights, we will push each new edge weight */
+    igraph_vector_clear(aggregated_edge_weights);
+    /* Simply resize the aggregated node weights and membership, they can be set
+     * directly */
+    IGRAPH_CHECK(igraph_vector_resize(aggregated_node_weights, nb_refined_clusters));
+    IGRAPH_CHECK(igraph_vector_resize(aggregated_membership, nb_refined_clusters));
+
+    IGRAPH_CHECK(igraph_vector_init(&edge_weight_to_cluster, nb_refined_clusters));
+    IGRAPH_FINALLY(igraph_vector_destroy, &edge_weight_to_cluster);
+
+    /* Initialize neighboring cluster */
+    IGRAPH_CHECK(igraph_vector_bool_init(&neighbor_cluster_added, nb_refined_clusters));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &neighbor_cluster_added);
+    IGRAPH_CHECK(igraph_vector_init(&neighbor_clusters, nb_refined_clusters));
+    IGRAPH_FINALLY(igraph_vector_destroy, &neighbor_clusters);
+
+    /* Check per cluster */
+    for (c = 0; c < nb_refined_clusters; c++) {
+        igraph_vector_t* refined_cluster = (igraph_vector_t*)VECTOR(refined_clusters)[c];
+        long int n_c = igraph_vector_size(refined_cluster);
+        long int v = -1;
+
+        /* Calculate the total edge weight to other clusters */
+        VECTOR(*aggregated_node_weights)[c] = 0.0;
+        nb_neigh_clusters = 0;
+        for (i = 0; i < n_c; i++) {
+            v = (long int)VECTOR(*refined_cluster)[i];
+            incident_edges = igraph_inclist_get(edges_per_node, v);
+            degree = igraph_vector_int_size(incident_edges);
+
+            for (j = 0; j < degree; j++) {
+                long int e = VECTOR(*incident_edges)[j];
+                long int u = (long int)IGRAPH_OTHER(graph, e, v);
+                long int c2 = VECTOR(*refined_membership)[u];
+
+                if (c2 > c) {
+                    if (!VECTOR(neighbor_cluster_added)[c2]) {
+                        VECTOR(neighbor_cluster_added)[c2] = 1;
+                        VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c2;
+                    }
+                    VECTOR(edge_weight_to_cluster)[c2] += VECTOR(*edge_weights)[e];
+                }
+            }
+
+            VECTOR(*aggregated_node_weights)[c] += VECTOR(*node_weights)[v];
+        }
+
+        /* Add actual edges from this cluster to the other clusters */
+        for (i = 0; i < nb_neigh_clusters; i++) {
+            long int c2 = VECTOR(neighbor_clusters)[i];
+
+            /* Add edge */
+            igraph_vector_push_back(&aggregated_edges, c); igraph_vector_push_back(&aggregated_edges, c2);
+
+            /* Add edge weight */
+            igraph_vector_push_back(aggregated_edge_weights, VECTOR(edge_weight_to_cluster)[c2]);
+
+            VECTOR(edge_weight_to_cluster)[c2] = 0.0;
+            VECTOR(neighbor_cluster_added)[c2] = 0;
+        }
+
+        VECTOR(*aggregated_membership)[c] = VECTOR(*membership)[v];
+
+    }
+
+    IGRAPH_CHECK(igraph_create(aggregated_graph, &aggregated_edges, nb_refined_clusters,
+                               IGRAPH_UNDIRECTED));
+
+    igraph_vector_destroy(&neighbor_clusters);
+    igraph_vector_bool_destroy(&neighbor_cluster_added);
+    igraph_vector_destroy(&edge_weight_to_cluster);
+    igraph_vector_destroy(&aggregated_edges);
+    igraph_vector_ptr_destroy_all(&refined_clusters);
+
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return IGRAPH_SUCCESS;
+}
+
+/* Calculate the quality of the partition.
+ *
+ * The quality is defined as
+ *
+ * 1 / 2m sum_ij (A_ij - gamma n_i n_j)d(s_i, s_j)
+ *
+ * where m is the total edge weight, A_ij is the weight of edge (i, j), gamma is
+ * the so-called resolution parameter, n_i is the node weight of node i, s_i is
+ * the cluster of node i and d(x, y) = 1 if and only if x = y and 0 otherwise.
+ *
+ * Note that by setting n_i = k_i the degree of node i and dividing gamma by 2m,
+ * we effectively optimize modularity. By setting n_i = 1 we optimize the
+ * Constant Potts Model.
+ *
+ * This can be represented as a sum over clusters as
+ *
+ * 1 / 2m sum_c (e_c - gamma N_c^2)
+ *
+ * where e_c = sum_ij A_ij d(s_i, c)d(s_j, c) is (twice) the internal edge
+ * weight in cluster c and N_c = sum_i n_i d(s_i, c) is the sum of the node
+ * weights inside cluster c. This is how the quality is calculated in practice.
+ *
+ */
+int igraph_i_community_leiden_quality(const igraph_t *graph, const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights,
+                                      const igraph_vector_t *membership, const igraph_integer_t nb_comms, const igraph_real_t resolution_parameter,
+                                      igraph_real_t *quality) {
+    igraph_vector_t cluster_weights;
+    igraph_real_t total_edge_weight = 0.0;
+    igraph_eit_t eit;
+    long int i, c, n = igraph_vcount(graph);;
+
+    *quality = 0.0;
+
+    /* Create the edgelist */
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+    i = 0;
+    while (!IGRAPH_EIT_END(eit)) {
+        igraph_integer_t e = IGRAPH_EIT_GET(eit), from, to;
+        IGRAPH_CHECK(igraph_edge(graph, e, &from, &to));
+        total_edge_weight += VECTOR(*edge_weights)[e];
+        /* We add the internal edge weights */
+        if (VECTOR(*membership)[(long int) from] == VECTOR(*membership)[(long int) to]) {
+            *quality += 2 * VECTOR(*edge_weights)[e];
+        }
+        IGRAPH_EIT_NEXT(eit);
+    }
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Initialize cluster weights and nb nodes */
+    IGRAPH_CHECK(igraph_vector_init(&cluster_weights, n));
+    IGRAPH_FINALLY(igraph_vector_destroy, &cluster_weights);
+    for (i = 0; i < n; i++) {
+        c = VECTOR(*membership)[i];
+        VECTOR(cluster_weights)[c] += VECTOR(*node_weights)[i];
+    }
+
+    /* We subtract gamma * N_c^2 */
+    for (c = 0; c < nb_comms; c++) {
+        *quality -= resolution_parameter * VECTOR(cluster_weights)[c] * VECTOR(cluster_weights)[c];
+    }
+
+    igraph_vector_destroy(&cluster_weights);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* We normalise by 2m */
+    *quality /= (2.0 * total_edge_weight);
+
+    return IGRAPH_SUCCESS;
+}
+
+/* This is the core of the Leiden algorithm and relies on subroutines to
+ * perform the three different phases: (1) local moving of nodes, (2)
+ * refinement of the partition and (3) aggregation of the network based on the
+ * refined partition, using the non-refined partition to create an initial
+ * partition for the aggregate network.
+ */
+int igraph_i_community_leiden(const igraph_t *graph,
+                              const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights,
+                              const igraph_real_t resolution_parameter, const igraph_real_t beta,
+                              igraph_vector_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) {
+    igraph_integer_t nb_refined_clusters;
+    long int i, c, n = igraph_vcount(graph);
+    igraph_t *aggregated_graph, *tmp_graph;
+    igraph_vector_t *aggregated_edge_weights, *aggregated_node_weights, *aggregated_membership;
+    igraph_vector_t tmp_edge_weights, tmp_node_weights, tmp_membership;
+    igraph_vector_t refined_membership;
+    igraph_vector_int_t aggregate_node;
+    igraph_vector_ptr_t clusters;
+    igraph_inclist_t edges_per_node;
+    igraph_bool_t continue_clustering;
+    igraph_integer_t level = 0;
+
+    /* Initialize temporary weights and membership to be used in aggregation */
+    IGRAPH_CHECK(igraph_vector_init(&tmp_edge_weights, 0));
+    IGRAPH_FINALLY(igraph_vector_destroy, &tmp_edge_weights);
+    IGRAPH_CHECK(igraph_vector_init(&tmp_node_weights, 0));
+    IGRAPH_FINALLY(igraph_vector_destroy, &tmp_node_weights);
+    IGRAPH_CHECK(igraph_vector_init(&tmp_membership, 0));
+    IGRAPH_FINALLY(igraph_vector_destroy, &tmp_membership);
+
+    /* Initialize clusters */
+    IGRAPH_CHECK(igraph_vector_ptr_init(&clusters, n));
+    igraph_vector_ptr_set_item_destructor(&clusters, igraph_vector_destroy);
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &clusters);
+    /* Initialize aggregate nodes, which initially is identical to simply the
+     * nodes in the graph. */
+    IGRAPH_CHECK(igraph_vector_int_init(&aggregate_node, n));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &aggregate_node);
+    for (i = 0; i < n; i++) {
+        VECTOR(aggregate_node)[i] = i;
+    }
+
+    IGRAPH_CHECK(igraph_vector_init(&refined_membership, 0));
+    IGRAPH_FINALLY(igraph_vector_destroy, &refined_membership);
+
+    /* Initialize aggregated graph, weights and membership. */
+    aggregated_graph = graph;
+    aggregated_edge_weights = edge_weights;
+    aggregated_node_weights = node_weights;
+    aggregated_membership = membership;
+
+    /* Clean membership and count number of *clusters */
+    IGRAPH_CHECK(igraph_reindex_membership(aggregated_membership, NULL, nb_clusters));
+
+    if (*nb_clusters > n) {
+        IGRAPH_ERROR("Too many communities in membership vector", IGRAPH_EINVAL);
+    }
+
+    do {
+
+        /* Get incidence list for fast iteration */
+        IGRAPH_CHECK(igraph_inclist_init(aggregated_graph, &edges_per_node, IGRAPH_ALL));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &edges_per_node);
+
+        /* Move around the nodes in order to increase the quality */
+        IGRAPH_CHECK(igraph_i_community_leiden_fastmovenodes(aggregated_graph,
+                     &edges_per_node,
+                     aggregated_edge_weights, aggregated_node_weights,
+                     resolution_parameter,
+                     nb_clusters,
+                     aggregated_membership));
+
+        /* We only continue clustering if not all clusters are represented by a
+         * single node yet
+         */
+        continue_clustering = (*nb_clusters < igraph_vcount(aggregated_graph));
+
+        if (continue_clustering) {
+            /* Set original membership */
+            if (level > 0) {
+                for (i = 0; i < n; i++) {
+                    long int v_aggregate = VECTOR(aggregate_node)[i];
+                    VECTOR(*membership)[i] = VECTOR(*aggregated_membership)[v_aggregate];
+                }
+            }
+
+            /* Get node sets for each cluster. */
+            IGRAPH_CHECK(igraph_i_community_get_clusters(aggregated_membership, &clusters));
+
+            /* Ensure refined membership is correct size */
+            IGRAPH_CHECK(igraph_vector_resize(&refined_membership, igraph_vcount(aggregated_graph)));
+
+            /* Refine each cluster */
+            nb_refined_clusters = 0;
+            for (c = 0; c < *nb_clusters; c++) {
+                igraph_vector_t* cluster = (igraph_vector_t*)VECTOR(clusters)[c];
+                IGRAPH_CHECK(igraph_i_community_leiden_mergenodes(aggregated_graph,
+                             &edges_per_node,
+                             aggregated_edge_weights, aggregated_node_weights,
+                             cluster, aggregated_membership, c,
+                             resolution_parameter, beta,
+                             &nb_refined_clusters, &refined_membership));
+                /* Empty cluster */
+                igraph_vector_clear(cluster);
+            }
+
+            /* If refinement didn't aggregate anything, we aggregate on the basis of
+             * the actual clustering */
+            if (nb_refined_clusters >= igraph_vcount(aggregated_graph)) {
+                igraph_vector_update(&refined_membership, aggregated_membership);
+            }
+
+            /* Keep track of aggregate node. */
+            for (i = 0; i < n; i++) {
+                /* Current aggregate node */
+                igraph_integer_t v_aggregate = VECTOR(aggregate_node)[i];
+                /* New aggregate node */
+                VECTOR(aggregate_node)[i] = (igraph_integer_t)VECTOR(refined_membership)[v_aggregate];
+            }
+
+            /* Allocate temporary graph */
+            tmp_graph = igraph_Calloc(1, igraph_t);
+            if (tmp_graph == 0) {
+                IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for aggregate graph", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(free, tmp_graph);
+
+            IGRAPH_CHECK(igraph_i_community_leiden_aggregate(
+                             aggregated_graph, &edges_per_node, aggregated_edge_weights, aggregated_node_weights,
+                             aggregated_membership, &refined_membership, nb_refined_clusters,
+                             tmp_graph, &tmp_edge_weights, &tmp_node_weights, &tmp_membership));
+
+            /* Graph has been created by aggregation, ensure it is properly destroyed if
+             * an error occurs. */
+            IGRAPH_FINALLY(igraph_destroy, tmp_graph);
+
+            if (level >= 1) {
+                /* Destroy previously allocated graph (note that aggregated_graph points to
+                 * the previously allocated tmp_graph). */
+                igraph_destroy(aggregated_graph);
+                igraph_Free(aggregated_graph);
+                IGRAPH_FINALLY_CLEAN(2);
+            }
+
+            /* On the lowest level, the actual graph and node and edge weights and
+             * membership are used. On higher levels, we will have to use a new graph
+             * and node and edge weights to represent them. We perform the allocation
+             * of memory here. We only allocate the memory once, and simply update
+             * them in any subsequent rounds.
+             */
+            if (level == 0) {
+                aggregated_edge_weights = igraph_Calloc(1, igraph_vector_t);
+                if (aggregated_edge_weights == 0) {
+                    IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for aggregate edge weights", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(free, aggregated_edge_weights);
+                IGRAPH_CHECK(igraph_vector_init(aggregated_edge_weights, 0));
+                IGRAPH_FINALLY(igraph_vector_destroy, aggregated_edge_weights);
+
+                aggregated_node_weights = igraph_Calloc(1, igraph_vector_t);
+                if (aggregated_node_weights == 0) {
+                    IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for aggregate node weights", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(free, aggregated_node_weights);
+                IGRAPH_CHECK(igraph_vector_init(aggregated_node_weights, 0));
+                IGRAPH_FINALLY(igraph_vector_destroy, aggregated_node_weights);
+
+                aggregated_membership = igraph_Calloc(1, igraph_vector_t);
+                if (aggregated_membership == 0) {
+                    IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for aggregate membership", IGRAPH_ENOMEM);
+                }
+                IGRAPH_FINALLY(free, aggregated_membership);
+                IGRAPH_CHECK(igraph_vector_init(aggregated_membership, 0));
+                IGRAPH_FINALLY(igraph_vector_destroy, aggregated_membership);
+            }
+
+            /* Set the aggregated graph correctly */
+            aggregated_graph = tmp_graph;
+
+            /* Update the aggregated administration. This does not allocate memory,
+             * it will always fit in existing memory allocated previously. */
+            igraph_vector_update(aggregated_edge_weights, &tmp_edge_weights);
+            igraph_vector_update(aggregated_node_weights, &tmp_node_weights);
+            igraph_vector_update(aggregated_membership, &tmp_membership);
+
+            level += 1;
+        }
+
+        /* We are done iterating, so we destroy the incidence list */
+        igraph_inclist_destroy(&edges_per_node);
+        IGRAPH_FINALLY_CLEAN(1);
+    } while (continue_clustering);
+
+    /* If memory was allocated to represent the aggregated administration we need
+     * to make sure it is properly freed. This is only done if we have at least
+     * passed on to the next level of aggregation.
+     */
+    if (level > 0) {
+        igraph_destroy(aggregated_graph);
+        igraph_Free(aggregated_graph);
+        igraph_vector_destroy(aggregated_membership);
+        igraph_Free(aggregated_membership);
+        igraph_vector_destroy(aggregated_node_weights);
+        igraph_Free(aggregated_node_weights);
+        igraph_vector_destroy(aggregated_edge_weights);
+        igraph_Free(aggregated_edge_weights);
+        IGRAPH_FINALLY_CLEAN(8);
+    }
+
+    /* Free remaining memory */
+    igraph_vector_destroy(&refined_membership);
+    igraph_vector_int_destroy(&aggregate_node);
+    igraph_vector_ptr_destroy_all(&clusters);
+    igraph_vector_destroy(&tmp_membership);
+    igraph_vector_destroy(&tmp_node_weights);
+    igraph_vector_destroy(&tmp_edge_weights);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    /* Calculate quality */
+    if (quality) {
+        igraph_i_community_leiden_quality(graph, edge_weights, node_weights, membership, *nb_clusters, resolution_parameter, quality);
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup communities
+ * \function igraph_community_leiden
+ * \brief Finding community structure using the Leiden algorithm.
+ *
+ * This function implements the Leiden algorithm for finding community
+ * structure, see Traag, V. A., Waltman, L., &amp; van Eck, N. J. (2019). From
+ * Louvain to Leiden: guaranteeing well-connected communities. Scientific
+ * reports, 9(1), 5233.  http://dx.doi.org/10.1038/s41598-019-41695-z.
+ *
+ * </para><para>
+ * It is similar to the multilevel algorithm, often called the Louvain
+ * algorithm, but it is faster and yields higher quality solutions. It can
+ * optimize both modularity and the Constant Potts Model, which does not suffer
+ * from the resolution-limit (see preprint http://arxiv.org/abs/1104.3083).
+ *
+ * </para><para>
+ * The Leiden algorithm consists of three phases: (1) local moving of nodes,
+ * (2) refinement of the partition and (3) aggregation of the network based on
+ * the refined partition, using the non-refined partition to create an initial
+ * partition for the aggregate network. In the local move procedure in the
+ * Leiden algorithm, only nodes whose neighborhood has changed are visited. The
+ * refinement is done by restarting from a singleton partition within each
+ * cluster and gradually merging the subclusters. When aggregating, a single
+ * cluster may then be represented by several nodes (which are the subclusters
+ * identified in the refinement).
+ *
+ * </para><para>
+ * The Leiden algorithm provides several guarantees. The Leiden algorithm is
+ * typically iterated: the output of one iteration is used as the input for the
+ * next iteration. At each iteration all clusters are guaranteed to be
+ * connected and well-separated. After an iteration in which nothing has
+ * changed, all nodes and some parts are guaranteed to be locally optimally
+ * assigned. Finally, asymptotically, all subsets of all clusters are
+ * guaranteed to be locally optimally assigned. For more details, please see
+ * Traag, Waltman &amp; van Eck (2019).
+ *
+ * </para><para>
+ * The objective function being optimized is
+ *
+ * </para><para>
+ * 1 / 2m sum_ij (A_ij - gamma n_i n_j)d(s_i, s_j)
+ *
+ * </para><para>
+ * where m is the total edge weight, A_ij is the weight of edge (i, j), gamma is
+ * the so-called resolution parameter, n_i is the node weight of node i, s_i is
+ * the cluster of node i and d(x, y) = 1 if and only if x = y and 0 otherwise.
+ * By setting n_i = k_i, the degree of node i, and dividing gamma by 2m, you
+ * effectively obtain an expression for modularity. Hence, the standard
+ * modularity will be optimized when you supply the degrees as \c node_weights
+ * and by supplying as a resolution parameter 1.0/(2*m), with m the number of
+ * edges.
+ *
+ * \param graph The input graph. It must be an undirected graph.
+ * \param edge_weights Numeric vector containing edge weights. If \c NULL, every edge
+ *    has equal weight of 1. The weights need not be non-negative.
+ * \param node_weights Numeric vector containing node weights.
+ * \param resolution_parameter The resolution parameter used, which is
+ *    represented by gamma in the objective function mentioned in the
+ *    documentation.
+ * \param beta The randomness used in the refinement step when merging. A small
+ *    amount of randomness (\c beta = 0.01) typically works well.
+ * \param start Start from membership vector. If this is true, the optimization
+ *    will start from the provided membership vector. If this is false, the
+ *    optimization will start from a singleton partition.
+ * \param membership The membership vector. This is both used as the initial
+ *    membership from which optimisation starts and is updated in place. It
+ *    must hence be properly initialized. When finding clusters from scratch it
+ *    is typically started using a singleton clustering. This can be achieved
+ *    using \c igraph_vector_init_seq.
+ * \param nb_clusters The number of clusters contained in \c membership. Must
+ *    not be a \c NULL pointer.
+ * \param quality The quality of the partition, in terms of the objective
+ *    function as included in the documentation. If \c NULL the quality will
+ *    not be calculated.
+ * \return Error code.
+ *
+ * Time complexity: near linear on sparse graphs.
+ *
+ * \example examples/simple/igraph_community_leiden.c
+ */
+int igraph_community_leiden(const igraph_t *graph,
+                            const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights,
+                            const igraph_real_t resolution_parameter, const igraph_real_t beta, const igraph_bool_t start,
+                            igraph_vector_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) {
+    igraph_vector_t *i_edge_weights, *i_node_weights;
+    int ret;
+    igraph_integer_t n = igraph_vcount(graph);
+
+    if (start) {
+        if (!membership) {
+            IGRAPH_ERROR("Cannot start optimization if membership is missing", IGRAPH_EINVAL);
+        }
+
+        if (igraph_vector_size(membership) != n) {
+            IGRAPH_ERROR("Initial membership length does not equal the number of vertices", IGRAPH_EINVAL);
+        }
+    } else {
+        int i;
+        if (!membership)
+            IGRAPH_ERROR("Membership vector should be supplied and initialized, "
+                         "even when not starting optimization from it", IGRAPH_EINVAL);
+
+        igraph_vector_resize(membership, n);
+        for (i = 0; i < n; i++) {
+            VECTOR(*membership)[i] = i;
+        }
+    }
+
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Leiden algorithm is only implemented for undirected graphs", IGRAPH_EINVAL);
+    }
+
+    /* Check edge weights to possibly use default */
+    if (!edge_weights) {
+        i_edge_weights = igraph_Calloc(1, igraph_vector_t);
+        if (i_edge_weights == 0) {
+            IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for edge weights", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_init(i_edge_weights, igraph_ecount(graph)));
+        IGRAPH_FINALLY(free, i_edge_weights);
+        IGRAPH_FINALLY(igraph_vector_destroy, i_edge_weights);
+        igraph_vector_fill(i_edge_weights, 1);
+    } else {
+        i_edge_weights = edge_weights;
+    }
+
+    /* Check edge weights to possibly use default */
+    if (!node_weights) {
+        i_node_weights = igraph_Calloc(1, igraph_vector_t);
+        if (i_node_weights == 0) {
+            IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for node weights", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_init(i_node_weights, n));
+        IGRAPH_FINALLY(free, i_node_weights);
+        IGRAPH_FINALLY(igraph_vector_destroy, i_node_weights);
+        igraph_vector_fill(i_node_weights, 1);
+    } else {
+        i_node_weights = node_weights;
+    }
+
+    /* Perform actual Leiden algorithm */
+    ret = igraph_i_community_leiden(graph, i_edge_weights, i_node_weights,
+                                    resolution_parameter, beta,
+                                    membership, nb_clusters, quality);
+
+    if (!edge_weights) {
+        igraph_vector_destroy(i_edge_weights);
+        igraph_Free(i_edge_weights);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    if (!node_weights) {
+        igraph_vector_destroy(i_node_weights);
+        igraph_Free(i_node_weights);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    return ret;
+}
diff --git a/igraph/src/complex.c b/igraph/src/complex.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/complex.c
@@ -0,0 +1,392 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_complex.h"
+#include "igraph_math.h"
+#include <math.h>
+
+/**
+ * \example igraph_complex.c
+ */
+
+igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = x;
+    IGRAPH_IMAG(res) = y;
+    return res;
+}
+
+igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = r * cos(theta);
+    IGRAPH_IMAG(res) = r * sin(theta);
+    return res;
+}
+
+igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1,
+                                    igraph_complex_t z2,
+                                    igraph_real_t tol) {
+    if (fabs(IGRAPH_REAL(z1) - IGRAPH_REAL(z2)) > tol ||
+        fabs(IGRAPH_IMAG(z1) - IGRAPH_IMAG(z2)) > tol) {
+        return 0;
+    }
+    return 1;
+}
+
+igraph_real_t igraph_complex_mod(igraph_complex_t z) {
+    igraph_real_t x = IGRAPH_REAL(z);
+    igraph_real_t y = IGRAPH_IMAG(z);
+    return hypot(x, y);
+}
+
+igraph_real_t igraph_complex_arg(igraph_complex_t z) {
+    igraph_real_t x = IGRAPH_REAL(z);
+    igraph_real_t y = IGRAPH_IMAG(z);
+    if (x == 0.0 && y == 0.0) {
+        return 0.0;
+    }
+    return atan2(y, x);
+}
+
+igraph_complex_t igraph_complex_add(igraph_complex_t z1,
+                                    igraph_complex_t z2) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z1) + IGRAPH_REAL(z2);
+    IGRAPH_IMAG(res) = IGRAPH_IMAG(z1) + IGRAPH_IMAG(z2);
+    return res;
+}
+
+igraph_complex_t igraph_complex_sub(igraph_complex_t z1,
+                                    igraph_complex_t z2) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z1) - IGRAPH_REAL(z2);
+    IGRAPH_IMAG(res) = IGRAPH_IMAG(z1) - IGRAPH_IMAG(z2);
+    return res;
+}
+
+igraph_complex_t igraph_complex_mul(igraph_complex_t z1,
+                                    igraph_complex_t z2) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z1) * IGRAPH_REAL(z2) -
+                       IGRAPH_IMAG(z1) * IGRAPH_IMAG(z2);
+    IGRAPH_IMAG(res) = IGRAPH_REAL(z1) * IGRAPH_IMAG(z2) +
+                       IGRAPH_IMAG(z1) * IGRAPH_REAL(z2);
+    return res;
+}
+
+igraph_complex_t igraph_complex_div(igraph_complex_t z1,
+                                    igraph_complex_t z2) {
+    igraph_complex_t res;
+    igraph_real_t z1r = IGRAPH_REAL(z1), z1i = IGRAPH_IMAG(z1);
+    igraph_real_t z2r = IGRAPH_REAL(z2), z2i = IGRAPH_IMAG(z2);
+    igraph_real_t s = 1.0 / igraph_complex_abs(z2);
+    igraph_real_t sz2r = s * z2r;
+    igraph_real_t sz2i = s * z2i;
+    IGRAPH_REAL(res) = (z1r * sz2r + z1i * sz2i) * s;
+    IGRAPH_IMAG(res) = (z1i * sz2r - z1r * sz2i) * s;
+    return res;
+}
+
+igraph_complex_t igraph_complex_add_real(igraph_complex_t z,
+        igraph_real_t x) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z) + x;
+    IGRAPH_IMAG(res) = IGRAPH_IMAG(z);
+    return res;
+}
+
+igraph_complex_t igraph_complex_add_imag(igraph_complex_t z,
+        igraph_real_t y) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z);
+    IGRAPH_IMAG(res) = IGRAPH_IMAG(z) + y;
+    return res;
+}
+
+igraph_complex_t igraph_complex_sub_real(igraph_complex_t z,
+        igraph_real_t x) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z) - x;
+    IGRAPH_IMAG(res) = IGRAPH_IMAG(z);
+    return res;
+}
+
+igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z,
+        igraph_real_t y) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z);
+    IGRAPH_IMAG(res) = IGRAPH_IMAG(z) - y;
+    return res;
+}
+
+igraph_complex_t igraph_complex_mul_real(igraph_complex_t z,
+        igraph_real_t x) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z) * x;
+    IGRAPH_IMAG(res) = IGRAPH_IMAG(z) * x;
+    return res;
+}
+
+igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z,
+        igraph_real_t y) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = - IGRAPH_IMAG(z) * y;
+    IGRAPH_IMAG(res) =   IGRAPH_REAL(z) * y;
+    return res;
+}
+
+igraph_complex_t igraph_complex_div_real(igraph_complex_t z,
+        igraph_real_t x) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = IGRAPH_REAL(z) / x;
+    IGRAPH_IMAG(res) = IGRAPH_IMAG(z) / x;
+    return res;
+}
+
+igraph_complex_t igraph_complex_div_imag(igraph_complex_t z,
+        igraph_real_t y) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) =   IGRAPH_IMAG(z) / y;
+    IGRAPH_IMAG(res) = - IGRAPH_REAL(z) / y;
+    return res;
+}
+
+igraph_complex_t igraph_complex_conj(igraph_complex_t z) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) =   IGRAPH_REAL(z);
+    IGRAPH_IMAG(res) = - IGRAPH_IMAG(z);
+    return res;
+}
+
+igraph_complex_t igraph_complex_neg(igraph_complex_t z) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = - IGRAPH_REAL(z);
+    IGRAPH_IMAG(res) = - IGRAPH_IMAG(z);
+    return res;
+}
+
+igraph_complex_t igraph_complex_inv(igraph_complex_t z) {
+    igraph_complex_t res;
+    igraph_real_t s = 1.0 / igraph_complex_abs(z);
+    IGRAPH_REAL(res) =   (IGRAPH_REAL(z) * s) * s;
+    IGRAPH_IMAG(res) = - (IGRAPH_IMAG(z) * s) * s;
+    return res;
+}
+
+igraph_real_t igraph_complex_abs(igraph_complex_t z) {
+    return hypot(IGRAPH_REAL(z), IGRAPH_IMAG(z));
+}
+
+igraph_real_t igraph_complex_logabs(igraph_complex_t z) {
+    igraph_real_t xabs = fabs(IGRAPH_REAL(z));
+    igraph_real_t yabs = fabs(IGRAPH_IMAG(z));
+    igraph_real_t max, u;
+    if (xabs >= yabs) {
+        max = xabs;
+        u = yabs / xabs;
+    } else {
+        max = yabs;
+        u = xabs / yabs;
+    }
+    return log (max) + 0.5 * log1p (u * u);
+}
+
+igraph_complex_t igraph_complex_sqrt(igraph_complex_t z) {
+    igraph_complex_t res;
+
+    if (IGRAPH_REAL(z) == 0.0 && IGRAPH_IMAG(z) == 0.0) {
+        IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0;
+    } else {
+        igraph_real_t x = fabs (IGRAPH_REAL(z));
+        igraph_real_t y = fabs (IGRAPH_IMAG(z));
+        igraph_real_t w;
+        if (x >= y)  {
+            igraph_real_t t = y / x;
+            w = sqrt (x) * sqrt (0.5 * (1.0 + sqrt (1.0 + t * t)));
+        } else {
+            igraph_real_t t = x / y;
+            w = sqrt (y) * sqrt (0.5 * (t + sqrt (1.0 + t * t)));
+        }
+
+        if (IGRAPH_REAL(z) >= 0.0) {
+            igraph_real_t ai = IGRAPH_IMAG(z);
+            IGRAPH_REAL(res) = w;
+            IGRAPH_IMAG(res) = ai / (2.0 * w);
+        } else {
+            igraph_real_t ai = IGRAPH_IMAG(z);
+            igraph_real_t vi = (ai >= 0) ? w : -w;
+            IGRAPH_REAL(res) = ai / (2.0 * vi);
+            IGRAPH_IMAG(res) = vi;
+        }
+    }
+
+    return res;
+}
+
+igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x) {
+    igraph_complex_t res;
+    if (x >= 0) {
+        IGRAPH_REAL(res) = sqrt(x);
+        IGRAPH_IMAG(res) = 0.0;
+    } else {
+        IGRAPH_REAL(res) = 0.0;
+        IGRAPH_IMAG(res) = sqrt(-x);
+    }
+    return res;
+}
+
+igraph_complex_t igraph_complex_exp(igraph_complex_t z) {
+    igraph_real_t rho   = exp(IGRAPH_REAL(z));
+    igraph_real_t theta = IGRAPH_IMAG(z);
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = rho * cos(theta);
+    IGRAPH_IMAG(res) = rho * sin(theta);
+    return res;
+}
+
+igraph_complex_t igraph_complex_pow(igraph_complex_t z1,
+                                    igraph_complex_t z2) {
+    igraph_complex_t res;
+
+    if (IGRAPH_REAL(z1) == 0 && IGRAPH_IMAG(z1) == 0.0) {
+        if (IGRAPH_REAL(z2) == 0 && IGRAPH_IMAG(z2) == 0.0) {
+            IGRAPH_REAL(res) = 1.0;
+            IGRAPH_IMAG(res) = 0.0;
+        } else {
+            IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0;
+        }
+    } else if (IGRAPH_REAL(z2) == 1.0 && IGRAPH_IMAG(z2) == 0.0) {
+        IGRAPH_REAL(res) = IGRAPH_REAL(z1);
+        IGRAPH_IMAG(res) = IGRAPH_IMAG(z1);
+    } else if (IGRAPH_REAL(z2) == -1.0 && IGRAPH_IMAG(z2) == 0.0) {
+        res = igraph_complex_inv(z1);
+    } else {
+        igraph_real_t logr = igraph_complex_logabs (z1);
+        igraph_real_t theta = igraph_complex_arg (z1);
+        igraph_real_t z2r = IGRAPH_REAL(z2), z2i = IGRAPH_IMAG(z2);
+        igraph_real_t rho = exp (logr * z2r - z2i * theta);
+        igraph_real_t beta = theta * z2r + z2i * logr;
+        IGRAPH_REAL(res) = rho * cos(beta);
+        IGRAPH_IMAG(res) = rho * sin(beta);
+    }
+
+    return res;
+}
+
+igraph_complex_t igraph_complex_pow_real(igraph_complex_t z,
+        igraph_real_t x) {
+    igraph_complex_t res;
+    if (IGRAPH_REAL(z) == 0.0 && IGRAPH_IMAG(z) == 0.0) {
+        if (x == 0) {
+            IGRAPH_REAL(res) = 1.0;
+            IGRAPH_IMAG(res) = 0.0;
+        } else {
+            IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0;
+        }
+    } else {
+        igraph_real_t logr = igraph_complex_logabs(z);
+        igraph_real_t theta = igraph_complex_arg(z);
+        igraph_real_t rho = exp (logr * x);
+        igraph_real_t beta = theta * x;
+        IGRAPH_REAL(res) = rho * cos(beta);
+        IGRAPH_IMAG(res) = rho * sin(beta);
+    }
+    return res;
+}
+
+igraph_complex_t igraph_complex_log(igraph_complex_t z) {
+    igraph_complex_t res;
+    IGRAPH_REAL(res) = igraph_complex_logabs(z);
+    IGRAPH_IMAG(res) = igraph_complex_arg(z);
+    return res;
+}
+
+igraph_complex_t igraph_complex_log10(igraph_complex_t z) {
+    return igraph_complex_mul_real(igraph_complex_log(z), 1 / log(10.0));
+}
+
+igraph_complex_t igraph_complex_log_b(igraph_complex_t z,
+                                      igraph_complex_t b) {
+    return igraph_complex_div (igraph_complex_log(z), igraph_complex_log(b));
+}
+
+igraph_complex_t igraph_complex_sin(igraph_complex_t z) {
+    igraph_real_t zr = IGRAPH_REAL(z);
+    igraph_real_t zi = IGRAPH_IMAG(z);
+    igraph_complex_t res;
+    if (zi == 0.0) {
+        IGRAPH_REAL(res) = sin(zr);
+        IGRAPH_IMAG(res) = 0.0;
+    } else {
+        IGRAPH_REAL(res) = sin(zr) * cosh(zi);
+        IGRAPH_IMAG(res) = cos(zr) * sinh(zi);
+    }
+    return res;
+}
+
+igraph_complex_t igraph_complex_cos(igraph_complex_t z) {
+    igraph_real_t zr = IGRAPH_REAL(z);
+    igraph_real_t zi = IGRAPH_IMAG(z);
+    igraph_complex_t res;
+    if (zi == 0.0) {
+        IGRAPH_REAL(res) = cos(zr);
+        IGRAPH_IMAG(res) = 0.0;
+    } else {
+        IGRAPH_REAL(res) = cos(zr) * cosh(zi);
+        IGRAPH_IMAG(res) = sin(zr) * sinh(-zi);
+    }
+    return res;
+}
+
+igraph_complex_t igraph_complex_tan(igraph_complex_t z) {
+    igraph_real_t zr = IGRAPH_REAL(z);
+    igraph_real_t zi = IGRAPH_IMAG(z);
+    igraph_complex_t res;
+    if (fabs (zi) < 1) {
+        igraph_real_t D = pow (cos (zr), 2.0) + pow (sinh (zi), 2.0);
+        IGRAPH_REAL(res) = 0.5 * sin (2 * zr) / D;
+        IGRAPH_IMAG(res) = 0.5 * sinh (2 * zi) / D;
+    } else {
+        igraph_real_t u = exp (-zi);
+        igraph_real_t C = 2 * u / (1 - pow (u, 2.0));
+        igraph_real_t D = 1 + pow (cos (zr), 2.0) * pow (C, 2.0);
+        igraph_real_t S = pow (C, 2.0);
+        igraph_real_t T = 1.0 / tanh (zi);
+        IGRAPH_REAL(res) = 0.5 * sin (2 * zr) * S / D;
+        IGRAPH_IMAG(res) = T / D;
+    }
+    return res;
+}
+
+igraph_complex_t igraph_complex_sec(igraph_complex_t z) {
+    return igraph_complex_inv(igraph_complex_cos(z));
+}
+
+igraph_complex_t igraph_complex_csc(igraph_complex_t z) {
+    return igraph_complex_inv(igraph_complex_sin(z));
+}
+
+igraph_complex_t igraph_complex_cot(igraph_complex_t z) {
+    return igraph_complex_inv(igraph_complex_tan(z));
+}
+
diff --git a/igraph/src/components.c b/igraph/src/components.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/components.c
@@ -0,0 +1,1248 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_components.h"
+#include "igraph_memory.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_progress.h"
+#include "igraph_structural.h"
+#include "igraph_dqueue.h"
+#include "igraph_stack.h"
+#include "igraph_vector.h"
+#include "config.h"
+#include <string.h>
+#include <limits.h>
+
+static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership,
+                                  igraph_vector_t *csize, igraph_integer_t *no);
+
+static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership,
+                                    igraph_vector_t *csize, igraph_integer_t *no);
+
+/**
+ * \ingroup structural
+ * \function igraph_clusters
+ * \brief Calculates the (weakly or strongly) connected components in a graph.
+ *
+ * \param graph The graph object to analyze.
+ * \param membership First half of the result will be stored here. For
+ *        every vertex the id of its component is given. The vector
+ *        has to be preinitialized and will be resized. Alternatively
+ *        this argument can be \c NULL, in which case it is ignored.
+ * \param csize The second half of the result. For every component it
+ *        gives its size, the order is defined by the component ids.
+ *        The vector has to be preinitialized and will be resized.
+ *        Alternatively this argument can be \c NULL, in which
+ *        case it is ignored.
+ * \param no Pointer to an integer, if not \c NULL then the number of
+ *        clusters will be stored here.
+ * \param mode For directed graph this specifies whether to calculate
+ *        weakly or strongly connected components. Possible values:
+ *        \c IGRAPH_WEAK,
+ *        \c IGRAPH_STRONG. This argument is
+ *        ignored for undirected graphs.
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid mode argument.
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the graph.
+ */
+
+int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership,
+                    igraph_vector_t *csize, igraph_integer_t *no,
+                    igraph_connectedness_t mode) {
+    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
+        return igraph_i_clusters_weak(graph, membership, csize, no);
+    } else if (mode == IGRAPH_STRONG) {
+        return igraph_i_clusters_strong(graph, membership, csize, no);
+    } else {
+        IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_EINVAL);
+    }
+
+    return 1;
+}
+
+static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership,
+                                  igraph_vector_t *csize, igraph_integer_t *no) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    char *already_added;
+    long int first_node, act_cluster_size = 0, no_of_clusters = 1;
+
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+
+    long int i;
+    igraph_vector_t neis = IGRAPH_VECTOR_NULL;
+
+    already_added = igraph_Calloc(no_of_nodes, char);
+    if (already_added == 0) {
+        IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, already_added);
+
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    /* Memory for result, csize is dynamically allocated */
+    if (membership) {
+        IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+    }
+    if (csize) {
+        igraph_vector_clear(csize);
+    }
+
+    /* The algorithm */
+
+    for (first_node = 0; first_node < no_of_nodes; ++first_node) {
+        if (already_added[first_node] == 1) {
+            continue;
+        }
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        already_added[first_node] = 1;
+        act_cluster_size = 1;
+        if (membership) {
+            VECTOR(*membership)[first_node] = no_of_clusters - 1;
+        }
+        IGRAPH_CHECK(igraph_dqueue_push(&q, first_node));
+
+        while ( !igraph_dqueue_empty(&q) ) {
+            long int act_node = (long int) igraph_dqueue_pop(&q);
+            IGRAPH_CHECK(igraph_neighbors(graph, &neis,
+                                          (igraph_integer_t) act_node, IGRAPH_ALL));
+            for (i = 0; i < igraph_vector_size(&neis); i++) {
+                long int neighbor = (long int) VECTOR(neis)[i];
+                if (already_added[neighbor] == 1) {
+                    continue;
+                }
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                already_added[neighbor] = 1;
+                act_cluster_size++;
+                if (membership) {
+                    VECTOR(*membership)[neighbor] = no_of_clusters - 1;
+                }
+            }
+        }
+        no_of_clusters++;
+        if (csize) {
+            IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size));
+        }
+    }
+
+    /* Cleaning up */
+
+    if (no) {
+        *no = (igraph_integer_t) no_of_clusters - 1;
+    }
+
+    igraph_Free(already_added);
+    igraph_dqueue_destroy(&q);
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership,
+                                    igraph_vector_t *csize, igraph_integer_t *no) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t next_nei = IGRAPH_VECTOR_NULL;
+
+    long int i, n, num_seen;
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+
+    long int no_of_clusters = 1;
+    long int act_cluster_size;
+
+    igraph_vector_t out = IGRAPH_VECTOR_NULL;
+    const igraph_vector_int_t* tmp;
+
+    igraph_adjlist_t adjlist;
+
+    /* The result */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&next_nei, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&out, 0);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    if (membership) {
+        IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+    }
+    IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes));
+
+    igraph_vector_null(&out);
+    if (csize) {
+        igraph_vector_clear(csize);
+    }
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    num_seen = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        tmp = igraph_adjlist_get(&adjlist, i);
+        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
+            continue;
+        }
+
+        IGRAPH_CHECK(igraph_dqueue_push(&q, i));
+        while (!igraph_dqueue_empty(&q)) {
+            long int act_node = (long int) igraph_dqueue_back(&q);
+            tmp = igraph_adjlist_get(&adjlist, act_node);
+            if (VECTOR(next_nei)[act_node] == 0) {
+                /* this is the first time we've met this vertex */
+                VECTOR(next_nei)[act_node]++;
+            } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) {
+                /* we've already met this vertex but it has more children */
+                long int neighbor = (long int) VECTOR(*tmp)[(long int)
+                                    VECTOR(next_nei)[act_node] - 1];
+                if (VECTOR(next_nei)[neighbor] == 0) {
+                    IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                }
+                VECTOR(next_nei)[act_node]++;
+            } else {
+                /* we've met this vertex and it has no more children */
+                IGRAPH_CHECK(igraph_vector_push_back(&out, act_node));
+                igraph_dqueue_pop_back(&q);
+                num_seen++;
+
+                if (num_seen % 10000 == 0) {
+                    /* time to report progress and allow the user to interrupt */
+                    IGRAPH_PROGRESS("Strongly connected components: ",
+                                    num_seen * 50.0 / no_of_nodes, NULL);
+                    IGRAPH_ALLOW_INTERRUPTION();
+                }
+            }
+        } /* while q */
+    }  /* for */
+
+    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);
+
+    igraph_adjlist_destroy(&adjlist);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    /* OK, we've the 'out' values for the nodes, let's use them in
+       decreasing order with the help of a heap */
+
+    igraph_vector_null(&next_nei);             /* mark already added vertices */
+    num_seen = 0;
+
+    while (!igraph_vector_empty(&out)) {
+        long int grandfather = (long int) igraph_vector_pop_back(&out);
+
+        if (VECTOR(next_nei)[grandfather] != 0) {
+            continue;
+        }
+        VECTOR(next_nei)[grandfather] = 1;
+        act_cluster_size = 1;
+        if (membership) {
+            VECTOR(*membership)[grandfather] = no_of_clusters - 1;
+        }
+        IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather));
+
+        num_seen++;
+        if (num_seen % 10000 == 0) {
+            /* time to report progress and allow the user to interrupt */
+            IGRAPH_PROGRESS("Strongly connected components: ",
+                            50.0 + num_seen * 50.0 / no_of_nodes, NULL);
+            IGRAPH_ALLOW_INTERRUPTION();
+        }
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int act_node = (long int) igraph_dqueue_pop_back(&q);
+            tmp = igraph_adjlist_get(&adjlist, act_node);
+            n = igraph_vector_int_size(tmp);
+            for (i = 0; i < n; i++) {
+                long int neighbor = (long int) VECTOR(*tmp)[i];
+                if (VECTOR(next_nei)[neighbor] != 0) {
+                    continue;
+                }
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                VECTOR(next_nei)[neighbor] = 1;
+                act_cluster_size++;
+                if (membership) {
+                    VECTOR(*membership)[neighbor] = no_of_clusters - 1;
+                }
+
+                num_seen++;
+                if (num_seen % 10000 == 0) {
+                    /* time to report progress and allow the user to interrupt */
+                    IGRAPH_PROGRESS("Strongly connected components: ",
+                                    50.0 + num_seen * 50.0 / no_of_nodes, NULL);
+                    IGRAPH_ALLOW_INTERRUPTION();
+                }
+            }
+        }
+
+        no_of_clusters++;
+        if (csize) {
+            IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size));
+        }
+    }
+
+    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);
+
+    if (no) {
+        *no = (igraph_integer_t) no_of_clusters - 1;
+    }
+
+    /* Clean up, return */
+
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_destroy(&out);
+    igraph_dqueue_destroy(&q);
+    igraph_vector_destroy(&next_nei);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res);
+
+/**
+ * \ingroup structural
+ * \function igraph_is_connected
+ * \brief Decides whether the graph is (weakly or strongly) connected.
+ *
+ * A graph with zero vertices (i.e. the null graph) is connected by definition.
+ *
+ * \param graph The graph object to analyze.
+ * \param res Pointer to a logical variable, the result will be stored
+ *        here.
+ * \param mode For a directed graph this specifies whether to calculate
+ *        weak or strong connectedness. Possible values:
+ *        \c IGRAPH_WEAK,
+ *        \c IGRAPH_STRONG. This argument is
+ *        ignored for undirected graphs.
+ * \return Error code:
+ *        \c IGRAPH_EINVAL: invalid mode argument.
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices
+ * plus the number of edges in the graph.
+ */
+
+int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res,
+                        igraph_connectedness_t mode) {
+    if (igraph_vcount(graph) == 0) {
+        *res = 1;
+        return IGRAPH_SUCCESS;
+    }
+
+    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
+        return igraph_is_connected_weak(graph, res);
+    } else if (mode == IGRAPH_STRONG) {
+        int retval;
+        igraph_integer_t no;
+        retval = igraph_i_clusters_strong(graph, 0, 0, &no);
+        *res = (no == 1);
+        return retval;
+    } else {
+        IGRAPH_ERROR("mode argument", IGRAPH_EINVAL);
+    }
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_is_connected_weak
+ * \brief Query whether the graph is weakly connected.
+ *
+ * A graph with zero vertices (i.e. the null graph) is weakly connected by
+ * definition. A directed graph is weakly connected if its undirected version
+ * is connected. In the case of undirected graphs, weakly connected and
+ * connected are equivalent.
+ *
+ * \param graph The graph object to analyze.
+ * \param res Pointer to a logical variable; the result will be stored here.
+ * \return Error code:
+ *        \c IGRAPH_ENOMEM: unable to allocate requested memory.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number of
+ * edges in the graph.
+ */
+
+int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    char *already_added;
+    igraph_vector_t neis = IGRAPH_VECTOR_NULL;
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+
+    long int i, j;
+
+    if (no_of_nodes == 0) {
+        *res = 1;
+        return IGRAPH_SUCCESS;
+    }
+
+    already_added = igraph_Calloc(no_of_nodes, char);
+    if (already_added == 0) {
+        IGRAPH_ERROR("is connected (weak) failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, already_added); /* TODO: hack */
+
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 10);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    /* Try to find at least two clusters */
+    already_added[0] = 1;
+    IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
+
+    j = 1;
+    while ( !igraph_dqueue_empty(&q)) {
+        long int actnode = (long int) igraph_dqueue_pop(&q);
+        IGRAPH_ALLOW_INTERRUPTION();
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode,
+                                      IGRAPH_ALL));
+        for (i = 0; i < igraph_vector_size(&neis); i++) {
+            long int neighbor = (long int) VECTOR(neis)[i];
+            if (already_added[neighbor] != 0) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+            j++;
+            already_added[neighbor]++;
+        }
+    }
+
+    /* Connected? */
+    *res = (j == no_of_nodes);
+
+    igraph_Free(already_added);
+    igraph_dqueue_destroy(&q);
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_decompose_destroy
+ * \brief Free the memory allocated by \ref igraph_decompose().
+ *
+ * \param complist The list of graph components, as returned by
+ *        \ref igraph_decompose().
+ *
+ * Time complexity: O(c), c is the number of components.
+ */
+
+void igraph_decompose_destroy(igraph_vector_ptr_t *complist) {
+    long int i;
+    for (i = 0; i < igraph_vector_ptr_size(complist); i++) {
+        if (VECTOR(*complist)[i] != 0) {
+            igraph_destroy(VECTOR(*complist)[i]);
+            igraph_free(VECTOR(*complist)[i]);
+        }
+    }
+}
+
+static int igraph_i_decompose_weak(const igraph_t *graph,
+                                   igraph_vector_ptr_t *components,
+                                   long int maxcompno, long int minelements);
+
+static int igraph_i_decompose_strong(const igraph_t *graph,
+                                     igraph_vector_ptr_t *components,
+                                     long int maxcompno, long int minelements);
+
+/**
+ * \function igraph_decompose
+ * \brief Decompose a graph into connected components.
+ *
+ * Create separate graph for each component of a graph. Note that the
+ * vertex ids in the new graphs will be different than in the original
+ * graph. (Except if there is only one component in the original graph.)
+ *
+ * \param graph The original graph.
+ * \param components This pointer vector will contain pointers to the
+ *   subcomponent graphs. It should be initialized before calling this
+ *   function and will be resized to hold the graphs. Don't forget to
+ *   call \ref igraph_destroy() and free() on the elements of
+ *   this pointer vector to free unneeded memory. Alternatively, you can
+ *   simply call \ref igraph_decompose_destroy() that does this for you.
+ * \param mode Either \c IGRAPH_WEAK or \c IGRAPH_STRONG for weakly
+ *    and strongly connected components respectively.
+ * \param maxcompno The maximum number of components to return. The
+ *    first \p maxcompno components will be returned (which hold at
+ *    least \p minelements vertices, see the next parameter), the
+ *    others will be ignored. Supply -1 here if you don't want to limit
+ *    the number of components.
+ * \param minelements The minimum number of vertices a component
+ *    should contain in order to place it in the \p components
+ *    vector. Eg. supply 2 here to ignore isolated vertices.
+ * \return Error code, \c IGRAPH_ENOMEM if there is not enough memory
+ *   to perform the operation.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges.
+ *
+ * \example examples/simple/igraph_decompose.c
+ */
+
+int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components,
+                     igraph_connectedness_t mode,
+                     long int maxcompno, long int minelements) {
+    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
+        return igraph_i_decompose_weak(graph, components, maxcompno, minelements);
+    } else if (mode == IGRAPH_STRONG) {
+        return igraph_i_decompose_strong(graph, components, maxcompno, minelements);
+    } else {
+        IGRAPH_ERROR("Cannot decompose graph", IGRAPH_EINVAL);
+    }
+
+    return 1;
+}
+
+static int igraph_i_decompose_weak(const igraph_t *graph,
+                                   igraph_vector_ptr_t *components,
+                                   long int maxcompno, long int minelements) {
+
+    long int actstart;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int resco = 0;   /* number of graphs created so far */
+    char *already_added;
+    igraph_dqueue_t q;
+    igraph_vector_t verts;
+    igraph_vector_t neis;
+    long int i;
+    igraph_t *newg;
+
+
+    if (maxcompno < 0) {
+        maxcompno = LONG_MAX;
+    }
+
+    igraph_vector_ptr_clear(components);
+    IGRAPH_FINALLY(igraph_decompose_destroy, components);
+
+    /* already_added keeps track of what nodes made it into a graph already */
+    already_added = igraph_Calloc(no_of_nodes, char);
+    if (already_added == 0) {
+        IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, already_added);
+
+    IGRAPH_CHECK(igraph_dqueue_init(&q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &q);
+    IGRAPH_VECTOR_INIT_FINALLY(&verts, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    /* add a node and its neighbors at once, recursively
+       then switch to next node that has not been added already */
+    for (actstart = 0; resco < maxcompno && actstart < no_of_nodes; actstart++) {
+
+        if (already_added[actstart]) {
+            continue;
+        }
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        igraph_vector_clear(&verts);
+
+        /* add the node itself */
+        already_added[actstart] = 1;
+        IGRAPH_CHECK(igraph_vector_push_back(&verts, actstart));
+        IGRAPH_CHECK(igraph_dqueue_push(&q, actstart));
+
+        /* add the neighbors, recursively */
+        while (!igraph_dqueue_empty(&q) ) {
+            /* pop from the queue of this component */
+            long int actvert = (long int) igraph_dqueue_pop(&q);
+            IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actvert,
+                                          IGRAPH_ALL));
+            /* iterate over the neighbors */
+            for (i = 0; i < igraph_vector_size(&neis); i++) {
+                long int neighbor = (long int) VECTOR(neis)[i];
+                if (already_added[neighbor] == 1) {
+                    continue;
+                }
+                /* add neighbor */
+                already_added[neighbor] = 1;
+
+                /* recursion: append neighbor to the queues */
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor));
+            }
+        }
+
+        /* ok, we have a component */
+        if (igraph_vector_size(&verts) < minelements) {
+            continue;
+        }
+
+        newg = igraph_Calloc(1, igraph_t);
+        if (newg == 0) {
+            IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(components, newg));
+        IGRAPH_CHECK(igraph_induced_subgraph(graph, newg,
+                                             igraph_vss_vector(&verts),
+                                             IGRAPH_SUBGRAPH_AUTO));
+        resco++;
+
+    } /* for actstart++ */
+
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&verts);
+    igraph_dqueue_destroy(&q);
+    igraph_free(already_added);
+    IGRAPH_FINALLY_CLEAN(5);  /* + components */
+
+    return 0;
+}
+
+static int igraph_i_decompose_strong(const igraph_t *graph,
+                                     igraph_vector_ptr_t *components,
+                                     long int maxcompno, long int minelements) {
+
+
+    long int no_of_nodes = igraph_vcount(graph);
+
+    /* this is a heap used twice for checking what nodes have
+     * been counted already */
+    igraph_vector_t next_nei = IGRAPH_VECTOR_NULL;
+
+    long int i, n, num_seen;
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+
+    long int no_of_clusters = 1;
+    long int act_cluster_size;
+
+    igraph_vector_t out = IGRAPH_VECTOR_NULL;
+    const igraph_vector_int_t* tmp;
+
+    igraph_adjlist_t adjlist;
+    igraph_vector_t verts;
+    igraph_t *newg;
+
+    igraph_vector_ptr_clear(components);
+    IGRAPH_FINALLY(igraph_decompose_destroy, components);
+
+    /* The result */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&verts, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&next_nei, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&out, 0);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes));
+
+    igraph_vector_null(&out);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    /* number of components seen */
+    num_seen = 0;
+    /* populate the 'out' vector by browsing a node and following up
+       all its neighbors recursively, then switching to the next
+       unassigned node */
+    for (i = 0; i < no_of_nodes; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /* get all the 'out' neighbors of this node
+         * NOTE: next_nei is initialized [0, 0, ...] */
+        tmp = igraph_adjlist_get(&adjlist, i);
+        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
+            continue;
+        }
+
+        /* add this node to the queue for this component */
+        IGRAPH_CHECK(igraph_dqueue_push(&q, i));
+
+        /* consume the tree from this node ("root") recursively
+         * until there is no more */
+        while (!igraph_dqueue_empty(&q)) {
+            /* this looks up but does NOT consume the queue */
+            long int act_node = (long int) igraph_dqueue_back(&q);
+
+            /* get all neighbors of this node */
+            tmp = igraph_adjlist_get(&adjlist, act_node);
+            if (VECTOR(next_nei)[act_node] == 0) {
+                /* this is the first time we've met this vertex,
+                     * because next_nei is initialized [0, 0, ...] */
+                VECTOR(next_nei)[act_node]++;
+                /* back to the queue, same vertex is up again */
+
+            } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) {
+                /* we've already met this vertex but it has more children */
+                long int neighbor = (long int) VECTOR(*tmp)[(long int)
+                                    VECTOR(next_nei)[act_node] - 1];
+                if (VECTOR(next_nei)[neighbor] == 0) {
+                    /* add the root of the other children to the queue */
+                    IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                }
+                VECTOR(next_nei)[act_node]++;
+            } else {
+                /* we've met this vertex and it has no more children */
+                IGRAPH_CHECK(igraph_vector_push_back(&out, act_node));
+                /* this consumes the queue, since there's nowhere to go */
+                igraph_dqueue_pop_back(&q);
+                num_seen++;
+
+                if (num_seen % 10000 == 0) {
+                    /* time to report progress and allow the user to interrupt */
+                    IGRAPH_PROGRESS("Strongly connected components: ",
+                                    num_seen * 50.0 / no_of_nodes, NULL);
+                    IGRAPH_ALLOW_INTERRUPTION();
+                }
+            }
+        } /* while q */
+    }  /* for */
+
+    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);
+
+    igraph_adjlist_destroy(&adjlist);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    /* OK, we've the 'out' values for the nodes, let's use them in
+     * decreasing order with the help of the next_nei heap */
+
+    igraph_vector_null(&next_nei);             /* mark already added vertices */
+
+    /* number of components built */
+    num_seen = 0;
+    while (!igraph_vector_empty(&out)) {
+        /* consume the vector from the last element */
+        long int grandfather = (long int) igraph_vector_pop_back(&out);
+
+        /* been here, done that
+         * NOTE: next_nei is initialized as [0, 0, ...] */
+        if (VECTOR(next_nei)[grandfather] != 0) {
+            continue;
+        }
+
+        /* collect all the members of this component */
+        igraph_vector_clear(&verts);
+
+        /* this node is gone for any future components */
+        VECTOR(next_nei)[grandfather] = 1;
+        act_cluster_size = 1;
+
+        /* add to component */
+        IGRAPH_CHECK(igraph_vector_push_back(&verts, grandfather));
+        IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather));
+
+        num_seen++;
+        if (num_seen % 10000 == 0) {
+            /* time to report progress and allow the user to interrupt */
+            IGRAPH_PROGRESS("Strongly connected components: ",
+                            50.0 + num_seen * 50.0 / no_of_nodes, NULL);
+            IGRAPH_ALLOW_INTERRUPTION();
+        }
+
+        while (!igraph_dqueue_empty(&q)) {
+            /* consume the queue from this node */
+            long int act_node = (long int) igraph_dqueue_pop_back(&q);
+            tmp = igraph_adjlist_get(&adjlist, act_node);
+            n = igraph_vector_int_size(tmp);
+            for (i = 0; i < n; i++) {
+                long int neighbor = (long int) VECTOR(*tmp)[i];
+                if (VECTOR(next_nei)[neighbor] != 0) {
+                    continue;
+                }
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                VECTOR(next_nei)[neighbor] = 1;
+                act_cluster_size++;
+
+                /* add to component */
+                IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor));
+
+                num_seen++;
+                if (num_seen % 10000 == 0) {
+                    /* time to report progress and allow the user to interrupt */
+                    IGRAPH_PROGRESS("Strongly connected components: ",
+                                    50.0 + num_seen * 50.0 / no_of_nodes, NULL);
+                    IGRAPH_ALLOW_INTERRUPTION();
+                }
+            }
+        }
+
+        /* ok, we have a component */
+        if (igraph_vector_size(&verts) < minelements) {
+            continue;
+        }
+
+        newg = igraph_Calloc(1, igraph_t);
+        if (newg == 0) {
+            IGRAPH_ERROR("Cannot decompose graph", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(components, newg));
+        IGRAPH_CHECK(igraph_induced_subgraph(graph, newg,
+                                             igraph_vss_vector(&verts),
+                                             IGRAPH_SUBGRAPH_AUTO));
+
+        no_of_clusters++;
+    }
+
+    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);
+
+    /* Clean up, return */
+
+    igraph_vector_destroy(&verts);
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_destroy(&out);
+    igraph_dqueue_destroy(&q);
+    igraph_vector_destroy(&next_nei);
+    IGRAPH_FINALLY_CLEAN(6);  /* + components */
+
+    return 0;
+
+}
+
+/**
+ * \function igraph_articulation_points
+ * Find the articulation points in a graph.
+ *
+ * A vertex is an articulation point if its removal increases
+ * the number of connected components in the graph.
+ * \param graph The input graph.
+ * \param res Pointer to an initialized vector, the
+ *    articulation points will be stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and edges.
+ *
+ * \sa \ref igraph_biconnected_components(), \ref igraph_clusters(), \ref igraph_bridges()
+ */
+
+int igraph_articulation_points(const igraph_t *graph,
+                               igraph_vector_t *res) {
+
+    igraph_integer_t no;
+    return igraph_biconnected_components(graph, &no, 0, 0, 0, res);
+}
+
+void igraph_i_free_vectorlist(igraph_vector_ptr_t *list);
+
+void igraph_i_free_vectorlist(igraph_vector_ptr_t *list) {
+    long int i, n = igraph_vector_ptr_size(list);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *v = VECTOR(*list)[i];
+        if (v) {
+            igraph_vector_destroy(v);
+            igraph_Free(v);
+        }
+    }
+    igraph_vector_ptr_destroy(list);
+}
+
+/**
+ * \function igraph_biconnected_components
+ * Calculate biconnected components
+ *
+ * A graph is biconnected if the removal of any single vertex (and
+ * its incident edges) does not disconnect it.
+ *
+ * </para><para>
+ * A biconnected component of a graph is a maximal biconnected
+ * subgraph of it. The biconnected components of a graph can be given
+ * by the partition of its edges: every edge is a member of exactly
+ * one biconnected component. Note that this is not true for
+ * vertices: the same vertex can be part of many biconnected
+ * components.
+ *
+ * </para><para>
+ * Somewhat arbitrarily, igraph does not consider comppnents containing
+ * a single vertex only as being biconnected. Isolated vertices will
+ * not be part of any of the biconnected components.
+ *
+ * \param graph The input graph.
+ * \param no The number of biconnected components will be stored here.
+ * \param tree_edges If not a NULL pointer, then the found components
+ *     are stored here, in a list of vectors. Every vector in the list
+ *     is a biconnected component, represented by its edges. More precisely,
+ *     a spanning tree of the biconnected component is returned.
+ *     Note you'll have to
+ *     destroy each vector first by calling \ref igraph_vector_destroy()
+ *     and then <code>free()</code> on it, plus you need to call
+ *     \ref igraph_vector_ptr_destroy() on the list to regain all
+ *     allocated memory.
+ * \param component_edges If not a NULL pointer, then the edges of the
+ *     biconnected components are stored here, in the same form as for
+ *     \c tree_edges.
+ * \param components If not a NULL pointer, then the vertices of the
+ *     biconnected components are stored here, in the same format as
+ *     for the previous two arguments.
+ * \param articulation_points If not a NULL pointer, then the
+ *     articulation points of the graph are stored in this vector.
+ *     A vertex is an articulation point if its removal increases the
+ *     number of (weakly) connected components in the graph.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges, but only if you do not calculate \c components and
+ * \c component_edges. If you calculate \c components, then it is
+ * quadratic in the number of vertices. If you calculate \c
+ * component_edges as well, then it is cubic in the number of
+ * vertices.
+ *
+ * \sa \ref igraph_articulation_points(), \ref igraph_clusters().
+ *
+ * \example examples/simple/igraph_biconnected_components.c
+ */
+
+int igraph_biconnected_components(const igraph_t *graph,
+                                  igraph_integer_t *no,
+                                  igraph_vector_ptr_t *tree_edges,
+                                  igraph_vector_ptr_t *component_edges,
+                                  igraph_vector_ptr_t *components,
+                                  igraph_vector_t *articulation_points) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_long_t nextptr;
+    igraph_vector_long_t num, low;
+    igraph_vector_bool_t found;
+    igraph_vector_int_t *adjedges;
+    igraph_stack_t path;
+    igraph_vector_t edgestack;
+    igraph_inclist_t inclist;
+    long int i, counter, rootdfs = 0;
+    igraph_vector_long_t vertex_added;
+    long int comps = 0;
+    igraph_vector_ptr_t *mycomponents = components, vcomponents;
+
+    IGRAPH_CHECK(igraph_vector_long_init(&nextptr, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &nextptr);
+    IGRAPH_CHECK(igraph_vector_long_init(&num, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &num);
+    IGRAPH_CHECK(igraph_vector_long_init(&low, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &low);
+    IGRAPH_CHECK(igraph_vector_bool_init(&found, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &found);
+
+    IGRAPH_CHECK(igraph_stack_init(&path, 100));
+    IGRAPH_FINALLY(igraph_stack_destroy, &path);
+    IGRAPH_VECTOR_INIT_FINALLY(&edgestack, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edgestack, 100));
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+
+    IGRAPH_CHECK(igraph_vector_long_init(&vertex_added, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &vertex_added);
+
+    if (no) {
+        *no = 0;
+    }
+    if (tree_edges) {
+        igraph_vector_ptr_clear(tree_edges);
+    }
+    if (components) {
+        igraph_vector_ptr_clear(components);
+    }
+    if (component_edges) {
+        igraph_vector_ptr_clear(component_edges);
+    }
+    if (articulation_points) {
+        igraph_vector_clear(articulation_points);
+    }
+    if (component_edges && !components) {
+        mycomponents = &vcomponents;
+        IGRAPH_CHECK(igraph_vector_ptr_init(mycomponents, 0));
+        IGRAPH_FINALLY(igraph_i_free_vectorlist, mycomponents);
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+
+        if (VECTOR(low)[i] != 0) {
+            continue;    /* already visited */
+        }
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        IGRAPH_CHECK(igraph_stack_push(&path, i));
+        counter = 1;
+        rootdfs = 0;
+        VECTOR(low)[i] = VECTOR(num)[i] = counter++;
+        while (!igraph_stack_empty(&path)) {
+            long int n;
+            long int act = (long int) igraph_stack_top(&path);
+            long int actnext = VECTOR(nextptr)[act];
+
+            adjedges = igraph_inclist_get(&inclist, act);
+            n = igraph_vector_int_size(adjedges);
+            if (actnext < n) {
+                /* Step down (maybe) */
+                long int edge = (long int) VECTOR(*adjedges)[actnext];
+                long int nei = IGRAPH_OTHER(graph, edge, act);
+                if (VECTOR(low)[nei] == 0) {
+                    if (act == i) {
+                        rootdfs++;
+                    }
+                    IGRAPH_CHECK(igraph_vector_push_back(&edgestack, edge));
+                    IGRAPH_CHECK(igraph_stack_push(&path, nei));
+                    VECTOR(low)[nei] = VECTOR(num)[nei] = counter++;
+                } else {
+                    /* Update low value if needed */
+                    if (VECTOR(num)[nei] < VECTOR(low)[act]) {
+                        VECTOR(low)[act] = VECTOR(num)[nei];
+                    }
+                }
+                VECTOR(nextptr)[act] += 1;
+            } else {
+                /* Step up */
+                igraph_stack_pop(&path);
+                if (!igraph_stack_empty(&path)) {
+                    long int prev = (long int) igraph_stack_top(&path);
+                    /* Update LOW value if needed */
+                    if (VECTOR(low)[act] < VECTOR(low)[prev]) {
+                        VECTOR(low)[prev] = VECTOR(low)[act];
+                    }
+                    /* Check for articulation point */
+                    if (VECTOR(low)[act] >= VECTOR(num)[prev]) {
+                        if (articulation_points && !VECTOR(found)[prev]
+                            && prev != i /* the root */) {
+                            IGRAPH_CHECK(igraph_vector_push_back(articulation_points, prev));
+                            VECTOR(found)[prev] = 1;
+                        }
+                        if (no) {
+                            *no += 1;
+                        }
+
+                        /*------------------------------------*/
+                        /* Record the biconnected component just found */
+                        if (tree_edges || mycomponents) {
+                            igraph_vector_t *v = 0, *v2 = 0;
+                            comps++;
+                            if (tree_edges) {
+                                v = igraph_Calloc(1, igraph_vector_t);
+                                if (!v) {
+                                    IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
+                                }
+                                IGRAPH_CHECK(igraph_vector_init(v, 0));
+                                IGRAPH_FINALLY(igraph_vector_destroy, v);
+                            }
+                            if (mycomponents) {
+                                v2 = igraph_Calloc(1, igraph_vector_t);
+                                if (!v2) {
+                                    IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
+                                }
+                                IGRAPH_CHECK(igraph_vector_init(v2, 0));
+                                IGRAPH_FINALLY(igraph_vector_destroy, v2);
+                            }
+
+                            while (!igraph_vector_empty(&edgestack)) {
+                                long int e = (long int) igraph_vector_pop_back(&edgestack);
+                                long int from = IGRAPH_FROM(graph, e);
+                                long int to = IGRAPH_TO(graph, e);
+                                if (tree_edges) {
+                                    IGRAPH_CHECK(igraph_vector_push_back(v, e));
+                                }
+                                if (mycomponents) {
+                                    if (VECTOR(vertex_added)[from] != comps) {
+                                        VECTOR(vertex_added)[from] = comps;
+                                        IGRAPH_CHECK(igraph_vector_push_back(v2, from));
+                                    }
+                                    if (VECTOR(vertex_added)[to] != comps) {
+                                        VECTOR(vertex_added)[to] = comps;
+                                        IGRAPH_CHECK(igraph_vector_push_back(v2, to));
+                                    }
+                                }
+                                if (from == prev || to == prev) {
+                                    break;
+                                }
+                            }
+
+                            if (mycomponents) {
+                                IGRAPH_CHECK(igraph_vector_ptr_push_back(mycomponents, v2));
+                                IGRAPH_FINALLY_CLEAN(1);
+                            }
+                            if (tree_edges) {
+                                IGRAPH_CHECK(igraph_vector_ptr_push_back(tree_edges, v));
+                                IGRAPH_FINALLY_CLEAN(1);
+                            }
+                            if (component_edges) {
+                                igraph_vector_t *nodes = VECTOR(*mycomponents)[comps - 1];
+                                igraph_vector_t *vv = igraph_Calloc(1, igraph_vector_t);
+                                long int ii, no_vert = igraph_vector_size(nodes);
+                                if (!vv) {
+                                    IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
+                                }
+                                IGRAPH_CHECK(igraph_vector_init(vv, 0));
+                                IGRAPH_FINALLY(igraph_vector_destroy, vv);
+                                for (ii = 0; ii < no_vert; ii++) {
+                                    long int vert = (long int) VECTOR(*nodes)[ii];
+                                    igraph_vector_int_t *edges = igraph_inclist_get(&inclist,
+                                                                 vert);
+                                    long int j, nn = igraph_vector_int_size(edges);
+                                    for (j = 0; j < nn; j++) {
+                                        long int e = (long int) VECTOR(*edges)[j];
+                                        long int nei = IGRAPH_OTHER(graph, e, vert);
+                                        if (VECTOR(vertex_added)[nei] == comps && nei < vert) {
+                                            IGRAPH_CHECK(igraph_vector_push_back(vv, e));
+                                        }
+                                    }
+                                }
+                                IGRAPH_CHECK(igraph_vector_ptr_push_back(component_edges, vv));
+                                IGRAPH_FINALLY_CLEAN(1);
+                            }
+                        } /* record component if requested */
+                        /*------------------------------------*/
+
+                    }
+                } /* !igraph_stack_empty(&path) */
+            }
+
+        } /* !igraph_stack_empty(&path) */
+
+        if (articulation_points && rootdfs >= 2) {
+            IGRAPH_CHECK(igraph_vector_push_back(articulation_points, i));
+        }
+
+    } /* i < no_of_nodes */
+
+    if (mycomponents != components) {
+        igraph_i_free_vectorlist(mycomponents);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_long_destroy(&vertex_added);
+    igraph_inclist_destroy(&inclist);
+    igraph_vector_destroy(&edgestack);
+    igraph_stack_destroy(&path);
+    igraph_vector_bool_destroy(&found);
+    igraph_vector_long_destroy(&low);
+    igraph_vector_long_destroy(&num);
+    igraph_vector_long_destroy(&nextptr);
+    IGRAPH_FINALLY_CLEAN(8);
+
+    return 0;
+}
+
+
+/* igraph_bridges -- find all bridges in the graph */
+/* based on https://www.geeksforgeeks.org/bridge-in-a-graph/ */
+
+static int igraph_i_bridges_rec(const igraph_t *graph, const igraph_inclist_t *il, igraph_integer_t u, igraph_integer_t *time, igraph_vector_t *bridges, igraph_vector_bool_t *visited, igraph_vector_int_t *disc, igraph_vector_int_t *low, igraph_vector_int_t *parent) {
+    igraph_vector_int_t *incedges;
+    long nc; /* neighbour count */
+    long i;
+
+    VECTOR(*visited)[u] = 1;
+
+    *time += 1;
+
+    VECTOR(*disc)[u] = *time;
+    VECTOR(*low)[u] = *time;
+
+    incedges = igraph_inclist_get(il, u);
+    nc = igraph_vector_int_size(incedges);
+    for (i = 0; i < nc; ++i) {
+        long edge = (long) VECTOR(*incedges)[i];
+        igraph_integer_t v = IGRAPH_TO(graph, edge) == u ? IGRAPH_FROM(graph, edge) : IGRAPH_TO(graph, edge);
+
+        if (! VECTOR(*visited)[v]) {
+            VECTOR(*parent)[v] = u;
+            IGRAPH_CHECK(igraph_i_bridges_rec(graph, il, v, time, bridges, visited, disc, low, parent));
+
+            VECTOR(*low)[u] = VECTOR(*low)[u] < VECTOR(*low)[v] ? VECTOR(*low)[u] : VECTOR(*low)[v];
+
+            if (VECTOR(*low)[v] > VECTOR(*disc)[u]) {
+                IGRAPH_CHECK(igraph_vector_push_back(bridges, edge));
+            }
+        } else if (v != VECTOR(*parent)[u]) {
+            VECTOR(*low)[u] = VECTOR(*low)[u] < VECTOR(*disc)[v] ? VECTOR(*low)[u] : VECTOR(*disc)[v];
+        }
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_bridges
+ * Find all bridges in a graph.
+ *
+ * An edge is a bridge if its removal increases the number of (weakly)
+ * connected components in the graph.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to an initialized vector, the
+ *    bridges will be stored here as edge indices.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and edges.
+ *
+ * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components(), \ref igraph_clusters()
+ */
+
+int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) {
+    igraph_inclist_t il;
+    igraph_vector_bool_t visited;
+    igraph_vector_int_t disc, low;
+    igraph_vector_int_t parent;
+    long n;
+    long i;
+    igraph_integer_t time;
+
+    n = igraph_vcount(graph);
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &il);
+
+    IGRAPH_CHECK(igraph_vector_bool_init(&visited, n));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&disc, n));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &disc);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&low, n));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &low);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&parent, n));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &parent);
+    for (i = 0; i < n; ++i) {
+        VECTOR(parent)[i] = -1;
+    }
+
+    igraph_vector_clear(bridges);
+
+    time = 0;
+    for (i = 0; i < n; ++i)
+        if (! VECTOR(visited)[i]) {
+            IGRAPH_CHECK(igraph_i_bridges_rec(graph, &il, i, &time, bridges, &visited, &disc, &low, &parent));
+        }
+
+    igraph_vector_int_destroy(&parent);
+    igraph_vector_int_destroy(&low);
+    igraph_vector_int_destroy(&disc);
+    igraph_vector_bool_destroy(&visited);
+    igraph_inclist_destroy(&il);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/conversion.c b/igraph/src/conversion.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/conversion.c
@@ -0,0 +1,953 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_conversion.h"
+#include "igraph_iterators.h"
+#include "igraph_interface.h"
+#include "igraph_attributes.h"
+#include "igraph_constructors.h"
+#include "igraph_structural.h"
+#include "igraph_types_internal.h"
+#include "igraph_sparsemat.h"
+#include "config.h"
+
+/**
+ * \ingroup conversion
+ * \function igraph_get_adjacency
+ * \brief Returns the adjacency matrix of a graph
+ *
+ * </para><para>
+ * The result is an incidence matrix, it contains numbers greater
+ * than one if there are multiple edges in the graph.
+ * \param graph Pointer to the graph to convert
+ * \param res Pointer to an initialized matrix object, it will be
+ *        resized if needed.
+ * \param type Constant giving the type of the adjacency matrix to
+ *        create for undirected graphs. It is ignored for directed
+ *        graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_GET_ADJACENCY_UPPER
+ *          the upper right triangle of the matrix is used.
+ *        \cli IGRAPH_GET_ADJACENCY_LOWER
+ *          the lower left triangle of the matrix is used.
+ *        \cli IGRAPH_GET_ADJACENCY_BOTH
+ *          the whole matrix is used, a symmetric matrix is returned.
+ *        \endclist
+ * \param type eids Logical, if true, then the edges ids plus one
+ *        are stored in the adjacency matrix, instead of the number of
+ *        edges between the two vertices. (The plus one is needed, since
+ *        edge ids start from zero, and zero means no edge in this case.)
+ * \return Error code:
+ *        \c IGRAPH_EINVAL invalid type argument.
+ *
+ * \sa igraph_get_adjacency_sparse if you want a sparse matrix representation
+ *
+ * Time complexity: O(|V||V|),
+ * |V| is the
+ * number of vertices in the graph.
+ */
+
+int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res,
+                         igraph_get_adjacency_t type, igraph_bool_t eids) {
+
+    igraph_eit_t edgeit;
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+    int retval = 0;
+    long int from, to;
+    igraph_integer_t ffrom, fto;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes));
+    igraph_matrix_null(res);
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &edgeit);
+
+    if (directed) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            long int edge = IGRAPH_EIT_GET(edgeit);
+            igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto);
+            from = ffrom;
+            to = fto;
+            if (eids) {
+                MATRIX(*res, from, to) = edge + 1;
+            } else {
+                MATRIX(*res, from, to) += 1;
+            }
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else if (type == IGRAPH_GET_ADJACENCY_UPPER) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            long int edge = IGRAPH_EIT_GET(edgeit);
+            igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto);
+            from = ffrom;
+            to = fto;
+            if (to < from) {
+                if (eids) {
+                    MATRIX(*res, to, from) = edge + 1;
+                } else {
+                    MATRIX(*res, to, from) += 1;
+                }
+            } else {
+                if (eids) {
+                    MATRIX(*res, from, to) = edge + 1;
+                } else {
+                    MATRIX(*res, from, to) += 1;
+                }
+            }
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else if (type == IGRAPH_GET_ADJACENCY_LOWER) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            long int edge = IGRAPH_EIT_GET(edgeit);
+            igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto);
+            from = ffrom;
+            to = fto;
+            if (to < from) {
+                if (eids) {
+                    MATRIX(*res, from, to) = edge + 1;
+                } else {
+                    MATRIX(*res, from, to) += 1;
+                }
+            } else {
+                if (eids) {
+                    MATRIX(*res, to, from) = edge + 1;
+                } else {
+                    MATRIX(*res, to, from) += 1;
+                }
+            }
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else if (type == IGRAPH_GET_ADJACENCY_BOTH) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            long int edge = IGRAPH_EIT_GET(edgeit);
+            igraph_edge(graph, (igraph_integer_t) edge, &ffrom, &fto);
+            from = ffrom;
+            to = fto;
+            if (eids) {
+                MATRIX(*res, from, to) = edge + 1;
+            } else {
+                MATRIX(*res, from, to) += 1;
+            }
+            if (from != to) {
+                if (eids) {
+                    MATRIX(*res, to, from) = edge + 1;
+                } else {
+                    MATRIX(*res, to, from) += 1;
+                }
+            }
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else {
+        IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL);
+    }
+
+    igraph_eit_destroy(&edgeit);
+    IGRAPH_FINALLY_CLEAN(1);
+    return retval;
+}
+
+/**
+ * \ingroup conversion
+ * \function igraph_get_adjacency_sparse
+ * \brief Returns the adjacency matrix of a graph in sparse matrix format
+ *
+ * </para><para>
+ * The result is an incidence matrix, it contains numbers greater
+ * than one if there are multiple edges in the graph.
+ * \param graph Pointer to the graph to convert
+ * \param res Pointer to an initialized sparse matrix object, it will be
+ *        resized if needed.
+ * \param type Constant giving the type of the adjacency matrix to
+ *        create for undirected graphs. It is ignored for directed
+ *        graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_GET_ADJACENCY_UPPER
+ *          the upper right triangle of the matrix is used.
+ *        \cli IGRAPH_GET_ADJACENCY_LOWER
+ *          the lower left triangle of the matrix is used.
+ *        \cli IGRAPH_GET_ADJACENCY_BOTH
+ *          the whole matrix is used, a symmetric matrix is returned.
+ *        \endclist
+ * \return Error code:
+ *        \c IGRAPH_EINVAL invalid type argument.
+ *
+ * \sa igraph_get_adjacency if you would like to get a normal matrix
+ *   ( \type igraph_matrix_t )
+ *
+ * Time complexity: O(|V||V|),
+ * |V| is the
+ * number of vertices in the graph.
+ */
+
+int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res,
+                                igraph_get_adjacency_t type) {
+
+    igraph_eit_t edgeit;
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+    int retval = 0;
+    long int from, to;
+    igraph_integer_t ffrom, fto;
+
+    igraph_spmatrix_null(res);
+    IGRAPH_CHECK(igraph_spmatrix_resize(res, no_of_nodes, no_of_nodes));
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &edgeit);
+
+    if (directed) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto);
+            from = ffrom;
+            to = fto;
+            igraph_spmatrix_add_e(res, from, to, 1);
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else if (type == IGRAPH_GET_ADJACENCY_UPPER) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto);
+            from = ffrom;
+            to = fto;
+            if (to < from) {
+                igraph_spmatrix_add_e(res, to, from, 1);
+            } else {
+                igraph_spmatrix_add_e(res, from, to, 1);
+            }
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else if (type == IGRAPH_GET_ADJACENCY_LOWER) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto);
+            from = ffrom;
+            to = fto;
+            if (to > from) {
+                igraph_spmatrix_add_e(res, to, from, 1);
+            } else {
+                igraph_spmatrix_add_e(res, from, to, 1);
+            }
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else if (type == IGRAPH_GET_ADJACENCY_BOTH) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto);
+            from = ffrom;
+            to = fto;
+            igraph_spmatrix_add_e(res, from, to, 1);
+            if (from != to) {
+                igraph_spmatrix_add_e(res, to, from, 1);
+            }
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else {
+        IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL);
+    }
+
+    igraph_eit_destroy(&edgeit);
+    IGRAPH_FINALLY_CLEAN(1);
+    return retval;
+}
+
+/**
+ * \ingroup conversion
+ * \function igraph_get_edgelist
+ * \brief Returns the list of edges in a graph
+ *
+ * </para><para>The order of the edges is given by the edge ids.
+ * \param graph Pointer to the graph object
+ * \param res Pointer to an initialized vector object, it will be
+ *        resized.
+ * \param bycol Logical, if true, the edges will be returned
+ *        columnwise, eg. the first edge is
+ *        <code>res[0]->res[|E|]</code>, the second is
+ *        <code>res[1]->res[|E|+1]</code>, etc.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|), the
+ * number of edges in the graph.
+ */
+
+int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol) {
+
+    igraph_eit_t edgeit;
+    long int no_of_edges = igraph_ecount(graph);
+    long int vptr = 0;
+    igraph_integer_t from, to;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, no_of_edges * 2));
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID),
+                                   &edgeit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &edgeit);
+
+    if (bycol) {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to);
+            VECTOR(*res)[vptr] = from;
+            VECTOR(*res)[vptr + no_of_edges] = to;
+            vptr++;
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    } else {
+        while (!IGRAPH_EIT_END(edgeit)) {
+            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to);
+            VECTOR(*res)[vptr++] = from;
+            VECTOR(*res)[vptr++] = to;
+            IGRAPH_EIT_NEXT(edgeit);
+        }
+    }
+
+    igraph_eit_destroy(&edgeit);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_to_directed
+ * \brief Convert an undirected graph to a directed one
+ *
+ * </para><para>
+ * If the supplied graph is directed, this function does nothing.
+ * \param graph The graph object to convert.
+ * \param mode Constant, specifies the details of how exactly the
+ *        conversion is done. Possible values: \c
+ *        IGRAPH_TO_DIRECTED_ARBITRARY: the number of edges in the
+ *        graph stays the same, an arbitrarily directed edge is
+ *        created for each undirected edge;
+ *         \c IGRAPH_TO_DIRECTED_MUTUAL: two directed edges are
+ *        created for each undirected edge, one in each direction.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges.
+ */
+
+int igraph_to_directed(igraph_t *graph,
+                       igraph_to_directed_t mode) {
+
+    if (mode != IGRAPH_TO_DIRECTED_ARBITRARY &&
+        mode != IGRAPH_TO_DIRECTED_MUTUAL) {
+        IGRAPH_ERROR("Cannot direct graph, invalid mode", IGRAPH_EINVAL);
+    }
+
+    if (igraph_is_directed(graph)) {
+        return 0;
+    }
+
+    if (mode == IGRAPH_TO_DIRECTED_ARBITRARY) {
+
+        igraph_t newgraph;
+        igraph_vector_t edges;
+        long int no_of_edges = igraph_ecount(graph);
+        long int no_of_nodes = igraph_vcount(graph);
+        long int size = no_of_edges * 2;
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, size);
+        IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
+
+        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
+                                   (igraph_integer_t) no_of_nodes,
+                                   IGRAPH_DIRECTED));
+        IGRAPH_FINALLY(igraph_destroy, &newgraph);
+        igraph_vector_destroy(&edges);
+        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
+        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1);
+        IGRAPH_FINALLY_CLEAN(2);
+        igraph_destroy(graph);
+        *graph = newgraph;
+
+    } else if (mode == IGRAPH_TO_DIRECTED_MUTUAL) {
+
+        igraph_t newgraph;
+        igraph_vector_t edges;
+        igraph_vector_t index;
+        long int no_of_edges = igraph_ecount(graph);
+        long int no_of_nodes = igraph_vcount(graph);
+        long int size = no_of_edges * 4;
+        long int i;
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, size));
+        IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
+        IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges * 4));
+        IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_edges * 2);
+        for (i = 0; i < no_of_edges; i++) {
+            VECTOR(edges)[no_of_edges * 2 + i * 2]  = VECTOR(edges)[i * 2 + 1];
+            VECTOR(edges)[no_of_edges * 2 + i * 2 + 1] = VECTOR(edges)[i * 2];
+            VECTOR(index)[i] = VECTOR(index)[no_of_edges + i] = i;
+        }
+
+        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
+                                   (igraph_integer_t) no_of_nodes,
+                                   IGRAPH_DIRECTED));
+        IGRAPH_FINALLY(igraph_destroy, &newgraph);
+        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
+        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1,/*edges=*/0);
+        IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &index));
+
+        igraph_vector_destroy(&index);
+        igraph_vector_destroy(&edges);
+        igraph_destroy(graph);
+        IGRAPH_FINALLY_CLEAN(3);
+        *graph = newgraph;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_to_undirected
+ * \brief Convert a directed graph to an undirected one.
+ *
+ * </para><para>
+ * If the supplied graph is undirected, this function does nothing.
+ * \param graph The graph object to convert.
+ * \param mode Constant, specifies the details of how exactly the
+ *        conversion is done. Possible values: \c
+ *        IGRAPH_TO_UNDIRECTED_EACH: the number of edges remains
+ *        constant, an undirected edge is created for each directed
+ *        one, this version might create graphs with multiple edges;
+ *        \c IGRAPH_TO_UNDIRECTED_COLLAPSE: one undirected edge will
+ *        be created for each pair of vertices which are connected
+ *        with at least one directed edge, no multiple edges will be
+ *        created. \c IGRAPH_TO_UNDIRECTED_MUTUAL creates an undirected
+ *        edge for each pair of mutual edges in the directed graph.
+ *        Non-mutual edges are lost. This mode might create multiple
+ *        edges.
+ * \param edge_comb What to do with the edge attributes. See the igraph
+ *        manual section about attributes for details.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges.
+ *
+ * \example examples/simple/igraph_to_undirected.c
+ */
+
+int igraph_to_undirected(igraph_t *graph,
+                         igraph_to_undirected_t mode,
+                         const igraph_attribute_combination_t *edge_comb) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vector_t edges;
+    igraph_t newgraph;
+    igraph_bool_t attr = edge_comb && igraph_has_attribute_table();
+
+    if (mode != IGRAPH_TO_UNDIRECTED_EACH &&
+        mode != IGRAPH_TO_UNDIRECTED_COLLAPSE &&
+        mode != IGRAPH_TO_UNDIRECTED_MUTUAL) {
+        IGRAPH_ERROR("Cannot undirect graph, invalid mode", IGRAPH_EINVAL);
+    }
+
+    if (!igraph_is_directed(graph)) {
+        return 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    if (mode == IGRAPH_TO_UNDIRECTED_EACH) {
+        igraph_es_t es;
+        igraph_eit_t eit;
+
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+        IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID));
+        IGRAPH_FINALLY(igraph_es_destroy, &es);
+        IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+        IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+        while (!IGRAPH_EIT_END(eit)) {
+            long int edge = IGRAPH_EIT_GET(eit);
+            igraph_integer_t from, to;
+            igraph_edge(graph, (igraph_integer_t) edge, &from, &to);
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, from));
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+            IGRAPH_EIT_NEXT(eit);
+        }
+
+        igraph_eit_destroy(&eit);
+        igraph_es_destroy(&es);
+        IGRAPH_FINALLY_CLEAN(2);
+
+        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
+                                   (igraph_integer_t) no_of_nodes,
+                                   IGRAPH_UNDIRECTED));
+        IGRAPH_FINALLY(igraph_destroy, &newgraph);
+        igraph_vector_destroy(&edges);
+        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
+        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1);
+        IGRAPH_FINALLY_CLEAN(2);
+        igraph_destroy(graph);
+        *graph = newgraph;
+
+    } else if (mode == IGRAPH_TO_UNDIRECTED_COLLAPSE) {
+        igraph_vector_t inadj, outadj;
+        long int i;
+        igraph_vector_t mergeinto;
+        long int actedge = 0;
+
+        if (attr) {
+            IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges);
+        }
+
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+        IGRAPH_VECTOR_INIT_FINALLY(&inadj, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&outadj, 0);
+
+        for (i = 0; i < no_of_nodes; i++) {
+            long int n_out, n_in;
+            long int p1 = -1, p2 = -1;
+            long int e1 = 0, e2 = 0, n1 = 0, n2 = 0;
+            IGRAPH_CHECK(igraph_incident(graph, &outadj, (igraph_integer_t) i,
+                                         IGRAPH_OUT));
+            IGRAPH_CHECK(igraph_incident(graph, &inadj, (igraph_integer_t) i,
+                                         IGRAPH_IN));
+            n_out = igraph_vector_size(&outadj);
+            n_in = igraph_vector_size(&inadj);
+
+#define STEPOUT() if ( (++p1) < n_out) {    \
+        e1 = (long int) VECTOR(outadj)[p1]; \
+        n1 = IGRAPH_TO(graph, e1);      \
+    }
+#define STEPIN()  if ( (++p2) < n_in) {         \
+        e2 = (long int) VECTOR(inadj )[p2]; \
+        n2 = IGRAPH_FROM(graph, e2);        \
+    }
+
+            STEPOUT();
+            STEPIN();
+
+            while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) {
+                long int last;
+                if (n1 == n2) {
+                    last = n1;
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, n1));
+                    if (attr) {
+                        VECTOR(mergeinto)[e1] = actedge;
+                        VECTOR(mergeinto)[e2] = actedge;
+                        actedge++;
+                    }
+                    while (p1 < n_out && last == n1) {
+                        STEPOUT();
+                    }
+                    while (p2 < n_in  && last == n2) {
+                        STEPIN ();
+                    }
+                } else if (n1 < n2) {
+                    last = n1;
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, n1));
+                    if (attr) {
+                        VECTOR(mergeinto)[e1] = actedge;
+                        actedge++;
+                    }
+                    while (p1 < n_out && last == n1) {
+                        STEPOUT();
+                    }
+                } else { /* n2<n1 */
+                    last = n2;
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, n2));
+                    if (attr) {
+                        VECTOR(mergeinto)[e2] = actedge;
+                        actedge++;
+                    }
+                    while (p2 < n_in && last == n2) {
+                        STEPIN();
+                    }
+                }
+            }
+            while (p1 < n_out && n1 <= i) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, n1));
+                if (attr) {
+                    VECTOR(mergeinto)[e1] = actedge;
+                    actedge++;
+                }
+                STEPOUT();
+            }
+            while (p2 < n_in && n2 <= i) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, n2));
+                if (attr) {
+                    VECTOR(mergeinto)[e2] = actedge;
+                    actedge++;
+                }
+                STEPIN();
+            }
+        }
+
+#undef STEPOUT
+#undef STEPIN
+
+        igraph_vector_destroy(&outadj);
+        igraph_vector_destroy(&inadj);
+        IGRAPH_FINALLY_CLEAN(2);
+
+        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
+                                   (igraph_integer_t) no_of_nodes,
+                                   IGRAPH_UNDIRECTED));
+        IGRAPH_FINALLY(igraph_destroy, &newgraph);
+        igraph_vector_destroy(&edges);
+        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
+        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 0); /* no edge attributes */
+
+        if (attr) {
+            igraph_fixed_vectorlist_t vl;
+            IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto,
+                         actedge));
+            IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl);
+
+            IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.v,
+                         edge_comb));
+
+            igraph_fixed_vectorlist_destroy(&vl);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        IGRAPH_FINALLY_CLEAN(2);
+        igraph_destroy(graph);
+        *graph = newgraph;
+
+        if (attr) {
+            igraph_vector_destroy(&mergeinto);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    } else if (mode == IGRAPH_TO_UNDIRECTED_MUTUAL) {
+        igraph_vector_t inadj, outadj;
+        long int i;
+        igraph_vector_t mergeinto;
+        long int actedge = 0;
+
+        if (attr) {
+            IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges);
+            igraph_vector_fill(&mergeinto, -1);
+        }
+
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+        IGRAPH_VECTOR_INIT_FINALLY(&inadj, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&outadj, 0);
+
+        for (i = 0; i < no_of_nodes; i++) {
+            long int n_out, n_in;
+            long int p1 = -1, p2 = -1;
+            long int e1 = 0, e2 = 0, n1 = 0, n2 = 0;
+            IGRAPH_CHECK(igraph_incident(graph, &outadj, (igraph_integer_t) i,
+                                         IGRAPH_OUT));
+            IGRAPH_CHECK(igraph_incident(graph, &inadj,  (igraph_integer_t) i,
+                                         IGRAPH_IN));
+            n_out = igraph_vector_size(&outadj);
+            n_in = igraph_vector_size(&inadj);
+
+#define STEPOUT() if ( (++p1) < n_out) {    \
+        e1 = (long int) VECTOR(outadj)[p1]; \
+        n1 = IGRAPH_TO(graph, e1);      \
+    }
+#define STEPIN()  if ( (++p2) < n_in) {         \
+        e2 = (long int) VECTOR(inadj )[p2]; \
+        n2 = IGRAPH_FROM(graph, e2);        \
+    }
+
+            STEPOUT();
+            STEPIN();
+
+            while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) {
+                if (n1 == n2) {
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, n1));
+                    if (attr) {
+                        VECTOR(mergeinto)[e1] = actedge;
+                        VECTOR(mergeinto)[e2] = actedge;
+                        actedge++;
+                    }
+                    STEPOUT();
+                    STEPIN();
+                } else if (n1 < n2) {
+                    STEPOUT();
+                } else { /* n2<n1 */
+                    STEPIN();
+                }
+            }
+        }
+
+#undef STEPOUT
+#undef STEPIN
+
+        igraph_vector_destroy(&outadj);
+        igraph_vector_destroy(&inadj);
+        IGRAPH_FINALLY_CLEAN(2);
+
+        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
+                                   (igraph_integer_t) no_of_nodes,
+                                   IGRAPH_UNDIRECTED));
+        IGRAPH_FINALLY(igraph_destroy, &newgraph);
+        igraph_vector_destroy(&edges);
+        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
+        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 0); /* no edge attributes */
+
+        if (attr) {
+            igraph_fixed_vectorlist_t vl;
+            IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto,
+                         actedge));
+            IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl);
+
+            IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.v,
+                         edge_comb));
+
+            igraph_fixed_vectorlist_destroy(&vl);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        IGRAPH_FINALLY_CLEAN(2);
+        igraph_destroy(graph);
+        *graph = newgraph;
+
+        if (attr) {
+            igraph_vector_destroy(&mergeinto);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_get_stochastic
+ * Stochastic adjacency matrix of a graph
+ *
+ * Stochastic matrix of a graph. The stochastic matrix of a graph is
+ * its adjacency matrix, normalized row-wise or column-wise, such that
+ * the sum of each row (or column) is one.
+ * \param graph The input graph.
+ * \param sparsemat Pointer to an initialized matrix, the
+ *    result is stored here.
+ * \param column_wise Whether to normalize column-wise. For undirected
+ *    graphs this argument does not have any effect.
+ * \return Error code.
+ *
+ * Time complexity: O(|V||V|), quadratic in the number of vertices.
+ *
+ * \sa igraph_get_stochastic_sparsemat(), the sparse version of this
+ * function.
+ */
+
+int igraph_get_stochastic(const igraph_t *graph,
+                          igraph_matrix_t *matrix,
+                          igraph_bool_t column_wise) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    igraph_real_t sum;
+    int i, j;
+
+    IGRAPH_CHECK(igraph_get_adjacency(graph, matrix,
+                                      IGRAPH_GET_ADJACENCY_BOTH, /*eids=*/ 0));
+
+    if (!column_wise) {
+        for (i = 0; i < no_of_nodes; i++) {
+            sum = 0.0;
+            for (j = 0; j < no_of_nodes; j++) {
+                sum += MATRIX(*matrix, i, j);
+            }
+            for (j = 0; j < no_of_nodes; j++) {
+                MATRIX(*matrix, i, j) /= sum;
+            }
+        }
+    } else {
+        for (i = 0; i < no_of_nodes; i++) {
+            sum = 0.0;
+            for (j = 0; j < no_of_nodes; j++) {
+                sum += MATRIX(*matrix, j, i);
+            }
+            for (j = 0; j < no_of_nodes; j++) {
+                MATRIX(*matrix, j, i) /= sum;
+            }
+        }
+    }
+
+    return 0;
+}
+int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat,
+                                 igraph_bool_t column_wise);
+
+
+int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat,
+                                 igraph_bool_t column_wise) {
+    igraph_vector_t sum;
+    int no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat);
+    int i;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes);
+
+    if (!column_wise) {
+        IGRAPH_CHECK(igraph_sparsemat_rowsums(sparsemat, &sum));
+        for (i = 0; i < no_of_nodes; i++) {
+            if (VECTOR(sum)[i] == 0.0) {
+                IGRAPH_ERROR("Zero out-degree vertices not allowed",
+                             IGRAPH_EINVAL);
+            }
+            VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i];
+        }
+        IGRAPH_CHECK(igraph_sparsemat_scale_rows(sparsemat, &sum));
+    } else {
+        IGRAPH_CHECK(igraph_sparsemat_colsums(sparsemat, &sum));
+        for (i = 0; i < no_of_nodes; i++) {
+            if (VECTOR(sum)[i] == 0.0) {
+                IGRAPH_ERROR("Zero out-degree vertices not allowed",
+                             IGRAPH_EINVAL);
+            }
+            VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i];
+        }
+        IGRAPH_CHECK(igraph_sparsemat_scale_cols(sparsemat, &sum));
+    }
+
+    igraph_vector_destroy(&sum);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_get_stochastic_sparsemat
+ * \brief Stochastic adjacency matrix of a graph
+ *
+ * Stochastic matrix of a graph. The stochastic matrix of a graph is
+ * its adjacency matrix, normalized row-wise or column-wise, such that
+ * the sum of each row (or column) is one.
+ * \param graph The input graph.
+ * \param sparsemat Pointer to an uninitialized sparse matrix, the
+ *    result is stored here.
+ * \param column_wise Whether to normalize column-wise. For undirected
+ *    graphs this argument does not have any effect.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ *
+ * \sa igraph_get_stochastic(), the dense version of this function.
+ */
+
+int igraph_get_stochastic_sparsemat(const igraph_t *graph,
+                                    igraph_sparsemat_t *sparsemat,
+                                    igraph_bool_t column_wise) {
+
+    IGRAPH_CHECK(igraph_get_sparsemat(graph, sparsemat));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, sparsemat);
+    IGRAPH_CHECK(igraph_i_normalize_sparsemat(sparsemat, column_wise));
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+
+/**
+ * \ingroup conversion
+ * \function igraph_to_prufer
+ * \brief Converts a tree to its Pr&uuml;fer sequence
+ *
+ * A Pr&uuml;fer sequence is a unique sequence of integers associated
+ * with a labelled tree. A tree on n >= 2 vertices can be represented by a
+ * sequence of n-2 integers, each between 0 and n-1 (inclusive).
+ *
+ * \param graph Pointer to an initialized graph object which
+          must be a tree on n >= 2 vertices.
+ * \param prufer A pointer to the integer vector that should hold the Pr&uuml;fer sequence;
+                 the vector must be initialized and will be resized to n - 2.
+ * \return Error code:
+ *          \clist
+ *          \cli IGRAPH_ENOMEM
+ *             there is not enough memory to perform the operation.
+ *          \cli IGRAPH_EINVAL
+ *             the graph is not a tree or it is has less than vertices
+ *          \endclist
+ *
+ * \sa \ref igraph_from_prufer()
+ *
+ */
+int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) {
+    /* For generating the Prüfer sequence, we enumerate the vertices u of the tree.
+       We keep track of the degrees of all vertices, treating vertices
+       of degree 0 as removed. We maintain the invariant that all leafs
+       that are still contained in the tree are >= u.
+       If u is a leaf, we remove it and add its unique neighbor to the prüfer
+       sequence. If the removal of u turns the neighbor into a leaf which is < u,
+       we repeat the procedure for the new leaf and so on. */
+    igraph_integer_t u;
+    igraph_vector_t degrees, neighbors;
+    igraph_integer_t prufer_index = 0;
+    igraph_integer_t n = igraph_vcount(graph);
+    igraph_bool_t is_tree = 0;
+
+    IGRAPH_CHECK(igraph_is_tree(graph, &is_tree, NULL, IGRAPH_ALL));
+
+    if (!is_tree) {
+        IGRAPH_ERROR("The graph must be a tree", IGRAPH_EINVAL);
+    }
+
+    if (n < 2) {
+        IGRAPH_ERROR("The tree must have at least 2 vertices", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_resize(prufer, n - 2));
+    IGRAPH_VECTOR_INIT_FINALLY(&degrees, n);
+    IGRAPH_VECTOR_INIT_FINALLY(&neighbors, 1);
+
+    IGRAPH_CHECK(igraph_degree(graph, &degrees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS));
+
+    for (u = 0; u < n; ++u) {
+        igraph_integer_t degree = VECTOR(degrees)[u];
+        igraph_integer_t leaf = u;
+
+        while (degree == 1 && leaf <= u) {
+            igraph_integer_t i;
+            igraph_integer_t neighbor = 0;
+            igraph_integer_t neighbor_count = 0;
+
+            VECTOR(degrees)[leaf] = 0; /* mark leaf v as deleted */
+
+            IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, leaf, IGRAPH_ALL));
+
+            /* Find the unique remaining neighbor of the leaf */
+            neighbor_count = igraph_vector_size(&neighbors);
+            for (i = 0; i < neighbor_count; i++) {
+                neighbor = VECTOR(neighbors)[i];
+                if (VECTOR(degrees)[neighbor] > 0) {
+                    break;
+                }
+            }
+
+            /* remember that we have removed the leaf */
+            VECTOR(degrees)[neighbor]--;
+            degree = VECTOR(degrees)[neighbor];
+
+            /* Add the neighbor to the prufer sequence unless it is the last vertex
+            (i.e. degree == 0) */
+            if (degree > 0) {
+                VECTOR(*prufer)[prufer_index] = neighbor;
+                prufer_index++;
+            }
+            leaf = neighbor;
+        }
+    }
+
+    igraph_vector_destroy(&degrees);
+    igraph_vector_destroy(&neighbors);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/cores.c b/igraph/src/cores.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cores.c
@@ -0,0 +1,159 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_community.h"
+#include "igraph_memory.h"
+#include "igraph_interface.h"
+#include "igraph_iterators.h"
+#include "config.h"
+
+/**
+ * \function igraph_coreness
+ * \brief Finding the coreness of the vertices in a network.
+ *
+ * The k-core of a graph is a maximal subgraph in which each vertex
+ * has at least degree k. (Degree here means the degree in the
+ * subgraph of course.). The coreness of a vertex is the highest order
+ * of a k-core containing the vertex.
+ *
+ * </para><para>
+ * This function implements the algorithm presented in Vladimir
+ * Batagelj, Matjaz Zaversnik: An O(m) Algorithm for Cores
+ * Decomposition of Networks.
+ * \param graph The input graph.
+ * \param cores Pointer to an initialized vector, the result of the
+ *        computation will be stored here. It will be resized as
+ *        needed. For each vertex it contains the highest order of a
+ *        core containing the vertex.
+ * \param mode For directed graph it specifies whether to calculate
+ *        in-cores, out-cores or the undirected version. It is ignored
+ *        for undirected graphs. Possible values: \c IGRAPH_ALL
+ *        undirected version, \c IGRAPH_IN in-cores, \c IGRAPH_OUT
+ *        out-cores.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|), the number of edges.
+ */
+
+int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores,
+                    igraph_neimode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int *bin, *vert, *pos;
+    long int maxdeg;
+    long int i, j = 0;
+    igraph_vector_t neis;
+    igraph_neimode_t omode;
+
+    if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) {
+        IGRAPH_ERROR("Invalid mode in k-cores", IGRAPH_EINVAL);
+    }
+    if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) {
+        mode = omode = IGRAPH_ALL;
+    } else if (mode == IGRAPH_IN) {
+        omode = IGRAPH_OUT;
+    } else {
+        omode = IGRAPH_IN;
+    }
+
+    vert = igraph_Calloc(no_of_nodes, long int);
+    if (vert == 0) {
+        IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, vert);
+    pos = igraph_Calloc(no_of_nodes, long int);
+    if (pos == 0) {
+        IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, pos);
+
+    /* maximum degree + degree of vertices */
+    IGRAPH_CHECK(igraph_degree(graph, cores, igraph_vss_all(), mode,
+                               IGRAPH_LOOPS));
+    maxdeg = (long int) igraph_vector_max(cores);
+
+    bin = igraph_Calloc(maxdeg + 1, long int);
+    if (bin == 0) {
+        IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, bin);
+
+    /* degree histogram */
+    for (i = 0; i < no_of_nodes; i++) {
+        bin[ (long int)VECTOR(*cores)[i] ] += 1;
+    }
+
+    /* start pointers */
+    j = 0;
+    for (i = 0; i <= maxdeg; i++) {
+        long int k = bin[i];
+        bin[i] = j;
+        j += k;
+    }
+
+    /* sort in vert (and corrupt bin) */
+    for (i = 0; i < no_of_nodes; i++) {
+        pos[i] = bin[(long int)VECTOR(*cores)[i]];
+        vert[pos[i]] = i;
+        bin[(long int)VECTOR(*cores)[i]] += 1;
+    }
+
+    /* correct bin */
+    for (i = maxdeg; i > 0; i--) {
+        bin[i] = bin[i - 1];
+    }
+    bin[0] = 0;
+
+    /* this is the main algorithm */
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg);
+    for (i = 0; i < no_of_nodes; i++) {
+        long int v = vert[i];
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, omode));
+        for (j = 0; j < igraph_vector_size(&neis); j++) {
+            long int u = (long int) VECTOR(neis)[j];
+            if (VECTOR(*cores)[u] > VECTOR(*cores)[v]) {
+                long int du = (long int) VECTOR(*cores)[u];
+                long int pu = pos[u];
+                long int pw = bin[du];
+                long int w = vert[pw];
+                if (u != w) {
+                    pos[u] = pw;
+                    pos[w] = pu;
+                    vert[pu] = w;
+                    vert[pw] = u;
+                }
+                bin[du] += 1;
+                VECTOR(*cores)[u] -= 1;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_free(bin);
+    igraph_free(pos);
+    igraph_free(vert);
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
diff --git a/igraph/src/cs_add.c b/igraph/src/cs_add.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_add.c
@@ -0,0 +1,48 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* C = alpha*A + beta*B */
+cs *cs_add (const cs *A, const cs *B, CS_ENTRY alpha, CS_ENTRY beta)
+{
+    CS_INT p, j, nz = 0, anz, *Cp, *Ci, *Bp, m, n, bnz, *w, values ;
+    CS_ENTRY *x, *Bx, *Cx ;
+    cs *C ;
+    if (!CS_CSC (A) || !CS_CSC (B)) return (NULL) ;         /* check inputs */
+    if (A->m != B->m || A->n != B->n) return (NULL) ;
+    m = A->m ; anz = A->p [A->n] ;
+    n = B->n ; Bp = B->p ; Bx = B->x ; bnz = Bp [n] ;
+    w = cs_calloc (m, sizeof (CS_INT)) ;                       /* get workspace */
+    values = (A->x != NULL) && (Bx != NULL) ;
+    x = values ? cs_malloc (m, sizeof (CS_ENTRY)) : NULL ;    /* get workspace */
+    C = cs_spalloc (m, n, anz + bnz, values, 0) ;           /* allocate result*/
+    if (!C || !w || (values && !x)) return (cs_done (C, w, x, 0)) ;
+    Cp = C->p ; Ci = C->i ; Cx = C->x ;
+    for (j = 0 ; j < n ; j++)
+    {
+        Cp [j] = nz ;                   /* column j of C starts here */
+        nz = cs_scatter (A, j, alpha, w, x, j+1, C, nz) ;   /* alpha*A(:,j)*/
+        nz = cs_scatter (B, j, beta, w, x, j+1, C, nz) ;    /* beta*B(:,j) */
+        if (values) for (p = Cp [j] ; p < nz ; p++) Cx [p] = x [Ci [p]] ;
+    }
+    Cp [n] = nz ;                       /* finalize the last column of C */
+    cs_sprealloc (C, 0) ;               /* remove extra space from C */
+    return (cs_done (C, w, x, 1)) ;     /* success; free workspace, return C */
+}
diff --git a/igraph/src/cs_amd.c b/igraph/src/cs_amd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_amd.c
@@ -0,0 +1,384 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* clear w */
+static CS_INT cs_wclear (CS_INT mark, CS_INT lemax, CS_INT *w, CS_INT n)
+{
+    CS_INT k ;
+    if (mark < 2 || (mark + lemax < 0))
+    {
+        for (k = 0 ; k < n ; k++) if (w [k] != 0) w [k] = 1 ;
+        mark = 2 ;
+    }
+    return (mark) ;     /* at this point, w [0..n-1] < mark holds */
+}
+
+/* keep off-diagonal entries; drop diagonal entries */
+static CS_INT cs_diag (CS_INT i, CS_INT j, CS_ENTRY aij, void *other) { return (i != j) ; }
+
+/* p = amd(A+A') if symmetric is true, or amd(A'A) otherwise */
+CS_INT *cs_amd (CS_INT order, const cs *A)  /* order 0:natural, 1:Chol, 2:LU, 3:QR */
+{
+    cs *C, *A2, *AT ;
+    CS_INT *Cp, *Ci, *last, *W, *len, *nv, *next, *P, *head, *elen, *degree, *w,
+        *hhead, *ATp, *ATi, d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1,
+        k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi, nvj, nvk, mark, wnvi,
+        ok, cnz, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, n, m, t ;
+    unsigned CS_INT h ;
+    /* --- Construct matrix C ----------------------------------------------- */
+    if (!CS_CSC (A) || order <= 0 || order > 3) return (NULL) ; /* check */
+    AT = cs_transpose (A, 0) ;              /* compute A' */
+    if (!AT) return (NULL) ;
+    m = A->m ; n = A->n ;
+    dense = CS_MAX (16, 10 * sqrt ((double) n)) ;   /* find dense threshold */
+    dense = CS_MIN (n-2, dense) ;
+    if (order == 1 && n == m)
+    {
+        C = cs_add (A, AT, 0, 0) ;          /* C = A+A' */
+    }
+    else if (order == 2)
+    {
+        ATp = AT->p ;                       /* drop dense columns from AT */
+        ATi = AT->i ;
+        for (p2 = 0, j = 0 ; j < m ; j++)
+        {
+            p = ATp [j] ;                   /* column j of AT starts here */
+            ATp [j] = p2 ;                  /* new column j starts here */
+            if (ATp [j+1] - p > dense) continue ;   /* skip dense col j */
+            for ( ; p < ATp [j+1] ; p++) ATi [p2++] = ATi [p] ;
+        }
+        ATp [m] = p2 ;                      /* finalize AT */
+        A2 = cs_transpose (AT, 0) ;         /* A2 = AT' */
+        C = A2 ? cs_multiply (AT, A2) : NULL ;  /* C=A'*A with no dense rows */
+        cs_spfree (A2) ;
+    }
+    else
+    {
+        C = cs_multiply (AT, A) ;           /* C=A'*A */
+    }
+    cs_spfree (AT) ;
+    if (!C) return (NULL) ;
+    cs_fkeep (C, &cs_diag, NULL) ;          /* drop diagonal entries */
+    Cp = C->p ;
+    cnz = Cp [n] ;
+    P = cs_malloc (n+1, sizeof (CS_INT)) ;     /* allocate result */
+    W = cs_malloc (8*(n+1), sizeof (CS_INT)) ; /* get workspace */
+    t = cnz + cnz/5 + 2*n ;                 /* add elbow room to C */
+    if (!P || !W || !cs_sprealloc (C, t)) return (cs_idone (P, C, W, 0)) ;
+    len  = W           ; nv     = W +   (n+1) ; next   = W + 2*(n+1) ;
+    head = W + 3*(n+1) ; elen   = W + 4*(n+1) ; degree = W + 5*(n+1) ;
+    w    = W + 6*(n+1) ; hhead  = W + 7*(n+1) ;
+    last = P ;                              /* use P as workspace for last */
+    /* --- Initialize quotient graph ---------------------------------------- */
+    for (k = 0 ; k < n ; k++) len [k] = Cp [k+1] - Cp [k] ;
+    len [n] = 0 ;
+    nzmax = C->nzmax ;
+    Ci = C->i ;
+    for (i = 0 ; i <= n ; i++)
+    {
+        head [i] = -1 ;                     /* degree list i is empty */
+        last [i] = -1 ;
+        next [i] = -1 ;
+        hhead [i] = -1 ;                    /* hash list i is empty */
+        nv [i] = 1 ;                        /* node i is just one node */
+        w [i] = 1 ;                         /* node i is alive */
+        elen [i] = 0 ;                      /* Ek of node i is empty */
+        degree [i] = len [i] ;              /* degree of node i */
+    }
+    mark = cs_wclear (0, 0, w, n) ;         /* clear w */
+    elen [n] = -2 ;                         /* n is a dead element */
+    Cp [n] = -1 ;                           /* n is a root of assembly tree */
+    w [n] = 0 ;                             /* n is a dead element */
+    /* --- Initialize degree lists ------------------------------------------ */
+    for (i = 0 ; i < n ; i++)
+    {
+        d = degree [i] ;
+        if (d == 0)                         /* node i is empty */
+        {
+            elen [i] = -2 ;                 /* element i is dead */
+            nel++ ;
+            Cp [i] = -1 ;                   /* i is a root of assembly tree */
+            w [i] = 0 ;
+        }
+        else if (d > dense)                 /* node i is dense */
+        {
+            nv [i] = 0 ;                    /* absorb i into element n */
+            elen [i] = -1 ;                 /* node i is dead */
+            nel++ ;
+            Cp [i] = CS_FLIP (n) ;
+            nv [n]++ ;
+        }
+        else
+        {
+            if (head [d] != -1) last [head [d]] = i ;
+            next [i] = head [d] ;           /* put node i in degree list d */
+            head [d] = i ;
+        }
+    }
+    while (nel < n)                         /* while (selecting pivots) do */
+    {
+        /* --- Select node of minimum approximate degree -------------------- */
+        for (k = -1 ; mindeg < n && (k = head [mindeg]) == -1 ; mindeg++) ;
+        if (next [k] != -1) last [next [k]] = -1 ;
+        head [mindeg] = next [k] ;          /* remove k from degree list */
+        elenk = elen [k] ;                  /* elenk = |Ek| */
+        nvk = nv [k] ;                      /* # of nodes k represents */
+        nel += nvk ;                        /* nv[k] nodes of A eliminated */
+        /* --- Garbage collection ------------------------------------------- */
+        if (elenk > 0 && cnz + mindeg >= nzmax)
+        {
+            for (j = 0 ; j < n ; j++)
+            {
+                if ((p = Cp [j]) >= 0)      /* j is a live node or element */
+                {
+                    Cp [j] = Ci [p] ;       /* save first entry of object */
+                    Ci [p] = CS_FLIP (j) ;  /* first entry is now CS_FLIP(j) */
+                }
+            }
+            for (q = 0, p = 0 ; p < cnz ; ) /* scan all of memory */
+            {
+                if ((j = CS_FLIP (Ci [p++])) >= 0)  /* found object j */
+                {
+                    Ci [q] = Cp [j] ;       /* restore first entry of object */
+                    Cp [j] = q++ ;          /* new pointer to object j */
+                    for (k3 = 0 ; k3 < len [j]-1 ; k3++) Ci [q++] = Ci [p++] ;
+                }
+            }
+            cnz = q ;                       /* Ci [cnz...nzmax-1] now free */
+        }
+        /* --- Construct new element ---------------------------------------- */
+        dk = 0 ;
+        nv [k] = -nvk ;                     /* flag k as in Lk */
+        p = Cp [k] ;
+        pk1 = (elenk == 0) ? p : cnz ;      /* do in place if elen[k] == 0 */
+        pk2 = pk1 ;
+        for (k1 = 1 ; k1 <= elenk + 1 ; k1++)
+        {
+            if (k1 > elenk)
+            {
+                e = k ;                     /* search the nodes in k */
+                pj = p ;                    /* list of nodes starts at Ci[pj]*/
+                ln = len [k] - elenk ;      /* length of list of nodes in k */
+            }
+            else
+            {
+                e = Ci [p++] ;              /* search the nodes in e */
+                pj = Cp [e] ;
+                ln = len [e] ;              /* length of list of nodes in e */
+            }
+            for (k2 = 1 ; k2 <= ln ; k2++)
+            {
+                i = Ci [pj++] ;
+                if ((nvi = nv [i]) <= 0) continue ; /* node i dead, or seen */
+                dk += nvi ;                 /* degree[Lk] += size of node i */
+                nv [i] = -nvi ;             /* negate nv[i] to denote i in Lk*/
+                Ci [pk2++] = i ;            /* place i in Lk */
+                if (next [i] != -1) last [next [i]] = last [i] ;
+                if (last [i] != -1)         /* remove i from degree list */
+                {
+                    next [last [i]] = next [i] ;
+                }
+                else
+                {
+                    head [degree [i]] = next [i] ;
+                }
+            }
+            if (e != k)
+            {
+                Cp [e] = CS_FLIP (k) ;      /* absorb e into k */
+                w [e] = 0 ;                 /* e is now a dead element */
+            }
+        }
+        if (elenk != 0) cnz = pk2 ;         /* Ci [cnz...nzmax] is free */
+        degree [k] = dk ;                   /* external degree of k - |Lk\i| */
+        Cp [k] = pk1 ;                      /* element k is in Ci[pk1..pk2-1] */
+        len [k] = pk2 - pk1 ;
+        elen [k] = -2 ;                     /* k is now an element */
+        /* --- Find set differences ----------------------------------------- */
+        mark = cs_wclear (mark, lemax, w, n) ;  /* clear w if necessary */
+        for (pk = pk1 ; pk < pk2 ; pk++)    /* scan 1: find |Le\Lk| */
+        {
+            i = Ci [pk] ;
+            if ((eln = elen [i]) <= 0) continue ;/* skip if elen[i] empty */
+            nvi = -nv [i] ;                      /* nv [i] was negated */
+            wnvi = mark - nvi ;
+            for (p = Cp [i] ; p <= Cp [i] + eln - 1 ; p++)  /* scan Ei */
+            {
+                e = Ci [p] ;
+                if (w [e] >= mark)
+                {
+                    w [e] -= nvi ;          /* decrement |Le\Lk| */
+                }
+                else if (w [e] != 0)        /* ensure e is a live element */
+                {
+                    w [e] = degree [e] + wnvi ; /* 1st time e seen in scan 1 */
+                }
+            }
+        }
+        /* --- Degree update ------------------------------------------------ */
+        for (pk = pk1 ; pk < pk2 ; pk++)    /* scan2: degree update */
+        {
+            i = Ci [pk] ;                   /* consider node i in Lk */
+            p1 = Cp [i] ;
+            p2 = p1 + elen [i] - 1 ;
+            pn = p1 ;
+            for (h = 0, d = 0, p = p1 ; p <= p2 ; p++)    /* scan Ei */
+            {
+                e = Ci [p] ;
+                if (w [e] != 0)             /* e is an unabsorbed element */
+                {
+                    dext = w [e] - mark ;   /* dext = |Le\Lk| */
+                    if (dext > 0)
+                    {
+                        d += dext ;         /* sum up the set differences */
+                        Ci [pn++] = e ;     /* keep e in Ei */
+                        h += e ;            /* compute the hash of node i */
+                    }
+                    else
+                    {
+                        Cp [e] = CS_FLIP (k) ;  /* aggressive absorb. e->k */
+                        w [e] = 0 ;             /* e is a dead element */
+                    }
+                }
+            }
+            elen [i] = pn - p1 + 1 ;        /* elen[i] = |Ei| */
+            p3 = pn ;
+            p4 = p1 + len [i] ;
+            for (p = p2 + 1 ; p < p4 ; p++) /* prune edges in Ai */
+            {
+                j = Ci [p] ;
+                if ((nvj = nv [j]) <= 0) continue ; /* node j dead or in Lk */
+                d += nvj ;                  /* degree(i) += |j| */
+                Ci [pn++] = j ;             /* place j in node list of i */
+                h += j ;                    /* compute hash for node i */
+            }
+            if (d == 0)                     /* check for mass elimination */
+            {
+                Cp [i] = CS_FLIP (k) ;      /* absorb i into k */
+                nvi = -nv [i] ;
+                dk -= nvi ;                 /* |Lk| -= |i| */
+                nvk += nvi ;                /* |k| += nv[i] */
+                nel += nvi ;
+                nv [i] = 0 ;
+                elen [i] = -1 ;             /* node i is dead */
+            }
+            else
+            {
+                degree [i] = CS_MIN (degree [i], d) ;   /* update degree(i) */
+                Ci [pn] = Ci [p3] ;         /* move first node to end */
+                Ci [p3] = Ci [p1] ;         /* move 1st el. to end of Ei */
+                Ci [p1] = k ;               /* add k as 1st element in of Ei */
+                len [i] = pn - p1 + 1 ;     /* new len of adj. list of node i */
+                h %= n ;                    /* finalize hash of i */
+                next [i] = hhead [h] ;      /* place i in hash bucket */
+                hhead [h] = i ;
+                last [i] = h ;              /* save hash of i in last[i] */
+            }
+        }                                   /* scan2 is done */
+        degree [k] = dk ;                   /* finalize |Lk| */
+        lemax = CS_MAX (lemax, dk) ;
+        mark = cs_wclear (mark+lemax, lemax, w, n) ;    /* clear w */
+        /* --- Supernode detection ------------------------------------------ */
+        for (pk = pk1 ; pk < pk2 ; pk++)
+        {
+            i = Ci [pk] ;
+            if (nv [i] >= 0) continue ;         /* skip if i is dead */
+            h = last [i] ;                      /* scan hash bucket of node i */
+            i = hhead [h] ;
+            hhead [h] = -1 ;                    /* hash bucket will be empty */
+            for ( ; i != -1 && next [i] != -1 ; i = next [i], mark++)
+            {
+                ln = len [i] ;
+                eln = elen [i] ;
+                for (p = Cp [i]+1 ; p <= Cp [i] + ln-1 ; p++) w [Ci [p]] = mark;
+                jlast = i ;
+                for (j = next [i] ; j != -1 ; ) /* compare i with all j */
+                {
+                    ok = (len [j] == ln) && (elen [j] == eln) ;
+                    for (p = Cp [j] + 1 ; ok && p <= Cp [j] + ln - 1 ; p++)
+                    {
+                        if (w [Ci [p]] != mark) ok = 0 ;    /* compare i and j*/
+                    }
+                    if (ok)                     /* i and j are identical */
+                    {
+                        Cp [j] = CS_FLIP (i) ;  /* absorb j into i */
+                        nv [i] += nv [j] ;
+                        nv [j] = 0 ;
+                        elen [j] = -1 ;         /* node j is dead */
+                        j = next [j] ;          /* delete j from hash bucket */
+                        next [jlast] = j ;
+                    }
+                    else
+                    {
+                        jlast = j ;             /* j and i are different */
+                        j = next [j] ;
+                    }
+                }
+            }
+        }
+        /* --- Finalize new element------------------------------------------ */
+        for (p = pk1, pk = pk1 ; pk < pk2 ; pk++)   /* finalize Lk */
+        {
+            i = Ci [pk] ;
+            if ((nvi = -nv [i]) <= 0) continue ;/* skip if i is dead */
+            nv [i] = nvi ;                      /* restore nv[i] */
+            d = degree [i] + dk - nvi ;         /* compute external degree(i) */
+            d = CS_MIN (d, n - nel - nvi) ;
+            if (head [d] != -1) last [head [d]] = i ;
+            next [i] = head [d] ;               /* put i back in degree list */
+            last [i] = -1 ;
+            head [d] = i ;
+            mindeg = CS_MIN (mindeg, d) ;       /* find new minimum degree */
+            degree [i] = d ;
+            Ci [p++] = i ;                      /* place i in Lk */
+        }
+        nv [k] = nvk ;                      /* # nodes absorbed into k */
+        if ((len [k] = p-pk1) == 0)         /* length of adj list of element k*/
+        {
+            Cp [k] = -1 ;                   /* k is a root of the tree */
+            w [k] = 0 ;                     /* k is now a dead element */
+        }
+        if (elenk != 0) cnz = p ;           /* free unused space in Lk */
+    }
+    /* --- Postordering ----------------------------------------------------- */
+    for (i = 0 ; i < n ; i++) Cp [i] = CS_FLIP (Cp [i]) ;/* fix assembly tree */
+    for (j = 0 ; j <= n ; j++) head [j] = -1 ;
+    for (j = n ; j >= 0 ; j--)              /* place unordered nodes in lists */
+    {
+        if (nv [j] > 0) continue ;          /* skip if j is an element */
+        next [j] = head [Cp [j]] ;          /* place j in list of its parent */
+        head [Cp [j]] = j ;
+    }
+    for (e = n ; e >= 0 ; e--)              /* place elements in lists */
+    {
+        if (nv [e] <= 0) continue ;         /* skip unless e is an element */
+        if (Cp [e] != -1)
+        {
+            next [e] = head [Cp [e]] ;      /* place e in list of its parent */
+            head [Cp [e]] = e ;
+        }
+    }
+    for (k = 0, i = 0 ; i <= n ; i++)       /* postorder the assembly tree */
+    {
+        if (Cp [i] == -1) k = cs_tdfs (i, k, head, next, P, w) ;
+    }
+    return (cs_idone (P, C, W, 1)) ;
+}
diff --git a/igraph/src/cs_chol.c b/igraph/src/cs_chol.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_chol.c
@@ -0,0 +1,79 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* L = chol (A, [pinv parent cp]), pinv is optional */
+csn *cs_chol (const cs *A, const css *S)
+{
+    CS_ENTRY d, lki, *Lx, *x, *Cx ;
+    CS_INT top, i, p, k, n, *Li, *Lp, *cp, *pinv, *s, *c, *parent, *Cp, *Ci ;
+    cs *L, *C, *E ;
+    csn *N ;
+    if (!CS_CSC (A) || !S || !S->cp || !S->parent) return (NULL) ;
+    n = A->n ;
+    N = cs_calloc (1, sizeof (csn)) ;       /* allocate result */
+    c = cs_malloc (2*n, sizeof (CS_INT)) ;     /* get CS_INT workspace */
+    x = cs_malloc (n, sizeof (CS_ENTRY)) ;    /* get CS_ENTRY workspace */
+    cp = S->cp ; pinv = S->pinv ; parent = S->parent ;
+    C = pinv ? cs_symperm (A, pinv, 1) : ((cs *) A) ;
+    E = pinv ? C : NULL ;           /* E is alias for A, or a copy E=A(p,p) */
+    if (!N || !c || !x || !C) return (cs_ndone (N, E, c, x, 0)) ;
+    s = c + n ;
+    Cp = C->p ; Ci = C->i ; Cx = C->x ;
+    N->L = L = cs_spalloc (n, n, cp [n], 1, 0) ;    /* allocate result */
+    if (!L) return (cs_ndone (N, E, c, x, 0)) ;
+    Lp = L->p ; Li = L->i ; Lx = L->x ;
+    for (k = 0 ; k < n ; k++) Lp [k] = c [k] = cp [k] ;
+    for (k = 0 ; k < n ; k++)       /* compute L(k,:) for L*L' = C */
+    {
+        /* --- Nonzero pattern of L(k,:) ------------------------------------ */
+        top = cs_ereach (C, k, parent, s, c) ;      /* find pattern of L(k,:) */
+        x [k] = 0 ;                                 /* x (0:k) is now zero */
+        for (p = Cp [k] ; p < Cp [k+1] ; p++)       /* x = full(triu(C(:,k))) */
+        {
+            if (Ci [p] <= k) x [Ci [p]] = Cx [p] ;
+        }
+        d = x [k] ;                     /* d = C(k,k) */
+        x [k] = 0 ;                     /* clear x for k+1st iteration */
+        /* --- Triangular solve --------------------------------------------- */
+        for ( ; top < n ; top++)    /* solve L(0:k-1,0:k-1) * x = C(:,k) */
+        {
+            i = s [top] ;               /* s [top..n-1] is pattern of L(k,:) */
+            lki = x [i] / Lx [Lp [i]] ; /* L(k,i) = x (i) / L(i,i) */
+            x [i] = 0 ;                 /* clear x for k+1st iteration */
+            for (p = Lp [i] + 1 ; p < c [i] ; p++)
+            {
+                x [Li [p]] -= Lx [p] * lki ;
+            }
+            d -= lki * CS_CONJ (lki) ;            /* d = d - L(k,i)*L(k,i) */
+            p = c [i]++ ;
+            Li [p] = k ;                /* store L(k,i) in column i */
+            Lx [p] = CS_CONJ (lki) ;
+        }
+        /* --- Compute L(k,k) ----------------------------------------------- */
+        if (CS_REAL (d) <= 0 || CS_IMAG (d) != 0)
+	    return (cs_ndone (N, E, c, x, 0)) ; /* not pos def */
+        p = c [k]++ ;
+        Li [p] = k ;                /* store L(k,k) = sqrt (d) in column k */
+        Lx [p] = sqrt (d) ;
+    }
+    Lp [n] = cp [n] ;               /* finalize L */
+    return (cs_ndone (N, E, c, x, 1)) ; /* success: free E,s,x; return N */
+}
diff --git a/igraph/src/cs_cholsol.c b/igraph/src/cs_cholsol.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_cholsol.c
@@ -0,0 +1,46 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* x=A\b where A is symmetric positive definite; b overwritten with solution */
+CS_INT cs_cholsol (CS_INT order, const cs *A, CS_ENTRY *b)
+{
+    CS_ENTRY *x ;
+    css *S ;
+    csn *N ;
+    CS_INT n, ok ;
+    if (!CS_CSC (A) || !b) return (0) ;     /* check inputs */
+    n = A->n ;
+    S = cs_schol (order, A) ;               /* ordering and symbolic analysis */
+    N = cs_chol (A, S) ;                    /* numeric Cholesky factorization */
+    x = cs_malloc (n, sizeof (CS_ENTRY)) ;    /* get workspace */
+    ok = (S && N && x) ;
+    if (ok)
+    {
+        cs_ipvec (S->pinv, b, x, n) ;   /* x = P*b */
+        cs_lsolve (N->L, x) ;           /* x = L\x */
+        cs_ltsolve (N->L, x) ;          /* x = L'\x */
+        cs_pvec (S->pinv, x, b, n) ;    /* b = P'*x */
+    }
+    cs_free (x) ;
+    cs_sfree (S) ;
+    cs_nfree (N) ;
+    return (ok) ;
+}
diff --git a/igraph/src/cs_compress.c b/igraph/src/cs_compress.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_compress.c
@@ -0,0 +1,42 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* C = compressed-column form of a triplet matrix T */
+cs *cs_compress (const cs *T)
+{
+    CS_INT m, n, nz, p, k, *Cp, *Ci, *w, *Ti, *Tj ;
+    CS_ENTRY *Cx, *Tx ;
+    cs *C ;
+    if (!CS_TRIPLET (T)) return (NULL) ;                /* check inputs */
+    m = T->m ; n = T->n ; Ti = T->i ; Tj = T->p ; Tx = T->x ; nz = T->nz ;
+    C = cs_spalloc (m, n, nz, Tx != NULL, 0) ;          /* allocate result */
+    w = cs_calloc (n, sizeof (CS_INT)) ;                   /* get workspace */
+    if (!C || !w) return (cs_done (C, w, NULL, 0)) ;    /* out of memory */
+    Cp = C->p ; Ci = C->i ; Cx = C->x ;
+    for (k = 0 ; k < nz ; k++) w [Tj [k]]++ ;           /* column counts */
+    cs_cumsum (Cp, w, n) ;                              /* column pointers */
+    for (k = 0 ; k < nz ; k++)
+    {
+        Ci [p = w [Tj [k]]++] = Ti [k] ;    /* A(i,j) is the pth entry in C */
+        if (Cx) Cx [p] = Tx [k] ;
+    }
+    return (cs_done (C, w, NULL, 1)) ;      /* success; free w and return C */
+}
diff --git a/igraph/src/cs_counts.c b/igraph/src/cs_counts.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_counts.c
@@ -0,0 +1,81 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* column counts of LL'=A or LL'=A'A, given parent & post ordering */
+#define HEAD(k,j) (ata ? head [k] : j)
+#define NEXT(J)   (ata ? next [J] : -1)
+static void init_ata (cs *AT, const CS_INT *post, CS_INT *w, CS_INT **head, CS_INT **next)
+{
+    CS_INT i, k, p, m = AT->n, n = AT->m, *ATp = AT->p, *ATi = AT->i ;
+    *head = w+4*n, *next = w+5*n+1 ;
+    for (k = 0 ; k < n ; k++) w [post [k]] = k ;    /* invert post */
+    for (i = 0 ; i < m ; i++)
+    {
+        for (k = n, p = ATp[i] ; p < ATp[i+1] ; p++) k = CS_MIN (k, w [ATi[p]]);
+        (*next) [i] = (*head) [k] ;     /* place row i in linked list k */
+        (*head) [k] = i ;
+    }
+}
+CS_INT *cs_counts (const cs *A, const CS_INT *parent, const CS_INT *post, CS_INT ata)
+{
+    CS_INT i, j, k, n, m, J, s, p, q, jleaf, *ATp, *ATi, *maxfirst, *prevleaf,
+        *ancestor, *head = NULL, *next = NULL, *colcount, *w, *first, *delta ;
+    cs *AT ;
+    if (!CS_CSC (A) || !parent || !post) return (NULL) ;    /* check inputs */
+    m = A->m ; n = A->n ;
+    s = 4*n + (ata ? (n+m+1) : 0) ;
+    delta = colcount = cs_malloc (n, sizeof (CS_INT)) ;    /* allocate result */
+    w = cs_malloc (s, sizeof (CS_INT)) ;                   /* get workspace */
+    AT = cs_transpose (A, 0) ;                          /* AT = A' */
+    if (!AT || !colcount || !w) return (cs_idone (colcount, AT, w, 0)) ;
+    ancestor = w ; maxfirst = w+n ; prevleaf = w+2*n ; first = w+3*n ;
+    for (k = 0 ; k < s ; k++) w [k] = -1 ;      /* clear workspace w [0..s-1] */
+    for (k = 0 ; k < n ; k++)                   /* find first [j] */
+    {
+        j = post [k] ;
+        delta [j] = (first [j] == -1) ? 1 : 0 ;  /* delta[j]=1 if j is a leaf */
+        for ( ; j != -1 && first [j] == -1 ; j = parent [j]) first [j] = k ;
+    }
+    ATp = AT->p ; ATi = AT->i ;
+    if (ata) init_ata (AT, post, w, &head, &next) ;
+    for (i = 0 ; i < n ; i++) ancestor [i] = i ; /* each node in its own set */
+    for (k = 0 ; k < n ; k++)
+    {
+        j = post [k] ;          /* j is the kth node in postordered etree */
+        if (parent [j] != -1) delta [parent [j]]-- ;    /* j is not a root */
+        for (J = HEAD (k,j) ; J != -1 ; J = NEXT (J))   /* J=j for LL'=A case */
+        {
+            for (p = ATp [J] ; p < ATp [J+1] ; p++)
+            {
+                i = ATi [p] ;
+                q = cs_leaf (i, j, first, maxfirst, prevleaf, ancestor, &jleaf);
+                if (jleaf >= 1) delta [j]++ ;   /* A(i,j) is in skeleton */
+                if (jleaf == 2) delta [q]-- ;   /* account for overlap in q */
+            }
+        }
+        if (parent [j] != -1) ancestor [j] = parent [j] ;
+    }
+    for (j = 0 ; j < n ; j++)           /* sum up delta's of each child */
+    {
+        if (parent [j] != -1) colcount [parent [j]] += colcount [j] ;
+    }
+    return (cs_idone (colcount, AT, w, 1)) ;    /* success: free workspace */
+} 
diff --git a/igraph/src/cs_cumsum.c b/igraph/src/cs_cumsum.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_cumsum.c
@@ -0,0 +1,37 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* p [0..n] = cumulative sum of c [0..n-1], and then copy p [0..n-1] into c */
+double cs_cumsum (CS_INT *p, CS_INT *c, CS_INT n)
+{
+    CS_INT i, nz = 0 ;
+    double nz2 = 0 ;
+    if (!p || !c) return (-1) ;     /* check inputs */
+    for (i = 0 ; i < n ; i++)
+    {
+        p [i] = nz ;
+        nz += c [i] ;
+        nz2 += c [i] ;              /* also in double to avoid CS_INT overflow */
+        c [i] = p [i] ;             /* also copy p[0..n-1] back into c[0..n-1]*/
+    }
+    p [n] = nz ;
+    return (nz2) ;                  /* return sum (c [0..n-1]) */
+}
diff --git a/igraph/src/cs_dfs.c b/igraph/src/cs_dfs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_dfs.c
@@ -0,0 +1,56 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* depth-first-search of the graph of a matrix, starting at node j */
+CS_INT cs_dfs (CS_INT j, cs *G, CS_INT top, CS_INT *xi, CS_INT *pstack, const CS_INT *pinv)
+{
+    CS_INT i, p, p2, done, jnew, head = 0, *Gp, *Gi ;
+    if (!CS_CSC (G) || !xi || !pstack) return (-1) ;    /* check inputs */
+    Gp = G->p ; Gi = G->i ;
+    xi [0] = j ;                /* initialize the recursion stack */
+    while (head >= 0)
+    {
+        j = xi [head] ;         /* get j from the top of the recursion stack */
+        jnew = pinv ? (pinv [j]) : j ;
+        if (!CS_MARKED (Gp, j))
+        {
+            CS_MARK (Gp, j) ;       /* mark node j as visited */
+            pstack [head] = (jnew < 0) ? 0 : CS_UNFLIP (Gp [jnew]) ;
+        }
+        done = 1 ;                  /* node j done if no unvisited neighbors */
+        p2 = (jnew < 0) ? 0 : CS_UNFLIP (Gp [jnew+1]) ;
+        for (p = pstack [head] ; p < p2 ; p++)  /* examine all neighbors of j */
+        {
+            i = Gi [p] ;            /* consider neighbor node i */
+            if (CS_MARKED (Gp, i)) continue ;   /* skip visited node i */
+            pstack [head] = p ;     /* pause depth-first search of node j */
+            xi [++head] = i ;       /* start dfs at node i */
+            done = 0 ;              /* node j is not done */
+            break ;                 /* break, to start dfs (i) */
+        }
+        if (done)               /* depth-first search at node j is done */
+        {
+            head-- ;            /* remove j from the recursion stack */
+            xi [--top] = j ;    /* and place in the output stack */
+        }
+    }
+    return (top) ;
+}
diff --git a/igraph/src/cs_dmperm.c b/igraph/src/cs_dmperm.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_dmperm.c
@@ -0,0 +1,164 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* breadth-first search for coarse decomposition (C0,C1,R1 or R0,R3,C3) */
+static CS_INT cs_bfs (const cs *A, CS_INT n, CS_INT *wi, CS_INT *wj, CS_INT *queue,
+    const CS_INT *imatch, const CS_INT *jmatch, CS_INT mark)
+{
+    CS_INT *Ap, *Ai, head = 0, tail = 0, j, i, p, j2 ;
+    cs *C ;
+    for (j = 0 ; j < n ; j++)           /* place all unmatched nodes in queue */
+    {
+        if (imatch [j] >= 0) continue ; /* skip j if matched */
+        wj [j] = 0 ;                    /* j in set C0 (R0 if transpose) */
+        queue [tail++] = j ;            /* place unmatched col j in queue */
+    }
+    if (tail == 0) return (1) ;         /* quick return if no unmatched nodes */
+    C = (mark == 1) ? ((cs *) A) : cs_transpose (A, 0) ;
+    if (!C) return (0) ;                /* bfs of C=A' to find R3,C3 from R0 */
+    Ap = C->p ; Ai = C->i ;
+    while (head < tail)                 /* while queue is not empty */
+    {
+        j = queue [head++] ;            /* get the head of the queue */
+        for (p = Ap [j] ; p < Ap [j+1] ; p++)
+        {
+            i = Ai [p] ;
+            if (wi [i] >= 0) continue ; /* skip if i is marked */
+            wi [i] = mark ;             /* i in set R1 (C3 if transpose) */
+            j2 = jmatch [i] ;           /* traverse alternating path to j2 */
+            if (wj [j2] >= 0) continue ;/* skip j2 if it is marked */
+            wj [j2] = mark ;            /* j2 in set C1 (R3 if transpose) */
+            queue [tail++] = j2 ;       /* add j2 to queue */
+        }
+    }
+    if (mark != 1) cs_spfree (C) ;      /* free A' if it was created */
+    return (1) ;
+}
+
+/* collect matched rows and columns into p and q */
+static void cs_matched (CS_INT n, const CS_INT *wj, const CS_INT *imatch, CS_INT *p, CS_INT *q,
+    CS_INT *cc, CS_INT *rr, CS_INT set, CS_INT mark)
+{
+    CS_INT kc = cc [set], j ;
+    CS_INT kr = rr [set-1] ;
+    for (j = 0 ; j < n ; j++)
+    {
+        if (wj [j] != mark) continue ;      /* skip if j is not in C set */
+        p [kr++] = imatch [j] ;
+        q [kc++] = j ;
+    }
+    cc [set+1] = kc ;
+    rr [set] = kr ;
+}
+
+/* collect unmatched rows into the permutation vector p */
+static void cs_unmatched (CS_INT m, const CS_INT *wi, CS_INT *p, CS_INT *rr, CS_INT set)
+{
+    CS_INT i, kr = rr [set] ;
+    for (i = 0 ; i < m ; i++) if (wi [i] == 0) p [kr++] = i ;
+    rr [set+1] = kr ;
+}
+
+/* return 1 if row i is in R2 */
+static CS_INT cs_rprune (CS_INT i, CS_INT j, CS_ENTRY aij, void *other)
+{
+    CS_INT *rr = (CS_INT *) other ;
+    return (i >= rr [1] && i < rr [2]) ;
+}
+
+/* Given A, compute coarse and then fine dmperm */
+csd *cs_dmperm (const cs *A, CS_INT seed)
+{
+    CS_INT m, n, i, j, k, cnz, nc, *jmatch, *imatch, *wi, *wj, *pinv, *Cp, *Ci,
+        *ps, *rs, nb1, nb2, *p, *q, *cc, *rr, *r, *s, ok ;
+    cs *C ;
+    csd *D, *scc ;
+    /* --- Maximum matching ------------------------------------------------- */
+    if (!CS_CSC (A)) return (NULL) ;            /* check inputs */
+    m = A->m ; n = A->n ;
+    D = cs_dalloc (m, n) ;                      /* allocate result */
+    if (!D) return (NULL) ;
+    p = D->p ; q = D->q ; r = D->r ; s = D->s ; cc = D->cc ; rr = D->rr ;
+    jmatch = cs_maxtrans (A, seed) ;            /* max transversal */
+    imatch = jmatch + m ;                       /* imatch = inverse of jmatch */
+    if (!jmatch) return (cs_ddone (D, NULL, jmatch, 0)) ;
+    /* --- Coarse decomposition --------------------------------------------- */
+    wi = r ; wj = s ;                           /* use r and s as workspace */
+    for (j = 0 ; j < n ; j++) wj [j] = -1 ;     /* unmark all cols for bfs */
+    for (i = 0 ; i < m ; i++) wi [i] = -1 ;     /* unmark all rows for bfs */
+    cs_bfs (A, n, wi, wj, q, imatch, jmatch, 1) ;       /* find C1, R1 from C0*/
+    ok = cs_bfs (A, m, wj, wi, p, jmatch, imatch, 3) ;  /* find R3, C3 from R0*/
+    if (!ok) return (cs_ddone (D, NULL, jmatch, 0)) ;
+    cs_unmatched (n, wj, q, cc, 0) ;                    /* unmatched set C0 */
+    cs_matched (n, wj, imatch, p, q, cc, rr, 1, 1) ;    /* set R1 and C1 */
+    cs_matched (n, wj, imatch, p, q, cc, rr, 2, -1) ;   /* set R2 and C2 */
+    cs_matched (n, wj, imatch, p, q, cc, rr, 3, 3) ;    /* set R3 and C3 */
+    cs_unmatched (m, wi, p, rr, 3) ;                    /* unmatched set R0 */
+    cs_free (jmatch) ;
+    /* --- Fine decomposition ----------------------------------------------- */
+    pinv = cs_pinv (p, m) ;         /* pinv=p' */
+    if (!pinv) return (cs_ddone (D, NULL, NULL, 0)) ;
+    C = cs_permute (A, pinv, q, 0) ;/* C=A(p,q) (it will hold A(R2,C2)) */
+    cs_free (pinv) ;
+    if (!C) return (cs_ddone (D, NULL, NULL, 0)) ;
+    Cp = C->p ;
+    nc = cc [3] - cc [2] ;          /* delete cols C0, C1, and C3 from C */
+    if (cc [2] > 0) for (j = cc [2] ; j <= cc [3] ; j++) Cp [j-cc[2]] = Cp [j] ;
+    C->n = nc ;
+    if (rr [2] - rr [1] < m)        /* delete rows R0, R1, and R3 from C */
+    {
+        cs_fkeep (C, cs_rprune, rr) ;
+        cnz = Cp [nc] ;
+        Ci = C->i ;
+        if (rr [1] > 0) for (k = 0 ; k < cnz ; k++) Ci [k] -= rr [1] ;
+    }
+    C->m = nc ;
+    scc = cs_scc (C) ;              /* find strongly connected components of C*/
+    if (!scc) return (cs_ddone (D, C, NULL, 0)) ;
+    /* --- Combine coarse and fine decompositions --------------------------- */
+    ps = scc->p ;                   /* C(ps,ps) is the permuted matrix */
+    rs = scc->r ;                   /* kth block is rs[k]..rs[k+1]-1 */
+    nb1 = scc->nb  ;                /* # of blocks of A(R2,C2) */
+    for (k = 0 ; k < nc ; k++) wj [k] = q [ps [k] + cc [2]] ;
+    for (k = 0 ; k < nc ; k++) q [k + cc [2]] = wj [k] ;
+    for (k = 0 ; k < nc ; k++) wi [k] = p [ps [k] + rr [1]] ;
+    for (k = 0 ; k < nc ; k++) p [k + rr [1]] = wi [k] ;
+    nb2 = 0 ;                       /* create the fine block partitions */
+    r [0] = s [0] = 0 ;
+    if (cc [2] > 0) nb2++ ;         /* leading coarse block A (R1, [C0 C1]) */
+    for (k = 0 ; k < nb1 ; k++)     /* coarse block A (R2,C2) */
+    {
+        r [nb2] = rs [k] + rr [1] ; /* A (R2,C2) splits into nb1 fine blocks */
+        s [nb2] = rs [k] + cc [2] ;
+        nb2++ ;
+    }
+    if (rr [2] < m)
+    {
+        r [nb2] = rr [2] ;          /* trailing coarse block A ([R3 R0], C3) */
+        s [nb2] = cc [3] ;
+        nb2++ ;
+    }
+    r [nb2] = m ;
+    s [nb2] = n ;
+    D->nb = nb2 ;
+    cs_dfree (scc) ;
+    return (cs_ddone (D, C, NULL, 1)) ;
+}
diff --git a/igraph/src/cs_droptol.c b/igraph/src/cs_droptol.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_droptol.c
@@ -0,0 +1,29 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+static CS_INT cs_tol (CS_INT i, CS_INT j, CS_ENTRY aij, void *tol)
+{
+    return (CS_ABS (aij) > *((double *) tol)) ;
+}
+CS_INT cs_droptol (cs *A, double tol)
+{
+    return (cs_fkeep (A, &cs_tol, &tol)) ;    /* keep all large entries */
+}
diff --git a/igraph/src/cs_dropzeros.c b/igraph/src/cs_dropzeros.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_dropzeros.c
@@ -0,0 +1,29 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+static CS_INT cs_nonzero (CS_INT i, CS_INT j, CS_ENTRY aij, void *other)
+{
+    return (aij != 0) ;
+}
+CS_INT cs_dropzeros (cs *A)
+{
+    return (cs_fkeep (A, &cs_nonzero, NULL)) ;  /* keep all nonzero entries */
+} 
diff --git a/igraph/src/cs_dupl.c b/igraph/src/cs_dupl.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_dupl.c
@@ -0,0 +1,54 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* remove duplicate entries from A */
+CS_INT cs_dupl (cs *A)
+{
+    CS_INT i, j, p, q, nz = 0, n, m, *Ap, *Ai, *w ;
+    CS_ENTRY *Ax ;
+    if (!CS_CSC (A)) return (0) ;               /* check inputs */
+    m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ;
+    w = cs_malloc (m, sizeof (CS_INT)) ;           /* get workspace */
+    if (!w) return (0) ;                        /* out of memory */
+    for (i = 0 ; i < m ; i++) w [i] = -1 ;      /* row i not yet seen */
+    for (j = 0 ; j < n ; j++)
+    {
+        q = nz ;                                /* column j will start at q */
+        for (p = Ap [j] ; p < Ap [j+1] ; p++)
+        {
+            i = Ai [p] ;                        /* A(i,j) is nonzero */
+            if (w [i] >= q)
+            {
+                Ax [w [i]] += Ax [p] ;          /* A(i,j) is a duplicate */
+            }
+            else
+            {
+                w [i] = nz ;                    /* record where row i occurs */
+                Ai [nz] = i ;                   /* keep A(i,j) */
+                Ax [nz++] = Ax [p] ;
+            }
+        }
+        Ap [j] = q ;                            /* record start of column j */
+    }
+    Ap [n] = nz ;                               /* finalize A */
+    cs_free (w) ;                               /* free workspace */
+    return (cs_sprealloc (A, 0)) ;              /* remove extra space from A */
+}
diff --git a/igraph/src/cs_entry.c b/igraph/src/cs_entry.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_entry.c
@@ -0,0 +1,33 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* add an entry to a triplet matrix; return 1 if ok, 0 otherwise */
+CS_INT cs_entry (cs *T, CS_INT i, CS_INT j, CS_ENTRY x)
+{
+    if (!CS_TRIPLET (T) || i < 0 || j < 0) return (0) ;     /* check inputs */
+    if (T->nz >= T->nzmax && !cs_sprealloc (T,2*(T->nzmax))) return (0) ;
+    if (T->x) T->x [T->nz] = x ;
+    T->i [T->nz] = i ;
+    T->p [T->nz++] = j ;
+    T->m = CS_MAX (T->m, i+1) ;
+    T->n = CS_MAX (T->n, j+1) ;
+    return (1) ;
+}
diff --git a/igraph/src/cs_ereach.c b/igraph/src/cs_ereach.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_ereach.c
@@ -0,0 +1,43 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* find nonzero pattern of Cholesky L(k,1:k-1) using etree and triu(A(:,k)) */
+CS_INT cs_ereach (const cs *A, CS_INT k, const CS_INT *parent, CS_INT *s, CS_INT *w)
+{
+    CS_INT i, p, n, len, top, *Ap, *Ai ;
+    if (!CS_CSC (A) || !parent || !s || !w) return (-1) ;   /* check inputs */
+    top = n = A->n ; Ap = A->p ; Ai = A->i ;
+    CS_MARK (w, k) ;                /* mark node k as visited */
+    for (p = Ap [k] ; p < Ap [k+1] ; p++)
+    {
+        i = Ai [p] ;                /* A(i,k) is nonzero */
+        if (i > k) continue ;       /* only use upper triangular part of A */
+        for (len = 0 ; !CS_MARKED (w,i) ; i = parent [i]) /* traverse up etree*/
+        {
+            s [len++] = i ;         /* L(k,i) is nonzero */
+            CS_MARK (w, i) ;        /* mark i as visited */
+        }
+        while (len > 0) s [--top] = s [--len] ; /* push path onto stack */
+    }
+    for (p = top ; p < n ; p++) CS_MARK (w, s [p]) ;    /* unmark all nodes */
+    CS_MARK (w, k) ;                /* unmark node k */
+    return (top) ;                  /* s [top..n-1] contains pattern of L(k,:)*/
+}
diff --git a/igraph/src/cs_etree.c b/igraph/src/cs_etree.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_etree.c
@@ -0,0 +1,50 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* compute the etree of A (using triu(A), or A'A without forming A'A */
+CS_INT *cs_etree (const cs *A, CS_INT ata)
+{
+    CS_INT i, k, p, m, n, inext, *Ap, *Ai, *w, *parent, *ancestor, *prev ;
+    if (!CS_CSC (A)) return (NULL) ;        /* check inputs */
+    m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ;
+    parent = cs_malloc (n, sizeof (CS_INT)) ;              /* allocate result */
+    w = cs_malloc (n + (ata ? m : 0), sizeof (CS_INT)) ;   /* get workspace */
+    if (!w || !parent) return (cs_idone (parent, NULL, w, 0)) ;
+    ancestor = w ; prev = w + n ;
+    if (ata) for (i = 0 ; i < m ; i++) prev [i] = -1 ;
+    for (k = 0 ; k < n ; k++)
+    {
+        parent [k] = -1 ;                   /* node k has no parent yet */
+        ancestor [k] = -1 ;                 /* nor does k have an ancestor */
+        for (p = Ap [k] ; p < Ap [k+1] ; p++)
+        {
+            i = ata ? (prev [Ai [p]]) : (Ai [p]) ;
+            for ( ; i != -1 && i < k ; i = inext)   /* traverse from i to k */
+            {
+                inext = ancestor [i] ;              /* inext = ancestor of i */
+                ancestor [i] = k ;                  /* path compression */
+                if (inext == -1) parent [i] = k ;   /* no anc., parent is k */
+            }
+            if (ata) prev [Ai [p]] = k ;
+        }
+    }
+    return (cs_idone (parent, NULL, w, 1)) ;
+}
diff --git a/igraph/src/cs_fkeep.c b/igraph/src/cs_fkeep.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_fkeep.c
@@ -0,0 +1,45 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* drop entries for which fkeep(A(i,j)) is false; return nz if OK, else -1 */
+CS_INT cs_fkeep (cs *A, CS_INT (*fkeep) (CS_INT, CS_INT, CS_ENTRY, void *), void *other)
+{
+    CS_INT j, p, nz = 0, n, *Ap, *Ai ;
+    CS_ENTRY *Ax ;
+    if (!CS_CSC (A) || !fkeep) return (-1) ;    /* check inputs */
+    n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ;
+    for (j = 0 ; j < n ; j++)
+    {
+        p = Ap [j] ;                        /* get current location of col j */
+        Ap [j] = nz ;                       /* record new location of col j */
+        for ( ; p < Ap [j+1] ; p++)
+        {
+            if (fkeep (Ai [p], j, Ax ? Ax [p] : 1, other))
+            {
+                if (Ax) Ax [nz] = Ax [p] ;  /* keep A(i,j) */
+                Ai [nz++] = Ai [p] ;
+            }
+        }
+    }
+    Ap [n] = nz ;                           /* finalize A */
+    cs_sprealloc (A, 0) ;                   /* remove extra space from A */
+    return (nz) ;
+}
diff --git a/igraph/src/cs_gaxpy.c b/igraph/src/cs_gaxpy.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_gaxpy.c
@@ -0,0 +1,37 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* y = A*x+y */
+CS_INT cs_gaxpy (const cs *A, const CS_ENTRY *x, CS_ENTRY *y)
+{
+    CS_INT p, j, n, *Ap, *Ai ;
+    CS_ENTRY *Ax ;
+    if (!CS_CSC (A) || !x || !y) return (0) ;       /* check inputs */
+    n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ;
+    for (j = 0 ; j < n ; j++)
+    {
+        for (p = Ap [j] ; p < Ap [j+1] ; p++)
+        {
+            y [Ai [p]] += Ax [p] * x [j] ;
+        }
+    }
+    return (1) ;
+}
diff --git a/igraph/src/cs_happly.c b/igraph/src/cs_happly.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_happly.c
@@ -0,0 +1,39 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* apply the ith Householder vector to x */
+CS_INT cs_happly (const cs *V, CS_INT i, double beta, CS_ENTRY *x)
+{
+    CS_INT p, *Vp, *Vi ;
+    CS_ENTRY *Vx, tau = 0 ;
+    if (!CS_CSC (V) || !x) return (0) ;     /* check inputs */
+    Vp = V->p ; Vi = V->i ; Vx = V->x ;
+    for (p = Vp [i] ; p < Vp [i+1] ; p++)   /* tau = v'*x */
+    {
+        tau += CS_CONJ (Vx [p]) * x [Vi [p]] ;
+    }
+    tau *= beta ;                           /* tau = beta*(v'*x) */
+    for (p = Vp [i] ; p < Vp [i+1] ; p++)   /* x = x - v*tau */
+    {
+        x [Vi [p]] -= Vx [p] * tau ;
+    }
+    return (1) ;
+}
diff --git a/igraph/src/cs_house.c b/igraph/src/cs_house.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_house.c
@@ -0,0 +1,50 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* create a Householder reflection [v,beta,s]=house(x), overwrite x with v,
+ * where (I-beta*v*v')*x = s*e1 and e1 = [1 0 ... 0]'.
+ * Note that this CXSparse version is different than CSparse.  See Higham,
+ * Accuracy & Stability of Num Algorithms, 2nd ed, 2002, page 357. */
+CS_ENTRY cs_house (CS_ENTRY *x, double *beta, CS_INT n)
+{
+    CS_ENTRY s = 0 ;
+    CS_INT i ;
+    if (!x || !beta) return (-1) ;          /* check inputs */
+    /* s = norm(x) */
+    for (i = 0 ; i < n ; i++) s += x [i] * CS_CONJ (x [i]) ;
+    s = sqrt (s) ;
+    if (s == 0)
+    {
+        (*beta) = 0 ;
+        x [0] = 1 ;
+    }
+    else
+    {
+        /* s = sign(x[0]) * norm (x) ; */
+        if (x [0] != 0)
+        {
+            s *= x [0] / CS_ABS (x [0]) ;
+        }
+        x [0] += s ;
+        (*beta) = 1. / CS_REAL (CS_CONJ (s) * x [0]) ;
+    }
+    return (-s) ;
+}
diff --git a/igraph/src/cs_ipvec.c b/igraph/src/cs_ipvec.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_ipvec.c
@@ -0,0 +1,29 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* x(p) = b, for dense vectors x and b; p=NULL denotes identity */
+CS_INT cs_ipvec (const CS_INT *p, const CS_ENTRY *b, CS_ENTRY *x, CS_INT n)
+{
+    CS_INT k ;
+    if (!x || !b) return (0) ;                              /* check inputs */
+    for (k = 0 ; k < n ; k++) x [p ? p [k] : k] = b [k] ;
+    return (1) ;
+}
diff --git a/igraph/src/cs_leaf.c b/igraph/src/cs_leaf.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_leaf.c
@@ -0,0 +1,42 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* consider A(i,j), node j in ith row subtree and return lca(jprev,j) */
+CS_INT cs_leaf (CS_INT i, CS_INT j, const CS_INT *first, CS_INT *maxfirst, CS_INT *prevleaf,
+    CS_INT *ancestor, CS_INT *jleaf)
+{
+    CS_INT q, s, sparent, jprev ;
+    if (!first || !maxfirst || !prevleaf || !ancestor || !jleaf) return (-1) ;
+    *jleaf = 0 ;
+    if (i <= j || first [j] <= maxfirst [i]) return (-1) ;  /* j not a leaf */
+    maxfirst [i] = first [j] ;      /* update max first[j] seen so far */
+    jprev = prevleaf [i] ;          /* jprev = previous leaf of ith subtree */
+    prevleaf [i] = j ;
+    *jleaf = (jprev == -1) ? 1: 2 ; /* j is first or subsequent leaf */
+    if (*jleaf == 1) return (i) ;   /* if 1st leaf, q = root of ith subtree */
+    for (q = jprev ; q != ancestor [q] ; q = ancestor [q]) ;
+    for (s = jprev ; s != q ; s = sparent)
+    {
+        sparent = ancestor [s] ;    /* path compression */
+        ancestor [s] = q ;
+    }
+    return (q) ;                    /* q = least common ancester (jprev,j) */
+}
diff --git a/igraph/src/cs_load.c b/igraph/src/cs_load.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_load.c
@@ -0,0 +1,46 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* load a triplet matrix from a file */
+cs *cs_load (FILE *f)
+{
+    CS_INT i, j ;
+    double x ;
+#ifdef CS_COMPLEX
+    double xi ;
+#endif
+    cs *T ;
+    if (!f) return (NULL) ;                             /* check inputs */
+    T = cs_spalloc (0, 0, 1, 1, 1) ;                    /* allocate result */
+#ifdef CS_COMPLEX
+    while (fscanf (f, ""CS_ID" "CS_ID" %lg %lg\n", &i, &j, &x, &xi) == 4)
+#else
+    while (fscanf (f, ""CS_ID" "CS_ID" %lg\n", &i, &j, &x) == 3)
+#endif
+    {
+#ifdef CS_COMPLEX
+        if (!cs_entry (T, i, j, x + xi*I)) return (cs_spfree (T)) ;
+#else
+        if (!cs_entry (T, i, j, x)) return (cs_spfree (T)) ;
+#endif
+    }
+    return (T) ;
+}
diff --git a/igraph/src/cs_lsolve.c b/igraph/src/cs_lsolve.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_lsolve.c
@@ -0,0 +1,38 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* solve Lx=b where x and b are dense.  x=b on input, solution on output. */
+CS_INT cs_lsolve (const cs *L, CS_ENTRY *x)
+{
+    CS_INT p, j, n, *Lp, *Li ;
+    CS_ENTRY *Lx ;
+    if (!CS_CSC (L) || !x) return (0) ;                     /* check inputs */
+    n = L->n ; Lp = L->p ; Li = L->i ; Lx = L->x ;
+    for (j = 0 ; j < n ; j++)
+    {
+        x [j] /= Lx [Lp [j]] ;
+        for (p = Lp [j]+1 ; p < Lp [j+1] ; p++)
+        {
+            x [Li [p]] -= Lx [p] * x [j] ;
+        }
+    }
+    return (1) ;
+}
diff --git a/igraph/src/cs_ltsolve.c b/igraph/src/cs_ltsolve.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_ltsolve.c
@@ -0,0 +1,38 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* solve L'x=b where x and b are dense.  x=b on input, solution on output. */
+CS_INT cs_ltsolve (const cs *L, CS_ENTRY *x)
+{
+    CS_INT p, j, n, *Lp, *Li ;
+    CS_ENTRY *Lx ;
+    if (!CS_CSC (L) || !x) return (0) ;                     /* check inputs */
+    n = L->n ; Lp = L->p ; Li = L->i ; Lx = L->x ;
+    for (j = n-1 ; j >= 0 ; j--)
+    {
+        for (p = Lp [j]+1 ; p < Lp [j+1] ; p++)
+        {
+            x [j] -= CS_CONJ (Lx [p]) * x [Li [p]] ;
+        }
+        x [j] /= CS_CONJ (Lx [Lp [j]]) ;
+    }
+    return (1) ;
+}
diff --git a/igraph/src/cs_lu.c b/igraph/src/cs_lu.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_lu.c
@@ -0,0 +1,107 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* [L,U,pinv]=lu(A, [q lnz unz]). lnz and unz can be guess */
+csn *cs_lu (const cs *A, const css *S, double tol)
+{
+    cs *L, *U ;
+    csn *N ;
+    CS_ENTRY pivot, *Lx, *Ux, *x ;
+    double a, t ;
+    CS_INT *Lp, *Li, *Up, *Ui, *pinv, *xi, *q, n, ipiv, k, top, p, i, col, lnz,unz;
+    if (!CS_CSC (A) || !S) return (NULL) ;          /* check inputs */
+    n = A->n ;
+    q = S->q ; lnz = S->lnz ; unz = S->unz ;
+    x = cs_malloc (n, sizeof (CS_ENTRY)) ;            /* get CS_ENTRY workspace */
+    xi = cs_malloc (2*n, sizeof (CS_INT)) ;            /* get CS_INT workspace */
+    N = cs_calloc (1, sizeof (csn)) ;               /* allocate result */
+    if (!x || !xi || !N) return (cs_ndone (N, NULL, xi, x, 0)) ;
+    N->L = L = cs_spalloc (n, n, lnz, 1, 0) ;       /* allocate result L */
+    N->U = U = cs_spalloc (n, n, unz, 1, 0) ;       /* allocate result U */
+    N->pinv = pinv = cs_malloc (n, sizeof (CS_INT)) ;  /* allocate result pinv */
+    if (!L || !U || !pinv) return (cs_ndone (N, NULL, xi, x, 0)) ;
+    Lp = L->p ; Up = U->p ;
+    for (i = 0 ; i < n ; i++) x [i] = 0 ;           /* clear workspace */
+    for (i = 0 ; i < n ; i++) pinv [i] = -1 ;       /* no rows pivotal yet */
+    for (k = 0 ; k <= n ; k++) Lp [k] = 0 ;         /* no cols of L yet */
+    lnz = unz = 0 ;
+    for (k = 0 ; k < n ; k++)       /* compute L(:,k) and U(:,k) */
+    {
+        /* --- Triangular solve --------------------------------------------- */
+        Lp [k] = lnz ;              /* L(:,k) starts here */
+        Up [k] = unz ;              /* U(:,k) starts here */
+        if ((lnz + n > L->nzmax && !cs_sprealloc (L, 2*L->nzmax + n)) ||
+            (unz + n > U->nzmax && !cs_sprealloc (U, 2*U->nzmax + n)))
+        {
+            return (cs_ndone (N, NULL, xi, x, 0)) ;
+        }
+        Li = L->i ; Lx = L->x ; Ui = U->i ; Ux = U->x ;
+        col = q ? (q [k]) : k ;
+        top = cs_spsolve (L, A, col, xi, x, pinv, 1) ;  /* x = L\A(:,col) */
+        /* --- Find pivot --------------------------------------------------- */
+        ipiv = -1 ;
+        a = -1 ;
+        for (p = top ; p < n ; p++)
+        {
+            i = xi [p] ;            /* x(i) is nonzero */
+            if (pinv [i] < 0)       /* row i is not yet pivotal */
+            {
+                if ((t = CS_ABS (x [i])) > a)
+                {
+                    a = t ;         /* largest pivot candidate so far */
+                    ipiv = i ;
+                }
+            }
+            else                    /* x(i) is the entry U(pinv[i],k) */
+            {
+                Ui [unz] = pinv [i] ;
+                Ux [unz++] = x [i] ;
+            }
+        }
+        if (ipiv == -1 || a <= 0) return (cs_ndone (N, NULL, xi, x, 0)) ;
+        if (pinv [col] < 0 && CS_ABS (x [col]) >= a*tol) ipiv = col ;
+        /* --- Divide by pivot ---------------------------------------------- */
+        pivot = x [ipiv] ;          /* the chosen pivot */
+        Ui [unz] = k ;              /* last entry in U(:,k) is U(k,k) */
+        Ux [unz++] = pivot ;
+        pinv [ipiv] = k ;           /* ipiv is the kth pivot row */
+        Li [lnz] = ipiv ;           /* first entry in L(:,k) is L(k,k) = 1 */
+        Lx [lnz++] = 1 ;
+        for (p = top ; p < n ; p++) /* L(k+1:n,k) = x / pivot */
+        {
+            i = xi [p] ;
+            if (pinv [i] < 0)       /* x(i) is an entry in L(:,k) */
+            {
+                Li [lnz] = i ;      /* save unpermuted row in L */
+                Lx [lnz++] = x [i] / pivot ;    /* scale pivot column */
+            }
+            x [i] = 0 ;             /* x [0..n-1] = 0 for next k */
+        }
+    }
+    /* --- Finalize L and U ------------------------------------------------- */
+    Lp [n] = lnz ;
+    Up [n] = unz ;
+    Li = L->i ;                     /* fix row indices of L for final pinv */
+    for (p = 0 ; p < lnz ; p++) Li [p] = pinv [Li [p]] ;
+    cs_sprealloc (L, 0) ;           /* remove extra space from L and U */
+    cs_sprealloc (U, 0) ;
+    return (cs_ndone (N, NULL, xi, x, 1)) ;     /* success */
+}
diff --git a/igraph/src/cs_lusol.c b/igraph/src/cs_lusol.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_lusol.c
@@ -0,0 +1,46 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* x=A\b where A is unsymmetric; b overwritten with solution */
+CS_INT cs_lusol (CS_INT order, const cs *A, CS_ENTRY *b, double tol)
+{
+    CS_ENTRY *x ;
+    css *S ;
+    csn *N ;
+    CS_INT n, ok ;
+    if (!CS_CSC (A) || !b) return (0) ;     /* check inputs */
+    n = A->n ;
+    S = cs_sqr (order, A, 0) ;              /* ordering and symbolic analysis */
+    N = cs_lu (A, S, tol) ;                 /* numeric LU factorization */
+    x = cs_malloc (n, sizeof (CS_ENTRY)) ;    /* get workspace */
+    ok = (S && N && x) ;
+    if (ok)
+    {
+        cs_ipvec (N->pinv, b, x, n) ;       /* x = b(p) */
+        cs_lsolve (N->L, x) ;               /* x = L\x */
+        cs_usolve (N->U, x) ;               /* x = U\x */
+        cs_ipvec (S->q, x, b, n) ;          /* b(q) = x */
+    }
+    cs_free (x) ;
+    cs_sfree (S) ;
+    cs_nfree (N) ;
+    return (ok) ;
+}
diff --git a/igraph/src/cs_malloc.c b/igraph/src/cs_malloc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_malloc.c
@@ -0,0 +1,55 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+#ifdef MATLAB_MEX_FILE
+#define malloc mxMalloc
+#define free mxFree
+#define realloc mxRealloc
+#define calloc mxCalloc
+#endif
+
+/* wrapper for malloc */
+void *cs_malloc (CS_INT n, size_t size)
+{
+    return (malloc (CS_MAX (n,1) * size)) ;
+}
+
+/* wrapper for calloc */
+void *cs_calloc (CS_INT n, size_t size)
+{
+    return (calloc (CS_MAX (n,1), size)) ;
+}
+
+/* wrapper for free */
+void *cs_free (void *p)
+{
+    if (p) free (p) ;       /* free p if it is not already NULL */
+    return (NULL) ;         /* return NULL to simplify the use of cs_free */
+}
+
+/* wrapper for realloc */
+void *cs_realloc (void *p, CS_INT n, size_t size, CS_INT *ok)
+{
+    void *pnew ;
+    pnew = realloc (p, CS_MAX (n,1) * size) ; /* realloc the block */
+    *ok = (pnew != NULL) ;                  /* realloc fails if pnew is NULL */
+    return ((*ok) ? pnew : p) ;             /* return original p if failure */
+}
diff --git a/igraph/src/cs_maxtrans.c b/igraph/src/cs_maxtrans.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_maxtrans.c
@@ -0,0 +1,112 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* find an augmenting path starting at column k and extend the match if found */
+static void cs_augment (CS_INT k, const cs *A, CS_INT *jmatch, CS_INT *cheap, CS_INT *w,
+        CS_INT *js, CS_INT *is, CS_INT *ps)
+{
+    CS_INT found = 0, p, i = -1, *Ap = A->p, *Ai = A->i, head = 0, j ;
+    js [0] = k ;                        /* start with just node k in jstack */
+    while (head >= 0)
+    {
+        /* --- Start (or continue) depth-first-search at node j ------------- */
+        j = js [head] ;                 /* get j from top of jstack */
+        if (w [j] != k)                 /* 1st time j visited for kth path */
+        {
+            w [j] = k ;                 /* mark j as visited for kth path */
+            for (p = cheap [j] ; p < Ap [j+1] && !found ; p++)
+            {
+                i = Ai [p] ;            /* try a cheap assignment (i,j) */
+                found = (jmatch [i] == -1) ;
+            }
+            cheap [j] = p ;             /* start here next time j is traversed*/
+            if (found)
+            {
+                is [head] = i ;         /* column j matched with row i */
+                break ;                 /* end of augmenting path */
+            }
+            ps [head] = Ap [j] ;        /* no cheap match: start dfs for j */
+        }
+        /* --- Depth-first-search of neighbors of j ------------------------- */
+        for (p = ps [head] ; p < Ap [j+1] ; p++)
+        {
+            i = Ai [p] ;                /* consider row i */
+            if (w [jmatch [i]] == k) continue ; /* skip jmatch [i] if marked */
+            ps [head] = p + 1 ;         /* pause dfs of node j */
+            is [head] = i ;             /* i will be matched with j if found */
+            js [++head] = jmatch [i] ;  /* start dfs at column jmatch [i] */
+            break ;
+        }
+        if (p == Ap [j+1]) head-- ;     /* node j is done; pop from stack */
+    }                                   /* augment the match if path found: */
+    if (found) for (p = head ; p >= 0 ; p--) jmatch [is [p]] = js [p] ;
+}
+
+/* find a maximum transveral */
+CS_INT *cs_maxtrans (const cs *A, CS_INT seed)  /*[jmatch [0..m-1]; imatch [0..n-1]]*/
+{
+    CS_INT i, j, k, n, m, p, n2 = 0, m2 = 0, *Ap, *jimatch, *w, *cheap, *js, *is,
+        *ps, *Ai, *Cp, *jmatch, *imatch, *q ;
+    cs *C ;
+    if (!CS_CSC (A)) return (NULL) ;                /* check inputs */
+    n = A->n ; m = A->m ; Ap = A->p ; Ai = A->i ;
+    w = jimatch = cs_calloc (m+n, sizeof (CS_INT)) ;   /* allocate result */
+    if (!jimatch) return (NULL) ;
+    for (k = 0, j = 0 ; j < n ; j++)    /* count nonempty rows and columns */
+    {
+        n2 += (Ap [j] < Ap [j+1]) ;
+        for (p = Ap [j] ; p < Ap [j+1] ; p++)
+        {
+            w [Ai [p]] = 1 ;
+            k += (j == Ai [p]) ;        /* count entries already on diagonal */
+        }
+    }
+    if (k == CS_MIN (m,n))              /* quick return if diagonal zero-free */
+    {
+        jmatch = jimatch ; imatch = jimatch + m ;
+        for (i = 0 ; i < k ; i++) jmatch [i] = i ;
+        for (      ; i < m ; i++) jmatch [i] = -1 ;
+        for (j = 0 ; j < k ; j++) imatch [j] = j ;
+        for (      ; j < n ; j++) imatch [j] = -1 ;
+        return (cs_idone (jimatch, NULL, NULL, 1)) ;
+    }
+    for (i = 0 ; i < m ; i++) m2 += w [i] ;
+    C = (m2 < n2) ? cs_transpose (A,0) : ((cs *) A) ; /* transpose if needed */
+    if (!C) return (cs_idone (jimatch, (m2 < n2) ? C : NULL, NULL, 0)) ;
+    n = C->n ; m = C->m ; Cp = C->p ;
+    jmatch = (m2 < n2) ? jimatch + n : jimatch ;
+    imatch = (m2 < n2) ? jimatch : jimatch + m ;
+    w = cs_malloc (5*n, sizeof (CS_INT)) ;             /* get workspace */
+    if (!w) return (cs_idone (jimatch, (m2 < n2) ? C : NULL, w, 0)) ;
+    cheap = w + n ; js = w + 2*n ; is = w + 3*n ; ps = w + 4*n ;
+    for (j = 0 ; j < n ; j++) cheap [j] = Cp [j] ;  /* for cheap assignment */
+    for (j = 0 ; j < n ; j++) w [j] = -1 ;          /* all columns unflagged */
+    for (i = 0 ; i < m ; i++) jmatch [i] = -1 ;     /* nothing matched yet */
+    q = cs_randperm (n, seed) ;                     /* q = random permutation */
+    for (k = 0 ; k < n ; k++)   /* augment, starting at column q[k] */
+    {
+        cs_augment (q ? q [k]: k, C, jmatch, cheap, w, js, is, ps) ;
+    }
+    cs_free (q) ;
+    for (j = 0 ; j < n ; j++) imatch [j] = -1 ;     /* find row match */
+    for (i = 0 ; i < m ; i++) if (jmatch [i] >= 0) imatch [jmatch [i]] = i ;
+    return (cs_idone (jimatch, (m2 < n2) ? C : NULL, w, 1)) ;
+}
diff --git a/igraph/src/cs_multiply.c b/igraph/src/cs_multiply.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_multiply.c
@@ -0,0 +1,55 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* C = A*B */
+cs *cs_multiply (const cs *A, const cs *B)
+{
+    CS_INT p, j, nz = 0, anz, *Cp, *Ci, *Bp, m, n, bnz, *w, values, *Bi ;
+    CS_ENTRY *x, *Bx, *Cx ;
+    cs *C ;
+    if (!CS_CSC (A) || !CS_CSC (B)) return (NULL) ;      /* check inputs */
+    if (A->n != B->m) return (NULL) ;
+    m = A->m ; anz = A->p [A->n] ;
+    n = B->n ; Bp = B->p ; Bi = B->i ; Bx = B->x ; bnz = Bp [n] ;
+    w = cs_calloc (m, sizeof (CS_INT)) ;                    /* get workspace */
+    values = (A->x != NULL) && (Bx != NULL) ;
+    x = values ? cs_malloc (m, sizeof (CS_ENTRY)) : NULL ; /* get workspace */
+    C = cs_spalloc (m, n, anz + bnz, values, 0) ;        /* allocate result */
+    if (!C || !w || (values && !x)) return (cs_done (C, w, x, 0)) ;
+    Cp = C->p ;
+    for (j = 0 ; j < n ; j++)
+    {
+        if (nz + m > C->nzmax && !cs_sprealloc (C, 2*(C->nzmax)+m))
+        {
+            return (cs_done (C, w, x, 0)) ;             /* out of memory */
+        } 
+        Ci = C->i ; Cx = C->x ;         /* C->i and C->x may be reallocated */
+        Cp [j] = nz ;                   /* column j of C starts here */
+        for (p = Bp [j] ; p < Bp [j+1] ; p++)
+        {
+            nz = cs_scatter (A, Bi [p], Bx ? Bx [p] : 1, w, x, j+1, C, nz) ;
+        }
+        if (values) for (p = Cp [j] ; p < nz ; p++) Cx [p] = x [Ci [p]] ;
+    }
+    Cp [n] = nz ;                       /* finalize the last column of C */
+    cs_sprealloc (C, 0) ;               /* remove extra space from C */
+    return (cs_done (C, w, x, 1)) ;     /* success; free workspace, return C */
+}
diff --git a/igraph/src/cs_norm.c b/igraph/src/cs_norm.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_norm.c
@@ -0,0 +1,36 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* 1-norm of a sparse matrix = max (sum (abs (A))), largest column sum */
+double cs_norm (const cs *A)
+{
+    CS_INT p, j, n, *Ap ;
+    CS_ENTRY *Ax ;
+    double norm = 0, s ;
+    if (!CS_CSC (A) || !A->x) return (-1) ;             /* check inputs */
+    n = A->n ; Ap = A->p ; Ax = A->x ;
+    for (j = 0 ; j < n ; j++)
+    {
+        for (s = 0, p = Ap [j] ; p < Ap [j+1] ; p++) s += CS_ABS (Ax [p]) ;
+        norm = CS_MAX (norm, s) ;
+    }
+    return (norm) ;
+}
diff --git a/igraph/src/cs_permute.c b/igraph/src/cs_permute.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_permute.c
@@ -0,0 +1,45 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* C = A(p,q) where p and q are permutations of 0..m-1 and 0..n-1. */
+cs *cs_permute (const cs *A, const CS_INT *pinv, const CS_INT *q, CS_INT values)
+{
+    CS_INT t, j, k, nz = 0, m, n, *Ap, *Ai, *Cp, *Ci ;
+    CS_ENTRY *Cx, *Ax ;
+    cs *C ;
+    if (!CS_CSC (A)) return (NULL) ;    /* check inputs */
+    m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ;
+    C = cs_spalloc (m, n, Ap [n], values && Ax != NULL, 0) ;  /* alloc result */
+    if (!C) return (cs_done (C, NULL, NULL, 0)) ;   /* out of memory */
+    Cp = C->p ; Ci = C->i ; Cx = C->x ;
+    for (k = 0 ; k < n ; k++)
+    {
+        Cp [k] = nz ;                   /* column k of C is column q[k] of A */
+        j = q ? (q [k]) : k ;
+        for (t = Ap [j] ; t < Ap [j+1] ; t++)
+        {
+            if (Cx) Cx [nz] = Ax [t] ;  /* row i of A is row pinv[i] of C */
+            Ci [nz++] = pinv ? (pinv [Ai [t]]) : Ai [t] ;
+        }
+    }
+    Cp [n] = nz ;                       /* finalize the last column of C */
+    return (cs_done (C, NULL, NULL, 1)) ;
+}
diff --git a/igraph/src/cs_pinv.c b/igraph/src/cs_pinv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_pinv.c
@@ -0,0 +1,31 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* pinv = p', or p = pinv' */
+CS_INT *cs_pinv (CS_INT const *p, CS_INT n)
+{
+    CS_INT k, *pinv ;
+    if (!p) return (NULL) ;                     /* p = NULL denotes identity */
+    pinv = cs_malloc (n, sizeof (CS_INT)) ;        /* allocate result */
+    if (!pinv) return (NULL) ;                  /* out of memory */
+    for (k = 0 ; k < n ; k++) pinv [p [k]] = k ;/* invert the permutation */
+    return (pinv) ;                             /* return result */
+}
diff --git a/igraph/src/cs_post.c b/igraph/src/cs_post.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_post.c
@@ -0,0 +1,44 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* post order a forest */
+CS_INT *cs_post (const CS_INT *parent, CS_INT n)
+{
+    CS_INT j, k = 0, *post, *w, *head, *next, *stack ;
+    if (!parent) return (NULL) ;                        /* check inputs */
+    post = cs_malloc (n, sizeof (CS_INT)) ;                /* allocate result */
+    w = cs_malloc (3*n, sizeof (CS_INT)) ;                 /* get workspace */
+    if (!w || !post) return (cs_idone (post, NULL, w, 0)) ;
+    head = w ; next = w + n ; stack = w + 2*n ;
+    for (j = 0 ; j < n ; j++) head [j] = -1 ;           /* empty linked lists */
+    for (j = n-1 ; j >= 0 ; j--)            /* traverse nodes in reverse order*/
+    {
+        if (parent [j] == -1) continue ;    /* j is a root */
+        next [j] = head [parent [j]] ;      /* add j to list of its parent */
+        head [parent [j]] = j ;
+    }
+    for (j = 0 ; j < n ; j++)
+    {
+        if (parent [j] != -1) continue ;    /* skip j if it is not a root */
+        k = cs_tdfs (j, k, head, next, post, stack) ;
+    }
+    return (cs_idone (post, NULL, w, 1)) ;  /* success; free w, return post */
+}
diff --git a/igraph/src/cs_print.c b/igraph/src/cs_print.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_print.c
@@ -0,0 +1,66 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* print a sparse matrix */
+/* CS_INT cs_print (const cs *A, CS_INT brief) */
+/* { */
+/*     CS_INT p, j, m, n, nzmax, nz, *Ap, *Ai ; */
+/*     CS_ENTRY *Ax ; */
+/*     if (!A) { printf ("(null)\n") ; return (0) ; } */
+/*     m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ; */
+/*     nzmax = A->nzmax ; nz = A->nz ; */
+/*     printf ("CXSparse Version %d.%d.%d, %s.  %s\n", CS_VER, CS_SUBVER, */
+/*         CS_SUBSUB, CS_DATE, CS_COPYRIGHT) ; */
+/*     if (nz < 0) */
+/*     { */
+/*         printf (""CS_ID"-by-"CS_ID", nzmax: "CS_ID" nnz: "CS_ID", 1-norm: %g\n", m, n, nzmax, */
+/*                 Ap [n], cs_norm (A)) ; */
+/*         for (j = 0 ; j < n ; j++) */
+/*         { */
+/*             printf ("    col "CS_ID" : locations "CS_ID" to "CS_ID"\n", j, Ap [j], Ap [j+1]-1); */
+/*             for (p = Ap [j] ; p < Ap [j+1] ; p++) */
+/*             { */
+/* #ifdef CS_COMPLEX */
+/*                 printf ("      "CS_ID" : (%g, %g)\n", Ai [p],  */
+/* 		    Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; */
+/* #else */
+/*                 printf ("      "CS_ID" : %g\n", Ai [p], Ax ? Ax [p] : 1) ; */
+/* #endif */
+/*                 if (brief && p > 20) { printf ("  ...\n") ; return (1) ; } */
+/*             } */
+/*         } */
+/*     } */
+/*     else */
+/*     { */
+/*         printf ("triplet: "CS_ID"-by-"CS_ID", nzmax: "CS_ID" nnz: "CS_ID"\n", m, n, nzmax, nz) ; */
+/*         for (p = 0 ; p < nz ; p++) */
+/*         { */
+/* #ifdef CS_COMPLEX */
+/*             printf ("    "CS_ID" "CS_ID" : (%g, %g)\n", Ai [p], Ap [p],  */
+/* 		    Ax ? CS_REAL (Ax [p]) : 1, Ax ? CS_IMAG (Ax [p]) : 0) ; */
+/* #else */
+/*             printf ("    "CS_ID" "CS_ID" : %g\n", Ai [p], Ap [p], Ax ? Ax [p] : 1) ; */
+/* #endif */
+/*             if (brief && p > 20) { printf ("  ...\n") ; return (1) ; } */
+/*         } */
+/*     } */
+/*     return (1) ; */
+/* } */
diff --git a/igraph/src/cs_pvec.c b/igraph/src/cs_pvec.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_pvec.c
@@ -0,0 +1,29 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* x = b(p), for dense vectors x and b; p=NULL denotes identity */
+CS_INT cs_pvec (const CS_INT *p, const CS_ENTRY *b, CS_ENTRY *x, CS_INT n)
+{
+    CS_INT k ;
+    if (!x || !b) return (0) ;                              /* check inputs */
+    for (k = 0 ; k < n ; k++) x [k] = b [p ? p [k] : k] ;
+    return (1) ;
+}
diff --git a/igraph/src/cs_qr.c b/igraph/src/cs_qr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_qr.c
@@ -0,0 +1,94 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* sparse QR factorization [V,beta,pinv,R] = qr (A) */
+csn *cs_qr (const cs *A, const css *S)
+{
+    CS_ENTRY *Rx, *Vx, *Ax, *x ;
+    double *Beta ;
+    CS_INT i, k, p, m, n, vnz, p1, top, m2, len, col, rnz, *s, *leftmost, *Ap, *Ai,
+        *parent, *Rp, *Ri, *Vp, *Vi, *w, *pinv, *q ;
+    cs *R, *V ;
+    csn *N ;
+    if (!CS_CSC (A) || !S) return (NULL) ;
+    m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ;
+    q = S->q ; parent = S->parent ; pinv = S->pinv ; m2 = S->m2 ;
+    vnz = S->lnz ; rnz = S->unz ; leftmost = S->leftmost ;
+    w = cs_malloc (m2+n, sizeof (CS_INT)) ;            /* get CS_INT workspace */
+    x = cs_malloc (m2, sizeof (CS_ENTRY)) ;           /* get CS_ENTRY workspace */
+    N = cs_calloc (1, sizeof (csn)) ;               /* allocate result */
+    if (!w || !x || !N) return (cs_ndone (N, NULL, w, x, 0)) ;
+    s = w + m2 ;                                    /* s is size n */
+    for (k = 0 ; k < m2 ; k++) x [k] = 0 ;          /* clear workspace x */
+    N->L = V = cs_spalloc (m2, n, vnz, 1, 0) ;      /* allocate result V */
+    N->U = R = cs_spalloc (m2, n, rnz, 1, 0) ;      /* allocate result R */
+    N->B = Beta = cs_malloc (n, sizeof (double)) ;  /* allocate result Beta */
+    if (!R || !V || !Beta) return (cs_ndone (N, NULL, w, x, 0)) ;
+    Rp = R->p ; Ri = R->i ; Rx = R->x ;
+    Vp = V->p ; Vi = V->i ; Vx = V->x ;
+    for (i = 0 ; i < m2 ; i++) w [i] = -1 ; /* clear w, to mark nodes */
+    rnz = 0 ; vnz = 0 ;
+    for (k = 0 ; k < n ; k++)               /* compute V and R */
+    {
+        Rp [k] = rnz ;                      /* R(:,k) starts here */
+        Vp [k] = p1 = vnz ;                 /* V(:,k) starts here */
+        w [k] = k ;                         /* add V(k,k) to pattern of V */
+        Vi [vnz++] = k ;
+        top = n ;
+        col = q ? q [k] : k ;
+        for (p = Ap [col] ; p < Ap [col+1] ; p++)   /* find R(:,k) pattern */
+        {
+            i = leftmost [Ai [p]] ;         /* i = min(find(A(i,q))) */
+            for (len = 0 ; w [i] != k ; i = parent [i]) /* traverse up to k */
+            {
+                s [len++] = i ;
+                w [i] = k ;
+            }
+            while (len > 0) s [--top] = s [--len] ; /* push path on stack */
+            i = pinv [Ai [p]] ;             /* i = permuted row of A(:,col) */
+            x [i] = Ax [p] ;                /* x (i) = A(:,col) */
+            if (i > k && w [i] < k)         /* pattern of V(:,k) = x (k+1:m) */
+            {
+                Vi [vnz++] = i ;            /* add i to pattern of V(:,k) */
+                w [i] = k ;
+            }
+        }
+        for (p = top ; p < n ; p++) /* for each i in pattern of R(:,k) */
+        {
+            i = s [p] ;                     /* R(i,k) is nonzero */
+            cs_happly (V, i, Beta [i], x) ; /* apply (V(i),Beta(i)) to x */
+            Ri [rnz] = i ;                  /* R(i,k) = x(i) */
+            Rx [rnz++] = x [i] ;
+            x [i] = 0 ;
+            if (parent [i] == k) vnz = cs_scatter (V, i, 0, w, NULL, k, V, vnz);
+        }
+        for (p = p1 ; p < vnz ; p++)        /* gather V(:,k) = x */
+        {
+            Vx [p] = x [Vi [p]] ;
+            x [Vi [p]] = 0 ;
+        }
+        Ri [rnz] = k ;                     /* R(k,k) = norm (x) */
+        Rx [rnz++] = cs_house (Vx+p1, Beta+k, vnz-p1) ; /* [v,beta]=house(x) */
+    }
+    Rp [n] = rnz ;                          /* finalize R */
+    Vp [n] = vnz ;                          /* finalize V */
+    return (cs_ndone (N, NULL, w, x, 1)) ;  /* success */
+}
diff --git a/igraph/src/cs_qrsol.c b/igraph/src/cs_qrsol.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_qrsol.c
@@ -0,0 +1,73 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* x=A\b where A can be rectangular; b overwritten with solution */
+CS_INT cs_qrsol (CS_INT order, const cs *A, CS_ENTRY *b)
+{
+    CS_ENTRY *x ;
+    css *S ;
+    csn *N ;
+    cs *AT = NULL ;
+    CS_INT k, m, n, ok ;
+    if (!CS_CSC (A) || !b) return (0) ; /* check inputs */
+    n = A->n ;
+    m = A->m ;
+    if (m >= n)
+    {
+        S = cs_sqr (order, A, 1) ;          /* ordering and symbolic analysis */
+        N = cs_qr (A, S) ;                  /* numeric QR factorization */
+        x = cs_calloc (S ? S->m2 : 1, sizeof (CS_ENTRY)) ;    /* get workspace */
+        ok = (S && N && x) ;
+        if (ok)
+        {
+            cs_ipvec (S->pinv, b, x, m) ;   /* x(0:m-1) = b(p(0:m-1) */
+            for (k = 0 ; k < n ; k++)       /* apply Householder refl. to x */
+            {
+                cs_happly (N->L, k, N->B [k], x) ;
+            }
+            cs_usolve (N->U, x) ;           /* x = R\x */
+            cs_ipvec (S->q, x, b, n) ;      /* b(q(0:n-1)) = x(0:n-1) */
+        }
+    }
+    else
+    {
+        AT = cs_transpose (A, 1) ;          /* Ax=b is underdetermined */
+        S = cs_sqr (order, AT, 1) ;         /* ordering and symbolic analysis */
+        N = cs_qr (AT, S) ;                 /* numeric QR factorization of A' */
+        x = cs_calloc (S ? S->m2 : 1, sizeof (CS_ENTRY)) ;    /* get workspace */
+        ok = (AT && S && N && x) ;
+        if (ok)
+        {
+            cs_pvec (S->q, b, x, m) ;       /* x(q(0:m-1)) = b(0:m-1) */
+            cs_utsolve (N->U, x) ;          /* x = R'\x */
+            for (k = m-1 ; k >= 0 ; k--)    /* apply Householder refl. to x */
+            {
+                cs_happly (N->L, k, N->B [k], x) ;
+            }
+            cs_pvec (S->pinv, x, b, n) ;    /* b(0:n-1) = x(p(0:n-1)) */
+        }
+    }
+    cs_free (x) ;
+    cs_sfree (S) ;
+    cs_nfree (N) ;
+    cs_spfree (AT) ;
+    return (ok) ;
+}
diff --git a/igraph/src/cs_randperm.c b/igraph/src/cs_randperm.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_randperm.c
@@ -0,0 +1,47 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "igraph_random.h"
+
+#include "cs.h"
+/* return a random permutation vector, the identity perm, or p = n-1:-1:0.
+ * seed = -1 means p = n-1:-1:0.  seed = 0 means p = identity.  otherwise
+ * p = random permutation.  */
+CS_INT *cs_randperm (CS_INT n, CS_INT seed)
+{
+    CS_INT *p, k, j, t ;
+    if (seed == 0) return (NULL) ;      /* return p = NULL (identity) */
+    p = cs_malloc (n, sizeof (CS_INT)) ;   /* allocate result */
+    if (!p) return (NULL) ;             /* out of memory */
+    for (k = 0 ; k < n ; k++) p [k] = n-k-1 ;
+    if (seed == -1) return (p) ;        /* return reverse permutation */
+    /* srand (seed) ;                      /\* get new random number seed *\/ */
+    RNG_BEGIN();
+    for (k = 0 ; k < n ; k++)
+    {
+        /* j = k + (rand ( ) % (n-k)) ;    /\* j = rand CS_INT in range k to n-1 *\/ */
+      j = k + RNG_INTEGER(k, n-1) ;
+        t = p [j] ;                     /* swap p[k] and p[j] */
+        p [j] = p [k] ;
+        p [k] = t ;
+    }
+    RNG_END();
+    return (p) ;
+}
diff --git a/igraph/src/cs_reach.c b/igraph/src/cs_reach.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_reach.c
@@ -0,0 +1,39 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* xi [top...n-1] = nodes reachable from graph of G*P' via nodes in B(:,k).
+ * xi [n...2n-1] used as workspace */
+CS_INT cs_reach (cs *G, const cs *B, CS_INT k, CS_INT *xi, const CS_INT *pinv)
+{
+    CS_INT p, n, top, *Bp, *Bi, *Gp ;
+    if (!CS_CSC (G) || !CS_CSC (B) || !xi) return (-1) ;    /* check inputs */
+    n = G->n ; Bp = B->p ; Bi = B->i ; Gp = G->p ;
+    top = n ;
+    for (p = Bp [k] ; p < Bp [k+1] ; p++)
+    {
+        if (!CS_MARKED (Gp, Bi [p]))    /* start a dfs at unmarked node i */
+        {
+            top = cs_dfs (Bi [p], G, top, xi, xi+n, pinv) ;
+        }
+    }
+    for (p = top ; p < n ; p++) CS_MARK (Gp, xi [p]) ;  /* restore G */
+    return (top) ;
+}
diff --git a/igraph/src/cs_scatter.c b/igraph/src/cs_scatter.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_scatter.c
@@ -0,0 +1,42 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* x = x + beta * A(:,j), where x is a dense vector and A(:,j) is sparse */
+CS_INT cs_scatter (const cs *A, CS_INT j, CS_ENTRY beta, CS_INT *w, CS_ENTRY *x, CS_INT mark,
+    cs *C, CS_INT nz)
+{
+    CS_INT i, p, *Ap, *Ai, *Ci ;
+    CS_ENTRY *Ax ;
+    if (!CS_CSC (A) || !w || !CS_CSC (C)) return (-1) ;     /* check inputs */
+    Ap = A->p ; Ai = A->i ; Ax = A->x ; Ci = C->i ;
+    for (p = Ap [j] ; p < Ap [j+1] ; p++)
+    {
+        i = Ai [p] ;                            /* A(i,j) is nonzero */
+        if (w [i] < mark)
+        {
+            w [i] = mark ;                      /* i is new entry in column j */
+            Ci [nz++] = i ;                     /* add i to pattern of C(:,j) */
+            if (x) x [i] = beta * Ax [p] ;      /* x(i) = beta*A(i,j) */
+        }
+        else if (x) x [i] += beta * Ax [p] ;    /* i exists in C(:,j) already */
+    }
+    return (nz) ;
+}
diff --git a/igraph/src/cs_scc.c b/igraph/src/cs_scc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_scc.c
@@ -0,0 +1,61 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* find the strongly connected components of a square matrix */
+csd *cs_scc (cs *A)     /* matrix A temporarily modified, then restored */
+{
+    CS_INT n, i, k, b, nb = 0, top, *xi, *pstack, *p, *r, *Ap, *ATp, *rcopy, *Blk ;
+    cs *AT ;
+    csd *D ;
+    if (!CS_CSC (A)) return (NULL) ;                /* check inputs */
+    n = A->n ; Ap = A->p ;
+    D = cs_dalloc (n, 0) ;                          /* allocate result */
+    AT = cs_transpose (A, 0) ;                      /* AT = A' */
+    xi = cs_malloc (2*n+1, sizeof (CS_INT)) ;          /* get workspace */
+    if (!D || !AT || !xi) return (cs_ddone (D, AT, xi, 0)) ;
+    Blk = xi ; rcopy = pstack = xi + n ;
+    p = D->p ; r = D->r ; ATp = AT->p ;
+    top = n ;
+    for (i = 0 ; i < n ; i++)   /* first dfs(A) to find finish times (xi) */
+    {
+        if (!CS_MARKED (Ap, i)) top = cs_dfs (i, A, top, xi, pstack, NULL) ;
+    }
+    for (i = 0 ; i < n ; i++) CS_MARK (Ap, i) ; /* restore A; unmark all nodes*/
+    top = n ;
+    nb = n ;
+    for (k = 0 ; k < n ; k++)   /* dfs(A') to find strongly connnected comp */
+    {
+        i = xi [k] ;            /* get i in reverse order of finish times */
+        if (CS_MARKED (ATp, i)) continue ;  /* skip node i if already ordered */
+        r [nb--] = top ;        /* node i is the start of a component in p */
+        top = cs_dfs (i, AT, top, p, pstack, NULL) ;
+    }
+    r [nb] = 0 ;                /* first block starts at zero; shift r up */
+    for (k = nb ; k <= n ; k++) r [k-nb] = r [k] ;
+    D->nb = nb = n-nb ;         /* nb = # of strongly connected components */
+    for (b = 0 ; b < nb ; b++)  /* sort each block in natural order */
+    {
+        for (k = r [b] ; k < r [b+1] ; k++) Blk [p [k]] = b ;
+    }
+    for (b = 0 ; b <= nb ; b++) rcopy [b] = r [b] ;
+    for (i = 0 ; i < n ; i++) p [rcopy [Blk [i]]++] = i ;
+    return (cs_ddone (D, AT, xi, 1)) ;
+}
diff --git a/igraph/src/cs_schol.c b/igraph/src/cs_schol.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_schol.c
@@ -0,0 +1,46 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* ordering and symbolic analysis for a Cholesky factorization */
+css *cs_schol (CS_INT order, const cs *A)
+{
+    CS_INT n, *c, *post, *P ;
+    cs *C ;
+    css *S ;
+    if (!CS_CSC (A)) return (NULL) ;        /* check inputs */
+    n = A->n ;
+    S = cs_calloc (1, sizeof (css)) ;       /* allocate result S */
+    if (!S) return (NULL) ;                 /* out of memory */
+    P = cs_amd (order, A) ;                 /* P = amd(A+A'), or natural */
+    S->pinv = cs_pinv (P, n) ;              /* find inverse permutation */
+    cs_free (P) ;
+    if (order && !S->pinv) return (cs_sfree (S)) ;
+    C = cs_symperm (A, S->pinv, 0) ;        /* C = spones(triu(A(P,P))) */
+    S->parent = cs_etree (C, 0) ;           /* find etree of C */
+    post = cs_post (S->parent, n) ;         /* postorder the etree */
+    c = cs_counts (C, S->parent, post, 0) ; /* find column counts of chol(C) */
+    cs_free (post) ;
+    cs_spfree (C) ;
+    S->cp = cs_malloc (n+1, sizeof (CS_INT)) ; /* allocate result S->cp */
+    S->unz = S->lnz = cs_cumsum (S->cp, c, n) ; /* find column pointers for L */
+    cs_free (c) ;
+    return ((S->lnz >= 0) ? S : cs_sfree (S)) ;
+}
diff --git a/igraph/src/cs_spsolve.c b/igraph/src/cs_spsolve.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_spsolve.c
@@ -0,0 +1,48 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* solve Gx=b(:,k), where G is either upper (lo=0) or lower (lo=1) triangular */
+CS_INT cs_spsolve (cs *G, const cs *B, CS_INT k, CS_INT *xi, CS_ENTRY *x, const CS_INT *pinv,
+    CS_INT lo)
+{
+    CS_INT j, J, p, q, px, top, n, *Gp, *Gi, *Bp, *Bi ;
+    CS_ENTRY *Gx, *Bx ;
+    if (!CS_CSC (G) || !CS_CSC (B) || !xi || !x) return (-1) ;
+    Gp = G->p ; Gi = G->i ; Gx = G->x ; n = G->n ;
+    Bp = B->p ; Bi = B->i ; Bx = B->x ;
+    top = cs_reach (G, B, k, xi, pinv) ;        /* xi[top..n-1]=Reach(B(:,k)) */
+    for (p = top ; p < n ; p++) x [xi [p]] = 0 ;    /* clear x */
+    for (p = Bp [k] ; p < Bp [k+1] ; p++) x [Bi [p]] = Bx [p] ; /* scatter B */
+    for (px = top ; px < n ; px++)
+    {
+        j = xi [px] ;                               /* x(j) is nonzero */
+        J = pinv ? (pinv [j]) : j ;                 /* j maps to col J of G */
+        if (J < 0) continue ;                       /* column J is empty */
+        x [j] /= Gx [lo ? (Gp [J]) : (Gp [J+1]-1)] ;/* x(j) /= G(j,j) */
+        p = lo ? (Gp [J]+1) : (Gp [J]) ;            /* lo: L(j,j) 1st entry */
+        q = lo ? (Gp [J+1]) : (Gp [J+1]-1) ;        /* up: U(j,j) last entry */
+        for ( ; p < q ; p++)
+        {
+            x [Gi [p]] -= Gx [p] * x [j] ;          /* x(i) -= G(i,j) * x(j) */
+        }
+    }
+    return (top) ;                                  /* return top of stack */
+}
diff --git a/igraph/src/cs_sqr.c b/igraph/src/cs_sqr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_sqr.c
@@ -0,0 +1,108 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* compute nnz(V) = S->lnz, S->pinv, S->leftmost, S->m2 from A and S->parent */
+static CS_INT cs_vcount (const cs *A, css *S)
+{
+    CS_INT i, k, p, pa, n = A->n, m = A->m, *Ap = A->p, *Ai = A->i, *next, *head,
+        *tail, *nque, *pinv, *leftmost, *w, *parent = S->parent ;
+    S->pinv = pinv = cs_malloc (m+n, sizeof (CS_INT)) ;        /* allocate pinv, */
+    S->leftmost = leftmost = cs_malloc (m, sizeof (CS_INT)) ;  /* and leftmost */
+    w = cs_malloc (m+3*n, sizeof (CS_INT)) ;   /* get workspace */
+    if (!pinv || !w || !leftmost)
+    {
+        cs_free (w) ;                       /* pinv and leftmost freed later */
+        return (0) ;                        /* out of memory */
+    }
+    next = w ; head = w + m ; tail = w + m + n ; nque = w + m + 2*n ;
+    for (k = 0 ; k < n ; k++) head [k] = -1 ;   /* queue k is empty */
+    for (k = 0 ; k < n ; k++) tail [k] = -1 ;
+    for (k = 0 ; k < n ; k++) nque [k] = 0 ;
+    for (i = 0 ; i < m ; i++) leftmost [i] = -1 ;
+    for (k = n-1 ; k >= 0 ; k--)
+    {
+        for (p = Ap [k] ; p < Ap [k+1] ; p++)
+        {
+            leftmost [Ai [p]] = k ;         /* leftmost[i] = min(find(A(i,:)))*/
+        }
+    }
+    for (i = m-1 ; i >= 0 ; i--)            /* scan rows in reverse order */
+    {
+        pinv [i] = -1 ;                     /* row i is not yet ordered */
+        k = leftmost [i] ;
+        if (k == -1) continue ;             /* row i is empty */
+        if (nque [k]++ == 0) tail [k] = i ; /* first row in queue k */
+        next [i] = head [k] ;               /* put i at head of queue k */
+        head [k] = i ;
+    }
+    S->lnz = 0 ;
+    S->m2 = m ;
+    for (k = 0 ; k < n ; k++)               /* find row permutation and nnz(V)*/
+    {
+        i = head [k] ;                      /* remove row i from queue k */
+        S->lnz++ ;                          /* count V(k,k) as nonzero */
+        if (i < 0) i = S->m2++ ;            /* add a fictitious row */
+        pinv [i] = k ;                      /* associate row i with V(:,k) */
+        if (--nque [k] <= 0) continue ;     /* skip if V(k+1:m,k) is empty */
+        S->lnz += nque [k] ;                /* nque [k] is nnz (V(k+1:m,k)) */
+        if ((pa = parent [k]) != -1)        /* move all rows to parent of k */
+        {
+            if (nque [pa] == 0) tail [pa] = tail [k] ;
+            next [tail [k]] = head [pa] ;
+            head [pa] = next [i] ;
+            nque [pa] += nque [k] ;
+        }
+    }
+    for (i = 0 ; i < m ; i++) if (pinv [i] < 0) pinv [i] = k++ ;
+    cs_free (w) ;
+    return (1) ;
+}
+
+/* symbolic ordering and analysis for QR or LU */
+css *cs_sqr (CS_INT order, const cs *A, CS_INT qr)
+{
+    CS_INT n, k, ok = 1, *post ;
+    css *S ;
+    if (!CS_CSC (A)) return (NULL) ;        /* check inputs */
+    n = A->n ;
+    S = cs_calloc (1, sizeof (css)) ;       /* allocate result S */
+    if (!S) return (NULL) ;                 /* out of memory */
+    S->q = cs_amd (order, A) ;              /* fill-reducing ordering */
+    if (order && !S->q) return (cs_sfree (S)) ;
+    if (qr)                                 /* QR symbolic analysis */
+    {
+        cs *C = order ? cs_permute (A, NULL, S->q, 0) : ((cs *) A) ;
+        S->parent = cs_etree (C, 1) ;       /* etree of C'*C, where C=A(:,q) */
+        post = cs_post (S->parent, n) ;
+        S->cp = cs_counts (C, S->parent, post, 1) ;  /* col counts chol(C'*C) */
+        cs_free (post) ;
+        ok = C && S->parent && S->cp && cs_vcount (C, S) ;
+        if (ok) for (S->unz = 0, k = 0 ; k < n ; k++) S->unz += S->cp [k] ;
+        ok = ok && S->lnz >= 0 && S->unz >= 0 ;     /* CS_INT overflow guard */
+        if (order) cs_spfree (C) ;
+    }
+    else
+    {
+        S->unz = 4*(A->p [n]) + n ;         /* for LU factorization only, */
+        S->lnz = S->unz ;                   /* guess nnz(L) and nnz(U) */
+    }
+    return (ok ? S : cs_sfree (S)) ;        /* return result S */
+}
diff --git a/igraph/src/cs_symperm.c b/igraph/src/cs_symperm.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_symperm.c
@@ -0,0 +1,59 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* C = A(p,p) where A and C are symmetric the upper part stored; pinv not p */
+cs *cs_symperm (const cs *A, const CS_INT *pinv, CS_INT values)
+{
+    CS_INT i, j, p, q, i2, j2, n, *Ap, *Ai, *Cp, *Ci, *w ;
+    CS_ENTRY *Cx, *Ax ;
+    cs *C ;
+    if (!CS_CSC (A)) return (NULL) ;                    /* check inputs */
+    n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ;
+    C = cs_spalloc (n, n, Ap [n], values && (Ax != NULL), 0) ; /* alloc result*/
+    w = cs_calloc (n, sizeof (CS_INT)) ;                   /* get workspace */
+    if (!C || !w) return (cs_done (C, w, NULL, 0)) ;    /* out of memory */
+    Cp = C->p ; Ci = C->i ; Cx = C->x ;
+    for (j = 0 ; j < n ; j++)           /* count entries in each column of C */
+    {
+        j2 = pinv ? pinv [j] : j ;      /* column j of A is column j2 of C */
+        for (p = Ap [j] ; p < Ap [j+1] ; p++)
+        {
+            i = Ai [p] ;
+            if (i > j) continue ;       /* skip lower triangular part of A */
+            i2 = pinv ? pinv [i] : i ;  /* row i of A is row i2 of C */
+            w [CS_MAX (i2, j2)]++ ;     /* column count of C */
+        }
+    }
+    cs_cumsum (Cp, w, n) ;              /* compute column pointers of C */
+    for (j = 0 ; j < n ; j++)
+    {
+        j2 = pinv ? pinv [j] : j ;      /* column j of A is column j2 of C */
+        for (p = Ap [j] ; p < Ap [j+1] ; p++)
+        {
+            i = Ai [p] ;
+            if (i > j) continue ;       /* skip lower triangular part of A*/
+            i2 = pinv ? pinv [i] : i ;  /* row i of A is row i2 of C */
+            Ci [q = w [CS_MAX (i2, j2)]++] = CS_MIN (i2, j2) ;
+            if (Cx) Cx [q] = (i2 <= j2) ? Ax [p] : CS_CONJ (Ax [p]) ;
+        }
+    }
+    return (cs_done (C, w, NULL, 1)) ;  /* success; free workspace, return C */
+}
diff --git a/igraph/src/cs_tdfs.c b/igraph/src/cs_tdfs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_tdfs.c
@@ -0,0 +1,44 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* depth-first search and postorder of a tree rooted at node j */
+CS_INT cs_tdfs (CS_INT j, CS_INT k, CS_INT *head, const CS_INT *next, CS_INT *post, CS_INT *stack)
+{
+    CS_INT i, p, top = 0 ;
+    if (!head || !next || !post || !stack) return (-1) ;    /* check inputs */
+    stack [0] = j ;                 /* place j on the stack */
+    while (top >= 0)                /* while (stack is not empty) */
+    {
+        p = stack [top] ;           /* p = top of stack */
+        i = head [p] ;              /* i = youngest child of p */
+        if (i == -1)
+        {
+            top-- ;                 /* p has no unordered children left */
+            post [k++] = p ;        /* node p is the kth postordered node */
+        }
+        else
+        {
+            head [p] = next [i] ;   /* remove i from children of p */
+            stack [++top] = i ;     /* start dfs on child node i */
+        }
+    }
+    return (k) ;
+}
diff --git a/igraph/src/cs_transpose.c b/igraph/src/cs_transpose.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_transpose.c
@@ -0,0 +1,45 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* C = A' */
+cs *cs_transpose (const cs *A, CS_INT values)
+{
+    CS_INT p, q, j, *Cp, *Ci, n, m, *Ap, *Ai, *w ;
+    CS_ENTRY *Cx, *Ax ;
+    cs *C ;
+    if (!CS_CSC (A)) return (NULL) ;    /* check inputs */
+    m = A->m ; n = A->n ; Ap = A->p ; Ai = A->i ; Ax = A->x ;
+    C = cs_spalloc (n, m, Ap [n], values && Ax, 0) ;       /* allocate result */
+    w = cs_calloc (m, sizeof (CS_INT)) ;                      /* get workspace */
+    if (!C || !w) return (cs_done (C, w, NULL, 0)) ;       /* out of memory */
+    Cp = C->p ; Ci = C->i ; Cx = C->x ;
+    for (p = 0 ; p < Ap [n] ; p++) w [Ai [p]]++ ;          /* row counts */
+    cs_cumsum (Cp, w, m) ;                                 /* row pointers */
+    for (j = 0 ; j < n ; j++)
+    {
+        for (p = Ap [j] ; p < Ap [j+1] ; p++)
+        {
+            Ci [q = w [Ai [p]]++] = j ; /* place A(i,j) as entry C(j,i) */
+            if (Cx) Cx [q] = (values > 0) ? CS_CONJ (Ax [p]) : Ax [p] ;
+        }
+    }
+    return (cs_done (C, w, NULL, 1)) ;  /* success; free w and return C */
+}
diff --git a/igraph/src/cs_updown.c b/igraph/src/cs_updown.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_updown.c
@@ -0,0 +1,68 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* sparse Cholesky update/downdate, L*L' + sigma*w*w' (sigma = +1 or -1) */
+CS_INT cs_updown (cs *L, CS_INT sigma, const cs *C, const CS_INT *parent)
+{
+    CS_INT n, p, f, j, *Lp, *Li, *Cp, *Ci ;
+    CS_ENTRY *Lx, *Cx, alpha, gamma, w1, w2, *w ;
+    double beta = 1, beta2 = 1, delta ;
+#ifdef CS_COMPLEX
+    cs_complex_t phase ;
+#endif
+    if (!CS_CSC (L) || !CS_CSC (C) || !parent) return (0) ;  /* check inputs */
+    Lp = L->p ; Li = L->i ; Lx = L->x ; n = L->n ;
+    Cp = C->p ; Ci = C->i ; Cx = C->x ;
+    if ((p = Cp [0]) >= Cp [1]) return (1) ;        /* return if C empty */
+    w = cs_malloc (n, sizeof (CS_ENTRY)) ;          /* get workspace */
+    if (!w) return (0) ;                            /* out of memory */
+    f = Ci [p] ;
+    for ( ; p < Cp [1] ; p++) f = CS_MIN (f, Ci [p]) ;  /* f = min (find (C)) */
+    for (j = f ; j != -1 ; j = parent [j]) w [j] = 0 ;  /* clear workspace w */
+    for (p = Cp [0] ; p < Cp [1] ; p++) w [Ci [p]] = Cx [p] ; /* w = C */
+    for (j = f ; j != -1 ; j = parent [j])          /* walk path f up to root */
+    {
+        p = Lp [j] ;
+        alpha = w [j] / Lx [p] ;                    /* alpha = w(j) / L(j,j) */
+        beta2 = beta*beta + sigma*alpha*CS_CONJ(alpha) ;
+        if (beta2 <= 0) break ;                     /* not positive definite */
+        beta2 = sqrt (beta2) ;
+        delta = (sigma > 0) ? (beta / beta2) : (beta2 / beta) ;
+        gamma = sigma * CS_CONJ(alpha) / (beta2 * beta) ;
+        Lx [p] = delta * Lx [p] + ((sigma > 0) ? (gamma * w [j]) : 0) ;
+        beta = beta2 ;
+#ifdef CS_COMPLEX
+        phase = CS_ABS (Lx [p]) / Lx [p] ;  /* phase = abs(L(j,j))/L(j,j)*/
+        Lx [p] *= phase ;                   /* L(j,j) = L(j,j) * phase */
+#endif
+        for (p++ ; p < Lp [j+1] ; p++)
+        {
+            w1 = w [Li [p]] ;
+            w [Li [p]] = w2 = w1 - alpha * Lx [p] ;
+            Lx [p] = delta * Lx [p] + gamma * ((sigma > 0) ? w1 : w2) ;
+#ifdef CS_COMPLEX
+            Lx [p] *= phase ;               /* L(i,j) = L(i,j) * phase */
+#endif
+        }
+    }
+    cs_free (w) ;
+    return (beta2 > 0) ;
+}
diff --git a/igraph/src/cs_usolve.c b/igraph/src/cs_usolve.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_usolve.c
@@ -0,0 +1,38 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* solve Ux=b where x and b are dense.  x=b on input, solution on output. */
+CS_INT cs_usolve (const cs *U, CS_ENTRY *x)
+{
+    CS_INT p, j, n, *Up, *Ui ;
+    CS_ENTRY *Ux ;
+    if (!CS_CSC (U) || !x) return (0) ;                     /* check inputs */
+    n = U->n ; Up = U->p ; Ui = U->i ; Ux = U->x ;
+    for (j = n-1 ; j >= 0 ; j--)
+    {
+        x [j] /= Ux [Up [j+1]-1] ;
+        for (p = Up [j] ; p < Up [j+1]-1 ; p++)
+        {
+            x [Ui [p]] -= Ux [p] * x [j] ;
+        }
+    }
+    return (1) ;
+}
diff --git a/igraph/src/cs_util.c b/igraph/src/cs_util.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_util.c
@@ -0,0 +1,139 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* allocate a sparse matrix (triplet form or compressed-column form) */
+cs *cs_spalloc (CS_INT m, CS_INT n, CS_INT nzmax, CS_INT values, CS_INT triplet)
+{
+    cs *A = cs_calloc (1, sizeof (cs)) ;    /* allocate the cs struct */
+    if (!A) return (NULL) ;                 /* out of memory */
+    A->m = m ;                              /* define dimensions and nzmax */
+    A->n = n ;
+    A->nzmax = nzmax = CS_MAX (nzmax, 1) ;
+    A->nz = triplet ? 0 : -1 ;              /* allocate triplet or comp.col */
+    A->p = cs_malloc (triplet ? nzmax : n+1, sizeof (CS_INT)) ;
+    A->i = cs_malloc (nzmax, sizeof (CS_INT)) ;
+    A->x = values ? cs_malloc (nzmax, sizeof (CS_ENTRY)) : NULL ;
+    return ((!A->p || !A->i || (values && !A->x)) ? cs_spfree (A) : A) ;
+}
+
+/* change the max # of entries sparse matrix */
+CS_INT cs_sprealloc (cs *A, CS_INT nzmax)
+{
+    CS_INT ok, oki, okj = 1, okx = 1 ;
+    if (!A) return (0) ;
+    if (nzmax <= 0) nzmax = (CS_CSC (A)) ? (A->p [A->n]) : A->nz ;
+    A->i = cs_realloc (A->i, nzmax, sizeof (CS_INT), &oki) ;
+    if (CS_TRIPLET (A)) A->p = cs_realloc (A->p, nzmax, sizeof (CS_INT), &okj) ;
+    if (A->x) A->x = cs_realloc (A->x, nzmax, sizeof (CS_ENTRY), &okx) ;
+    ok = (oki && okj && okx) ;
+    if (ok) A->nzmax = nzmax ;
+    return (ok) ;
+}
+
+/* free a sparse matrix */
+cs *cs_spfree (cs *A)
+{
+    if (!A) return (NULL) ;     /* do nothing if A already NULL */
+    cs_free (A->p) ;
+    cs_free (A->i) ;
+    cs_free (A->x) ;
+    return (cs_free (A)) ;      /* free the cs struct and return NULL */
+}
+
+/* free a numeric factorization */
+csn *cs_nfree (csn *N)
+{
+    if (!N) return (NULL) ;     /* do nothing if N already NULL */
+    cs_spfree (N->L) ;
+    cs_spfree (N->U) ;
+    cs_free (N->pinv) ;
+    cs_free (N->B) ;
+    return (cs_free (N)) ;      /* free the csn struct and return NULL */
+}
+
+/* free a symbolic factorization */
+css *cs_sfree (css *S)
+{
+    if (!S) return (NULL) ;     /* do nothing if S already NULL */
+    cs_free (S->pinv) ;
+    cs_free (S->q) ;
+    cs_free (S->parent) ;
+    cs_free (S->cp) ;
+    cs_free (S->leftmost) ;
+    return (cs_free (S)) ;      /* free the css struct and return NULL */
+}
+
+/* allocate a cs_dmperm or cs_scc result */
+csd *cs_dalloc (CS_INT m, CS_INT n)
+{
+    csd *D ;
+    D = cs_calloc (1, sizeof (csd)) ;
+    if (!D) return (NULL) ;
+    D->p = cs_malloc (m, sizeof (CS_INT)) ;
+    D->r = cs_malloc (m+6, sizeof (CS_INT)) ;
+    D->q = cs_malloc (n, sizeof (CS_INT)) ;
+    D->s = cs_malloc (n+6, sizeof (CS_INT)) ;
+    return ((!D->p || !D->r || !D->q || !D->s) ? cs_dfree (D) : D) ;
+}
+
+/* free a cs_dmperm or cs_scc result */
+csd *cs_dfree (csd *D)
+{
+    if (!D) return (NULL) ;     /* do nothing if D already NULL */
+    cs_free (D->p) ;
+    cs_free (D->q) ;
+    cs_free (D->r) ;
+    cs_free (D->s) ;
+    return (cs_free (D)) ;
+}
+
+/* free workspace and return a sparse matrix result */
+cs *cs_done (cs *C, void *w, void *x, CS_INT ok)
+{
+    cs_free (w) ;                       /* free workspace */
+    cs_free (x) ;
+    return (ok ? C : cs_spfree (C)) ;   /* return result if OK, else free it */
+}
+
+/* free workspace and return CS_INT array result */
+CS_INT *cs_idone (CS_INT *p, cs *C, void *w, CS_INT ok)
+{
+    cs_spfree (C) ;                     /* free temporary matrix */
+    cs_free (w) ;                       /* free workspace */
+    return (ok ? p : cs_free (p)) ;     /* return result if OK, else free it */
+}
+
+/* free workspace and return a numeric factorization (Cholesky, LU, or QR) */
+csn *cs_ndone (csn *N, cs *C, void *w, void *x, CS_INT ok)
+{
+    cs_spfree (C) ;                     /* free temporary matrix */
+    cs_free (w) ;                       /* free workspace */
+    cs_free (x) ;
+    return (ok ? N : cs_nfree (N)) ;    /* return result if OK, else free it */
+}
+
+/* free workspace and return a csd result */
+csd *cs_ddone (csd *D, cs *C, void *w, CS_INT ok)
+{
+    cs_spfree (C) ;                     /* free temporary matrix */
+    cs_free (w) ;                       /* free workspace */
+    return (ok ? D : cs_dfree (D)) ;    /* return result if OK, else free it */
+}
diff --git a/igraph/src/cs_utsolve.c b/igraph/src/cs_utsolve.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/cs_utsolve.c
@@ -0,0 +1,38 @@
+/*
+ * CXSPARSE: a Concise Sparse Matrix package - Extended.
+ * Copyright (c) 2006-2009, Timothy A. Davis.
+ * http://www.cise.ufl.edu/research/sparse/CXSparse
+ * 
+ * CXSparse is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * CXSparse is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this Module; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cs.h"
+/* solve U'x=b where x and b are dense.  x=b on input, solution on output. */
+CS_INT cs_utsolve (const cs *U, CS_ENTRY *x)
+{
+    CS_INT p, j, n, *Up, *Ui ;
+    CS_ENTRY *Ux ;
+    if (!CS_CSC (U) || !x) return (0) ;                     /* check inputs */
+    n = U->n ; Up = U->p ; Ui = U->i ; Ux = U->x ;
+    for (j = 0 ; j < n ; j++)
+    {
+        for (p = Up [j] ; p < Up [j+1]-1 ; p++)
+        {
+            x [j] -= CS_CONJ (Ux [p]) * x [Ui [p]] ;
+        }
+        x [j] /= CS_CONJ (Ux [Up [j+1]-1]) ;
+    }
+    return (1) ;
+}
diff --git a/igraph/src/ctype.c b/igraph/src/ctype.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ctype.c
@@ -0,0 +1,2 @@
+#define My_ctype_DEF
+#include "ctype.h"
diff --git a/igraph/src/d_abs.c b/igraph/src/d_abs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_abs.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double d_abs(x) doublereal *x;
+#else
+double d_abs(doublereal *x)
+#endif
+{
+if(*x >= 0)
+	return(*x);
+return(- *x);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_acos.c b/igraph/src/d_acos.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_acos.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double acos();
+double d_acos(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_acos(doublereal *x)
+#endif
+{
+return( acos(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_asin.c b/igraph/src/d_asin.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_asin.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double asin();
+double d_asin(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_asin(doublereal *x)
+#endif
+{
+return( asin(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_atan.c b/igraph/src/d_atan.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_atan.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double atan();
+double d_atan(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_atan(doublereal *x)
+#endif
+{
+return( atan(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_atn2.c b/igraph/src/d_atn2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_atn2.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double atan2();
+double d_atn2(x,y) doublereal *x, *y;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_atn2(doublereal *x, doublereal *y)
+#endif
+{
+return( atan2(*x,*y) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_cnjg.c b/igraph/src/d_cnjg.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_cnjg.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ VOID
+#ifdef KR_headers
+d_cnjg(r, z) doublecomplex *r, *z;
+#else
+d_cnjg(doublecomplex *r, doublecomplex *z)
+#endif
+{
+	doublereal zi = z->i;
+	r->r = z->r;
+	r->i = -zi;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_cos.c b/igraph/src/d_cos.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_cos.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double cos();
+double d_cos(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_cos(doublereal *x)
+#endif
+{
+return( cos(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_cosh.c b/igraph/src/d_cosh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_cosh.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double cosh();
+double d_cosh(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_cosh(doublereal *x)
+#endif
+{
+return( cosh(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_dim.c b/igraph/src/d_dim.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_dim.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double d_dim(a,b) doublereal *a, *b;
+#else
+double d_dim(doublereal *a, doublereal *b)
+#endif
+{
+return( *a > *b ? *a - *b : 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_exp.c b/igraph/src/d_exp.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_exp.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double exp();
+double d_exp(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_exp(doublereal *x)
+#endif
+{
+return( exp(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_imag.c b/igraph/src/d_imag.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_imag.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double d_imag(z) doublecomplex *z;
+#else
+double d_imag(doublecomplex *z)
+#endif
+{
+return(z->i);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_int.c b/igraph/src/d_int.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_int.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double floor();
+double d_int(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_int(doublereal *x)
+#endif
+{
+return( (*x>0) ? floor(*x) : -floor(- *x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_lg10.c b/igraph/src/d_lg10.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_lg10.c
@@ -0,0 +1,21 @@
+#include "f2c.h"
+
+#define log10e 0.43429448190325182765
+
+#ifdef KR_headers
+double log();
+double d_lg10(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_lg10(doublereal *x)
+#endif
+{
+return( log10e * log(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_log.c b/igraph/src/d_log.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_log.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double log();
+double d_log(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_log(doublereal *x)
+#endif
+{
+return( log(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_mod.c b/igraph/src/d_mod.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_mod.c
@@ -0,0 +1,46 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+#ifdef IEEE_drem
+double drem();
+#else
+double floor();
+#endif
+double d_mod(x,y) doublereal *x, *y;
+#else
+#ifdef IEEE_drem
+double drem(double, double);
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#endif
+double d_mod(doublereal *x, doublereal *y)
+#endif
+{
+#ifdef IEEE_drem
+	double xa, ya, z;
+	if ((ya = *y) < 0.)
+		ya = -ya;
+	z = drem(xa = *x, ya);
+	if (xa > 0) {
+		if (z < 0)
+			z += ya;
+		}
+	else if (z > 0)
+		z -= ya;
+	return z;
+#else
+	double quotient;
+	if( (quotient = *x / *y) >= 0)
+		quotient = floor(quotient);
+	else
+		quotient = -floor(-quotient);
+	return(*x - (*y) * quotient );
+#endif
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_nint.c b/igraph/src/d_nint.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_nint.c
@@ -0,0 +1,20 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double floor();
+double d_nint(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_nint(doublereal *x)
+#endif
+{
+return( (*x)>=0 ?
+	floor(*x + .5) : -floor(.5 - *x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_prod.c b/igraph/src/d_prod.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_prod.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double d_prod(x,y) real *x, *y;
+#else
+double d_prod(real *x, real *y)
+#endif
+{
+return( (*x) * (*y) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_sign.c b/igraph/src/d_sign.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_sign.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double d_sign(a,b) doublereal *a, *b;
+#else
+double d_sign(doublereal *a, doublereal *b)
+#endif
+{
+double x;
+x = (*a >= 0 ? *a : - *a);
+return( *b >= 0 ? x : -x);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_sin.c b/igraph/src/d_sin.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_sin.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sin();
+double d_sin(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_sin(doublereal *x)
+#endif
+{
+return( sin(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_sinh.c b/igraph/src/d_sinh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_sinh.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sinh();
+double d_sinh(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_sinh(doublereal *x)
+#endif
+{
+return( sinh(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_sqrt.c b/igraph/src/d_sqrt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_sqrt.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sqrt();
+double d_sqrt(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_sqrt(doublereal *x)
+#endif
+{
+return( sqrt(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_tan.c b/igraph/src/d_tan.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_tan.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double tan();
+double d_tan(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_tan(doublereal *x)
+#endif
+{
+return( tan(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/d_tanh.c b/igraph/src/d_tanh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/d_tanh.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double tanh();
+double d_tanh(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double d_tanh(doublereal *x)
+#endif
+{
+return( tanh(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/dasum.c b/igraph/src/dasum.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dasum.c
@@ -0,0 +1,89 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+doublereal igraphdasum_(integer *n, doublereal *dx, integer *incx)
+{
+    /* System generated locals */
+    integer i__1, i__2;
+    doublereal ret_val, d__1, d__2, d__3, d__4, d__5, d__6;
+
+    /* Local variables */
+    integer i__, m, mp1;
+    doublereal dtemp;
+    integer nincx;
+
+
+/*  Purpose   
+    =======   
+
+       DASUM takes the sum of the absolute values.   
+
+    Further Details   
+    ===============   
+
+       jack dongarra, linpack, 3/11/78.   
+       modified 3/93 to return if incx .le. 0.   
+       modified 12/3/93, array(1) declarations changed to array(*)   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --dx;
+
+    /* Function Body */
+    ret_val = 0.;
+    dtemp = 0.;
+    if (*n <= 0 || *incx <= 0) {
+	return ret_val;
+    }
+    if (*incx == 1) {
+/*        code for increment equal to 1   
+
+
+          clean-up loop */
+
+	m = *n % 6;
+	if (m != 0) {
+	    i__1 = m;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		dtemp += (d__1 = dx[i__], abs(d__1));
+	    }
+	    if (*n < 6) {
+		ret_val = dtemp;
+		return ret_val;
+	    }
+	}
+	mp1 = m + 1;
+	i__1 = *n;
+	for (i__ = mp1; i__ <= i__1; i__ += 6) {
+	    dtemp = dtemp + (d__1 = dx[i__], abs(d__1)) + (d__2 = dx[i__ + 1],
+		     abs(d__2)) + (d__3 = dx[i__ + 2], abs(d__3)) + (d__4 = 
+		    dx[i__ + 3], abs(d__4)) + (d__5 = dx[i__ + 4], abs(d__5)) 
+		    + (d__6 = dx[i__ + 5], abs(d__6));
+	}
+    } else {
+
+/*        code for increment not equal to 1 */
+
+	nincx = *n * *incx;
+	i__1 = nincx;
+	i__2 = *incx;
+	for (i__ = 1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) {
+	    dtemp += (d__1 = dx[i__], abs(d__1));
+	}
+    }
+    ret_val = dtemp;
+    return ret_val;
+} /* igraphdasum_ */
+
diff --git a/igraph/src/daxpy.c b/igraph/src/daxpy.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/daxpy.c
@@ -0,0 +1,97 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdaxpy_(integer *n, doublereal *da, doublereal *dx, 
+	integer *incx, doublereal *dy, integer *incy)
+{
+    /* System generated locals */
+    integer i__1;
+
+    /* Local variables */
+    integer i__, m, ix, iy, mp1;
+
+
+/*  Purpose   
+    =======   
+
+       DAXPY constant times a vector plus a vector.   
+       uses unrolled loops for increments equal to one.   
+
+    Further Details   
+    ===============   
+
+       jack dongarra, linpack, 3/11/78.   
+       modified 12/3/93, array(1) declarations changed to array(*)   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --dy;
+    --dx;
+
+    /* Function Body */
+    if (*n <= 0) {
+	return 0;
+    }
+    if (*da == 0.) {
+	return 0;
+    }
+    if (*incx == 1 && *incy == 1) {
+
+/*        code for both increments equal to 1   
+
+
+          clean-up loop */
+
+	m = *n % 4;
+	if (m != 0) {
+	    i__1 = m;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		dy[i__] += *da * dx[i__];
+	    }
+	}
+	if (*n < 4) {
+	    return 0;
+	}
+	mp1 = m + 1;
+	i__1 = *n;
+	for (i__ = mp1; i__ <= i__1; i__ += 4) {
+	    dy[i__] += *da * dx[i__];
+	    dy[i__ + 1] += *da * dx[i__ + 1];
+	    dy[i__ + 2] += *da * dx[i__ + 2];
+	    dy[i__ + 3] += *da * dx[i__ + 3];
+	}
+    } else {
+
+/*        code for unequal increments or equal increments   
+            not equal to 1 */
+
+	ix = 1;
+	iy = 1;
+	if (*incx < 0) {
+	    ix = (-(*n) + 1) * *incx + 1;
+	}
+	if (*incy < 0) {
+	    iy = (-(*n) + 1) * *incy + 1;
+	}
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    dy[iy] += *da * dx[ix];
+	    ix += *incx;
+	    iy += *incy;
+	}
+    }
+    return 0;
+} /* igraphdaxpy_ */
+
diff --git a/igraph/src/dcopy.c b/igraph/src/dcopy.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dcopy.c
@@ -0,0 +1,97 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdcopy_(integer *n, doublereal *dx, integer *incx, 
+	doublereal *dy, integer *incy)
+{
+    /* System generated locals */
+    integer i__1;
+
+    /* Local variables */
+    integer i__, m, ix, iy, mp1;
+
+
+/*  Purpose   
+    =======   
+
+       DCOPY copies a vector, x, to a vector, y.   
+       uses unrolled loops for increments equal to one.   
+
+    Further Details   
+    ===============   
+
+       jack dongarra, linpack, 3/11/78.   
+       modified 12/3/93, array(1) declarations changed to array(*)   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --dy;
+    --dx;
+
+    /* Function Body */
+    if (*n <= 0) {
+	return 0;
+    }
+    if (*incx == 1 && *incy == 1) {
+
+/*        code for both increments equal to 1   
+
+
+          clean-up loop */
+
+	m = *n % 7;
+	if (m != 0) {
+	    i__1 = m;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		dy[i__] = dx[i__];
+	    }
+	    if (*n < 7) {
+		return 0;
+	    }
+	}
+	mp1 = m + 1;
+	i__1 = *n;
+	for (i__ = mp1; i__ <= i__1; i__ += 7) {
+	    dy[i__] = dx[i__];
+	    dy[i__ + 1] = dx[i__ + 1];
+	    dy[i__ + 2] = dx[i__ + 2];
+	    dy[i__ + 3] = dx[i__ + 3];
+	    dy[i__ + 4] = dx[i__ + 4];
+	    dy[i__ + 5] = dx[i__ + 5];
+	    dy[i__ + 6] = dx[i__ + 6];
+	}
+    } else {
+
+/*        code for unequal increments or equal increments   
+            not equal to 1 */
+
+	ix = 1;
+	iy = 1;
+	if (*incx < 0) {
+	    ix = (-(*n) + 1) * *incx + 1;
+	}
+	if (*incy < 0) {
+	    iy = (-(*n) + 1) * *incy + 1;
+	}
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    dy[iy] = dx[ix];
+	    ix += *incx;
+	    iy += *incy;
+	}
+    }
+    return 0;
+} /* igraphdcopy_ */
+
diff --git a/igraph/src/ddot.c b/igraph/src/ddot.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ddot.c
@@ -0,0 +1,99 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+doublereal igraphddot_(integer *n, doublereal *dx, integer *incx, doublereal *dy, 
+	integer *incy)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal ret_val;
+
+    /* Local variables */
+    integer i__, m, ix, iy, mp1;
+    doublereal dtemp;
+
+
+/*  Purpose   
+    =======   
+
+       DDOT forms the dot product of two vectors.   
+       uses unrolled loops for increments equal to one.   
+
+    Further Details   
+    ===============   
+
+       jack dongarra, linpack, 3/11/78.   
+       modified 12/3/93, array(1) declarations changed to array(*)   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --dy;
+    --dx;
+
+    /* Function Body */
+    ret_val = 0.;
+    dtemp = 0.;
+    if (*n <= 0) {
+	return ret_val;
+    }
+    if (*incx == 1 && *incy == 1) {
+
+/*        code for both increments equal to 1   
+
+
+          clean-up loop */
+
+	m = *n % 5;
+	if (m != 0) {
+	    i__1 = m;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		dtemp += dx[i__] * dy[i__];
+	    }
+	    if (*n < 5) {
+		ret_val = dtemp;
+		return ret_val;
+	    }
+	}
+	mp1 = m + 1;
+	i__1 = *n;
+	for (i__ = mp1; i__ <= i__1; i__ += 5) {
+	    dtemp = dtemp + dx[i__] * dy[i__] + dx[i__ + 1] * dy[i__ + 1] + 
+		    dx[i__ + 2] * dy[i__ + 2] + dx[i__ + 3] * dy[i__ + 3] + 
+		    dx[i__ + 4] * dy[i__ + 4];
+	}
+    } else {
+
+/*        code for unequal increments or equal increments   
+            not equal to 1 */
+
+	ix = 1;
+	iy = 1;
+	if (*incx < 0) {
+	    ix = (-(*n) + 1) * *incx + 1;
+	}
+	if (*incy < 0) {
+	    iy = (-(*n) + 1) * *incy + 1;
+	}
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    dtemp += dx[ix] * dy[iy];
+	    ix += *incx;
+	    iy += *incy;
+	}
+    }
+    ret_val = dtemp;
+    return ret_val;
+} /* igraphddot_ */
+
diff --git a/igraph/src/decomposition.c b/igraph/src/decomposition.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/decomposition.c
@@ -0,0 +1,471 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2008-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_structural.h"
+#include "igraph_error.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+
+/**
+ * \function igraph_maximum_cardinality_search
+ * Maximum cardinality search
+ *
+ * This function implements the maximum cardinality search algorithm
+ * discussed in
+ * Robert E Tarjan and Mihalis Yannakakis: Simple linear-time
+ * algorithms to test chordality of graphs, test acyclicity of
+ * hypergraphs, and selectively reduce acyclic hypergraphs.
+ * SIAM Journal of Computation 13, 566--579, 1984.
+ *
+ * \param graph The input graph, which should be undirected and simple.
+ *   of the edges is ignored.
+ * \param alpha Pointer to an initialized vector, the result is stored here.
+ *   It will be resized, as needed. Upon return it contains
+ *   the rank of the each vertex.
+ * \param alpham1 Pointer to an initialized vector or a \c NULL
+ *   pointer. If not \c NULL, then the inverse of \p alpha is stored
+ *   here.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in terms of the number of
+ * vertices and edges.
+ *
+ * \sa \ref igraph_is_chordal().
+ */
+
+int igraph_maximum_cardinality_search(const igraph_t *graph,
+                                      igraph_vector_t *alpha,
+                                      igraph_vector_t *alpham1) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_long_t size;
+    igraph_vector_long_t head, next, prev; /* doubly linked list with head */
+    long int i;
+    igraph_adjlist_t adjlist;
+    igraph_bool_t simple;
+
+    /***************/
+    /* local j, v; */
+    /***************/
+
+    long int j, v;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Maximum cardinality search works on undirected graphs only", IGRAPH_EINVAL);
+    }
+
+    igraph_is_simple(graph, &simple);
+    if (!simple) {
+        IGRAPH_ERROR("Maximum cardinality search works on simple graphs only", IGRAPH_EINVAL);
+    }
+
+    if (no_of_nodes == 0) {
+        igraph_vector_clear(alpha);
+        if (alpham1) {
+            igraph_vector_clear(alpham1);
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    IGRAPH_CHECK(igraph_vector_long_init(&size, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &size);
+    IGRAPH_CHECK(igraph_vector_long_init(&head, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &head);
+    IGRAPH_CHECK(igraph_vector_long_init(&next, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &next);
+    IGRAPH_CHECK(igraph_vector_long_init(&prev, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &prev);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    IGRAPH_CHECK(igraph_vector_resize(alpha, no_of_nodes));
+    if (alpham1) {
+        IGRAPH_CHECK(igraph_vector_resize(alpham1, no_of_nodes));
+    }
+
+    /***********************************************/
+    /* for i in [0,n-1] -> set(i) := emptyset rof; */
+    /***********************************************/
+
+    /* nothing to do, 'head' contains all zeros */
+
+    /*********************************************************/
+    /* for v in vertices -> size(v):=0; add v to set(0) rof; */
+    /*********************************************************/
+
+    VECTOR(head)[0] = 1;
+    for (v = 0; v < no_of_nodes; v++) {
+        VECTOR(next)[v] = v + 2;
+        VECTOR(prev)[v] = v;
+    }
+    VECTOR(next)[no_of_nodes - 1] = 0;
+    /* size is already all zero */
+
+    /***************/
+    /* i:=n; j:=0; */
+    /***************/
+
+    i = no_of_nodes; j = 0;
+
+    /**************/
+    /* do i>=1 -> */
+    /**************/
+
+    while (i >= 1) {
+        long int x, k, len;
+        igraph_vector_int_t *neis;
+
+        /********************************/
+        /* v :=  delete any from set(j) */
+        /********************************/
+
+        v = VECTOR(head)[j] - 1;
+        x = VECTOR(next)[v];
+        VECTOR(head)[j] = x;
+        if (x != 0) {
+            VECTOR(prev)[x - 1] = 0;
+        }
+
+        /*************************************************/
+        /* alpha(v) := i; alpham1(i) := v; size(v) := -1 */
+        /*************************************************/
+
+        VECTOR(*alpha)[v] = i - 1;
+        if (alpham1) {
+            VECTOR(*alpham1)[i - 1] = v;
+        }
+        VECTOR(size)[v] = -1;
+
+        /********************************************/
+        /* for {v,w} in E such that size(w) >= 0 -> */
+        /********************************************/
+
+        neis = igraph_adjlist_get(&adjlist, v);
+        len = igraph_vector_int_size(neis);
+        for (k = 0; k < len; k++) {
+            long int w = (long int) VECTOR(*neis)[k];
+            long int ws = VECTOR(size)[w];
+            if (ws >= 0) {
+
+                /******************************/
+                /* delete w from set(size(w)) */
+                /******************************/
+
+                long int nw = VECTOR(next)[w];
+                long int pw = VECTOR(prev)[w];
+                if (nw != 0) {
+                    VECTOR(prev)[nw - 1] = pw;
+                }
+                if (pw != 0) {
+                    VECTOR(next)[pw - 1] = nw;
+                } else {
+                    VECTOR(head)[ws] = nw;
+                }
+
+                /******************************/
+                /* size(w) := size(w)+1       */
+                /******************************/
+
+                VECTOR(size)[w] += 1;
+
+                /******************************/
+                /* add w to set(size(w))      */
+                /******************************/
+
+                ws = VECTOR(size)[w];
+                nw = VECTOR(head)[ws];
+                VECTOR(next)[w] = nw;
+                VECTOR(prev)[w] = 0;
+                if (nw != 0) {
+                    VECTOR(prev)[nw - 1] = w + 1;
+                }
+                VECTOR(head)[ws] = w + 1;
+
+            }
+        }
+
+        /***********************/
+        /* i := i-1; j := j+1; */
+        /***********************/
+
+        i -= 1;
+        j += 1;
+
+        /*********************************************/
+        /* do j>=0 and set(j)=emptyset -> j:=j-1; od */
+        /*********************************************/
+
+        if (j < no_of_nodes) {
+            while (j >= 0 && VECTOR(head)[j] == 0) {
+                j--;
+            }
+        }
+    }
+
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_long_destroy(&prev);
+    igraph_vector_long_destroy(&next);
+    igraph_vector_long_destroy(&head);
+    igraph_vector_long_destroy(&size);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \function igraph_is_chordal
+ * Decides whether a graph is chordal
+ *
+ * A graph is chordal if each of its cycles of four or more nodes
+ * has a chord, which is an edge joining two nodes that are not
+ * adjacent in the cycle. An equivalent definition is that any
+ * chordless cycles have at most three nodes.
+ *
+ * If either \p alpha or \p alpha1 is given, then the other is
+ * calculated by taking simply the inverse. If neither are given,
+ * then \ref igraph_maximum_cardinality_search() is called to calculate
+ * them.
+ * \param graph The input graph, it might be directed, but edge
+ *    direction is ignored.
+ * \param alpha Either an alpha vector coming from
+ *    \ref igraph_maximum_cardinality_search() (on the same graph), or a
+ *    null pointer.
+ * \param alpham1 Either an inverse alpha vector coming from \ref
+ *    igraph_maximum_cardinality_search() (on the same graph) or a null
+ *    pointer.
+ * \param chordal Pointer to a boolean, the result is stored here.
+ * \param fill_in Pointer to an initialized vector, or a null
+ *    pointer. If not a null pointer, then the fill-in of the graph is
+ *    stored here. The fill-in is the set of edges that are needed to
+ *    make the graph chordal. The vector is resized as needed.
+ * \param newgraph Pointer to an uninitialized graph, or a null
+ *   pointer. If not a null pointer, then a new triangulated graph is
+ *   created here. This essentially means adding the fill-in edges to
+ *   the original graph.
+ * \return Error code.
+ *
+ * Time complexity: O(n).
+ *
+ * \sa \ref igraph_maximum_cardinality_search().
+ */
+
+int igraph_is_chordal(const igraph_t *graph,
+                      const igraph_vector_t *alpha,
+                      const igraph_vector_t *alpham1,
+                      igraph_bool_t *chordal,
+                      igraph_vector_t *fill_in,
+                      igraph_t *newgraph) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    const igraph_vector_t *my_alpha = alpha, *my_alpham1 = alpham1;
+    igraph_vector_t v_alpha, v_alpham1;
+    igraph_vector_long_t f, index;
+    long int i;
+    igraph_adjlist_t adjlist;
+    igraph_vector_long_t mark;
+    igraph_bool_t calc_edges = fill_in || newgraph;
+    igraph_vector_t *my_fill_in = fill_in, v_fill_in;
+
+    /*****************/
+    /* local v, w, x */
+    /*****************/
+
+    long int v, w, x;
+
+    if (!chordal && !calc_edges) {
+        /* Nothing to calculate */
+        return 0;
+    }
+
+    if (!alpha && !alpham1) {
+        IGRAPH_VECTOR_INIT_FINALLY(&v_alpha, no_of_nodes);
+        my_alpha = &v_alpha;
+        IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes);
+        my_alpham1 = &v_alpham1;
+        IGRAPH_CHECK(igraph_maximum_cardinality_search(graph,
+                     (igraph_vector_t*) my_alpha,
+                     (igraph_vector_t*) my_alpham1));
+    } else if (alpha && !alpham1) {
+        long int v;
+        IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes);
+        my_alpham1 = &v_alpham1;
+        for (v = 0; v < no_of_nodes; v++) {
+            long int i = (long int) VECTOR(*my_alpha)[v];
+            VECTOR(*my_alpham1)[i] = v;
+        }
+    } else if (!alpha && alpham1) {
+        long int i;
+        IGRAPH_VECTOR_INIT_FINALLY(&v_alpha, no_of_nodes);
+        my_alpha = &v_alpha;
+        for (i = 0; i < no_of_nodes; i++) {
+            long int v = (long int) VECTOR(*my_alpham1)[i];
+            VECTOR(*my_alpha)[v] = i;
+        }
+    }
+
+    if (!fill_in && newgraph) {
+        IGRAPH_VECTOR_INIT_FINALLY(&v_fill_in, 0);
+        my_fill_in = &v_fill_in;
+    }
+
+    IGRAPH_CHECK(igraph_vector_long_init(&f, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &f);
+    IGRAPH_CHECK(igraph_vector_long_init(&index, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &index);
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+    IGRAPH_CHECK(igraph_vector_long_init(&mark, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &mark);
+    if (my_fill_in) {
+        igraph_vector_clear(my_fill_in);
+    }
+
+    if (chordal) {
+        *chordal = 1;
+    }
+
+    /*********************/
+    /* for i in [1,n] -> */
+    /*********************/
+
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_vector_int_t *neis;
+        long int j, len;
+
+        /**********************************************/
+        /* w := alpham1(i); f(w) := w; index(w) := i; */
+        /**********************************************/
+
+        w = (long int) VECTOR(*my_alpham1)[i];
+        VECTOR(f)[w] = w;
+        VECTOR(index)[w] = i;
+
+        /******************************************/
+        /* for {v,w} in E such that alpha(v)<i -> */
+        /******************************************/
+
+        neis = igraph_adjlist_get(&adjlist, w);
+        len = igraph_vector_int_size(neis);
+        for (j = 0; j < len; j++) {
+            v = (long int) VECTOR(*neis)[j];
+            VECTOR(mark)[v] = w + 1;
+        }
+
+        for (j = 0; j < len; j++) {
+            v = (long int) VECTOR(*neis)[j];
+            if (VECTOR(*my_alpha)[v] >= i) {
+                continue;
+            }
+
+            /**********/
+            /* x := v */
+            /**********/
+
+            x = v;
+
+            /********************/
+            /* do index(x)<i -> */
+            /********************/
+
+            while (VECTOR(index)[x] < i) {
+
+                /******************/
+                /* index(x) := i; */
+                /******************/
+
+                VECTOR(index)[x] = i;
+
+                /**********************************/
+                /* add {x,w} to E union F(alpha); */
+                /**********************************/
+
+                if (VECTOR(mark)[x] != w + 1) {
+
+                    if (chordal) {
+                        *chordal = 0;
+                    }
+
+                    if (my_fill_in) {
+                        IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, x));
+                        IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, w));
+                    }
+
+                    if (!calc_edges) {
+                        /* make sure that we exit from all loops */
+                        i = no_of_nodes;
+                        j = len;
+                        break;
+                    }
+                }
+
+                /*************/
+                /* x := f(x) */
+                /*************/
+
+                x = VECTOR(f)[x];
+
+            } /* while (VECTOR(index)[x] < i) */
+
+            /*****************************/
+            /* if (f(x)=x -> f(x):=w; fi */
+            /*****************************/
+
+            if (VECTOR(f)[x] == x) {
+                VECTOR(f)[x] = w;
+            }
+        }
+    }
+
+    igraph_vector_long_destroy(&mark);
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_long_destroy(&index);
+    igraph_vector_long_destroy(&f);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    if (newgraph) {
+        IGRAPH_CHECK(igraph_copy(newgraph, graph));
+        IGRAPH_FINALLY(igraph_destroy, newgraph);
+        IGRAPH_CHECK(igraph_add_edges(newgraph, my_fill_in, 0));
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (!fill_in && newgraph) {
+        igraph_vector_destroy(&v_fill_in);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (!alpha && !alpham1) {
+        igraph_vector_destroy(&v_alpham1);
+        igraph_vector_destroy(&v_alpha);
+        IGRAPH_FINALLY_CLEAN(2);
+    } else if (alpha && !alpham1) {
+        igraph_vector_destroy(&v_alpham1);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else if (!alpha && alpham1) {
+        igraph_vector_destroy(&v_alpha);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/defs.cc b/igraph/src/defs.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/defs.cc
@@ -0,0 +1,42 @@
+#include <cstdlib>
+#include <cstdio>
+#include "defs.hh"
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+#ifndef USING_R
+
+void
+fatal_error(const char* fmt, ...)
+{
+  va_list ap;
+  va_start(ap, fmt);
+  fprintf(stderr,"Bliss fatal error: ");
+  vfprintf(stderr, fmt, ap);
+  fprintf(stderr, "\nAborting!\n");
+  va_end(ap);
+  exit(1);
+}
+
+#endif
+
+}
diff --git a/igraph/src/degree_sequence.cpp b/igraph/src/degree_sequence.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/degree_sequence.cpp
@@ -0,0 +1,490 @@
+/*
+  Constructing realizations of degree sequences and bi-degree sequences.
+  Copyright (C) 2018 Szabolcs Horvat <szhorvat@gmail.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301 USA
+*/
+
+#include "igraph_constructors.h"
+#include "igraph_interface.h"
+
+#include <vector>
+#include <list>
+#include <algorithm>
+#include <utility>
+
+
+// (vertex, degree) pair
+struct vd_pair {
+    long vertex;
+    igraph_integer_t degree;
+
+    vd_pair(long vertex, igraph_integer_t degree) : vertex(vertex), degree(degree) {}
+};
+
+// (indegree, outdegree)
+typedef std::pair<igraph_integer_t, igraph_integer_t> bidegree;
+
+// (vertex, bidegree) pair
+struct vbd_pair {
+    long vertex;
+    bidegree degree;
+
+    vbd_pair(long vertex, bidegree degree) : vertex(vertex), degree(degree) {}
+};
+
+// Comparison function for vertex-degree pairs.
+// Also used for lexicographic sorting of bi-degrees.
+template<typename T> inline bool degree_greater(const T &a, const T &b) {
+    return a.degree > b.degree;
+}
+
+template<typename T> inline bool degree_less(const T &a, const T &b) {
+    return a.degree < b.degree;
+}
+
+
+// Generate undirected realization as edge-list.
+// If largest=true, always choose the vertex with the largest remaining degree to connect up next.
+// Otherwise, always choose the one with the smallest remaining degree.
+static int igraph_i_havel_hakimi(const igraph_vector_t *deg, igraph_vector_t *edges, bool largest) {
+    long n = igraph_vector_size(deg);
+
+    long ec = 0; // number of edges added so far
+
+    std::vector<vd_pair> vertices;
+    vertices.reserve(n);
+    for (int i = 0; i < n; ++i) {
+        vertices.push_back(vd_pair(i, VECTOR(*deg)[i]));
+    }
+
+    while (! vertices.empty()) {
+        if (largest) {
+            std::stable_sort(vertices.begin(), vertices.end(), degree_less<vd_pair>);
+        } else {
+            std::stable_sort(vertices.begin(), vertices.end(), degree_greater<vd_pair>);
+        }
+
+        // take the next vertex to be connected up
+        vd_pair vd = vertices.back();
+        vertices.pop_back();
+
+        if (vd.degree < 0) {
+            IGRAPH_ERROR("Vertex degrees must be positive", IGRAPH_EINVAL);
+        }
+
+        if (vd.degree == 0) {
+            continue;
+        }
+
+        if (vertices.size() < size_t(vd.degree)) {
+            goto fail;
+        }
+
+        if (largest) {
+            for (int i = 0; i < vd.degree; ++i) {
+                if (--(vertices[vertices.size() - 1 - i].degree) < 0) {
+                    goto fail;
+                }
+
+                VECTOR(*edges)[2 * (ec + i)] = vd.vertex;
+                VECTOR(*edges)[2 * (ec + i) + 1] = vertices[vertices.size() - 1 - i].vertex;
+            }
+        } else {
+            // this loop can only be reached if all zero-degree nodes have already been removed
+            // therefore decrementing remaining degrees is safe
+            for (int i = 0; i < vd.degree; ++i) {
+                vertices[i].degree--;
+
+                VECTOR(*edges)[2 * (ec + i)] = vd.vertex;
+                VECTOR(*edges)[2 * (ec + i) + 1] = vertices[i].vertex;
+            }
+        }
+
+        ec += vd.degree;
+    }
+
+    return IGRAPH_SUCCESS;
+
+fail:
+    IGRAPH_ERROR("The given degree sequence is not realizable", IGRAPH_EINVAL);
+}
+
+
+// Choose vertices in the order of their IDs.
+static int igraph_i_havel_hakimi_index(const igraph_vector_t *deg, igraph_vector_t *edges) {
+    long n = igraph_vector_size(deg);
+
+    long ec = 0; // number of edges added so far
+
+    typedef std::list<vd_pair> vlist;
+    vlist vertices;
+    for (int i = 0; i < n; ++i) {
+        vertices.push_back(vd_pair(i, VECTOR(*deg)[i]));
+    }
+
+    std::vector<vlist::iterator> pointers;
+    pointers.reserve(n);
+    for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) {
+        pointers.push_back(it);
+    }
+
+    for (std::vector<vlist::iterator>::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) {
+        vertices.sort(degree_greater<vd_pair>);
+
+        vd_pair vd = **pt;
+        vertices.erase(*pt);
+
+        if (vd.degree < 0) {
+            IGRAPH_ERROR("Vertex degrees must be positive", IGRAPH_EINVAL);
+        }
+
+        if (vd.degree == 0) {
+            continue;
+        }
+
+        int k;
+        vlist::iterator it;
+        for (it = vertices.begin(), k = 0;
+             k != vd.degree && it != vertices.end();
+             ++it, ++k) {
+            if (--(it->degree) < 0) {
+                goto fail;
+            }
+
+            VECTOR(*edges)[2 * (ec + k)] = vd.vertex;
+            VECTOR(*edges)[2 * (ec + k) + 1] = it->vertex;
+        }
+        if (it == vertices.end() && k < vd.degree) {
+            goto fail;
+        }
+
+        ec += vd.degree;
+    }
+
+    return IGRAPH_SUCCESS;
+
+fail:
+    IGRAPH_ERROR("The given degree sequence is not realizable", IGRAPH_EINVAL);
+}
+
+
+inline bool is_nonzero_outdeg(const vbd_pair &vd) {
+    return (vd.degree.second != 0);
+}
+
+
+// The below implementations of the Kleitman-Wang algorithm follow the description in https://arxiv.org/abs/0905.4913
+
+// Realize bi-degree sequence as edge list
+// If smallest=true, always choose the vertex with "smallest" bi-degree for connecting up next,
+// otherwise choose the "largest" (based on lexicographic bi-degree ordering).
+static int igraph_i_kleitman_wang(const igraph_vector_t *outdeg, const igraph_vector_t *indeg, igraph_vector_t *edges, bool smallest) {
+    long n = igraph_vector_size(indeg); // number of vertices
+
+    long ec = 0; // number of edges added so far
+
+    std::vector<vbd_pair> vertices;
+    vertices.reserve(n);
+    for (int i = 0; i < n; ++i) {
+        vertices.push_back(vbd_pair(i, bidegree(VECTOR(*indeg)[i], VECTOR(*outdeg)[i])));
+    }
+
+    while (true) {
+        // sort vertices by (in, out) degree pairs in decreasing order
+        std::stable_sort(vertices.begin(), vertices.end(), degree_greater<vbd_pair>);
+
+        // remove (0,0)-degree vertices
+        while (!vertices.empty() && vertices.back().degree == bidegree(0, 0)) {
+            vertices.pop_back();
+        }
+
+        // if no vertices remain, stop
+        if (vertices.empty()) {
+            break;
+        }
+
+        // choose a vertex the out-stubs of which will be connected
+        vbd_pair *vdp;
+        if (smallest) {
+            vdp = &*std::find_if(vertices.rbegin(), vertices.rend(), is_nonzero_outdeg);
+        } else {
+            vdp = &*std::find_if(vertices.begin(), vertices.end(), is_nonzero_outdeg);
+        }
+
+
+        if (vdp->degree.first < 0 || vdp->degree.second < 0) {
+            IGRAPH_ERROR("Vertex degrees must be positive", IGRAPH_EINVAL);
+        }
+
+        // are there a sufficient number of other vertices to connect to?
+        if (vertices.size() < vdp->degree.second - 1) {
+            goto fail;
+        }
+
+        // create the connections
+        int k = 0;
+        for (std::vector<vbd_pair>::iterator it = vertices.begin();
+             k < vdp->degree.second;
+             ++it) {
+            if (it->vertex == vdp->vertex) {
+                continue;    // do not create a self-loop
+            }
+            if (--(it->degree.first) < 0) {
+                goto fail;
+            }
+
+            VECTOR(*edges)[2 * (ec + k)] = vdp->vertex;
+            VECTOR(*edges)[2 * (ec + k) + 1] = it->vertex;
+
+            k++;
+        }
+
+        ec += vdp->degree.second;
+        vdp->degree.second = 0;
+    }
+
+    return IGRAPH_SUCCESS;
+
+fail:
+    IGRAPH_ERROR("The given directed degree sequence is not realizable", IGRAPH_EINVAL);
+}
+
+
+// Choose vertices in the order of their IDs.
+static int igraph_i_kleitman_wang_index(const igraph_vector_t *outdeg, const igraph_vector_t *indeg, igraph_vector_t *edges) {
+    long n = igraph_vector_size(indeg); // number of vertices
+
+    long ec = 0; // number of edges added so far
+
+    typedef std::list<vbd_pair> vlist;
+    vlist vertices;
+    for (int i = 0; i < n; ++i) {
+        vertices.push_back(vbd_pair(i, bidegree(VECTOR(*indeg)[i], VECTOR(*outdeg)[i])));
+    }
+
+    std::vector<vlist::iterator> pointers;
+    pointers.reserve(n);
+    for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) {
+        pointers.push_back(it);
+    }
+
+    for (std::vector<vlist::iterator>::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) {
+        // sort vertices by (in, out) degree pairs in decreasing order
+        // note: std::list::sort does a stable sort
+        vertices.sort(degree_greater<vbd_pair>);
+
+        // choose a vertex the out-stubs of which will be connected
+        vbd_pair &vd = **pt;
+
+        if (vd.degree.second == 0) {
+            continue;
+        }
+
+        if (vd.degree.first < 0 || vd.degree.second < 0) {
+            IGRAPH_ERROR("Vertex degrees must be positive", IGRAPH_EINVAL);
+        }
+
+        int k = 0;
+        vlist::iterator it;
+        for (it = vertices.begin();
+             k != vd.degree.second && it != vertices.end();
+             ++it) {
+            if (it->vertex == vd.vertex) {
+                continue;
+            }
+
+            if (--(it->degree.first) < 0) {
+                goto fail;
+            }
+
+            VECTOR(*edges)[2 * (ec + k)] = vd.vertex;
+            VECTOR(*edges)[2 * (ec + k) + 1] = it->vertex;
+
+            ++k;
+        }
+        if (it == vertices.end() && k < vd.degree.second) {
+            goto fail;
+        }
+
+        ec += vd.degree.second;
+        vd.degree.second = 0;
+    }
+
+    return IGRAPH_SUCCESS;
+
+fail:
+    IGRAPH_ERROR("The given directed degree sequence is not realizable", IGRAPH_EINVAL);
+}
+
+
+static int igraph_i_realize_undirected_degree_sequence(
+    igraph_t *graph,
+    const igraph_vector_t *deg,
+    igraph_realize_degseq_t method) {
+    long node_count = igraph_vector_size(deg);
+    long deg_sum = long(igraph_vector_sum(deg));
+
+    if (deg_sum % 2 != 0) {
+        IGRAPH_ERROR("The sum of degrees must be even for an undirected graph", IGRAPH_EINVAL);
+    }
+
+    igraph_vector_t edges;
+    IGRAPH_CHECK(igraph_vector_init(&edges, deg_sum));
+    IGRAPH_FINALLY(igraph_vector_destroy, &edges);
+
+    switch (method) {
+    case IGRAPH_REALIZE_DEGSEQ_SMALLEST:
+        IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, false));
+        break;
+    case IGRAPH_REALIZE_DEGSEQ_LARGEST:
+        IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, true));
+        break;
+    case IGRAPH_REALIZE_DEGSEQ_INDEX:
+        IGRAPH_CHECK(igraph_i_havel_hakimi_index(deg, &edges));
+        break;
+    default:
+        IGRAPH_ERROR("Invalid degree sequence realization method", IGRAPH_EINVAL);
+    }
+
+    igraph_create(graph, &edges, igraph_integer_t(node_count), false);
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+static int igraph_i_realize_directed_degree_sequence(
+    igraph_t *graph,
+    const igraph_vector_t *outdeg,
+    const igraph_vector_t *indeg,
+    igraph_realize_degseq_t method) {
+    long node_count = igraph_vector_size(outdeg);
+    long edge_count = long(igraph_vector_sum(outdeg));
+
+    if (igraph_vector_size(indeg) != node_count) {
+        IGRAPH_ERROR("In- and out-degree sequences must have the same length", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_sum(indeg) != edge_count) {
+        IGRAPH_ERROR("In- and out-degree sequences do not sum to the same value", IGRAPH_EINVAL);
+    }
+
+    igraph_vector_t edges;
+    IGRAPH_CHECK(igraph_vector_init(&edges, 2 * edge_count));
+    IGRAPH_FINALLY(igraph_vector_destroy, &edges);
+
+    switch (method) {
+    case IGRAPH_REALIZE_DEGSEQ_SMALLEST:
+        IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, true));
+        break;
+    case IGRAPH_REALIZE_DEGSEQ_LARGEST:
+        IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, false));
+        break;
+    case IGRAPH_REALIZE_DEGSEQ_INDEX:
+        IGRAPH_CHECK(igraph_i_kleitman_wang_index(outdeg, indeg, &edges));
+        break;
+    default:
+        IGRAPH_ERROR("Invalid bi-degree sequence realization method", IGRAPH_EINVAL);
+    }
+
+    igraph_create(graph, &edges, igraph_integer_t(node_count), true);
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/**
+ * \ingroup generators
+ * \function igraph_realize_degree_sequence
+ * \brief Generates a graph with the given degree sequence
+ *
+ * This function constructs a simple graph that realizes the given degree sequence
+ * using the Havel-Hakimi algorithm, or the given (directed) out- and in-degree
+ * sequences using the related Kleitman-Wang algorithm.
+ *
+ * The algorithms work by choosing an arbitrary vertex and connecting all its stubs
+ * to other vertices of highest degree.  In the directed case, the "highest" (in, out) degree
+ * pairs are determined based on lexicographic ordering.
+ *
+ * The \c method parameter controls the order in which the vertices to be connected are chosen.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param outdeg The degree sequence for a simple undirected graph
+ *        (if \p indeg is NULL or of length zero), or the out-degree sequence of
+ *        a directed graph (if \p indeg is of nonzero size).
+ * \param indeg It is either a zero-length vector or \c NULL (if an undirected graph
+ *        is generated), or the in-degree sequence.
+ * \param method The method to generate the graph. Possible values:
+ *        \clist
+ *          \cli IGRAPH_REALIZE_DEGSEQ_SMALLEST
+ *          The vertex with smallest remaining degree is selected first. The result is usually
+ *          a graph with high negative degree assortativity. In the undirected case, this method
+ *          is guaranteed to generate a connected graph, provided that a connected realization exists.
+ *          See http://szhorvat.net/pelican/hh-connected-graphs.html for a proof.
+ *          In the directed case it tends to generate weakly connected graphs, but this is not
+ *          guaranteed.
+ *          \cli IGRAPH_REALIZE_DEGSEQ_LARGEST
+ *          The vertex with the largest remaining degree is selected first. The result
+ *          is usually a graph with high positive degree assortativity, and is often disconnected.
+ *          \cli IGRAPH_REALIZE_DEGSEQ_INDEX
+ *          The vertices are selected in order of their index (i.e. their position in the degree vector).
+ *          Note that sorting the degree vector and using the \c INDEX method is not equivalent
+ *          to the \c SMALLEST method above, as \c SMALLEST uses the smallest \em remaining
+ *          degree for selecting vertices, not the smallest \em initial degree.
+ *         \endclist
+ * \return Error code:
+ *          \clist
+ *          \cli IGRAPH_ENOMEM
+ *           There is not enough memory to perform the operation.
+ *          \cli IGRAPH_EINVAL
+ *           Invalid method parameter, or invalid in- and/or out-degree vectors.
+ *           The degree vectors should be non-negative, the length
+ *           and sum of \p outdeg and \p indeg should match for directed graphs.
+ *          \endclist
+ *
+ * \sa  \ref igraph_is_graphical_degree_sequence()
+ *      \ref igraph_degree_sequence_game()
+ *      \ref igraph_k_regular_game()
+ *      \ref igraph_rewire()
+ *
+ */
+
+int igraph_realize_degree_sequence(
+    igraph_t *graph,
+    const igraph_vector_t *outdeg, const igraph_vector_t *indeg,
+    igraph_realize_degseq_t method) {
+    long n = igraph_vector_size(outdeg);
+    if (n != igraph_integer_t(n)) { // does the vector size fit into an igraph_integer_t ?
+        IGRAPH_ERROR("Degree sequence vector too long", IGRAPH_EINVAL);
+    }
+
+    bool directed = bool(indeg) && igraph_vector_size(indeg) != 0;
+
+    try {
+        if (directed) {
+            return igraph_i_realize_directed_degree_sequence(graph, outdeg, indeg, method);
+        } else {
+            return igraph_i_realize_undirected_degree_sequence(graph, outdeg, method);
+        }
+    } catch (const std::bad_alloc &) {
+        IGRAPH_ERROR("Cannot realize degree sequence due to insufficient memory", IGRAPH_ENOMEM);
+    }
+}
diff --git a/igraph/src/derf_.c b/igraph/src/derf_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/derf_.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double erf();
+double derf_(x) doublereal *x;
+#else
+extern double erf(double);
+double derf_(doublereal *x)
+#endif
+{
+return( erf(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/derfc_.c b/igraph/src/derfc_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/derfc_.c
@@ -0,0 +1,20 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern double erfc();
+
+double derfc_(x) doublereal *x;
+#else
+extern double erfc(double);
+
+double derfc_(doublereal *x)
+#endif
+{
+return( erfc(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/dfe.c b/igraph/src/dfe.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dfe.c
@@ -0,0 +1,151 @@
+#include "f2c.h"
+#include "fio.h"
+#include "fmt.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ int
+y_rsk(Void)
+{
+	if(f__curunit->uend || f__curunit->url <= f__recpos
+		|| f__curunit->url == 1) return 0;
+	do {
+		getc(f__cf);
+	} while(++f__recpos < f__curunit->url);
+	return 0;
+}
+
+ int
+y_getc(Void)
+{
+	int ch;
+	if(f__curunit->uend) return(-1);
+	if((ch=getc(f__cf))!=EOF)
+	{
+		f__recpos++;
+		if(f__curunit->url>=f__recpos ||
+			f__curunit->url==1)
+			return(ch);
+		else	return(' ');
+	}
+	if(feof(f__cf))
+	{
+		f__curunit->uend=1;
+		errno=0;
+		return(-1);
+	}
+	err(f__elist->cierr,errno,"readingd");
+}
+
+ static int
+y_rev(Void)
+{
+	if (f__recpos < f__hiwater)
+		f__recpos = f__hiwater;
+	if (f__curunit->url > 1)
+		while(f__recpos < f__curunit->url)
+			(*f__putn)(' ');
+	if (f__recpos)
+		f__putbuf(0);
+	f__recpos = 0;
+	return(0);
+}
+
+ static int
+y_err(Void)
+{
+	err(f__elist->cierr, 110, "dfe");
+}
+
+ static int
+y_newrec(Void)
+{
+	y_rev();
+	f__hiwater = f__cursor = 0;
+	return(1);
+}
+
+ int
+#ifdef KR_headers
+c_dfe(a) cilist *a;
+#else
+c_dfe(cilist *a)
+#endif
+{
+	f__sequential=0;
+	f__formatted=f__external=1;
+	f__elist=a;
+	f__cursor=f__scale=f__recpos=0;
+	f__curunit = &f__units[a->ciunit];
+	if(a->ciunit>MXUNIT || a->ciunit<0)
+		err(a->cierr,101,"startchk");
+	if(f__curunit->ufd==NULL && fk_open(DIR,FMT,a->ciunit))
+		err(a->cierr,104,"dfe");
+	f__cf=f__curunit->ufd;
+	if(!f__curunit->ufmt) err(a->cierr,102,"dfe")
+	if(!f__curunit->useek) err(a->cierr,104,"dfe")
+	f__fmtbuf=a->cifmt;
+	if(a->cirec <= 0)
+		err(a->cierr,130,"dfe")
+	FSEEK(f__cf,(OFF_T)f__curunit->url * (a->cirec-1),SEEK_SET);
+	f__curunit->uend = 0;
+	return(0);
+}
+#ifdef KR_headers
+integer s_rdfe(a) cilist *a;
+#else
+integer s_rdfe(cilist *a)
+#endif
+{
+	int n;
+	if(!f__init) f_init();
+	f__reading=1;
+	if(n=c_dfe(a))return(n);
+	if(f__curunit->uwrt && f__nowreading(f__curunit))
+		err(a->cierr,errno,"read start");
+	f__getn = y_getc;
+	f__doed = rd_ed;
+	f__doned = rd_ned;
+	f__dorevert = f__donewrec = y_err;
+	f__doend = y_rsk;
+	if(pars_f(f__fmtbuf)<0)
+		err(a->cierr,100,"read start");
+	fmt_bg();
+	return(0);
+}
+#ifdef KR_headers
+integer s_wdfe(a) cilist *a;
+#else
+integer s_wdfe(cilist *a)
+#endif
+{
+	int n;
+	if(!f__init) f_init();
+	f__reading=0;
+	if(n=c_dfe(a)) return(n);
+	if(f__curunit->uwrt != 1 && f__nowwriting(f__curunit))
+		err(a->cierr,errno,"startwrt");
+	f__putn = x_putc;
+	f__doed = w_ed;
+	f__doned= w_ned;
+	f__dorevert = y_err;
+	f__donewrec = y_newrec;
+	f__doend = y_rev;
+	if(pars_f(f__fmtbuf)<0)
+		err(a->cierr,100,"startwrt");
+	fmt_bg();
+	return(0);
+}
+integer e_rdfe(Void)
+{
+	en_fio();
+	return 0;
+}
+integer e_wdfe(Void)
+{
+	return en_fio();
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/dgebak.c b/igraph/src/dgebak.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgebak.c
@@ -0,0 +1,301 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DGEBAK   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGEBAK + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgebak.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgebak.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgebak.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGEBAK( JOB, SIDE, N, ILO, IHI, SCALE, M, V, LDV,   
+                            INFO )   
+
+         CHARACTER          JOB, SIDE   
+         INTEGER            IHI, ILO, INFO, LDV, M, N   
+         DOUBLE PRECISION   SCALE( * ), V( LDV, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGEBAK forms the right or left eigenvectors of a real general matrix   
+   > by backward transformation on the computed eigenvectors of the   
+   > balanced matrix output by DGEBAL.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOB   
+   > \verbatim   
+   >          JOB is CHARACTER*1   
+   >          Specifies the type of backward transformation required:   
+   >          = 'N', do nothing, return immediately;   
+   >          = 'P', do backward transformation for permutation only;   
+   >          = 'S', do backward transformation for scaling only;   
+   >          = 'B', do backward transformations for both permutation and   
+   >                 scaling.   
+   >          JOB must be the same as the argument JOB supplied to DGEBAL.   
+   > \endverbatim   
+   >   
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'R':  V contains right eigenvectors;   
+   >          = 'L':  V contains left eigenvectors.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of rows of the matrix V.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >          The integers ILO and IHI determined by DGEBAL.   
+   >          1 <= ILO <= IHI <= N, if N > 0; ILO=1 and IHI=0, if N=0.   
+   > \endverbatim   
+   >   
+   > \param[in] SCALE   
+   > \verbatim   
+   >          SCALE is DOUBLE PRECISION array, dimension (N)   
+   >          Details of the permutation and scaling factors, as returned   
+   >          by DGEBAL.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of columns of the matrix V.  M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array, dimension (LDV,M)   
+   >          On entry, the matrix of right or left eigenvectors to be   
+   >          transformed, as returned by DHSEIN or DTREVC.   
+   >          On exit, V is overwritten by the transformed eigenvectors.   
+   > \endverbatim   
+   >   
+   > \param[in] LDV   
+   > \verbatim   
+   >          LDV is INTEGER   
+   >          The leading dimension of the array V. LDV >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleGEcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdgebak_(char *job, char *side, integer *n, integer *ilo, 
+	integer *ihi, doublereal *scale, integer *m, doublereal *v, integer *
+	ldv, integer *info)
+{
+    /* System generated locals */
+    integer v_dim1, v_offset, i__1;
+
+    /* Local variables */
+    integer i__, k;
+    doublereal s;
+    integer ii;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdswap_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    logical leftv;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical rightv;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Decode and Test the input parameters   
+
+       Parameter adjustments */
+    --scale;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+
+    /* Function Body */
+    rightv = igraphlsame_(side, "R");
+    leftv = igraphlsame_(side, "L");
+
+    *info = 0;
+    if (! igraphlsame_(job, "N") && ! igraphlsame_(job, "P") && ! igraphlsame_(job, "S") 
+	    && ! igraphlsame_(job, "B")) {
+	*info = -1;
+    } else if (! rightv && ! leftv) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -3;
+    } else if (*ilo < 1 || *ilo > max(1,*n)) {
+	*info = -4;
+    } else if (*ihi < min(*ilo,*n) || *ihi > *n) {
+	*info = -5;
+    } else if (*m < 0) {
+	*info = -7;
+    } else if (*ldv < max(1,*n)) {
+	*info = -9;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGEBAK", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+    if (*m == 0) {
+	return 0;
+    }
+    if (igraphlsame_(job, "N")) {
+	return 0;
+    }
+
+    if (*ilo == *ihi) {
+	goto L30;
+    }
+
+/*     Backward balance */
+
+    if (igraphlsame_(job, "S") || igraphlsame_(job, "B")) {
+
+	if (rightv) {
+	    i__1 = *ihi;
+	    for (i__ = *ilo; i__ <= i__1; ++i__) {
+		s = scale[i__];
+		igraphdscal_(m, &s, &v[i__ + v_dim1], ldv);
+/* L10: */
+	    }
+	}
+
+	if (leftv) {
+	    i__1 = *ihi;
+	    for (i__ = *ilo; i__ <= i__1; ++i__) {
+		s = 1. / scale[i__];
+		igraphdscal_(m, &s, &v[i__ + v_dim1], ldv);
+/* L20: */
+	    }
+	}
+
+    }
+
+/*     Backward permutation   
+
+       For  I = ILO-1 step -1 until 1,   
+                IHI+1 step 1 until N do -- */
+
+L30:
+    if (igraphlsame_(job, "P") || igraphlsame_(job, "B")) {
+	if (rightv) {
+	    i__1 = *n;
+	    for (ii = 1; ii <= i__1; ++ii) {
+		i__ = ii;
+		if (i__ >= *ilo && i__ <= *ihi) {
+		    goto L40;
+		}
+		if (i__ < *ilo) {
+		    i__ = *ilo - ii;
+		}
+		k = (integer) scale[i__];
+		if (k == i__) {
+		    goto L40;
+		}
+		igraphdswap_(m, &v[i__ + v_dim1], ldv, &v[k + v_dim1], ldv);
+L40:
+		;
+	    }
+	}
+
+	if (leftv) {
+	    i__1 = *n;
+	    for (ii = 1; ii <= i__1; ++ii) {
+		i__ = ii;
+		if (i__ >= *ilo && i__ <= *ihi) {
+		    goto L50;
+		}
+		if (i__ < *ilo) {
+		    i__ = *ilo - ii;
+		}
+		k = (integer) scale[i__];
+		if (k == i__) {
+		    goto L50;
+		}
+		igraphdswap_(m, &v[i__ + v_dim1], ldv, &v[k + v_dim1], ldv);
+L50:
+		;
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DGEBAK */
+
+} /* igraphdgebak_ */
+
diff --git a/igraph/src/dgebal.c b/igraph/src/dgebal.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgebal.c
@@ -0,0 +1,466 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DGEBAL   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGEBAL + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgebal.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgebal.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgebal.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGEBAL( JOB, N, A, LDA, ILO, IHI, SCALE, INFO )   
+
+         CHARACTER          JOB   
+         INTEGER            IHI, ILO, INFO, LDA, N   
+         DOUBLE PRECISION   A( LDA, * ), SCALE( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGEBAL balances a general real matrix A.  This involves, first,   
+   > permuting A by a similarity transformation to isolate eigenvalues   
+   > in the first 1 to ILO-1 and last IHI+1 to N elements on the   
+   > diagonal; and second, applying a diagonal similarity transformation   
+   > to rows and columns ILO to IHI to make the rows and columns as   
+   > close in norm as possible.  Both steps are optional.   
+   >   
+   > Balancing may reduce the 1-norm of the matrix, and improve the   
+   > accuracy of the computed eigenvalues and/or eigenvectors.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOB   
+   > \verbatim   
+   >          JOB is CHARACTER*1   
+   >          Specifies the operations to be performed on A:   
+   >          = 'N':  none:  simply set ILO = 1, IHI = N, SCALE(I) = 1.0   
+   >                  for i = 1,...,N;   
+   >          = 'P':  permute only;   
+   >          = 'S':  scale only;   
+   >          = 'B':  both permute and scale.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE array, dimension (LDA,N)   
+   >          On entry, the input matrix A.   
+   >          On exit,  A is overwritten by the balanced matrix.   
+   >          If JOB = 'N', A is not referenced.   
+   >          See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   > \param[out] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >          ILO and IHI are set to integers such that on exit   
+   >          A(i,j) = 0 if i > j and j = 1,...,ILO-1 or I = IHI+1,...,N.   
+   >          If JOB = 'N' or 'S', ILO = 1 and IHI = N.   
+   > \endverbatim   
+   >   
+   > \param[out] SCALE   
+   > \verbatim   
+   >          SCALE is DOUBLE array, dimension (N)   
+   >          Details of the permutations and scaling factors applied to   
+   >          A.  If P(j) is the index of the row and column interchanged   
+   >          with row and column j and D(j) is the scaling factor   
+   >          applied to row and column j, then   
+   >          SCALE(j) = P(j)    for j = 1,...,ILO-1   
+   >                   = D(j)    for j = ILO,...,IHI   
+   >                   = P(j)    for j = IHI+1,...,N.   
+   >          The order in which the interchanges are made is N to IHI+1,   
+   >          then 1 to ILO-1.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit.   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2013   
+
+   > \ingroup doubleGEcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The permutations consist of row and column interchanges which put   
+   >  the matrix in the form   
+   >   
+   >             ( T1   X   Y  )   
+   >     P A P = (  0   B   Z  )   
+   >             (  0   0   T2 )   
+   >   
+   >  where T1 and T2 are upper triangular matrices whose eigenvalues lie   
+   >  along the diagonal.  The column indices ILO and IHI mark the starting   
+   >  and ending columns of the submatrix B. Balancing consists of applying   
+   >  a diagonal similarity transformation inv(D) * B * D to make the   
+   >  1-norms of each row of B and its corresponding column nearly equal.   
+   >  The output matrix is   
+   >   
+   >     ( T1     X*D          Y    )   
+   >     (  0  inv(D)*B*D  inv(D)*Z ).   
+   >     (  0      0           T2   )   
+   >   
+   >  Information about the permutations P and the diagonal matrix D is   
+   >  returned in the vector SCALE.   
+   >   
+   >  This subroutine is based on the EISPACK routine BALANC.   
+   >   
+   >  Modified by Tzu-Yi Chen, Computer Science Division, University of   
+   >    California at Berkeley, USA   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdgebal_(char *job, integer *n, doublereal *a, integer *
+	lda, integer *ilo, integer *ihi, doublereal *scale, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    doublereal c__, f, g;
+    integer i__, j, k, l, m;
+    doublereal r__, s, ca, ra;
+    integer ica, ira, iexc;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdswap_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    doublereal sfmin1, sfmin2, sfmax1, sfmax2;
+    extern doublereal igraphdlamch_(char *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    extern logical igraphdisnan_(doublereal *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical noconv;
+
+
+/*  -- LAPACK computational routine (version 3.5.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2013   
+
+
+    =====================================================================   
+
+
+       Test the input parameters   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --scale;
+
+    /* Function Body */
+    *info = 0;
+    if (! igraphlsame_(job, "N") && ! igraphlsame_(job, "P") && ! igraphlsame_(job, "S") 
+	    && ! igraphlsame_(job, "B")) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*n)) {
+	*info = -4;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGEBAL", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+    k = 1;
+    l = *n;
+
+    if (*n == 0) {
+	goto L210;
+    }
+
+    if (igraphlsame_(job, "N")) {
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    scale[i__] = 1.;
+/* L10: */
+	}
+	goto L210;
+    }
+
+    if (igraphlsame_(job, "S")) {
+	goto L120;
+    }
+
+/*     Permutation to isolate eigenvalues if possible */
+
+    goto L50;
+
+/*     Row and column exchange. */
+
+L20:
+    scale[m] = (doublereal) j;
+    if (j == m) {
+	goto L30;
+    }
+
+    igraphdswap_(&l, &a[j * a_dim1 + 1], &c__1, &a[m * a_dim1 + 1], &c__1);
+    i__1 = *n - k + 1;
+    igraphdswap_(&i__1, &a[j + k * a_dim1], lda, &a[m + k * a_dim1], lda);
+
+L30:
+    switch (iexc) {
+	case 1:  goto L40;
+	case 2:  goto L80;
+    }
+
+/*     Search for rows isolating an eigenvalue and push them down. */
+
+L40:
+    if (l == 1) {
+	goto L210;
+    }
+    --l;
+
+L50:
+    for (j = l; j >= 1; --j) {
+
+	i__1 = l;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    if (i__ == j) {
+		goto L60;
+	    }
+	    if (a[j + i__ * a_dim1] != 0.) {
+		goto L70;
+	    }
+L60:
+	    ;
+	}
+
+	m = l;
+	iexc = 1;
+	goto L20;
+L70:
+	;
+    }
+
+    goto L90;
+
+/*     Search for columns isolating an eigenvalue and push them left. */
+
+L80:
+    ++k;
+
+L90:
+    i__1 = l;
+    for (j = k; j <= i__1; ++j) {
+
+	i__2 = l;
+	for (i__ = k; i__ <= i__2; ++i__) {
+	    if (i__ == j) {
+		goto L100;
+	    }
+	    if (a[i__ + j * a_dim1] != 0.) {
+		goto L110;
+	    }
+L100:
+	    ;
+	}
+
+	m = k;
+	iexc = 2;
+	goto L20;
+L110:
+	;
+    }
+
+L120:
+    i__1 = l;
+    for (i__ = k; i__ <= i__1; ++i__) {
+	scale[i__] = 1.;
+/* L130: */
+    }
+
+    if (igraphlsame_(job, "P")) {
+	goto L210;
+    }
+
+/*     Balance the submatrix in rows K to L.   
+
+       Iterative loop for norm reduction */
+
+    sfmin1 = igraphdlamch_("S") / igraphdlamch_("P");
+    sfmax1 = 1. / sfmin1;
+    sfmin2 = sfmin1 * 2.;
+    sfmax2 = 1. / sfmin2;
+
+L140:
+    noconv = FALSE_;
+
+    i__1 = l;
+    for (i__ = k; i__ <= i__1; ++i__) {
+
+	i__2 = l - k + 1;
+	c__ = igraphdnrm2_(&i__2, &a[k + i__ * a_dim1], &c__1);
+	i__2 = l - k + 1;
+	r__ = igraphdnrm2_(&i__2, &a[i__ + k * a_dim1], lda);
+	ica = igraphidamax_(&l, &a[i__ * a_dim1 + 1], &c__1);
+	ca = (d__1 = a[ica + i__ * a_dim1], abs(d__1));
+	i__2 = *n - k + 1;
+	ira = igraphidamax_(&i__2, &a[i__ + k * a_dim1], lda);
+	ra = (d__1 = a[i__ + (ira + k - 1) * a_dim1], abs(d__1));
+
+/*        Guard against zero C or R due to underflow. */
+
+	if (c__ == 0. || r__ == 0.) {
+	    goto L200;
+	}
+	g = r__ / 2.;
+	f = 1.;
+	s = c__ + r__;
+L160:
+/* Computing MAX */
+	d__1 = max(f,c__);
+/* Computing MIN */
+	d__2 = min(r__,g);
+	if (c__ >= g || max(d__1,ca) >= sfmax2 || min(d__2,ra) <= sfmin2) {
+	    goto L170;
+	}
+	d__1 = c__ + f + ca + r__ + g + ra;
+	if (igraphdisnan_(&d__1)) {
+
+/*           Exit if NaN to avoid infinite loop */
+
+	    *info = -3;
+	    i__2 = -(*info);
+	    igraphxerbla_("DGEBAL", &i__2, (ftnlen)6);
+	    return 0;
+	}
+	f *= 2.;
+	c__ *= 2.;
+	ca *= 2.;
+	r__ /= 2.;
+	g /= 2.;
+	ra /= 2.;
+	goto L160;
+
+L170:
+	g = c__ / 2.;
+L180:
+/* Computing MIN */
+	d__1 = min(f,c__), d__1 = min(d__1,g);
+	if (g < r__ || max(r__,ra) >= sfmax2 || min(d__1,ca) <= sfmin2) {
+	    goto L190;
+	}
+	f /= 2.;
+	c__ /= 2.;
+	g /= 2.;
+	ca /= 2.;
+	r__ *= 2.;
+	ra *= 2.;
+	goto L180;
+
+/*        Now balance. */
+
+L190:
+	if (c__ + r__ >= s * .95) {
+	    goto L200;
+	}
+	if (f < 1. && scale[i__] < 1.) {
+	    if (f * scale[i__] <= sfmin1) {
+		goto L200;
+	    }
+	}
+	if (f > 1. && scale[i__] > 1.) {
+	    if (scale[i__] >= sfmax1 / f) {
+		goto L200;
+	    }
+	}
+	g = 1. / f;
+	scale[i__] *= f;
+	noconv = TRUE_;
+
+	i__2 = *n - k + 1;
+	igraphdscal_(&i__2, &g, &a[i__ + k * a_dim1], lda);
+	igraphdscal_(&l, &f, &a[i__ * a_dim1 + 1], &c__1);
+
+L200:
+	;
+    }
+
+    if (noconv) {
+	goto L140;
+    }
+
+L210:
+    *ilo = k;
+    *ihi = l;
+
+    return 0;
+
+/*     End of DGEBAL */
+
+} /* igraphdgebal_ */
+
diff --git a/igraph/src/dgeev.c b/igraph/src/dgeev.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgeev.c
@@ -0,0 +1,644 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c__0 = 0;
+static integer c_n1 = -1;
+
+/* > \brief <b> DGEEV computes the eigenvalues and, optionally, the left and/or right eigenvectors for GE matr
+ices</b>   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGEEV + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgeev.f
+">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgeev.f
+">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgeev.f
+">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGEEV( JOBVL, JOBVR, N, A, LDA, WR, WI, VL, LDVL, VR,   
+                           LDVR, WORK, LWORK, INFO )   
+
+         CHARACTER          JOBVL, JOBVR   
+         INTEGER            INFO, LDA, LDVL, LDVR, LWORK, N   
+         DOUBLE PRECISION   A( LDA, * ), VL( LDVL, * ), VR( LDVR, * ),   
+        $                   WI( * ), WORK( * ), WR( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGEEV computes for an N-by-N real nonsymmetric matrix A, the   
+   > eigenvalues and, optionally, the left and/or right eigenvectors.   
+   >   
+   > The right eigenvector v(j) of A satisfies   
+   >                  A * v(j) = lambda(j) * v(j)   
+   > where lambda(j) is its eigenvalue.   
+   > The left eigenvector u(j) of A satisfies   
+   >               u(j)**H * A = lambda(j) * u(j)**H   
+   > where u(j)**H denotes the conjugate-transpose of u(j).   
+   >   
+   > The computed eigenvectors are normalized to have Euclidean norm   
+   > equal to 1 and largest component real.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOBVL   
+   > \verbatim   
+   >          JOBVL is CHARACTER*1   
+   >          = 'N': left eigenvectors of A are not computed;   
+   >          = 'V': left eigenvectors of A are computed.   
+   > \endverbatim   
+   >   
+   > \param[in] JOBVR   
+   > \verbatim   
+   >          JOBVR is CHARACTER*1   
+   >          = 'N': right eigenvectors of A are not computed;   
+   >          = 'V': right eigenvectors of A are computed.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the N-by-N matrix A.   
+   >          On exit, A has been overwritten.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] WR   
+   > \verbatim   
+   >          WR is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] WI   
+   > \verbatim   
+   >          WI is DOUBLE PRECISION array, dimension (N)   
+   >          WR and WI contain the real and imaginary parts,   
+   >          respectively, of the computed eigenvalues.  Complex   
+   >          conjugate pairs of eigenvalues appear consecutively   
+   >          with the eigenvalue having the positive imaginary part   
+   >          first.   
+   > \endverbatim   
+   >   
+   > \param[out] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION array, dimension (LDVL,N)   
+   >          If JOBVL = 'V', the left eigenvectors u(j) are stored one   
+   >          after another in the columns of VL, in the same order   
+   >          as their eigenvalues.   
+   >          If JOBVL = 'N', VL is not referenced.   
+   >          If the j-th eigenvalue is real, then u(j) = VL(:,j),   
+   >          the j-th column of VL.   
+   >          If the j-th and (j+1)-st eigenvalues form a complex   
+   >          conjugate pair, then u(j) = VL(:,j) + i*VL(:,j+1) and   
+   >          u(j+1) = VL(:,j) - i*VL(:,j+1).   
+   > \endverbatim   
+   >   
+   > \param[in] LDVL   
+   > \verbatim   
+   >          LDVL is INTEGER   
+   >          The leading dimension of the array VL.  LDVL >= 1; if   
+   >          JOBVL = 'V', LDVL >= N.   
+   > \endverbatim   
+   >   
+   > \param[out] VR   
+   > \verbatim   
+   >          VR is DOUBLE PRECISION array, dimension (LDVR,N)   
+   >          If JOBVR = 'V', the right eigenvectors v(j) are stored one   
+   >          after another in the columns of VR, in the same order   
+   >          as their eigenvalues.   
+   >          If JOBVR = 'N', VR is not referenced.   
+   >          If the j-th eigenvalue is real, then v(j) = VR(:,j),   
+   >          the j-th column of VR.   
+   >          If the j-th and (j+1)-st eigenvalues form a complex   
+   >          conjugate pair, then v(j) = VR(:,j) + i*VR(:,j+1) and   
+   >          v(j+1) = VR(:,j) - i*VR(:,j+1).   
+   > \endverbatim   
+   >   
+   > \param[in] LDVR   
+   > \verbatim   
+   >          LDVR is INTEGER   
+   >          The leading dimension of the array VR.  LDVR >= 1; if   
+   >          JOBVR = 'V', LDVR >= N.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.  LWORK >= max(1,3*N), and   
+   >          if JOBVL = 'V' or JOBVR = 'V', LWORK >= 4*N.  For good   
+   >          performance, LWORK must generally be larger.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value.   
+   >          > 0:  if INFO = i, the QR algorithm failed to compute all the   
+   >                eigenvalues, and no eigenvectors have been computed;   
+   >                elements i+1:N of WR and WI contain eigenvalues which   
+   >                have converged.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleGEeigen   
+
+    =====================================================================   
+   Subroutine */ int igraphdgeev_(char *jobvl, char *jobvr, integer *n, doublereal *
+	a, integer *lda, doublereal *wr, doublereal *wi, doublereal *vl, 
+	integer *ldvl, doublereal *vr, integer *ldvr, doublereal *work, 
+	integer *lwork, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, vl_dim1, vl_offset, vr_dim1, vr_offset, i__1, 
+	    i__2, i__3;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, k;
+    doublereal r__, cs, sn;
+    integer ihi;
+    doublereal scl;
+    integer ilo;
+    doublereal dum[1], eps;
+    integer ibal;
+    char side[1];
+    doublereal anrm;
+    integer ierr, itau;
+    extern /* Subroutine */ int igraphdrot_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *);
+    integer iwrk, nout;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    extern logical igraphlsame_(char *, char *);
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlabad_(doublereal *, doublereal *), igraphdgebak_(
+	    char *, char *, integer *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *), 
+	    igraphdgebal_(char *, integer *, doublereal *, integer *, integer *, 
+	    integer *, doublereal *, integer *);
+    logical scalea;
+    extern doublereal igraphdlamch_(char *);
+    doublereal cscale;
+    extern doublereal igraphdlange_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *);
+    extern /* Subroutine */ int igraphdgehrd_(integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *), igraphdlascl_(char *, integer *, integer *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdlacpy_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), 
+	    igraphdlartg_(doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *), igraphxerbla_(char *, integer *, ftnlen);
+    logical select[1];
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    doublereal bignum;
+    extern /* Subroutine */ int igraphdorghr_(integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *), igraphdhseqr_(char *, char *, integer *, integer *, integer 
+	    *, doublereal *, integer *, doublereal *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, integer *), igraphdtrevc_(char *, char *, logical *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, integer *, integer *, doublereal *, integer *);
+    integer minwrk, maxwrk;
+    logical wantvl;
+    doublereal smlnum;
+    integer hswork;
+    logical lquery, wantvr;
+
+
+/*  -- LAPACK driver routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --wr;
+    --wi;
+    vl_dim1 = *ldvl;
+    vl_offset = 1 + vl_dim1;
+    vl -= vl_offset;
+    vr_dim1 = *ldvr;
+    vr_offset = 1 + vr_dim1;
+    vr -= vr_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    lquery = *lwork == -1;
+    wantvl = igraphlsame_(jobvl, "V");
+    wantvr = igraphlsame_(jobvr, "V");
+    if (! wantvl && ! igraphlsame_(jobvl, "N")) {
+	*info = -1;
+    } else if (! wantvr && ! igraphlsame_(jobvr, "N")) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -3;
+    } else if (*lda < max(1,*n)) {
+	*info = -5;
+    } else if (*ldvl < 1 || wantvl && *ldvl < *n) {
+	*info = -9;
+    } else if (*ldvr < 1 || wantvr && *ldvr < *n) {
+	*info = -11;
+    }
+
+/*     Compute workspace   
+        (Note: Comments in the code beginning "Workspace:" describe the   
+         minimal amount of workspace needed at that point in the code,   
+         as well as the preferred amount for good performance.   
+         NB refers to the optimal block size for the immediately   
+         following subroutine, as returned by ILAENV.   
+         HSWORK refers to the workspace preferred by DHSEQR, as   
+         calculated below. HSWORK is computed assuming ILO=1 and IHI=N,   
+         the worst case.) */
+
+    if (*info == 0) {
+	if (*n == 0) {
+	    minwrk = 1;
+	    maxwrk = 1;
+	} else {
+	    maxwrk = (*n << 1) + *n * igraphilaenv_(&c__1, "DGEHRD", " ", n, &c__1, 
+		    n, &c__0, (ftnlen)6, (ftnlen)1);
+	    if (wantvl) {
+		minwrk = *n << 2;
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = (*n << 1) + (*n - 1) * igraphilaenv_(&c__1, 
+			"DORGHR", " ", n, &c__1, n, &c_n1, (ftnlen)6, (ftnlen)
+			1);
+		maxwrk = max(i__1,i__2);
+		igraphdhseqr_("S", "V", n, &c__1, n, &a[a_offset], lda, &wr[1], &wi[
+			1], &vl[vl_offset], ldvl, &work[1], &c_n1, info);
+		hswork = (integer) work[1];
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = *n + 1, i__1 = max(i__1,i__2), i__2 = *
+			n + hswork;
+		maxwrk = max(i__1,i__2);
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = *n << 2;
+		maxwrk = max(i__1,i__2);
+	    } else if (wantvr) {
+		minwrk = *n << 2;
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = (*n << 1) + (*n - 1) * igraphilaenv_(&c__1, 
+			"DORGHR", " ", n, &c__1, n, &c_n1, (ftnlen)6, (ftnlen)
+			1);
+		maxwrk = max(i__1,i__2);
+		igraphdhseqr_("S", "V", n, &c__1, n, &a[a_offset], lda, &wr[1], &wi[
+			1], &vr[vr_offset], ldvr, &work[1], &c_n1, info);
+		hswork = (integer) work[1];
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = *n + 1, i__1 = max(i__1,i__2), i__2 = *
+			n + hswork;
+		maxwrk = max(i__1,i__2);
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = *n << 2;
+		maxwrk = max(i__1,i__2);
+	    } else {
+		minwrk = *n * 3;
+		igraphdhseqr_("E", "N", n, &c__1, n, &a[a_offset], lda, &wr[1], &wi[
+			1], &vr[vr_offset], ldvr, &work[1], &c_n1, info);
+		hswork = (integer) work[1];
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = *n + 1, i__1 = max(i__1,i__2), i__2 = *
+			n + hswork;
+		maxwrk = max(i__1,i__2);
+	    }
+	    maxwrk = max(maxwrk,minwrk);
+	}
+	work[1] = (doublereal) maxwrk;
+
+	if (*lwork < minwrk && ! lquery) {
+	    *info = -13;
+	}
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGEEV ", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+/*     Get machine constants */
+
+    eps = igraphdlamch_("P");
+    smlnum = igraphdlamch_("S");
+    bignum = 1. / smlnum;
+    igraphdlabad_(&smlnum, &bignum);
+    smlnum = sqrt(smlnum) / eps;
+    bignum = 1. / smlnum;
+
+/*     Scale A if max element outside range [SMLNUM,BIGNUM] */
+
+    anrm = igraphdlange_("M", n, n, &a[a_offset], lda, dum);
+    scalea = FALSE_;
+    if (anrm > 0. && anrm < smlnum) {
+	scalea = TRUE_;
+	cscale = smlnum;
+    } else if (anrm > bignum) {
+	scalea = TRUE_;
+	cscale = bignum;
+    }
+    if (scalea) {
+	igraphdlascl_("G", &c__0, &c__0, &anrm, &cscale, n, n, &a[a_offset], lda, &
+		ierr);
+    }
+
+/*     Balance the matrix   
+       (Workspace: need N) */
+
+    ibal = 1;
+    igraphdgebal_("B", n, &a[a_offset], lda, &ilo, &ihi, &work[ibal], &ierr);
+
+/*     Reduce to upper Hessenberg form   
+       (Workspace: need 3*N, prefer 2*N+N*NB) */
+
+    itau = ibal + *n;
+    iwrk = itau + *n;
+    i__1 = *lwork - iwrk + 1;
+    igraphdgehrd_(n, &ilo, &ihi, &a[a_offset], lda, &work[itau], &work[iwrk], &i__1,
+	     &ierr);
+
+    if (wantvl) {
+
+/*        Want left eigenvectors   
+          Copy Householder vectors to VL */
+
+	*(unsigned char *)side = 'L';
+	igraphdlacpy_("L", n, n, &a[a_offset], lda, &vl[vl_offset], ldvl)
+		;
+
+/*        Generate orthogonal matrix in VL   
+          (Workspace: need 3*N-1, prefer 2*N+(N-1)*NB) */
+
+	i__1 = *lwork - iwrk + 1;
+	igraphdorghr_(n, &ilo, &ihi, &vl[vl_offset], ldvl, &work[itau], &work[iwrk],
+		 &i__1, &ierr);
+
+/*        Perform QR iteration, accumulating Schur vectors in VL   
+          (Workspace: need N+1, prefer N+HSWORK (see comments) ) */
+
+	iwrk = itau;
+	i__1 = *lwork - iwrk + 1;
+	igraphdhseqr_("S", "V", n, &ilo, &ihi, &a[a_offset], lda, &wr[1], &wi[1], &
+		vl[vl_offset], ldvl, &work[iwrk], &i__1, info);
+
+	if (wantvr) {
+
+/*           Want left and right eigenvectors   
+             Copy Schur vectors to VR */
+
+	    *(unsigned char *)side = 'B';
+	    igraphdlacpy_("F", n, n, &vl[vl_offset], ldvl, &vr[vr_offset], ldvr);
+	}
+
+    } else if (wantvr) {
+
+/*        Want right eigenvectors   
+          Copy Householder vectors to VR */
+
+	*(unsigned char *)side = 'R';
+	igraphdlacpy_("L", n, n, &a[a_offset], lda, &vr[vr_offset], ldvr)
+		;
+
+/*        Generate orthogonal matrix in VR   
+          (Workspace: need 3*N-1, prefer 2*N+(N-1)*NB) */
+
+	i__1 = *lwork - iwrk + 1;
+	igraphdorghr_(n, &ilo, &ihi, &vr[vr_offset], ldvr, &work[itau], &work[iwrk],
+		 &i__1, &ierr);
+
+/*        Perform QR iteration, accumulating Schur vectors in VR   
+          (Workspace: need N+1, prefer N+HSWORK (see comments) ) */
+
+	iwrk = itau;
+	i__1 = *lwork - iwrk + 1;
+	igraphdhseqr_("S", "V", n, &ilo, &ihi, &a[a_offset], lda, &wr[1], &wi[1], &
+		vr[vr_offset], ldvr, &work[iwrk], &i__1, info);
+
+    } else {
+
+/*        Compute eigenvalues only   
+          (Workspace: need N+1, prefer N+HSWORK (see comments) ) */
+
+	iwrk = itau;
+	i__1 = *lwork - iwrk + 1;
+	igraphdhseqr_("E", "N", n, &ilo, &ihi, &a[a_offset], lda, &wr[1], &wi[1], &
+		vr[vr_offset], ldvr, &work[iwrk], &i__1, info);
+    }
+
+/*     If INFO > 0 from DHSEQR, then quit */
+
+    if (*info > 0) {
+	goto L50;
+    }
+
+    if (wantvl || wantvr) {
+
+/*        Compute left and/or right eigenvectors   
+          (Workspace: need 4*N) */
+
+	igraphdtrevc_(side, "B", select, n, &a[a_offset], lda, &vl[vl_offset], ldvl,
+		 &vr[vr_offset], ldvr, n, &nout, &work[iwrk], &ierr);
+    }
+
+    if (wantvl) {
+
+/*        Undo balancing of left eigenvectors   
+          (Workspace: need N) */
+
+	igraphdgebak_("B", "L", n, &ilo, &ihi, &work[ibal], n, &vl[vl_offset], ldvl,
+		 &ierr);
+
+/*        Normalize left eigenvectors and make largest component real */
+
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    if (wi[i__] == 0.) {
+		scl = 1. / igraphdnrm2_(n, &vl[i__ * vl_dim1 + 1], &c__1);
+		igraphdscal_(n, &scl, &vl[i__ * vl_dim1 + 1], &c__1);
+	    } else if (wi[i__] > 0.) {
+		d__1 = igraphdnrm2_(n, &vl[i__ * vl_dim1 + 1], &c__1);
+		d__2 = igraphdnrm2_(n, &vl[(i__ + 1) * vl_dim1 + 1], &c__1);
+		scl = 1. / igraphdlapy2_(&d__1, &d__2);
+		igraphdscal_(n, &scl, &vl[i__ * vl_dim1 + 1], &c__1);
+		igraphdscal_(n, &scl, &vl[(i__ + 1) * vl_dim1 + 1], &c__1);
+		i__2 = *n;
+		for (k = 1; k <= i__2; ++k) {
+/* Computing 2nd power */
+		    d__1 = vl[k + i__ * vl_dim1];
+/* Computing 2nd power */
+		    d__2 = vl[k + (i__ + 1) * vl_dim1];
+		    work[iwrk + k - 1] = d__1 * d__1 + d__2 * d__2;
+/* L10: */
+		}
+		k = igraphidamax_(n, &work[iwrk], &c__1);
+		igraphdlartg_(&vl[k + i__ * vl_dim1], &vl[k + (i__ + 1) * vl_dim1], 
+			&cs, &sn, &r__);
+		igraphdrot_(n, &vl[i__ * vl_dim1 + 1], &c__1, &vl[(i__ + 1) * 
+			vl_dim1 + 1], &c__1, &cs, &sn);
+		vl[k + (i__ + 1) * vl_dim1] = 0.;
+	    }
+/* L20: */
+	}
+    }
+
+    if (wantvr) {
+
+/*        Undo balancing of right eigenvectors   
+          (Workspace: need N) */
+
+	igraphdgebak_("B", "R", n, &ilo, &ihi, &work[ibal], n, &vr[vr_offset], ldvr,
+		 &ierr);
+
+/*        Normalize right eigenvectors and make largest component real */
+
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    if (wi[i__] == 0.) {
+		scl = 1. / igraphdnrm2_(n, &vr[i__ * vr_dim1 + 1], &c__1);
+		igraphdscal_(n, &scl, &vr[i__ * vr_dim1 + 1], &c__1);
+	    } else if (wi[i__] > 0.) {
+		d__1 = igraphdnrm2_(n, &vr[i__ * vr_dim1 + 1], &c__1);
+		d__2 = igraphdnrm2_(n, &vr[(i__ + 1) * vr_dim1 + 1], &c__1);
+		scl = 1. / igraphdlapy2_(&d__1, &d__2);
+		igraphdscal_(n, &scl, &vr[i__ * vr_dim1 + 1], &c__1);
+		igraphdscal_(n, &scl, &vr[(i__ + 1) * vr_dim1 + 1], &c__1);
+		i__2 = *n;
+		for (k = 1; k <= i__2; ++k) {
+/* Computing 2nd power */
+		    d__1 = vr[k + i__ * vr_dim1];
+/* Computing 2nd power */
+		    d__2 = vr[k + (i__ + 1) * vr_dim1];
+		    work[iwrk + k - 1] = d__1 * d__1 + d__2 * d__2;
+/* L30: */
+		}
+		k = igraphidamax_(n, &work[iwrk], &c__1);
+		igraphdlartg_(&vr[k + i__ * vr_dim1], &vr[k + (i__ + 1) * vr_dim1], 
+			&cs, &sn, &r__);
+		igraphdrot_(n, &vr[i__ * vr_dim1 + 1], &c__1, &vr[(i__ + 1) * 
+			vr_dim1 + 1], &c__1, &cs, &sn);
+		vr[k + (i__ + 1) * vr_dim1] = 0.;
+	    }
+/* L40: */
+	}
+    }
+
+/*     Undo scaling if necessary */
+
+L50:
+    if (scalea) {
+	i__1 = *n - *info;
+/* Computing MAX */
+	i__3 = *n - *info;
+	i__2 = max(i__3,1);
+	igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &i__1, &c__1, &wr[*info + 
+		1], &i__2, &ierr);
+	i__1 = *n - *info;
+/* Computing MAX */
+	i__3 = *n - *info;
+	i__2 = max(i__3,1);
+	igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &i__1, &c__1, &wi[*info + 
+		1], &i__2, &ierr);
+	if (*info > 0) {
+	    i__1 = ilo - 1;
+	    igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &i__1, &c__1, &wr[1], 
+		    n, &ierr);
+	    i__1 = ilo - 1;
+	    igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &i__1, &c__1, &wi[1], 
+		    n, &ierr);
+	}
+    }
+
+    work[1] = (doublereal) maxwrk;
+    return 0;
+
+/*     End of DGEEV */
+
+} /* igraphdgeev_ */
+
diff --git a/igraph/src/dgeevx.c b/igraph/src/dgeevx.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgeevx.c
@@ -0,0 +1,811 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c__0 = 0;
+static integer c_n1 = -1;
+
+/* > \brief <b> DGEEVX computes the eigenvalues and, optionally, the left and/or right eigenvectors for GE mat
+rices</b>   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGEEVX + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgeevx.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgeevx.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgeevx.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGEEVX( BALANC, JOBVL, JOBVR, SENSE, N, A, LDA, WR, WI,   
+                            VL, LDVL, VR, LDVR, ILO, IHI, SCALE, ABNRM,   
+                            RCONDE, RCONDV, WORK, LWORK, IWORK, INFO )   
+
+         CHARACTER          BALANC, JOBVL, JOBVR, SENSE   
+         INTEGER            IHI, ILO, INFO, LDA, LDVL, LDVR, LWORK, N   
+         DOUBLE PRECISION   ABNRM   
+         INTEGER            IWORK( * )   
+         DOUBLE PRECISION   A( LDA, * ), RCONDE( * ), RCONDV( * ),   
+        $                   SCALE( * ), VL( LDVL, * ), VR( LDVR, * ),   
+        $                   WI( * ), WORK( * ), WR( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGEEVX computes for an N-by-N real nonsymmetric matrix A, the   
+   > eigenvalues and, optionally, the left and/or right eigenvectors.   
+   >   
+   > Optionally also, it computes a balancing transformation to improve   
+   > the conditioning of the eigenvalues and eigenvectors (ILO, IHI,   
+   > SCALE, and ABNRM), reciprocal condition numbers for the eigenvalues   
+   > (RCONDE), and reciprocal condition numbers for the right   
+   > eigenvectors (RCONDV).   
+   >   
+   > The right eigenvector v(j) of A satisfies   
+   >                  A * v(j) = lambda(j) * v(j)   
+   > where lambda(j) is its eigenvalue.   
+   > The left eigenvector u(j) of A satisfies   
+   >               u(j)**H * A = lambda(j) * u(j)**H   
+   > where u(j)**H denotes the conjugate-transpose of u(j).   
+   >   
+   > The computed eigenvectors are normalized to have Euclidean norm   
+   > equal to 1 and largest component real.   
+   >   
+   > Balancing a matrix means permuting the rows and columns to make it   
+   > more nearly upper triangular, and applying a diagonal similarity   
+   > transformation D * A * D**(-1), where D is a diagonal matrix, to   
+   > make its rows and columns closer in norm and the condition numbers   
+   > of its eigenvalues and eigenvectors smaller.  The computed   
+   > reciprocal condition numbers correspond to the balanced matrix.   
+   > Permuting rows and columns will not change the condition numbers   
+   > (in exact arithmetic) but diagonal scaling will.  For further   
+   > explanation of balancing, see section 4.10.2 of the LAPACK   
+   > Users' Guide.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] BALANC   
+   > \verbatim   
+   >          BALANC is CHARACTER*1   
+   >          Indicates how the input matrix should be diagonally scaled   
+   >          and/or permuted to improve the conditioning of its   
+   >          eigenvalues.   
+   >          = 'N': Do not diagonally scale or permute;   
+   >          = 'P': Perform permutations to make the matrix more nearly   
+   >                 upper triangular. Do not diagonally scale;   
+   >          = 'S': Diagonally scale the matrix, i.e. replace A by   
+   >                 D*A*D**(-1), where D is a diagonal matrix chosen   
+   >                 to make the rows and columns of A more equal in   
+   >                 norm. Do not permute;   
+   >          = 'B': Both diagonally scale and permute A.   
+   >   
+   >          Computed reciprocal condition numbers will be for the matrix   
+   >          after balancing and/or permuting. Permuting does not change   
+   >          condition numbers (in exact arithmetic), but balancing does.   
+   > \endverbatim   
+   >   
+   > \param[in] JOBVL   
+   > \verbatim   
+   >          JOBVL is CHARACTER*1   
+   >          = 'N': left eigenvectors of A are not computed;   
+   >          = 'V': left eigenvectors of A are computed.   
+   >          If SENSE = 'E' or 'B', JOBVL must = 'V'.   
+   > \endverbatim   
+   >   
+   > \param[in] JOBVR   
+   > \verbatim   
+   >          JOBVR is CHARACTER*1   
+   >          = 'N': right eigenvectors of A are not computed;   
+   >          = 'V': right eigenvectors of A are computed.   
+   >          If SENSE = 'E' or 'B', JOBVR must = 'V'.   
+   > \endverbatim   
+   >   
+   > \param[in] SENSE   
+   > \verbatim   
+   >          SENSE is CHARACTER*1   
+   >          Determines which reciprocal condition numbers are computed.   
+   >          = 'N': None are computed;   
+   >          = 'E': Computed for eigenvalues only;   
+   >          = 'V': Computed for right eigenvectors only;   
+   >          = 'B': Computed for eigenvalues and right eigenvectors.   
+   >   
+   >          If SENSE = 'E' or 'B', both left and right eigenvectors   
+   >          must also be computed (JOBVL = 'V' and JOBVR = 'V').   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the N-by-N matrix A.   
+   >          On exit, A has been overwritten.  If JOBVL = 'V' or   
+   >          JOBVR = 'V', A contains the real Schur form of the balanced   
+   >          version of the input matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] WR   
+   > \verbatim   
+   >          WR is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] WI   
+   > \verbatim   
+   >          WI is DOUBLE PRECISION array, dimension (N)   
+   >          WR and WI contain the real and imaginary parts,   
+   >          respectively, of the computed eigenvalues.  Complex   
+   >          conjugate pairs of eigenvalues will appear consecutively   
+   >          with the eigenvalue having the positive imaginary part   
+   >          first.   
+   > \endverbatim   
+   >   
+   > \param[out] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION array, dimension (LDVL,N)   
+   >          If JOBVL = 'V', the left eigenvectors u(j) are stored one   
+   >          after another in the columns of VL, in the same order   
+   >          as their eigenvalues.   
+   >          If JOBVL = 'N', VL is not referenced.   
+   >          If the j-th eigenvalue is real, then u(j) = VL(:,j),   
+   >          the j-th column of VL.   
+   >          If the j-th and (j+1)-st eigenvalues form a complex   
+   >          conjugate pair, then u(j) = VL(:,j) + i*VL(:,j+1) and   
+   >          u(j+1) = VL(:,j) - i*VL(:,j+1).   
+   > \endverbatim   
+   >   
+   > \param[in] LDVL   
+   > \verbatim   
+   >          LDVL is INTEGER   
+   >          The leading dimension of the array VL.  LDVL >= 1; if   
+   >          JOBVL = 'V', LDVL >= N.   
+   > \endverbatim   
+   >   
+   > \param[out] VR   
+   > \verbatim   
+   >          VR is DOUBLE PRECISION array, dimension (LDVR,N)   
+   >          If JOBVR = 'V', the right eigenvectors v(j) are stored one   
+   >          after another in the columns of VR, in the same order   
+   >          as their eigenvalues.   
+   >          If JOBVR = 'N', VR is not referenced.   
+   >          If the j-th eigenvalue is real, then v(j) = VR(:,j),   
+   >          the j-th column of VR.   
+   >          If the j-th and (j+1)-st eigenvalues form a complex   
+   >          conjugate pair, then v(j) = VR(:,j) + i*VR(:,j+1) and   
+   >          v(j+1) = VR(:,j) - i*VR(:,j+1).   
+   > \endverbatim   
+   >   
+   > \param[in] LDVR   
+   > \verbatim   
+   >          LDVR is INTEGER   
+   >          The leading dimension of the array VR.  LDVR >= 1, and if   
+   >          JOBVR = 'V', LDVR >= N.   
+   > \endverbatim   
+   >   
+   > \param[out] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[out] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >          ILO and IHI are integer values determined when A was   
+   >          balanced.  The balanced A(i,j) = 0 if I > J and   
+   >          J = 1,...,ILO-1 or I = IHI+1,...,N.   
+   > \endverbatim   
+   >   
+   > \param[out] SCALE   
+   > \verbatim   
+   >          SCALE is DOUBLE PRECISION array, dimension (N)   
+   >          Details of the permutations and scaling factors applied   
+   >          when balancing A.  If P(j) is the index of the row and column   
+   >          interchanged with row and column j, and D(j) is the scaling   
+   >          factor applied to row and column j, then   
+   >          SCALE(J) = P(J),    for J = 1,...,ILO-1   
+   >                   = D(J),    for J = ILO,...,IHI   
+   >                   = P(J)     for J = IHI+1,...,N.   
+   >          The order in which the interchanges are made is N to IHI+1,   
+   >          then 1 to ILO-1.   
+   > \endverbatim   
+   >   
+   > \param[out] ABNRM   
+   > \verbatim   
+   >          ABNRM is DOUBLE PRECISION   
+   >          The one-norm of the balanced matrix (the maximum   
+   >          of the sum of absolute values of elements of any column).   
+   > \endverbatim   
+   >   
+   > \param[out] RCONDE   
+   > \verbatim   
+   >          RCONDE is DOUBLE PRECISION array, dimension (N)   
+   >          RCONDE(j) is the reciprocal condition number of the j-th   
+   >          eigenvalue.   
+   > \endverbatim   
+   >   
+   > \param[out] RCONDV   
+   > \verbatim   
+   >          RCONDV is DOUBLE PRECISION array, dimension (N)   
+   >          RCONDV(j) is the reciprocal condition number of the j-th   
+   >          right eigenvector.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.   If SENSE = 'N' or 'E',   
+   >          LWORK >= max(1,2*N), and if JOBVL = 'V' or JOBVR = 'V',   
+   >          LWORK >= 3*N.  If SENSE = 'V' or 'B', LWORK >= N*(N+6).   
+   >          For good performance, LWORK must generally be larger.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (2*N-2)   
+   >          If SENSE = 'N' or 'E', not referenced.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value.   
+   >          > 0:  if INFO = i, the QR algorithm failed to compute all the   
+   >                eigenvalues, and no eigenvectors or condition numbers   
+   >                have been computed; elements 1:ILO-1 and i+1:N of WR   
+   >                and WI contain eigenvalues which have converged.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleGEeigen   
+
+    =====================================================================   
+   Subroutine */ int igraphdgeevx_(char *balanc, char *jobvl, char *jobvr, char *
+	sense, integer *n, doublereal *a, integer *lda, doublereal *wr, 
+	doublereal *wi, doublereal *vl, integer *ldvl, doublereal *vr, 
+	integer *ldvr, integer *ilo, integer *ihi, doublereal *scale, 
+	doublereal *abnrm, doublereal *rconde, doublereal *rcondv, doublereal 
+	*work, integer *lwork, integer *iwork, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, vl_dim1, vl_offset, vr_dim1, vr_offset, i__1, 
+	    i__2, i__3;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, k;
+    doublereal r__, cs, sn;
+    char job[1];
+    doublereal scl, dum[1], eps;
+    char side[1];
+    doublereal anrm;
+    integer ierr, itau;
+    extern /* Subroutine */ int igraphdrot_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *);
+    integer iwrk, nout;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    integer icond;
+    extern logical igraphlsame_(char *, char *);
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlabad_(doublereal *, doublereal *), igraphdgebak_(
+	    char *, char *, integer *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *), 
+	    igraphdgebal_(char *, integer *, doublereal *, integer *, integer *, 
+	    integer *, doublereal *, integer *);
+    logical scalea;
+    extern doublereal igraphdlamch_(char *);
+    doublereal cscale;
+    extern doublereal igraphdlange_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *);
+    extern /* Subroutine */ int igraphdgehrd_(integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *), igraphdlascl_(char *, integer *, integer *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdlacpy_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), 
+	    igraphdlartg_(doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *), igraphxerbla_(char *, integer *, ftnlen);
+    logical select[1];
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    doublereal bignum;
+    extern /* Subroutine */ int igraphdorghr_(integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *), igraphdhseqr_(char *, char *, integer *, integer *, integer 
+	    *, doublereal *, integer *, doublereal *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, integer *), igraphdtrevc_(char *, char *, logical *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, integer *, integer *, doublereal *, integer *), igraphdtrsna_(char *, char *, logical *, integer *, doublereal 
+	    *, integer *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *, integer *);
+    integer minwrk, maxwrk;
+    logical wantvl, wntsnb;
+    integer hswork;
+    logical wntsne;
+    doublereal smlnum;
+    logical lquery, wantvr, wntsnn, wntsnv;
+
+
+/*  -- LAPACK driver routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --wr;
+    --wi;
+    vl_dim1 = *ldvl;
+    vl_offset = 1 + vl_dim1;
+    vl -= vl_offset;
+    vr_dim1 = *ldvr;
+    vr_offset = 1 + vr_dim1;
+    vr -= vr_offset;
+    --scale;
+    --rconde;
+    --rcondv;
+    --work;
+    --iwork;
+
+    /* Function Body */
+    *info = 0;
+    lquery = *lwork == -1;
+    wantvl = igraphlsame_(jobvl, "V");
+    wantvr = igraphlsame_(jobvr, "V");
+    wntsnn = igraphlsame_(sense, "N");
+    wntsne = igraphlsame_(sense, "E");
+    wntsnv = igraphlsame_(sense, "V");
+    wntsnb = igraphlsame_(sense, "B");
+    if (! (igraphlsame_(balanc, "N") || igraphlsame_(balanc, "S") || igraphlsame_(balanc, "P") 
+	    || igraphlsame_(balanc, "B"))) {
+	*info = -1;
+    } else if (! wantvl && ! igraphlsame_(jobvl, "N")) {
+	*info = -2;
+    } else if (! wantvr && ! igraphlsame_(jobvr, "N")) {
+	*info = -3;
+    } else if (! (wntsnn || wntsne || wntsnb || wntsnv) || (wntsne || wntsnb) 
+	    && ! (wantvl && wantvr)) {
+	*info = -4;
+    } else if (*n < 0) {
+	*info = -5;
+    } else if (*lda < max(1,*n)) {
+	*info = -7;
+    } else if (*ldvl < 1 || wantvl && *ldvl < *n) {
+	*info = -11;
+    } else if (*ldvr < 1 || wantvr && *ldvr < *n) {
+	*info = -13;
+    }
+
+/*     Compute workspace   
+        (Note: Comments in the code beginning "Workspace:" describe the   
+         minimal amount of workspace needed at that point in the code,   
+         as well as the preferred amount for good performance.   
+         NB refers to the optimal block size for the immediately   
+         following subroutine, as returned by ILAENV.   
+         HSWORK refers to the workspace preferred by DHSEQR, as   
+         calculated below. HSWORK is computed assuming ILO=1 and IHI=N,   
+         the worst case.) */
+
+    if (*info == 0) {
+	if (*n == 0) {
+	    minwrk = 1;
+	    maxwrk = 1;
+	} else {
+	    maxwrk = *n + *n * igraphilaenv_(&c__1, "DGEHRD", " ", n, &c__1, n, &
+		    c__0, (ftnlen)6, (ftnlen)1);
+
+	    if (wantvl) {
+		igraphdhseqr_("S", "V", n, &c__1, n, &a[a_offset], lda, &wr[1], &wi[
+			1], &vl[vl_offset], ldvl, &work[1], &c_n1, info);
+	    } else if (wantvr) {
+		igraphdhseqr_("S", "V", n, &c__1, n, &a[a_offset], lda, &wr[1], &wi[
+			1], &vr[vr_offset], ldvr, &work[1], &c_n1, info);
+	    } else {
+		if (wntsnn) {
+		    igraphdhseqr_("E", "N", n, &c__1, n, &a[a_offset], lda, &wr[1], 
+			    &wi[1], &vr[vr_offset], ldvr, &work[1], &c_n1, 
+			    info);
+		} else {
+		    igraphdhseqr_("S", "N", n, &c__1, n, &a[a_offset], lda, &wr[1], 
+			    &wi[1], &vr[vr_offset], ldvr, &work[1], &c_n1, 
+			    info);
+		}
+	    }
+	    hswork = (integer) work[1];
+
+	    if (! wantvl && ! wantvr) {
+		minwrk = *n << 1;
+		if (! wntsnn) {
+/* Computing MAX */
+		    i__1 = minwrk, i__2 = *n * *n + *n * 6;
+		    minwrk = max(i__1,i__2);
+		}
+		maxwrk = max(maxwrk,hswork);
+		if (! wntsnn) {
+/* Computing MAX */
+		    i__1 = maxwrk, i__2 = *n * *n + *n * 6;
+		    maxwrk = max(i__1,i__2);
+		}
+	    } else {
+		minwrk = *n * 3;
+		if (! wntsnn && ! wntsne) {
+/* Computing MAX */
+		    i__1 = minwrk, i__2 = *n * *n + *n * 6;
+		    minwrk = max(i__1,i__2);
+		}
+		maxwrk = max(maxwrk,hswork);
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = *n + (*n - 1) * igraphilaenv_(&c__1, "DORGHR",
+			 " ", n, &c__1, n, &c_n1, (ftnlen)6, (ftnlen)1);
+		maxwrk = max(i__1,i__2);
+		if (! wntsnn && ! wntsne) {
+/* Computing MAX */
+		    i__1 = maxwrk, i__2 = *n * *n + *n * 6;
+		    maxwrk = max(i__1,i__2);
+		}
+/* Computing MAX */
+		i__1 = maxwrk, i__2 = *n * 3;
+		maxwrk = max(i__1,i__2);
+	    }
+	    maxwrk = max(maxwrk,minwrk);
+	}
+	work[1] = (doublereal) maxwrk;
+
+	if (*lwork < minwrk && ! lquery) {
+	    *info = -21;
+	}
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGEEVX", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+/*     Get machine constants */
+
+    eps = igraphdlamch_("P");
+    smlnum = igraphdlamch_("S");
+    bignum = 1. / smlnum;
+    igraphdlabad_(&smlnum, &bignum);
+    smlnum = sqrt(smlnum) / eps;
+    bignum = 1. / smlnum;
+
+/*     Scale A if max element outside range [SMLNUM,BIGNUM] */
+
+    icond = 0;
+    anrm = igraphdlange_("M", n, n, &a[a_offset], lda, dum);
+    scalea = FALSE_;
+    if (anrm > 0. && anrm < smlnum) {
+	scalea = TRUE_;
+	cscale = smlnum;
+    } else if (anrm > bignum) {
+	scalea = TRUE_;
+	cscale = bignum;
+    }
+    if (scalea) {
+	igraphdlascl_("G", &c__0, &c__0, &anrm, &cscale, n, n, &a[a_offset], lda, &
+		ierr);
+    }
+
+/*     Balance the matrix and compute ABNRM */
+
+    igraphdgebal_(balanc, n, &a[a_offset], lda, ilo, ihi, &scale[1], &ierr);
+    *abnrm = igraphdlange_("1", n, n, &a[a_offset], lda, dum);
+    if (scalea) {
+	dum[0] = *abnrm;
+	igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &c__1, &c__1, dum, &c__1, &
+		ierr);
+	*abnrm = dum[0];
+    }
+
+/*     Reduce to upper Hessenberg form   
+       (Workspace: need 2*N, prefer N+N*NB) */
+
+    itau = 1;
+    iwrk = itau + *n;
+    i__1 = *lwork - iwrk + 1;
+    igraphdgehrd_(n, ilo, ihi, &a[a_offset], lda, &work[itau], &work[iwrk], &i__1, &
+	    ierr);
+
+    if (wantvl) {
+
+/*        Want left eigenvectors   
+          Copy Householder vectors to VL */
+
+	*(unsigned char *)side = 'L';
+	igraphdlacpy_("L", n, n, &a[a_offset], lda, &vl[vl_offset], ldvl)
+		;
+
+/*        Generate orthogonal matrix in VL   
+          (Workspace: need 2*N-1, prefer N+(N-1)*NB) */
+
+	i__1 = *lwork - iwrk + 1;
+	igraphdorghr_(n, ilo, ihi, &vl[vl_offset], ldvl, &work[itau], &work[iwrk], &
+		i__1, &ierr);
+
+/*        Perform QR iteration, accumulating Schur vectors in VL   
+          (Workspace: need 1, prefer HSWORK (see comments) ) */
+
+	iwrk = itau;
+	i__1 = *lwork - iwrk + 1;
+	igraphdhseqr_("S", "V", n, ilo, ihi, &a[a_offset], lda, &wr[1], &wi[1], &vl[
+		vl_offset], ldvl, &work[iwrk], &i__1, info);
+
+	if (wantvr) {
+
+/*           Want left and right eigenvectors   
+             Copy Schur vectors to VR */
+
+	    *(unsigned char *)side = 'B';
+	    igraphdlacpy_("F", n, n, &vl[vl_offset], ldvl, &vr[vr_offset], ldvr);
+	}
+
+    } else if (wantvr) {
+
+/*        Want right eigenvectors   
+          Copy Householder vectors to VR */
+
+	*(unsigned char *)side = 'R';
+	igraphdlacpy_("L", n, n, &a[a_offset], lda, &vr[vr_offset], ldvr)
+		;
+
+/*        Generate orthogonal matrix in VR   
+          (Workspace: need 2*N-1, prefer N+(N-1)*NB) */
+
+	i__1 = *lwork - iwrk + 1;
+	igraphdorghr_(n, ilo, ihi, &vr[vr_offset], ldvr, &work[itau], &work[iwrk], &
+		i__1, &ierr);
+
+/*        Perform QR iteration, accumulating Schur vectors in VR   
+          (Workspace: need 1, prefer HSWORK (see comments) ) */
+
+	iwrk = itau;
+	i__1 = *lwork - iwrk + 1;
+	igraphdhseqr_("S", "V", n, ilo, ihi, &a[a_offset], lda, &wr[1], &wi[1], &vr[
+		vr_offset], ldvr, &work[iwrk], &i__1, info);
+
+    } else {
+
+/*        Compute eigenvalues only   
+          If condition numbers desired, compute Schur form */
+
+	if (wntsnn) {
+	    *(unsigned char *)job = 'E';
+	} else {
+	    *(unsigned char *)job = 'S';
+	}
+
+/*        (Workspace: need 1, prefer HSWORK (see comments) ) */
+
+	iwrk = itau;
+	i__1 = *lwork - iwrk + 1;
+	igraphdhseqr_(job, "N", n, ilo, ihi, &a[a_offset], lda, &wr[1], &wi[1], &vr[
+		vr_offset], ldvr, &work[iwrk], &i__1, info);
+    }
+
+/*     If INFO > 0 from DHSEQR, then quit */
+
+    if (*info > 0) {
+	goto L50;
+    }
+
+    if (wantvl || wantvr) {
+
+/*        Compute left and/or right eigenvectors   
+          (Workspace: need 3*N) */
+
+	igraphdtrevc_(side, "B", select, n, &a[a_offset], lda, &vl[vl_offset], ldvl,
+		 &vr[vr_offset], ldvr, n, &nout, &work[iwrk], &ierr);
+    }
+
+/*     Compute condition numbers if desired   
+       (Workspace: need N*N+6*N unless SENSE = 'E') */
+
+    if (! wntsnn) {
+	igraphdtrsna_(sense, "A", select, n, &a[a_offset], lda, &vl[vl_offset], 
+		ldvl, &vr[vr_offset], ldvr, &rconde[1], &rcondv[1], n, &nout, 
+		&work[iwrk], n, &iwork[1], &icond);
+    }
+
+    if (wantvl) {
+
+/*        Undo balancing of left eigenvectors */
+
+	igraphdgebak_(balanc, "L", n, ilo, ihi, &scale[1], n, &vl[vl_offset], ldvl, 
+		&ierr);
+
+/*        Normalize left eigenvectors and make largest component real */
+
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    if (wi[i__] == 0.) {
+		scl = 1. / igraphdnrm2_(n, &vl[i__ * vl_dim1 + 1], &c__1);
+		igraphdscal_(n, &scl, &vl[i__ * vl_dim1 + 1], &c__1);
+	    } else if (wi[i__] > 0.) {
+		d__1 = igraphdnrm2_(n, &vl[i__ * vl_dim1 + 1], &c__1);
+		d__2 = igraphdnrm2_(n, &vl[(i__ + 1) * vl_dim1 + 1], &c__1);
+		scl = 1. / igraphdlapy2_(&d__1, &d__2);
+		igraphdscal_(n, &scl, &vl[i__ * vl_dim1 + 1], &c__1);
+		igraphdscal_(n, &scl, &vl[(i__ + 1) * vl_dim1 + 1], &c__1);
+		i__2 = *n;
+		for (k = 1; k <= i__2; ++k) {
+/* Computing 2nd power */
+		    d__1 = vl[k + i__ * vl_dim1];
+/* Computing 2nd power */
+		    d__2 = vl[k + (i__ + 1) * vl_dim1];
+		    work[k] = d__1 * d__1 + d__2 * d__2;
+/* L10: */
+		}
+		k = igraphidamax_(n, &work[1], &c__1);
+		igraphdlartg_(&vl[k + i__ * vl_dim1], &vl[k + (i__ + 1) * vl_dim1], 
+			&cs, &sn, &r__);
+		igraphdrot_(n, &vl[i__ * vl_dim1 + 1], &c__1, &vl[(i__ + 1) * 
+			vl_dim1 + 1], &c__1, &cs, &sn);
+		vl[k + (i__ + 1) * vl_dim1] = 0.;
+	    }
+/* L20: */
+	}
+    }
+
+    if (wantvr) {
+
+/*        Undo balancing of right eigenvectors */
+
+	igraphdgebak_(balanc, "R", n, ilo, ihi, &scale[1], n, &vr[vr_offset], ldvr, 
+		&ierr);
+
+/*        Normalize right eigenvectors and make largest component real */
+
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    if (wi[i__] == 0.) {
+		scl = 1. / igraphdnrm2_(n, &vr[i__ * vr_dim1 + 1], &c__1);
+		igraphdscal_(n, &scl, &vr[i__ * vr_dim1 + 1], &c__1);
+	    } else if (wi[i__] > 0.) {
+		d__1 = igraphdnrm2_(n, &vr[i__ * vr_dim1 + 1], &c__1);
+		d__2 = igraphdnrm2_(n, &vr[(i__ + 1) * vr_dim1 + 1], &c__1);
+		scl = 1. / igraphdlapy2_(&d__1, &d__2);
+		igraphdscal_(n, &scl, &vr[i__ * vr_dim1 + 1], &c__1);
+		igraphdscal_(n, &scl, &vr[(i__ + 1) * vr_dim1 + 1], &c__1);
+		i__2 = *n;
+		for (k = 1; k <= i__2; ++k) {
+/* Computing 2nd power */
+		    d__1 = vr[k + i__ * vr_dim1];
+/* Computing 2nd power */
+		    d__2 = vr[k + (i__ + 1) * vr_dim1];
+		    work[k] = d__1 * d__1 + d__2 * d__2;
+/* L30: */
+		}
+		k = igraphidamax_(n, &work[1], &c__1);
+		igraphdlartg_(&vr[k + i__ * vr_dim1], &vr[k + (i__ + 1) * vr_dim1], 
+			&cs, &sn, &r__);
+		igraphdrot_(n, &vr[i__ * vr_dim1 + 1], &c__1, &vr[(i__ + 1) * 
+			vr_dim1 + 1], &c__1, &cs, &sn);
+		vr[k + (i__ + 1) * vr_dim1] = 0.;
+	    }
+/* L40: */
+	}
+    }
+
+/*     Undo scaling if necessary */
+
+L50:
+    if (scalea) {
+	i__1 = *n - *info;
+/* Computing MAX */
+	i__3 = *n - *info;
+	i__2 = max(i__3,1);
+	igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &i__1, &c__1, &wr[*info + 
+		1], &i__2, &ierr);
+	i__1 = *n - *info;
+/* Computing MAX */
+	i__3 = *n - *info;
+	i__2 = max(i__3,1);
+	igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &i__1, &c__1, &wi[*info + 
+		1], &i__2, &ierr);
+	if (*info == 0) {
+	    if ((wntsnv || wntsnb) && icond == 0) {
+		igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, n, &c__1, &rcondv[
+			1], n, &ierr);
+	    }
+	} else {
+	    i__1 = *ilo - 1;
+	    igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &i__1, &c__1, &wr[1], 
+		    n, &ierr);
+	    i__1 = *ilo - 1;
+	    igraphdlascl_("G", &c__0, &c__0, &cscale, &anrm, &i__1, &c__1, &wi[1], 
+		    n, &ierr);
+	}
+    }
+
+    work[1] = (doublereal) maxwrk;
+    return 0;
+
+/*     End of DGEEVX */
+
+} /* igraphdgeevx_ */
+
diff --git a/igraph/src/dgehd2.c b/igraph/src/dgehd2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgehd2.c
@@ -0,0 +1,255 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DGEHD2 reduces a general square matrix to upper Hessenberg form using an unblocked algorithm. 
+  
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGEHD2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgehd2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgehd2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgehd2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGEHD2( N, ILO, IHI, A, LDA, TAU, WORK, INFO )   
+
+         INTEGER            IHI, ILO, INFO, LDA, N   
+         DOUBLE PRECISION   A( LDA, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGEHD2 reduces a real general matrix A to upper Hessenberg form H by   
+   > an orthogonal similarity transformation:  Q**T * A * Q = H .   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >   
+   >          It is assumed that A is already upper triangular in rows   
+   >          and columns 1:ILO-1 and IHI+1:N. ILO and IHI are normally   
+   >          set by a previous call to DGEBAL; otherwise they should be   
+   >          set to 1 and N respectively. See Further Details.   
+   >          1 <= ILO <= IHI <= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the n by n general matrix to be reduced.   
+   >          On exit, the upper triangle and the first subdiagonal of A   
+   >          are overwritten with the upper Hessenberg matrix H, and the   
+   >          elements below the first subdiagonal, with the array TAU,   
+   >          represent the orthogonal matrix Q as a product of elementary   
+   >          reflectors. See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (N-1)   
+   >          The scalar factors of the elementary reflectors (see Further   
+   >          Details).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit.   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleGEcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The matrix Q is represented as a product of (ihi-ilo) elementary   
+   >  reflectors   
+   >   
+   >     Q = H(ilo) H(ilo+1) . . . H(ihi-1).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(1:i) = 0, v(i+1) = 1 and v(ihi+1:n) = 0; v(i+2:ihi) is stored on   
+   >  exit in A(i+2:ihi,i), and tau in TAU(i).   
+   >   
+   >  The contents of A are illustrated by the following example, with   
+   >  n = 7, ilo = 2 and ihi = 6:   
+   >   
+   >  on entry,                        on exit,   
+   >   
+   >  ( a   a   a   a   a   a   a )    (  a   a   h   h   h   h   a )   
+   >  (     a   a   a   a   a   a )    (      a   h   h   h   h   a )   
+   >  (     a   a   a   a   a   a )    (      h   h   h   h   h   h )   
+   >  (     a   a   a   a   a   a )    (      v2  h   h   h   h   h )   
+   >  (     a   a   a   a   a   a )    (      v2  v3  h   h   h   h )   
+   >  (     a   a   a   a   a   a )    (      v2  v3  v4  h   h   h )   
+   >  (                         a )    (                          a )   
+   >   
+   >  where a denotes an element of the original matrix A, h denotes a   
+   >  modified element of the upper Hessenberg matrix H, and vi denotes an   
+   >  element of the vector defining H(i).   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdgehd2_(integer *n, integer *ilo, integer *ihi, 
+	doublereal *a, integer *lda, doublereal *tau, doublereal *work, 
+	integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__;
+    doublereal aii;
+    extern /* Subroutine */ int igraphdlarf_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *), igraphdlarfg_(integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *), igraphxerbla_(char *, integer *,
+	     ftnlen);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input parameters   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    if (*n < 0) {
+	*info = -1;
+    } else if (*ilo < 1 || *ilo > max(1,*n)) {
+	*info = -2;
+    } else if (*ihi < min(*ilo,*n) || *ihi > *n) {
+	*info = -3;
+    } else if (*lda < max(1,*n)) {
+	*info = -5;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGEHD2", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+    i__1 = *ihi - 1;
+    for (i__ = *ilo; i__ <= i__1; ++i__) {
+
+/*        Compute elementary reflector H(i) to annihilate A(i+2:ihi,i) */
+
+	i__2 = *ihi - i__;
+/* Computing MIN */
+	i__3 = i__ + 2;
+	igraphdlarfg_(&i__2, &a[i__ + 1 + i__ * a_dim1], &a[min(i__3,*n) + i__ * 
+		a_dim1], &c__1, &tau[i__]);
+	aii = a[i__ + 1 + i__ * a_dim1];
+	a[i__ + 1 + i__ * a_dim1] = 1.;
+
+/*        Apply H(i) to A(1:ihi,i+1:ihi) from the right */
+
+	i__2 = *ihi - i__;
+	igraphdlarf_("Right", ihi, &i__2, &a[i__ + 1 + i__ * a_dim1], &c__1, &tau[
+		i__], &a[(i__ + 1) * a_dim1 + 1], lda, &work[1]);
+
+/*        Apply H(i) to A(i+1:ihi,i+1:n) from the left */
+
+	i__2 = *ihi - i__;
+	i__3 = *n - i__;
+	igraphdlarf_("Left", &i__2, &i__3, &a[i__ + 1 + i__ * a_dim1], &c__1, &tau[
+		i__], &a[i__ + 1 + (i__ + 1) * a_dim1], lda, &work[1]);
+
+	a[i__ + 1 + i__ * a_dim1] = aii;
+/* L10: */
+    }
+
+    return 0;
+
+/*     End of DGEHD2 */
+
+} /* igraphdgehd2_ */
+
diff --git a/igraph/src/dgehrd.c b/igraph/src/dgehrd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgehrd.c
@@ -0,0 +1,406 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__3 = 3;
+static integer c__2 = 2;
+static integer c__65 = 65;
+static doublereal c_b25 = -1.;
+static doublereal c_b26 = 1.;
+
+/* > \brief \b DGEHRD   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGEHRD + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgehrd.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgehrd.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgehrd.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGEHRD( N, ILO, IHI, A, LDA, TAU, WORK, LWORK, INFO )   
+
+         INTEGER            IHI, ILO, INFO, LDA, LWORK, N   
+         DOUBLE PRECISION  A( LDA, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGEHRD reduces a real general matrix A to upper Hessenberg form H by   
+   > an orthogonal similarity transformation:  Q**T * A * Q = H .   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >   
+   >          It is assumed that A is already upper triangular in rows   
+   >          and columns 1:ILO-1 and IHI+1:N. ILO and IHI are normally   
+   >          set by a previous call to DGEBAL; otherwise they should be   
+   >          set to 1 and N respectively. See Further Details.   
+   >          1 <= ILO <= IHI <= N, if N > 0; ILO=1 and IHI=0, if N=0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the N-by-N general matrix to be reduced.   
+   >          On exit, the upper triangle and the first subdiagonal of A   
+   >          are overwritten with the upper Hessenberg matrix H, and the   
+   >          elements below the first subdiagonal, with the array TAU,   
+   >          represent the orthogonal matrix Q as a product of elementary   
+   >          reflectors. See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (N-1)   
+   >          The scalar factors of the elementary reflectors (see Further   
+   >          Details). Elements 1:ILO-1 and IHI:N-1 of TAU are set to   
+   >          zero.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (LWORK)   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The length of the array WORK.  LWORK >= max(1,N).   
+   >          For optimum performance LWORK >= N*NB, where NB is the   
+   >          optimal blocksize.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleGEcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The matrix Q is represented as a product of (ihi-ilo) elementary   
+   >  reflectors   
+   >   
+   >     Q = H(ilo) H(ilo+1) . . . H(ihi-1).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(1:i) = 0, v(i+1) = 1 and v(ihi+1:n) = 0; v(i+2:ihi) is stored on   
+   >  exit in A(i+2:ihi,i), and tau in TAU(i).   
+   >   
+   >  The contents of A are illustrated by the following example, with   
+   >  n = 7, ilo = 2 and ihi = 6:   
+   >   
+   >  on entry,                        on exit,   
+   >   
+   >  ( a   a   a   a   a   a   a )    (  a   a   h   h   h   h   a )   
+   >  (     a   a   a   a   a   a )    (      a   h   h   h   h   a )   
+   >  (     a   a   a   a   a   a )    (      h   h   h   h   h   h )   
+   >  (     a   a   a   a   a   a )    (      v2  h   h   h   h   h )   
+   >  (     a   a   a   a   a   a )    (      v2  v3  h   h   h   h )   
+   >  (     a   a   a   a   a   a )    (      v2  v3  v4  h   h   h )   
+   >  (                         a )    (                          a )   
+   >   
+   >  where a denotes an element of the original matrix A, h denotes a   
+   >  modified element of the upper Hessenberg matrix H, and vi denotes an   
+   >  element of the vector defining H(i).   
+   >   
+   >  This file is a slight modification of LAPACK-3.0's DGEHRD   
+   >  subroutine incorporating improvements proposed by Quintana-Orti and   
+   >  Van de Geijn (2006). (See DLAHR2.)   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdgehrd_(integer *n, integer *ilo, integer *ihi, 
+	doublereal *a, integer *lda, doublereal *tau, doublereal *work, 
+	integer *lwork, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3, i__4;
+
+    /* Local variables */
+    integer i__, j;
+    doublereal t[4160]	/* was [65][64] */;
+    integer ib;
+    doublereal ei;
+    integer nb, nh, nx, iws;
+    extern /* Subroutine */ int igraphdgemm_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *);
+    integer nbmin, iinfo;
+    extern /* Subroutine */ int igraphdtrmm_(char *, char *, char *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdaxpy_(
+	    integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *), igraphdgehd2_(integer *, integer *, integer *, doublereal *,
+	     integer *, doublereal *, doublereal *, integer *), igraphdlahr2_(
+	    integer *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *), 
+	    igraphdlarfb_(char *, char *, char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    integer ldwork, lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+/* Computing MIN */
+    i__1 = 64, i__2 = igraphilaenv_(&c__1, "DGEHRD", " ", n, ilo, ihi, &c_n1, (
+	    ftnlen)6, (ftnlen)1);
+    nb = min(i__1,i__2);
+    lwkopt = *n * nb;
+    work[1] = (doublereal) lwkopt;
+    lquery = *lwork == -1;
+    if (*n < 0) {
+	*info = -1;
+    } else if (*ilo < 1 || *ilo > max(1,*n)) {
+	*info = -2;
+    } else if (*ihi < min(*ilo,*n) || *ihi > *n) {
+	*info = -3;
+    } else if (*lda < max(1,*n)) {
+	*info = -5;
+    } else if (*lwork < max(1,*n) && ! lquery) {
+	*info = -8;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGEHRD", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Set elements 1:ILO-1 and IHI:N-1 of TAU to zero */
+
+    i__1 = *ilo - 1;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	tau[i__] = 0.;
+/* L10: */
+    }
+    i__1 = *n - 1;
+    for (i__ = max(1,*ihi); i__ <= i__1; ++i__) {
+	tau[i__] = 0.;
+/* L20: */
+    }
+
+/*     Quick return if possible */
+
+    nh = *ihi - *ilo + 1;
+    if (nh <= 1) {
+	work[1] = 1.;
+	return 0;
+    }
+
+/*     Determine the block size   
+
+   Computing MIN */
+    i__1 = 64, i__2 = igraphilaenv_(&c__1, "DGEHRD", " ", n, ilo, ihi, &c_n1, (
+	    ftnlen)6, (ftnlen)1);
+    nb = min(i__1,i__2);
+    nbmin = 2;
+    iws = 1;
+    if (nb > 1 && nb < nh) {
+
+/*        Determine when to cross over from blocked to unblocked code   
+          (last block is always handled by unblocked code)   
+
+   Computing MAX */
+	i__1 = nb, i__2 = igraphilaenv_(&c__3, "DGEHRD", " ", n, ilo, ihi, &c_n1, (
+		ftnlen)6, (ftnlen)1);
+	nx = max(i__1,i__2);
+	if (nx < nh) {
+
+/*           Determine if workspace is large enough for blocked code */
+
+	    iws = *n * nb;
+	    if (*lwork < iws) {
+
+/*              Not enough workspace to use optimal NB:  determine the   
+                minimum value of NB, and reduce NB or force use of   
+                unblocked code   
+
+   Computing MAX */
+		i__1 = 2, i__2 = igraphilaenv_(&c__2, "DGEHRD", " ", n, ilo, ihi, &
+			c_n1, (ftnlen)6, (ftnlen)1);
+		nbmin = max(i__1,i__2);
+		if (*lwork >= *n * nbmin) {
+		    nb = *lwork / *n;
+		} else {
+		    nb = 1;
+		}
+	    }
+	}
+    }
+    ldwork = *n;
+
+    if (nb < nbmin || nb >= nh) {
+
+/*        Use unblocked code below */
+
+	i__ = *ilo;
+
+    } else {
+
+/*        Use blocked code */
+
+	i__1 = *ihi - 1 - nx;
+	i__2 = nb;
+	for (i__ = *ilo; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) {
+/* Computing MIN */
+	    i__3 = nb, i__4 = *ihi - i__;
+	    ib = min(i__3,i__4);
+
+/*           Reduce columns i:i+ib-1 to Hessenberg form, returning the   
+             matrices V and T of the block reflector H = I - V*T*V**T   
+             which performs the reduction, and also the matrix Y = A*V*T */
+
+	    igraphdlahr2_(ihi, &i__, &ib, &a[i__ * a_dim1 + 1], lda, &tau[i__], t, &
+		    c__65, &work[1], &ldwork);
+
+/*           Apply the block reflector H to A(1:ihi,i+ib:ihi) from the   
+             right, computing  A := A - Y * V**T. V(i+ib,ib-1) must be set   
+             to 1 */
+
+	    ei = a[i__ + ib + (i__ + ib - 1) * a_dim1];
+	    a[i__ + ib + (i__ + ib - 1) * a_dim1] = 1.;
+	    i__3 = *ihi - i__ - ib + 1;
+	    igraphdgemm_("No transpose", "Transpose", ihi, &i__3, &ib, &c_b25, &
+		    work[1], &ldwork, &a[i__ + ib + i__ * a_dim1], lda, &
+		    c_b26, &a[(i__ + ib) * a_dim1 + 1], lda);
+	    a[i__ + ib + (i__ + ib - 1) * a_dim1] = ei;
+
+/*           Apply the block reflector H to A(1:i,i+1:i+ib-1) from the   
+             right */
+
+	    i__3 = ib - 1;
+	    igraphdtrmm_("Right", "Lower", "Transpose", "Unit", &i__, &i__3, &c_b26,
+		     &a[i__ + 1 + i__ * a_dim1], lda, &work[1], &ldwork);
+	    i__3 = ib - 2;
+	    for (j = 0; j <= i__3; ++j) {
+		igraphdaxpy_(&i__, &c_b25, &work[ldwork * j + 1], &c__1, &a[(i__ + 
+			j + 1) * a_dim1 + 1], &c__1);
+/* L30: */
+	    }
+
+/*           Apply the block reflector H to A(i+1:ihi,i+ib:n) from the   
+             left */
+
+	    i__3 = *ihi - i__;
+	    i__4 = *n - i__ - ib + 1;
+	    igraphdlarfb_("Left", "Transpose", "Forward", "Columnwise", &i__3, &
+		    i__4, &ib, &a[i__ + 1 + i__ * a_dim1], lda, t, &c__65, &a[
+		    i__ + 1 + (i__ + ib) * a_dim1], lda, &work[1], &ldwork);
+/* L40: */
+	}
+    }
+
+/*     Use unblocked code to reduce the rest of the matrix */
+
+    igraphdgehd2_(n, &i__, ihi, &a[a_offset], lda, &tau[1], &work[1], &iinfo);
+    work[1] = (doublereal) iws;
+
+    return 0;
+
+/*     End of DGEHRD */
+
+} /* igraphdgehrd_ */
+
diff --git a/igraph/src/dgemm.c b/igraph/src/dgemm.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgemm.c
@@ -0,0 +1,378 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdgemm_(char *transa, char *transb, integer *m, integer *
+	n, integer *k, doublereal *alpha, doublereal *a, integer *lda, 
+	doublereal *b, integer *ldb, doublereal *beta, doublereal *c__, 
+	integer *ldc)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, c_dim1, c_offset, i__1, i__2, 
+	    i__3;
+
+    /* Local variables */
+    integer i__, j, l, info;
+    logical nota, notb;
+    doublereal temp;
+    integer ncola;
+    extern logical igraphlsame_(char *, char *);
+    integer nrowa, nrowb;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  Purpose   
+    =======   
+
+    DGEMM  performs one of the matrix-matrix operations   
+
+       C := alpha*op( A )*op( B ) + beta*C,   
+
+    where  op( X ) is one of   
+
+       op( X ) = X   or   op( X ) = X**T,   
+
+    alpha and beta are scalars, and A, B and C are matrices, with op( A )   
+    an m by k matrix,  op( B )  a  k by n matrix and  C an m by n matrix.   
+
+    Arguments   
+    ==========   
+
+    TRANSA - CHARACTER*1.   
+             On entry, TRANSA specifies the form of op( A ) to be used in   
+             the matrix multiplication as follows:   
+
+                TRANSA = 'N' or 'n',  op( A ) = A.   
+
+                TRANSA = 'T' or 't',  op( A ) = A**T.   
+
+                TRANSA = 'C' or 'c',  op( A ) = A**T.   
+
+             Unchanged on exit.   
+
+    TRANSB - CHARACTER*1.   
+             On entry, TRANSB specifies the form of op( B ) to be used in   
+             the matrix multiplication as follows:   
+
+                TRANSB = 'N' or 'n',  op( B ) = B.   
+
+                TRANSB = 'T' or 't',  op( B ) = B**T.   
+
+                TRANSB = 'C' or 'c',  op( B ) = B**T.   
+
+             Unchanged on exit.   
+
+    M      - INTEGER.   
+             On entry,  M  specifies  the number  of rows  of the  matrix   
+             op( A )  and of the  matrix  C.  M  must  be at least  zero.   
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry,  N  specifies the number  of columns of the matrix   
+             op( B ) and the number of columns of the matrix C. N must be   
+             at least zero.   
+             Unchanged on exit.   
+
+    K      - INTEGER.   
+             On entry,  K  specifies  the number of columns of the matrix   
+             op( A ) and the number of rows of the matrix op( B ). K must   
+             be at least  zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry, ALPHA specifies the scalar alpha.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is   
+             k  when  TRANSA = 'N' or 'n',  and is  m  otherwise.   
+             Before entry with  TRANSA = 'N' or 'n',  the leading  m by k   
+             part of the array  A  must contain the matrix  A,  otherwise   
+             the leading  k by m  part of the array  A  must contain  the   
+             matrix A.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program. When  TRANSA = 'N' or 'n' then   
+             LDA must be at least  max( 1, m ), otherwise  LDA must be at   
+             least  max( 1, k ).   
+             Unchanged on exit.   
+
+    B      - DOUBLE PRECISION array of DIMENSION ( LDB, kb ), where kb is   
+             n  when  TRANSB = 'N' or 'n',  and is  k  otherwise.   
+             Before entry with  TRANSB = 'N' or 'n',  the leading  k by n   
+             part of the array  B  must contain the matrix  B,  otherwise   
+             the leading  n by k  part of the array  B  must contain  the   
+             matrix B.   
+             Unchanged on exit.   
+
+    LDB    - INTEGER.   
+             On entry, LDB specifies the first dimension of B as declared   
+             in the calling (sub) program. When  TRANSB = 'N' or 'n' then   
+             LDB must be at least  max( 1, k ), otherwise  LDB must be at   
+             least  max( 1, n ).   
+             Unchanged on exit.   
+
+    BETA   - DOUBLE PRECISION.   
+             On entry,  BETA  specifies the scalar  beta.  When  BETA  is   
+             supplied as zero then C need not be set on input.   
+             Unchanged on exit.   
+
+    C      - DOUBLE PRECISION array of DIMENSION ( LDC, n ).   
+             Before entry, the leading  m by n  part of the array  C must   
+             contain the matrix  C,  except when  beta  is zero, in which   
+             case C need not be set on entry.   
+             On exit, the array  C  is overwritten by the  m by n  matrix   
+             ( alpha*op( A )*op( B ) + beta*C ).   
+
+    LDC    - INTEGER.   
+             On entry, LDC specifies the first dimension of C as declared   
+             in  the  calling  (sub)  program.   LDC  must  be  at  least   
+             max( 1, m ).   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 3 Blas routine.   
+
+    -- Written on 8-February-1989.   
+       Jack Dongarra, Argonne National Laboratory.   
+       Iain Duff, AERE Harwell.   
+       Jeremy Du Croz, Numerical Algorithms Group Ltd.   
+       Sven Hammarling, Numerical Algorithms Group Ltd.   
+
+    =====================================================================   
+
+
+       Set  NOTA  and  NOTB  as  true if  A  and  B  respectively are not   
+       transposed and set  NROWA, NCOLA and  NROWB  as the number of rows   
+       and  columns of  A  and the  number of  rows  of  B  respectively.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+
+    /* Function Body */
+    nota = igraphlsame_(transa, "N");
+    notb = igraphlsame_(transb, "N");
+    if (nota) {
+	nrowa = *m;
+	ncola = *k;
+    } else {
+	nrowa = *k;
+	ncola = *m;
+    }
+    if (notb) {
+	nrowb = *k;
+    } else {
+	nrowb = *n;
+    }
+
+/*     Test the input parameters. */
+
+    info = 0;
+    if (! nota && ! igraphlsame_(transa, "C") && ! igraphlsame_(
+	    transa, "T")) {
+	info = 1;
+    } else if (! notb && ! igraphlsame_(transb, "C") && ! 
+	    igraphlsame_(transb, "T")) {
+	info = 2;
+    } else if (*m < 0) {
+	info = 3;
+    } else if (*n < 0) {
+	info = 4;
+    } else if (*k < 0) {
+	info = 5;
+    } else if (*lda < max(1,nrowa)) {
+	info = 8;
+    } else if (*ldb < max(1,nrowb)) {
+	info = 10;
+    } else if (*ldc < max(1,*m)) {
+	info = 13;
+    }
+    if (info != 0) {
+	igraphxerbla_("DGEMM ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*m == 0 || *n == 0 || (*alpha == 0. || *k == 0) && *beta == 1.) {
+	return 0;
+    }
+
+/*     And if  alpha.eq.zero. */
+
+    if (*alpha == 0.) {
+	if (*beta == 0.) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    c__[i__ + j * c_dim1] = 0.;
+/* L10: */
+		}
+/* L20: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L30: */
+		}
+/* L40: */
+	    }
+	}
+	return 0;
+    }
+
+/*     Start the operations. */
+
+    if (notb) {
+	if (nota) {
+
+/*           Form  C := alpha*A*B + beta*C. */
+
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (*beta == 0.) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L50: */
+		    }
+		} else if (*beta != 1.) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L60: */
+		    }
+		}
+		i__2 = *k;
+		for (l = 1; l <= i__2; ++l) {
+		    if (b[l + j * b_dim1] != 0.) {
+			temp = *alpha * b[l + j * b_dim1];
+			i__3 = *m;
+			for (i__ = 1; i__ <= i__3; ++i__) {
+			    c__[i__ + j * c_dim1] += temp * a[i__ + l * 
+				    a_dim1];
+/* L70: */
+			}
+		    }
+/* L80: */
+		}
+/* L90: */
+	    }
+	} else {
+
+/*           Form  C := alpha*A**T*B + beta*C */
+
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    temp = 0.;
+		    i__3 = *k;
+		    for (l = 1; l <= i__3; ++l) {
+			temp += a[l + i__ * a_dim1] * b[l + j * b_dim1];
+/* L100: */
+		    }
+		    if (*beta == 0.) {
+			c__[i__ + j * c_dim1] = *alpha * temp;
+		    } else {
+			c__[i__ + j * c_dim1] = *alpha * temp + *beta * c__[
+				i__ + j * c_dim1];
+		    }
+/* L110: */
+		}
+/* L120: */
+	    }
+	}
+    } else {
+	if (nota) {
+
+/*           Form  C := alpha*A*B**T + beta*C */
+
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (*beta == 0.) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L130: */
+		    }
+		} else if (*beta != 1.) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L140: */
+		    }
+		}
+		i__2 = *k;
+		for (l = 1; l <= i__2; ++l) {
+		    if (b[j + l * b_dim1] != 0.) {
+			temp = *alpha * b[j + l * b_dim1];
+			i__3 = *m;
+			for (i__ = 1; i__ <= i__3; ++i__) {
+			    c__[i__ + j * c_dim1] += temp * a[i__ + l * 
+				    a_dim1];
+/* L150: */
+			}
+		    }
+/* L160: */
+		}
+/* L170: */
+	    }
+	} else {
+
+/*           Form  C := alpha*A**T*B**T + beta*C */
+
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    temp = 0.;
+		    i__3 = *k;
+		    for (l = 1; l <= i__3; ++l) {
+			temp += a[l + i__ * a_dim1] * b[j + l * b_dim1];
+/* L180: */
+		    }
+		    if (*beta == 0.) {
+			c__[i__ + j * c_dim1] = *alpha * temp;
+		    } else {
+			c__[i__ + j * c_dim1] = *alpha * temp + *beta * c__[
+				i__ + j * c_dim1];
+		    }
+/* L190: */
+		}
+/* L200: */
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DGEMM . */
+
+} /* igraphdgemm_ */
+
diff --git a/igraph/src/dgemv.c b/igraph/src/dgemv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgemv.c
@@ -0,0 +1,302 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdgemv_(char *trans, integer *m, integer *n, doublereal *
+	alpha, doublereal *a, integer *lda, doublereal *x, integer *incx, 
+	doublereal *beta, doublereal *y, integer *incy)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j, ix, iy, jx, jy, kx, ky, info;
+    doublereal temp;
+    integer lenx, leny;
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  Purpose   
+    =======   
+
+    DGEMV  performs one of the matrix-vector operations   
+
+       y := alpha*A*x + beta*y,   or   y := alpha*A**T*x + beta*y,   
+
+    where alpha and beta are scalars, x and y are vectors and A is an   
+    m by n matrix.   
+
+    Arguments   
+    ==========   
+
+    TRANS  - CHARACTER*1.   
+             On entry, TRANS specifies the operation to be performed as   
+             follows:   
+
+                TRANS = 'N' or 'n'   y := alpha*A*x + beta*y.   
+
+                TRANS = 'T' or 't'   y := alpha*A**T*x + beta*y.   
+
+                TRANS = 'C' or 'c'   y := alpha*A**T*x + beta*y.   
+
+             Unchanged on exit.   
+
+    M      - INTEGER.   
+             On entry, M specifies the number of rows of the matrix A.   
+             M must be at least zero.   
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry, N specifies the number of columns of the matrix A.   
+             N must be at least zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry, ALPHA specifies the scalar alpha.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, n ).   
+             Before entry, the leading m by n part of the array A must   
+             contain the matrix of coefficients.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program. LDA must be at least   
+             max( 1, m ).   
+             Unchanged on exit.   
+
+    X      - DOUBLE PRECISION array of DIMENSION at least   
+             ( 1 + ( n - 1 )*abs( INCX ) ) when TRANS = 'N' or 'n'   
+             and at least   
+             ( 1 + ( m - 1 )*abs( INCX ) ) otherwise.   
+             Before entry, the incremented array X must contain the   
+             vector x.   
+             Unchanged on exit.   
+
+    INCX   - INTEGER.   
+             On entry, INCX specifies the increment for the elements of   
+             X. INCX must not be zero.   
+             Unchanged on exit.   
+
+    BETA   - DOUBLE PRECISION.   
+             On entry, BETA specifies the scalar beta. When BETA is   
+             supplied as zero then Y need not be set on input.   
+             Unchanged on exit.   
+
+    Y      - DOUBLE PRECISION array of DIMENSION at least   
+             ( 1 + ( m - 1 )*abs( INCY ) ) when TRANS = 'N' or 'n'   
+             and at least   
+             ( 1 + ( n - 1 )*abs( INCY ) ) otherwise.   
+             Before entry with BETA non-zero, the incremented array Y   
+             must contain the vector y. On exit, Y is overwritten by the   
+             updated vector y.   
+
+    INCY   - INTEGER.   
+             On entry, INCY specifies the increment for the elements of   
+             Y. INCY must not be zero.   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 2 Blas routine.   
+    The vector and matrix arguments are not referenced when N = 0, or M = 0   
+
+    -- Written on 22-October-1986.   
+       Jack Dongarra, Argonne National Lab.   
+       Jeremy Du Croz, Nag Central Office.   
+       Sven Hammarling, Nag Central Office.   
+       Richard Hanson, Sandia National Labs.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --x;
+    --y;
+
+    /* Function Body */
+    info = 0;
+    if (! igraphlsame_(trans, "N") && ! igraphlsame_(trans, "T") && ! igraphlsame_(trans, "C")
+	    ) {
+	info = 1;
+    } else if (*m < 0) {
+	info = 2;
+    } else if (*n < 0) {
+	info = 3;
+    } else if (*lda < max(1,*m)) {
+	info = 6;
+    } else if (*incx == 0) {
+	info = 8;
+    } else if (*incy == 0) {
+	info = 11;
+    }
+    if (info != 0) {
+	igraphxerbla_("DGEMV ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*m == 0 || *n == 0 || *alpha == 0. && *beta == 1.) {
+	return 0;
+    }
+
+/*     Set  LENX  and  LENY, the lengths of the vectors x and y, and set   
+       up the start points in  X  and  Y. */
+
+    if (igraphlsame_(trans, "N")) {
+	lenx = *n;
+	leny = *m;
+    } else {
+	lenx = *m;
+	leny = *n;
+    }
+    if (*incx > 0) {
+	kx = 1;
+    } else {
+	kx = 1 - (lenx - 1) * *incx;
+    }
+    if (*incy > 0) {
+	ky = 1;
+    } else {
+	ky = 1 - (leny - 1) * *incy;
+    }
+
+/*     Start the operations. In this version the elements of A are   
+       accessed sequentially with one pass through A.   
+
+       First form  y := beta*y. */
+
+    if (*beta != 1.) {
+	if (*incy == 1) {
+	    if (*beta == 0.) {
+		i__1 = leny;
+		for (i__ = 1; i__ <= i__1; ++i__) {
+		    y[i__] = 0.;
+/* L10: */
+		}
+	    } else {
+		i__1 = leny;
+		for (i__ = 1; i__ <= i__1; ++i__) {
+		    y[i__] = *beta * y[i__];
+/* L20: */
+		}
+	    }
+	} else {
+	    iy = ky;
+	    if (*beta == 0.) {
+		i__1 = leny;
+		for (i__ = 1; i__ <= i__1; ++i__) {
+		    y[iy] = 0.;
+		    iy += *incy;
+/* L30: */
+		}
+	    } else {
+		i__1 = leny;
+		for (i__ = 1; i__ <= i__1; ++i__) {
+		    y[iy] = *beta * y[iy];
+		    iy += *incy;
+/* L40: */
+		}
+	    }
+	}
+    }
+    if (*alpha == 0.) {
+	return 0;
+    }
+    if (igraphlsame_(trans, "N")) {
+
+/*        Form  y := alpha*A*x + y. */
+
+	jx = kx;
+	if (*incy == 1) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (x[jx] != 0.) {
+		    temp = *alpha * x[jx];
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			y[i__] += temp * a[i__ + j * a_dim1];
+/* L50: */
+		    }
+		}
+		jx += *incx;
+/* L60: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (x[jx] != 0.) {
+		    temp = *alpha * x[jx];
+		    iy = ky;
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			y[iy] += temp * a[i__ + j * a_dim1];
+			iy += *incy;
+/* L70: */
+		    }
+		}
+		jx += *incx;
+/* L80: */
+	    }
+	}
+    } else {
+
+/*        Form  y := alpha*A**T*x + y. */
+
+	jy = ky;
+	if (*incx == 1) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		temp = 0.;
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    temp += a[i__ + j * a_dim1] * x[i__];
+/* L90: */
+		}
+		y[jy] += *alpha * temp;
+		jy += *incy;
+/* L100: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		temp = 0.;
+		ix = kx;
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    temp += a[i__ + j * a_dim1] * x[ix];
+		    ix += *incx;
+/* L110: */
+		}
+		y[jy] += *alpha * temp;
+		jy += *incy;
+/* L120: */
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DGEMV . */
+
+} /* igraphdgemv_ */
+
diff --git a/igraph/src/dgeqr2.c b/igraph/src/dgeqr2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgeqr2.c
@@ -0,0 +1,220 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DGEQR2 computes the QR factorization of a general rectangular matrix using an unblocked algorit
+hm.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGEQR2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgeqr2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgeqr2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgeqr2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGEQR2( M, N, A, LDA, TAU, WORK, INFO )   
+
+         INTEGER            INFO, LDA, M, N   
+         DOUBLE PRECISION   A( LDA, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGEQR2 computes a QR factorization of a real m by n matrix A:   
+   > A = Q * R.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.  M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the m by n matrix A.   
+   >          On exit, the elements on and above the diagonal of the array   
+   >          contain the min(m,n) by n upper trapezoidal matrix R (R is   
+   >          upper triangular if m >= n); the elements below the diagonal,   
+   >          with the array TAU, represent the orthogonal matrix Q as a   
+   >          product of elementary reflectors (see Further Details).   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (min(M,N))   
+   >          The scalar factors of the elementary reflectors (see Further   
+   >          Details).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleGEcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The matrix Q is represented as a product of elementary reflectors   
+   >   
+   >     Q = H(1) H(2) . . . H(k), where k = min(m,n).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(1:i-1) = 0 and v(i) = 1; v(i+1:m) is stored on exit in A(i+1:m,i),   
+   >  and tau in TAU(i).   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdgeqr2_(integer *m, integer *n, doublereal *a, integer *
+	lda, doublereal *tau, doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__, k;
+    doublereal aii;
+    extern /* Subroutine */ int igraphdlarf_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *), igraphdlarfg_(integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *), igraphxerbla_(char *, integer *,
+	     ftnlen);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    if (*m < 0) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*m)) {
+	*info = -4;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGEQR2", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+    k = min(*m,*n);
+
+    i__1 = k;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+
+/*        Generate elementary reflector H(i) to annihilate A(i+1:m,i) */
+
+	i__2 = *m - i__ + 1;
+/* Computing MIN */
+	i__3 = i__ + 1;
+	igraphdlarfg_(&i__2, &a[i__ + i__ * a_dim1], &a[min(i__3,*m) + i__ * a_dim1]
+		, &c__1, &tau[i__]);
+	if (i__ < *n) {
+
+/*           Apply H(i) to A(i:m,i+1:n) from the left */
+
+	    aii = a[i__ + i__ * a_dim1];
+	    a[i__ + i__ * a_dim1] = 1.;
+	    i__2 = *m - i__ + 1;
+	    i__3 = *n - i__;
+	    igraphdlarf_("Left", &i__2, &i__3, &a[i__ + i__ * a_dim1], &c__1, &tau[
+		    i__], &a[i__ + (i__ + 1) * a_dim1], lda, &work[1]);
+	    a[i__ + i__ * a_dim1] = aii;
+	}
+/* L10: */
+    }
+    return 0;
+
+/*     End of DGEQR2 */
+
+} /* igraphdgeqr2_ */
+
diff --git a/igraph/src/dger.c b/igraph/src/dger.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dger.c
@@ -0,0 +1,185 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdger_(integer *m, integer *n, doublereal *alpha, 
+	doublereal *x, integer *incx, doublereal *y, integer *incy, 
+	doublereal *a, integer *lda)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j, ix, jy, kx, info;
+    doublereal temp;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  Purpose   
+    =======   
+
+    DGER   performs the rank 1 operation   
+
+       A := alpha*x*y**T + A,   
+
+    where alpha is a scalar, x is an m element vector, y is an n element   
+    vector and A is an m by n matrix.   
+
+    Arguments   
+    ==========   
+
+    M      - INTEGER.   
+             On entry, M specifies the number of rows of the matrix A.   
+             M must be at least zero.   
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry, N specifies the number of columns of the matrix A.   
+             N must be at least zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry, ALPHA specifies the scalar alpha.   
+             Unchanged on exit.   
+
+    X      - DOUBLE PRECISION array of dimension at least   
+             ( 1 + ( m - 1 )*abs( INCX ) ).   
+             Before entry, the incremented array X must contain the m   
+             element vector x.   
+             Unchanged on exit.   
+
+    INCX   - INTEGER.   
+             On entry, INCX specifies the increment for the elements of   
+             X. INCX must not be zero.   
+             Unchanged on exit.   
+
+    Y      - DOUBLE PRECISION array of dimension at least   
+             ( 1 + ( n - 1 )*abs( INCY ) ).   
+             Before entry, the incremented array Y must contain the n   
+             element vector y.   
+             Unchanged on exit.   
+
+    INCY   - INTEGER.   
+             On entry, INCY specifies the increment for the elements of   
+             Y. INCY must not be zero.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, n ).   
+             Before entry, the leading m by n part of the array A must   
+             contain the matrix of coefficients. On exit, A is   
+             overwritten by the updated matrix.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program. LDA must be at least   
+             max( 1, m ).   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 2 Blas routine.   
+
+    -- Written on 22-October-1986.   
+       Jack Dongarra, Argonne National Lab.   
+       Jeremy Du Croz, Nag Central Office.   
+       Sven Hammarling, Nag Central Office.   
+       Richard Hanson, Sandia National Labs.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    --x;
+    --y;
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    info = 0;
+    if (*m < 0) {
+	info = 1;
+    } else if (*n < 0) {
+	info = 2;
+    } else if (*incx == 0) {
+	info = 5;
+    } else if (*incy == 0) {
+	info = 7;
+    } else if (*lda < max(1,*m)) {
+	info = 9;
+    }
+    if (info != 0) {
+	igraphxerbla_("DGER  ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*m == 0 || *n == 0 || *alpha == 0.) {
+	return 0;
+    }
+
+/*     Start the operations. In this version the elements of A are   
+       accessed sequentially with one pass through A. */
+
+    if (*incy > 0) {
+	jy = 1;
+    } else {
+	jy = 1 - (*n - 1) * *incy;
+    }
+    if (*incx == 1) {
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    if (y[jy] != 0.) {
+		temp = *alpha * y[jy];
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    a[i__ + j * a_dim1] += x[i__] * temp;
+/* L10: */
+		}
+	    }
+	    jy += *incy;
+/* L20: */
+	}
+    } else {
+	if (*incx > 0) {
+	    kx = 1;
+	} else {
+	    kx = 1 - (*m - 1) * *incx;
+	}
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    if (y[jy] != 0.) {
+		temp = *alpha * y[jy];
+		ix = kx;
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    a[i__ + j * a_dim1] += x[ix] * temp;
+		    ix += *incx;
+/* L30: */
+		}
+	    }
+	    jy += *incy;
+/* L40: */
+	}
+    }
+
+    return 0;
+
+/*     End of DGER  . */
+
+} /* igraphdger_ */
+
diff --git a/igraph/src/dgesv.c b/igraph/src/dgesv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgesv.c
@@ -0,0 +1,201 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief <b> DGESV computes the solution to system of linear equations A * X = B for GE matrices</b>   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGESV + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgesv.f
+">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgesv.f
+">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgesv.f
+">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGESV( N, NRHS, A, LDA, IPIV, B, LDB, INFO )   
+
+         INTEGER            INFO, LDA, LDB, N, NRHS   
+         INTEGER            IPIV( * )   
+         DOUBLE PRECISION   A( LDA, * ), B( LDB, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGESV computes the solution to a real system of linear equations   
+   >    A * X = B,   
+   > where A is an N-by-N matrix and X and B are N-by-NRHS matrices.   
+   >   
+   > The LU decomposition with partial pivoting and row interchanges is   
+   > used to factor A as   
+   >    A = P * L * U,   
+   > where P is a permutation matrix, L is unit lower triangular, and U is   
+   > upper triangular.  The factored form of A is then used to solve the   
+   > system of equations A * X = B.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of linear equations, i.e., the order of the   
+   >          matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] NRHS   
+   > \verbatim   
+   >          NRHS is INTEGER   
+   >          The number of right hand sides, i.e., the number of columns   
+   >          of the matrix B.  NRHS >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the N-by-N coefficient matrix A.   
+   >          On exit, the factors L and U from the factorization   
+   >          A = P*L*U; the unit diagonal elements of L are not stored.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] IPIV   
+   > \verbatim   
+   >          IPIV is INTEGER array, dimension (N)   
+   >          The pivot indices that define the permutation matrix P;   
+   >          row i of the matrix was interchanged with row IPIV(i).   
+   > \endverbatim   
+   >   
+   > \param[in,out] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (LDB,NRHS)   
+   >          On entry, the N-by-NRHS matrix of right hand side matrix B.   
+   >          On exit, if INFO = 0, the N-by-NRHS solution matrix X.   
+   > \endverbatim   
+   >   
+   > \param[in] LDB   
+   > \verbatim   
+   >          LDB is INTEGER   
+   >          The leading dimension of the array B.  LDB >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  if INFO = i, U(i,i) is exactly zero.  The factorization   
+   >                has been completed, but the factor U is exactly   
+   >                singular, so the solution could not be computed.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleGEsolve   
+
+    =====================================================================   
+   Subroutine */ int igraphdgesv_(integer *n, integer *nrhs, doublereal *a, integer 
+	*lda, integer *ipiv, doublereal *b, integer *ldb, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, i__1;
+
+    /* Local variables */
+    extern /* Subroutine */ int igraphdgetrf_(integer *, integer *, doublereal *, 
+	    integer *, integer *, integer *), igraphxerbla_(char *, integer *, 
+	    ftnlen), igraphdgetrs_(char *, integer *, integer *, doublereal *, 
+	    integer *, integer *, doublereal *, integer *, integer *);
+
+
+/*  -- LAPACK driver routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --ipiv;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+
+    /* Function Body */
+    *info = 0;
+    if (*n < 0) {
+	*info = -1;
+    } else if (*nrhs < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*n)) {
+	*info = -4;
+    } else if (*ldb < max(1,*n)) {
+	*info = -7;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGESV ", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Compute the LU factorization of A. */
+
+    igraphdgetrf_(n, n, &a[a_offset], lda, &ipiv[1], info);
+    if (*info == 0) {
+
+/*        Solve the system A*X = B, overwriting B with X. */
+
+	igraphdgetrs_("No transpose", n, nrhs, &a[a_offset], lda, &ipiv[1], &b[
+		b_offset], ldb, info);
+    }
+    return 0;
+
+/*     End of DGESV */
+
+} /* igraphdgesv_ */
+
diff --git a/igraph/src/dgetf2.c b/igraph/src/dgetf2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgetf2.c
@@ -0,0 +1,244 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b8 = -1.;
+
+/* > \brief \b DGETF2 computes the LU factorization of a general m-by-n matrix using partial pivoting with row
+ interchanges (unblocked algorithm).   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGETF2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgetf2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgetf2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgetf2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGETF2( M, N, A, LDA, IPIV, INFO )   
+
+         INTEGER            INFO, LDA, M, N   
+         INTEGER            IPIV( * )   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGETF2 computes an LU factorization of a general m-by-n matrix A   
+   > using partial pivoting with row interchanges.   
+   >   
+   > The factorization has the form   
+   >    A = P * L * U   
+   > where P is a permutation matrix, L is lower triangular with unit   
+   > diagonal elements (lower trapezoidal if m > n), and U is upper   
+   > triangular (upper trapezoidal if m < n).   
+   >   
+   > This is the right-looking Level 2 BLAS version of the algorithm.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.  M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the m by n matrix to be factored.   
+   >          On exit, the factors L and U from the factorization   
+   >          A = P*L*U; the unit diagonal elements of L are not stored.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] IPIV   
+   > \verbatim   
+   >          IPIV is INTEGER array, dimension (min(M,N))   
+   >          The pivot indices; for 1 <= i <= min(M,N), row i of the   
+   >          matrix was interchanged with row IPIV(i).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -k, the k-th argument had an illegal value   
+   >          > 0: if INFO = k, U(k,k) is exactly zero. The factorization   
+   >               has been completed, but the factor U is exactly   
+   >               singular, and division by zero will occur if it is used   
+   >               to solve a system of equations.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleGEcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdgetf2_(integer *m, integer *n, doublereal *a, integer *
+	lda, integer *ipiv, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+    doublereal d__1;
+
+    /* Local variables */
+    integer i__, j, jp;
+    extern /* Subroutine */ int igraphdger_(integer *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *), igraphdscal_(integer *, doublereal *, doublereal *, integer 
+	    *);
+    doublereal sfmin;
+    extern /* Subroutine */ int igraphdswap_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    extern doublereal igraphdlamch_(char *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --ipiv;
+
+    /* Function Body */
+    *info = 0;
+    if (*m < 0) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*m)) {
+	*info = -4;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGETF2", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0) {
+	return 0;
+    }
+
+/*     Compute machine safe minimum */
+
+    sfmin = igraphdlamch_("S");
+
+    i__1 = min(*m,*n);
+    for (j = 1; j <= i__1; ++j) {
+
+/*        Find pivot and test for singularity. */
+
+	i__2 = *m - j + 1;
+	jp = j - 1 + igraphidamax_(&i__2, &a[j + j * a_dim1], &c__1);
+	ipiv[j] = jp;
+	if (a[jp + j * a_dim1] != 0.) {
+
+/*           Apply the interchange to columns 1:N. */
+
+	    if (jp != j) {
+		igraphdswap_(n, &a[j + a_dim1], lda, &a[jp + a_dim1], lda);
+	    }
+
+/*           Compute elements J+1:M of J-th column. */
+
+	    if (j < *m) {
+		if ((d__1 = a[j + j * a_dim1], abs(d__1)) >= sfmin) {
+		    i__2 = *m - j;
+		    d__1 = 1. / a[j + j * a_dim1];
+		    igraphdscal_(&i__2, &d__1, &a[j + 1 + j * a_dim1], &c__1);
+		} else {
+		    i__2 = *m - j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			a[j + i__ + j * a_dim1] /= a[j + j * a_dim1];
+/* L20: */
+		    }
+		}
+	    }
+
+	} else if (*info == 0) {
+
+	    *info = j;
+	}
+
+	if (j < min(*m,*n)) {
+
+/*           Update trailing submatrix. */
+
+	    i__2 = *m - j;
+	    i__3 = *n - j;
+	    igraphdger_(&i__2, &i__3, &c_b8, &a[j + 1 + j * a_dim1], &c__1, &a[j + (
+		    j + 1) * a_dim1], lda, &a[j + 1 + (j + 1) * a_dim1], lda);
+	}
+/* L10: */
+    }
+    return 0;
+
+/*     End of DGETF2 */
+
+} /* igraphdgetf2_ */
+
diff --git a/igraph/src/dgetrf.c b/igraph/src/dgetrf.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgetrf.c
@@ -0,0 +1,270 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static doublereal c_b16 = 1.;
+static doublereal c_b19 = -1.;
+
+/* > \brief \b DGETRF   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGETRF + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgetrf.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgetrf.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgetrf.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGETRF( M, N, A, LDA, IPIV, INFO )   
+
+         INTEGER            INFO, LDA, M, N   
+         INTEGER            IPIV( * )   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGETRF computes an LU factorization of a general M-by-N matrix A   
+   > using partial pivoting with row interchanges.   
+   >   
+   > The factorization has the form   
+   >    A = P * L * U   
+   > where P is a permutation matrix, L is lower triangular with unit   
+   > diagonal elements (lower trapezoidal if m > n), and U is upper   
+   > triangular (upper trapezoidal if m < n).   
+   >   
+   > This is the right-looking Level 3 BLAS version of the algorithm.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.  M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the M-by-N matrix to be factored.   
+   >          On exit, the factors L and U from the factorization   
+   >          A = P*L*U; the unit diagonal elements of L are not stored.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] IPIV   
+   > \verbatim   
+   >          IPIV is INTEGER array, dimension (min(M,N))   
+   >          The pivot indices; for 1 <= i <= min(M,N), row i of the   
+   >          matrix was interchanged with row IPIV(i).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  if INFO = i, U(i,i) is exactly zero. The factorization   
+   >                has been completed, but the factor U is exactly   
+   >                singular, and division by zero will occur if it is used   
+   >                to solve a system of equations.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleGEcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdgetrf_(integer *m, integer *n, doublereal *a, integer *
+	lda, integer *ipiv, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3, i__4, i__5;
+
+    /* Local variables */
+    integer i__, j, jb, nb;
+    extern /* Subroutine */ int igraphdgemm_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *);
+    integer iinfo;
+    extern /* Subroutine */ int igraphdtrsm_(char *, char *, char *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdgetf2_(
+	    integer *, integer *, doublereal *, integer *, integer *, integer 
+	    *), igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    extern /* Subroutine */ int igraphdlaswp_(integer *, doublereal *, integer *, 
+	    integer *, integer *, integer *, integer *);
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --ipiv;
+
+    /* Function Body */
+    *info = 0;
+    if (*m < 0) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*m)) {
+	*info = -4;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGETRF", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0) {
+	return 0;
+    }
+
+/*     Determine the block size for this environment. */
+
+    nb = igraphilaenv_(&c__1, "DGETRF", " ", m, n, &c_n1, &c_n1, (ftnlen)6, (ftnlen)
+	    1);
+    if (nb <= 1 || nb >= min(*m,*n)) {
+
+/*        Use unblocked code. */
+
+	igraphdgetf2_(m, n, &a[a_offset], lda, &ipiv[1], info);
+    } else {
+
+/*        Use blocked code. */
+
+	i__1 = min(*m,*n);
+	i__2 = nb;
+	for (j = 1; i__2 < 0 ? j >= i__1 : j <= i__1; j += i__2) {
+/* Computing MIN */
+	    i__3 = min(*m,*n) - j + 1;
+	    jb = min(i__3,nb);
+
+/*           Factor diagonal and subdiagonal blocks and test for exact   
+             singularity. */
+
+	    i__3 = *m - j + 1;
+	    igraphdgetf2_(&i__3, &jb, &a[j + j * a_dim1], lda, &ipiv[j], &iinfo);
+
+/*           Adjust INFO and the pivot indices. */
+
+	    if (*info == 0 && iinfo > 0) {
+		*info = iinfo + j - 1;
+	    }
+/* Computing MIN */
+	    i__4 = *m, i__5 = j + jb - 1;
+	    i__3 = min(i__4,i__5);
+	    for (i__ = j; i__ <= i__3; ++i__) {
+		ipiv[i__] = j - 1 + ipiv[i__];
+/* L10: */
+	    }
+
+/*           Apply interchanges to columns 1:J-1. */
+
+	    i__3 = j - 1;
+	    i__4 = j + jb - 1;
+	    igraphdlaswp_(&i__3, &a[a_offset], lda, &j, &i__4, &ipiv[1], &c__1);
+
+	    if (j + jb <= *n) {
+
+/*              Apply interchanges to columns J+JB:N. */
+
+		i__3 = *n - j - jb + 1;
+		i__4 = j + jb - 1;
+		igraphdlaswp_(&i__3, &a[(j + jb) * a_dim1 + 1], lda, &j, &i__4, &
+			ipiv[1], &c__1);
+
+/*              Compute block row of U. */
+
+		i__3 = *n - j - jb + 1;
+		igraphdtrsm_("Left", "Lower", "No transpose", "Unit", &jb, &i__3, &
+			c_b16, &a[j + j * a_dim1], lda, &a[j + (j + jb) * 
+			a_dim1], lda);
+		if (j + jb <= *m) {
+
+/*                 Update trailing submatrix. */
+
+		    i__3 = *m - j - jb + 1;
+		    i__4 = *n - j - jb + 1;
+		    igraphdgemm_("No transpose", "No transpose", &i__3, &i__4, &jb, 
+			    &c_b19, &a[j + jb + j * a_dim1], lda, &a[j + (j + 
+			    jb) * a_dim1], lda, &c_b16, &a[j + jb + (j + jb) *
+			     a_dim1], lda);
+		}
+	    }
+/* L20: */
+	}
+    }
+    return 0;
+
+/*     End of DGETRF */
+
+} /* igraphdgetrf_ */
+
diff --git a/igraph/src/dgetrs.c b/igraph/src/dgetrs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgetrs.c
@@ -0,0 +1,246 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b12 = 1.;
+static integer c_n1 = -1;
+
+/* > \brief \b DGETRS   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DGETRS + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgetrs.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgetrs.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgetrs.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DGETRS( TRANS, N, NRHS, A, LDA, IPIV, B, LDB, INFO )   
+
+         CHARACTER          TRANS   
+         INTEGER            INFO, LDA, LDB, N, NRHS   
+         INTEGER            IPIV( * )   
+         DOUBLE PRECISION   A( LDA, * ), B( LDB, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DGETRS solves a system of linear equations   
+   >    A * X = B  or  A**T * X = B   
+   > with a general N-by-N matrix A using the LU factorization computed   
+   > by DGETRF.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] TRANS   
+   > \verbatim   
+   >          TRANS is CHARACTER*1   
+   >          Specifies the form of the system of equations:   
+   >          = 'N':  A * X = B  (No transpose)   
+   >          = 'T':  A**T* X = B  (Transpose)   
+   >          = 'C':  A**T* X = B  (Conjugate transpose = Transpose)   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] NRHS   
+   > \verbatim   
+   >          NRHS is INTEGER   
+   >          The number of right hand sides, i.e., the number of columns   
+   >          of the matrix B.  NRHS >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The factors L and U from the factorization A = P*L*U   
+   >          as computed by DGETRF.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] IPIV   
+   > \verbatim   
+   >          IPIV is INTEGER array, dimension (N)   
+   >          The pivot indices from DGETRF; for 1<=i<=N, row i of the   
+   >          matrix was interchanged with row IPIV(i).   
+   > \endverbatim   
+   >   
+   > \param[in,out] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (LDB,NRHS)   
+   >          On entry, the right hand side matrix B.   
+   >          On exit, the solution matrix X.   
+   > \endverbatim   
+   >   
+   > \param[in] LDB   
+   > \verbatim   
+   >          LDB is INTEGER   
+   >          The leading dimension of the array B.  LDB >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleGEcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdgetrs_(char *trans, integer *n, integer *nrhs, 
+	doublereal *a, integer *lda, integer *ipiv, doublereal *b, integer *
+	ldb, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, i__1;
+
+    /* Local variables */
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdtrsm_(char *, char *, char *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphxerbla_(
+	    char *, integer *, ftnlen), igraphdlaswp_(integer *, doublereal *, 
+	    integer *, integer *, integer *, integer *, integer *);
+    logical notran;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --ipiv;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+
+    /* Function Body */
+    *info = 0;
+    notran = igraphlsame_(trans, "N");
+    if (! notran && ! igraphlsame_(trans, "T") && ! igraphlsame_(
+	    trans, "C")) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*nrhs < 0) {
+	*info = -3;
+    } else if (*lda < max(1,*n)) {
+	*info = -5;
+    } else if (*ldb < max(1,*n)) {
+	*info = -8;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DGETRS", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0 || *nrhs == 0) {
+	return 0;
+    }
+
+    if (notran) {
+
+/*        Solve A * X = B.   
+
+          Apply row interchanges to the right hand sides. */
+
+	igraphdlaswp_(nrhs, &b[b_offset], ldb, &c__1, n, &ipiv[1], &c__1);
+
+/*        Solve L*X = B, overwriting B with X. */
+
+	igraphdtrsm_("Left", "Lower", "No transpose", "Unit", n, nrhs, &c_b12, &a[
+		a_offset], lda, &b[b_offset], ldb);
+
+/*        Solve U*X = B, overwriting B with X. */
+
+	igraphdtrsm_("Left", "Upper", "No transpose", "Non-unit", n, nrhs, &c_b12, &
+		a[a_offset], lda, &b[b_offset], ldb);
+    } else {
+
+/*        Solve A**T * X = B.   
+
+          Solve U**T *X = B, overwriting B with X. */
+
+	igraphdtrsm_("Left", "Upper", "Transpose", "Non-unit", n, nrhs, &c_b12, &a[
+		a_offset], lda, &b[b_offset], ldb);
+
+/*        Solve L**T *X = B, overwriting B with X. */
+
+	igraphdtrsm_("Left", "Lower", "Transpose", "Unit", n, nrhs, &c_b12, &a[
+		a_offset], lda, &b[b_offset], ldb);
+
+/*        Apply row interchanges to the solution vectors. */
+
+	igraphdlaswp_(nrhs, &b[b_offset], ldb, &c__1, n, &ipiv[1], &c_n1);
+    }
+
+    return 0;
+
+/*     End of DGETRS */
+
+} /* igraphdgetrs_ */
+
diff --git a/igraph/src/dgetv0.c b/igraph/src/dgetv0.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dgetv0.c
@@ -0,0 +1,480 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b24 = 1.;
+static doublereal c_b26 = 0.;
+static doublereal c_b29 = -1.;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dgetv0   
+
+   \Description:   
+    Generate a random initial residual vector for the Arnoldi process.   
+    Force the residual vector to be in the range of the operator OP.   
+
+   \Usage:   
+    call dgetv0   
+       ( IDO, BMAT, ITRY, INITV, N, J, V, LDV, RESID, RNORM,   
+         IPNTR, WORKD, IERR )   
+
+   \Arguments   
+    IDO     Integer.  (INPUT/OUTPUT)   
+            Reverse communication flag.  IDO must be zero on the first   
+            call to dgetv0.   
+            -------------------------------------------------------------   
+            IDO =  0: first call to the reverse communication interface   
+            IDO = -1: compute  Y = OP * X  where   
+                      IPNTR(1) is the pointer into WORKD for X,   
+                      IPNTR(2) is the pointer into WORKD for Y.   
+                      This is for the initialization phase to force the   
+                      starting vector into the range of OP.   
+            IDO =  2: compute  Y = B * X  where   
+                      IPNTR(1) is the pointer into WORKD for X,   
+                      IPNTR(2) is the pointer into WORKD for Y.   
+            IDO = 99: done   
+            -------------------------------------------------------------   
+
+    BMAT    Character*1.  (INPUT)   
+            BMAT specifies the type of the matrix B in the (generalized)   
+            eigenvalue problem A*x = lambda*B*x.   
+            B = 'I' -> standard eigenvalue problem A*x = lambda*x   
+            B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x   
+
+    ITRY    Integer.  (INPUT)   
+            ITRY counts the number of times that dgetv0 is called.   
+            It should be set to 1 on the initial call to dgetv0.   
+
+    INITV   Logical variable.  (INPUT)   
+            .TRUE.  => the initial residual vector is given in RESID.   
+            .FALSE. => generate a random initial residual vector.   
+
+    N       Integer.  (INPUT)   
+            Dimension of the problem.   
+
+    J       Integer.  (INPUT)   
+            Index of the residual vector to be generated, with respect to   
+            the Arnoldi process.  J > 1 in case of a "restart".   
+
+    V       Double precision N by J array.  (INPUT)   
+            The first J-1 columns of V contain the current Arnoldi basis   
+            if this is a "restart".   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling   
+            program.   
+
+    RESID   Double precision array of length N.  (INPUT/OUTPUT)   
+            Initial residual vector to be generated.  If RESID is   
+            provided, force RESID into the range of the operator OP.   
+
+    RNORM   Double precision scalar.  (OUTPUT)   
+            B-norm of the generated residual.   
+
+    IPNTR   Integer array of length 3.  (OUTPUT)   
+
+    WORKD   Double precision work array of length 2*N.  (REVERSE COMMUNICATION).   
+            On exit, WORK(1:N) = B*RESID to be used in SSAITR.   
+
+    IERR    Integer.  (OUTPUT)   
+            =  0: Normal exit.   
+            = -1: Cannot generate a nontrivial restarted residual vector   
+                  in the range of the operator OP.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+
+   \Routines called:   
+       second  ARPACK utility routine for timing.   
+       dvout   ARPACK utility routine for vector output.   
+       dlarnv  LAPACK routine for generating a random vector.   
+       dgemv   Level 2 BLAS routine for matrix vector multiplication.   
+       dcopy   Level 1 BLAS that copies one vector to another.   
+       ddot    Level 1 BLAS that computes the scalar product of two vectors.   
+       dnrm2   Level 1 BLAS that computes the norm of a vector.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \SCCS Information: @(#)   
+   FILE: getv0.F   SID: 2.6   DATE OF SID: 8/27/96   RELEASE: 2   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdgetv0_(integer *ido, char *bmat, integer *itry, logical 
+	*initv, integer *n, integer *j, doublereal *v, integer *ldv, 
+	doublereal *resid, doublereal *rnorm, integer *ipntr, doublereal *
+	workd, integer *ierr)
+{
+    /* Initialized data */
+
+    IGRAPH_F77_SAVE logical inits = TRUE_;
+
+    /* System generated locals */
+    integer v_dim1, v_offset, i__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    real t0, t1, t2, t3;
+    integer jj, nbx = 0;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    IGRAPH_F77_SAVE integer iter;
+    IGRAPH_F77_SAVE logical orth;
+    integer nopx = 0;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    IGRAPH_F77_SAVE integer iseed[4];
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *);
+    integer idist;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    IGRAPH_F77_SAVE logical first;
+    real tmvbx = 0;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen);
+    integer mgetv0 = 0;
+    real tgetv0 = 0;
+    IGRAPH_F77_SAVE doublereal rnorm0;
+    extern /* Subroutine */ int igraphsecond_(real *);
+    integer logfil, ndigit;
+    extern /* Subroutine */ int igraphdlarnv_(integer *, integer *, integer *, 
+	    doublereal *);
+    IGRAPH_F77_SAVE integer msglvl;
+    real tmvopx = 0;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %------------------------%   
+       | Local Scalars & Arrays |   
+       %------------------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------%   
+       | Data Statements |   
+       %-----------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    --ipntr;
+
+    /* Function Body   
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+
+       %-----------------------------------%   
+       | Initialize the seed of the LAPACK |   
+       | random number generator           |   
+       %-----------------------------------% */
+
+    if (inits) {
+	iseed[0] = 1;
+	iseed[1] = 3;
+	iseed[2] = 5;
+	iseed[3] = 7;
+	inits = FALSE_;
+    }
+
+    if (*ido == 0) {
+
+/*        %-------------------------------%   
+          | Initialize timing statistics  |   
+          | & message level for debugging |   
+          %-------------------------------% */
+
+	igraphsecond_(&t0);
+	msglvl = mgetv0;
+
+	*ierr = 0;
+	iter = 0;
+	first = FALSE_;
+	orth = FALSE_;
+
+/*        %-----------------------------------------------------%   
+          | Possibly generate a random starting vector in RESID |   
+          | Use a LAPACK random number generator used by the    |   
+          | matrix generation routines.                         |   
+          |    idist = 1: uniform (0,1)  distribution;          |   
+          |    idist = 2: uniform (-1,1) distribution;          |   
+          |    idist = 3: normal  (0,1)  distribution;          |   
+          %-----------------------------------------------------% */
+
+	if (! (*initv)) {
+	    idist = 2;
+	    igraphdlarnv_(&idist, iseed, n, &resid[1]);
+	}
+
+/*        %----------------------------------------------------------%   
+          | Force the starting vector into the range of OP to handle |   
+          | the generalized problem when B is possibly (singular).   |   
+          %----------------------------------------------------------% */
+
+	igraphsecond_(&t2);
+	if (*(unsigned char *)bmat == 'G') {
+	    ++nopx;
+	    ipntr[1] = 1;
+	    ipntr[2] = *n + 1;
+	    igraphdcopy_(n, &resid[1], &c__1, &workd[1], &c__1);
+	    *ido = -1;
+	    goto L9000;
+	}
+    }
+
+/*     %-----------------------------------------%   
+       | Back from computing OP*(initial-vector) |   
+       %-----------------------------------------% */
+
+    if (first) {
+	goto L20;
+    }
+
+/*     %-----------------------------------------------%   
+       | Back from computing B*(orthogonalized-vector) |   
+       %-----------------------------------------------% */
+
+    if (orth) {
+	goto L40;
+    }
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvopx += t3 - t2;
+    }
+
+/*     %------------------------------------------------------%   
+       | Starting vector is now in the range of OP; r = OP*r; |   
+       | Compute B-norm of starting vector.                   |   
+       %------------------------------------------------------% */
+
+    igraphsecond_(&t2);
+    first = TRUE_;
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	igraphdcopy_(n, &workd[*n + 1], &c__1, &resid[1], &c__1);
+	ipntr[1] = *n + 1;
+	ipntr[2] = 1;
+	*ido = 2;
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[1], &c__1);
+    }
+
+L20:
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+    first = FALSE_;
+    if (*(unsigned char *)bmat == 'G') {
+	rnorm0 = igraphddot_(n, &resid[1], &c__1, &workd[1], &c__1);
+	rnorm0 = sqrt((abs(rnorm0)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	rnorm0 = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+    *rnorm = rnorm0;
+
+/*     %---------------------------------------------%   
+       | Exit if this is the very first Arnoldi step |   
+       %---------------------------------------------% */
+
+    if (*j == 1) {
+	goto L50;
+    }
+
+/*     %----------------------------------------------------------------   
+       | Otherwise need to B-orthogonalize the starting vector against |   
+       | the current Arnoldi basis using Gram-Schmidt with iter. ref.  |   
+       | This is the case where an invariant subspace is encountered   |   
+       | in the middle of the Arnoldi factorization.                   |   
+       |                                                               |   
+       |       s = V^{T}*B*r;   r = r - V*s;                           |   
+       |                                                               |   
+       | Stopping criteria used for iter. ref. is discussed in         |   
+       | Parlett's book, page 107 and in Gragg & Reichel TOMS paper.   |   
+       %---------------------------------------------------------------% */
+
+    orth = TRUE_;
+L30:
+
+    i__1 = *j - 1;
+    igraphdgemv_("T", n, &i__1, &c_b24, &v[v_offset], ldv, &workd[1], &c__1, &c_b26,
+	     &workd[*n + 1], &c__1);
+    i__1 = *j - 1;
+    igraphdgemv_("N", n, &i__1, &c_b29, &v[v_offset], ldv, &workd[*n + 1], &c__1, &
+	    c_b24, &resid[1], &c__1);
+
+/*     %----------------------------------------------------------%   
+       | Compute the B-norm of the orthogonalized starting vector |   
+       %----------------------------------------------------------% */
+
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	igraphdcopy_(n, &resid[1], &c__1, &workd[*n + 1], &c__1);
+	ipntr[1] = *n + 1;
+	ipntr[2] = 1;
+	*ido = 2;
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[1], &c__1);
+    }
+
+L40:
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+    if (*(unsigned char *)bmat == 'G') {
+	*rnorm = igraphddot_(n, &resid[1], &c__1, &workd[1], &c__1);
+	*rnorm = sqrt((abs(*rnorm)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	*rnorm = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+
+/*     %--------------------------------------%   
+       | Check for further orthogonalization. |   
+       %--------------------------------------% */
+
+    if (msglvl > 2) {
+	igraphdvout_(&logfil, &c__1, &rnorm0, &ndigit, "_getv0: re-orthonalization"
+		" ; rnorm0 is", (ftnlen)38);
+	igraphdvout_(&logfil, &c__1, rnorm, &ndigit, "_getv0: re-orthonalization ;"
+		" rnorm is", (ftnlen)37);
+    }
+
+    if (*rnorm > rnorm0 * .717f) {
+	goto L50;
+    }
+
+    ++iter;
+    if (iter <= 1) {
+
+/*        %-----------------------------------%   
+          | Perform iterative refinement step |   
+          %-----------------------------------% */
+
+	rnorm0 = *rnorm;
+	goto L30;
+    } else {
+
+/*        %------------------------------------%   
+          | Iterative refinement step "failed" |   
+          %------------------------------------% */
+
+	i__1 = *n;
+	for (jj = 1; jj <= i__1; ++jj) {
+	    resid[jj] = 0.;
+/* L45: */
+	}
+	*rnorm = 0.;
+	*ierr = -1;
+    }
+
+L50:
+
+    if (msglvl > 0) {
+	igraphdvout_(&logfil, &c__1, rnorm, &ndigit, "_getv0: B-norm of initial / "
+		"restarted starting vector", (ftnlen)53);
+    }
+    if (msglvl > 2) {
+	igraphdvout_(&logfil, n, &resid[1], &ndigit, "_getv0: initial / restarted "
+		"starting vector", (ftnlen)43);
+    }
+    *ido = 99;
+
+    igraphsecond_(&t1);
+    tgetv0 += t1 - t0;
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dgetv0 |   
+       %---------------% */
+
+} /* igraphdgetv0_ */
+
diff --git a/igraph/src/dhseqr.c b/igraph/src/dhseqr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dhseqr.c
@@ -0,0 +1,574 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b11 = 0.;
+static doublereal c_b12 = 1.;
+static integer c__12 = 12;
+static integer c__2 = 2;
+static integer c__49 = 49;
+
+/* > \brief \b DHSEQR   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DHSEQR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dhseqr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dhseqr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dhseqr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DHSEQR( JOB, COMPZ, N, ILO, IHI, H, LDH, WR, WI, Z,   
+                            LDZ, WORK, LWORK, INFO )   
+
+         INTEGER            IHI, ILO, INFO, LDH, LDZ, LWORK, N   
+         CHARACTER          COMPZ, JOB   
+         DOUBLE PRECISION   H( LDH, * ), WI( * ), WORK( * ), WR( * ),   
+        $                   Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >    DHSEQR computes the eigenvalues of a Hessenberg matrix H   
+   >    and, optionally, the matrices T and Z from the Schur decomposition   
+   >    H = Z T Z**T, where T is an upper quasi-triangular matrix (the   
+   >    Schur form), and Z is the orthogonal matrix of Schur vectors.   
+   >   
+   >    Optionally Z may be postmultiplied into an input orthogonal   
+   >    matrix Q so that this routine can give the Schur factorization   
+   >    of a matrix A which has been reduced to the Hessenberg form H   
+   >    by the orthogonal matrix Q:  A = Q*H*Q**T = (QZ)*T*(QZ)**T.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOB   
+   > \verbatim   
+   >          JOB is CHARACTER*1   
+   >           = 'E':  compute eigenvalues only;   
+   >           = 'S':  compute eigenvalues and the Schur form T.   
+   > \endverbatim   
+   >   
+   > \param[in] COMPZ   
+   > \verbatim   
+   >          COMPZ is CHARACTER*1   
+   >           = 'N':  no Schur vectors are computed;   
+   >           = 'I':  Z is initialized to the unit matrix and the matrix Z   
+   >                   of Schur vectors of H is returned;   
+   >           = 'V':  Z must contain an orthogonal matrix Q on entry, and   
+   >                   the product Q*Z is returned.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >           The order of the matrix H.  N .GE. 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >   
+   >           It is assumed that H is already upper triangular in rows   
+   >           and columns 1:ILO-1 and IHI+1:N. ILO and IHI are normally   
+   >           set by a previous call to DGEBAL, and then passed to ZGEHRD   
+   >           when the matrix output by DGEBAL is reduced to Hessenberg   
+   >           form. Otherwise ILO and IHI should be set to 1 and N   
+   >           respectively.  If N.GT.0, then 1.LE.ILO.LE.IHI.LE.N.   
+   >           If N = 0, then ILO = 1 and IHI = 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] H   
+   > \verbatim   
+   >          H is DOUBLE PRECISION array, dimension (LDH,N)   
+   >           On entry, the upper Hessenberg matrix H.   
+   >           On exit, if INFO = 0 and JOB = 'S', then H contains the   
+   >           upper quasi-triangular matrix T from the Schur decomposition   
+   >           (the Schur form); 2-by-2 diagonal blocks (corresponding to   
+   >           complex conjugate pairs of eigenvalues) are returned in   
+   >           standard form, with H(i,i) = H(i+1,i+1) and   
+   >           H(i+1,i)*H(i,i+1).LT.0. If INFO = 0 and JOB = 'E', the   
+   >           contents of H are unspecified on exit.  (The output value of   
+   >           H when INFO.GT.0 is given under the description of INFO   
+   >           below.)   
+   >   
+   >           Unlike earlier versions of DHSEQR, this subroutine may   
+   >           explicitly H(i,j) = 0 for i.GT.j and j = 1, 2, ... ILO-1   
+   >           or j = IHI+1, IHI+2, ... N.   
+   > \endverbatim   
+   >   
+   > \param[in] LDH   
+   > \verbatim   
+   >          LDH is INTEGER   
+   >           The leading dimension of the array H. LDH .GE. max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] WR   
+   > \verbatim   
+   >          WR is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] WI   
+   > \verbatim   
+   >          WI is DOUBLE PRECISION array, dimension (N)   
+   >   
+   >           The real and imaginary parts, respectively, of the computed   
+   >           eigenvalues. If two eigenvalues are computed as a complex   
+   >           conjugate pair, they are stored in consecutive elements of   
+   >           WR and WI, say the i-th and (i+1)th, with WI(i) .GT. 0 and   
+   >           WI(i+1) .LT. 0. If JOB = 'S', the eigenvalues are stored in   
+   >           the same order as on the diagonal of the Schur form returned   
+   >           in H, with WR(i) = H(i,i) and, if H(i:i+1,i:i+1) is a 2-by-2   
+   >           diagonal block, WI(i) = sqrt(-H(i+1,i)*H(i,i+1)) and   
+   >           WI(i+1) = -WI(i).   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ,N)   
+   >           If COMPZ = 'N', Z is not referenced.   
+   >           If COMPZ = 'I', on entry Z need not be set and on exit,   
+   >           if INFO = 0, Z contains the orthogonal matrix Z of the Schur   
+   >           vectors of H.  If COMPZ = 'V', on entry Z must contain an   
+   >           N-by-N matrix Q, which is assumed to be equal to the unit   
+   >           matrix except for the submatrix Z(ILO:IHI,ILO:IHI). On exit,   
+   >           if INFO = 0, Z contains Q*Z.   
+   >           Normally Q is the orthogonal matrix generated by DORGHR   
+   >           after the call to DGEHRD which formed the Hessenberg matrix   
+   >           H. (The output value of Z when INFO.GT.0 is given under   
+   >           the description of INFO below.)   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >           The leading dimension of the array Z.  if COMPZ = 'I' or   
+   >           COMPZ = 'V', then LDZ.GE.MAX(1,N).  Otherwize, LDZ.GE.1.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (LWORK)   
+   >           On exit, if INFO = 0, WORK(1) returns an estimate of   
+   >           the optimal value for LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >           The dimension of the array WORK.  LWORK .GE. max(1,N)   
+   >           is sufficient and delivers very good and sometimes   
+   >           optimal performance.  However, LWORK as large as 11*N   
+   >           may be required for optimal performance.  A workspace   
+   >           query is recommended to determine the optimal workspace   
+   >           size.   
+   >   
+   >           If LWORK = -1, then DHSEQR does a workspace query.   
+   >           In this case, DHSEQR checks the input parameters and   
+   >           estimates the optimal workspace size for the given   
+   >           values of N, ILO and IHI.  The estimate is returned   
+   >           in WORK(1).  No error message related to LWORK is   
+   >           issued by XERBLA.  Neither H nor Z are accessed.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >             =  0:  successful exit   
+   >           .LT. 0:  if INFO = -i, the i-th argument had an illegal   
+   >                    value   
+   >           .GT. 0:  if INFO = i, DHSEQR failed to compute all of   
+   >                the eigenvalues.  Elements 1:ilo-1 and i+1:n of WR   
+   >                and WI contain those eigenvalues which have been   
+   >                successfully computed.  (Failures are rare.)   
+   >   
+   >                If INFO .GT. 0 and JOB = 'E', then on exit, the   
+   >                remaining unconverged eigenvalues are the eigen-   
+   >                values of the upper Hessenberg matrix rows and   
+   >                columns ILO through INFO of the final, output   
+   >                value of H.   
+   >   
+   >                If INFO .GT. 0 and JOB   = 'S', then on exit   
+   >   
+   >           (*)  (initial value of H)*U  = U*(final value of H)   
+   >   
+   >                where U is an orthogonal matrix.  The final   
+   >                value of H is upper Hessenberg and quasi-triangular   
+   >                in rows and columns INFO+1 through IHI.   
+   >   
+   >                If INFO .GT. 0 and COMPZ = 'V', then on exit   
+   >   
+   >                  (final value of Z)  =  (initial value of Z)*U   
+   >   
+   >                where U is the orthogonal matrix in (*) (regard-   
+   >                less of the value of JOB.)   
+   >   
+   >                If INFO .GT. 0 and COMPZ = 'I', then on exit   
+   >                      (final value of Z)  = U   
+   >                where U is the orthogonal matrix in (*) (regard-   
+   >                less of the value of JOB.)   
+   >   
+   >                If INFO .GT. 0 and COMPZ = 'N', then Z is not   
+   >                accessed.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >       Karen Braman and Ralph Byers, Department of Mathematics,   
+   >       University of Kansas, USA   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >             Default values supplied by   
+   >             ILAENV(ISPEC,'DHSEQR',JOB(:1)//COMPZ(:1),N,ILO,IHI,LWORK).   
+   >             It is suggested that these defaults be adjusted in order   
+   >             to attain best performance in each particular   
+   >             computational environment.   
+   >   
+   >            ISPEC=12: The DLAHQR vs DLAQR0 crossover point.   
+   >                      Default: 75. (Must be at least 11.)   
+   >   
+   >            ISPEC=13: Recommended deflation window size.   
+   >                      This depends on ILO, IHI and NS.  NS is the   
+   >                      number of simultaneous shifts returned   
+   >                      by ILAENV(ISPEC=15).  (See ISPEC=15 below.)   
+   >                      The default for (IHI-ILO+1).LE.500 is NS.   
+   >                      The default for (IHI-ILO+1).GT.500 is 3*NS/2.   
+   >   
+   >            ISPEC=14: Nibble crossover point. (See IPARMQ for   
+   >                      details.)  Default: 14% of deflation window   
+   >                      size.   
+   >   
+   >            ISPEC=15: Number of simultaneous shifts in a multishift   
+   >                      QR iteration.   
+   >   
+   >                      If IHI-ILO+1 is ...   
+   >   
+   >                      greater than      ...but less    ... the   
+   >                      or equal to ...      than        default is   
+   >   
+   >                           1               30          NS =   2(+)   
+   >                          30               60          NS =   4(+)   
+   >                          60              150          NS =  10(+)   
+   >                         150              590          NS =  **   
+   >                         590             3000          NS =  64   
+   >                        3000             6000          NS = 128   
+   >                        6000             infinity      NS = 256   
+   >   
+   >                  (+)  By default some or all matrices of this order   
+   >                       are passed to the implicit double shift routine   
+   >                       DLAHQR and this parameter is ignored.  See   
+   >                       ISPEC=12 above and comments in IPARMQ for   
+   >                       details.   
+   >   
+   >                 (**)  The asterisks (**) indicate an ad-hoc   
+   >                       function of N increasing from 10 to 64.   
+   >   
+   >            ISPEC=16: Select structured matrix multiply.   
+   >                      If the number of simultaneous shifts (specified   
+   >                      by ISPEC=15) is less than 14, then the default   
+   >                      for ISPEC=16 is 0.  Otherwise the default for   
+   >                      ISPEC=16 is 2.   
+   > \endverbatim   
+
+   > \par References:   
+    ================   
+   >   
+   >       K. Braman, R. Byers and R. Mathias, The Multi-Shift QR   
+   >       Algorithm Part I: Maintaining Well Focused Shifts, and Level 3   
+   >       Performance, SIAM Journal of Matrix Analysis, volume 23, pages   
+   >       929--947, 2002.   
+   > \n   
+   >       K. Braman, R. Byers and R. Mathias, The Multi-Shift QR   
+   >       Algorithm Part II: Aggressive Early Deflation, SIAM Journal   
+   >       of Matrix Analysis, volume 23, pages 948--973, 2002.   
+
+    =====================================================================   
+   Subroutine */ int igraphdhseqr_(char *job, char *compz, integer *n, integer *ilo,
+	 integer *ihi, doublereal *h__, integer *ldh, doublereal *wr, 
+	doublereal *wi, doublereal *z__, integer *ldz, doublereal *work, 
+	integer *lwork, integer *info)
+{
+    /* System generated locals */
+    address a__1[2];
+    integer h_dim1, h_offset, z_dim1, z_offset, i__1, i__2[2], i__3;
+    doublereal d__1;
+    char ch__1[2];
+
+    /* Builtin functions   
+       Subroutine */ int s_cat(char *, char **, integer *, integer *, ftnlen);
+
+    /* Local variables */
+    integer i__;
+    doublereal hl[2401]	/* was [49][49] */;
+    integer kbot, nmin;
+    extern logical igraphlsame_(char *, char *);
+    logical initz;
+    doublereal workl[49];
+    logical wantt, wantz;
+    extern /* Subroutine */ int igraphdlaqr0_(logical *, logical *, integer *, 
+	    integer *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, integer *, integer *), igraphdlahqr_(logical *, logical *,
+	     integer *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *), igraphdlacpy_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), 
+	    igraphdlaset_(char *, integer *, integer *, doublereal *, doublereal *, 
+	    doublereal *, integer *);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       ==== Matrices of order NTINY or smaller must be processed by   
+       .    DLAHQR because of insufficient subdiagonal scratch space.   
+       .    (This is a hard limit.) ====   
+
+       ==== NL allocates some local workspace to help small matrices   
+       .    through a rare DLAHQR failure.  NL .GT. NTINY = 11 is   
+       .    required and NL .LE. NMIN = ILAENV(ISPEC=12,...) is recom-   
+       .    mended.  (The default value of NMIN is 75.)  Using NL = 49   
+       .    allows up to six simultaneous shifts and a 16-by-16   
+       .    deflation window.  ====   
+
+       ==== Decode and check the input parameters. ====   
+
+       Parameter adjustments */
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    --wr;
+    --wi;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --work;
+
+    /* Function Body */
+    wantt = igraphlsame_(job, "S");
+    initz = igraphlsame_(compz, "I");
+    wantz = initz || igraphlsame_(compz, "V");
+    work[1] = (doublereal) max(1,*n);
+    lquery = *lwork == -1;
+
+    *info = 0;
+    if (! igraphlsame_(job, "E") && ! wantt) {
+	*info = -1;
+    } else if (! igraphlsame_(compz, "N") && ! wantz) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -3;
+    } else if (*ilo < 1 || *ilo > max(1,*n)) {
+	*info = -4;
+    } else if (*ihi < min(*ilo,*n) || *ihi > *n) {
+	*info = -5;
+    } else if (*ldh < max(1,*n)) {
+	*info = -7;
+    } else if (*ldz < 1 || wantz && *ldz < max(1,*n)) {
+	*info = -11;
+    } else if (*lwork < max(1,*n) && ! lquery) {
+	*info = -13;
+    }
+
+    if (*info != 0) {
+
+/*        ==== Quick return in case of invalid argument. ==== */
+
+	i__1 = -(*info);
+	igraphxerbla_("DHSEQR", &i__1, (ftnlen)6);
+	return 0;
+
+    } else if (*n == 0) {
+
+/*        ==== Quick return in case N = 0; nothing to do. ==== */
+
+	return 0;
+
+    } else if (lquery) {
+
+/*        ==== Quick return in case of a workspace query ==== */
+
+	igraphdlaqr0_(&wantt, &wantz, n, ilo, ihi, &h__[h_offset], ldh, &wr[1], &wi[
+		1], ilo, ihi, &z__[z_offset], ldz, &work[1], lwork, info);
+/*        ==== Ensure reported workspace size is backward-compatible with   
+          .    previous LAPACK versions. ====   
+   Computing MAX */
+	d__1 = (doublereal) max(1,*n);
+	work[1] = max(d__1,work[1]);
+	return 0;
+
+    } else {
+
+/*        ==== copy eigenvalues isolated by DGEBAL ==== */
+
+	i__1 = *ilo - 1;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    wr[i__] = h__[i__ + i__ * h_dim1];
+	    wi[i__] = 0.;
+/* L10: */
+	}
+	i__1 = *n;
+	for (i__ = *ihi + 1; i__ <= i__1; ++i__) {
+	    wr[i__] = h__[i__ + i__ * h_dim1];
+	    wi[i__] = 0.;
+/* L20: */
+	}
+
+/*        ==== Initialize Z, if requested ==== */
+
+	if (initz) {
+	    igraphdlaset_("A", n, n, &c_b11, &c_b12, &z__[z_offset], ldz)
+		    ;
+	}
+
+/*        ==== Quick return if possible ==== */
+
+	if (*ilo == *ihi) {
+	    wr[*ilo] = h__[*ilo + *ilo * h_dim1];
+	    wi[*ilo] = 0.;
+	    return 0;
+	}
+
+/*        ==== DLAHQR/DLAQR0 crossover point ====   
+
+   Writing concatenation */
+	i__2[0] = 1, a__1[0] = job;
+	i__2[1] = 1, a__1[1] = compz;
+	s_cat(ch__1, a__1, i__2, &c__2, (ftnlen)2);
+	nmin = igraphilaenv_(&c__12, "DHSEQR", ch__1, n, ilo, ihi, lwork, (ftnlen)6,
+		 (ftnlen)2);
+	nmin = max(11,nmin);
+
+/*        ==== DLAQR0 for big matrices; DLAHQR for small ones ==== */
+
+	if (*n > nmin) {
+	    igraphdlaqr0_(&wantt, &wantz, n, ilo, ihi, &h__[h_offset], ldh, &wr[1], 
+		    &wi[1], ilo, ihi, &z__[z_offset], ldz, &work[1], lwork, 
+		    info);
+	} else {
+
+/*           ==== Small matrix ==== */
+
+	    igraphdlahqr_(&wantt, &wantz, n, ilo, ihi, &h__[h_offset], ldh, &wr[1], 
+		    &wi[1], ilo, ihi, &z__[z_offset], ldz, info);
+
+	    if (*info > 0) {
+
+/*              ==== A rare DLAHQR failure!  DLAQR0 sometimes succeeds   
+                .    when DLAHQR fails. ==== */
+
+		kbot = *info;
+
+		if (*n >= 49) {
+
+/*                 ==== Larger matrices have enough subdiagonal scratch   
+                   .    space to call DLAQR0 directly. ==== */
+
+		    igraphdlaqr0_(&wantt, &wantz, n, ilo, &kbot, &h__[h_offset], 
+			    ldh, &wr[1], &wi[1], ilo, ihi, &z__[z_offset], 
+			    ldz, &work[1], lwork, info);
+
+		} else {
+
+/*                 ==== Tiny matrices don't have enough subdiagonal   
+                   .    scratch space to benefit from DLAQR0.  Hence,   
+                   .    tiny matrices must be copied into a larger   
+                   .    array before calling DLAQR0. ==== */
+
+		    igraphdlacpy_("A", n, n, &h__[h_offset], ldh, hl, &c__49);
+		    hl[*n + 1 + *n * 49 - 50] = 0.;
+		    i__1 = 49 - *n;
+		    igraphdlaset_("A", &c__49, &i__1, &c_b11, &c_b11, &hl[(*n + 1) *
+			     49 - 49], &c__49);
+		    igraphdlaqr0_(&wantt, &wantz, &c__49, ilo, &kbot, hl, &c__49, &
+			    wr[1], &wi[1], ilo, ihi, &z__[z_offset], ldz, 
+			    workl, &c__49, info);
+		    if (wantt || *info != 0) {
+			igraphdlacpy_("A", n, n, hl, &c__49, &h__[h_offset], ldh);
+		    }
+		}
+	    }
+	}
+
+/*        ==== Clear out the trash, if necessary. ==== */
+
+	if ((wantt || *info != 0) && *n > 2) {
+	    i__1 = *n - 2;
+	    i__3 = *n - 2;
+	    igraphdlaset_("L", &i__1, &i__3, &c_b11, &c_b11, &h__[h_dim1 + 3], ldh);
+	}
+
+/*        ==== Ensure reported workspace size is backward-compatible with   
+          .    previous LAPACK versions. ====   
+
+   Computing MAX */
+	d__1 = (doublereal) max(1,*n);
+	work[1] = max(d__1,work[1]);
+    }
+
+/*     ==== End of DHSEQR ==== */
+
+    return 0;
+} /* igraphdhseqr_ */
+
diff --git a/igraph/src/disnan.c b/igraph/src/disnan.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/disnan.c
@@ -0,0 +1,95 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DISNAN tests input for NaN.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DISNAN + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/disnan.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/disnan.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/disnan.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         LOGICAL FUNCTION DISNAN( DIN )   
+
+         DOUBLE PRECISION   DIN   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DISNAN returns .TRUE. if its argument is NaN, and .FALSE.   
+   > otherwise.  To be replaced by the Fortran 2003 intrinsic in the   
+   > future.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] DIN   
+   > \verbatim   
+   >          DIN is DOUBLE PRECISION   
+   >          Input to test for NaN.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    ===================================================================== */
+logical igraphdisnan_(doublereal *din)
+{
+    /* System generated locals */
+    logical ret_val;
+
+    /* Local variables */
+    extern logical igraphdlaisnan_(doublereal *, doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ===================================================================== */
+
+    ret_val = igraphdlaisnan_(din, din);
+    return ret_val;
+} /* igraphdisnan_ */
+
diff --git a/igraph/src/distances.c b/igraph/src/distances.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/distances.c
@@ -0,0 +1,211 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_datatype.h"
+#include "igraph_dqueue.h"
+#include "igraph_iterators.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_vector.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+
+int igraph_i_eccentricity(const igraph_t *graph,
+                          igraph_vector_t *res,
+                          igraph_vs_t vids,
+                          igraph_neimode_t mode,
+                          const igraph_adjlist_t *adjlist) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    igraph_dqueue_long_t q;
+    igraph_vit_t vit;
+    igraph_vector_int_t counted;
+    int i, mark = 1;
+    igraph_vector_t vneis;
+    igraph_vector_int_t *neis;
+
+    IGRAPH_CHECK(igraph_dqueue_long_init(&q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q);
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&counted, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &counted);
+
+    if (!adjlist) {
+        IGRAPH_VECTOR_INIT_FINALLY(&vneis, 0);
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit)));
+    igraph_vector_fill(res, -1);
+
+    for (i = 0, IGRAPH_VIT_RESET(vit);
+         !IGRAPH_VIT_END(vit);
+         IGRAPH_VIT_NEXT(vit), mark++, i++) {
+
+        long int source;
+        source = IGRAPH_VIT_GET(vit);
+        IGRAPH_CHECK(igraph_dqueue_long_push(&q, source));
+        IGRAPH_CHECK(igraph_dqueue_long_push(&q, 0));
+        VECTOR(counted)[source] = mark;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        while (!igraph_dqueue_long_empty(&q)) {
+            long int act = igraph_dqueue_long_pop(&q);
+            long int dist = igraph_dqueue_long_pop(&q);
+            int j, n;
+
+            if (dist > VECTOR(*res)[i]) {
+                VECTOR(*res)[i] = dist;
+            }
+
+            if (adjlist) {
+                neis = igraph_adjlist_get(adjlist, act);
+                n = (int) igraph_vector_int_size(neis);
+                for (j = 0; j < n; j++) {
+                    int nei = (int) VECTOR(*neis)[j];
+                    if (VECTOR(counted)[nei] != mark) {
+                        VECTOR(counted)[nei] = mark;
+                        IGRAPH_CHECK(igraph_dqueue_long_push(&q, nei));
+                        IGRAPH_CHECK(igraph_dqueue_long_push(&q, dist + 1));
+                    }
+                }
+            } else {
+                IGRAPH_CHECK(igraph_neighbors(graph, &vneis,
+                                              (igraph_integer_t) act, mode));
+                n = (int) igraph_vector_size(&vneis);
+                for (j = 0; j < n; j++) {
+                    int nei = (int) VECTOR(vneis)[j];
+                    if (VECTOR(counted)[nei] != mark) {
+                        VECTOR(counted)[nei] = mark;
+                        IGRAPH_CHECK(igraph_dqueue_long_push(&q, nei));
+                        IGRAPH_CHECK(igraph_dqueue_long_push(&q, dist + 1));
+                    }
+                }
+            }
+        } /* while !igraph_dqueue_long_empty(dqueue) */
+
+    } /* for IGRAPH_VIT_NEXT(vit) */
+
+    if (!adjlist) {
+        igraph_vector_destroy(&vneis);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    igraph_vector_int_destroy(&counted);
+    igraph_vit_destroy(&vit);
+    igraph_dqueue_long_destroy(&q);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_eccentricity
+ * Eccentricity of some vertices
+ *
+ * The eccentricity of a vertex is calculated by measuring the shortest
+ * distance from (or to) the vertex, to (or from) all vertices in the
+ * graph, and taking the maximum.
+ *
+ * </para><para>
+ * This implementation ignores vertex pairs that are in different
+ * components. Isolated vertices have eccentricity zero.
+ *
+ * \param graph The input graph, it can be directed or undirected.
+ * \param res Pointer to an initialized vector, the result is stored
+ *    here.
+ * \param vids The vertices for which the eccentricity is calculated.
+ * \param mode What kind of paths to consider for the calculation:
+ *    \c IGRAPH_OUT, paths that follow edge directions;
+ *    \c IGRAPH_IN, paths that follow the opposite directions; and
+ *    \c IGRAPH_ALL, paths that ignore edge directions. This argument
+ *    is ignored for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(v*(|V|+|E|)), where |V| is the number of
+ * vertices, |E| is the number of edges and v is the number of
+ * vertices for which eccentricity is calculated.
+ *
+ * \sa \ref igraph_radius().
+ *
+ * \example examples/simple/igraph_eccentricity.c
+ */
+
+int igraph_eccentricity(const igraph_t *graph,
+                        igraph_vector_t *res,
+                        igraph_vs_t vids,
+                        igraph_neimode_t mode) {
+
+    return igraph_i_eccentricity(graph, res, vids, mode, /*adjlist=*/ 0);
+}
+
+/**
+ * \function igraph_radius
+ * Radius of a graph
+ *
+ * The radius of a graph is the defined as the minimum eccentricity of
+ * its vertices, see \ref igraph_eccentricity().
+ *
+ * \param graph The input graph, it can be directed or undirected.
+ * \param radius Pointer to a real variable, the result is stored
+ *   here.
+ * \param mode What kind of paths to consider for the calculation:
+ *    \c IGRAPH_OUT, paths that follow edge directions;
+ *    \c IGRAPH_IN, paths that follow the opposite directions; and
+ *    \c IGRAPH_ALL, paths that ignore edge directions. This argument
+ *    is ignored for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|(|V|+|E|)), where |V| is the number of
+ * vertices and |E| is the number of edges.
+ *
+ * \sa \ref igraph_eccentricity().
+ *
+ * \example examples/simple/igraph_radius.c
+ */
+
+int igraph_radius(const igraph_t *graph, igraph_real_t *radius,
+                  igraph_neimode_t mode) {
+
+    int no_of_nodes = igraph_vcount(graph);
+
+    if (no_of_nodes == 0) {
+        *radius = IGRAPH_NAN;
+    } else {
+        igraph_adjlist_t adjlist;
+        igraph_vector_t ecc;
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+        IGRAPH_VECTOR_INIT_FINALLY(&ecc, igraph_vcount(graph));
+        IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc, igraph_vss_all(),
+                                           mode, &adjlist));
+        *radius = igraph_vector_min(&ecc);
+        igraph_vector_destroy(&ecc);
+        igraph_adjlist_destroy(&adjlist);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/dlabad.c b/igraph/src/dlabad.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlabad.c
@@ -0,0 +1,118 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLABAD   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLABAD + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlabad.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlabad.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlabad.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLABAD( SMALL, LARGE )   
+
+         DOUBLE PRECISION   LARGE, SMALL   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLABAD takes as input the values computed by DLAMCH for underflow and   
+   > overflow, and returns the square root of each of these values if the   
+   > log of LARGE is sufficiently large.  This subroutine is intended to   
+   > identify machines with a large exponent range, such as the Crays, and   
+   > redefine the underflow and overflow limits to be the square roots of   
+   > the values computed by DLAMCH.  This subroutine is needed because   
+   > DLAMCH does not compensate for poor arithmetic in the upper half of   
+   > the exponent range, as is found on a Cray.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in,out] SMALL   
+   > \verbatim   
+   >          SMALL is DOUBLE PRECISION   
+   >          On entry, the underflow threshold as computed by DLAMCH.   
+   >          On exit, if LOG10(LARGE) is sufficiently large, the square   
+   >          root of SMALL, otherwise unchanged.   
+   > \endverbatim   
+   >   
+   > \param[in,out] LARGE   
+   > \verbatim   
+   >          LARGE is DOUBLE PRECISION   
+   >          On entry, the overflow threshold as computed by DLAMCH.   
+   >          On exit, if LOG10(LARGE) is sufficiently large, the square   
+   >          root of LARGE, otherwise unchanged.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlabad_(doublereal *small, doublereal *large)
+{
+    /* Builtin functions */
+    double d_lg10(doublereal *), sqrt(doublereal);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       If it looks like we're on a Cray, take the square root of   
+       SMALL and LARGE to avoid overflow and underflow problems. */
+
+    if (d_lg10(large) > 2e3) {
+	*small = sqrt(*small);
+	*large = sqrt(*large);
+    }
+
+    return 0;
+
+/*     End of DLABAD */
+
+} /* igraphdlabad_ */
+
diff --git a/igraph/src/dlacn2.c b/igraph/src/dlacn2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlacn2.c
@@ -0,0 +1,332 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b11 = 1.;
+
+/* > \brief \b DLACN2 estimates the 1-norm of a square matrix, using reverse communication for evaluating matr
+ix-vector products.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLACN2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlacn2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlacn2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlacn2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLACN2( N, V, X, ISGN, EST, KASE, ISAVE )   
+
+         INTEGER            KASE, N   
+         DOUBLE PRECISION   EST   
+         INTEGER            ISGN( * ), ISAVE( 3 )   
+         DOUBLE PRECISION   V( * ), X( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLACN2 estimates the 1-norm of a square, real matrix A.   
+   > Reverse communication is used for evaluating matrix-vector products.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >         The order of the matrix.  N >= 1.   
+   > \endverbatim   
+   >   
+   > \param[out] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array, dimension (N)   
+   >         On the final return, V = A*W,  where  EST = norm(V)/norm(W)   
+   >         (W is not returned).   
+   > \endverbatim   
+   >   
+   > \param[in,out] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION array, dimension (N)   
+   >         On an intermediate return, X should be overwritten by   
+   >               A * X,   if KASE=1,   
+   >               A**T * X,  if KASE=2,   
+   >         and DLACN2 must be re-called with all the other parameters   
+   >         unchanged.   
+   > \endverbatim   
+   >   
+   > \param[out] ISGN   
+   > \verbatim   
+   >          ISGN is INTEGER array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[in,out] EST   
+   > \verbatim   
+   >          EST is DOUBLE PRECISION   
+   >         On entry with KASE = 1 or 2 and ISAVE(1) = 3, EST should be   
+   >         unchanged from the previous call to DLACN2.   
+   >         On exit, EST is an estimate (a lower bound) for norm(A).   
+   > \endverbatim   
+   >   
+   > \param[in,out] KASE   
+   > \verbatim   
+   >          KASE is INTEGER   
+   >         On the initial call to DLACN2, KASE should be 0.   
+   >         On an intermediate return, KASE will be 1 or 2, indicating   
+   >         whether X should be overwritten by A * X  or A**T * X.   
+   >         On the final return from DLACN2, KASE will again be 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] ISAVE   
+   > \verbatim   
+   >          ISAVE is INTEGER array, dimension (3)   
+   >         ISAVE is used to save variables between calls to DLACN2   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  Originally named SONEST, dated March 16, 1988.   
+   >   
+   >  This is a thread safe version of DLACON, which uses the array ISAVE   
+   >  in place of a SAVE statement, as follows:   
+   >   
+   >     DLACON     DLACN2   
+   >      JUMP     ISAVE(1)   
+   >      J        ISAVE(2)   
+   >      ITER     ISAVE(3)   
+   > \endverbatim   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >     Nick Higham, University of Manchester   
+
+   > \par References:   
+    ================   
+   >   
+   >  N.J. Higham, "FORTRAN codes for estimating the one-norm of   
+   >  a real or complex matrix, with applications to condition estimation",   
+   >  ACM Trans. Math. Soft., vol. 14, no. 4, pp. 381-396, December 1988.   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlacn2_(integer *n, doublereal *v, doublereal *x, 
+	integer *isgn, doublereal *est, integer *kase, integer *isave)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1;
+
+    /* Builtin functions */
+    double d_sign(doublereal *, doublereal *);
+    integer i_dnnt(doublereal *);
+
+    /* Local variables */
+    integer i__;
+    doublereal temp;
+    extern doublereal igraphdasum_(integer *, doublereal *, integer *);
+    integer jlast;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    doublereal altsgn, estold;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --isave;
+    --isgn;
+    --x;
+    --v;
+
+    /* Function Body */
+    if (*kase == 0) {
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    x[i__] = 1. / (doublereal) (*n);
+/* L10: */
+	}
+	*kase = 1;
+	isave[1] = 1;
+	return 0;
+    }
+
+    switch (isave[1]) {
+	case 1:  goto L20;
+	case 2:  goto L40;
+	case 3:  goto L70;
+	case 4:  goto L110;
+	case 5:  goto L140;
+    }
+
+/*     ................ ENTRY   (ISAVE( 1 ) = 1)   
+       FIRST ITERATION.  X HAS BEEN OVERWRITTEN BY A*X. */
+
+L20:
+    if (*n == 1) {
+	v[1] = x[1];
+	*est = abs(v[1]);
+/*        ... QUIT */
+	goto L150;
+    }
+    *est = igraphdasum_(n, &x[1], &c__1);
+
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	x[i__] = d_sign(&c_b11, &x[i__]);
+	isgn[i__] = i_dnnt(&x[i__]);
+/* L30: */
+    }
+    *kase = 2;
+    isave[1] = 2;
+    return 0;
+
+/*     ................ ENTRY   (ISAVE( 1 ) = 2)   
+       FIRST ITERATION.  X HAS BEEN OVERWRITTEN BY TRANSPOSE(A)*X. */
+
+L40:
+    isave[2] = igraphidamax_(n, &x[1], &c__1);
+    isave[3] = 2;
+
+/*     MAIN LOOP - ITERATIONS 2,3,...,ITMAX. */
+
+L50:
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	x[i__] = 0.;
+/* L60: */
+    }
+    x[isave[2]] = 1.;
+    *kase = 1;
+    isave[1] = 3;
+    return 0;
+
+/*     ................ ENTRY   (ISAVE( 1 ) = 3)   
+       X HAS BEEN OVERWRITTEN BY A*X. */
+
+L70:
+    igraphdcopy_(n, &x[1], &c__1, &v[1], &c__1);
+    estold = *est;
+    *est = igraphdasum_(n, &v[1], &c__1);
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	d__1 = d_sign(&c_b11, &x[i__]);
+	if (i_dnnt(&d__1) != isgn[i__]) {
+	    goto L90;
+	}
+/* L80: */
+    }
+/*     REPEATED SIGN VECTOR DETECTED, HENCE ALGORITHM HAS CONVERGED. */
+    goto L120;
+
+L90:
+/*     TEST FOR CYCLING. */
+    if (*est <= estold) {
+	goto L120;
+    }
+
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	x[i__] = d_sign(&c_b11, &x[i__]);
+	isgn[i__] = i_dnnt(&x[i__]);
+/* L100: */
+    }
+    *kase = 2;
+    isave[1] = 4;
+    return 0;
+
+/*     ................ ENTRY   (ISAVE( 1 ) = 4)   
+       X HAS BEEN OVERWRITTEN BY TRANSPOSE(A)*X. */
+
+L110:
+    jlast = isave[2];
+    isave[2] = igraphidamax_(n, &x[1], &c__1);
+    if (x[jlast] != (d__1 = x[isave[2]], abs(d__1)) && isave[3] < 5) {
+	++isave[3];
+	goto L50;
+    }
+
+/*     ITERATION COMPLETE.  FINAL STAGE. */
+
+L120:
+    altsgn = 1.;
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	x[i__] = altsgn * ((doublereal) (i__ - 1) / (doublereal) (*n - 1) + 
+		1.);
+	altsgn = -altsgn;
+/* L130: */
+    }
+    *kase = 1;
+    isave[1] = 5;
+    return 0;
+
+/*     ................ ENTRY   (ISAVE( 1 ) = 5)   
+       X HAS BEEN OVERWRITTEN BY A*X. */
+
+L140:
+    temp = igraphdasum_(n, &x[1], &c__1) / (doublereal) (*n * 3) * 2.;
+    if (temp > *est) {
+	igraphdcopy_(n, &x[1], &c__1, &v[1], &c__1);
+	*est = temp;
+    }
+
+L150:
+    *kase = 0;
+    return 0;
+
+/*     End of DLACN2 */
+
+} /* igraphdlacn2_ */
+
diff --git a/igraph/src/dlacpy.c b/igraph/src/dlacpy.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlacpy.c
@@ -0,0 +1,182 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLACPY copies all or part of one two-dimensional array to another.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLACPY + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlacpy.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlacpy.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlacpy.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLACPY( UPLO, M, N, A, LDA, B, LDB )   
+
+         CHARACTER          UPLO   
+         INTEGER            LDA, LDB, M, N   
+         DOUBLE PRECISION   A( LDA, * ), B( LDB, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLACPY copies all or part of a two-dimensional matrix A to another   
+   > matrix B.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          Specifies the part of the matrix A to be copied to B.   
+   >          = 'U':      Upper triangular part   
+   >          = 'L':      Lower triangular part   
+   >          Otherwise:  All of the matrix A   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.  M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The m by n matrix A.  If UPLO = 'U', only the upper triangle   
+   >          or trapezoid is accessed; if UPLO = 'L', only the lower   
+   >          triangle or trapezoid is accessed.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (LDB,N)   
+   >          On exit, B = A in the locations specified by UPLO.   
+   > \endverbatim   
+   >   
+   > \param[in] LDB   
+   > \verbatim   
+   >          LDB is INTEGER   
+   >          The leading dimension of the array B.  LDB >= max(1,M).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlacpy_(char *uplo, integer *m, integer *n, doublereal *
+	a, integer *lda, doublereal *b, integer *ldb)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j;
+    extern logical igraphlsame_(char *, char *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+
+    /* Function Body */
+    if (igraphlsame_(uplo, "U")) {
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = min(j,*m);
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		b[i__ + j * b_dim1] = a[i__ + j * a_dim1];
+/* L10: */
+	    }
+/* L20: */
+	}
+    } else if (igraphlsame_(uplo, "L")) {
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = j; i__ <= i__2; ++i__) {
+		b[i__ + j * b_dim1] = a[i__ + j * a_dim1];
+/* L30: */
+	    }
+/* L40: */
+	}
+    } else {
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		b[i__ + j * b_dim1] = a[i__ + j * a_dim1];
+/* L50: */
+	    }
+/* L60: */
+	}
+    }
+    return 0;
+
+/*     End of DLACPY */
+
+} /* igraphdlacpy_ */
+
diff --git a/igraph/src/dladiv.c b/igraph/src/dladiv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dladiv.c
@@ -0,0 +1,246 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLADIV performs complex division in real arithmetic, avoiding unnecessary overflow.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLADIV + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dladiv.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dladiv.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dladiv.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLADIV( A, B, C, D, P, Q )   
+
+         DOUBLE PRECISION   A, B, C, D, P, Q   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLADIV performs complex division in  real arithmetic   
+   >   
+   >                       a + i*b   
+   >            p + i*q = ---------   
+   >                       c + i*d   
+   >   
+   > The algorithm is due to Michael Baudin and Robert L. Smith   
+   > and can be found in the paper   
+   > "A Robust Complex Division in Scilab"   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION   
+   >          The scalars a, b, c, and d in the above expression.   
+   > \endverbatim   
+   >   
+   > \param[out] P   
+   > \verbatim   
+   >          P is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[out] Q   
+   > \verbatim   
+   >          Q is DOUBLE PRECISION   
+   >          The scalars p and q in the above expression.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date January 2013   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdladiv_(doublereal *a, doublereal *b, doublereal *c__, 
+	doublereal *d__, doublereal *p, doublereal *q)
+{
+    /* System generated locals */
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    doublereal s, aa, ab, bb, cc, cd, dd, be, un, ov, eps;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int dladiv1_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.5.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       January 2013   
+
+
+    ===================================================================== */
+
+
+
+    aa = *a;
+    bb = *b;
+    cc = *c__;
+    dd = *d__;
+/* Computing MAX */
+    d__1 = abs(*a), d__2 = abs(*b);
+    ab = max(d__1,d__2);
+/* Computing MAX */
+    d__1 = abs(*c__), d__2 = abs(*d__);
+    cd = max(d__1,d__2);
+    s = 1.;
+    ov = igraphdlamch_("Overflow threshold");
+    un = igraphdlamch_("Safe minimum");
+    eps = igraphdlamch_("Epsilon");
+    be = 2. / (eps * eps);
+    if (ab >= ov * .5) {
+	aa *= .5;
+	bb *= .5;
+	s *= 2.;
+    }
+    if (cd >= ov * .5) {
+	cc *= .5;
+	dd *= .5;
+	s *= .5;
+    }
+    if (ab <= un * 2. / eps) {
+	aa *= be;
+	bb *= be;
+	s /= be;
+    }
+    if (cd <= un * 2. / eps) {
+	cc *= be;
+	dd *= be;
+	s *= be;
+    }
+    if (abs(*d__) <= abs(*c__)) {
+	dladiv1_(&aa, &bb, &cc, &dd, p, q);
+    } else {
+	dladiv1_(&bb, &aa, &dd, &cc, p, q);
+	*q = -(*q);
+    }
+    *p *= s;
+    *q *= s;
+
+    return 0;
+
+/*     End of DLADIV */
+
+} /* igraphdladiv_   
+
+   Subroutine */ int dladiv1_(doublereal *a, doublereal *b, doublereal *c__, 
+	doublereal *d__, doublereal *p, doublereal *q)
+{
+    doublereal r__, t;
+    extern doublereal dladiv2_(doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.5.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       January 2013   
+
+
+    ===================================================================== */
+
+
+
+    r__ = *d__ / *c__;
+    t = 1. / (*c__ + *d__ * r__);
+    *p = dladiv2_(a, b, c__, d__, &r__, &t);
+    *a = -(*a);
+    *q = dladiv2_(b, a, c__, d__, &r__, &t);
+
+    return 0;
+
+/*     End of DLADIV1 */
+
+} /* dladiv1_ */
+
+doublereal dladiv2_(doublereal *a, doublereal *b, doublereal *c__, doublereal 
+	*d__, doublereal *r__, doublereal *t)
+{
+    /* System generated locals */
+    doublereal ret_val;
+
+    /* Local variables */
+    doublereal br;
+
+
+/*  -- LAPACK auxiliary routine (version 3.5.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       January 2013   
+
+
+    ===================================================================== */
+
+
+
+    if (*r__ != 0.) {
+	br = *b * *r__;
+	if (br != 0.) {
+	    ret_val = (*a + br) * *t;
+	} else {
+	    ret_val = *a * *t + *b * *t * *r__;
+	}
+    } else {
+	ret_val = (*a + *d__ * (*b / *c__)) * *t;
+    }
+
+    return ret_val;
+
+/*     End of DLADIV12 */
+
+} /* dladiv2_ */
+
diff --git a/igraph/src/dlae2.c b/igraph/src/dlae2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlae2.c
@@ -0,0 +1,196 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAE2 computes the eigenvalues of a 2-by-2 symmetric matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAE2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlae2.f
+">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlae2.f
+">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlae2.f
+">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAE2( A, B, C, RT1, RT2 )   
+
+         DOUBLE PRECISION   A, B, C, RT1, RT2   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAE2  computes the eigenvalues of a 2-by-2 symmetric matrix   
+   >    [  A   B  ]   
+   >    [  B   C  ].   
+   > On return, RT1 is the eigenvalue of larger absolute value, and RT2   
+   > is the eigenvalue of smaller absolute value.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION   
+   >          The (1,1) element of the 2-by-2 matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION   
+   >          The (1,2) and (2,1) elements of the 2-by-2 matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION   
+   >          The (2,2) element of the 2-by-2 matrix.   
+   > \endverbatim   
+   >   
+   > \param[out] RT1   
+   > \verbatim   
+   >          RT1 is DOUBLE PRECISION   
+   >          The eigenvalue of larger absolute value.   
+   > \endverbatim   
+   >   
+   > \param[out] RT2   
+   > \verbatim   
+   >          RT2 is DOUBLE PRECISION   
+   >          The eigenvalue of smaller absolute value.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  RT1 is accurate to a few ulps barring over/underflow.   
+   >   
+   >  RT2 may be inaccurate if there is massive cancellation in the   
+   >  determinant A*C-B*B; higher precision or correctly rounded or   
+   >  correctly truncated arithmetic would be needed to compute RT2   
+   >  accurately in all cases.   
+   >   
+   >  Overflow is possible only if RT1 is within a factor of 5 of overflow.   
+   >  Underflow is harmless if the input data is 0 or exceeds   
+   >     underflow_threshold / macheps.   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlae2_(doublereal *a, doublereal *b, doublereal *c__, 
+	doublereal *rt1, doublereal *rt2)
+{
+    /* System generated locals */
+    doublereal d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    doublereal ab, df, tb, sm, rt, adf, acmn, acmx;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Compute the eigenvalues */
+
+    sm = *a + *c__;
+    df = *a - *c__;
+    adf = abs(df);
+    tb = *b + *b;
+    ab = abs(tb);
+    if (abs(*a) > abs(*c__)) {
+	acmx = *a;
+	acmn = *c__;
+    } else {
+	acmx = *c__;
+	acmn = *a;
+    }
+    if (adf > ab) {
+/* Computing 2nd power */
+	d__1 = ab / adf;
+	rt = adf * sqrt(d__1 * d__1 + 1.);
+    } else if (adf < ab) {
+/* Computing 2nd power */
+	d__1 = adf / ab;
+	rt = ab * sqrt(d__1 * d__1 + 1.);
+    } else {
+
+/*        Includes case AB=ADF=0 */
+
+	rt = ab * sqrt(2.);
+    }
+    if (sm < 0.) {
+	*rt1 = (sm - rt) * .5;
+
+/*        Order of execution important.   
+          To get fully accurate smaller eigenvalue,   
+          next line needs to be executed in higher precision. */
+
+	*rt2 = acmx / *rt1 * acmn - *b / *rt1 * *b;
+    } else if (sm > 0.) {
+	*rt1 = (sm + rt) * .5;
+
+/*        Order of execution important.   
+          To get fully accurate smaller eigenvalue,   
+          next line needs to be executed in higher precision. */
+
+	*rt2 = acmx / *rt1 * acmn - *b / *rt1 * *b;
+    } else {
+
+/*        Includes case RT1 = RT2 = 0 */
+
+	*rt1 = rt * .5;
+	*rt2 = rt * -.5;
+    }
+    return 0;
+
+/*     End of DLAE2 */
+
+} /* igraphdlae2_ */
+
diff --git a/igraph/src/dlaebz.c b/igraph/src/dlaebz.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaebz.c
@@ -0,0 +1,727 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAEBZ computes the number of eigenvalues of a real symmetric tridiagonal matrix which are less
+ than or equal to a given value, and performs other tasks required by the routine sstebz.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAEBZ + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaebz.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaebz.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaebz.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAEBZ( IJOB, NITMAX, N, MMAX, MINP, NBMIN, ABSTOL,   
+                            RELTOL, PIVMIN, D, E, E2, NVAL, AB, C, MOUT,   
+                            NAB, WORK, IWORK, INFO )   
+
+         INTEGER            IJOB, INFO, MINP, MMAX, MOUT, N, NBMIN, NITMAX   
+         DOUBLE PRECISION   ABSTOL, PIVMIN, RELTOL   
+         INTEGER            IWORK( * ), NAB( MMAX, * ), NVAL( * )   
+         DOUBLE PRECISION   AB( MMAX, * ), C( * ), D( * ), E( * ), E2( * ),   
+        $                   WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAEBZ contains the iteration loops which compute and use the   
+   > function N(w), which is the count of eigenvalues of a symmetric   
+   > tridiagonal matrix T less than or equal to its argument  w.  It   
+   > performs a choice of two types of loops:   
+   >   
+   > IJOB=1, followed by   
+   > IJOB=2: It takes as input a list of intervals and returns a list of   
+   >         sufficiently small intervals whose union contains the same   
+   >         eigenvalues as the union of the original intervals.   
+   >         The input intervals are (AB(j,1),AB(j,2)], j=1,...,MINP.   
+   >         The output interval (AB(j,1),AB(j,2)] will contain   
+   >         eigenvalues NAB(j,1)+1,...,NAB(j,2), where 1 <= j <= MOUT.   
+   >   
+   > IJOB=3: It performs a binary search in each input interval   
+   >         (AB(j,1),AB(j,2)] for a point  w(j)  such that   
+   >         N(w(j))=NVAL(j), and uses  C(j)  as the starting point of   
+   >         the search.  If such a w(j) is found, then on output   
+   >         AB(j,1)=AB(j,2)=w.  If no such w(j) is found, then on output   
+   >         (AB(j,1),AB(j,2)] will be a small interval containing the   
+   >         point where N(w) jumps through NVAL(j), unless that point   
+   >         lies outside the initial interval.   
+   >   
+   > Note that the intervals are in all cases half-open intervals,   
+   > i.e., of the form  (a,b] , which includes  b  but not  a .   
+   >   
+   > To avoid underflow, the matrix should be scaled so that its largest   
+   > element is no greater than  overflow**(1/2) * underflow**(1/4)   
+   > in absolute value.  To assure the most accurate computation   
+   > of small eigenvalues, the matrix should be scaled to be   
+   > not much smaller than that, either.   
+   >   
+   > See W. Kahan "Accurate Eigenvalues of a Symmetric Tridiagonal   
+   > Matrix", Report CS41, Computer Science Dept., Stanford   
+   > University, July 21, 1966   
+   >   
+   > Note: the arguments are, in general, *not* checked for unreasonable   
+   > values.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] IJOB   
+   > \verbatim   
+   >          IJOB is INTEGER   
+   >          Specifies what is to be done:   
+   >          = 1:  Compute NAB for the initial intervals.   
+   >          = 2:  Perform bisection iteration to find eigenvalues of T.   
+   >          = 3:  Perform bisection iteration to invert N(w), i.e.,   
+   >                to find a point which has a specified number of   
+   >                eigenvalues of T to its left.   
+   >          Other values will cause DLAEBZ to return with INFO=-1.   
+   > \endverbatim   
+   >   
+   > \param[in] NITMAX   
+   > \verbatim   
+   >          NITMAX is INTEGER   
+   >          The maximum number of "levels" of bisection to be   
+   >          performed, i.e., an interval of width W will not be made   
+   >          smaller than 2^(-NITMAX) * W.  If not all intervals   
+   >          have converged after NITMAX iterations, then INFO is set   
+   >          to the number of non-converged intervals.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The dimension n of the tridiagonal matrix T.  It must be at   
+   >          least 1.   
+   > \endverbatim   
+   >   
+   > \param[in] MMAX   
+   > \verbatim   
+   >          MMAX is INTEGER   
+   >          The maximum number of intervals.  If more than MMAX intervals   
+   >          are generated, then DLAEBZ will quit with INFO=MMAX+1.   
+   > \endverbatim   
+   >   
+   > \param[in] MINP   
+   > \verbatim   
+   >          MINP is INTEGER   
+   >          The initial number of intervals.  It may not be greater than   
+   >          MMAX.   
+   > \endverbatim   
+   >   
+   > \param[in] NBMIN   
+   > \verbatim   
+   >          NBMIN is INTEGER   
+   >          The smallest number of intervals that should be processed   
+   >          using a vector loop.  If zero, then only the scalar loop   
+   >          will be used.   
+   > \endverbatim   
+   >   
+   > \param[in] ABSTOL   
+   > \verbatim   
+   >          ABSTOL is DOUBLE PRECISION   
+   >          The minimum (absolute) width of an interval.  When an   
+   >          interval is narrower than ABSTOL, or than RELTOL times the   
+   >          larger (in magnitude) endpoint, then it is considered to be   
+   >          sufficiently small, i.e., converged.  This must be at least   
+   >          zero.   
+   > \endverbatim   
+   >   
+   > \param[in] RELTOL   
+   > \verbatim   
+   >          RELTOL is DOUBLE PRECISION   
+   >          The minimum relative width of an interval.  When an interval   
+   >          is narrower than ABSTOL, or than RELTOL times the larger (in   
+   >          magnitude) endpoint, then it is considered to be   
+   >          sufficiently small, i.e., converged.  Note: this should   
+   >          always be at least radix*machine epsilon.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum absolute value of a "pivot" in the Sturm   
+   >          sequence loop.   
+   >          This must be at least  max |e(j)**2|*safe_min  and at   
+   >          least safe_min, where safe_min is at least   
+   >          the smallest number that can divide one without overflow.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N)   
+   >          The offdiagonal elements of the tridiagonal matrix T in   
+   >          positions 1 through N-1.  E(N) is arbitrary.   
+   > \endverbatim   
+   >   
+   > \param[in] E2   
+   > \verbatim   
+   >          E2 is DOUBLE PRECISION array, dimension (N)   
+   >          The squares of the offdiagonal elements of the tridiagonal   
+   >          matrix T.  E2(N) is ignored.   
+   > \endverbatim   
+   >   
+   > \param[in,out] NVAL   
+   > \verbatim   
+   >          NVAL is INTEGER array, dimension (MINP)   
+   >          If IJOB=1 or 2, not referenced.   
+   >          If IJOB=3, the desired values of N(w).  The elements of NVAL   
+   >          will be reordered to correspond with the intervals in AB.   
+   >          Thus, NVAL(j) on output will not, in general be the same as   
+   >          NVAL(j) on input, but it will correspond with the interval   
+   >          (AB(j,1),AB(j,2)] on output.   
+   > \endverbatim   
+   >   
+   > \param[in,out] AB   
+   > \verbatim   
+   >          AB is DOUBLE PRECISION array, dimension (MMAX,2)   
+   >          The endpoints of the intervals.  AB(j,1) is  a(j), the left   
+   >          endpoint of the j-th interval, and AB(j,2) is b(j), the   
+   >          right endpoint of the j-th interval.  The input intervals   
+   >          will, in general, be modified, split, and reordered by the   
+   >          calculation.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (MMAX)   
+   >          If IJOB=1, ignored.   
+   >          If IJOB=2, workspace.   
+   >          If IJOB=3, then on input C(j) should be initialized to the   
+   >          first search point in the binary search.   
+   > \endverbatim   
+   >   
+   > \param[out] MOUT   
+   > \verbatim   
+   >          MOUT is INTEGER   
+   >          If IJOB=1, the number of eigenvalues in the intervals.   
+   >          If IJOB=2 or 3, the number of intervals output.   
+   >          If IJOB=3, MOUT will equal MINP.   
+   > \endverbatim   
+   >   
+   > \param[in,out] NAB   
+   > \verbatim   
+   >          NAB is INTEGER array, dimension (MMAX,2)   
+   >          If IJOB=1, then on output NAB(i,j) will be set to N(AB(i,j)).   
+   >          If IJOB=2, then on input, NAB(i,j) should be set.  It must   
+   >             satisfy the condition:   
+   >             N(AB(i,1)) <= NAB(i,1) <= NAB(i,2) <= N(AB(i,2)),   
+   >             which means that in interval i only eigenvalues   
+   >             NAB(i,1)+1,...,NAB(i,2) will be considered.  Usually,   
+   >             NAB(i,j)=N(AB(i,j)), from a previous call to DLAEBZ with   
+   >             IJOB=1.   
+   >             On output, NAB(i,j) will contain   
+   >             max(na(k),min(nb(k),N(AB(i,j)))), where k is the index of   
+   >             the input interval that the output interval   
+   >             (AB(j,1),AB(j,2)] came from, and na(k) and nb(k) are the   
+   >             the input values of NAB(k,1) and NAB(k,2).   
+   >          If IJOB=3, then on output, NAB(i,j) contains N(AB(i,j)),   
+   >             unless N(w) > NVAL(i) for all search points  w , in which   
+   >             case NAB(i,1) will not be modified, i.e., the output   
+   >             value will be the same as the input value (modulo   
+   >             reorderings -- see NVAL and AB), or unless N(w) < NVAL(i)   
+   >             for all search points  w , in which case NAB(i,2) will   
+   >             not be modified.  Normally, NAB should be set to some   
+   >             distinctive value(s) before DLAEBZ is called.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MMAX)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (MMAX)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:       All intervals converged.   
+   >          = 1--MMAX: The last INFO intervals did not converge.   
+   >          = MMAX+1:  More than MMAX intervals were generated.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >      This routine is intended to be called only by other LAPACK   
+   >  routines, thus the interface is less user-friendly.  It is intended   
+   >  for two purposes:   
+   >   
+   >  (a) finding eigenvalues.  In this case, DLAEBZ should have one or   
+   >      more initial intervals set up in AB, and DLAEBZ should be called   
+   >      with IJOB=1.  This sets up NAB, and also counts the eigenvalues.   
+   >      Intervals with no eigenvalues would usually be thrown out at   
+   >      this point.  Also, if not all the eigenvalues in an interval i   
+   >      are desired, NAB(i,1) can be increased or NAB(i,2) decreased.   
+   >      For example, set NAB(i,1)=NAB(i,2)-1 to get the largest   
+   >      eigenvalue.  DLAEBZ is then called with IJOB=2 and MMAX   
+   >      no smaller than the value of MOUT returned by the call with   
+   >      IJOB=1.  After this (IJOB=2) call, eigenvalues NAB(i,1)+1   
+   >      through NAB(i,2) are approximately AB(i,1) (or AB(i,2)) to the   
+   >      tolerance specified by ABSTOL and RELTOL.   
+   >   
+   >  (b) finding an interval (a',b'] containing eigenvalues w(f),...,w(l).   
+   >      In this case, start with a Gershgorin interval  (a,b).  Set up   
+   >      AB to contain 2 search intervals, both initially (a,b).  One   
+   >      NVAL element should contain  f-1  and the other should contain  l   
+   >      , while C should contain a and b, resp.  NAB(i,1) should be -1   
+   >      and NAB(i,2) should be N+1, to flag an error if the desired   
+   >      interval does not lie in (a,b).  DLAEBZ is then called with   
+   >      IJOB=3.  On exit, if w(f-1) < w(f), then one of the intervals --   
+   >      j -- will have AB(j,1)=AB(j,2) and NAB(j,1)=NAB(j,2)=f-1, while   
+   >      if, to the specified tolerance, w(f-k)=...=w(f+r), k > 0 and r   
+   >      >= 0, then the interval will have  N(AB(j,1))=NAB(j,1)=f-k and   
+   >      N(AB(j,2))=NAB(j,2)=f+r.  The cases w(l) < w(l+1) and   
+   >      w(l-r)=...=w(l+k) are handled similarly.   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaebz_(integer *ijob, integer *nitmax, integer *n, 
+	integer *mmax, integer *minp, integer *nbmin, doublereal *abstol, 
+	doublereal *reltol, doublereal *pivmin, doublereal *d__, doublereal *
+	e, doublereal *e2, integer *nval, doublereal *ab, doublereal *c__, 
+	integer *mout, integer *nab, doublereal *work, integer *iwork, 
+	integer *info)
+{
+    /* System generated locals */
+    integer nab_dim1, nab_offset, ab_dim1, ab_offset, i__1, i__2, i__3, i__4, 
+	    i__5, i__6;
+    doublereal d__1, d__2, d__3, d__4;
+
+    /* Local variables */
+    integer j, kf, ji, kl, jp, jit;
+    doublereal tmp1, tmp2;
+    integer itmp1, itmp2, kfnew, klnew;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Check for Errors   
+
+       Parameter adjustments */
+    nab_dim1 = *mmax;
+    nab_offset = 1 + nab_dim1;
+    nab -= nab_offset;
+    ab_dim1 = *mmax;
+    ab_offset = 1 + ab_dim1;
+    ab -= ab_offset;
+    --d__;
+    --e;
+    --e2;
+    --nval;
+    --c__;
+    --work;
+    --iwork;
+
+    /* Function Body */
+    *info = 0;
+    if (*ijob < 1 || *ijob > 3) {
+	*info = -1;
+	return 0;
+    }
+
+/*     Initialize NAB */
+
+    if (*ijob == 1) {
+
+/*        Compute the number of eigenvalues in the initial intervals. */
+
+	*mout = 0;
+	i__1 = *minp;
+	for (ji = 1; ji <= i__1; ++ji) {
+	    for (jp = 1; jp <= 2; ++jp) {
+		tmp1 = d__[1] - ab[ji + jp * ab_dim1];
+		if (abs(tmp1) < *pivmin) {
+		    tmp1 = -(*pivmin);
+		}
+		nab[ji + jp * nab_dim1] = 0;
+		if (tmp1 <= 0.) {
+		    nab[ji + jp * nab_dim1] = 1;
+		}
+
+		i__2 = *n;
+		for (j = 2; j <= i__2; ++j) {
+		    tmp1 = d__[j] - e2[j - 1] / tmp1 - ab[ji + jp * ab_dim1];
+		    if (abs(tmp1) < *pivmin) {
+			tmp1 = -(*pivmin);
+		    }
+		    if (tmp1 <= 0.) {
+			++nab[ji + jp * nab_dim1];
+		    }
+/* L10: */
+		}
+/* L20: */
+	    }
+	    *mout = *mout + nab[ji + (nab_dim1 << 1)] - nab[ji + nab_dim1];
+/* L30: */
+	}
+	return 0;
+    }
+
+/*     Initialize for loop   
+
+       KF and KL have the following meaning:   
+          Intervals 1,...,KF-1 have converged.   
+          Intervals KF,...,KL  still need to be refined. */
+
+    kf = 1;
+    kl = *minp;
+
+/*     If IJOB=2, initialize C.   
+       If IJOB=3, use the user-supplied starting point. */
+
+    if (*ijob == 2) {
+	i__1 = *minp;
+	for (ji = 1; ji <= i__1; ++ji) {
+	    c__[ji] = (ab[ji + ab_dim1] + ab[ji + (ab_dim1 << 1)]) * .5;
+/* L40: */
+	}
+    }
+
+/*     Iteration loop */
+
+    i__1 = *nitmax;
+    for (jit = 1; jit <= i__1; ++jit) {
+
+/*        Loop over intervals */
+
+	if (kl - kf + 1 >= *nbmin && *nbmin > 0) {
+
+/*           Begin of Parallel Version of the loop */
+
+	    i__2 = kl;
+	    for (ji = kf; ji <= i__2; ++ji) {
+
+/*              Compute N(c), the number of eigenvalues less than c */
+
+		work[ji] = d__[1] - c__[ji];
+		iwork[ji] = 0;
+		if (work[ji] <= *pivmin) {
+		    iwork[ji] = 1;
+/* Computing MIN */
+		    d__1 = work[ji], d__2 = -(*pivmin);
+		    work[ji] = min(d__1,d__2);
+		}
+
+		i__3 = *n;
+		for (j = 2; j <= i__3; ++j) {
+		    work[ji] = d__[j] - e2[j - 1] / work[ji] - c__[ji];
+		    if (work[ji] <= *pivmin) {
+			++iwork[ji];
+/* Computing MIN */
+			d__1 = work[ji], d__2 = -(*pivmin);
+			work[ji] = min(d__1,d__2);
+		    }
+/* L50: */
+		}
+/* L60: */
+	    }
+
+	    if (*ijob <= 2) {
+
+/*              IJOB=2: Choose all intervals containing eigenvalues. */
+
+		klnew = kl;
+		i__2 = kl;
+		for (ji = kf; ji <= i__2; ++ji) {
+
+/*                 Insure that N(w) is monotone   
+
+   Computing MIN   
+   Computing MAX */
+		    i__5 = nab[ji + nab_dim1], i__6 = iwork[ji];
+		    i__3 = nab[ji + (nab_dim1 << 1)], i__4 = max(i__5,i__6);
+		    iwork[ji] = min(i__3,i__4);
+
+/*                 Update the Queue -- add intervals if both halves   
+                   contain eigenvalues. */
+
+		    if (iwork[ji] == nab[ji + (nab_dim1 << 1)]) {
+
+/*                    No eigenvalue in the upper interval:   
+                      just use the lower interval. */
+
+			ab[ji + (ab_dim1 << 1)] = c__[ji];
+
+		    } else if (iwork[ji] == nab[ji + nab_dim1]) {
+
+/*                    No eigenvalue in the lower interval:   
+                      just use the upper interval. */
+
+			ab[ji + ab_dim1] = c__[ji];
+		    } else {
+			++klnew;
+			if (klnew <= *mmax) {
+
+/*                       Eigenvalue in both intervals -- add upper to   
+                         queue. */
+
+			    ab[klnew + (ab_dim1 << 1)] = ab[ji + (ab_dim1 << 
+				    1)];
+			    nab[klnew + (nab_dim1 << 1)] = nab[ji + (nab_dim1 
+				    << 1)];
+			    ab[klnew + ab_dim1] = c__[ji];
+			    nab[klnew + nab_dim1] = iwork[ji];
+			    ab[ji + (ab_dim1 << 1)] = c__[ji];
+			    nab[ji + (nab_dim1 << 1)] = iwork[ji];
+			} else {
+			    *info = *mmax + 1;
+			}
+		    }
+/* L70: */
+		}
+		if (*info != 0) {
+		    return 0;
+		}
+		kl = klnew;
+	    } else {
+
+/*              IJOB=3: Binary search.  Keep only the interval containing   
+                        w   s.t. N(w) = NVAL */
+
+		i__2 = kl;
+		for (ji = kf; ji <= i__2; ++ji) {
+		    if (iwork[ji] <= nval[ji]) {
+			ab[ji + ab_dim1] = c__[ji];
+			nab[ji + nab_dim1] = iwork[ji];
+		    }
+		    if (iwork[ji] >= nval[ji]) {
+			ab[ji + (ab_dim1 << 1)] = c__[ji];
+			nab[ji + (nab_dim1 << 1)] = iwork[ji];
+		    }
+/* L80: */
+		}
+	    }
+
+	} else {
+
+/*           End of Parallel Version of the loop   
+
+             Begin of Serial Version of the loop */
+
+	    klnew = kl;
+	    i__2 = kl;
+	    for (ji = kf; ji <= i__2; ++ji) {
+
+/*              Compute N(w), the number of eigenvalues less than w */
+
+		tmp1 = c__[ji];
+		tmp2 = d__[1] - tmp1;
+		itmp1 = 0;
+		if (tmp2 <= *pivmin) {
+		    itmp1 = 1;
+/* Computing MIN */
+		    d__1 = tmp2, d__2 = -(*pivmin);
+		    tmp2 = min(d__1,d__2);
+		}
+
+		i__3 = *n;
+		for (j = 2; j <= i__3; ++j) {
+		    tmp2 = d__[j] - e2[j - 1] / tmp2 - tmp1;
+		    if (tmp2 <= *pivmin) {
+			++itmp1;
+/* Computing MIN */
+			d__1 = tmp2, d__2 = -(*pivmin);
+			tmp2 = min(d__1,d__2);
+		    }
+/* L90: */
+		}
+
+		if (*ijob <= 2) {
+
+/*                 IJOB=2: Choose all intervals containing eigenvalues.   
+
+                   Insure that N(w) is monotone   
+
+   Computing MIN   
+   Computing MAX */
+		    i__5 = nab[ji + nab_dim1];
+		    i__3 = nab[ji + (nab_dim1 << 1)], i__4 = max(i__5,itmp1);
+		    itmp1 = min(i__3,i__4);
+
+/*                 Update the Queue -- add intervals if both halves   
+                   contain eigenvalues. */
+
+		    if (itmp1 == nab[ji + (nab_dim1 << 1)]) {
+
+/*                    No eigenvalue in the upper interval:   
+                      just use the lower interval. */
+
+			ab[ji + (ab_dim1 << 1)] = tmp1;
+
+		    } else if (itmp1 == nab[ji + nab_dim1]) {
+
+/*                    No eigenvalue in the lower interval:   
+                      just use the upper interval. */
+
+			ab[ji + ab_dim1] = tmp1;
+		    } else if (klnew < *mmax) {
+
+/*                    Eigenvalue in both intervals -- add upper to queue. */
+
+			++klnew;
+			ab[klnew + (ab_dim1 << 1)] = ab[ji + (ab_dim1 << 1)];
+			nab[klnew + (nab_dim1 << 1)] = nab[ji + (nab_dim1 << 
+				1)];
+			ab[klnew + ab_dim1] = tmp1;
+			nab[klnew + nab_dim1] = itmp1;
+			ab[ji + (ab_dim1 << 1)] = tmp1;
+			nab[ji + (nab_dim1 << 1)] = itmp1;
+		    } else {
+			*info = *mmax + 1;
+			return 0;
+		    }
+		} else {
+
+/*                 IJOB=3: Binary search.  Keep only the interval   
+                           containing  w  s.t. N(w) = NVAL */
+
+		    if (itmp1 <= nval[ji]) {
+			ab[ji + ab_dim1] = tmp1;
+			nab[ji + nab_dim1] = itmp1;
+		    }
+		    if (itmp1 >= nval[ji]) {
+			ab[ji + (ab_dim1 << 1)] = tmp1;
+			nab[ji + (nab_dim1 << 1)] = itmp1;
+		    }
+		}
+/* L100: */
+	    }
+	    kl = klnew;
+
+	}
+
+/*        Check for convergence */
+
+	kfnew = kf;
+	i__2 = kl;
+	for (ji = kf; ji <= i__2; ++ji) {
+	    tmp1 = (d__1 = ab[ji + (ab_dim1 << 1)] - ab[ji + ab_dim1], abs(
+		    d__1));
+/* Computing MAX */
+	    d__3 = (d__1 = ab[ji + (ab_dim1 << 1)], abs(d__1)), d__4 = (d__2 =
+		     ab[ji + ab_dim1], abs(d__2));
+	    tmp2 = max(d__3,d__4);
+/* Computing MAX */
+	    d__1 = max(*abstol,*pivmin), d__2 = *reltol * tmp2;
+	    if (tmp1 < max(d__1,d__2) || nab[ji + nab_dim1] >= nab[ji + (
+		    nab_dim1 << 1)]) {
+
+/*              Converged -- Swap with position KFNEW,   
+                             then increment KFNEW */
+
+		if (ji > kfnew) {
+		    tmp1 = ab[ji + ab_dim1];
+		    tmp2 = ab[ji + (ab_dim1 << 1)];
+		    itmp1 = nab[ji + nab_dim1];
+		    itmp2 = nab[ji + (nab_dim1 << 1)];
+		    ab[ji + ab_dim1] = ab[kfnew + ab_dim1];
+		    ab[ji + (ab_dim1 << 1)] = ab[kfnew + (ab_dim1 << 1)];
+		    nab[ji + nab_dim1] = nab[kfnew + nab_dim1];
+		    nab[ji + (nab_dim1 << 1)] = nab[kfnew + (nab_dim1 << 1)];
+		    ab[kfnew + ab_dim1] = tmp1;
+		    ab[kfnew + (ab_dim1 << 1)] = tmp2;
+		    nab[kfnew + nab_dim1] = itmp1;
+		    nab[kfnew + (nab_dim1 << 1)] = itmp2;
+		    if (*ijob == 3) {
+			itmp1 = nval[ji];
+			nval[ji] = nval[kfnew];
+			nval[kfnew] = itmp1;
+		    }
+		}
+		++kfnew;
+	    }
+/* L110: */
+	}
+	kf = kfnew;
+
+/*        Choose Midpoints */
+
+	i__2 = kl;
+	for (ji = kf; ji <= i__2; ++ji) {
+	    c__[ji] = (ab[ji + ab_dim1] + ab[ji + (ab_dim1 << 1)]) * .5;
+/* L120: */
+	}
+
+/*        If no more intervals to refine, quit. */
+
+	if (kf > kl) {
+	    goto L140;
+	}
+/* L130: */
+    }
+
+/*     Converged */
+
+L140:
+/* Computing MAX */
+    i__1 = kl + 1 - kf;
+    *info = max(i__1,0);
+    *mout = kl;
+
+    return 0;
+
+/*     End of DLAEBZ */
+
+} /* igraphdlaebz_ */
+
diff --git a/igraph/src/dlaev2.c b/igraph/src/dlaev2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaev2.c
@@ -0,0 +1,249 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAEV2 computes the eigenvalues and eigenvectors of a 2-by-2 symmetric/Hermitian matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAEV2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaev2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaev2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaev2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAEV2( A, B, C, RT1, RT2, CS1, SN1 )   
+
+         DOUBLE PRECISION   A, B, C, CS1, RT1, RT2, SN1   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAEV2 computes the eigendecomposition of a 2-by-2 symmetric matrix   
+   >    [  A   B  ]   
+   >    [  B   C  ].   
+   > On return, RT1 is the eigenvalue of larger absolute value, RT2 is the   
+   > eigenvalue of smaller absolute value, and (CS1,SN1) is the unit right   
+   > eigenvector for RT1, giving the decomposition   
+   >   
+   >    [ CS1  SN1 ] [  A   B  ] [ CS1 -SN1 ]  =  [ RT1  0  ]   
+   >    [-SN1  CS1 ] [  B   C  ] [ SN1  CS1 ]     [  0  RT2 ].   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION   
+   >          The (1,1) element of the 2-by-2 matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION   
+   >          The (1,2) element and the conjugate of the (2,1) element of   
+   >          the 2-by-2 matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION   
+   >          The (2,2) element of the 2-by-2 matrix.   
+   > \endverbatim   
+   >   
+   > \param[out] RT1   
+   > \verbatim   
+   >          RT1 is DOUBLE PRECISION   
+   >          The eigenvalue of larger absolute value.   
+   > \endverbatim   
+   >   
+   > \param[out] RT2   
+   > \verbatim   
+   >          RT2 is DOUBLE PRECISION   
+   >          The eigenvalue of smaller absolute value.   
+   > \endverbatim   
+   >   
+   > \param[out] CS1   
+   > \verbatim   
+   >          CS1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[out] SN1   
+   > \verbatim   
+   >          SN1 is DOUBLE PRECISION   
+   >          The vector (CS1, SN1) is a unit right eigenvector for RT1.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  RT1 is accurate to a few ulps barring over/underflow.   
+   >   
+   >  RT2 may be inaccurate if there is massive cancellation in the   
+   >  determinant A*C-B*B; higher precision or correctly rounded or   
+   >  correctly truncated arithmetic would be needed to compute RT2   
+   >  accurately in all cases.   
+   >   
+   >  CS1 and SN1 are accurate to a few ulps barring over/underflow.   
+   >   
+   >  Overflow is possible only if RT1 is within a factor of 5 of overflow.   
+   >  Underflow is harmless if the input data is 0 or exceeds   
+   >     underflow_threshold / macheps.   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaev2_(doublereal *a, doublereal *b, doublereal *c__, 
+	doublereal *rt1, doublereal *rt2, doublereal *cs1, doublereal *sn1)
+{
+    /* System generated locals */
+    doublereal d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    doublereal ab, df, cs, ct, tb, sm, tn, rt, adf, acs;
+    integer sgn1, sgn2;
+    doublereal acmn, acmx;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Compute the eigenvalues */
+
+    sm = *a + *c__;
+    df = *a - *c__;
+    adf = abs(df);
+    tb = *b + *b;
+    ab = abs(tb);
+    if (abs(*a) > abs(*c__)) {
+	acmx = *a;
+	acmn = *c__;
+    } else {
+	acmx = *c__;
+	acmn = *a;
+    }
+    if (adf > ab) {
+/* Computing 2nd power */
+	d__1 = ab / adf;
+	rt = adf * sqrt(d__1 * d__1 + 1.);
+    } else if (adf < ab) {
+/* Computing 2nd power */
+	d__1 = adf / ab;
+	rt = ab * sqrt(d__1 * d__1 + 1.);
+    } else {
+
+/*        Includes case AB=ADF=0 */
+
+	rt = ab * sqrt(2.);
+    }
+    if (sm < 0.) {
+	*rt1 = (sm - rt) * .5;
+	sgn1 = -1;
+
+/*        Order of execution important.   
+          To get fully accurate smaller eigenvalue,   
+          next line needs to be executed in higher precision. */
+
+	*rt2 = acmx / *rt1 * acmn - *b / *rt1 * *b;
+    } else if (sm > 0.) {
+	*rt1 = (sm + rt) * .5;
+	sgn1 = 1;
+
+/*        Order of execution important.   
+          To get fully accurate smaller eigenvalue,   
+          next line needs to be executed in higher precision. */
+
+	*rt2 = acmx / *rt1 * acmn - *b / *rt1 * *b;
+    } else {
+
+/*        Includes case RT1 = RT2 = 0 */
+
+	*rt1 = rt * .5;
+	*rt2 = rt * -.5;
+	sgn1 = 1;
+    }
+
+/*     Compute the eigenvector */
+
+    if (df >= 0.) {
+	cs = df + rt;
+	sgn2 = 1;
+    } else {
+	cs = df - rt;
+	sgn2 = -1;
+    }
+    acs = abs(cs);
+    if (acs > ab) {
+	ct = -tb / cs;
+	*sn1 = 1. / sqrt(ct * ct + 1.);
+	*cs1 = ct * *sn1;
+    } else {
+	if (ab == 0.) {
+	    *cs1 = 1.;
+	    *sn1 = 0.;
+	} else {
+	    tn = -cs / tb;
+	    *cs1 = 1. / sqrt(tn * tn + 1.);
+	    *sn1 = tn * *cs1;
+	}
+    }
+    if (sgn1 == sgn2) {
+	tn = *cs1;
+	*cs1 = -(*sn1);
+	*sn1 = tn;
+    }
+    return 0;
+
+/*     End of DLAEV2 */
+
+} /* igraphdlaev2_ */
+
diff --git a/igraph/src/dlaexc.c b/igraph/src/dlaexc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaexc.c
@@ -0,0 +1,524 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c__4 = 4;
+static logical c_false = FALSE_;
+static integer c_n1 = -1;
+static integer c__2 = 2;
+static integer c__3 = 3;
+
+/* > \brief \b DLAEXC swaps adjacent diagonal blocks of a real upper quasi-triangular matrix in Schur canonica
+l form, by an orthogonal similarity transformation.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAEXC + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaexc.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaexc.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaexc.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAEXC( WANTQ, N, T, LDT, Q, LDQ, J1, N1, N2, WORK,   
+                            INFO )   
+
+         LOGICAL            WANTQ   
+         INTEGER            INFO, J1, LDQ, LDT, N, N1, N2   
+         DOUBLE PRECISION   Q( LDQ, * ), T( LDT, * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAEXC swaps adjacent diagonal blocks T11 and T22 of order 1 or 2 in   
+   > an upper quasi-triangular matrix T by an orthogonal similarity   
+   > transformation.   
+   >   
+   > T must be in Schur canonical form, that is, block upper triangular   
+   > with 1-by-1 and 2-by-2 diagonal blocks; each 2-by-2 diagonal block   
+   > has its diagonal elemnts equal and its off-diagonal elements of   
+   > opposite sign.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] WANTQ   
+   > \verbatim   
+   >          WANTQ is LOGICAL   
+   >          = .TRUE. : accumulate the transformation in the matrix Q;   
+   >          = .FALSE.: do not accumulate the transformation.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix T. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,N)   
+   >          On entry, the upper quasi-triangular matrix T, in Schur   
+   >          canonical form.   
+   >          On exit, the updated matrix T, again in Schur canonical form.   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is INTEGER   
+   >          The leading dimension of the array T. LDT >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in,out] Q   
+   > \verbatim   
+   >          Q is DOUBLE PRECISION array, dimension (LDQ,N)   
+   >          On entry, if WANTQ is .TRUE., the orthogonal matrix Q.   
+   >          On exit, if WANTQ is .TRUE., the updated matrix Q.   
+   >          If WANTQ is .FALSE., Q is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDQ   
+   > \verbatim   
+   >          LDQ is INTEGER   
+   >          The leading dimension of the array Q.   
+   >          LDQ >= 1; and if WANTQ is .TRUE., LDQ >= N.   
+   > \endverbatim   
+   >   
+   > \param[in] J1   
+   > \verbatim   
+   >          J1 is INTEGER   
+   >          The index of the first row of the first block T11.   
+   > \endverbatim   
+   >   
+   > \param[in] N1   
+   > \verbatim   
+   >          N1 is INTEGER   
+   >          The order of the first block T11. N1 = 0, 1 or 2.   
+   > \endverbatim   
+   >   
+   > \param[in] N2   
+   > \verbatim   
+   >          N2 is INTEGER   
+   >          The order of the second block T22. N2 = 0, 1 or 2.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          = 1: the transformed matrix T would be too far from Schur   
+   >               form; the blocks are not swapped and T and Q are   
+   >               unchanged.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlaexc_(logical *wantq, integer *n, doublereal *t, 
+	integer *ldt, doublereal *q, integer *ldq, integer *j1, integer *n1, 
+	integer *n2, doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer q_dim1, q_offset, t_dim1, t_offset, i__1;
+    doublereal d__1, d__2, d__3;
+
+    /* Local variables */
+    doublereal d__[16]	/* was [4][4] */;
+    integer k;
+    doublereal u[3], x[4]	/* was [2][2] */;
+    integer j2, j3, j4;
+    doublereal u1[3], u2[3];
+    integer nd;
+    doublereal cs, t11, t22, t33, sn, wi1, wi2, wr1, wr2, eps, tau, tau1, 
+	    tau2;
+    integer ierr;
+    doublereal temp;
+    extern /* Subroutine */ int igraphdrot_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *);
+    doublereal scale, dnorm, xnorm;
+    extern /* Subroutine */ int igraphdlanv2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *), igraphdlasy2_(
+	    logical *, logical *, integer *, integer *, integer *, doublereal 
+	    *, integer *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *);
+    extern doublereal igraphdlamch_(char *), igraphdlange_(char *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *);
+    extern /* Subroutine */ int igraphdlarfg_(integer *, doublereal *, doublereal *,
+	     integer *, doublereal *), igraphdlacpy_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), 
+	    igraphdlartg_(doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *), igraphdlarfx_(char *, integer *, integer *, doublereal *,
+	     doublereal *, doublereal *, integer *, doublereal *);
+    doublereal thresh, smlnum;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    q_dim1 = *ldq;
+    q_offset = 1 + q_dim1;
+    q -= q_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+
+/*     Quick return if possible */
+
+    if (*n == 0 || *n1 == 0 || *n2 == 0) {
+	return 0;
+    }
+    if (*j1 + *n1 > *n) {
+	return 0;
+    }
+
+    j2 = *j1 + 1;
+    j3 = *j1 + 2;
+    j4 = *j1 + 3;
+
+    if (*n1 == 1 && *n2 == 1) {
+
+/*        Swap two 1-by-1 blocks. */
+
+	t11 = t[*j1 + *j1 * t_dim1];
+	t22 = t[j2 + j2 * t_dim1];
+
+/*        Determine the transformation to perform the interchange. */
+
+	d__1 = t22 - t11;
+	igraphdlartg_(&t[*j1 + j2 * t_dim1], &d__1, &cs, &sn, &temp);
+
+/*        Apply transformation to the matrix T. */
+
+	if (j3 <= *n) {
+	    i__1 = *n - *j1 - 1;
+	    igraphdrot_(&i__1, &t[*j1 + j3 * t_dim1], ldt, &t[j2 + j3 * t_dim1], 
+		    ldt, &cs, &sn);
+	}
+	i__1 = *j1 - 1;
+	igraphdrot_(&i__1, &t[*j1 * t_dim1 + 1], &c__1, &t[j2 * t_dim1 + 1], &c__1, 
+		&cs, &sn);
+
+	t[*j1 + *j1 * t_dim1] = t22;
+	t[j2 + j2 * t_dim1] = t11;
+
+	if (*wantq) {
+
+/*           Accumulate transformation in the matrix Q. */
+
+	    igraphdrot_(n, &q[*j1 * q_dim1 + 1], &c__1, &q[j2 * q_dim1 + 1], &c__1, 
+		    &cs, &sn);
+	}
+
+    } else {
+
+/*        Swapping involves at least one 2-by-2 block.   
+
+          Copy the diagonal block of order N1+N2 to the local array D   
+          and compute its norm. */
+
+	nd = *n1 + *n2;
+	igraphdlacpy_("Full", &nd, &nd, &t[*j1 + *j1 * t_dim1], ldt, d__, &c__4);
+	dnorm = igraphdlange_("Max", &nd, &nd, d__, &c__4, &work[1]);
+
+/*        Compute machine-dependent threshold for test for accepting   
+          swap. */
+
+	eps = igraphdlamch_("P");
+	smlnum = igraphdlamch_("S") / eps;
+/* Computing MAX */
+	d__1 = eps * 10. * dnorm;
+	thresh = max(d__1,smlnum);
+
+/*        Solve T11*X - X*T22 = scale*T12 for X. */
+
+	igraphdlasy2_(&c_false, &c_false, &c_n1, n1, n2, d__, &c__4, &d__[*n1 + 1 + 
+		(*n1 + 1 << 2) - 5], &c__4, &d__[(*n1 + 1 << 2) - 4], &c__4, &
+		scale, x, &c__2, &xnorm, &ierr);
+
+/*        Swap the adjacent diagonal blocks. */
+
+	k = *n1 + *n1 + *n2 - 3;
+	switch (k) {
+	    case 1:  goto L10;
+	    case 2:  goto L20;
+	    case 3:  goto L30;
+	}
+
+L10:
+
+/*        N1 = 1, N2 = 2: generate elementary reflector H so that:   
+
+          ( scale, X11, X12 ) H = ( 0, 0, * ) */
+
+	u[0] = scale;
+	u[1] = x[0];
+	u[2] = x[2];
+	igraphdlarfg_(&c__3, &u[2], u, &c__1, &tau);
+	u[2] = 1.;
+	t11 = t[*j1 + *j1 * t_dim1];
+
+/*        Perform swap provisionally on diagonal block in D. */
+
+	igraphdlarfx_("L", &c__3, &c__3, u, &tau, d__, &c__4, &work[1]);
+	igraphdlarfx_("R", &c__3, &c__3, u, &tau, d__, &c__4, &work[1]);
+
+/*        Test whether to reject swap.   
+
+   Computing MAX */
+	d__2 = abs(d__[2]), d__3 = abs(d__[6]), d__2 = max(d__2,d__3), d__3 = 
+		(d__1 = d__[10] - t11, abs(d__1));
+	if (max(d__2,d__3) > thresh) {
+	    goto L50;
+	}
+
+/*        Accept swap: apply transformation to the entire matrix T. */
+
+	i__1 = *n - *j1 + 1;
+	igraphdlarfx_("L", &c__3, &i__1, u, &tau, &t[*j1 + *j1 * t_dim1], ldt, &
+		work[1]);
+	igraphdlarfx_("R", &j2, &c__3, u, &tau, &t[*j1 * t_dim1 + 1], ldt, &work[1]);
+
+	t[j3 + *j1 * t_dim1] = 0.;
+	t[j3 + j2 * t_dim1] = 0.;
+	t[j3 + j3 * t_dim1] = t11;
+
+	if (*wantq) {
+
+/*           Accumulate transformation in the matrix Q. */
+
+	    igraphdlarfx_("R", n, &c__3, u, &tau, &q[*j1 * q_dim1 + 1], ldq, &work[
+		    1]);
+	}
+	goto L40;
+
+L20:
+
+/*        N1 = 2, N2 = 1: generate elementary reflector H so that:   
+
+          H (  -X11 ) = ( * )   
+            (  -X21 ) = ( 0 )   
+            ( scale ) = ( 0 ) */
+
+	u[0] = -x[0];
+	u[1] = -x[1];
+	u[2] = scale;
+	igraphdlarfg_(&c__3, u, &u[1], &c__1, &tau);
+	u[0] = 1.;
+	t33 = t[j3 + j3 * t_dim1];
+
+/*        Perform swap provisionally on diagonal block in D. */
+
+	igraphdlarfx_("L", &c__3, &c__3, u, &tau, d__, &c__4, &work[1]);
+	igraphdlarfx_("R", &c__3, &c__3, u, &tau, d__, &c__4, &work[1]);
+
+/*        Test whether to reject swap.   
+
+   Computing MAX */
+	d__2 = abs(d__[1]), d__3 = abs(d__[2]), d__2 = max(d__2,d__3), d__3 = 
+		(d__1 = d__[0] - t33, abs(d__1));
+	if (max(d__2,d__3) > thresh) {
+	    goto L50;
+	}
+
+/*        Accept swap: apply transformation to the entire matrix T. */
+
+	igraphdlarfx_("R", &j3, &c__3, u, &tau, &t[*j1 * t_dim1 + 1], ldt, &work[1]);
+	i__1 = *n - *j1;
+	igraphdlarfx_("L", &c__3, &i__1, u, &tau, &t[*j1 + j2 * t_dim1], ldt, &work[
+		1]);
+
+	t[*j1 + *j1 * t_dim1] = t33;
+	t[j2 + *j1 * t_dim1] = 0.;
+	t[j3 + *j1 * t_dim1] = 0.;
+
+	if (*wantq) {
+
+/*           Accumulate transformation in the matrix Q. */
+
+	    igraphdlarfx_("R", n, &c__3, u, &tau, &q[*j1 * q_dim1 + 1], ldq, &work[
+		    1]);
+	}
+	goto L40;
+
+L30:
+
+/*        N1 = 2, N2 = 2: generate elementary reflectors H(1) and H(2) so   
+          that:   
+
+          H(2) H(1) (  -X11  -X12 ) = (  *  * )   
+                    (  -X21  -X22 )   (  0  * )   
+                    ( scale    0  )   (  0  0 )   
+                    (    0  scale )   (  0  0 ) */
+
+	u1[0] = -x[0];
+	u1[1] = -x[1];
+	u1[2] = scale;
+	igraphdlarfg_(&c__3, u1, &u1[1], &c__1, &tau1);
+	u1[0] = 1.;
+
+	temp = -tau1 * (x[2] + u1[1] * x[3]);
+	u2[0] = -temp * u1[1] - x[3];
+	u2[1] = -temp * u1[2];
+	u2[2] = scale;
+	igraphdlarfg_(&c__3, u2, &u2[1], &c__1, &tau2);
+	u2[0] = 1.;
+
+/*        Perform swap provisionally on diagonal block in D. */
+
+	igraphdlarfx_("L", &c__3, &c__4, u1, &tau1, d__, &c__4, &work[1])
+		;
+	igraphdlarfx_("R", &c__4, &c__3, u1, &tau1, d__, &c__4, &work[1])
+		;
+	igraphdlarfx_("L", &c__3, &c__4, u2, &tau2, &d__[1], &c__4, &work[1]);
+	igraphdlarfx_("R", &c__4, &c__3, u2, &tau2, &d__[4], &c__4, &work[1]);
+
+/*        Test whether to reject swap.   
+
+   Computing MAX */
+	d__1 = abs(d__[2]), d__2 = abs(d__[6]), d__1 = max(d__1,d__2), d__2 = 
+		abs(d__[3]), d__1 = max(d__1,d__2), d__2 = abs(d__[7]);
+	if (max(d__1,d__2) > thresh) {
+	    goto L50;
+	}
+
+/*        Accept swap: apply transformation to the entire matrix T. */
+
+	i__1 = *n - *j1 + 1;
+	igraphdlarfx_("L", &c__3, &i__1, u1, &tau1, &t[*j1 + *j1 * t_dim1], ldt, &
+		work[1]);
+	igraphdlarfx_("R", &j4, &c__3, u1, &tau1, &t[*j1 * t_dim1 + 1], ldt, &work[
+		1]);
+	i__1 = *n - *j1 + 1;
+	igraphdlarfx_("L", &c__3, &i__1, u2, &tau2, &t[j2 + *j1 * t_dim1], ldt, &
+		work[1]);
+	igraphdlarfx_("R", &j4, &c__3, u2, &tau2, &t[j2 * t_dim1 + 1], ldt, &work[1]
+		);
+
+	t[j3 + *j1 * t_dim1] = 0.;
+	t[j3 + j2 * t_dim1] = 0.;
+	t[j4 + *j1 * t_dim1] = 0.;
+	t[j4 + j2 * t_dim1] = 0.;
+
+	if (*wantq) {
+
+/*           Accumulate transformation in the matrix Q. */
+
+	    igraphdlarfx_("R", n, &c__3, u1, &tau1, &q[*j1 * q_dim1 + 1], ldq, &
+		    work[1]);
+	    igraphdlarfx_("R", n, &c__3, u2, &tau2, &q[j2 * q_dim1 + 1], ldq, &work[
+		    1]);
+	}
+
+L40:
+
+	if (*n2 == 2) {
+
+/*           Standardize new 2-by-2 block T11 */
+
+	    igraphdlanv2_(&t[*j1 + *j1 * t_dim1], &t[*j1 + j2 * t_dim1], &t[j2 + *
+		    j1 * t_dim1], &t[j2 + j2 * t_dim1], &wr1, &wi1, &wr2, &
+		    wi2, &cs, &sn);
+	    i__1 = *n - *j1 - 1;
+	    igraphdrot_(&i__1, &t[*j1 + (*j1 + 2) * t_dim1], ldt, &t[j2 + (*j1 + 2) 
+		    * t_dim1], ldt, &cs, &sn);
+	    i__1 = *j1 - 1;
+	    igraphdrot_(&i__1, &t[*j1 * t_dim1 + 1], &c__1, &t[j2 * t_dim1 + 1], &
+		    c__1, &cs, &sn);
+	    if (*wantq) {
+		igraphdrot_(n, &q[*j1 * q_dim1 + 1], &c__1, &q[j2 * q_dim1 + 1], &
+			c__1, &cs, &sn);
+	    }
+	}
+
+	if (*n1 == 2) {
+
+/*           Standardize new 2-by-2 block T22 */
+
+	    j3 = *j1 + *n2;
+	    j4 = j3 + 1;
+	    igraphdlanv2_(&t[j3 + j3 * t_dim1], &t[j3 + j4 * t_dim1], &t[j4 + j3 * 
+		    t_dim1], &t[j4 + j4 * t_dim1], &wr1, &wi1, &wr2, &wi2, &
+		    cs, &sn);
+	    if (j3 + 2 <= *n) {
+		i__1 = *n - j3 - 1;
+		igraphdrot_(&i__1, &t[j3 + (j3 + 2) * t_dim1], ldt, &t[j4 + (j3 + 2)
+			 * t_dim1], ldt, &cs, &sn);
+	    }
+	    i__1 = j3 - 1;
+	    igraphdrot_(&i__1, &t[j3 * t_dim1 + 1], &c__1, &t[j4 * t_dim1 + 1], &
+		    c__1, &cs, &sn);
+	    if (*wantq) {
+		igraphdrot_(n, &q[j3 * q_dim1 + 1], &c__1, &q[j4 * q_dim1 + 1], &
+			c__1, &cs, &sn);
+	    }
+	}
+
+    }
+    return 0;
+
+/*     Exit with INFO = 1 if swap was rejected. */
+
+L50:
+    *info = 1;
+    return 0;
+
+/*     End of DLAEXC */
+
+} /* igraphdlaexc_ */
+
diff --git a/igraph/src/dlagtf.c b/igraph/src/dlagtf.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlagtf.c
@@ -0,0 +1,285 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAGTF computes an LU factorization of a matrix T-λI, where T is a general tridiagonal matrix,
+ and λ a scalar, using partial pivoting with row interchanges.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAGTF + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlagtf.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlagtf.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlagtf.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAGTF( N, A, LAMBDA, B, C, TOL, D, IN, INFO )   
+
+         INTEGER            INFO, N   
+         DOUBLE PRECISION   LAMBDA, TOL   
+         INTEGER            IN( * )   
+         DOUBLE PRECISION   A( * ), B( * ), C( * ), D( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAGTF factorizes the matrix (T - lambda*I), where T is an n by n   
+   > tridiagonal matrix and lambda is a scalar, as   
+   >   
+   >    T - lambda*I = PLU,   
+   >   
+   > where P is a permutation matrix, L is a unit lower tridiagonal matrix   
+   > with at most one non-zero sub-diagonal elements per column and U is   
+   > an upper triangular matrix with at most two non-zero super-diagonal   
+   > elements per column.   
+   >   
+   > The factorization is obtained by Gaussian elimination with partial   
+   > pivoting and implicit row scaling.   
+   >   
+   > The parameter LAMBDA is included in the routine so that DLAGTF may   
+   > be used, in conjunction with DLAGTS, to obtain eigenvectors of T by   
+   > inverse iteration.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, A must contain the diagonal elements of T.   
+   >   
+   >          On exit, A is overwritten by the n diagonal elements of the   
+   >          upper triangular matrix U of the factorization of T.   
+   > \endverbatim   
+   >   
+   > \param[in] LAMBDA   
+   > \verbatim   
+   >          LAMBDA is DOUBLE PRECISION   
+   >          On entry, the scalar lambda.   
+   > \endverbatim   
+   >   
+   > \param[in,out] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (N-1)   
+   >          On entry, B must contain the (n-1) super-diagonal elements of   
+   >          T.   
+   >   
+   >          On exit, B is overwritten by the (n-1) super-diagonal   
+   >          elements of the matrix U of the factorization of T.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (N-1)   
+   >          On entry, C must contain the (n-1) sub-diagonal elements of   
+   >          T.   
+   >   
+   >          On exit, C is overwritten by the (n-1) sub-diagonal elements   
+   >          of the matrix L of the factorization of T.   
+   > \endverbatim   
+   >   
+   > \param[in] TOL   
+   > \verbatim   
+   >          TOL is DOUBLE PRECISION   
+   >          On entry, a relative tolerance used to indicate whether or   
+   >          not the matrix (T - lambda*I) is nearly singular. TOL should   
+   >          normally be chose as approximately the largest relative error   
+   >          in the elements of T. For example, if the elements of T are   
+   >          correct to about 4 significant figures, then TOL should be   
+   >          set to about 5*10**(-4). If TOL is supplied as less than eps,   
+   >          where eps is the relative machine precision, then the value   
+   >          eps is used in place of TOL.   
+   > \endverbatim   
+   >   
+   > \param[out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N-2)   
+   >          On exit, D is overwritten by the (n-2) second super-diagonal   
+   >          elements of the matrix U of the factorization of T.   
+   > \endverbatim   
+   >   
+   > \param[out] IN   
+   > \verbatim   
+   >          IN is INTEGER array, dimension (N)   
+   >          On exit, IN contains details of the permutation matrix P. If   
+   >          an interchange occurred at the kth step of the elimination,   
+   >          then IN(k) = 1, otherwise IN(k) = 0. The element IN(n)   
+   >          returns the smallest positive integer j such that   
+   >   
+   >             abs( u(j,j) ).le. norm( (T - lambda*I)(j) )*TOL,   
+   >   
+   >          where norm( A(j) ) denotes the sum of the absolute values of   
+   >          the jth row of the matrix A. If no such j exists then IN(n)   
+   >          is returned as zero. If IN(n) is returned as positive, then a   
+   >          diagonal element of U is small, indicating that   
+   >          (T - lambda*I) is singular or nearly singular,   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0   : successful exit   
+   >          .lt. 0: if INFO = -k, the kth argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdlagtf_(integer *n, doublereal *a, doublereal *lambda, 
+	doublereal *b, doublereal *c__, doublereal *tol, doublereal *d__, 
+	integer *in, integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    integer k;
+    doublereal tl, eps, piv1, piv2, temp, mult, scale1, scale2;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Parameter adjustments */
+    --in;
+    --d__;
+    --c__;
+    --b;
+    --a;
+
+    /* Function Body */
+    *info = 0;
+    if (*n < 0) {
+	*info = -1;
+	i__1 = -(*info);
+	igraphxerbla_("DLAGTF", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+    if (*n == 0) {
+	return 0;
+    }
+
+    a[1] -= *lambda;
+    in[*n] = 0;
+    if (*n == 1) {
+	if (a[1] == 0.) {
+	    in[1] = 1;
+	}
+	return 0;
+    }
+
+    eps = igraphdlamch_("Epsilon");
+
+    tl = max(*tol,eps);
+    scale1 = abs(a[1]) + abs(b[1]);
+    i__1 = *n - 1;
+    for (k = 1; k <= i__1; ++k) {
+	a[k + 1] -= *lambda;
+	scale2 = (d__1 = c__[k], abs(d__1)) + (d__2 = a[k + 1], abs(d__2));
+	if (k < *n - 1) {
+	    scale2 += (d__1 = b[k + 1], abs(d__1));
+	}
+	if (a[k] == 0.) {
+	    piv1 = 0.;
+	} else {
+	    piv1 = (d__1 = a[k], abs(d__1)) / scale1;
+	}
+	if (c__[k] == 0.) {
+	    in[k] = 0;
+	    piv2 = 0.;
+	    scale1 = scale2;
+	    if (k < *n - 1) {
+		d__[k] = 0.;
+	    }
+	} else {
+	    piv2 = (d__1 = c__[k], abs(d__1)) / scale2;
+	    if (piv2 <= piv1) {
+		in[k] = 0;
+		scale1 = scale2;
+		c__[k] /= a[k];
+		a[k + 1] -= c__[k] * b[k];
+		if (k < *n - 1) {
+		    d__[k] = 0.;
+		}
+	    } else {
+		in[k] = 1;
+		mult = a[k] / c__[k];
+		a[k] = c__[k];
+		temp = a[k + 1];
+		a[k + 1] = b[k] - mult * temp;
+		if (k < *n - 1) {
+		    d__[k] = b[k + 1];
+		    b[k + 1] = -mult * d__[k];
+		}
+		b[k] = temp;
+		c__[k] = mult;
+	    }
+	}
+	if (max(piv1,piv2) <= tl && in[*n] == 0) {
+	    in[*n] = k;
+	}
+/* L10: */
+    }
+    if ((d__1 = a[*n], abs(d__1)) <= scale1 * tl && in[*n] == 0) {
+	in[*n] = *n;
+    }
+
+    return 0;
+
+/*     End of DLAGTF */
+
+} /* igraphdlagtf_ */
+
diff --git a/igraph/src/dlagts.c b/igraph/src/dlagts.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlagts.c
@@ -0,0 +1,415 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAGTS solves the system of equations (T-λI)x = y or (T-λI)Tx = y,where T is a general tridia
+gonal matrix and λ a scalar, using the LU factorization computed by slagtf.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAGTS + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlagts.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlagts.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlagts.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAGTS( JOB, N, A, B, C, D, IN, Y, TOL, INFO )   
+
+         INTEGER            INFO, JOB, N   
+         DOUBLE PRECISION   TOL   
+         INTEGER            IN( * )   
+         DOUBLE PRECISION   A( * ), B( * ), C( * ), D( * ), Y( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAGTS may be used to solve one of the systems of equations   
+   >   
+   >    (T - lambda*I)*x = y   or   (T - lambda*I)**T*x = y,   
+   >   
+   > where T is an n by n tridiagonal matrix, for x, following the   
+   > factorization of (T - lambda*I) as   
+   >   
+   >    (T - lambda*I) = P*L*U ,   
+   >   
+   > by routine DLAGTF. The choice of equation to be solved is   
+   > controlled by the argument JOB, and in each case there is an option   
+   > to perturb zero or very small diagonal elements of U, this option   
+   > being intended for use in applications such as inverse iteration.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOB   
+   > \verbatim   
+   >          JOB is INTEGER   
+   >          Specifies the job to be performed by DLAGTS as follows:   
+   >          =  1: The equations  (T - lambda*I)x = y  are to be solved,   
+   >                but diagonal elements of U are not to be perturbed.   
+   >          = -1: The equations  (T - lambda*I)x = y  are to be solved   
+   >                and, if overflow would otherwise occur, the diagonal   
+   >                elements of U are to be perturbed. See argument TOL   
+   >                below.   
+   >          =  2: The equations  (T - lambda*I)**Tx = y  are to be solved,   
+   >                but diagonal elements of U are not to be perturbed.   
+   >          = -2: The equations  (T - lambda*I)**Tx = y  are to be solved   
+   >                and, if overflow would otherwise occur, the diagonal   
+   >                elements of U are to be perturbed. See argument TOL   
+   >                below.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, A must contain the diagonal elements of U as   
+   >          returned from DLAGTF.   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (N-1)   
+   >          On entry, B must contain the first super-diagonal elements of   
+   >          U as returned from DLAGTF.   
+   > \endverbatim   
+   >   
+   > \param[in] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (N-1)   
+   >          On entry, C must contain the sub-diagonal elements of L as   
+   >          returned from DLAGTF.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N-2)   
+   >          On entry, D must contain the second super-diagonal elements   
+   >          of U as returned from DLAGTF.   
+   > \endverbatim   
+   >   
+   > \param[in] IN   
+   > \verbatim   
+   >          IN is INTEGER array, dimension (N)   
+   >          On entry, IN must contain details of the matrix P as returned   
+   >          from DLAGTF.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Y   
+   > \verbatim   
+   >          Y is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the right hand side vector y.   
+   >          On exit, Y is overwritten by the solution vector x.   
+   > \endverbatim   
+   >   
+   > \param[in,out] TOL   
+   > \verbatim   
+   >          TOL is DOUBLE PRECISION   
+   >          On entry, with  JOB .lt. 0, TOL should be the minimum   
+   >          perturbation to be made to very small diagonal elements of U.   
+   >          TOL should normally be chosen as about eps*norm(U), where eps   
+   >          is the relative machine precision, but if TOL is supplied as   
+   >          non-positive, then it is reset to eps*max( abs( u(i,j) ) ).   
+   >          If  JOB .gt. 0  then TOL is not referenced.   
+   >   
+   >          On exit, TOL is changed as described above, only if TOL is   
+   >          non-positive on entry. Otherwise TOL is unchanged.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0   : successful exit   
+   >          .lt. 0: if INFO = -i, the i-th argument had an illegal value   
+   >          .gt. 0: overflow would occur when computing the INFO(th)   
+   >                  element of the solution vector x. This can only occur   
+   >                  when JOB is supplied as positive and either means   
+   >                  that a diagonal element of U is very small, or that   
+   >                  the elements of the right-hand side vector y are very   
+   >                  large.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlagts_(integer *job, integer *n, doublereal *a, 
+	doublereal *b, doublereal *c__, doublereal *d__, integer *in, 
+	doublereal *y, doublereal *tol, integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2, d__3, d__4, d__5;
+
+    /* Builtin functions */
+    double d_sign(doublereal *, doublereal *);
+
+    /* Local variables */
+    integer k;
+    doublereal ak, eps, temp, pert, absak, sfmin;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    doublereal bignum;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --y;
+    --in;
+    --d__;
+    --c__;
+    --b;
+    --a;
+
+    /* Function Body */
+    *info = 0;
+    if (abs(*job) > 2 || *job == 0) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DLAGTS", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+    if (*n == 0) {
+	return 0;
+    }
+
+    eps = igraphdlamch_("Epsilon");
+    sfmin = igraphdlamch_("Safe minimum");
+    bignum = 1. / sfmin;
+
+    if (*job < 0) {
+	if (*tol <= 0.) {
+	    *tol = abs(a[1]);
+	    if (*n > 1) {
+/* Computing MAX */
+		d__1 = *tol, d__2 = abs(a[2]), d__1 = max(d__1,d__2), d__2 = 
+			abs(b[1]);
+		*tol = max(d__1,d__2);
+	    }
+	    i__1 = *n;
+	    for (k = 3; k <= i__1; ++k) {
+/* Computing MAX */
+		d__4 = *tol, d__5 = (d__1 = a[k], abs(d__1)), d__4 = max(d__4,
+			d__5), d__5 = (d__2 = b[k - 1], abs(d__2)), d__4 = 
+			max(d__4,d__5), d__5 = (d__3 = d__[k - 2], abs(d__3));
+		*tol = max(d__4,d__5);
+/* L10: */
+	    }
+	    *tol *= eps;
+	    if (*tol == 0.) {
+		*tol = eps;
+	    }
+	}
+    }
+
+    if (abs(*job) == 1) {
+	i__1 = *n;
+	for (k = 2; k <= i__1; ++k) {
+	    if (in[k - 1] == 0) {
+		y[k] -= c__[k - 1] * y[k - 1];
+	    } else {
+		temp = y[k - 1];
+		y[k - 1] = y[k];
+		y[k] = temp - c__[k - 1] * y[k];
+	    }
+/* L20: */
+	}
+	if (*job == 1) {
+	    for (k = *n; k >= 1; --k) {
+		if (k <= *n - 2) {
+		    temp = y[k] - b[k] * y[k + 1] - d__[k] * y[k + 2];
+		} else if (k == *n - 1) {
+		    temp = y[k] - b[k] * y[k + 1];
+		} else {
+		    temp = y[k];
+		}
+		ak = a[k];
+		absak = abs(ak);
+		if (absak < 1.) {
+		    if (absak < sfmin) {
+			if (absak == 0. || abs(temp) * sfmin > absak) {
+			    *info = k;
+			    return 0;
+			} else {
+			    temp *= bignum;
+			    ak *= bignum;
+			}
+		    } else if (abs(temp) > absak * bignum) {
+			*info = k;
+			return 0;
+		    }
+		}
+		y[k] = temp / ak;
+/* L30: */
+	    }
+	} else {
+	    for (k = *n; k >= 1; --k) {
+		if (k <= *n - 2) {
+		    temp = y[k] - b[k] * y[k + 1] - d__[k] * y[k + 2];
+		} else if (k == *n - 1) {
+		    temp = y[k] - b[k] * y[k + 1];
+		} else {
+		    temp = y[k];
+		}
+		ak = a[k];
+		pert = d_sign(tol, &ak);
+L40:
+		absak = abs(ak);
+		if (absak < 1.) {
+		    if (absak < sfmin) {
+			if (absak == 0. || abs(temp) * sfmin > absak) {
+			    ak += pert;
+			    pert *= 2;
+			    goto L40;
+			} else {
+			    temp *= bignum;
+			    ak *= bignum;
+			}
+		    } else if (abs(temp) > absak * bignum) {
+			ak += pert;
+			pert *= 2;
+			goto L40;
+		    }
+		}
+		y[k] = temp / ak;
+/* L50: */
+	    }
+	}
+    } else {
+
+/*        Come to here if  JOB = 2 or -2 */
+
+	if (*job == 2) {
+	    i__1 = *n;
+	    for (k = 1; k <= i__1; ++k) {
+		if (k >= 3) {
+		    temp = y[k] - b[k - 1] * y[k - 1] - d__[k - 2] * y[k - 2];
+		} else if (k == 2) {
+		    temp = y[k] - b[k - 1] * y[k - 1];
+		} else {
+		    temp = y[k];
+		}
+		ak = a[k];
+		absak = abs(ak);
+		if (absak < 1.) {
+		    if (absak < sfmin) {
+			if (absak == 0. || abs(temp) * sfmin > absak) {
+			    *info = k;
+			    return 0;
+			} else {
+			    temp *= bignum;
+			    ak *= bignum;
+			}
+		    } else if (abs(temp) > absak * bignum) {
+			*info = k;
+			return 0;
+		    }
+		}
+		y[k] = temp / ak;
+/* L60: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (k = 1; k <= i__1; ++k) {
+		if (k >= 3) {
+		    temp = y[k] - b[k - 1] * y[k - 1] - d__[k - 2] * y[k - 2];
+		} else if (k == 2) {
+		    temp = y[k] - b[k - 1] * y[k - 1];
+		} else {
+		    temp = y[k];
+		}
+		ak = a[k];
+		pert = d_sign(tol, &ak);
+L70:
+		absak = abs(ak);
+		if (absak < 1.) {
+		    if (absak < sfmin) {
+			if (absak == 0. || abs(temp) * sfmin > absak) {
+			    ak += pert;
+			    pert *= 2;
+			    goto L70;
+			} else {
+			    temp *= bignum;
+			    ak *= bignum;
+			}
+		    } else if (abs(temp) > absak * bignum) {
+			ak += pert;
+			pert *= 2;
+			goto L70;
+		    }
+		}
+		y[k] = temp / ak;
+/* L80: */
+	    }
+	}
+
+	for (k = *n; k >= 2; --k) {
+	    if (in[k - 1] == 0) {
+		y[k - 1] -= c__[k - 1] * y[k];
+	    } else {
+		temp = y[k - 1];
+		y[k - 1] = y[k];
+		y[k] = temp - c__[k - 1] * y[k];
+	    }
+/* L90: */
+	}
+    }
+
+/*     End of DLAGTS */
+
+    return 0;
+} /* igraphdlagts_ */
+
diff --git a/igraph/src/dlahqr.c b/igraph/src/dlahqr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlahqr.c
@@ -0,0 +1,711 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DLAHQR computes the eigenvalues and Schur factorization of an upper Hessenberg matrix, using th
+e double-shift/single-shift QR algorithm.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAHQR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlahqr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlahqr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlahqr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAHQR( WANTT, WANTZ, N, ILO, IHI, H, LDH, WR, WI,   
+                            ILOZ, IHIZ, Z, LDZ, INFO )   
+
+         INTEGER            IHI, IHIZ, ILO, ILOZ, INFO, LDH, LDZ, N   
+         LOGICAL            WANTT, WANTZ   
+         DOUBLE PRECISION   H( LDH, * ), WI( * ), WR( * ), Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >    DLAHQR is an auxiliary routine called by DHSEQR to update the   
+   >    eigenvalues and Schur decomposition already computed by DHSEQR, by   
+   >    dealing with the Hessenberg submatrix in rows and columns ILO to   
+   >    IHI.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] WANTT   
+   > \verbatim   
+   >          WANTT is LOGICAL   
+   >          = .TRUE. : the full Schur form T is required;   
+   >          = .FALSE.: only eigenvalues are required.   
+   > \endverbatim   
+   >   
+   > \param[in] WANTZ   
+   > \verbatim   
+   >          WANTZ is LOGICAL   
+   >          = .TRUE. : the matrix of Schur vectors Z is required;   
+   >          = .FALSE.: Schur vectors are not required.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix H.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >          It is assumed that H is already upper quasi-triangular in   
+   >          rows and columns IHI+1:N, and that H(ILO,ILO-1) = 0 (unless   
+   >          ILO = 1). DLAHQR works primarily with the Hessenberg   
+   >          submatrix in rows and columns ILO to IHI, but applies   
+   >          transformations to all of H if WANTT is .TRUE..   
+   >          1 <= ILO <= max(1,IHI); IHI <= N.   
+   > \endverbatim   
+   >   
+   > \param[in,out] H   
+   > \verbatim   
+   >          H is DOUBLE PRECISION array, dimension (LDH,N)   
+   >          On entry, the upper Hessenberg matrix H.   
+   >          On exit, if INFO is zero and if WANTT is .TRUE., H is upper   
+   >          quasi-triangular in rows and columns ILO:IHI, with any   
+   >          2-by-2 diagonal blocks in standard form. If INFO is zero   
+   >          and WANTT is .FALSE., the contents of H are unspecified on   
+   >          exit.  The output state of H if INFO is nonzero is given   
+   >          below under the description of INFO.   
+   > \endverbatim   
+   >   
+   > \param[in] LDH   
+   > \verbatim   
+   >          LDH is INTEGER   
+   >          The leading dimension of the array H. LDH >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] WR   
+   > \verbatim   
+   >          WR is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] WI   
+   > \verbatim   
+   >          WI is DOUBLE PRECISION array, dimension (N)   
+   >          The real and imaginary parts, respectively, of the computed   
+   >          eigenvalues ILO to IHI are stored in the corresponding   
+   >          elements of WR and WI. If two eigenvalues are computed as a   
+   >          complex conjugate pair, they are stored in consecutive   
+   >          elements of WR and WI, say the i-th and (i+1)th, with   
+   >          WI(i) > 0 and WI(i+1) < 0. If WANTT is .TRUE., the   
+   >          eigenvalues are stored in the same order as on the diagonal   
+   >          of the Schur form returned in H, with WR(i) = H(i,i), and, if   
+   >          H(i:i+1,i:i+1) is a 2-by-2 diagonal block,   
+   >          WI(i) = sqrt(H(i+1,i)*H(i,i+1)) and WI(i+1) = -WI(i).   
+   > \endverbatim   
+   >   
+   > \param[in] ILOZ   
+   > \verbatim   
+   >          ILOZ is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHIZ   
+   > \verbatim   
+   >          IHIZ is INTEGER   
+   >          Specify the rows of Z to which transformations must be   
+   >          applied if WANTZ is .TRUE..   
+   >          1 <= ILOZ <= ILO; IHI <= IHIZ <= N.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ,N)   
+   >          If WANTZ is .TRUE., on entry Z must contain the current   
+   >          matrix Z of transformations accumulated by DHSEQR, and on   
+   >          exit Z has been updated; transformations are applied only to   
+   >          the submatrix Z(ILOZ:IHIZ,ILO:IHI).   
+   >          If WANTZ is .FALSE., Z is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >          The leading dimension of the array Z. LDZ >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >           =   0: successful exit   
+   >          .GT. 0: If INFO = i, DLAHQR failed to compute all the   
+   >                  eigenvalues ILO to IHI in a total of 30 iterations   
+   >                  per eigenvalue; elements i+1:ihi of WR and WI   
+   >                  contain those eigenvalues which have been   
+   >                  successfully computed.   
+   >   
+   >                  If INFO .GT. 0 and WANTT is .FALSE., then on exit,   
+   >                  the remaining unconverged eigenvalues are the   
+   >                  eigenvalues of the upper Hessenberg matrix rows   
+   >                  and columns ILO thorugh INFO of the final, output   
+   >                  value of H.   
+   >   
+   >                  If INFO .GT. 0 and WANTT is .TRUE., then on exit   
+   >          (*)       (initial value of H)*U  = U*(final value of H)   
+   >                  where U is an orthognal matrix.    The final   
+   >                  value of H is upper Hessenberg and triangular in   
+   >                  rows and columns INFO+1 through IHI.   
+   >   
+   >                  If INFO .GT. 0 and WANTZ is .TRUE., then on exit   
+   >                      (final value of Z)  = (initial value of Z)*U   
+   >                  where U is the orthogonal matrix in (*)   
+   >                  (regardless of the value of WANTT.)   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >     02-96 Based on modifications by   
+   >     David Day, Sandia National Laboratory, USA   
+   >   
+   >     12-04 Further modifications by   
+   >     Ralph Byers, University of Kansas, USA   
+   >     This is a modified version of DLAHQR from LAPACK version 3.0.   
+   >     It is (1) more robust against overflow and underflow and   
+   >     (2) adopts the more conservative Ahues & Tisseur stopping   
+   >     criterion (LAWN 122, 1997).   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlahqr_(logical *wantt, logical *wantz, integer *n, 
+	integer *ilo, integer *ihi, doublereal *h__, integer *ldh, doublereal 
+	*wr, doublereal *wi, integer *iloz, integer *ihiz, doublereal *z__, 
+	integer *ldz, integer *info)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, z_dim1, z_offset, i__1, i__2, i__3;
+    doublereal d__1, d__2, d__3, d__4;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j, k, l, m;
+    doublereal s, v[3];
+    integer i1, i2;
+    doublereal t1, t2, t3, v2, v3, aa, ab, ba, bb, h11, h12, h21, h22, cs;
+    integer nh;
+    doublereal sn;
+    integer nr;
+    doublereal tr;
+    integer nz;
+    doublereal det, h21s;
+    integer its;
+    doublereal ulp, sum, tst, rt1i, rt2i, rt1r, rt2r;
+    extern /* Subroutine */ int igraphdrot_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *), igraphdcopy_(
+	    integer *, doublereal *, integer *, doublereal *, integer *), 
+	    igraphdlanv2_(doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *), igraphdlabad_(doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlarfg_(integer *, doublereal *, doublereal *,
+	     integer *, doublereal *);
+    doublereal safmin, safmax, rtdisc, smlnum;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =========================================================   
+
+
+       Parameter adjustments */
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    --wr;
+    --wi;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+
+    /* Function Body */
+    *info = 0;
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+    if (*ilo == *ihi) {
+	wr[*ilo] = h__[*ilo + *ilo * h_dim1];
+	wi[*ilo] = 0.;
+	return 0;
+    }
+
+/*     ==== clear out the trash ==== */
+    i__1 = *ihi - 3;
+    for (j = *ilo; j <= i__1; ++j) {
+	h__[j + 2 + j * h_dim1] = 0.;
+	h__[j + 3 + j * h_dim1] = 0.;
+/* L10: */
+    }
+    if (*ilo <= *ihi - 2) {
+	h__[*ihi + (*ihi - 2) * h_dim1] = 0.;
+    }
+
+    nh = *ihi - *ilo + 1;
+    nz = *ihiz - *iloz + 1;
+
+/*     Set machine-dependent constants for the stopping criterion. */
+
+    safmin = igraphdlamch_("SAFE MINIMUM");
+    safmax = 1. / safmin;
+    igraphdlabad_(&safmin, &safmax);
+    ulp = igraphdlamch_("PRECISION");
+    smlnum = safmin * ((doublereal) nh / ulp);
+
+/*     I1 and I2 are the indices of the first row and last column of H   
+       to which transformations must be applied. If eigenvalues only are   
+       being computed, I1 and I2 are set inside the main loop. */
+
+    if (*wantt) {
+	i1 = 1;
+	i2 = *n;
+    }
+
+/*     The main loop begins here. I is the loop index and decreases from   
+       IHI to ILO in steps of 1 or 2. Each iteration of the loop works   
+       with the active submatrix in rows and columns L to I.   
+       Eigenvalues I+1 to IHI have already converged. Either L = ILO or   
+       H(L,L-1) is negligible so that the matrix splits. */
+
+    i__ = *ihi;
+L20:
+    l = *ilo;
+    if (i__ < *ilo) {
+	goto L160;
+    }
+
+/*     Perform QR iterations on rows and columns ILO to I until a   
+       submatrix of order 1 or 2 splits off at the bottom because a   
+       subdiagonal element has become negligible. */
+
+    for (its = 0; its <= 30; ++its) {
+
+/*        Look for a single small subdiagonal element. */
+
+	i__1 = l + 1;
+	for (k = i__; k >= i__1; --k) {
+	    if ((d__1 = h__[k + (k - 1) * h_dim1], abs(d__1)) <= smlnum) {
+		goto L40;
+	    }
+	    tst = (d__1 = h__[k - 1 + (k - 1) * h_dim1], abs(d__1)) + (d__2 = 
+		    h__[k + k * h_dim1], abs(d__2));
+	    if (tst == 0.) {
+		if (k - 2 >= *ilo) {
+		    tst += (d__1 = h__[k - 1 + (k - 2) * h_dim1], abs(d__1));
+		}
+		if (k + 1 <= *ihi) {
+		    tst += (d__1 = h__[k + 1 + k * h_dim1], abs(d__1));
+		}
+	    }
+/*           ==== The following is a conservative small subdiagonal   
+             .    deflation  criterion due to Ahues & Tisseur (LAWN 122,   
+             .    1997). It has better mathematical foundation and   
+             .    improves accuracy in some cases.  ==== */
+	    if ((d__1 = h__[k + (k - 1) * h_dim1], abs(d__1)) <= ulp * tst) {
+/* Computing MAX */
+		d__3 = (d__1 = h__[k + (k - 1) * h_dim1], abs(d__1)), d__4 = (
+			d__2 = h__[k - 1 + k * h_dim1], abs(d__2));
+		ab = max(d__3,d__4);
+/* Computing MIN */
+		d__3 = (d__1 = h__[k + (k - 1) * h_dim1], abs(d__1)), d__4 = (
+			d__2 = h__[k - 1 + k * h_dim1], abs(d__2));
+		ba = min(d__3,d__4);
+/* Computing MAX */
+		d__3 = (d__1 = h__[k + k * h_dim1], abs(d__1)), d__4 = (d__2 =
+			 h__[k - 1 + (k - 1) * h_dim1] - h__[k + k * h_dim1], 
+			abs(d__2));
+		aa = max(d__3,d__4);
+/* Computing MIN */
+		d__3 = (d__1 = h__[k + k * h_dim1], abs(d__1)), d__4 = (d__2 =
+			 h__[k - 1 + (k - 1) * h_dim1] - h__[k + k * h_dim1], 
+			abs(d__2));
+		bb = min(d__3,d__4);
+		s = aa + ab;
+/* Computing MAX */
+		d__1 = smlnum, d__2 = ulp * (bb * (aa / s));
+		if (ba * (ab / s) <= max(d__1,d__2)) {
+		    goto L40;
+		}
+	    }
+/* L30: */
+	}
+L40:
+	l = k;
+	if (l > *ilo) {
+
+/*           H(L,L-1) is negligible */
+
+	    h__[l + (l - 1) * h_dim1] = 0.;
+	}
+
+/*        Exit from loop if a submatrix of order 1 or 2 has split off. */
+
+	if (l >= i__ - 1) {
+	    goto L150;
+	}
+
+/*        Now the active submatrix is in rows and columns L to I. If   
+          eigenvalues only are being computed, only the active submatrix   
+          need be transformed. */
+
+	if (! (*wantt)) {
+	    i1 = l;
+	    i2 = i__;
+	}
+
+	if (its == 10) {
+
+/*           Exceptional shift. */
+
+	    s = (d__1 = h__[l + 1 + l * h_dim1], abs(d__1)) + (d__2 = h__[l + 
+		    2 + (l + 1) * h_dim1], abs(d__2));
+	    h11 = s * .75 + h__[l + l * h_dim1];
+	    h12 = s * -.4375;
+	    h21 = s;
+	    h22 = h11;
+	} else if (its == 20) {
+
+/*           Exceptional shift. */
+
+	    s = (d__1 = h__[i__ + (i__ - 1) * h_dim1], abs(d__1)) + (d__2 = 
+		    h__[i__ - 1 + (i__ - 2) * h_dim1], abs(d__2));
+	    h11 = s * .75 + h__[i__ + i__ * h_dim1];
+	    h12 = s * -.4375;
+	    h21 = s;
+	    h22 = h11;
+	} else {
+
+/*           Prepare to use Francis' double shift   
+             (i.e. 2nd degree generalized Rayleigh quotient) */
+
+	    h11 = h__[i__ - 1 + (i__ - 1) * h_dim1];
+	    h21 = h__[i__ + (i__ - 1) * h_dim1];
+	    h12 = h__[i__ - 1 + i__ * h_dim1];
+	    h22 = h__[i__ + i__ * h_dim1];
+	}
+	s = abs(h11) + abs(h12) + abs(h21) + abs(h22);
+	if (s == 0.) {
+	    rt1r = 0.;
+	    rt1i = 0.;
+	    rt2r = 0.;
+	    rt2i = 0.;
+	} else {
+	    h11 /= s;
+	    h21 /= s;
+	    h12 /= s;
+	    h22 /= s;
+	    tr = (h11 + h22) / 2.;
+	    det = (h11 - tr) * (h22 - tr) - h12 * h21;
+	    rtdisc = sqrt((abs(det)));
+	    if (det >= 0.) {
+
+/*              ==== complex conjugate shifts ==== */
+
+		rt1r = tr * s;
+		rt2r = rt1r;
+		rt1i = rtdisc * s;
+		rt2i = -rt1i;
+	    } else {
+
+/*              ==== real shifts (use only one of them)  ==== */
+
+		rt1r = tr + rtdisc;
+		rt2r = tr - rtdisc;
+		if ((d__1 = rt1r - h22, abs(d__1)) <= (d__2 = rt2r - h22, abs(
+			d__2))) {
+		    rt1r *= s;
+		    rt2r = rt1r;
+		} else {
+		    rt2r *= s;
+		    rt1r = rt2r;
+		}
+		rt1i = 0.;
+		rt2i = 0.;
+	    }
+	}
+
+/*        Look for two consecutive small subdiagonal elements. */
+
+	i__1 = l;
+	for (m = i__ - 2; m >= i__1; --m) {
+/*           Determine the effect of starting the double-shift QR   
+             iteration at row M, and see if this would make H(M,M-1)   
+             negligible.  (The following uses scaling to avoid   
+             overflows and most underflows.) */
+
+	    h21s = h__[m + 1 + m * h_dim1];
+	    s = (d__1 = h__[m + m * h_dim1] - rt2r, abs(d__1)) + abs(rt2i) + 
+		    abs(h21s);
+	    h21s = h__[m + 1 + m * h_dim1] / s;
+	    v[0] = h21s * h__[m + (m + 1) * h_dim1] + (h__[m + m * h_dim1] - 
+		    rt1r) * ((h__[m + m * h_dim1] - rt2r) / s) - rt1i * (rt2i 
+		    / s);
+	    v[1] = h21s * (h__[m + m * h_dim1] + h__[m + 1 + (m + 1) * h_dim1]
+		     - rt1r - rt2r);
+	    v[2] = h21s * h__[m + 2 + (m + 1) * h_dim1];
+	    s = abs(v[0]) + abs(v[1]) + abs(v[2]);
+	    v[0] /= s;
+	    v[1] /= s;
+	    v[2] /= s;
+	    if (m == l) {
+		goto L60;
+	    }
+	    if ((d__1 = h__[m + (m - 1) * h_dim1], abs(d__1)) * (abs(v[1]) + 
+		    abs(v[2])) <= ulp * abs(v[0]) * ((d__2 = h__[m - 1 + (m - 
+		    1) * h_dim1], abs(d__2)) + (d__3 = h__[m + m * h_dim1], 
+		    abs(d__3)) + (d__4 = h__[m + 1 + (m + 1) * h_dim1], abs(
+		    d__4)))) {
+		goto L60;
+	    }
+/* L50: */
+	}
+L60:
+
+/*        Double-shift QR step */
+
+	i__1 = i__ - 1;
+	for (k = m; k <= i__1; ++k) {
+
+/*           The first iteration of this loop determines a reflection G   
+             from the vector V and applies it from left and right to H,   
+             thus creating a nonzero bulge below the subdiagonal.   
+
+             Each subsequent iteration determines a reflection G to   
+             restore the Hessenberg form in the (K-1)th column, and thus   
+             chases the bulge one step toward the bottom of the active   
+             submatrix. NR is the order of G.   
+
+   Computing MIN */
+	    i__2 = 3, i__3 = i__ - k + 1;
+	    nr = min(i__2,i__3);
+	    if (k > m) {
+		igraphdcopy_(&nr, &h__[k + (k - 1) * h_dim1], &c__1, v, &c__1);
+	    }
+	    igraphdlarfg_(&nr, v, &v[1], &c__1, &t1);
+	    if (k > m) {
+		h__[k + (k - 1) * h_dim1] = v[0];
+		h__[k + 1 + (k - 1) * h_dim1] = 0.;
+		if (k < i__ - 1) {
+		    h__[k + 2 + (k - 1) * h_dim1] = 0.;
+		}
+	    } else if (m > l) {
+/*               ==== Use the following instead of   
+                 .    H( K, K-1 ) = -H( K, K-1 ) to   
+                 .    avoid a bug when v(2) and v(3)   
+                 .    underflow. ==== */
+		h__[k + (k - 1) * h_dim1] *= 1. - t1;
+	    }
+	    v2 = v[1];
+	    t2 = t1 * v2;
+	    if (nr == 3) {
+		v3 = v[2];
+		t3 = t1 * v3;
+
+/*              Apply G from the left to transform the rows of the matrix   
+                in columns K to I2. */
+
+		i__2 = i2;
+		for (j = k; j <= i__2; ++j) {
+		    sum = h__[k + j * h_dim1] + v2 * h__[k + 1 + j * h_dim1] 
+			    + v3 * h__[k + 2 + j * h_dim1];
+		    h__[k + j * h_dim1] -= sum * t1;
+		    h__[k + 1 + j * h_dim1] -= sum * t2;
+		    h__[k + 2 + j * h_dim1] -= sum * t3;
+/* L70: */
+		}
+
+/*              Apply G from the right to transform the columns of the   
+                matrix in rows I1 to min(K+3,I).   
+
+   Computing MIN */
+		i__3 = k + 3;
+		i__2 = min(i__3,i__);
+		for (j = i1; j <= i__2; ++j) {
+		    sum = h__[j + k * h_dim1] + v2 * h__[j + (k + 1) * h_dim1]
+			     + v3 * h__[j + (k + 2) * h_dim1];
+		    h__[j + k * h_dim1] -= sum * t1;
+		    h__[j + (k + 1) * h_dim1] -= sum * t2;
+		    h__[j + (k + 2) * h_dim1] -= sum * t3;
+/* L80: */
+		}
+
+		if (*wantz) {
+
+/*                 Accumulate transformations in the matrix Z */
+
+		    i__2 = *ihiz;
+		    for (j = *iloz; j <= i__2; ++j) {
+			sum = z__[j + k * z_dim1] + v2 * z__[j + (k + 1) * 
+				z_dim1] + v3 * z__[j + (k + 2) * z_dim1];
+			z__[j + k * z_dim1] -= sum * t1;
+			z__[j + (k + 1) * z_dim1] -= sum * t2;
+			z__[j + (k + 2) * z_dim1] -= sum * t3;
+/* L90: */
+		    }
+		}
+	    } else if (nr == 2) {
+
+/*              Apply G from the left to transform the rows of the matrix   
+                in columns K to I2. */
+
+		i__2 = i2;
+		for (j = k; j <= i__2; ++j) {
+		    sum = h__[k + j * h_dim1] + v2 * h__[k + 1 + j * h_dim1];
+		    h__[k + j * h_dim1] -= sum * t1;
+		    h__[k + 1 + j * h_dim1] -= sum * t2;
+/* L100: */
+		}
+
+/*              Apply G from the right to transform the columns of the   
+                matrix in rows I1 to min(K+3,I). */
+
+		i__2 = i__;
+		for (j = i1; j <= i__2; ++j) {
+		    sum = h__[j + k * h_dim1] + v2 * h__[j + (k + 1) * h_dim1]
+			    ;
+		    h__[j + k * h_dim1] -= sum * t1;
+		    h__[j + (k + 1) * h_dim1] -= sum * t2;
+/* L110: */
+		}
+
+		if (*wantz) {
+
+/*                 Accumulate transformations in the matrix Z */
+
+		    i__2 = *ihiz;
+		    for (j = *iloz; j <= i__2; ++j) {
+			sum = z__[j + k * z_dim1] + v2 * z__[j + (k + 1) * 
+				z_dim1];
+			z__[j + k * z_dim1] -= sum * t1;
+			z__[j + (k + 1) * z_dim1] -= sum * t2;
+/* L120: */
+		    }
+		}
+	    }
+/* L130: */
+	}
+
+/* L140: */
+    }
+
+/*     Failure to converge in remaining number of iterations */
+
+    *info = i__;
+    return 0;
+
+L150:
+
+    if (l == i__) {
+
+/*        H(I,I-1) is negligible: one eigenvalue has converged. */
+
+	wr[i__] = h__[i__ + i__ * h_dim1];
+	wi[i__] = 0.;
+    } else if (l == i__ - 1) {
+
+/*        H(I-1,I-2) is negligible: a pair of eigenvalues have converged.   
+
+          Transform the 2-by-2 submatrix to standard Schur form,   
+          and compute and store the eigenvalues. */
+
+	igraphdlanv2_(&h__[i__ - 1 + (i__ - 1) * h_dim1], &h__[i__ - 1 + i__ * 
+		h_dim1], &h__[i__ + (i__ - 1) * h_dim1], &h__[i__ + i__ * 
+		h_dim1], &wr[i__ - 1], &wi[i__ - 1], &wr[i__], &wi[i__], &cs, 
+		&sn);
+
+	if (*wantt) {
+
+/*           Apply the transformation to the rest of H. */
+
+	    if (i2 > i__) {
+		i__1 = i2 - i__;
+		igraphdrot_(&i__1, &h__[i__ - 1 + (i__ + 1) * h_dim1], ldh, &h__[
+			i__ + (i__ + 1) * h_dim1], ldh, &cs, &sn);
+	    }
+	    i__1 = i__ - i1 - 1;
+	    igraphdrot_(&i__1, &h__[i1 + (i__ - 1) * h_dim1], &c__1, &h__[i1 + i__ *
+		     h_dim1], &c__1, &cs, &sn);
+	}
+	if (*wantz) {
+
+/*           Apply the transformation to Z. */
+
+	    igraphdrot_(&nz, &z__[*iloz + (i__ - 1) * z_dim1], &c__1, &z__[*iloz + 
+		    i__ * z_dim1], &c__1, &cs, &sn);
+	}
+    }
+
+/*     return to start of the main loop with new value of I. */
+
+    i__ = l - 1;
+    goto L20;
+
+L160:
+    return 0;
+
+/*     End of DLAHQR */
+
+} /* igraphdlahqr_ */
+
diff --git a/igraph/src/dlahr2.c b/igraph/src/dlahr2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlahr2.c
@@ -0,0 +1,392 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b4 = -1.;
+static doublereal c_b5 = 1.;
+static integer c__1 = 1;
+static doublereal c_b38 = 0.;
+
+/* > \brief \b DLAHR2 reduces the specified number of first columns of a general rectangular matrix A so that 
+elements below the specified subdiagonal are zero, and returns auxiliary matrices which are needed to 
+apply the transformation to the unreduced part   
+   of A.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAHR2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlahr2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlahr2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlahr2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAHR2( N, K, NB, A, LDA, TAU, T, LDT, Y, LDY )   
+
+         INTEGER            K, LDA, LDT, LDY, N, NB   
+         DOUBLE PRECISION  A( LDA, * ), T( LDT, NB ), TAU( NB ),   
+        $                   Y( LDY, NB )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAHR2 reduces the first NB columns of A real general n-BY-(n-k+1)   
+   > matrix A so that elements below the k-th subdiagonal are zero. The   
+   > reduction is performed by an orthogonal similarity transformation   
+   > Q**T * A * Q. The routine returns the matrices V and T which determine   
+   > Q as a block reflector I - V*T*V**T, and also the matrix Y = A * V * T.   
+   >   
+   > This is an auxiliary routine called by DGEHRD.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] K   
+   > \verbatim   
+   >          K is INTEGER   
+   >          The offset for the reduction. Elements below the k-th   
+   >          subdiagonal in the first NB columns are reduced to zero.   
+   >          K < N.   
+   > \endverbatim   
+   >   
+   > \param[in] NB   
+   > \verbatim   
+   >          NB is INTEGER   
+   >          The number of columns to be reduced.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N-K+1)   
+   >          On entry, the n-by-(n-k+1) general matrix A.   
+   >          On exit, the elements on and above the k-th subdiagonal in   
+   >          the first NB columns are overwritten with the corresponding   
+   >          elements of the reduced matrix; the elements below the k-th   
+   >          subdiagonal, with the array TAU, represent the matrix Q as a   
+   >          product of elementary reflectors. The other columns of A are   
+   >          unchanged. See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (NB)   
+   >          The scalar factors of the elementary reflectors. See Further   
+   >          Details.   
+   > \endverbatim   
+   >   
+   > \param[out] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,NB)   
+   >          The upper triangular matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is INTEGER   
+   >          The leading dimension of the array T.  LDT >= NB.   
+   > \endverbatim   
+   >   
+   > \param[out] Y   
+   > \verbatim   
+   >          Y is DOUBLE PRECISION array, dimension (LDY,NB)   
+   >          The n-by-nb matrix Y.   
+   > \endverbatim   
+   >   
+   > \param[in] LDY   
+   > \verbatim   
+   >          LDY is INTEGER   
+   >          The leading dimension of the array Y. LDY >= N.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The matrix Q is represented as a product of nb elementary reflectors   
+   >   
+   >     Q = H(1) H(2) . . . H(nb).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(1:i+k-1) = 0, v(i+k) = 1; v(i+k+1:n) is stored on exit in   
+   >  A(i+k+1:n,i), and tau in TAU(i).   
+   >   
+   >  The elements of the vectors v together form the (n-k+1)-by-nb matrix   
+   >  V which is needed, with T and Y, to apply the transformation to the   
+   >  unreduced part of the matrix, using an update of the form:   
+   >  A := (I - V*T*V**T) * (A - Y*V**T).   
+   >   
+   >  The contents of A on exit are illustrated by the following example   
+   >  with n = 7, k = 3 and nb = 2:   
+   >   
+   >     ( a   a   a   a   a )   
+   >     ( a   a   a   a   a )   
+   >     ( a   a   a   a   a )   
+   >     ( h   h   a   a   a )   
+   >     ( v1  h   a   a   a )   
+   >     ( v1  v2  a   a   a )   
+   >     ( v1  v2  a   a   a )   
+   >   
+   >  where a denotes an element of the original matrix A, h denotes a   
+   >  modified element of the upper Hessenberg matrix H, and vi denotes an   
+   >  element of the vector defining H(i).   
+   >   
+   >  This subroutine is a slight modification of LAPACK-3.0's DLAHRD   
+   >  incorporating improvements proposed by Quintana-Orti and Van de   
+   >  Gejin. Note that the entries of A(1:K,2:NB) differ from those   
+   >  returned by the original LAPACK-3.0's DLAHRD routine. (This   
+   >  subroutine is not backward compatible with LAPACK-3.0's DLAHRD.)   
+   > \endverbatim   
+
+   > \par References:   
+    ================   
+   >   
+   >  Gregorio Quintana-Orti and Robert van de Geijn, "Improving the   
+   >  performance of reduction to Hessenberg form," ACM Transactions on   
+   >  Mathematical Software, 32(2):180-194, June 2006.   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlahr2_(integer *n, integer *k, integer *nb, doublereal *
+	a, integer *lda, doublereal *tau, doublereal *t, integer *ldt, 
+	doublereal *y, integer *ldy)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, t_dim1, t_offset, y_dim1, y_offset, i__1, i__2, 
+	    i__3;
+    doublereal d__1;
+
+    /* Local variables */
+    integer i__;
+    doublereal ei;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *), igraphdgemm_(char *, char *, integer *, integer *, integer *
+	    , doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *), igraphdgemv_(
+	    char *, integer *, integer *, doublereal *, doublereal *, integer 
+	    *, doublereal *, integer *, doublereal *, doublereal *, integer *), igraphdcopy_(integer *, doublereal *, integer *, doublereal *,
+	     integer *), igraphdtrmm_(char *, char *, char *, char *, integer *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *), igraphdaxpy_(integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *), 
+	    igraphdtrmv_(char *, char *, char *, integer *, doublereal *, integer *,
+	     doublereal *, integer *), igraphdlarfg_(
+	    integer *, doublereal *, doublereal *, integer *, doublereal *), 
+	    igraphdlacpy_(char *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Quick return if possible   
+
+       Parameter adjustments */
+    --tau;
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    y_dim1 = *ldy;
+    y_offset = 1 + y_dim1;
+    y -= y_offset;
+
+    /* Function Body */
+    if (*n <= 1) {
+	return 0;
+    }
+
+    i__1 = *nb;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	if (i__ > 1) {
+
+/*           Update A(K+1:N,I)   
+
+             Update I-th column of A - Y * V**T */
+
+	    i__2 = *n - *k;
+	    i__3 = i__ - 1;
+	    igraphdgemv_("NO TRANSPOSE", &i__2, &i__3, &c_b4, &y[*k + 1 + y_dim1], 
+		    ldy, &a[*k + i__ - 1 + a_dim1], lda, &c_b5, &a[*k + 1 + 
+		    i__ * a_dim1], &c__1);
+
+/*           Apply I - V * T**T * V**T to this column (call it b) from the   
+             left, using the last column of T as workspace   
+
+             Let  V = ( V1 )   and   b = ( b1 )   (first I-1 rows)   
+                      ( V2 )             ( b2 )   
+
+             where V1 is unit lower triangular   
+
+             w := V1**T * b1 */
+
+	    i__2 = i__ - 1;
+	    igraphdcopy_(&i__2, &a[*k + 1 + i__ * a_dim1], &c__1, &t[*nb * t_dim1 + 
+		    1], &c__1);
+	    i__2 = i__ - 1;
+	    igraphdtrmv_("Lower", "Transpose", "UNIT", &i__2, &a[*k + 1 + a_dim1], 
+		    lda, &t[*nb * t_dim1 + 1], &c__1);
+
+/*           w := w + V2**T * b2 */
+
+	    i__2 = *n - *k - i__ + 1;
+	    i__3 = i__ - 1;
+	    igraphdgemv_("Transpose", &i__2, &i__3, &c_b5, &a[*k + i__ + a_dim1], 
+		    lda, &a[*k + i__ + i__ * a_dim1], &c__1, &c_b5, &t[*nb * 
+		    t_dim1 + 1], &c__1);
+
+/*           w := T**T * w */
+
+	    i__2 = i__ - 1;
+	    igraphdtrmv_("Upper", "Transpose", "NON-UNIT", &i__2, &t[t_offset], ldt,
+		     &t[*nb * t_dim1 + 1], &c__1);
+
+/*           b2 := b2 - V2*w */
+
+	    i__2 = *n - *k - i__ + 1;
+	    i__3 = i__ - 1;
+	    igraphdgemv_("NO TRANSPOSE", &i__2, &i__3, &c_b4, &a[*k + i__ + a_dim1],
+		     lda, &t[*nb * t_dim1 + 1], &c__1, &c_b5, &a[*k + i__ + 
+		    i__ * a_dim1], &c__1);
+
+/*           b1 := b1 - V1*w */
+
+	    i__2 = i__ - 1;
+	    igraphdtrmv_("Lower", "NO TRANSPOSE", "UNIT", &i__2, &a[*k + 1 + a_dim1]
+		    , lda, &t[*nb * t_dim1 + 1], &c__1);
+	    i__2 = i__ - 1;
+	    igraphdaxpy_(&i__2, &c_b4, &t[*nb * t_dim1 + 1], &c__1, &a[*k + 1 + i__ 
+		    * a_dim1], &c__1);
+
+	    a[*k + i__ - 1 + (i__ - 1) * a_dim1] = ei;
+	}
+
+/*        Generate the elementary reflector H(I) to annihilate   
+          A(K+I+1:N,I) */
+
+	i__2 = *n - *k - i__ + 1;
+/* Computing MIN */
+	i__3 = *k + i__ + 1;
+	igraphdlarfg_(&i__2, &a[*k + i__ + i__ * a_dim1], &a[min(i__3,*n) + i__ * 
+		a_dim1], &c__1, &tau[i__]);
+	ei = a[*k + i__ + i__ * a_dim1];
+	a[*k + i__ + i__ * a_dim1] = 1.;
+
+/*        Compute  Y(K+1:N,I) */
+
+	i__2 = *n - *k;
+	i__3 = *n - *k - i__ + 1;
+	igraphdgemv_("NO TRANSPOSE", &i__2, &i__3, &c_b5, &a[*k + 1 + (i__ + 1) * 
+		a_dim1], lda, &a[*k + i__ + i__ * a_dim1], &c__1, &c_b38, &y[*
+		k + 1 + i__ * y_dim1], &c__1);
+	i__2 = *n - *k - i__ + 1;
+	i__3 = i__ - 1;
+	igraphdgemv_("Transpose", &i__2, &i__3, &c_b5, &a[*k + i__ + a_dim1], lda, &
+		a[*k + i__ + i__ * a_dim1], &c__1, &c_b38, &t[i__ * t_dim1 + 
+		1], &c__1);
+	i__2 = *n - *k;
+	i__3 = i__ - 1;
+	igraphdgemv_("NO TRANSPOSE", &i__2, &i__3, &c_b4, &y[*k + 1 + y_dim1], ldy, 
+		&t[i__ * t_dim1 + 1], &c__1, &c_b5, &y[*k + 1 + i__ * y_dim1],
+		 &c__1);
+	i__2 = *n - *k;
+	igraphdscal_(&i__2, &tau[i__], &y[*k + 1 + i__ * y_dim1], &c__1);
+
+/*        Compute T(1:I,I) */
+
+	i__2 = i__ - 1;
+	d__1 = -tau[i__];
+	igraphdscal_(&i__2, &d__1, &t[i__ * t_dim1 + 1], &c__1);
+	i__2 = i__ - 1;
+	igraphdtrmv_("Upper", "No Transpose", "NON-UNIT", &i__2, &t[t_offset], ldt, 
+		&t[i__ * t_dim1 + 1], &c__1)
+		;
+	t[i__ + i__ * t_dim1] = tau[i__];
+
+/* L10: */
+    }
+    a[*k + *nb + *nb * a_dim1] = ei;
+
+/*     Compute Y(1:K,1:NB) */
+
+    igraphdlacpy_("ALL", k, nb, &a[(a_dim1 << 1) + 1], lda, &y[y_offset], ldy);
+    igraphdtrmm_("RIGHT", "Lower", "NO TRANSPOSE", "UNIT", k, nb, &c_b5, &a[*k + 1 
+	    + a_dim1], lda, &y[y_offset], ldy);
+    if (*n > *k + *nb) {
+	i__1 = *n - *k - *nb;
+	igraphdgemm_("NO TRANSPOSE", "NO TRANSPOSE", k, nb, &i__1, &c_b5, &a[(*nb + 
+		2) * a_dim1 + 1], lda, &a[*k + 1 + *nb + a_dim1], lda, &c_b5, 
+		&y[y_offset], ldy);
+    }
+    igraphdtrmm_("RIGHT", "Upper", "NO TRANSPOSE", "NON-UNIT", k, nb, &c_b5, &t[
+	    t_offset], ldt, &y[y_offset], ldy);
+
+    return 0;
+
+/*     End of DLAHR2 */
+
+} /* igraphdlahr2_ */
+
diff --git a/igraph/src/dlaisnan.c b/igraph/src/dlaisnan.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaisnan.c
@@ -0,0 +1,107 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAISNAN tests input for NaN by comparing two arguments for inequality.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAISNAN + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaisna
+n.f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaisna
+n.f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaisna
+n.f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         LOGICAL FUNCTION DLAISNAN( DIN1, DIN2 )   
+
+         DOUBLE PRECISION   DIN1, DIN2   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > This routine is not for general use.  It exists solely to avoid   
+   > over-optimization in DISNAN.   
+   >   
+   > DLAISNAN checks for NaNs by comparing its two arguments for   
+   > inequality.  NaN is the only floating-point value where NaN != NaN   
+   > returns .TRUE.  To check for NaNs, pass the same variable as both   
+   > arguments.   
+   >   
+   > A compiler must assume that the two arguments are   
+   > not the same variable, and the test will not be optimized away.   
+   > Interprocedural or whole-program optimization may delete this   
+   > test.  The ISNAN functions will be replaced by the correct   
+   > Fortran 03 intrinsic once the intrinsic is widely available.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] DIN1   
+   > \verbatim   
+   >          DIN1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] DIN2   
+   > \verbatim   
+   >          DIN2 is DOUBLE PRECISION   
+   >          Two numbers to compare for inequality.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    ===================================================================== */
+logical igraphdlaisnan_(doublereal *din1, doublereal *din2)
+{
+    /* System generated locals */
+    logical ret_val;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ===================================================================== */
+
+    ret_val = *din1 != *din2;
+    return ret_val;
+} /* igraphdlaisnan_ */
+
diff --git a/igraph/src/dlaln2.c b/igraph/src/dlaln2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaln2.c
@@ -0,0 +1,658 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLALN2 solves a 1-by-1 or 2-by-2 linear system of equations of the specified form.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLALN2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaln2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaln2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaln2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLALN2( LTRANS, NA, NW, SMIN, CA, A, LDA, D1, D2, B,   
+                            LDB, WR, WI, X, LDX, SCALE, XNORM, INFO )   
+
+         LOGICAL            LTRANS   
+         INTEGER            INFO, LDA, LDB, LDX, NA, NW   
+         DOUBLE PRECISION   CA, D1, D2, SCALE, SMIN, WI, WR, XNORM   
+         DOUBLE PRECISION   A( LDA, * ), B( LDB, * ), X( LDX, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLALN2 solves a system of the form  (ca A - w D ) X = s B   
+   > or (ca A**T - w D) X = s B   with possible scaling ("s") and   
+   > perturbation of A.  (A**T means A-transpose.)   
+   >   
+   > A is an NA x NA real matrix, ca is a real scalar, D is an NA x NA   
+   > real diagonal matrix, w is a real or complex value, and X and B are   
+   > NA x 1 matrices -- real if w is real, complex if w is complex.  NA   
+   > may be 1 or 2.   
+   >   
+   > If w is complex, X and B are represented as NA x 2 matrices,   
+   > the first column of each being the real part and the second   
+   > being the imaginary part.   
+   >   
+   > "s" is a scaling factor (.LE. 1), computed by DLALN2, which is   
+   > so chosen that X can be computed without overflow.  X is further   
+   > scaled if necessary to assure that norm(ca A - w D)*norm(X) is less   
+   > than overflow.   
+   >   
+   > If both singular values of (ca A - w D) are less than SMIN,   
+   > SMIN*identity will be used instead of (ca A - w D).  If only one   
+   > singular value is less than SMIN, one element of (ca A - w D) will be   
+   > perturbed enough to make the smallest singular value roughly SMIN.   
+   > If both singular values are at least SMIN, (ca A - w D) will not be   
+   > perturbed.  In any case, the perturbation will be at most some small   
+   > multiple of max( SMIN, ulp*norm(ca A - w D) ).  The singular values   
+   > are computed by infinity-norm approximations, and thus will only be   
+   > correct to a factor of 2 or so.   
+   >   
+   > Note: all input quantities are assumed to be smaller than overflow   
+   > by a reasonable factor.  (See BIGNUM.)   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] LTRANS   
+   > \verbatim   
+   >          LTRANS is LOGICAL   
+   >          =.TRUE.:  A-transpose will be used.   
+   >          =.FALSE.: A will be used (not transposed.)   
+   > \endverbatim   
+   >   
+   > \param[in] NA   
+   > \verbatim   
+   >          NA is INTEGER   
+   >          The size of the matrix A.  It may (only) be 1 or 2.   
+   > \endverbatim   
+   >   
+   > \param[in] NW   
+   > \verbatim   
+   >          NW is INTEGER   
+   >          1 if "w" is real, 2 if "w" is complex.  It may only be 1   
+   >          or 2.   
+   > \endverbatim   
+   >   
+   > \param[in] SMIN   
+   > \verbatim   
+   >          SMIN is DOUBLE PRECISION   
+   >          The desired lower bound on the singular values of A.  This   
+   >          should be a safe distance away from underflow or overflow,   
+   >          say, between (underflow/machine precision) and  (machine   
+   >          precision * overflow ).  (See BIGNUM and ULP.)   
+   > \endverbatim   
+   >   
+   > \param[in] CA   
+   > \verbatim   
+   >          CA is DOUBLE PRECISION   
+   >          The coefficient c, which A is multiplied by.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,NA)   
+   >          The NA x NA matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of A.  It must be at least NA.   
+   > \endverbatim   
+   >   
+   > \param[in] D1   
+   > \verbatim   
+   >          D1 is DOUBLE PRECISION   
+   >          The 1,1 element in the diagonal matrix D.   
+   > \endverbatim   
+   >   
+   > \param[in] D2   
+   > \verbatim   
+   >          D2 is DOUBLE PRECISION   
+   >          The 2,2 element in the diagonal matrix D.  Not used if NW=1.   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (LDB,NW)   
+   >          The NA x NW matrix B (right-hand side).  If NW=2 ("w" is   
+   >          complex), column 1 contains the real part of B and column 2   
+   >          contains the imaginary part.   
+   > \endverbatim   
+   >   
+   > \param[in] LDB   
+   > \verbatim   
+   >          LDB is INTEGER   
+   >          The leading dimension of B.  It must be at least NA.   
+   > \endverbatim   
+   >   
+   > \param[in] WR   
+   > \verbatim   
+   >          WR is DOUBLE PRECISION   
+   >          The real part of the scalar "w".   
+   > \endverbatim   
+   >   
+   > \param[in] WI   
+   > \verbatim   
+   >          WI is DOUBLE PRECISION   
+   >          The imaginary part of the scalar "w".  Not used if NW=1.   
+   > \endverbatim   
+   >   
+   > \param[out] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION array, dimension (LDX,NW)   
+   >          The NA x NW matrix X (unknowns), as computed by DLALN2.   
+   >          If NW=2 ("w" is complex), on exit, column 1 will contain   
+   >          the real part of X and column 2 will contain the imaginary   
+   >          part.   
+   > \endverbatim   
+   >   
+   > \param[in] LDX   
+   > \verbatim   
+   >          LDX is INTEGER   
+   >          The leading dimension of X.  It must be at least NA.   
+   > \endverbatim   
+   >   
+   > \param[out] SCALE   
+   > \verbatim   
+   >          SCALE is DOUBLE PRECISION   
+   >          The scale factor that B must be multiplied by to insure   
+   >          that overflow does not occur when computing X.  Thus,   
+   >          (ca A - w D) X  will be SCALE*B, not B (ignoring   
+   >          perturbations of A.)  It will be at most 1.   
+   > \endverbatim   
+   >   
+   > \param[out] XNORM   
+   > \verbatim   
+   >          XNORM is DOUBLE PRECISION   
+   >          The infinity-norm of X, when X is regarded as an NA x NW   
+   >          real matrix.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          An error flag.  It will be set to zero if no error occurs,   
+   >          a negative number if an argument is in error, or a positive   
+   >          number if  ca A - w D  had to be perturbed.   
+   >          The possible values are:   
+   >          = 0: No error occurred, and (ca A - w D) did not have to be   
+   >                 perturbed.   
+   >          = 1: (ca A - w D) had to be perturbed to make its smallest   
+   >               (or only) singular value greater than SMIN.   
+   >          NOTE: In the interests of speed, this routine does not   
+   >                check the inputs for errors.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlaln2_(logical *ltrans, integer *na, integer *nw, 
+	doublereal *smin, doublereal *ca, doublereal *a, integer *lda, 
+	doublereal *d1, doublereal *d2, doublereal *b, integer *ldb, 
+	doublereal *wr, doublereal *wi, doublereal *x, integer *ldx, 
+	doublereal *scale, doublereal *xnorm, integer *info)
+{
+    /* Initialized data */
+
+    static logical zswap[4] = { FALSE_,FALSE_,TRUE_,TRUE_ };
+    static logical rswap[4] = { FALSE_,TRUE_,FALSE_,TRUE_ };
+    static integer ipivot[16]	/* was [4][4] */ = { 1,2,3,4,2,1,4,3,3,4,1,2,
+	    4,3,2,1 };
+
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, x_dim1, x_offset;
+    doublereal d__1, d__2, d__3, d__4, d__5, d__6;
+    IGRAPH_F77_SAVE doublereal equiv_0[4], equiv_1[4];
+
+    /* Local variables */
+    integer j;
+#define ci (equiv_0)
+#define cr (equiv_1)
+    doublereal bi1, bi2, br1, br2, xi1, xi2, xr1, xr2, ci21, ci22, cr21, cr22,
+	     li21, csi, ui11, lr21, ui12, ui22;
+#define civ (equiv_0)
+    doublereal csr, ur11, ur12, ur22;
+#define crv (equiv_1)
+    doublereal bbnd, cmax, ui11r, ui12s, temp, ur11r, ur12s, u22abs;
+    integer icmax;
+    doublereal bnorm, cnorm, smini;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdladiv_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *);
+    doublereal bignum, smlnum;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+    x_dim1 = *ldx;
+    x_offset = 1 + x_dim1;
+    x -= x_offset;
+
+    /* Function Body   
+
+       Compute BIGNUM */
+
+    smlnum = 2. * igraphdlamch_("Safe minimum");
+    bignum = 1. / smlnum;
+    smini = max(*smin,smlnum);
+
+/*     Don't check for input errors */
+
+    *info = 0;
+
+/*     Standard Initializations */
+
+    *scale = 1.;
+
+    if (*na == 1) {
+
+/*        1 x 1  (i.e., scalar) system   C X = B */
+
+	if (*nw == 1) {
+
+/*           Real 1x1 system.   
+
+             C = ca A - w D */
+
+	    csr = *ca * a[a_dim1 + 1] - *wr * *d1;
+	    cnorm = abs(csr);
+
+/*           If | C | < SMINI, use C = SMINI */
+
+	    if (cnorm < smini) {
+		csr = smini;
+		cnorm = smini;
+		*info = 1;
+	    }
+
+/*           Check scaling for  X = B / C */
+
+	    bnorm = (d__1 = b[b_dim1 + 1], abs(d__1));
+	    if (cnorm < 1. && bnorm > 1.) {
+		if (bnorm > bignum * cnorm) {
+		    *scale = 1. / bnorm;
+		}
+	    }
+
+/*           Compute X */
+
+	    x[x_dim1 + 1] = b[b_dim1 + 1] * *scale / csr;
+	    *xnorm = (d__1 = x[x_dim1 + 1], abs(d__1));
+	} else {
+
+/*           Complex 1x1 system (w is complex)   
+
+             C = ca A - w D */
+
+	    csr = *ca * a[a_dim1 + 1] - *wr * *d1;
+	    csi = -(*wi) * *d1;
+	    cnorm = abs(csr) + abs(csi);
+
+/*           If | C | < SMINI, use C = SMINI */
+
+	    if (cnorm < smini) {
+		csr = smini;
+		csi = 0.;
+		cnorm = smini;
+		*info = 1;
+	    }
+
+/*           Check scaling for  X = B / C */
+
+	    bnorm = (d__1 = b[b_dim1 + 1], abs(d__1)) + (d__2 = b[(b_dim1 << 
+		    1) + 1], abs(d__2));
+	    if (cnorm < 1. && bnorm > 1.) {
+		if (bnorm > bignum * cnorm) {
+		    *scale = 1. / bnorm;
+		}
+	    }
+
+/*           Compute X */
+
+	    d__1 = *scale * b[b_dim1 + 1];
+	    d__2 = *scale * b[(b_dim1 << 1) + 1];
+	    igraphdladiv_(&d__1, &d__2, &csr, &csi, &x[x_dim1 + 1], &x[(x_dim1 << 1)
+		     + 1]);
+	    *xnorm = (d__1 = x[x_dim1 + 1], abs(d__1)) + (d__2 = x[(x_dim1 << 
+		    1) + 1], abs(d__2));
+	}
+
+    } else {
+
+/*        2x2 System   
+
+          Compute the real part of  C = ca A - w D  (or  ca A**T - w D ) */
+
+	cr[0] = *ca * a[a_dim1 + 1] - *wr * *d1;
+	cr[3] = *ca * a[(a_dim1 << 1) + 2] - *wr * *d2;
+	if (*ltrans) {
+	    cr[2] = *ca * a[a_dim1 + 2];
+	    cr[1] = *ca * a[(a_dim1 << 1) + 1];
+	} else {
+	    cr[1] = *ca * a[a_dim1 + 2];
+	    cr[2] = *ca * a[(a_dim1 << 1) + 1];
+	}
+
+	if (*nw == 1) {
+
+/*           Real 2x2 system  (w is real)   
+
+             Find the largest element in C */
+
+	    cmax = 0.;
+	    icmax = 0;
+
+	    for (j = 1; j <= 4; ++j) {
+		if ((d__1 = crv[j - 1], abs(d__1)) > cmax) {
+		    cmax = (d__1 = crv[j - 1], abs(d__1));
+		    icmax = j;
+		}
+/* L10: */
+	    }
+
+/*           If norm(C) < SMINI, use SMINI*identity. */
+
+	    if (cmax < smini) {
+/* Computing MAX */
+		d__3 = (d__1 = b[b_dim1 + 1], abs(d__1)), d__4 = (d__2 = b[
+			b_dim1 + 2], abs(d__2));
+		bnorm = max(d__3,d__4);
+		if (smini < 1. && bnorm > 1.) {
+		    if (bnorm > bignum * smini) {
+			*scale = 1. / bnorm;
+		    }
+		}
+		temp = *scale / smini;
+		x[x_dim1 + 1] = temp * b[b_dim1 + 1];
+		x[x_dim1 + 2] = temp * b[b_dim1 + 2];
+		*xnorm = temp * bnorm;
+		*info = 1;
+		return 0;
+	    }
+
+/*           Gaussian elimination with complete pivoting. */
+
+	    ur11 = crv[icmax - 1];
+	    cr21 = crv[ipivot[(icmax << 2) - 3] - 1];
+	    ur12 = crv[ipivot[(icmax << 2) - 2] - 1];
+	    cr22 = crv[ipivot[(icmax << 2) - 1] - 1];
+	    ur11r = 1. / ur11;
+	    lr21 = ur11r * cr21;
+	    ur22 = cr22 - ur12 * lr21;
+
+/*           If smaller pivot < SMINI, use SMINI */
+
+	    if (abs(ur22) < smini) {
+		ur22 = smini;
+		*info = 1;
+	    }
+	    if (rswap[icmax - 1]) {
+		br1 = b[b_dim1 + 2];
+		br2 = b[b_dim1 + 1];
+	    } else {
+		br1 = b[b_dim1 + 1];
+		br2 = b[b_dim1 + 2];
+	    }
+	    br2 -= lr21 * br1;
+/* Computing MAX */
+	    d__2 = (d__1 = br1 * (ur22 * ur11r), abs(d__1)), d__3 = abs(br2);
+	    bbnd = max(d__2,d__3);
+	    if (bbnd > 1. && abs(ur22) < 1.) {
+		if (bbnd >= bignum * abs(ur22)) {
+		    *scale = 1. / bbnd;
+		}
+	    }
+
+	    xr2 = br2 * *scale / ur22;
+	    xr1 = *scale * br1 * ur11r - xr2 * (ur11r * ur12);
+	    if (zswap[icmax - 1]) {
+		x[x_dim1 + 1] = xr2;
+		x[x_dim1 + 2] = xr1;
+	    } else {
+		x[x_dim1 + 1] = xr1;
+		x[x_dim1 + 2] = xr2;
+	    }
+/* Computing MAX */
+	    d__1 = abs(xr1), d__2 = abs(xr2);
+	    *xnorm = max(d__1,d__2);
+
+/*           Further scaling if  norm(A) norm(X) > overflow */
+
+	    if (*xnorm > 1. && cmax > 1.) {
+		if (*xnorm > bignum / cmax) {
+		    temp = cmax / bignum;
+		    x[x_dim1 + 1] = temp * x[x_dim1 + 1];
+		    x[x_dim1 + 2] = temp * x[x_dim1 + 2];
+		    *xnorm = temp * *xnorm;
+		    *scale = temp * *scale;
+		}
+	    }
+	} else {
+
+/*           Complex 2x2 system  (w is complex)   
+
+             Find the largest element in C */
+
+	    ci[0] = -(*wi) * *d1;
+	    ci[1] = 0.;
+	    ci[2] = 0.;
+	    ci[3] = -(*wi) * *d2;
+	    cmax = 0.;
+	    icmax = 0;
+
+	    for (j = 1; j <= 4; ++j) {
+		if ((d__1 = crv[j - 1], abs(d__1)) + (d__2 = civ[j - 1], abs(
+			d__2)) > cmax) {
+		    cmax = (d__1 = crv[j - 1], abs(d__1)) + (d__2 = civ[j - 1]
+			    , abs(d__2));
+		    icmax = j;
+		}
+/* L20: */
+	    }
+
+/*           If norm(C) < SMINI, use SMINI*identity. */
+
+	    if (cmax < smini) {
+/* Computing MAX */
+		d__5 = (d__1 = b[b_dim1 + 1], abs(d__1)) + (d__2 = b[(b_dim1 
+			<< 1) + 1], abs(d__2)), d__6 = (d__3 = b[b_dim1 + 2], 
+			abs(d__3)) + (d__4 = b[(b_dim1 << 1) + 2], abs(d__4));
+		bnorm = max(d__5,d__6);
+		if (smini < 1. && bnorm > 1.) {
+		    if (bnorm > bignum * smini) {
+			*scale = 1. / bnorm;
+		    }
+		}
+		temp = *scale / smini;
+		x[x_dim1 + 1] = temp * b[b_dim1 + 1];
+		x[x_dim1 + 2] = temp * b[b_dim1 + 2];
+		x[(x_dim1 << 1) + 1] = temp * b[(b_dim1 << 1) + 1];
+		x[(x_dim1 << 1) + 2] = temp * b[(b_dim1 << 1) + 2];
+		*xnorm = temp * bnorm;
+		*info = 1;
+		return 0;
+	    }
+
+/*           Gaussian elimination with complete pivoting. */
+
+	    ur11 = crv[icmax - 1];
+	    ui11 = civ[icmax - 1];
+	    cr21 = crv[ipivot[(icmax << 2) - 3] - 1];
+	    ci21 = civ[ipivot[(icmax << 2) - 3] - 1];
+	    ur12 = crv[ipivot[(icmax << 2) - 2] - 1];
+	    ui12 = civ[ipivot[(icmax << 2) - 2] - 1];
+	    cr22 = crv[ipivot[(icmax << 2) - 1] - 1];
+	    ci22 = civ[ipivot[(icmax << 2) - 1] - 1];
+	    if (icmax == 1 || icmax == 4) {
+
+/*              Code when off-diagonals of pivoted C are real */
+
+		if (abs(ur11) > abs(ui11)) {
+		    temp = ui11 / ur11;
+/* Computing 2nd power */
+		    d__1 = temp;
+		    ur11r = 1. / (ur11 * (d__1 * d__1 + 1.));
+		    ui11r = -temp * ur11r;
+		} else {
+		    temp = ur11 / ui11;
+/* Computing 2nd power */
+		    d__1 = temp;
+		    ui11r = -1. / (ui11 * (d__1 * d__1 + 1.));
+		    ur11r = -temp * ui11r;
+		}
+		lr21 = cr21 * ur11r;
+		li21 = cr21 * ui11r;
+		ur12s = ur12 * ur11r;
+		ui12s = ur12 * ui11r;
+		ur22 = cr22 - ur12 * lr21;
+		ui22 = ci22 - ur12 * li21;
+	    } else {
+
+/*              Code when diagonals of pivoted C are real */
+
+		ur11r = 1. / ur11;
+		ui11r = 0.;
+		lr21 = cr21 * ur11r;
+		li21 = ci21 * ur11r;
+		ur12s = ur12 * ur11r;
+		ui12s = ui12 * ur11r;
+		ur22 = cr22 - ur12 * lr21 + ui12 * li21;
+		ui22 = -ur12 * li21 - ui12 * lr21;
+	    }
+	    u22abs = abs(ur22) + abs(ui22);
+
+/*           If smaller pivot < SMINI, use SMINI */
+
+	    if (u22abs < smini) {
+		ur22 = smini;
+		ui22 = 0.;
+		*info = 1;
+	    }
+	    if (rswap[icmax - 1]) {
+		br2 = b[b_dim1 + 1];
+		br1 = b[b_dim1 + 2];
+		bi2 = b[(b_dim1 << 1) + 1];
+		bi1 = b[(b_dim1 << 1) + 2];
+	    } else {
+		br1 = b[b_dim1 + 1];
+		br2 = b[b_dim1 + 2];
+		bi1 = b[(b_dim1 << 1) + 1];
+		bi2 = b[(b_dim1 << 1) + 2];
+	    }
+	    br2 = br2 - lr21 * br1 + li21 * bi1;
+	    bi2 = bi2 - li21 * br1 - lr21 * bi1;
+/* Computing MAX */
+	    d__1 = (abs(br1) + abs(bi1)) * (u22abs * (abs(ur11r) + abs(ui11r))
+		    ), d__2 = abs(br2) + abs(bi2);
+	    bbnd = max(d__1,d__2);
+	    if (bbnd > 1. && u22abs < 1.) {
+		if (bbnd >= bignum * u22abs) {
+		    *scale = 1. / bbnd;
+		    br1 = *scale * br1;
+		    bi1 = *scale * bi1;
+		    br2 = *scale * br2;
+		    bi2 = *scale * bi2;
+		}
+	    }
+
+	    igraphdladiv_(&br2, &bi2, &ur22, &ui22, &xr2, &xi2);
+	    xr1 = ur11r * br1 - ui11r * bi1 - ur12s * xr2 + ui12s * xi2;
+	    xi1 = ui11r * br1 + ur11r * bi1 - ui12s * xr2 - ur12s * xi2;
+	    if (zswap[icmax - 1]) {
+		x[x_dim1 + 1] = xr2;
+		x[x_dim1 + 2] = xr1;
+		x[(x_dim1 << 1) + 1] = xi2;
+		x[(x_dim1 << 1) + 2] = xi1;
+	    } else {
+		x[x_dim1 + 1] = xr1;
+		x[x_dim1 + 2] = xr2;
+		x[(x_dim1 << 1) + 1] = xi1;
+		x[(x_dim1 << 1) + 2] = xi2;
+	    }
+/* Computing MAX */
+	    d__1 = abs(xr1) + abs(xi1), d__2 = abs(xr2) + abs(xi2);
+	    *xnorm = max(d__1,d__2);
+
+/*           Further scaling if  norm(A) norm(X) > overflow */
+
+	    if (*xnorm > 1. && cmax > 1.) {
+		if (*xnorm > bignum / cmax) {
+		    temp = cmax / bignum;
+		    x[x_dim1 + 1] = temp * x[x_dim1 + 1];
+		    x[x_dim1 + 2] = temp * x[x_dim1 + 2];
+		    x[(x_dim1 << 1) + 1] = temp * x[(x_dim1 << 1) + 1];
+		    x[(x_dim1 << 1) + 2] = temp * x[(x_dim1 << 1) + 2];
+		    *xnorm = temp * *xnorm;
+		    *scale = temp * *scale;
+		}
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DLALN2 */
+
+} /* igraphdlaln2_ */
+
+#undef crv
+#undef civ
+#undef cr
+#undef ci
+
+
diff --git a/igraph/src/dlamch.c b/igraph/src/dlamch.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlamch.c
@@ -0,0 +1,204 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b2 = 0.;
+
+/* > \brief \b DLAMCH   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+    Definition:   
+    ===========   
+
+        DOUBLE PRECISION FUNCTION DLAMCH( CMACH )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAMCH determines double precision machine parameters.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] CMACH   
+   > \verbatim   
+   >          Specifies the value to be returned by DLAMCH:   
+   >          = 'E' or 'e',   DLAMCH := eps   
+   >          = 'S' or 's ,   DLAMCH := sfmin   
+   >          = 'B' or 'b',   DLAMCH := base   
+   >          = 'P' or 'p',   DLAMCH := eps*base   
+   >          = 'N' or 'n',   DLAMCH := t   
+   >          = 'R' or 'r',   DLAMCH := rnd   
+   >          = 'M' or 'm',   DLAMCH := emin   
+   >          = 'U' or 'u',   DLAMCH := rmin   
+   >          = 'L' or 'l',   DLAMCH := emax   
+   >          = 'O' or 'o',   DLAMCH := rmax   
+   >          where   
+   >          eps   = relative machine precision   
+   >          sfmin = safe minimum, such that 1/sfmin does not overflow   
+   >          base  = base of the machine   
+   >          prec  = eps*base   
+   >          t     = number of (base) digits in the mantissa   
+   >          rnd   = 1.0 when rounding occurs in addition, 0.0 otherwise   
+   >          emin  = minimum exponent before (gradual) underflow   
+   >          rmin  = underflow threshold - base**(emin-1)   
+   >          emax  = largest exponent before overflow   
+   >          rmax  = overflow threshold  - (base**emax)*(1-eps)   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERauxiliary   
+
+    ===================================================================== */
+doublereal igraphdlamch_(char *cmach)
+{
+    /* System generated locals */
+    doublereal ret_val;
+
+    /* Local variables */
+    extern doublereal radixdbl_(doublereal *), digitsdbl_(doublereal *), 
+	    epsilondbl_(doublereal *);
+    doublereal rnd, eps, rmach;
+    extern logical igraphlsame_(char *, char *);
+    doublereal small, sfmin;
+    extern integer minexponentdbl_(doublereal *), maxexponentdbl_(doublereal *
+	    );
+    extern doublereal hugedbl_(doublereal *), tinydbl_(doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+
+   =====================================================================   
+
+
+
+       Assume rounding, not chopping. Always. */
+
+    rnd = 1.;
+
+    if (1. == rnd) {
+	eps = epsilondbl_(&c_b2) * .5f;
+    } else {
+	eps = epsilondbl_(&c_b2);
+    }
+
+    if (igraphlsame_(cmach, "E")) {
+	rmach = eps;
+    } else if (igraphlsame_(cmach, "S")) {
+	sfmin = tinydbl_(&c_b2);
+	small = 1. / hugedbl_(&c_b2);
+	if (small >= sfmin) {
+
+/*           Use SMALL plus a bit, to avoid the possibility of rounding   
+             causing overflow when computing  1/sfmin. */
+
+	    sfmin = small * (eps + 1.);
+	}
+	rmach = sfmin;
+    } else if (igraphlsame_(cmach, "B")) {
+	rmach = radixdbl_(&c_b2);
+    } else if (igraphlsame_(cmach, "P")) {
+	rmach = eps * radixdbl_(&c_b2);
+    } else if (igraphlsame_(cmach, "N")) {
+	rmach = digitsdbl_(&c_b2);
+    } else if (igraphlsame_(cmach, "R")) {
+	rmach = rnd;
+    } else if (igraphlsame_(cmach, "M")) {
+	rmach = (doublereal) minexponentdbl_(&c_b2);
+    } else if (igraphlsame_(cmach, "U")) {
+	rmach = tinydbl_(&c_b2);
+    } else if (igraphlsame_(cmach, "L")) {
+	rmach = (doublereal) maxexponentdbl_(&c_b2);
+    } else if (igraphlsame_(cmach, "O")) {
+	rmach = hugedbl_(&c_b2);
+    } else {
+	rmach = 0.;
+    }
+
+    ret_val = rmach;
+    return ret_val;
+
+/*     End of DLAMCH */
+
+} /* igraphdlamch_   
+
+   ***********************************************************************   
+   > \brief \b DLAMC3   
+   > \details   
+   > \b Purpose:   
+   > \verbatim   
+   > DLAMC3  is intended to force  A  and  B  to be stored prior to doing   
+   > the addition of  A  and  B ,  for use in situations where optimizers   
+   > might hold one of these in a register.   
+   > \endverbatim   
+   > \author LAPACK is a software package provided by Univ. of Tennessee, Univ. of California Berkeley, Univ. 
+of Colorado Denver and NAG Ltd..   
+   > \date November 2011   
+   > \ingroup auxOTHERauxiliary   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is a DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is a DOUBLE PRECISION   
+   >          The values A and B.   
+   > \endverbatim   
+   > */
+doublereal igraphdlamc3_(doublereal *a, doublereal *b)
+{
+    /* System generated locals */
+    doublereal ret_val;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.0) --   
+       Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..   
+       November 2010   
+
+   ===================================================================== */
+
+
+    ret_val = *a + *b;
+
+    return ret_val;
+
+/*     End of DLAMC3 */
+
+} /* igraphdlamc3_ */
+
diff --git a/igraph/src/dlaneg.c b/igraph/src/dlaneg.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaneg.c
@@ -0,0 +1,269 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLANEG computes the Sturm count.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLANEG + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaneg.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaneg.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaneg.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         INTEGER FUNCTION DLANEG( N, D, LLD, SIGMA, PIVMIN, R )   
+
+         INTEGER            N, R   
+         DOUBLE PRECISION   PIVMIN, SIGMA   
+         DOUBLE PRECISION   D( * ), LLD( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLANEG computes the Sturm count, the number of negative pivots   
+   > encountered while factoring tridiagonal T - sigma I = L D L^T.   
+   > This implementation works directly on the factors without forming   
+   > the tridiagonal matrix T.  The Sturm count is also the number of   
+   > eigenvalues of T less than sigma.   
+   >   
+   > This routine is called from DLARRB.   
+   >   
+   > The current routine does not use the PIVMIN parameter but rather   
+   > requires IEEE-754 propagation of Infinities and NaNs.  This   
+   > routine also has no input range restrictions but does require   
+   > default exception handling such that x/0 produces Inf when x is   
+   > non-zero, and Inf/Inf produces NaN.  For more information, see:   
+   >   
+   >   Marques, Riedy, and Voemel, "Benefits of IEEE-754 Features in   
+   >   Modern Symmetric Tridiagonal Eigensolvers," SIAM Journal on   
+   >   Scientific Computing, v28, n5, 2006.  DOI 10.1137/050641624   
+   >   (Tech report version in LAWN 172 with the same title.)   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The N diagonal elements of the diagonal matrix D.   
+   > \endverbatim   
+   >   
+   > \param[in] LLD   
+   > \verbatim   
+   >          LLD is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (N-1) elements L(i)*L(i)*D(i).   
+   > \endverbatim   
+   >   
+   > \param[in] SIGMA   
+   > \verbatim   
+   >          SIGMA is DOUBLE PRECISION   
+   >          Shift amount in T - sigma I = L D L^T.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot in the Sturm sequence.  May be used   
+   >          when zero pivots are encountered on non-IEEE-754   
+   >          architectures.   
+   > \endverbatim   
+   >   
+   > \param[in] R   
+   > \verbatim   
+   >          R is INTEGER   
+   >          The twist index for the twisted factorization that is used   
+   >          for the negcount.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >     Osni Marques, LBNL/NERSC, USA \n   
+   >     Christof Voemel, University of California, Berkeley, USA \n   
+   >     Jason Riedy, University of California, Berkeley, USA \n   
+   >   
+    ===================================================================== */
+integer igraphdlaneg_(integer *n, doublereal *d__, doublereal *lld, doublereal *
+	sigma, doublereal *pivmin, integer *r__)
+{
+    /* System generated locals */
+    integer ret_val, i__1, i__2, i__3, i__4;
+
+    /* Local variables */
+    integer j;
+    doublereal p, t;
+    integer bj;
+    doublereal tmp;
+    integer neg1, neg2;
+    doublereal bsav, gamma, dplus;
+    extern logical igraphdisnan_(doublereal *);
+    integer negcnt;
+    logical sawnan;
+    doublereal dminus;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+       Some architectures propagate Infinities and NaNs very slowly, so   
+       the code computes counts in BLKLEN chunks.  Then a NaN can   
+       propagate at most BLKLEN columns before being detected.  This is   
+       not a general tuning parameter; it needs only to be just large   
+       enough that the overhead is tiny in common cases.   
+       Parameter adjustments */
+    --lld;
+    --d__;
+
+    /* Function Body */
+    negcnt = 0;
+/*     I) upper part: L D L^T - SIGMA I = L+ D+ L+^T */
+    t = -(*sigma);
+    i__1 = *r__ - 1;
+    for (bj = 1; bj <= i__1; bj += 128) {
+	neg1 = 0;
+	bsav = t;
+/* Computing MIN */
+	i__3 = bj + 127, i__4 = *r__ - 1;
+	i__2 = min(i__3,i__4);
+	for (j = bj; j <= i__2; ++j) {
+	    dplus = d__[j] + t;
+	    if (dplus < 0.) {
+		++neg1;
+	    }
+	    tmp = t / dplus;
+	    t = tmp * lld[j] - *sigma;
+/* L21: */
+	}
+	sawnan = igraphdisnan_(&t);
+/*     Run a slower version of the above loop if a NaN is detected.   
+       A NaN should occur only with a zero pivot after an infinite   
+       pivot.  In that case, substituting 1 for T/DPLUS is the   
+       correct limit. */
+	if (sawnan) {
+	    neg1 = 0;
+	    t = bsav;
+/* Computing MIN */
+	    i__3 = bj + 127, i__4 = *r__ - 1;
+	    i__2 = min(i__3,i__4);
+	    for (j = bj; j <= i__2; ++j) {
+		dplus = d__[j] + t;
+		if (dplus < 0.) {
+		    ++neg1;
+		}
+		tmp = t / dplus;
+		if (igraphdisnan_(&tmp)) {
+		    tmp = 1.;
+		}
+		t = tmp * lld[j] - *sigma;
+/* L22: */
+	    }
+	}
+	negcnt += neg1;
+/* L210: */
+    }
+
+/*     II) lower part: L D L^T - SIGMA I = U- D- U-^T */
+    p = d__[*n] - *sigma;
+    i__1 = *r__;
+    for (bj = *n - 1; bj >= i__1; bj += -128) {
+	neg2 = 0;
+	bsav = p;
+/* Computing MAX */
+	i__3 = bj - 127;
+	i__2 = max(i__3,*r__);
+	for (j = bj; j >= i__2; --j) {
+	    dminus = lld[j] + p;
+	    if (dminus < 0.) {
+		++neg2;
+	    }
+	    tmp = p / dminus;
+	    p = tmp * d__[j] - *sigma;
+/* L23: */
+	}
+	sawnan = igraphdisnan_(&p);
+/*     As above, run a slower version that substitutes 1 for Inf/Inf. */
+
+	if (sawnan) {
+	    neg2 = 0;
+	    p = bsav;
+/* Computing MAX */
+	    i__3 = bj - 127;
+	    i__2 = max(i__3,*r__);
+	    for (j = bj; j >= i__2; --j) {
+		dminus = lld[j] + p;
+		if (dminus < 0.) {
+		    ++neg2;
+		}
+		tmp = p / dminus;
+		if (igraphdisnan_(&tmp)) {
+		    tmp = 1.;
+		}
+		p = tmp * d__[j] - *sigma;
+/* L24: */
+	    }
+	}
+	negcnt += neg2;
+/* L230: */
+    }
+
+/*     III) Twist index   
+         T was shifted by SIGMA initially. */
+    gamma = t + *sigma + p;
+    if (gamma < 0.) {
+	++negcnt;
+    }
+    ret_val = negcnt;
+    return ret_val;
+} /* igraphdlaneg_ */
+
diff --git a/igraph/src/dlange.c b/igraph/src/dlange.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlange.c
@@ -0,0 +1,254 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DLANGE returns the value of the 1-norm, Frobenius norm, infinity-norm, or the largest absolute 
+value of any element of a general rectangular matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLANGE + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlange.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlange.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlange.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         DOUBLE PRECISION FUNCTION DLANGE( NORM, M, N, A, LDA, WORK )   
+
+         CHARACTER          NORM   
+         INTEGER            LDA, M, N   
+         DOUBLE PRECISION   A( LDA, * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLANGE  returns the value of the one norm,  or the Frobenius norm, or   
+   > the  infinity norm,  or the  element of  largest absolute value  of a   
+   > real matrix A.   
+   > \endverbatim   
+   >   
+   > \return DLANGE   
+   > \verbatim   
+   >   
+   >    DLANGE = ( max(abs(A(i,j))), NORM = 'M' or 'm'   
+   >             (   
+   >             ( norm1(A),         NORM = '1', 'O' or 'o'   
+   >             (   
+   >             ( normI(A),         NORM = 'I' or 'i'   
+   >             (   
+   >             ( normF(A),         NORM = 'F', 'f', 'E' or 'e'   
+   >   
+   > where  norm1  denotes the  one norm of a matrix (maximum column sum),   
+   > normI  denotes the  infinity norm  of a matrix  (maximum row sum) and   
+   > normF  denotes the  Frobenius norm of a matrix (square root of sum of   
+   > squares).  Note that  max(abs(A(i,j)))  is not a consistent matrix norm.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] NORM   
+   > \verbatim   
+   >          NORM is CHARACTER*1   
+   >          Specifies the value to be returned in DLANGE as described   
+   >          above.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.  M >= 0.  When M = 0,   
+   >          DLANGE is set to zero.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.  N >= 0.  When N = 0,   
+   >          DLANGE is set to zero.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The m by n matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(M,1).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)),   
+   >          where LWORK >= M when NORM = 'I'; otherwise, WORK is not   
+   >          referenced.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleGEauxiliary   
+
+    ===================================================================== */
+doublereal igraphdlange_(char *norm, integer *m, integer *n, doublereal *a, integer 
+	*lda, doublereal *work)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+    doublereal ret_val, d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j;
+    doublereal sum, temp, scale;
+    extern logical igraphlsame_(char *, char *);
+    doublereal value = 0.;
+    extern logical igraphdisnan_(doublereal *);
+    extern /* Subroutine */ int igraphdlassq_(integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --work;
+
+    /* Function Body */
+    if (min(*m,*n) == 0) {
+	value = 0.;
+    } else if (igraphlsame_(norm, "M")) {
+
+/*        Find max(abs(A(i,j))). */
+
+	value = 0.;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		temp = (d__1 = a[i__ + j * a_dim1], abs(d__1));
+		if (value < temp || igraphdisnan_(&temp)) {
+		    value = temp;
+		}
+/* L10: */
+	    }
+/* L20: */
+	}
+    } else if (igraphlsame_(norm, "O") || *(unsigned char *)
+	    norm == '1') {
+
+/*        Find norm1(A). */
+
+	value = 0.;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = 0.;
+	    i__2 = *m;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		sum += (d__1 = a[i__ + j * a_dim1], abs(d__1));
+/* L30: */
+	    }
+	    if (value < sum || igraphdisnan_(&sum)) {
+		value = sum;
+	    }
+/* L40: */
+	}
+    } else if (igraphlsame_(norm, "I")) {
+
+/*        Find normI(A). */
+
+	i__1 = *m;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    work[i__] = 0.;
+/* L50: */
+	}
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		work[i__] += (d__1 = a[i__ + j * a_dim1], abs(d__1));
+/* L60: */
+	    }
+/* L70: */
+	}
+	value = 0.;
+	i__1 = *m;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    temp = work[i__];
+	    if (value < temp || igraphdisnan_(&temp)) {
+		value = temp;
+	    }
+/* L80: */
+	}
+    } else if (igraphlsame_(norm, "F") || igraphlsame_(norm, "E")) {
+
+/*        Find normF(A). */
+
+	scale = 0.;
+	sum = 1.;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    igraphdlassq_(m, &a[j * a_dim1 + 1], &c__1, &scale, &sum);
+/* L90: */
+	}
+	value = scale * sqrt(sum);
+    }
+
+    ret_val = value;
+    return ret_val;
+
+/*     End of DLANGE */
+
+} /* igraphdlange_ */
+
diff --git a/igraph/src/dlanhs.c b/igraph/src/dlanhs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlanhs.c
@@ -0,0 +1,257 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DLANHS returns the value of the 1-norm, Frobenius norm, infinity-norm, or the largest absolute 
+value of any element of an upper Hessenberg matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLANHS + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlanhs.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlanhs.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlanhs.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         DOUBLE PRECISION FUNCTION DLANHS( NORM, N, A, LDA, WORK )   
+
+         CHARACTER          NORM   
+         INTEGER            LDA, N   
+         DOUBLE PRECISION   A( LDA, * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLANHS  returns the value of the one norm,  or the Frobenius norm, or   
+   > the  infinity norm,  or the  element of  largest absolute value  of a   
+   > Hessenberg matrix A.   
+   > \endverbatim   
+   >   
+   > \return DLANHS   
+   > \verbatim   
+   >   
+   >    DLANHS = ( max(abs(A(i,j))), NORM = 'M' or 'm'   
+   >             (   
+   >             ( norm1(A),         NORM = '1', 'O' or 'o'   
+   >             (   
+   >             ( normI(A),         NORM = 'I' or 'i'   
+   >             (   
+   >             ( normF(A),         NORM = 'F', 'f', 'E' or 'e'   
+   >   
+   > where  norm1  denotes the  one norm of a matrix (maximum column sum),   
+   > normI  denotes the  infinity norm  of a matrix  (maximum row sum) and   
+   > normF  denotes the  Frobenius norm of a matrix (square root of sum of   
+   > squares).  Note that  max(abs(A(i,j)))  is not a consistent matrix norm.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] NORM   
+   > \verbatim   
+   >          NORM is CHARACTER*1   
+   >          Specifies the value to be returned in DLANHS as described   
+   >          above.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.  When N = 0, DLANHS is   
+   >          set to zero.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The n by n upper Hessenberg matrix A; the part of A below the   
+   >          first sub-diagonal is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(N,1).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)),   
+   >          where LWORK >= N when NORM = 'I'; otherwise, WORK is not   
+   >          referenced.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+    ===================================================================== */
+doublereal igraphdlanhs_(char *norm, integer *n, doublereal *a, integer *lda, 
+	doublereal *work)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3, i__4;
+    doublereal ret_val, d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j;
+    doublereal sum, scale;
+    extern logical igraphlsame_(char *, char *);
+    doublereal value = 0.;
+    extern logical igraphdisnan_(doublereal *);
+    extern /* Subroutine */ int igraphdlassq_(integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --work;
+
+    /* Function Body */
+    if (*n == 0) {
+	value = 0.;
+    } else if (igraphlsame_(norm, "M")) {
+
+/*        Find max(abs(A(i,j))). */
+
+	value = 0.;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MIN */
+	    i__3 = *n, i__4 = j + 1;
+	    i__2 = min(i__3,i__4);
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		sum = (d__1 = a[i__ + j * a_dim1], abs(d__1));
+		if (value < sum || igraphdisnan_(&sum)) {
+		    value = sum;
+		}
+/* L10: */
+	    }
+/* L20: */
+	}
+    } else if (igraphlsame_(norm, "O") || *(unsigned char *)
+	    norm == '1') {
+
+/*        Find norm1(A). */
+
+	value = 0.;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = 0.;
+/* Computing MIN */
+	    i__3 = *n, i__4 = j + 1;
+	    i__2 = min(i__3,i__4);
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		sum += (d__1 = a[i__ + j * a_dim1], abs(d__1));
+/* L30: */
+	    }
+	    if (value < sum || igraphdisnan_(&sum)) {
+		value = sum;
+	    }
+/* L40: */
+	}
+    } else if (igraphlsame_(norm, "I")) {
+
+/*        Find normI(A). */
+
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    work[i__] = 0.;
+/* L50: */
+	}
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MIN */
+	    i__3 = *n, i__4 = j + 1;
+	    i__2 = min(i__3,i__4);
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		work[i__] += (d__1 = a[i__ + j * a_dim1], abs(d__1));
+/* L60: */
+	    }
+/* L70: */
+	}
+	value = 0.;
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    sum = work[i__];
+	    if (value < sum || igraphdisnan_(&sum)) {
+		value = sum;
+	    }
+/* L80: */
+	}
+    } else if (igraphlsame_(norm, "F") || igraphlsame_(norm, "E")) {
+
+/*        Find normF(A). */
+
+	scale = 0.;
+	sum = 1.;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MIN */
+	    i__3 = *n, i__4 = j + 1;
+	    i__2 = min(i__3,i__4);
+	    igraphdlassq_(&i__2, &a[j * a_dim1 + 1], &c__1, &scale, &sum);
+/* L90: */
+	}
+	value = scale * sqrt(sum);
+    }
+
+    ret_val = value;
+    return ret_val;
+
+/*     End of DLANHS */
+
+} /* igraphdlanhs_ */
+
diff --git a/igraph/src/dlanst.c b/igraph/src/dlanst.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlanst.c
@@ -0,0 +1,215 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DLANST returns the value of the 1-norm, or the Frobenius norm, or the infinity norm, or the ele
+ment of largest absolute value of a real symmetric tridiagonal matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLANST + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlanst.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlanst.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlanst.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         DOUBLE PRECISION FUNCTION DLANST( NORM, N, D, E )   
+
+         CHARACTER          NORM   
+         INTEGER            N   
+         DOUBLE PRECISION   D( * ), E( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLANST  returns the value of the one norm,  or the Frobenius norm, or   
+   > the  infinity norm,  or the  element of  largest absolute value  of a   
+   > real symmetric tridiagonal matrix A.   
+   > \endverbatim   
+   >   
+   > \return DLANST   
+   > \verbatim   
+   >   
+   >    DLANST = ( max(abs(A(i,j))), NORM = 'M' or 'm'   
+   >             (   
+   >             ( norm1(A),         NORM = '1', 'O' or 'o'   
+   >             (   
+   >             ( normI(A),         NORM = 'I' or 'i'   
+   >             (   
+   >             ( normF(A),         NORM = 'F', 'f', 'E' or 'e'   
+   >   
+   > where  norm1  denotes the  one norm of a matrix (maximum column sum),   
+   > normI  denotes the  infinity norm  of a matrix  (maximum row sum) and   
+   > normF  denotes the  Frobenius norm of a matrix (square root of sum of   
+   > squares).  Note that  max(abs(A(i,j)))  is not a consistent matrix norm.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] NORM   
+   > \verbatim   
+   >          NORM is CHARACTER*1   
+   >          Specifies the value to be returned in DLANST as described   
+   >          above.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.  When N = 0, DLANST is   
+   >          set to zero.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The diagonal elements of A.   
+   > \endverbatim   
+   >   
+   > \param[in] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (n-1) sub-diagonal or super-diagonal elements of A.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    ===================================================================== */
+doublereal igraphdlanst_(char *norm, integer *n, doublereal *d__, doublereal *e)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal ret_val, d__1, d__2, d__3;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__;
+    doublereal sum, scale;
+    extern logical igraphlsame_(char *, char *);
+    doublereal anorm = 0.;
+    extern logical igraphdisnan_(doublereal *);
+    extern /* Subroutine */ int igraphdlassq_(integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --e;
+    --d__;
+
+    /* Function Body */
+    if (*n <= 0) {
+	anorm = 0.;
+    } else if (igraphlsame_(norm, "M")) {
+
+/*        Find max(abs(A(i,j))). */
+
+	anorm = (d__1 = d__[*n], abs(d__1));
+	i__1 = *n - 1;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    sum = (d__1 = d__[i__], abs(d__1));
+	    if (anorm < sum || igraphdisnan_(&sum)) {
+		anorm = sum;
+	    }
+	    sum = (d__1 = e[i__], abs(d__1));
+	    if (anorm < sum || igraphdisnan_(&sum)) {
+		anorm = sum;
+	    }
+/* L10: */
+	}
+    } else if (igraphlsame_(norm, "O") || *(unsigned char *)
+	    norm == '1' || igraphlsame_(norm, "I")) {
+
+/*        Find norm1(A). */
+
+	if (*n == 1) {
+	    anorm = abs(d__[1]);
+	} else {
+	    anorm = abs(d__[1]) + abs(e[1]);
+	    sum = (d__1 = e[*n - 1], abs(d__1)) + (d__2 = d__[*n], abs(d__2));
+	    if (anorm < sum || igraphdisnan_(&sum)) {
+		anorm = sum;
+	    }
+	    i__1 = *n - 1;
+	    for (i__ = 2; i__ <= i__1; ++i__) {
+		sum = (d__1 = d__[i__], abs(d__1)) + (d__2 = e[i__], abs(d__2)
+			) + (d__3 = e[i__ - 1], abs(d__3));
+		if (anorm < sum || igraphdisnan_(&sum)) {
+		    anorm = sum;
+		}
+/* L20: */
+	    }
+	}
+    } else if (igraphlsame_(norm, "F") || igraphlsame_(norm, "E")) {
+
+/*        Find normF(A). */
+
+	scale = 0.;
+	sum = 1.;
+	if (*n > 1) {
+	    i__1 = *n - 1;
+	    igraphdlassq_(&i__1, &e[1], &c__1, &scale, &sum);
+	    sum *= 2;
+	}
+	igraphdlassq_(n, &d__[1], &c__1, &scale, &sum);
+	anorm = scale * sqrt(sum);
+    }
+
+    ret_val = anorm;
+    return ret_val;
+
+/*     End of DLANST */
+
+} /* igraphdlanst_ */
+
diff --git a/igraph/src/dlansy.c b/igraph/src/dlansy.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlansy.c
@@ -0,0 +1,293 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DLANSY returns the value of the 1-norm, or the Frobenius norm, or the infinity norm, or the ele
+ment of largest absolute value of a real symmetric matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLANSY + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlansy.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlansy.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlansy.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         DOUBLE PRECISION FUNCTION DLANSY( NORM, UPLO, N, A, LDA, WORK )   
+
+         CHARACTER          NORM, UPLO   
+         INTEGER            LDA, N   
+         DOUBLE PRECISION   A( LDA, * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLANSY  returns the value of the one norm,  or the Frobenius norm, or   
+   > the  infinity norm,  or the  element of  largest absolute value  of a   
+   > real symmetric matrix A.   
+   > \endverbatim   
+   >   
+   > \return DLANSY   
+   > \verbatim   
+   >   
+   >    DLANSY = ( max(abs(A(i,j))), NORM = 'M' or 'm'   
+   >             (   
+   >             ( norm1(A),         NORM = '1', 'O' or 'o'   
+   >             (   
+   >             ( normI(A),         NORM = 'I' or 'i'   
+   >             (   
+   >             ( normF(A),         NORM = 'F', 'f', 'E' or 'e'   
+   >   
+   > where  norm1  denotes the  one norm of a matrix (maximum column sum),   
+   > normI  denotes the  infinity norm  of a matrix  (maximum row sum) and   
+   > normF  denotes the  Frobenius norm of a matrix (square root of sum of   
+   > squares).  Note that  max(abs(A(i,j)))  is not a consistent matrix norm.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] NORM   
+   > \verbatim   
+   >          NORM is CHARACTER*1   
+   >          Specifies the value to be returned in DLANSY as described   
+   >          above.   
+   > \endverbatim   
+   >   
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          Specifies whether the upper or lower triangular part of the   
+   >          symmetric matrix A is to be referenced.   
+   >          = 'U':  Upper triangular part of A is referenced   
+   >          = 'L':  Lower triangular part of A is referenced   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.  When N = 0, DLANSY is   
+   >          set to zero.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The symmetric matrix A.  If UPLO = 'U', the leading n by n   
+   >          upper triangular part of A contains the upper triangular part   
+   >          of the matrix A, and the strictly lower triangular part of A   
+   >          is not referenced.  If UPLO = 'L', the leading n by n lower   
+   >          triangular part of A contains the lower triangular part of   
+   >          the matrix A, and the strictly upper triangular part of A is   
+   >          not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(N,1).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)),   
+   >          where LWORK >= N when NORM = 'I' or '1' or 'O'; otherwise,   
+   >          WORK is not referenced.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleSYauxiliary   
+
+    ===================================================================== */
+doublereal igraphdlansy_(char *norm, char *uplo, integer *n, doublereal *a, integer 
+	*lda, doublereal *work)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+    doublereal ret_val, d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j;
+    doublereal sum, absa, scale;
+    extern logical igraphlsame_(char *, char *);
+    doublereal value = 0.;
+    extern logical igraphdisnan_(doublereal *);
+    extern /* Subroutine */ int igraphdlassq_(integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --work;
+
+    /* Function Body */
+    if (*n == 0) {
+	value = 0.;
+    } else if (igraphlsame_(norm, "M")) {
+
+/*        Find max(abs(A(i,j))). */
+
+	value = 0.;
+	if (igraphlsame_(uplo, "U")) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = j;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    sum = (d__1 = a[i__ + j * a_dim1], abs(d__1));
+		    if (value < sum || igraphdisnan_(&sum)) {
+			value = sum;
+		    }
+/* L10: */
+		}
+/* L20: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *n;
+		for (i__ = j; i__ <= i__2; ++i__) {
+		    sum = (d__1 = a[i__ + j * a_dim1], abs(d__1));
+		    if (value < sum || igraphdisnan_(&sum)) {
+			value = sum;
+		    }
+/* L30: */
+		}
+/* L40: */
+	    }
+	}
+    } else if (igraphlsame_(norm, "I") || igraphlsame_(norm, "O") || *(unsigned char *)norm == '1') {
+
+/*        Find normI(A) ( = norm1(A), since A is symmetric). */
+
+	value = 0.;
+	if (igraphlsame_(uplo, "U")) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		sum = 0.;
+		i__2 = j - 1;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    absa = (d__1 = a[i__ + j * a_dim1], abs(d__1));
+		    sum += absa;
+		    work[i__] += absa;
+/* L50: */
+		}
+		work[j] = sum + (d__1 = a[j + j * a_dim1], abs(d__1));
+/* L60: */
+	    }
+	    i__1 = *n;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		sum = work[i__];
+		if (value < sum || igraphdisnan_(&sum)) {
+		    value = sum;
+		}
+/* L70: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		work[i__] = 0.;
+/* L80: */
+	    }
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		sum = work[j] + (d__1 = a[j + j * a_dim1], abs(d__1));
+		i__2 = *n;
+		for (i__ = j + 1; i__ <= i__2; ++i__) {
+		    absa = (d__1 = a[i__ + j * a_dim1], abs(d__1));
+		    sum += absa;
+		    work[i__] += absa;
+/* L90: */
+		}
+		if (value < sum || igraphdisnan_(&sum)) {
+		    value = sum;
+		}
+/* L100: */
+	    }
+	}
+    } else if (igraphlsame_(norm, "F") || igraphlsame_(norm, "E")) {
+
+/*        Find normF(A). */
+
+	scale = 0.;
+	sum = 1.;
+	if (igraphlsame_(uplo, "U")) {
+	    i__1 = *n;
+	    for (j = 2; j <= i__1; ++j) {
+		i__2 = j - 1;
+		igraphdlassq_(&i__2, &a[j * a_dim1 + 1], &c__1, &scale, &sum);
+/* L110: */
+	    }
+	} else {
+	    i__1 = *n - 1;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *n - j;
+		igraphdlassq_(&i__2, &a[j + 1 + j * a_dim1], &c__1, &scale, &sum);
+/* L120: */
+	    }
+	}
+	sum *= 2;
+	i__1 = *lda + 1;
+	igraphdlassq_(n, &a[a_offset], &i__1, &scale, &sum);
+	value = scale * sqrt(sum);
+    }
+
+    ret_val = value;
+    return ret_val;
+
+/*     End of DLANSY */
+
+} /* igraphdlansy_ */
+
diff --git a/igraph/src/dlanv2.c b/igraph/src/dlanv2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlanv2.c
@@ -0,0 +1,310 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b4 = 1.;
+
+/* > \brief \b DLANV2 computes the Schur factorization of a real 2-by-2 nonsymmetric matrix in standard form. 
+  
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLANV2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlanv2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlanv2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlanv2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLANV2( A, B, C, D, RT1R, RT1I, RT2R, RT2I, CS, SN )   
+
+         DOUBLE PRECISION   A, B, C, CS, D, RT1I, RT1R, RT2I, RT2R, SN   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLANV2 computes the Schur factorization of a real 2-by-2 nonsymmetric   
+   > matrix in standard form:   
+   >   
+   >      [ A  B ] = [ CS -SN ] [ AA  BB ] [ CS  SN ]   
+   >      [ C  D ]   [ SN  CS ] [ CC  DD ] [-SN  CS ]   
+   >   
+   > where either   
+   > 1) CC = 0 so that AA and DD are real eigenvalues of the matrix, or   
+   > 2) AA = DD and BB*CC < 0, so that AA + or - sqrt(BB*CC) are complex   
+   > conjugate eigenvalues.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION   
+   >          On entry, the elements of the input matrix.   
+   >          On exit, they are overwritten by the elements of the   
+   >          standardised Schur form.   
+   > \endverbatim   
+   >   
+   > \param[out] RT1R   
+   > \verbatim   
+   >          RT1R is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[out] RT1I   
+   > \verbatim   
+   >          RT1I is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[out] RT2R   
+   > \verbatim   
+   >          RT2R is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[out] RT2I   
+   > \verbatim   
+   >          RT2I is DOUBLE PRECISION   
+   >          The real and imaginary parts of the eigenvalues. If the   
+   >          eigenvalues are a complex conjugate pair, RT1I > 0.   
+   > \endverbatim   
+   >   
+   > \param[out] CS   
+   > \verbatim   
+   >          CS is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[out] SN   
+   > \verbatim   
+   >          SN is DOUBLE PRECISION   
+   >          Parameters of the rotation matrix.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  Modified by V. Sima, Research Institute for Informatics, Bucharest,   
+   >  Romania, to reduce the risk of cancellation errors,   
+   >  when computing real eigenvalues, and to ensure, if possible, that   
+   >  abs(RT1R) >= abs(RT2R).   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlanv2_(doublereal *a, doublereal *b, doublereal *c__, 
+	doublereal *d__, doublereal *rt1r, doublereal *rt1i, doublereal *rt2r,
+	 doublereal *rt2i, doublereal *cs, doublereal *sn)
+{
+    /* System generated locals */
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double d_sign(doublereal *, doublereal *), sqrt(doublereal);
+
+    /* Local variables */
+    doublereal p, z__, aa, bb, cc, dd, cs1, sn1, sab, sac, eps, tau, temp, 
+	    scale, bcmax, bcmis, sigma;
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *), igraphdlamch_(char *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ===================================================================== */
+
+
+    eps = igraphdlamch_("P");
+    if (*c__ == 0.) {
+	*cs = 1.;
+	*sn = 0.;
+	goto L10;
+
+    } else if (*b == 0.) {
+
+/*        Swap rows and columns */
+
+	*cs = 0.;
+	*sn = 1.;
+	temp = *d__;
+	*d__ = *a;
+	*a = temp;
+	*b = -(*c__);
+	*c__ = 0.;
+	goto L10;
+    } else if (*a - *d__ == 0. && d_sign(&c_b4, b) != d_sign(&c_b4, c__)) {
+	*cs = 1.;
+	*sn = 0.;
+	goto L10;
+    } else {
+
+	temp = *a - *d__;
+	p = temp * .5;
+/* Computing MAX */
+	d__1 = abs(*b), d__2 = abs(*c__);
+	bcmax = max(d__1,d__2);
+/* Computing MIN */
+	d__1 = abs(*b), d__2 = abs(*c__);
+	bcmis = min(d__1,d__2) * d_sign(&c_b4, b) * d_sign(&c_b4, c__);
+/* Computing MAX */
+	d__1 = abs(p);
+	scale = max(d__1,bcmax);
+	z__ = p / scale * p + bcmax / scale * bcmis;
+
+/*        If Z is of the order of the machine accuracy, postpone the   
+          decision on the nature of eigenvalues */
+
+	if (z__ >= eps * 4.) {
+
+/*           Real eigenvalues. Compute A and D. */
+
+	    d__1 = sqrt(scale) * sqrt(z__);
+	    z__ = p + d_sign(&d__1, &p);
+	    *a = *d__ + z__;
+	    *d__ -= bcmax / z__ * bcmis;
+
+/*           Compute B and the rotation matrix */
+
+	    tau = igraphdlapy2_(c__, &z__);
+	    *cs = z__ / tau;
+	    *sn = *c__ / tau;
+	    *b -= *c__;
+	    *c__ = 0.;
+	} else {
+
+/*           Complex eigenvalues, or real (almost) equal eigenvalues.   
+             Make diagonal elements equal. */
+
+	    sigma = *b + *c__;
+	    tau = igraphdlapy2_(&sigma, &temp);
+	    *cs = sqrt((abs(sigma) / tau + 1.) * .5);
+	    *sn = -(p / (tau * *cs)) * d_sign(&c_b4, &sigma);
+
+/*           Compute [ AA  BB ] = [ A  B ] [ CS -SN ]   
+                     [ CC  DD ]   [ C  D ] [ SN  CS ] */
+
+	    aa = *a * *cs + *b * *sn;
+	    bb = -(*a) * *sn + *b * *cs;
+	    cc = *c__ * *cs + *d__ * *sn;
+	    dd = -(*c__) * *sn + *d__ * *cs;
+
+/*           Compute [ A  B ] = [ CS  SN ] [ AA  BB ]   
+                     [ C  D ]   [-SN  CS ] [ CC  DD ] */
+
+	    *a = aa * *cs + cc * *sn;
+	    *b = bb * *cs + dd * *sn;
+	    *c__ = -aa * *sn + cc * *cs;
+	    *d__ = -bb * *sn + dd * *cs;
+
+	    temp = (*a + *d__) * .5;
+	    *a = temp;
+	    *d__ = temp;
+
+	    if (*c__ != 0.) {
+		if (*b != 0.) {
+		    if (d_sign(&c_b4, b) == d_sign(&c_b4, c__)) {
+
+/*                    Real eigenvalues: reduce to upper triangular form */
+
+			sab = sqrt((abs(*b)));
+			sac = sqrt((abs(*c__)));
+			d__1 = sab * sac;
+			p = d_sign(&d__1, c__);
+			tau = 1. / sqrt((d__1 = *b + *c__, abs(d__1)));
+			*a = temp + p;
+			*d__ = temp - p;
+			*b -= *c__;
+			*c__ = 0.;
+			cs1 = sab * tau;
+			sn1 = sac * tau;
+			temp = *cs * cs1 - *sn * sn1;
+			*sn = *cs * sn1 + *sn * cs1;
+			*cs = temp;
+		    }
+		} else {
+		    *b = -(*c__);
+		    *c__ = 0.;
+		    temp = *cs;
+		    *cs = -(*sn);
+		    *sn = temp;
+		}
+	    }
+	}
+
+    }
+
+L10:
+
+/*     Store eigenvalues in (RT1R,RT1I) and (RT2R,RT2I). */
+
+    *rt1r = *a;
+    *rt2r = *d__;
+    if (*c__ == 0.) {
+	*rt1i = 0.;
+	*rt2i = 0.;
+    } else {
+	*rt1i = sqrt((abs(*b))) * sqrt((abs(*c__)));
+	*rt2i = -(*rt1i);
+    }
+    return 0;
+
+/*     End of DLANV2 */
+
+} /* igraphdlanv2_ */
+
diff --git a/igraph/src/dlapy2.c b/igraph/src/dlapy2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlapy2.c
@@ -0,0 +1,116 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAPY2 returns sqrt(x2+y2).   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAPY2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlapy2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlapy2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlapy2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         DOUBLE PRECISION FUNCTION DLAPY2( X, Y )   
+
+         DOUBLE PRECISION   X, Y   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAPY2 returns sqrt(x**2+y**2), taking care not to cause unnecessary   
+   > overflow.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] Y   
+   > \verbatim   
+   >          Y is DOUBLE PRECISION   
+   >          X and Y specify the values x and y.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    ===================================================================== */
+doublereal igraphdlapy2_(doublereal *x, doublereal *y)
+{
+    /* System generated locals */
+    doublereal ret_val, d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    doublereal w, z__, xabs, yabs;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ===================================================================== */
+
+
+    xabs = abs(*x);
+    yabs = abs(*y);
+    w = max(xabs,yabs);
+    z__ = min(xabs,yabs);
+    if (z__ == 0.) {
+	ret_val = w;
+    } else {
+/* Computing 2nd power */
+	d__1 = z__ / w;
+	ret_val = w * sqrt(d__1 * d__1 + 1.);
+    }
+    return ret_val;
+
+/*     End of DLAPY2 */
+
+} /* igraphdlapy2_ */
+
diff --git a/igraph/src/dlaqr0.c b/igraph/src/dlaqr0.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaqr0.c
@@ -0,0 +1,849 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__13 = 13;
+static integer c__15 = 15;
+static integer c_n1 = -1;
+static integer c__12 = 12;
+static integer c__14 = 14;
+static integer c__16 = 16;
+static logical c_false = FALSE_;
+static integer c__1 = 1;
+static integer c__3 = 3;
+
+/* > \brief \b DLAQR0 computes the eigenvalues of a Hessenberg matrix, and optionally the matrices from the Sc
+hur decomposition.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAQR0 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaqr0.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaqr0.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaqr0.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAQR0( WANTT, WANTZ, N, ILO, IHI, H, LDH, WR, WI,   
+                            ILOZ, IHIZ, Z, LDZ, WORK, LWORK, INFO )   
+
+         INTEGER            IHI, IHIZ, ILO, ILOZ, INFO, LDH, LDZ, LWORK, N   
+         LOGICAL            WANTT, WANTZ   
+         DOUBLE PRECISION   H( LDH, * ), WI( * ), WORK( * ), WR( * ),   
+        $                   Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >    DLAQR0 computes the eigenvalues of a Hessenberg matrix H   
+   >    and, optionally, the matrices T and Z from the Schur decomposition   
+   >    H = Z T Z**T, where T is an upper quasi-triangular matrix (the   
+   >    Schur form), and Z is the orthogonal matrix of Schur vectors.   
+   >   
+   >    Optionally Z may be postmultiplied into an input orthogonal   
+   >    matrix Q so that this routine can give the Schur factorization   
+   >    of a matrix A which has been reduced to the Hessenberg form H   
+   >    by the orthogonal matrix Q:  A = Q*H*Q**T = (QZ)*T*(QZ)**T.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] WANTT   
+   > \verbatim   
+   >          WANTT is LOGICAL   
+   >          = .TRUE. : the full Schur form T is required;   
+   >          = .FALSE.: only eigenvalues are required.   
+   > \endverbatim   
+   >   
+   > \param[in] WANTZ   
+   > \verbatim   
+   >          WANTZ is LOGICAL   
+   >          = .TRUE. : the matrix of Schur vectors Z is required;   
+   >          = .FALSE.: Schur vectors are not required.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >           The order of the matrix H.  N .GE. 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >           It is assumed that H is already upper triangular in rows   
+   >           and columns 1:ILO-1 and IHI+1:N and, if ILO.GT.1,   
+   >           H(ILO,ILO-1) is zero. ILO and IHI are normally set by a   
+   >           previous call to DGEBAL, and then passed to DGEHRD when the   
+   >           matrix output by DGEBAL is reduced to Hessenberg form.   
+   >           Otherwise, ILO and IHI should be set to 1 and N,   
+   >           respectively.  If N.GT.0, then 1.LE.ILO.LE.IHI.LE.N.   
+   >           If N = 0, then ILO = 1 and IHI = 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] H   
+   > \verbatim   
+   >          H is DOUBLE PRECISION array, dimension (LDH,N)   
+   >           On entry, the upper Hessenberg matrix H.   
+   >           On exit, if INFO = 0 and WANTT is .TRUE., then H contains   
+   >           the upper quasi-triangular matrix T from the Schur   
+   >           decomposition (the Schur form); 2-by-2 diagonal blocks   
+   >           (corresponding to complex conjugate pairs of eigenvalues)   
+   >           are returned in standard form, with H(i,i) = H(i+1,i+1)   
+   >           and H(i+1,i)*H(i,i+1).LT.0. If INFO = 0 and WANTT is   
+   >           .FALSE., then the contents of H are unspecified on exit.   
+   >           (The output value of H when INFO.GT.0 is given under the   
+   >           description of INFO below.)   
+   >   
+   >           This subroutine may explicitly set H(i,j) = 0 for i.GT.j and   
+   >           j = 1, 2, ... ILO-1 or j = IHI+1, IHI+2, ... N.   
+   > \endverbatim   
+   >   
+   > \param[in] LDH   
+   > \verbatim   
+   >          LDH is INTEGER   
+   >           The leading dimension of the array H. LDH .GE. max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] WR   
+   > \verbatim   
+   >          WR is DOUBLE PRECISION array, dimension (IHI)   
+   > \endverbatim   
+   >   
+   > \param[out] WI   
+   > \verbatim   
+   >          WI is DOUBLE PRECISION array, dimension (IHI)   
+   >           The real and imaginary parts, respectively, of the computed   
+   >           eigenvalues of H(ILO:IHI,ILO:IHI) are stored in WR(ILO:IHI)   
+   >           and WI(ILO:IHI). If two eigenvalues are computed as a   
+   >           complex conjugate pair, they are stored in consecutive   
+   >           elements of WR and WI, say the i-th and (i+1)th, with   
+   >           WI(i) .GT. 0 and WI(i+1) .LT. 0. If WANTT is .TRUE., then   
+   >           the eigenvalues are stored in the same order as on the   
+   >           diagonal of the Schur form returned in H, with   
+   >           WR(i) = H(i,i) and, if H(i:i+1,i:i+1) is a 2-by-2 diagonal   
+   >           block, WI(i) = sqrt(-H(i+1,i)*H(i,i+1)) and   
+   >           WI(i+1) = -WI(i).   
+   > \endverbatim   
+   >   
+   > \param[in] ILOZ   
+   > \verbatim   
+   >          ILOZ is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHIZ   
+   > \verbatim   
+   >          IHIZ is INTEGER   
+   >           Specify the rows of Z to which transformations must be   
+   >           applied if WANTZ is .TRUE..   
+   >           1 .LE. ILOZ .LE. ILO; IHI .LE. IHIZ .LE. N.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ,IHI)   
+   >           If WANTZ is .FALSE., then Z is not referenced.   
+   >           If WANTZ is .TRUE., then Z(ILO:IHI,ILOZ:IHIZ) is   
+   >           replaced by Z(ILO:IHI,ILOZ:IHIZ)*U where U is the   
+   >           orthogonal Schur factor of H(ILO:IHI,ILO:IHI).   
+   >           (The output value of Z when INFO.GT.0 is given under   
+   >           the description of INFO below.)   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >           The leading dimension of the array Z.  if WANTZ is .TRUE.   
+   >           then LDZ.GE.MAX(1,IHIZ).  Otherwize, LDZ.GE.1.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension LWORK   
+   >           On exit, if LWORK = -1, WORK(1) returns an estimate of   
+   >           the optimal value for LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >           The dimension of the array WORK.  LWORK .GE. max(1,N)   
+   >           is sufficient, but LWORK typically as large as 6*N may   
+   >           be required for optimal performance.  A workspace query   
+   >           to determine the optimal workspace size is recommended.   
+   >   
+   >           If LWORK = -1, then DLAQR0 does a workspace query.   
+   >           In this case, DLAQR0 checks the input parameters and   
+   >           estimates the optimal workspace size for the given   
+   >           values of N, ILO and IHI.  The estimate is returned   
+   >           in WORK(1).  No error message related to LWORK is   
+   >           issued by XERBLA.  Neither H nor Z are accessed.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >             =  0:  successful exit   
+   >           .GT. 0:  if INFO = i, DLAQR0 failed to compute all of   
+   >                the eigenvalues.  Elements 1:ilo-1 and i+1:n of WR   
+   >                and WI contain those eigenvalues which have been   
+   >                successfully computed.  (Failures are rare.)   
+   >   
+   >                If INFO .GT. 0 and WANT is .FALSE., then on exit,   
+   >                the remaining unconverged eigenvalues are the eigen-   
+   >                values of the upper Hessenberg matrix rows and   
+   >                columns ILO through INFO of the final, output   
+   >                value of H.   
+   >   
+   >                If INFO .GT. 0 and WANTT is .TRUE., then on exit   
+   >   
+   >           (*)  (initial value of H)*U  = U*(final value of H)   
+   >   
+   >                where U is an orthogonal matrix.  The final   
+   >                value of H is upper Hessenberg and quasi-triangular   
+   >                in rows and columns INFO+1 through IHI.   
+   >   
+   >                If INFO .GT. 0 and WANTZ is .TRUE., then on exit   
+   >   
+   >                  (final value of Z(ILO:IHI,ILOZ:IHIZ)   
+   >                   =  (initial value of Z(ILO:IHI,ILOZ:IHIZ)*U   
+   >   
+   >                where U is the orthogonal matrix in (*) (regard-   
+   >                less of the value of WANTT.)   
+   >   
+   >                If INFO .GT. 0 and WANTZ is .FALSE., then Z is not   
+   >                accessed.   
+   > \endverbatim   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >       Karen Braman and Ralph Byers, Department of Mathematics,   
+   >       University of Kansas, USA   
+
+   > \par References:   
+    ================   
+   >   
+   >       K. Braman, R. Byers and R. Mathias, The Multi-Shift QR   
+   >       Algorithm Part I: Maintaining Well Focused Shifts, and Level 3   
+   >       Performance, SIAM Journal of Matrix Analysis, volume 23, pages   
+   >       929--947, 2002.   
+   > \n   
+   >       K. Braman, R. Byers and R. Mathias, The Multi-Shift QR   
+   >       Algorithm Part II: Aggressive Early Deflation, SIAM Journal   
+   >       of Matrix Analysis, volume 23, pages 948--973, 2002.   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlaqr0_(logical *wantt, logical *wantz, integer *n, 
+	integer *ilo, integer *ihi, doublereal *h__, integer *ldh, doublereal 
+	*wr, doublereal *wi, integer *iloz, integer *ihiz, doublereal *z__, 
+	integer *ldz, doublereal *work, integer *lwork, integer *info)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, z_dim1, z_offset, i__1, i__2, i__3, i__4, i__5;
+    doublereal d__1, d__2, d__3, d__4;
+
+    /* Local variables */
+    integer i__, k;
+    doublereal aa, bb, cc, dd;
+    integer ld;
+    doublereal cs;
+    integer nh, it, ks, kt;
+    doublereal sn;
+    integer ku, kv, ls, ns;
+    doublereal ss;
+    integer nw, inf, kdu, nho, nve, kwh, nsr, nwr, kwv, ndec, ndfl, kbot, 
+	    nmin;
+    doublereal swap;
+    integer ktop;
+    doublereal zdum[1]	/* was [1][1] */;
+    integer kacc22, itmax, nsmax, nwmax, kwtop;
+    extern /* Subroutine */ int igraphdlanv2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *), igraphdlaqr3_(
+	    logical *, logical *, integer *, integer *, integer *, integer *, 
+	    doublereal *, integer *, integer *, integer *, doublereal *, 
+	    integer *, integer *, integer *, doublereal *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *), 
+	    igraphdlaqr4_(logical *, logical *, integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *, 
+	    integer *), igraphdlaqr5_(logical *, logical *, integer *, integer *, 
+	    integer *, integer *, integer *, doublereal *, doublereal *, 
+	    doublereal *, integer *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *, 
+	    integer *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *);
+    integer nibble;
+    extern /* Subroutine */ int igraphdlahqr_(logical *, logical *, integer *, 
+	    integer *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *), igraphdlacpy_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    char jbcmpz[2];
+    integer nwupbd;
+    logical sorted;
+    integer lwkopt;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ================================================================   
+
+
+       ==== Matrices of order NTINY or smaller must be processed by   
+       .    DLAHQR because of insufficient subdiagonal scratch space.   
+       .    (This is a hard limit.) ====   
+
+       ==== Exceptional deflation windows:  try to cure rare   
+       .    slow convergence by varying the size of the   
+       .    deflation window after KEXNW iterations. ====   
+
+       ==== Exceptional shifts: try to cure rare slow convergence   
+       .    with ad-hoc exceptional shifts every KEXSH iterations.   
+       .    ====   
+
+       ==== The constants WILK1 and WILK2 are used to form the   
+       .    exceptional shifts. ====   
+       Parameter adjustments */
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    --wr;
+    --wi;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+
+/*     ==== Quick return for N = 0: nothing to do. ==== */
+
+    if (*n == 0) {
+	work[1] = 1.;
+	return 0;
+    }
+
+    if (*n <= 11) {
+
+/*        ==== Tiny matrices must use DLAHQR. ==== */
+
+	lwkopt = 1;
+	if (*lwork != -1) {
+	    igraphdlahqr_(wantt, wantz, n, ilo, ihi, &h__[h_offset], ldh, &wr[1], &
+		    wi[1], iloz, ihiz, &z__[z_offset], ldz, info);
+	}
+    } else {
+
+/*        ==== Use small bulge multi-shift QR with aggressive early   
+          .    deflation on larger-than-tiny matrices. ====   
+
+          ==== Hope for the best. ==== */
+
+	*info = 0;
+
+/*        ==== Set up job flags for ILAENV. ==== */
+
+	if (*wantt) {
+	    *(unsigned char *)jbcmpz = 'S';
+	} else {
+	    *(unsigned char *)jbcmpz = 'E';
+	}
+	if (*wantz) {
+	    *(unsigned char *)&jbcmpz[1] = 'V';
+	} else {
+	    *(unsigned char *)&jbcmpz[1] = 'N';
+	}
+
+/*        ==== NWR = recommended deflation window size.  At this   
+          .    point,  N .GT. NTINY = 11, so there is enough   
+          .    subdiagonal workspace for NWR.GE.2 as required.   
+          .    (In fact, there is enough subdiagonal space for   
+          .    NWR.GE.3.) ==== */
+
+	nwr = igraphilaenv_(&c__13, "DLAQR0", jbcmpz, n, ilo, ihi, lwork, (ftnlen)6,
+		 (ftnlen)2);
+	nwr = max(2,nwr);
+/* Computing MIN */
+	i__1 = *ihi - *ilo + 1, i__2 = (*n - 1) / 3, i__1 = min(i__1,i__2);
+	nwr = min(i__1,nwr);
+
+/*        ==== NSR = recommended number of simultaneous shifts.   
+          .    At this point N .GT. NTINY = 11, so there is at   
+          .    enough subdiagonal workspace for NSR to be even   
+          .    and greater than or equal to two as required. ==== */
+
+	nsr = igraphilaenv_(&c__15, "DLAQR0", jbcmpz, n, ilo, ihi, lwork, (ftnlen)6,
+		 (ftnlen)2);
+/* Computing MIN */
+	i__1 = nsr, i__2 = (*n + 6) / 9, i__1 = min(i__1,i__2), i__2 = *ihi - 
+		*ilo;
+	nsr = min(i__1,i__2);
+/* Computing MAX */
+	i__1 = 2, i__2 = nsr - nsr % 2;
+	nsr = max(i__1,i__2);
+
+/*        ==== Estimate optimal workspace ====   
+
+          ==== Workspace query call to DLAQR3 ==== */
+
+	i__1 = nwr + 1;
+	igraphdlaqr3_(wantt, wantz, n, ilo, ihi, &i__1, &h__[h_offset], ldh, iloz, 
+		ihiz, &z__[z_offset], ldz, &ls, &ld, &wr[1], &wi[1], &h__[
+		h_offset], ldh, n, &h__[h_offset], ldh, n, &h__[h_offset], 
+		ldh, &work[1], &c_n1);
+
+/*        ==== Optimal workspace = MAX(DLAQR5, DLAQR3) ====   
+
+   Computing MAX */
+	i__1 = nsr * 3 / 2, i__2 = (integer) work[1];
+	lwkopt = max(i__1,i__2);
+
+/*        ==== Quick return in case of workspace query. ==== */
+
+	if (*lwork == -1) {
+	    work[1] = (doublereal) lwkopt;
+	    return 0;
+	}
+
+/*        ==== DLAHQR/DLAQR0 crossover point ==== */
+
+	nmin = igraphilaenv_(&c__12, "DLAQR0", jbcmpz, n, ilo, ihi, lwork, (ftnlen)
+		6, (ftnlen)2);
+	nmin = max(11,nmin);
+
+/*        ==== Nibble crossover point ==== */
+
+	nibble = igraphilaenv_(&c__14, "DLAQR0", jbcmpz, n, ilo, ihi, lwork, (
+		ftnlen)6, (ftnlen)2);
+	nibble = max(0,nibble);
+
+/*        ==== Accumulate reflections during ttswp?  Use block   
+          .    2-by-2 structure during matrix-matrix multiply? ==== */
+
+	kacc22 = igraphilaenv_(&c__16, "DLAQR0", jbcmpz, n, ilo, ihi, lwork, (
+		ftnlen)6, (ftnlen)2);
+	kacc22 = max(0,kacc22);
+	kacc22 = min(2,kacc22);
+
+/*        ==== NWMAX = the largest possible deflation window for   
+          .    which there is sufficient workspace. ====   
+
+   Computing MIN */
+	i__1 = (*n - 1) / 3, i__2 = *lwork / 2;
+	nwmax = min(i__1,i__2);
+	nw = nwmax;
+
+/*        ==== NSMAX = the Largest number of simultaneous shifts   
+          .    for which there is sufficient workspace. ====   
+
+   Computing MIN */
+	i__1 = (*n + 6) / 9, i__2 = (*lwork << 1) / 3;
+	nsmax = min(i__1,i__2);
+	nsmax -= nsmax % 2;
+
+/*        ==== NDFL: an iteration count restarted at deflation. ==== */
+
+	ndfl = 1;
+
+/*        ==== ITMAX = iteration limit ====   
+
+   Computing MAX */
+	i__1 = 10, i__2 = *ihi - *ilo + 1;
+	itmax = max(i__1,i__2) * 30;
+
+/*        ==== Last row and column in the active block ==== */
+
+	kbot = *ihi;
+
+/*        ==== Main Loop ==== */
+
+	i__1 = itmax;
+	for (it = 1; it <= i__1; ++it) {
+
+/*           ==== Done when KBOT falls below ILO ==== */
+
+	    if (kbot < *ilo) {
+		goto L90;
+	    }
+
+/*           ==== Locate active block ==== */
+
+	    i__2 = *ilo + 1;
+	    for (k = kbot; k >= i__2; --k) {
+		if (h__[k + (k - 1) * h_dim1] == 0.) {
+		    goto L20;
+		}
+/* L10: */
+	    }
+	    k = *ilo;
+L20:
+	    ktop = k;
+
+/*           ==== Select deflation window size:   
+             .    Typical Case:   
+             .      If possible and advisable, nibble the entire   
+             .      active block.  If not, use size MIN(NWR,NWMAX)   
+             .      or MIN(NWR+1,NWMAX) depending upon which has   
+             .      the smaller corresponding subdiagonal entry   
+             .      (a heuristic).   
+             .   
+             .    Exceptional Case:   
+             .      If there have been no deflations in KEXNW or   
+             .      more iterations, then vary the deflation window   
+             .      size.   At first, because, larger windows are,   
+             .      in general, more powerful than smaller ones,   
+             .      rapidly increase the window to the maximum possible.   
+             .      Then, gradually reduce the window size. ==== */
+
+	    nh = kbot - ktop + 1;
+	    nwupbd = min(nh,nwmax);
+	    if (ndfl < 5) {
+		nw = min(nwupbd,nwr);
+	    } else {
+/* Computing MIN */
+		i__2 = nwupbd, i__3 = nw << 1;
+		nw = min(i__2,i__3);
+	    }
+	    if (nw < nwmax) {
+		if (nw >= nh - 1) {
+		    nw = nh;
+		} else {
+		    kwtop = kbot - nw + 1;
+		    if ((d__1 = h__[kwtop + (kwtop - 1) * h_dim1], abs(d__1)) 
+			    > (d__2 = h__[kwtop - 1 + (kwtop - 2) * h_dim1], 
+			    abs(d__2))) {
+			++nw;
+		    }
+		}
+	    }
+	    if (ndfl < 5) {
+		ndec = -1;
+	    } else if (ndec >= 0 || nw >= nwupbd) {
+		++ndec;
+		if (nw - ndec < 2) {
+		    ndec = 0;
+		}
+		nw -= ndec;
+	    }
+
+/*           ==== Aggressive early deflation:   
+             .    split workspace under the subdiagonal into   
+             .      - an nw-by-nw work array V in the lower   
+             .        left-hand-corner,   
+             .      - an NW-by-at-least-NW-but-more-is-better   
+             .        (NW-by-NHO) horizontal work array along   
+             .        the bottom edge,   
+             .      - an at-least-NW-but-more-is-better (NHV-by-NW)   
+             .        vertical work array along the left-hand-edge.   
+             .        ==== */
+
+	    kv = *n - nw + 1;
+	    kt = nw + 1;
+	    nho = *n - nw - 1 - kt + 1;
+	    kwv = nw + 2;
+	    nve = *n - nw - kwv + 1;
+
+/*           ==== Aggressive early deflation ==== */
+
+	    igraphdlaqr3_(wantt, wantz, n, &ktop, &kbot, &nw, &h__[h_offset], ldh, 
+		    iloz, ihiz, &z__[z_offset], ldz, &ls, &ld, &wr[1], &wi[1],
+		     &h__[kv + h_dim1], ldh, &nho, &h__[kv + kt * h_dim1], 
+		    ldh, &nve, &h__[kwv + h_dim1], ldh, &work[1], lwork);
+
+/*           ==== Adjust KBOT accounting for new deflations. ==== */
+
+	    kbot -= ld;
+
+/*           ==== KS points to the shifts. ==== */
+
+	    ks = kbot - ls + 1;
+
+/*           ==== Skip an expensive QR sweep if there is a (partly   
+             .    heuristic) reason to expect that many eigenvalues   
+             .    will deflate without it.  Here, the QR sweep is   
+             .    skipped if many eigenvalues have just been deflated   
+             .    or if the remaining active block is small. */
+
+	    if (ld == 0 || ld * 100 <= nw * nibble && kbot - ktop + 1 > min(
+		    nmin,nwmax)) {
+
+/*              ==== NS = nominal number of simultaneous shifts.   
+                .    This may be lowered (slightly) if DLAQR3   
+                .    did not provide that many shifts. ====   
+
+   Computing MIN   
+   Computing MAX */
+		i__4 = 2, i__5 = kbot - ktop;
+		i__2 = min(nsmax,nsr), i__3 = max(i__4,i__5);
+		ns = min(i__2,i__3);
+		ns -= ns % 2;
+
+/*              ==== If there have been no deflations   
+                .    in a multiple of KEXSH iterations,   
+                .    then try exceptional shifts.   
+                .    Otherwise use shifts provided by   
+                .    DLAQR3 above or from the eigenvalues   
+                .    of a trailing principal submatrix. ==== */
+
+		if (ndfl % 6 == 0) {
+		    ks = kbot - ns + 1;
+/* Computing MAX */
+		    i__3 = ks + 1, i__4 = ktop + 2;
+		    i__2 = max(i__3,i__4);
+		    for (i__ = kbot; i__ >= i__2; i__ += -2) {
+			ss = (d__1 = h__[i__ + (i__ - 1) * h_dim1], abs(d__1))
+				 + (d__2 = h__[i__ - 1 + (i__ - 2) * h_dim1], 
+				abs(d__2));
+			aa = ss * .75 + h__[i__ + i__ * h_dim1];
+			bb = ss;
+			cc = ss * -.4375;
+			dd = aa;
+			igraphdlanv2_(&aa, &bb, &cc, &dd, &wr[i__ - 1], &wi[i__ - 1]
+				, &wr[i__], &wi[i__], &cs, &sn);
+/* L30: */
+		    }
+		    if (ks == ktop) {
+			wr[ks + 1] = h__[ks + 1 + (ks + 1) * h_dim1];
+			wi[ks + 1] = 0.;
+			wr[ks] = wr[ks + 1];
+			wi[ks] = wi[ks + 1];
+		    }
+		} else {
+
+/*                 ==== Got NS/2 or fewer shifts? Use DLAQR4 or   
+                   .    DLAHQR on a trailing principal submatrix to   
+                   .    get more. (Since NS.LE.NSMAX.LE.(N+6)/9,   
+                   .    there is enough space below the subdiagonal   
+                   .    to fit an NS-by-NS scratch array.) ==== */
+
+		    if (kbot - ks + 1 <= ns / 2) {
+			ks = kbot - ns + 1;
+			kt = *n - ns + 1;
+			igraphdlacpy_("A", &ns, &ns, &h__[ks + ks * h_dim1], ldh, &
+				h__[kt + h_dim1], ldh);
+			if (ns > nmin) {
+			    igraphdlaqr4_(&c_false, &c_false, &ns, &c__1, &ns, &h__[
+				    kt + h_dim1], ldh, &wr[ks], &wi[ks], &
+				    c__1, &c__1, zdum, &c__1, &work[1], lwork,
+				     &inf);
+			} else {
+			    igraphdlahqr_(&c_false, &c_false, &ns, &c__1, &ns, &h__[
+				    kt + h_dim1], ldh, &wr[ks], &wi[ks], &
+				    c__1, &c__1, zdum, &c__1, &inf);
+			}
+			ks += inf;
+
+/*                    ==== In case of a rare QR failure use   
+                      .    eigenvalues of the trailing 2-by-2   
+                      .    principal submatrix.  ==== */
+
+			if (ks >= kbot) {
+			    aa = h__[kbot - 1 + (kbot - 1) * h_dim1];
+			    cc = h__[kbot + (kbot - 1) * h_dim1];
+			    bb = h__[kbot - 1 + kbot * h_dim1];
+			    dd = h__[kbot + kbot * h_dim1];
+			    igraphdlanv2_(&aa, &bb, &cc, &dd, &wr[kbot - 1], &wi[
+				    kbot - 1], &wr[kbot], &wi[kbot], &cs, &sn)
+				    ;
+			    ks = kbot - 1;
+			}
+		    }
+
+		    if (kbot - ks + 1 > ns) {
+
+/*                    ==== Sort the shifts (Helps a little)   
+                      .    Bubble sort keeps complex conjugate   
+                      .    pairs together. ==== */
+
+			sorted = FALSE_;
+			i__2 = ks + 1;
+			for (k = kbot; k >= i__2; --k) {
+			    if (sorted) {
+				goto L60;
+			    }
+			    sorted = TRUE_;
+			    i__3 = k - 1;
+			    for (i__ = ks; i__ <= i__3; ++i__) {
+				if ((d__1 = wr[i__], abs(d__1)) + (d__2 = wi[
+					i__], abs(d__2)) < (d__3 = wr[i__ + 1]
+					, abs(d__3)) + (d__4 = wi[i__ + 1], 
+					abs(d__4))) {
+				    sorted = FALSE_;
+
+				    swap = wr[i__];
+				    wr[i__] = wr[i__ + 1];
+				    wr[i__ + 1] = swap;
+
+				    swap = wi[i__];
+				    wi[i__] = wi[i__ + 1];
+				    wi[i__ + 1] = swap;
+				}
+/* L40: */
+			    }
+/* L50: */
+			}
+L60:
+			;
+		    }
+
+/*                 ==== Shuffle shifts into pairs of real shifts   
+                   .    and pairs of complex conjugate shifts   
+                   .    assuming complex conjugate shifts are   
+                   .    already adjacent to one another. (Yes,   
+                   .    they are.)  ==== */
+
+		    i__2 = ks + 2;
+		    for (i__ = kbot; i__ >= i__2; i__ += -2) {
+			if (wi[i__] != -wi[i__ - 1]) {
+
+			    swap = wr[i__];
+			    wr[i__] = wr[i__ - 1];
+			    wr[i__ - 1] = wr[i__ - 2];
+			    wr[i__ - 2] = swap;
+
+			    swap = wi[i__];
+			    wi[i__] = wi[i__ - 1];
+			    wi[i__ - 1] = wi[i__ - 2];
+			    wi[i__ - 2] = swap;
+			}
+/* L70: */
+		    }
+		}
+
+/*              ==== If there are only two shifts and both are   
+                .    real, then use only one.  ==== */
+
+		if (kbot - ks + 1 == 2) {
+		    if (wi[kbot] == 0.) {
+			if ((d__1 = wr[kbot] - h__[kbot + kbot * h_dim1], abs(
+				d__1)) < (d__2 = wr[kbot - 1] - h__[kbot + 
+				kbot * h_dim1], abs(d__2))) {
+			    wr[kbot - 1] = wr[kbot];
+			} else {
+			    wr[kbot] = wr[kbot - 1];
+			}
+		    }
+		}
+
+/*              ==== Use up to NS of the the smallest magnatiude   
+                .    shifts.  If there aren't NS shifts available,   
+                .    then use them all, possibly dropping one to   
+                .    make the number of shifts even. ====   
+
+   Computing MIN */
+		i__2 = ns, i__3 = kbot - ks + 1;
+		ns = min(i__2,i__3);
+		ns -= ns % 2;
+		ks = kbot - ns + 1;
+
+/*              ==== Small-bulge multi-shift QR sweep:   
+                .    split workspace under the subdiagonal into   
+                .    - a KDU-by-KDU work array U in the lower   
+                .      left-hand-corner,   
+                .    - a KDU-by-at-least-KDU-but-more-is-better   
+                .      (KDU-by-NHo) horizontal work array WH along   
+                .      the bottom edge,   
+                .    - and an at-least-KDU-but-more-is-better-by-KDU   
+                .      (NVE-by-KDU) vertical work WV arrow along   
+                .      the left-hand-edge. ==== */
+
+		kdu = ns * 3 - 3;
+		ku = *n - kdu + 1;
+		kwh = kdu + 1;
+		nho = *n - kdu - 3 - (kdu + 1) + 1;
+		kwv = kdu + 4;
+		nve = *n - kdu - kwv + 1;
+
+/*              ==== Small-bulge multi-shift QR sweep ==== */
+
+		igraphdlaqr5_(wantt, wantz, &kacc22, n, &ktop, &kbot, &ns, &wr[ks], 
+			&wi[ks], &h__[h_offset], ldh, iloz, ihiz, &z__[
+			z_offset], ldz, &work[1], &c__3, &h__[ku + h_dim1], 
+			ldh, &nve, &h__[kwv + h_dim1], ldh, &nho, &h__[ku + 
+			kwh * h_dim1], ldh);
+	    }
+
+/*           ==== Note progress (or the lack of it). ==== */
+
+	    if (ld > 0) {
+		ndfl = 1;
+	    } else {
+		++ndfl;
+	    }
+
+/*           ==== End of main loop ====   
+   L80: */
+	}
+
+/*        ==== Iteration limit exceeded.  Set INFO to show where   
+          .    the problem occurred and exit. ==== */
+
+	*info = kbot;
+L90:
+	;
+    }
+
+/*     ==== Return the optimal value of LWORK. ==== */
+
+    work[1] = (doublereal) lwkopt;
+
+/*     ==== End of DLAQR0 ==== */
+
+    return 0;
+} /* igraphdlaqr0_ */
+
diff --git a/igraph/src/dlaqr1.c b/igraph/src/dlaqr1.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaqr1.c
@@ -0,0 +1,198 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAQR1 sets a scalar multiple of the first column of the product of 2-by-2 or 3-by-3 matrix H a
+nd specified shifts.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAQR1 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaqr1.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaqr1.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaqr1.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAQR1( N, H, LDH, SR1, SI1, SR2, SI2, V )   
+
+         DOUBLE PRECISION   SI1, SI2, SR1, SR2   
+         INTEGER            LDH, N   
+         DOUBLE PRECISION   H( LDH, * ), V( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >      Given a 2-by-2 or 3-by-3 matrix H, DLAQR1 sets v to a   
+   >      scalar multiple of the first column of the product   
+   >   
+   >      (*)  K = (H - (sr1 + i*si1)*I)*(H - (sr2 + i*si2)*I)   
+   >   
+   >      scaling to avoid overflows and most underflows. It   
+   >      is assumed that either   
+   >   
+   >              1) sr1 = sr2 and si1 = -si2   
+   >          or   
+   >              2) si1 = si2 = 0.   
+   >   
+   >      This is useful for starting double implicit shift bulges   
+   >      in the QR algorithm.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is integer   
+   >              Order of the matrix H. N must be either 2 or 3.   
+   > \endverbatim   
+   >   
+   > \param[in] H   
+   > \verbatim   
+   >          H is DOUBLE PRECISION array of dimension (LDH,N)   
+   >              The 2-by-2 or 3-by-3 matrix H in (*).   
+   > \endverbatim   
+   >   
+   > \param[in] LDH   
+   > \verbatim   
+   >          LDH is integer   
+   >              The leading dimension of H as declared in   
+   >              the calling procedure.  LDH.GE.N   
+   > \endverbatim   
+   >   
+   > \param[in] SR1   
+   > \verbatim   
+   >          SR1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] SI1   
+   > \verbatim   
+   >          SI1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] SR2   
+   > \verbatim   
+   >          SR2 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] SI2   
+   > \verbatim   
+   >          SI2 is DOUBLE PRECISION   
+   >              The shifts in (*).   
+   > \endverbatim   
+   >   
+   > \param[out] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array of dimension N   
+   >              A scalar multiple of the first column of the   
+   >              matrix K in (*).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >       Karen Braman and Ralph Byers, Department of Mathematics,   
+   >       University of Kansas, USA   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaqr1_(integer *n, doublereal *h__, integer *ldh, 
+	doublereal *sr1, doublereal *si1, doublereal *sr2, doublereal *si2, 
+	doublereal *v)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset;
+    doublereal d__1, d__2, d__3;
+
+    /* Local variables */
+    doublereal s, h21s, h31s;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ================================================================   
+
+       Parameter adjustments */
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    --v;
+
+    /* Function Body */
+    if (*n == 2) {
+	s = (d__1 = h__[h_dim1 + 1] - *sr2, abs(d__1)) + abs(*si2) + (d__2 = 
+		h__[h_dim1 + 2], abs(d__2));
+	if (s == 0.) {
+	    v[1] = 0.;
+	    v[2] = 0.;
+	} else {
+	    h21s = h__[h_dim1 + 2] / s;
+	    v[1] = h21s * h__[(h_dim1 << 1) + 1] + (h__[h_dim1 + 1] - *sr1) * 
+		    ((h__[h_dim1 + 1] - *sr2) / s) - *si1 * (*si2 / s);
+	    v[2] = h21s * (h__[h_dim1 + 1] + h__[(h_dim1 << 1) + 2] - *sr1 - *
+		    sr2);
+	}
+    } else {
+	s = (d__1 = h__[h_dim1 + 1] - *sr2, abs(d__1)) + abs(*si2) + (d__2 = 
+		h__[h_dim1 + 2], abs(d__2)) + (d__3 = h__[h_dim1 + 3], abs(
+		d__3));
+	if (s == 0.) {
+	    v[1] = 0.;
+	    v[2] = 0.;
+	    v[3] = 0.;
+	} else {
+	    h21s = h__[h_dim1 + 2] / s;
+	    h31s = h__[h_dim1 + 3] / s;
+	    v[1] = (h__[h_dim1 + 1] - *sr1) * ((h__[h_dim1 + 1] - *sr2) / s) 
+		    - *si1 * (*si2 / s) + h__[(h_dim1 << 1) + 1] * h21s + h__[
+		    h_dim1 * 3 + 1] * h31s;
+	    v[2] = h21s * (h__[h_dim1 + 1] + h__[(h_dim1 << 1) + 2] - *sr1 - *
+		    sr2) + h__[h_dim1 * 3 + 2] * h31s;
+	    v[3] = h31s * (h__[h_dim1 + 1] + h__[h_dim1 * 3 + 3] - *sr1 - *
+		    sr2) + h21s * h__[(h_dim1 << 1) + 3];
+	}
+    }
+    return 0;
+} /* igraphdlaqr1_ */
+
diff --git a/igraph/src/dlaqr2.c b/igraph/src/dlaqr2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaqr2.c
@@ -0,0 +1,821 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static doublereal c_b12 = 0.;
+static doublereal c_b13 = 1.;
+static logical c_true = TRUE_;
+
+/* > \brief \b DLAQR2 performs the orthogonal similarity transformation of a Hessenberg matrix to detect and d
+eflate fully converged eigenvalues from a trailing principal submatrix (aggressive early deflation). 
+  
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAQR2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaqr2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaqr2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaqr2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAQR2( WANTT, WANTZ, N, KTOP, KBOT, NW, H, LDH, ILOZ,   
+                            IHIZ, Z, LDZ, NS, ND, SR, SI, V, LDV, NH, T,   
+                            LDT, NV, WV, LDWV, WORK, LWORK )   
+
+         INTEGER            IHIZ, ILOZ, KBOT, KTOP, LDH, LDT, LDV, LDWV,   
+        $                   LDZ, LWORK, N, ND, NH, NS, NV, NW   
+         LOGICAL            WANTT, WANTZ   
+         DOUBLE PRECISION   H( LDH, * ), SI( * ), SR( * ), T( LDT, * ),   
+        $                   V( LDV, * ), WORK( * ), WV( LDWV, * ),   
+        $                   Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >    DLAQR2 is identical to DLAQR3 except that it avoids   
+   >    recursion by calling DLAHQR instead of DLAQR4.   
+   >   
+   >    Aggressive early deflation:   
+   >   
+   >    This subroutine accepts as input an upper Hessenberg matrix   
+   >    H and performs an orthogonal similarity transformation   
+   >    designed to detect and deflate fully converged eigenvalues from   
+   >    a trailing principal submatrix.  On output H has been over-   
+   >    written by a new Hessenberg matrix that is a perturbation of   
+   >    an orthogonal similarity transformation of H.  It is to be   
+   >    hoped that the final version of H has many zero subdiagonal   
+   >    entries.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] WANTT   
+   > \verbatim   
+   >          WANTT is LOGICAL   
+   >          If .TRUE., then the Hessenberg matrix H is fully updated   
+   >          so that the quasi-triangular Schur factor may be   
+   >          computed (in cooperation with the calling subroutine).   
+   >          If .FALSE., then only enough of H is updated to preserve   
+   >          the eigenvalues.   
+   > \endverbatim   
+   >   
+   > \param[in] WANTZ   
+   > \verbatim   
+   >          WANTZ is LOGICAL   
+   >          If .TRUE., then the orthogonal matrix Z is updated so   
+   >          so that the orthogonal Schur factor may be computed   
+   >          (in cooperation with the calling subroutine).   
+   >          If .FALSE., then Z is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix H and (if WANTZ is .TRUE.) the   
+   >          order of the orthogonal matrix Z.   
+   > \endverbatim   
+   >   
+   > \param[in] KTOP   
+   > \verbatim   
+   >          KTOP is INTEGER   
+   >          It is assumed that either KTOP = 1 or H(KTOP,KTOP-1)=0.   
+   >          KBOT and KTOP together determine an isolated block   
+   >          along the diagonal of the Hessenberg matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] KBOT   
+   > \verbatim   
+   >          KBOT is INTEGER   
+   >          It is assumed without a check that either   
+   >          KBOT = N or H(KBOT+1,KBOT)=0.  KBOT and KTOP together   
+   >          determine an isolated block along the diagonal of the   
+   >          Hessenberg matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] NW   
+   > \verbatim   
+   >          NW is INTEGER   
+   >          Deflation window size.  1 .LE. NW .LE. (KBOT-KTOP+1).   
+   > \endverbatim   
+   >   
+   > \param[in,out] H   
+   > \verbatim   
+   >          H is DOUBLE PRECISION array, dimension (LDH,N)   
+   >          On input the initial N-by-N section of H stores the   
+   >          Hessenberg matrix undergoing aggressive early deflation.   
+   >          On output H has been transformed by an orthogonal   
+   >          similarity transformation, perturbed, and the returned   
+   >          to Hessenberg form that (it is to be hoped) has some   
+   >          zero subdiagonal entries.   
+   > \endverbatim   
+   >   
+   > \param[in] LDH   
+   > \verbatim   
+   >          LDH is integer   
+   >          Leading dimension of H just as declared in the calling   
+   >          subroutine.  N .LE. LDH   
+   > \endverbatim   
+   >   
+   > \param[in] ILOZ   
+   > \verbatim   
+   >          ILOZ is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHIZ   
+   > \verbatim   
+   >          IHIZ is INTEGER   
+   >          Specify the rows of Z to which transformations must be   
+   >          applied if WANTZ is .TRUE.. 1 .LE. ILOZ .LE. IHIZ .LE. N.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ,N)   
+   >          IF WANTZ is .TRUE., then on output, the orthogonal   
+   >          similarity transformation mentioned above has been   
+   >          accumulated into Z(ILOZ:IHIZ,ILO:IHI) from the right.   
+   >          If WANTZ is .FALSE., then Z is unreferenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is integer   
+   >          The leading dimension of Z just as declared in the   
+   >          calling subroutine.  1 .LE. LDZ.   
+   > \endverbatim   
+   >   
+   > \param[out] NS   
+   > \verbatim   
+   >          NS is integer   
+   >          The number of unconverged (ie approximate) eigenvalues   
+   >          returned in SR and SI that may be used as shifts by the   
+   >          calling subroutine.   
+   > \endverbatim   
+   >   
+   > \param[out] ND   
+   > \verbatim   
+   >          ND is integer   
+   >          The number of converged eigenvalues uncovered by this   
+   >          subroutine.   
+   > \endverbatim   
+   >   
+   > \param[out] SR   
+   > \verbatim   
+   >          SR is DOUBLE PRECISION array, dimension (KBOT)   
+   > \endverbatim   
+   >   
+   > \param[out] SI   
+   > \verbatim   
+   >          SI is DOUBLE PRECISION array, dimension (KBOT)   
+   >          On output, the real and imaginary parts of approximate   
+   >          eigenvalues that may be used for shifts are stored in   
+   >          SR(KBOT-ND-NS+1) through SR(KBOT-ND) and   
+   >          SI(KBOT-ND-NS+1) through SI(KBOT-ND), respectively.   
+   >          The real and imaginary parts of converged eigenvalues   
+   >          are stored in SR(KBOT-ND+1) through SR(KBOT) and   
+   >          SI(KBOT-ND+1) through SI(KBOT), respectively.   
+   > \endverbatim   
+   >   
+   > \param[out] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array, dimension (LDV,NW)   
+   >          An NW-by-NW work array.   
+   > \endverbatim   
+   >   
+   > \param[in] LDV   
+   > \verbatim   
+   >          LDV is integer scalar   
+   >          The leading dimension of V just as declared in the   
+   >          calling subroutine.  NW .LE. LDV   
+   > \endverbatim   
+   >   
+   > \param[in] NH   
+   > \verbatim   
+   >          NH is integer scalar   
+   >          The number of columns of T.  NH.GE.NW.   
+   > \endverbatim   
+   >   
+   > \param[out] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,NW)   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is integer   
+   >          The leading dimension of T just as declared in the   
+   >          calling subroutine.  NW .LE. LDT   
+   > \endverbatim   
+   >   
+   > \param[in] NV   
+   > \verbatim   
+   >          NV is integer   
+   >          The number of rows of work array WV available for   
+   >          workspace.  NV.GE.NW.   
+   > \endverbatim   
+   >   
+   > \param[out] WV   
+   > \verbatim   
+   >          WV is DOUBLE PRECISION array, dimension (LDWV,NW)   
+   > \endverbatim   
+   >   
+   > \param[in] LDWV   
+   > \verbatim   
+   >          LDWV is integer   
+   >          The leading dimension of W just as declared in the   
+   >          calling subroutine.  NW .LE. LDV   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (LWORK)   
+   >          On exit, WORK(1) is set to an estimate of the optimal value   
+   >          of LWORK for the given values of N, NW, KTOP and KBOT.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is integer   
+   >          The dimension of the work array WORK.  LWORK = 2*NW   
+   >          suffices, but greater efficiency may result from larger   
+   >          values of LWORK.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; DLAQR2   
+   >          only estimates the optimal workspace size for the given   
+   >          values of N, NW, KTOP and KBOT.  The estimate is returned   
+   >          in WORK(1).  No error message related to LWORK is issued   
+   >          by XERBLA.  Neither H nor Z are accessed.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >       Karen Braman and Ralph Byers, Department of Mathematics,   
+   >       University of Kansas, USA   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaqr2_(logical *wantt, logical *wantz, integer *n, 
+	integer *ktop, integer *kbot, integer *nw, doublereal *h__, integer *
+	ldh, integer *iloz, integer *ihiz, doublereal *z__, integer *ldz, 
+	integer *ns, integer *nd, doublereal *sr, doublereal *si, doublereal *
+	v, integer *ldv, integer *nh, doublereal *t, integer *ldt, integer *
+	nv, doublereal *wv, integer *ldwv, doublereal *work, integer *lwork)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, t_dim1, t_offset, v_dim1, v_offset, wv_dim1, 
+	    wv_offset, z_dim1, z_offset, i__1, i__2, i__3, i__4;
+    doublereal d__1, d__2, d__3, d__4, d__5, d__6;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j, k;
+    doublereal s, aa, bb, cc, dd, cs, sn;
+    integer jw;
+    doublereal evi, evk, foo;
+    integer kln;
+    doublereal tau, ulp;
+    integer lwk1, lwk2;
+    doublereal beta;
+    integer kend, kcol, info, ifst, ilst, ltop, krow;
+    extern /* Subroutine */ int igraphdlarf_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *), igraphdgemm_(char *, char *, integer *, integer *
+	    , integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *);
+    logical bulge;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    integer infqr, kwtop;
+    extern /* Subroutine */ int igraphdlanv2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *), igraphdlabad_(
+	    doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdgehrd_(integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *), igraphdlarfg_(integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *), igraphdlahqr_(logical *, logical *, integer *,
+	     integer *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *), igraphdlacpy_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    doublereal safmin;
+    extern /* Subroutine */ int igraphdlaset_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *);
+    doublereal safmax;
+    extern /* Subroutine */ int igraphdtrexc_(char *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *, integer *, 
+	    doublereal *, integer *), igraphdormhr_(char *, char *, integer 
+	    *, integer *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    integer *);
+    logical sorted;
+    doublereal smlnum;
+    integer lwkopt;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ================================================================   
+
+       ==== Estimate optimal workspace. ====   
+
+       Parameter adjustments */
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --sr;
+    --si;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    wv_dim1 = *ldwv;
+    wv_offset = 1 + wv_dim1;
+    wv -= wv_offset;
+    --work;
+
+    /* Function Body   
+   Computing MIN */
+    i__1 = *nw, i__2 = *kbot - *ktop + 1;
+    jw = min(i__1,i__2);
+    if (jw <= 2) {
+	lwkopt = 1;
+    } else {
+
+/*        ==== Workspace query call to DGEHRD ==== */
+
+	i__1 = jw - 1;
+	igraphdgehrd_(&jw, &c__1, &i__1, &t[t_offset], ldt, &work[1], &work[1], &
+		c_n1, &info);
+	lwk1 = (integer) work[1];
+
+/*        ==== Workspace query call to DORMHR ==== */
+
+	i__1 = jw - 1;
+	igraphdormhr_("R", "N", &jw, &jw, &c__1, &i__1, &t[t_offset], ldt, &work[1],
+		 &v[v_offset], ldv, &work[1], &c_n1, &info);
+	lwk2 = (integer) work[1];
+
+/*        ==== Optimal workspace ==== */
+
+	lwkopt = jw + max(lwk1,lwk2);
+    }
+
+/*     ==== Quick return in case of workspace query. ==== */
+
+    if (*lwork == -1) {
+	work[1] = (doublereal) lwkopt;
+	return 0;
+    }
+
+/*     ==== Nothing to do ...   
+       ... for an empty active block ... ==== */
+    *ns = 0;
+    *nd = 0;
+    work[1] = 1.;
+    if (*ktop > *kbot) {
+	return 0;
+    }
+/*     ... nor for an empty deflation window. ==== */
+    if (*nw < 1) {
+	return 0;
+    }
+
+/*     ==== Machine constants ==== */
+
+    safmin = igraphdlamch_("SAFE MINIMUM");
+    safmax = 1. / safmin;
+    igraphdlabad_(&safmin, &safmax);
+    ulp = igraphdlamch_("PRECISION");
+    smlnum = safmin * ((doublereal) (*n) / ulp);
+
+/*     ==== Setup deflation window ====   
+
+   Computing MIN */
+    i__1 = *nw, i__2 = *kbot - *ktop + 1;
+    jw = min(i__1,i__2);
+    kwtop = *kbot - jw + 1;
+    if (kwtop == *ktop) {
+	s = 0.;
+    } else {
+	s = h__[kwtop + (kwtop - 1) * h_dim1];
+    }
+
+    if (*kbot == kwtop) {
+
+/*        ==== 1-by-1 deflation window: not much to do ==== */
+
+	sr[kwtop] = h__[kwtop + kwtop * h_dim1];
+	si[kwtop] = 0.;
+	*ns = 1;
+	*nd = 0;
+/* Computing MAX */
+	d__2 = smlnum, d__3 = ulp * (d__1 = h__[kwtop + kwtop * h_dim1], abs(
+		d__1));
+	if (abs(s) <= max(d__2,d__3)) {
+	    *ns = 0;
+	    *nd = 1;
+	    if (kwtop > *ktop) {
+		h__[kwtop + (kwtop - 1) * h_dim1] = 0.;
+	    }
+	}
+	work[1] = 1.;
+	return 0;
+    }
+
+/*     ==== Convert to spike-triangular form.  (In case of a   
+       .    rare QR failure, this routine continues to do   
+       .    aggressive early deflation using that part of   
+       .    the deflation window that converged using INFQR   
+       .    here and there to keep track.) ==== */
+
+    igraphdlacpy_("U", &jw, &jw, &h__[kwtop + kwtop * h_dim1], ldh, &t[t_offset], 
+	    ldt);
+    i__1 = jw - 1;
+    i__2 = *ldh + 1;
+    i__3 = *ldt + 1;
+    igraphdcopy_(&i__1, &h__[kwtop + 1 + kwtop * h_dim1], &i__2, &t[t_dim1 + 2], &
+	    i__3);
+
+    igraphdlaset_("A", &jw, &jw, &c_b12, &c_b13, &v[v_offset], ldv);
+    igraphdlahqr_(&c_true, &c_true, &jw, &c__1, &jw, &t[t_offset], ldt, &sr[kwtop], 
+	    &si[kwtop], &c__1, &jw, &v[v_offset], ldv, &infqr);
+
+/*     ==== DTREXC needs a clean margin near the diagonal ==== */
+
+    i__1 = jw - 3;
+    for (j = 1; j <= i__1; ++j) {
+	t[j + 2 + j * t_dim1] = 0.;
+	t[j + 3 + j * t_dim1] = 0.;
+/* L10: */
+    }
+    if (jw > 2) {
+	t[jw + (jw - 2) * t_dim1] = 0.;
+    }
+
+/*     ==== Deflation detection loop ==== */
+
+    *ns = jw;
+    ilst = infqr + 1;
+L20:
+    if (ilst <= *ns) {
+	if (*ns == 1) {
+	    bulge = FALSE_;
+	} else {
+	    bulge = t[*ns + (*ns - 1) * t_dim1] != 0.;
+	}
+
+/*        ==== Small spike tip test for deflation ==== */
+
+	if (! bulge) {
+
+/*           ==== Real eigenvalue ==== */
+
+	    foo = (d__1 = t[*ns + *ns * t_dim1], abs(d__1));
+	    if (foo == 0.) {
+		foo = abs(s);
+	    }
+/* Computing MAX */
+	    d__2 = smlnum, d__3 = ulp * foo;
+	    if ((d__1 = s * v[*ns * v_dim1 + 1], abs(d__1)) <= max(d__2,d__3))
+		     {
+
+/*              ==== Deflatable ==== */
+
+		--(*ns);
+	    } else {
+
+/*              ==== Undeflatable.   Move it up out of the way.   
+                .    (DTREXC can not fail in this case.) ==== */
+
+		ifst = *ns;
+		igraphdtrexc_("V", &jw, &t[t_offset], ldt, &v[v_offset], ldv, &ifst,
+			 &ilst, &work[1], &info);
+		++ilst;
+	    }
+	} else {
+
+/*           ==== Complex conjugate pair ==== */
+
+	    foo = (d__3 = t[*ns + *ns * t_dim1], abs(d__3)) + sqrt((d__1 = t[*
+		    ns + (*ns - 1) * t_dim1], abs(d__1))) * sqrt((d__2 = t[*
+		    ns - 1 + *ns * t_dim1], abs(d__2)));
+	    if (foo == 0.) {
+		foo = abs(s);
+	    }
+/* Computing MAX */
+	    d__3 = (d__1 = s * v[*ns * v_dim1 + 1], abs(d__1)), d__4 = (d__2 =
+		     s * v[(*ns - 1) * v_dim1 + 1], abs(d__2));
+/* Computing MAX */
+	    d__5 = smlnum, d__6 = ulp * foo;
+	    if (max(d__3,d__4) <= max(d__5,d__6)) {
+
+/*              ==== Deflatable ==== */
+
+		*ns += -2;
+	    } else {
+
+/*              ==== Undeflatable. Move them up out of the way.   
+                .    Fortunately, DTREXC does the right thing with   
+                .    ILST in case of a rare exchange failure. ==== */
+
+		ifst = *ns;
+		igraphdtrexc_("V", &jw, &t[t_offset], ldt, &v[v_offset], ldv, &ifst,
+			 &ilst, &work[1], &info);
+		ilst += 2;
+	    }
+	}
+
+/*        ==== End deflation detection loop ==== */
+
+	goto L20;
+    }
+
+/*        ==== Return to Hessenberg form ==== */
+
+    if (*ns == 0) {
+	s = 0.;
+    }
+
+    if (*ns < jw) {
+
+/*        ==== sorting diagonal blocks of T improves accuracy for   
+          .    graded matrices.  Bubble sort deals well with   
+          .    exchange failures. ==== */
+
+	sorted = FALSE_;
+	i__ = *ns + 1;
+L30:
+	if (sorted) {
+	    goto L50;
+	}
+	sorted = TRUE_;
+
+	kend = i__ - 1;
+	i__ = infqr + 1;
+	if (i__ == *ns) {
+	    k = i__ + 1;
+	} else if (t[i__ + 1 + i__ * t_dim1] == 0.) {
+	    k = i__ + 1;
+	} else {
+	    k = i__ + 2;
+	}
+L40:
+	if (k <= kend) {
+	    if (k == i__ + 1) {
+		evi = (d__1 = t[i__ + i__ * t_dim1], abs(d__1));
+	    } else {
+		evi = (d__3 = t[i__ + i__ * t_dim1], abs(d__3)) + sqrt((d__1 =
+			 t[i__ + 1 + i__ * t_dim1], abs(d__1))) * sqrt((d__2 =
+			 t[i__ + (i__ + 1) * t_dim1], abs(d__2)));
+	    }
+
+	    if (k == kend) {
+		evk = (d__1 = t[k + k * t_dim1], abs(d__1));
+	    } else if (t[k + 1 + k * t_dim1] == 0.) {
+		evk = (d__1 = t[k + k * t_dim1], abs(d__1));
+	    } else {
+		evk = (d__3 = t[k + k * t_dim1], abs(d__3)) + sqrt((d__1 = t[
+			k + 1 + k * t_dim1], abs(d__1))) * sqrt((d__2 = t[k + 
+			(k + 1) * t_dim1], abs(d__2)));
+	    }
+
+	    if (evi >= evk) {
+		i__ = k;
+	    } else {
+		sorted = FALSE_;
+		ifst = i__;
+		ilst = k;
+		igraphdtrexc_("V", &jw, &t[t_offset], ldt, &v[v_offset], ldv, &ifst,
+			 &ilst, &work[1], &info);
+		if (info == 0) {
+		    i__ = ilst;
+		} else {
+		    i__ = k;
+		}
+	    }
+	    if (i__ == kend) {
+		k = i__ + 1;
+	    } else if (t[i__ + 1 + i__ * t_dim1] == 0.) {
+		k = i__ + 1;
+	    } else {
+		k = i__ + 2;
+	    }
+	    goto L40;
+	}
+	goto L30;
+L50:
+	;
+    }
+
+/*     ==== Restore shift/eigenvalue array from T ==== */
+
+    i__ = jw;
+L60:
+    if (i__ >= infqr + 1) {
+	if (i__ == infqr + 1) {
+	    sr[kwtop + i__ - 1] = t[i__ + i__ * t_dim1];
+	    si[kwtop + i__ - 1] = 0.;
+	    --i__;
+	} else if (t[i__ + (i__ - 1) * t_dim1] == 0.) {
+	    sr[kwtop + i__ - 1] = t[i__ + i__ * t_dim1];
+	    si[kwtop + i__ - 1] = 0.;
+	    --i__;
+	} else {
+	    aa = t[i__ - 1 + (i__ - 1) * t_dim1];
+	    cc = t[i__ + (i__ - 1) * t_dim1];
+	    bb = t[i__ - 1 + i__ * t_dim1];
+	    dd = t[i__ + i__ * t_dim1];
+	    igraphdlanv2_(&aa, &bb, &cc, &dd, &sr[kwtop + i__ - 2], &si[kwtop + i__ 
+		    - 2], &sr[kwtop + i__ - 1], &si[kwtop + i__ - 1], &cs, &
+		    sn);
+	    i__ += -2;
+	}
+	goto L60;
+    }
+
+    if (*ns < jw || s == 0.) {
+	if (*ns > 1 && s != 0.) {
+
+/*           ==== Reflect spike back into lower triangle ==== */
+
+	    igraphdcopy_(ns, &v[v_offset], ldv, &work[1], &c__1);
+	    beta = work[1];
+	    igraphdlarfg_(ns, &beta, &work[2], &c__1, &tau);
+	    work[1] = 1.;
+
+	    i__1 = jw - 2;
+	    i__2 = jw - 2;
+	    igraphdlaset_("L", &i__1, &i__2, &c_b12, &c_b12, &t[t_dim1 + 3], ldt);
+
+	    igraphdlarf_("L", ns, &jw, &work[1], &c__1, &tau, &t[t_offset], ldt, &
+		    work[jw + 1]);
+	    igraphdlarf_("R", ns, ns, &work[1], &c__1, &tau, &t[t_offset], ldt, &
+		    work[jw + 1]);
+	    igraphdlarf_("R", &jw, ns, &work[1], &c__1, &tau, &v[v_offset], ldv, &
+		    work[jw + 1]);
+
+	    i__1 = *lwork - jw;
+	    igraphdgehrd_(&jw, &c__1, ns, &t[t_offset], ldt, &work[1], &work[jw + 1]
+		    , &i__1, &info);
+	}
+
+/*        ==== Copy updated reduced window into place ==== */
+
+	if (kwtop > 1) {
+	    h__[kwtop + (kwtop - 1) * h_dim1] = s * v[v_dim1 + 1];
+	}
+	igraphdlacpy_("U", &jw, &jw, &t[t_offset], ldt, &h__[kwtop + kwtop * h_dim1]
+		, ldh);
+	i__1 = jw - 1;
+	i__2 = *ldt + 1;
+	i__3 = *ldh + 1;
+	igraphdcopy_(&i__1, &t[t_dim1 + 2], &i__2, &h__[kwtop + 1 + kwtop * h_dim1],
+		 &i__3);
+
+/*        ==== Accumulate orthogonal matrix in order update   
+          .    H and Z, if requested.  ==== */
+
+	if (*ns > 1 && s != 0.) {
+	    i__1 = *lwork - jw;
+	    igraphdormhr_("R", "N", &jw, ns, &c__1, ns, &t[t_offset], ldt, &work[1],
+		     &v[v_offset], ldv, &work[jw + 1], &i__1, &info);
+	}
+
+/*        ==== Update vertical slab in H ==== */
+
+	if (*wantt) {
+	    ltop = 1;
+	} else {
+	    ltop = *ktop;
+	}
+	i__1 = kwtop - 1;
+	i__2 = *nv;
+	for (krow = ltop; i__2 < 0 ? krow >= i__1 : krow <= i__1; krow += 
+		i__2) {
+/* Computing MIN */
+	    i__3 = *nv, i__4 = kwtop - krow;
+	    kln = min(i__3,i__4);
+	    igraphdgemm_("N", "N", &kln, &jw, &jw, &c_b13, &h__[krow + kwtop * 
+		    h_dim1], ldh, &v[v_offset], ldv, &c_b12, &wv[wv_offset], 
+		    ldwv);
+	    igraphdlacpy_("A", &kln, &jw, &wv[wv_offset], ldwv, &h__[krow + kwtop * 
+		    h_dim1], ldh);
+/* L70: */
+	}
+
+/*        ==== Update horizontal slab in H ==== */
+
+	if (*wantt) {
+	    i__2 = *n;
+	    i__1 = *nh;
+	    for (kcol = *kbot + 1; i__1 < 0 ? kcol >= i__2 : kcol <= i__2; 
+		    kcol += i__1) {
+/* Computing MIN */
+		i__3 = *nh, i__4 = *n - kcol + 1;
+		kln = min(i__3,i__4);
+		igraphdgemm_("C", "N", &jw, &kln, &jw, &c_b13, &v[v_offset], ldv, &
+			h__[kwtop + kcol * h_dim1], ldh, &c_b12, &t[t_offset],
+			 ldt);
+		igraphdlacpy_("A", &jw, &kln, &t[t_offset], ldt, &h__[kwtop + kcol *
+			 h_dim1], ldh);
+/* L80: */
+	    }
+	}
+
+/*        ==== Update vertical slab in Z ==== */
+
+	if (*wantz) {
+	    i__1 = *ihiz;
+	    i__2 = *nv;
+	    for (krow = *iloz; i__2 < 0 ? krow >= i__1 : krow <= i__1; krow +=
+		     i__2) {
+/* Computing MIN */
+		i__3 = *nv, i__4 = *ihiz - krow + 1;
+		kln = min(i__3,i__4);
+		igraphdgemm_("N", "N", &kln, &jw, &jw, &c_b13, &z__[krow + kwtop * 
+			z_dim1], ldz, &v[v_offset], ldv, &c_b12, &wv[
+			wv_offset], ldwv);
+		igraphdlacpy_("A", &kln, &jw, &wv[wv_offset], ldwv, &z__[krow + 
+			kwtop * z_dim1], ldz);
+/* L90: */
+	    }
+	}
+    }
+
+/*     ==== Return the number of deflations ... ==== */
+
+    *nd = jw - *ns;
+
+/*     ==== ... and the number of shifts. (Subtracting   
+       .    INFQR from the spike length takes care   
+       .    of the case of a rare QR failure while   
+       .    calculating eigenvalues of the deflation   
+       .    window.)  ==== */
+
+    *ns -= infqr;
+
+/*      ==== Return optimal workspace. ==== */
+
+    work[1] = (doublereal) lwkopt;
+
+/*     ==== End of DLAQR2 ==== */
+
+    return 0;
+} /* igraphdlaqr2_ */
+
diff --git a/igraph/src/dlaqr3.c b/igraph/src/dlaqr3.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaqr3.c
@@ -0,0 +1,840 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static logical c_true = TRUE_;
+static doublereal c_b17 = 0.;
+static doublereal c_b18 = 1.;
+static integer c__12 = 12;
+
+/* > \brief \b DLAQR3 performs the orthogonal similarity transformation of a Hessenberg matrix to detect and d
+eflate fully converged eigenvalues from a trailing principal submatrix (aggressive early deflation). 
+  
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAQR3 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaqr3.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaqr3.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaqr3.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAQR3( WANTT, WANTZ, N, KTOP, KBOT, NW, H, LDH, ILOZ,   
+                            IHIZ, Z, LDZ, NS, ND, SR, SI, V, LDV, NH, T,   
+                            LDT, NV, WV, LDWV, WORK, LWORK )   
+
+         INTEGER            IHIZ, ILOZ, KBOT, KTOP, LDH, LDT, LDV, LDWV,   
+        $                   LDZ, LWORK, N, ND, NH, NS, NV, NW   
+         LOGICAL            WANTT, WANTZ   
+         DOUBLE PRECISION   H( LDH, * ), SI( * ), SR( * ), T( LDT, * ),   
+        $                   V( LDV, * ), WORK( * ), WV( LDWV, * ),   
+        $                   Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >    Aggressive early deflation:   
+   >   
+   >    DLAQR3 accepts as input an upper Hessenberg matrix   
+   >    H and performs an orthogonal similarity transformation   
+   >    designed to detect and deflate fully converged eigenvalues from   
+   >    a trailing principal submatrix.  On output H has been over-   
+   >    written by a new Hessenberg matrix that is a perturbation of   
+   >    an orthogonal similarity transformation of H.  It is to be   
+   >    hoped that the final version of H has many zero subdiagonal   
+   >    entries.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] WANTT   
+   > \verbatim   
+   >          WANTT is LOGICAL   
+   >          If .TRUE., then the Hessenberg matrix H is fully updated   
+   >          so that the quasi-triangular Schur factor may be   
+   >          computed (in cooperation with the calling subroutine).   
+   >          If .FALSE., then only enough of H is updated to preserve   
+   >          the eigenvalues.   
+   > \endverbatim   
+   >   
+   > \param[in] WANTZ   
+   > \verbatim   
+   >          WANTZ is LOGICAL   
+   >          If .TRUE., then the orthogonal matrix Z is updated so   
+   >          so that the orthogonal Schur factor may be computed   
+   >          (in cooperation with the calling subroutine).   
+   >          If .FALSE., then Z is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix H and (if WANTZ is .TRUE.) the   
+   >          order of the orthogonal matrix Z.   
+   > \endverbatim   
+   >   
+   > \param[in] KTOP   
+   > \verbatim   
+   >          KTOP is INTEGER   
+   >          It is assumed that either KTOP = 1 or H(KTOP,KTOP-1)=0.   
+   >          KBOT and KTOP together determine an isolated block   
+   >          along the diagonal of the Hessenberg matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] KBOT   
+   > \verbatim   
+   >          KBOT is INTEGER   
+   >          It is assumed without a check that either   
+   >          KBOT = N or H(KBOT+1,KBOT)=0.  KBOT and KTOP together   
+   >          determine an isolated block along the diagonal of the   
+   >          Hessenberg matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] NW   
+   > \verbatim   
+   >          NW is INTEGER   
+   >          Deflation window size.  1 .LE. NW .LE. (KBOT-KTOP+1).   
+   > \endverbatim   
+   >   
+   > \param[in,out] H   
+   > \verbatim   
+   >          H is DOUBLE PRECISION array, dimension (LDH,N)   
+   >          On input the initial N-by-N section of H stores the   
+   >          Hessenberg matrix undergoing aggressive early deflation.   
+   >          On output H has been transformed by an orthogonal   
+   >          similarity transformation, perturbed, and the returned   
+   >          to Hessenberg form that (it is to be hoped) has some   
+   >          zero subdiagonal entries.   
+   > \endverbatim   
+   >   
+   > \param[in] LDH   
+   > \verbatim   
+   >          LDH is integer   
+   >          Leading dimension of H just as declared in the calling   
+   >          subroutine.  N .LE. LDH   
+   > \endverbatim   
+   >   
+   > \param[in] ILOZ   
+   > \verbatim   
+   >          ILOZ is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHIZ   
+   > \verbatim   
+   >          IHIZ is INTEGER   
+   >          Specify the rows of Z to which transformations must be   
+   >          applied if WANTZ is .TRUE.. 1 .LE. ILOZ .LE. IHIZ .LE. N.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ,N)   
+   >          IF WANTZ is .TRUE., then on output, the orthogonal   
+   >          similarity transformation mentioned above has been   
+   >          accumulated into Z(ILOZ:IHIZ,ILO:IHI) from the right.   
+   >          If WANTZ is .FALSE., then Z is unreferenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is integer   
+   >          The leading dimension of Z just as declared in the   
+   >          calling subroutine.  1 .LE. LDZ.   
+   > \endverbatim   
+   >   
+   > \param[out] NS   
+   > \verbatim   
+   >          NS is integer   
+   >          The number of unconverged (ie approximate) eigenvalues   
+   >          returned in SR and SI that may be used as shifts by the   
+   >          calling subroutine.   
+   > \endverbatim   
+   >   
+   > \param[out] ND   
+   > \verbatim   
+   >          ND is integer   
+   >          The number of converged eigenvalues uncovered by this   
+   >          subroutine.   
+   > \endverbatim   
+   >   
+   > \param[out] SR   
+   > \verbatim   
+   >          SR is DOUBLE PRECISION array, dimension (KBOT)   
+   > \endverbatim   
+   >   
+   > \param[out] SI   
+   > \verbatim   
+   >          SI is DOUBLE PRECISION array, dimension (KBOT)   
+   >          On output, the real and imaginary parts of approximate   
+   >          eigenvalues that may be used for shifts are stored in   
+   >          SR(KBOT-ND-NS+1) through SR(KBOT-ND) and   
+   >          SI(KBOT-ND-NS+1) through SI(KBOT-ND), respectively.   
+   >          The real and imaginary parts of converged eigenvalues   
+   >          are stored in SR(KBOT-ND+1) through SR(KBOT) and   
+   >          SI(KBOT-ND+1) through SI(KBOT), respectively.   
+   > \endverbatim   
+   >   
+   > \param[out] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array, dimension (LDV,NW)   
+   >          An NW-by-NW work array.   
+   > \endverbatim   
+   >   
+   > \param[in] LDV   
+   > \verbatim   
+   >          LDV is integer scalar   
+   >          The leading dimension of V just as declared in the   
+   >          calling subroutine.  NW .LE. LDV   
+   > \endverbatim   
+   >   
+   > \param[in] NH   
+   > \verbatim   
+   >          NH is integer scalar   
+   >          The number of columns of T.  NH.GE.NW.   
+   > \endverbatim   
+   >   
+   > \param[out] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,NW)   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is integer   
+   >          The leading dimension of T just as declared in the   
+   >          calling subroutine.  NW .LE. LDT   
+   > \endverbatim   
+   >   
+   > \param[in] NV   
+   > \verbatim   
+   >          NV is integer   
+   >          The number of rows of work array WV available for   
+   >          workspace.  NV.GE.NW.   
+   > \endverbatim   
+   >   
+   > \param[out] WV   
+   > \verbatim   
+   >          WV is DOUBLE PRECISION array, dimension (LDWV,NW)   
+   > \endverbatim   
+   >   
+   > \param[in] LDWV   
+   > \verbatim   
+   >          LDWV is integer   
+   >          The leading dimension of W just as declared in the   
+   >          calling subroutine.  NW .LE. LDV   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (LWORK)   
+   >          On exit, WORK(1) is set to an estimate of the optimal value   
+   >          of LWORK for the given values of N, NW, KTOP and KBOT.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is integer   
+   >          The dimension of the work array WORK.  LWORK = 2*NW   
+   >          suffices, but greater efficiency may result from larger   
+   >          values of LWORK.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; DLAQR3   
+   >          only estimates the optimal workspace size for the given   
+   >          values of N, NW, KTOP and KBOT.  The estimate is returned   
+   >          in WORK(1).  No error message related to LWORK is issued   
+   >          by XERBLA.  Neither H nor Z are accessed.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >       Karen Braman and Ralph Byers, Department of Mathematics,   
+   >       University of Kansas, USA   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaqr3_(logical *wantt, logical *wantz, integer *n, 
+	integer *ktop, integer *kbot, integer *nw, doublereal *h__, integer *
+	ldh, integer *iloz, integer *ihiz, doublereal *z__, integer *ldz, 
+	integer *ns, integer *nd, doublereal *sr, doublereal *si, doublereal *
+	v, integer *ldv, integer *nh, doublereal *t, integer *ldt, integer *
+	nv, doublereal *wv, integer *ldwv, doublereal *work, integer *lwork)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, t_dim1, t_offset, v_dim1, v_offset, wv_dim1, 
+	    wv_offset, z_dim1, z_offset, i__1, i__2, i__3, i__4;
+    doublereal d__1, d__2, d__3, d__4, d__5, d__6;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j, k;
+    doublereal s, aa, bb, cc, dd, cs, sn;
+    integer jw;
+    doublereal evi, evk, foo;
+    integer kln;
+    doublereal tau, ulp;
+    integer lwk1, lwk2, lwk3;
+    doublereal beta;
+    integer kend, kcol, info, nmin, ifst, ilst, ltop, krow;
+    extern /* Subroutine */ int igraphdlarf_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *), igraphdgemm_(char *, char *, integer *, integer *
+	    , integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *);
+    logical bulge;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    integer infqr, kwtop;
+    extern /* Subroutine */ int igraphdlanv2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *), igraphdlaqr4_(
+	    logical *, logical *, integer *, integer *, integer *, doublereal 
+	    *, integer *, doublereal *, doublereal *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, integer *), 
+	    igraphdlabad_(doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdgehrd_(integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *), igraphdlarfg_(integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *), igraphdlahqr_(logical *, logical *, integer *,
+	     integer *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *), igraphdlacpy_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    doublereal safmin;
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    doublereal safmax;
+    extern /* Subroutine */ int igraphdlaset_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *), 
+	    igraphdtrexc_(char *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, integer *, integer *, doublereal *, integer *),
+	     igraphdormhr_(char *, char *, integer *, integer *, integer *, integer 
+	    *, doublereal *, integer *, doublereal *, doublereal *, integer *,
+	     doublereal *, integer *, integer *);
+    logical sorted;
+    doublereal smlnum;
+    integer lwkopt;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ================================================================   
+
+       ==== Estimate optimal workspace. ====   
+
+       Parameter adjustments */
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --sr;
+    --si;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    wv_dim1 = *ldwv;
+    wv_offset = 1 + wv_dim1;
+    wv -= wv_offset;
+    --work;
+
+    /* Function Body   
+   Computing MIN */
+    i__1 = *nw, i__2 = *kbot - *ktop + 1;
+    jw = min(i__1,i__2);
+    if (jw <= 2) {
+	lwkopt = 1;
+    } else {
+
+/*        ==== Workspace query call to DGEHRD ==== */
+
+	i__1 = jw - 1;
+	igraphdgehrd_(&jw, &c__1, &i__1, &t[t_offset], ldt, &work[1], &work[1], &
+		c_n1, &info);
+	lwk1 = (integer) work[1];
+
+/*        ==== Workspace query call to DORMHR ==== */
+
+	i__1 = jw - 1;
+	igraphdormhr_("R", "N", &jw, &jw, &c__1, &i__1, &t[t_offset], ldt, &work[1],
+		 &v[v_offset], ldv, &work[1], &c_n1, &info);
+	lwk2 = (integer) work[1];
+
+/*        ==== Workspace query call to DLAQR4 ==== */
+
+	igraphdlaqr4_(&c_true, &c_true, &jw, &c__1, &jw, &t[t_offset], ldt, &sr[1], 
+		&si[1], &c__1, &jw, &v[v_offset], ldv, &work[1], &c_n1, &
+		infqr);
+	lwk3 = (integer) work[1];
+
+/*        ==== Optimal workspace ====   
+
+   Computing MAX */
+	i__1 = jw + max(lwk1,lwk2);
+	lwkopt = max(i__1,lwk3);
+    }
+
+/*     ==== Quick return in case of workspace query. ==== */
+
+    if (*lwork == -1) {
+	work[1] = (doublereal) lwkopt;
+	return 0;
+    }
+
+/*     ==== Nothing to do ...   
+       ... for an empty active block ... ==== */
+    *ns = 0;
+    *nd = 0;
+    work[1] = 1.;
+    if (*ktop > *kbot) {
+	return 0;
+    }
+/*     ... nor for an empty deflation window. ==== */
+    if (*nw < 1) {
+	return 0;
+    }
+
+/*     ==== Machine constants ==== */
+
+    safmin = igraphdlamch_("SAFE MINIMUM");
+    safmax = 1. / safmin;
+    igraphdlabad_(&safmin, &safmax);
+    ulp = igraphdlamch_("PRECISION");
+    smlnum = safmin * ((doublereal) (*n) / ulp);
+
+/*     ==== Setup deflation window ====   
+
+   Computing MIN */
+    i__1 = *nw, i__2 = *kbot - *ktop + 1;
+    jw = min(i__1,i__2);
+    kwtop = *kbot - jw + 1;
+    if (kwtop == *ktop) {
+	s = 0.;
+    } else {
+	s = h__[kwtop + (kwtop - 1) * h_dim1];
+    }
+
+    if (*kbot == kwtop) {
+
+/*        ==== 1-by-1 deflation window: not much to do ==== */
+
+	sr[kwtop] = h__[kwtop + kwtop * h_dim1];
+	si[kwtop] = 0.;
+	*ns = 1;
+	*nd = 0;
+/* Computing MAX */
+	d__2 = smlnum, d__3 = ulp * (d__1 = h__[kwtop + kwtop * h_dim1], abs(
+		d__1));
+	if (abs(s) <= max(d__2,d__3)) {
+	    *ns = 0;
+	    *nd = 1;
+	    if (kwtop > *ktop) {
+		h__[kwtop + (kwtop - 1) * h_dim1] = 0.;
+	    }
+	}
+	work[1] = 1.;
+	return 0;
+    }
+
+/*     ==== Convert to spike-triangular form.  (In case of a   
+       .    rare QR failure, this routine continues to do   
+       .    aggressive early deflation using that part of   
+       .    the deflation window that converged using INFQR   
+       .    here and there to keep track.) ==== */
+
+    igraphdlacpy_("U", &jw, &jw, &h__[kwtop + kwtop * h_dim1], ldh, &t[t_offset], 
+	    ldt);
+    i__1 = jw - 1;
+    i__2 = *ldh + 1;
+    i__3 = *ldt + 1;
+    igraphdcopy_(&i__1, &h__[kwtop + 1 + kwtop * h_dim1], &i__2, &t[t_dim1 + 2], &
+	    i__3);
+
+    igraphdlaset_("A", &jw, &jw, &c_b17, &c_b18, &v[v_offset], ldv);
+    nmin = igraphilaenv_(&c__12, "DLAQR3", "SV", &jw, &c__1, &jw, lwork, (ftnlen)6, 
+	    (ftnlen)2);
+    if (jw > nmin) {
+	igraphdlaqr4_(&c_true, &c_true, &jw, &c__1, &jw, &t[t_offset], ldt, &sr[
+		kwtop], &si[kwtop], &c__1, &jw, &v[v_offset], ldv, &work[1], 
+		lwork, &infqr);
+    } else {
+	igraphdlahqr_(&c_true, &c_true, &jw, &c__1, &jw, &t[t_offset], ldt, &sr[
+		kwtop], &si[kwtop], &c__1, &jw, &v[v_offset], ldv, &infqr);
+    }
+
+/*     ==== DTREXC needs a clean margin near the diagonal ==== */
+
+    i__1 = jw - 3;
+    for (j = 1; j <= i__1; ++j) {
+	t[j + 2 + j * t_dim1] = 0.;
+	t[j + 3 + j * t_dim1] = 0.;
+/* L10: */
+    }
+    if (jw > 2) {
+	t[jw + (jw - 2) * t_dim1] = 0.;
+    }
+
+/*     ==== Deflation detection loop ==== */
+
+    *ns = jw;
+    ilst = infqr + 1;
+L20:
+    if (ilst <= *ns) {
+	if (*ns == 1) {
+	    bulge = FALSE_;
+	} else {
+	    bulge = t[*ns + (*ns - 1) * t_dim1] != 0.;
+	}
+
+/*        ==== Small spike tip test for deflation ==== */
+
+	if (! bulge) {
+
+/*           ==== Real eigenvalue ==== */
+
+	    foo = (d__1 = t[*ns + *ns * t_dim1], abs(d__1));
+	    if (foo == 0.) {
+		foo = abs(s);
+	    }
+/* Computing MAX */
+	    d__2 = smlnum, d__3 = ulp * foo;
+	    if ((d__1 = s * v[*ns * v_dim1 + 1], abs(d__1)) <= max(d__2,d__3))
+		     {
+
+/*              ==== Deflatable ==== */
+
+		--(*ns);
+	    } else {
+
+/*              ==== Undeflatable.   Move it up out of the way.   
+                .    (DTREXC can not fail in this case.) ==== */
+
+		ifst = *ns;
+		igraphdtrexc_("V", &jw, &t[t_offset], ldt, &v[v_offset], ldv, &ifst,
+			 &ilst, &work[1], &info);
+		++ilst;
+	    }
+	} else {
+
+/*           ==== Complex conjugate pair ==== */
+
+	    foo = (d__3 = t[*ns + *ns * t_dim1], abs(d__3)) + sqrt((d__1 = t[*
+		    ns + (*ns - 1) * t_dim1], abs(d__1))) * sqrt((d__2 = t[*
+		    ns - 1 + *ns * t_dim1], abs(d__2)));
+	    if (foo == 0.) {
+		foo = abs(s);
+	    }
+/* Computing MAX */
+	    d__3 = (d__1 = s * v[*ns * v_dim1 + 1], abs(d__1)), d__4 = (d__2 =
+		     s * v[(*ns - 1) * v_dim1 + 1], abs(d__2));
+/* Computing MAX */
+	    d__5 = smlnum, d__6 = ulp * foo;
+	    if (max(d__3,d__4) <= max(d__5,d__6)) {
+
+/*              ==== Deflatable ==== */
+
+		*ns += -2;
+	    } else {
+
+/*              ==== Undeflatable. Move them up out of the way.   
+                .    Fortunately, DTREXC does the right thing with   
+                .    ILST in case of a rare exchange failure. ==== */
+
+		ifst = *ns;
+		igraphdtrexc_("V", &jw, &t[t_offset], ldt, &v[v_offset], ldv, &ifst,
+			 &ilst, &work[1], &info);
+		ilst += 2;
+	    }
+	}
+
+/*        ==== End deflation detection loop ==== */
+
+	goto L20;
+    }
+
+/*        ==== Return to Hessenberg form ==== */
+
+    if (*ns == 0) {
+	s = 0.;
+    }
+
+    if (*ns < jw) {
+
+/*        ==== sorting diagonal blocks of T improves accuracy for   
+          .    graded matrices.  Bubble sort deals well with   
+          .    exchange failures. ==== */
+
+	sorted = FALSE_;
+	i__ = *ns + 1;
+L30:
+	if (sorted) {
+	    goto L50;
+	}
+	sorted = TRUE_;
+
+	kend = i__ - 1;
+	i__ = infqr + 1;
+	if (i__ == *ns) {
+	    k = i__ + 1;
+	} else if (t[i__ + 1 + i__ * t_dim1] == 0.) {
+	    k = i__ + 1;
+	} else {
+	    k = i__ + 2;
+	}
+L40:
+	if (k <= kend) {
+	    if (k == i__ + 1) {
+		evi = (d__1 = t[i__ + i__ * t_dim1], abs(d__1));
+	    } else {
+		evi = (d__3 = t[i__ + i__ * t_dim1], abs(d__3)) + sqrt((d__1 =
+			 t[i__ + 1 + i__ * t_dim1], abs(d__1))) * sqrt((d__2 =
+			 t[i__ + (i__ + 1) * t_dim1], abs(d__2)));
+	    }
+
+	    if (k == kend) {
+		evk = (d__1 = t[k + k * t_dim1], abs(d__1));
+	    } else if (t[k + 1 + k * t_dim1] == 0.) {
+		evk = (d__1 = t[k + k * t_dim1], abs(d__1));
+	    } else {
+		evk = (d__3 = t[k + k * t_dim1], abs(d__3)) + sqrt((d__1 = t[
+			k + 1 + k * t_dim1], abs(d__1))) * sqrt((d__2 = t[k + 
+			(k + 1) * t_dim1], abs(d__2)));
+	    }
+
+	    if (evi >= evk) {
+		i__ = k;
+	    } else {
+		sorted = FALSE_;
+		ifst = i__;
+		ilst = k;
+		igraphdtrexc_("V", &jw, &t[t_offset], ldt, &v[v_offset], ldv, &ifst,
+			 &ilst, &work[1], &info);
+		if (info == 0) {
+		    i__ = ilst;
+		} else {
+		    i__ = k;
+		}
+	    }
+	    if (i__ == kend) {
+		k = i__ + 1;
+	    } else if (t[i__ + 1 + i__ * t_dim1] == 0.) {
+		k = i__ + 1;
+	    } else {
+		k = i__ + 2;
+	    }
+	    goto L40;
+	}
+	goto L30;
+L50:
+	;
+    }
+
+/*     ==== Restore shift/eigenvalue array from T ==== */
+
+    i__ = jw;
+L60:
+    if (i__ >= infqr + 1) {
+	if (i__ == infqr + 1) {
+	    sr[kwtop + i__ - 1] = t[i__ + i__ * t_dim1];
+	    si[kwtop + i__ - 1] = 0.;
+	    --i__;
+	} else if (t[i__ + (i__ - 1) * t_dim1] == 0.) {
+	    sr[kwtop + i__ - 1] = t[i__ + i__ * t_dim1];
+	    si[kwtop + i__ - 1] = 0.;
+	    --i__;
+	} else {
+	    aa = t[i__ - 1 + (i__ - 1) * t_dim1];
+	    cc = t[i__ + (i__ - 1) * t_dim1];
+	    bb = t[i__ - 1 + i__ * t_dim1];
+	    dd = t[i__ + i__ * t_dim1];
+	    igraphdlanv2_(&aa, &bb, &cc, &dd, &sr[kwtop + i__ - 2], &si[kwtop + i__ 
+		    - 2], &sr[kwtop + i__ - 1], &si[kwtop + i__ - 1], &cs, &
+		    sn);
+	    i__ += -2;
+	}
+	goto L60;
+    }
+
+    if (*ns < jw || s == 0.) {
+	if (*ns > 1 && s != 0.) {
+
+/*           ==== Reflect spike back into lower triangle ==== */
+
+	    igraphdcopy_(ns, &v[v_offset], ldv, &work[1], &c__1);
+	    beta = work[1];
+	    igraphdlarfg_(ns, &beta, &work[2], &c__1, &tau);
+	    work[1] = 1.;
+
+	    i__1 = jw - 2;
+	    i__2 = jw - 2;
+	    igraphdlaset_("L", &i__1, &i__2, &c_b17, &c_b17, &t[t_dim1 + 3], ldt);
+
+	    igraphdlarf_("L", ns, &jw, &work[1], &c__1, &tau, &t[t_offset], ldt, &
+		    work[jw + 1]);
+	    igraphdlarf_("R", ns, ns, &work[1], &c__1, &tau, &t[t_offset], ldt, &
+		    work[jw + 1]);
+	    igraphdlarf_("R", &jw, ns, &work[1], &c__1, &tau, &v[v_offset], ldv, &
+		    work[jw + 1]);
+
+	    i__1 = *lwork - jw;
+	    igraphdgehrd_(&jw, &c__1, ns, &t[t_offset], ldt, &work[1], &work[jw + 1]
+		    , &i__1, &info);
+	}
+
+/*        ==== Copy updated reduced window into place ==== */
+
+	if (kwtop > 1) {
+	    h__[kwtop + (kwtop - 1) * h_dim1] = s * v[v_dim1 + 1];
+	}
+	igraphdlacpy_("U", &jw, &jw, &t[t_offset], ldt, &h__[kwtop + kwtop * h_dim1]
+		, ldh);
+	i__1 = jw - 1;
+	i__2 = *ldt + 1;
+	i__3 = *ldh + 1;
+	igraphdcopy_(&i__1, &t[t_dim1 + 2], &i__2, &h__[kwtop + 1 + kwtop * h_dim1],
+		 &i__3);
+
+/*        ==== Accumulate orthogonal matrix in order update   
+          .    H and Z, if requested.  ==== */
+
+	if (*ns > 1 && s != 0.) {
+	    i__1 = *lwork - jw;
+	    igraphdormhr_("R", "N", &jw, ns, &c__1, ns, &t[t_offset], ldt, &work[1],
+		     &v[v_offset], ldv, &work[jw + 1], &i__1, &info);
+	}
+
+/*        ==== Update vertical slab in H ==== */
+
+	if (*wantt) {
+	    ltop = 1;
+	} else {
+	    ltop = *ktop;
+	}
+	i__1 = kwtop - 1;
+	i__2 = *nv;
+	for (krow = ltop; i__2 < 0 ? krow >= i__1 : krow <= i__1; krow += 
+		i__2) {
+/* Computing MIN */
+	    i__3 = *nv, i__4 = kwtop - krow;
+	    kln = min(i__3,i__4);
+	    igraphdgemm_("N", "N", &kln, &jw, &jw, &c_b18, &h__[krow + kwtop * 
+		    h_dim1], ldh, &v[v_offset], ldv, &c_b17, &wv[wv_offset], 
+		    ldwv);
+	    igraphdlacpy_("A", &kln, &jw, &wv[wv_offset], ldwv, &h__[krow + kwtop * 
+		    h_dim1], ldh);
+/* L70: */
+	}
+
+/*        ==== Update horizontal slab in H ==== */
+
+	if (*wantt) {
+	    i__2 = *n;
+	    i__1 = *nh;
+	    for (kcol = *kbot + 1; i__1 < 0 ? kcol >= i__2 : kcol <= i__2; 
+		    kcol += i__1) {
+/* Computing MIN */
+		i__3 = *nh, i__4 = *n - kcol + 1;
+		kln = min(i__3,i__4);
+		igraphdgemm_("C", "N", &jw, &kln, &jw, &c_b18, &v[v_offset], ldv, &
+			h__[kwtop + kcol * h_dim1], ldh, &c_b17, &t[t_offset],
+			 ldt);
+		igraphdlacpy_("A", &jw, &kln, &t[t_offset], ldt, &h__[kwtop + kcol *
+			 h_dim1], ldh);
+/* L80: */
+	    }
+	}
+
+/*        ==== Update vertical slab in Z ==== */
+
+	if (*wantz) {
+	    i__1 = *ihiz;
+	    i__2 = *nv;
+	    for (krow = *iloz; i__2 < 0 ? krow >= i__1 : krow <= i__1; krow +=
+		     i__2) {
+/* Computing MIN */
+		i__3 = *nv, i__4 = *ihiz - krow + 1;
+		kln = min(i__3,i__4);
+		igraphdgemm_("N", "N", &kln, &jw, &jw, &c_b18, &z__[krow + kwtop * 
+			z_dim1], ldz, &v[v_offset], ldv, &c_b17, &wv[
+			wv_offset], ldwv);
+		igraphdlacpy_("A", &kln, &jw, &wv[wv_offset], ldwv, &z__[krow + 
+			kwtop * z_dim1], ldz);
+/* L90: */
+	    }
+	}
+    }
+
+/*     ==== Return the number of deflations ... ==== */
+
+    *nd = jw - *ns;
+
+/*     ==== ... and the number of shifts. (Subtracting   
+       .    INFQR from the spike length takes care   
+       .    of the case of a rare QR failure while   
+       .    calculating eigenvalues of the deflation   
+       .    window.)  ==== */
+
+    *ns -= infqr;
+
+/*      ==== Return optimal workspace. ==== */
+
+    work[1] = (doublereal) lwkopt;
+
+/*     ==== End of DLAQR3 ==== */
+
+    return 0;
+} /* igraphdlaqr3_ */
+
diff --git a/igraph/src/dlaqr4.c b/igraph/src/dlaqr4.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaqr4.c
@@ -0,0 +1,844 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__13 = 13;
+static integer c__15 = 15;
+static integer c_n1 = -1;
+static integer c__12 = 12;
+static integer c__14 = 14;
+static integer c__16 = 16;
+static logical c_false = FALSE_;
+static integer c__1 = 1;
+static integer c__3 = 3;
+
+/* > \brief \b DLAQR4 computes the eigenvalues of a Hessenberg matrix, and optionally the matrices from the Sc
+hur decomposition.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAQR4 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaqr4.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaqr4.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaqr4.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAQR4( WANTT, WANTZ, N, ILO, IHI, H, LDH, WR, WI,   
+                            ILOZ, IHIZ, Z, LDZ, WORK, LWORK, INFO )   
+
+         INTEGER            IHI, IHIZ, ILO, ILOZ, INFO, LDH, LDZ, LWORK, N   
+         LOGICAL            WANTT, WANTZ   
+         DOUBLE PRECISION   H( LDH, * ), WI( * ), WORK( * ), WR( * ),   
+        $                   Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >    DLAQR4 implements one level of recursion for DLAQR0.   
+   >    It is a complete implementation of the small bulge multi-shift   
+   >    QR algorithm.  It may be called by DLAQR0 and, for large enough   
+   >    deflation window size, it may be called by DLAQR3.  This   
+   >    subroutine is identical to DLAQR0 except that it calls DLAQR2   
+   >    instead of DLAQR3.   
+   >   
+   >    DLAQR4 computes the eigenvalues of a Hessenberg matrix H   
+   >    and, optionally, the matrices T and Z from the Schur decomposition   
+   >    H = Z T Z**T, where T is an upper quasi-triangular matrix (the   
+   >    Schur form), and Z is the orthogonal matrix of Schur vectors.   
+   >   
+   >    Optionally Z may be postmultiplied into an input orthogonal   
+   >    matrix Q so that this routine can give the Schur factorization   
+   >    of a matrix A which has been reduced to the Hessenberg form H   
+   >    by the orthogonal matrix Q:  A = Q*H*Q**T = (QZ)*T*(QZ)**T.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] WANTT   
+   > \verbatim   
+   >          WANTT is LOGICAL   
+   >          = .TRUE. : the full Schur form T is required;   
+   >          = .FALSE.: only eigenvalues are required.   
+   > \endverbatim   
+   >   
+   > \param[in] WANTZ   
+   > \verbatim   
+   >          WANTZ is LOGICAL   
+   >          = .TRUE. : the matrix of Schur vectors Z is required;   
+   >          = .FALSE.: Schur vectors are not required.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >           The order of the matrix H.  N .GE. 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >           It is assumed that H is already upper triangular in rows   
+   >           and columns 1:ILO-1 and IHI+1:N and, if ILO.GT.1,   
+   >           H(ILO,ILO-1) is zero. ILO and IHI are normally set by a   
+   >           previous call to DGEBAL, and then passed to DGEHRD when the   
+   >           matrix output by DGEBAL is reduced to Hessenberg form.   
+   >           Otherwise, ILO and IHI should be set to 1 and N,   
+   >           respectively.  If N.GT.0, then 1.LE.ILO.LE.IHI.LE.N.   
+   >           If N = 0, then ILO = 1 and IHI = 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] H   
+   > \verbatim   
+   >          H is DOUBLE PRECISION array, dimension (LDH,N)   
+   >           On entry, the upper Hessenberg matrix H.   
+   >           On exit, if INFO = 0 and WANTT is .TRUE., then H contains   
+   >           the upper quasi-triangular matrix T from the Schur   
+   >           decomposition (the Schur form); 2-by-2 diagonal blocks   
+   >           (corresponding to complex conjugate pairs of eigenvalues)   
+   >           are returned in standard form, with H(i,i) = H(i+1,i+1)   
+   >           and H(i+1,i)*H(i,i+1).LT.0. If INFO = 0 and WANTT is   
+   >           .FALSE., then the contents of H are unspecified on exit.   
+   >           (The output value of H when INFO.GT.0 is given under the   
+   >           description of INFO below.)   
+   >   
+   >           This subroutine may explicitly set H(i,j) = 0 for i.GT.j and   
+   >           j = 1, 2, ... ILO-1 or j = IHI+1, IHI+2, ... N.   
+   > \endverbatim   
+   >   
+   > \param[in] LDH   
+   > \verbatim   
+   >          LDH is INTEGER   
+   >           The leading dimension of the array H. LDH .GE. max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] WR   
+   > \verbatim   
+   >          WR is DOUBLE PRECISION array, dimension (IHI)   
+   > \endverbatim   
+   >   
+   > \param[out] WI   
+   > \verbatim   
+   >          WI is DOUBLE PRECISION array, dimension (IHI)   
+   >           The real and imaginary parts, respectively, of the computed   
+   >           eigenvalues of H(ILO:IHI,ILO:IHI) are stored in WR(ILO:IHI)   
+   >           and WI(ILO:IHI). If two eigenvalues are computed as a   
+   >           complex conjugate pair, they are stored in consecutive   
+   >           elements of WR and WI, say the i-th and (i+1)th, with   
+   >           WI(i) .GT. 0 and WI(i+1) .LT. 0. If WANTT is .TRUE., then   
+   >           the eigenvalues are stored in the same order as on the   
+   >           diagonal of the Schur form returned in H, with   
+   >           WR(i) = H(i,i) and, if H(i:i+1,i:i+1) is a 2-by-2 diagonal   
+   >           block, WI(i) = sqrt(-H(i+1,i)*H(i,i+1)) and   
+   >           WI(i+1) = -WI(i).   
+   > \endverbatim   
+   >   
+   > \param[in] ILOZ   
+   > \verbatim   
+   >          ILOZ is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHIZ   
+   > \verbatim   
+   >          IHIZ is INTEGER   
+   >           Specify the rows of Z to which transformations must be   
+   >           applied if WANTZ is .TRUE..   
+   >           1 .LE. ILOZ .LE. ILO; IHI .LE. IHIZ .LE. N.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ,IHI)   
+   >           If WANTZ is .FALSE., then Z is not referenced.   
+   >           If WANTZ is .TRUE., then Z(ILO:IHI,ILOZ:IHIZ) is   
+   >           replaced by Z(ILO:IHI,ILOZ:IHIZ)*U where U is the   
+   >           orthogonal Schur factor of H(ILO:IHI,ILO:IHI).   
+   >           (The output value of Z when INFO.GT.0 is given under   
+   >           the description of INFO below.)   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >           The leading dimension of the array Z.  if WANTZ is .TRUE.   
+   >           then LDZ.GE.MAX(1,IHIZ).  Otherwize, LDZ.GE.1.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension LWORK   
+   >           On exit, if LWORK = -1, WORK(1) returns an estimate of   
+   >           the optimal value for LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >           The dimension of the array WORK.  LWORK .GE. max(1,N)   
+   >           is sufficient, but LWORK typically as large as 6*N may   
+   >           be required for optimal performance.  A workspace query   
+   >           to determine the optimal workspace size is recommended.   
+   >   
+   >           If LWORK = -1, then DLAQR4 does a workspace query.   
+   >           In this case, DLAQR4 checks the input parameters and   
+   >           estimates the optimal workspace size for the given   
+   >           values of N, ILO and IHI.  The estimate is returned   
+   >           in WORK(1).  No error message related to LWORK is   
+   >           issued by XERBLA.  Neither H nor Z are accessed.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >             =  0:  successful exit   
+   >           .GT. 0:  if INFO = i, DLAQR4 failed to compute all of   
+   >                the eigenvalues.  Elements 1:ilo-1 and i+1:n of WR   
+   >                and WI contain those eigenvalues which have been   
+   >                successfully computed.  (Failures are rare.)   
+   >   
+   >                If INFO .GT. 0 and WANT is .FALSE., then on exit,   
+   >                the remaining unconverged eigenvalues are the eigen-   
+   >                values of the upper Hessenberg matrix rows and   
+   >                columns ILO through INFO of the final, output   
+   >                value of H.   
+   >   
+   >                If INFO .GT. 0 and WANTT is .TRUE., then on exit   
+   >   
+   >           (*)  (initial value of H)*U  = U*(final value of H)   
+   >   
+   >                where U is a orthogonal matrix.  The final   
+   >                value of  H is upper Hessenberg and triangular in   
+   >                rows and columns INFO+1 through IHI.   
+   >   
+   >                If INFO .GT. 0 and WANTZ is .TRUE., then on exit   
+   >   
+   >                  (final value of Z(ILO:IHI,ILOZ:IHIZ)   
+   >                   =  (initial value of Z(ILO:IHI,ILOZ:IHIZ)*U   
+   >   
+   >                where U is the orthogonal matrix in (*) (regard-   
+   >                less of the value of WANTT.)   
+   >   
+   >                If INFO .GT. 0 and WANTZ is .FALSE., then Z is not   
+   >                accessed.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >       Karen Braman and Ralph Byers, Department of Mathematics,   
+   >       University of Kansas, USA   
+
+   > \par References:   
+    ================   
+   >   
+   >       K. Braman, R. Byers and R. Mathias, The Multi-Shift QR   
+   >       Algorithm Part I: Maintaining Well Focused Shifts, and Level 3   
+   >       Performance, SIAM Journal of Matrix Analysis, volume 23, pages   
+   >       929--947, 2002.   
+   > \n   
+   >       K. Braman, R. Byers and R. Mathias, The Multi-Shift QR   
+   >       Algorithm Part II: Aggressive Early Deflation, SIAM Journal   
+   >       of Matrix Analysis, volume 23, pages 948--973, 2002.   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaqr4_(logical *wantt, logical *wantz, integer *n, 
+	integer *ilo, integer *ihi, doublereal *h__, integer *ldh, doublereal 
+	*wr, doublereal *wi, integer *iloz, integer *ihiz, doublereal *z__, 
+	integer *ldz, doublereal *work, integer *lwork, integer *info)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, z_dim1, z_offset, i__1, i__2, i__3, i__4, i__5;
+    doublereal d__1, d__2, d__3, d__4;
+
+    /* Local variables */
+    integer i__, k;
+    doublereal aa, bb, cc, dd;
+    integer ld;
+    doublereal cs;
+    integer nh, it, ks, kt;
+    doublereal sn;
+    integer ku, kv, ls, ns;
+    doublereal ss;
+    integer nw, inf, kdu, nho, nve, kwh, nsr, nwr, kwv, ndec, ndfl, kbot, 
+	    nmin;
+    doublereal swap;
+    integer ktop;
+    doublereal zdum[1]	/* was [1][1] */;
+    integer kacc22, itmax, nsmax, nwmax, kwtop;
+    extern /* Subroutine */ int igraphdlaqr2_(logical *, logical *, integer *, 
+	    integer *, integer *, integer *, doublereal *, integer *, integer 
+	    *, integer *, doublereal *, integer *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *, integer *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdlanv2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *), igraphdlaqr5_(
+	    logical *, logical *, integer *, integer *, integer *, integer *, 
+	    integer *, doublereal *, doublereal *, doublereal *, integer *, 
+	    integer *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *, doublereal *, integer *);
+    integer nibble;
+    extern /* Subroutine */ int igraphdlahqr_(logical *, logical *, integer *, 
+	    integer *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *), igraphdlacpy_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    char jbcmpz[2];
+    integer nwupbd;
+    logical sorted;
+    integer lwkopt;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ================================================================   
+
+       ==== Matrices of order NTINY or smaller must be processed by   
+       .    DLAHQR because of insufficient subdiagonal scratch space.   
+       .    (This is a hard limit.) ====   
+
+       ==== Exceptional deflation windows:  try to cure rare   
+       .    slow convergence by varying the size of the   
+       .    deflation window after KEXNW iterations. ====   
+
+       ==== Exceptional shifts: try to cure rare slow convergence   
+       .    with ad-hoc exceptional shifts every KEXSH iterations.   
+       .    ====   
+
+       ==== The constants WILK1 and WILK2 are used to form the   
+       .    exceptional shifts. ====   
+       Parameter adjustments */
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    --wr;
+    --wi;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+
+/*     ==== Quick return for N = 0: nothing to do. ==== */
+
+    if (*n == 0) {
+	work[1] = 1.;
+	return 0;
+    }
+
+    if (*n <= 11) {
+
+/*        ==== Tiny matrices must use DLAHQR. ==== */
+
+	lwkopt = 1;
+	if (*lwork != -1) {
+	    igraphdlahqr_(wantt, wantz, n, ilo, ihi, &h__[h_offset], ldh, &wr[1], &
+		    wi[1], iloz, ihiz, &z__[z_offset], ldz, info);
+	}
+    } else {
+
+/*        ==== Use small bulge multi-shift QR with aggressive early   
+          .    deflation on larger-than-tiny matrices. ====   
+
+          ==== Hope for the best. ==== */
+
+	*info = 0;
+
+/*        ==== Set up job flags for ILAENV. ==== */
+
+	if (*wantt) {
+	    *(unsigned char *)jbcmpz = 'S';
+	} else {
+	    *(unsigned char *)jbcmpz = 'E';
+	}
+	if (*wantz) {
+	    *(unsigned char *)&jbcmpz[1] = 'V';
+	} else {
+	    *(unsigned char *)&jbcmpz[1] = 'N';
+	}
+
+/*        ==== NWR = recommended deflation window size.  At this   
+          .    point,  N .GT. NTINY = 11, so there is enough   
+          .    subdiagonal workspace for NWR.GE.2 as required.   
+          .    (In fact, there is enough subdiagonal space for   
+          .    NWR.GE.3.) ==== */
+
+	nwr = igraphilaenv_(&c__13, "DLAQR4", jbcmpz, n, ilo, ihi, lwork, (ftnlen)6,
+		 (ftnlen)2);
+	nwr = max(2,nwr);
+/* Computing MIN */
+	i__1 = *ihi - *ilo + 1, i__2 = (*n - 1) / 3, i__1 = min(i__1,i__2);
+	nwr = min(i__1,nwr);
+
+/*        ==== NSR = recommended number of simultaneous shifts.   
+          .    At this point N .GT. NTINY = 11, so there is at   
+          .    enough subdiagonal workspace for NSR to be even   
+          .    and greater than or equal to two as required. ==== */
+
+	nsr = igraphilaenv_(&c__15, "DLAQR4", jbcmpz, n, ilo, ihi, lwork, (ftnlen)6,
+		 (ftnlen)2);
+/* Computing MIN */
+	i__1 = nsr, i__2 = (*n + 6) / 9, i__1 = min(i__1,i__2), i__2 = *ihi - 
+		*ilo;
+	nsr = min(i__1,i__2);
+/* Computing MAX */
+	i__1 = 2, i__2 = nsr - nsr % 2;
+	nsr = max(i__1,i__2);
+
+/*        ==== Estimate optimal workspace ====   
+
+          ==== Workspace query call to DLAQR2 ==== */
+
+	i__1 = nwr + 1;
+	igraphdlaqr2_(wantt, wantz, n, ilo, ihi, &i__1, &h__[h_offset], ldh, iloz, 
+		ihiz, &z__[z_offset], ldz, &ls, &ld, &wr[1], &wi[1], &h__[
+		h_offset], ldh, n, &h__[h_offset], ldh, n, &h__[h_offset], 
+		ldh, &work[1], &c_n1);
+
+/*        ==== Optimal workspace = MAX(DLAQR5, DLAQR2) ====   
+
+   Computing MAX */
+	i__1 = nsr * 3 / 2, i__2 = (integer) work[1];
+	lwkopt = max(i__1,i__2);
+
+/*        ==== Quick return in case of workspace query. ==== */
+
+	if (*lwork == -1) {
+	    work[1] = (doublereal) lwkopt;
+	    return 0;
+	}
+
+/*        ==== DLAHQR/DLAQR0 crossover point ==== */
+
+	nmin = igraphilaenv_(&c__12, "DLAQR4", jbcmpz, n, ilo, ihi, lwork, (ftnlen)
+		6, (ftnlen)2);
+	nmin = max(11,nmin);
+
+/*        ==== Nibble crossover point ==== */
+
+	nibble = igraphilaenv_(&c__14, "DLAQR4", jbcmpz, n, ilo, ihi, lwork, (
+		ftnlen)6, (ftnlen)2);
+	nibble = max(0,nibble);
+
+/*        ==== Accumulate reflections during ttswp?  Use block   
+          .    2-by-2 structure during matrix-matrix multiply? ==== */
+
+	kacc22 = igraphilaenv_(&c__16, "DLAQR4", jbcmpz, n, ilo, ihi, lwork, (
+		ftnlen)6, (ftnlen)2);
+	kacc22 = max(0,kacc22);
+	kacc22 = min(2,kacc22);
+
+/*        ==== NWMAX = the largest possible deflation window for   
+          .    which there is sufficient workspace. ====   
+
+   Computing MIN */
+	i__1 = (*n - 1) / 3, i__2 = *lwork / 2;
+	nwmax = min(i__1,i__2);
+	nw = nwmax;
+
+/*        ==== NSMAX = the Largest number of simultaneous shifts   
+          .    for which there is sufficient workspace. ====   
+
+   Computing MIN */
+	i__1 = (*n + 6) / 9, i__2 = (*lwork << 1) / 3;
+	nsmax = min(i__1,i__2);
+	nsmax -= nsmax % 2;
+
+/*        ==== NDFL: an iteration count restarted at deflation. ==== */
+
+	ndfl = 1;
+
+/*        ==== ITMAX = iteration limit ====   
+
+   Computing MAX */
+	i__1 = 10, i__2 = *ihi - *ilo + 1;
+	itmax = max(i__1,i__2) * 30;
+
+/*        ==== Last row and column in the active block ==== */
+
+	kbot = *ihi;
+
+/*        ==== Main Loop ==== */
+
+	i__1 = itmax;
+	for (it = 1; it <= i__1; ++it) {
+
+/*           ==== Done when KBOT falls below ILO ==== */
+
+	    if (kbot < *ilo) {
+		goto L90;
+	    }
+
+/*           ==== Locate active block ==== */
+
+	    i__2 = *ilo + 1;
+	    for (k = kbot; k >= i__2; --k) {
+		if (h__[k + (k - 1) * h_dim1] == 0.) {
+		    goto L20;
+		}
+/* L10: */
+	    }
+	    k = *ilo;
+L20:
+	    ktop = k;
+
+/*           ==== Select deflation window size:   
+             .    Typical Case:   
+             .      If possible and advisable, nibble the entire   
+             .      active block.  If not, use size MIN(NWR,NWMAX)   
+             .      or MIN(NWR+1,NWMAX) depending upon which has   
+             .      the smaller corresponding subdiagonal entry   
+             .      (a heuristic).   
+             .   
+             .    Exceptional Case:   
+             .      If there have been no deflations in KEXNW or   
+             .      more iterations, then vary the deflation window   
+             .      size.   At first, because, larger windows are,   
+             .      in general, more powerful than smaller ones,   
+             .      rapidly increase the window to the maximum possible.   
+             .      Then, gradually reduce the window size. ==== */
+
+	    nh = kbot - ktop + 1;
+	    nwupbd = min(nh,nwmax);
+	    if (ndfl < 5) {
+		nw = min(nwupbd,nwr);
+	    } else {
+/* Computing MIN */
+		i__2 = nwupbd, i__3 = nw << 1;
+		nw = min(i__2,i__3);
+	    }
+	    if (nw < nwmax) {
+		if (nw >= nh - 1) {
+		    nw = nh;
+		} else {
+		    kwtop = kbot - nw + 1;
+		    if ((d__1 = h__[kwtop + (kwtop - 1) * h_dim1], abs(d__1)) 
+			    > (d__2 = h__[kwtop - 1 + (kwtop - 2) * h_dim1], 
+			    abs(d__2))) {
+			++nw;
+		    }
+		}
+	    }
+	    if (ndfl < 5) {
+		ndec = -1;
+	    } else if (ndec >= 0 || nw >= nwupbd) {
+		++ndec;
+		if (nw - ndec < 2) {
+		    ndec = 0;
+		}
+		nw -= ndec;
+	    }
+
+/*           ==== Aggressive early deflation:   
+             .    split workspace under the subdiagonal into   
+             .      - an nw-by-nw work array V in the lower   
+             .        left-hand-corner,   
+             .      - an NW-by-at-least-NW-but-more-is-better   
+             .        (NW-by-NHO) horizontal work array along   
+             .        the bottom edge,   
+             .      - an at-least-NW-but-more-is-better (NHV-by-NW)   
+             .        vertical work array along the left-hand-edge.   
+             .        ==== */
+
+	    kv = *n - nw + 1;
+	    kt = nw + 1;
+	    nho = *n - nw - 1 - kt + 1;
+	    kwv = nw + 2;
+	    nve = *n - nw - kwv + 1;
+
+/*           ==== Aggressive early deflation ==== */
+
+	    igraphdlaqr2_(wantt, wantz, n, &ktop, &kbot, &nw, &h__[h_offset], ldh, 
+		    iloz, ihiz, &z__[z_offset], ldz, &ls, &ld, &wr[1], &wi[1],
+		     &h__[kv + h_dim1], ldh, &nho, &h__[kv + kt * h_dim1], 
+		    ldh, &nve, &h__[kwv + h_dim1], ldh, &work[1], lwork);
+
+/*           ==== Adjust KBOT accounting for new deflations. ==== */
+
+	    kbot -= ld;
+
+/*           ==== KS points to the shifts. ==== */
+
+	    ks = kbot - ls + 1;
+
+/*           ==== Skip an expensive QR sweep if there is a (partly   
+             .    heuristic) reason to expect that many eigenvalues   
+             .    will deflate without it.  Here, the QR sweep is   
+             .    skipped if many eigenvalues have just been deflated   
+             .    or if the remaining active block is small. */
+
+	    if (ld == 0 || ld * 100 <= nw * nibble && kbot - ktop + 1 > min(
+		    nmin,nwmax)) {
+
+/*              ==== NS = nominal number of simultaneous shifts.   
+                .    This may be lowered (slightly) if DLAQR2   
+                .    did not provide that many shifts. ====   
+
+   Computing MIN   
+   Computing MAX */
+		i__4 = 2, i__5 = kbot - ktop;
+		i__2 = min(nsmax,nsr), i__3 = max(i__4,i__5);
+		ns = min(i__2,i__3);
+		ns -= ns % 2;
+
+/*              ==== If there have been no deflations   
+                .    in a multiple of KEXSH iterations,   
+                .    then try exceptional shifts.   
+                .    Otherwise use shifts provided by   
+                .    DLAQR2 above or from the eigenvalues   
+                .    of a trailing principal submatrix. ==== */
+
+		if (ndfl % 6 == 0) {
+		    ks = kbot - ns + 1;
+/* Computing MAX */
+		    i__3 = ks + 1, i__4 = ktop + 2;
+		    i__2 = max(i__3,i__4);
+		    for (i__ = kbot; i__ >= i__2; i__ += -2) {
+			ss = (d__1 = h__[i__ + (i__ - 1) * h_dim1], abs(d__1))
+				 + (d__2 = h__[i__ - 1 + (i__ - 2) * h_dim1], 
+				abs(d__2));
+			aa = ss * .75 + h__[i__ + i__ * h_dim1];
+			bb = ss;
+			cc = ss * -.4375;
+			dd = aa;
+			igraphdlanv2_(&aa, &bb, &cc, &dd, &wr[i__ - 1], &wi[i__ - 1]
+				, &wr[i__], &wi[i__], &cs, &sn);
+/* L30: */
+		    }
+		    if (ks == ktop) {
+			wr[ks + 1] = h__[ks + 1 + (ks + 1) * h_dim1];
+			wi[ks + 1] = 0.;
+			wr[ks] = wr[ks + 1];
+			wi[ks] = wi[ks + 1];
+		    }
+		} else {
+
+/*                 ==== Got NS/2 or fewer shifts? Use DLAHQR   
+                   .    on a trailing principal submatrix to   
+                   .    get more. (Since NS.LE.NSMAX.LE.(N+6)/9,   
+                   .    there is enough space below the subdiagonal   
+                   .    to fit an NS-by-NS scratch array.) ==== */
+
+		    if (kbot - ks + 1 <= ns / 2) {
+			ks = kbot - ns + 1;
+			kt = *n - ns + 1;
+			igraphdlacpy_("A", &ns, &ns, &h__[ks + ks * h_dim1], ldh, &
+				h__[kt + h_dim1], ldh);
+			igraphdlahqr_(&c_false, &c_false, &ns, &c__1, &ns, &h__[kt 
+				+ h_dim1], ldh, &wr[ks], &wi[ks], &c__1, &
+				c__1, zdum, &c__1, &inf);
+			ks += inf;
+
+/*                    ==== In case of a rare QR failure use   
+                      .    eigenvalues of the trailing 2-by-2   
+                      .    principal submatrix.  ==== */
+
+			if (ks >= kbot) {
+			    aa = h__[kbot - 1 + (kbot - 1) * h_dim1];
+			    cc = h__[kbot + (kbot - 1) * h_dim1];
+			    bb = h__[kbot - 1 + kbot * h_dim1];
+			    dd = h__[kbot + kbot * h_dim1];
+			    igraphdlanv2_(&aa, &bb, &cc, &dd, &wr[kbot - 1], &wi[
+				    kbot - 1], &wr[kbot], &wi[kbot], &cs, &sn)
+				    ;
+			    ks = kbot - 1;
+			}
+		    }
+
+		    if (kbot - ks + 1 > ns) {
+
+/*                    ==== Sort the shifts (Helps a little)   
+                      .    Bubble sort keeps complex conjugate   
+                      .    pairs together. ==== */
+
+			sorted = FALSE_;
+			i__2 = ks + 1;
+			for (k = kbot; k >= i__2; --k) {
+			    if (sorted) {
+				goto L60;
+			    }
+			    sorted = TRUE_;
+			    i__3 = k - 1;
+			    for (i__ = ks; i__ <= i__3; ++i__) {
+				if ((d__1 = wr[i__], abs(d__1)) + (d__2 = wi[
+					i__], abs(d__2)) < (d__3 = wr[i__ + 1]
+					, abs(d__3)) + (d__4 = wi[i__ + 1], 
+					abs(d__4))) {
+				    sorted = FALSE_;
+
+				    swap = wr[i__];
+				    wr[i__] = wr[i__ + 1];
+				    wr[i__ + 1] = swap;
+
+				    swap = wi[i__];
+				    wi[i__] = wi[i__ + 1];
+				    wi[i__ + 1] = swap;
+				}
+/* L40: */
+			    }
+/* L50: */
+			}
+L60:
+			;
+		    }
+
+/*                 ==== Shuffle shifts into pairs of real shifts   
+                   .    and pairs of complex conjugate shifts   
+                   .    assuming complex conjugate shifts are   
+                   .    already adjacent to one another. (Yes,   
+                   .    they are.)  ==== */
+
+		    i__2 = ks + 2;
+		    for (i__ = kbot; i__ >= i__2; i__ += -2) {
+			if (wi[i__] != -wi[i__ - 1]) {
+
+			    swap = wr[i__];
+			    wr[i__] = wr[i__ - 1];
+			    wr[i__ - 1] = wr[i__ - 2];
+			    wr[i__ - 2] = swap;
+
+			    swap = wi[i__];
+			    wi[i__] = wi[i__ - 1];
+			    wi[i__ - 1] = wi[i__ - 2];
+			    wi[i__ - 2] = swap;
+			}
+/* L70: */
+		    }
+		}
+
+/*              ==== If there are only two shifts and both are   
+                .    real, then use only one.  ==== */
+
+		if (kbot - ks + 1 == 2) {
+		    if (wi[kbot] == 0.) {
+			if ((d__1 = wr[kbot] - h__[kbot + kbot * h_dim1], abs(
+				d__1)) < (d__2 = wr[kbot - 1] - h__[kbot + 
+				kbot * h_dim1], abs(d__2))) {
+			    wr[kbot - 1] = wr[kbot];
+			} else {
+			    wr[kbot] = wr[kbot - 1];
+			}
+		    }
+		}
+
+/*              ==== Use up to NS of the the smallest magnatiude   
+                .    shifts.  If there aren't NS shifts available,   
+                .    then use them all, possibly dropping one to   
+                .    make the number of shifts even. ====   
+
+   Computing MIN */
+		i__2 = ns, i__3 = kbot - ks + 1;
+		ns = min(i__2,i__3);
+		ns -= ns % 2;
+		ks = kbot - ns + 1;
+
+/*              ==== Small-bulge multi-shift QR sweep:   
+                .    split workspace under the subdiagonal into   
+                .    - a KDU-by-KDU work array U in the lower   
+                .      left-hand-corner,   
+                .    - a KDU-by-at-least-KDU-but-more-is-better   
+                .      (KDU-by-NHo) horizontal work array WH along   
+                .      the bottom edge,   
+                .    - and an at-least-KDU-but-more-is-better-by-KDU   
+                .      (NVE-by-KDU) vertical work WV arrow along   
+                .      the left-hand-edge. ==== */
+
+		kdu = ns * 3 - 3;
+		ku = *n - kdu + 1;
+		kwh = kdu + 1;
+		nho = *n - kdu - 3 - (kdu + 1) + 1;
+		kwv = kdu + 4;
+		nve = *n - kdu - kwv + 1;
+
+/*              ==== Small-bulge multi-shift QR sweep ==== */
+
+		igraphdlaqr5_(wantt, wantz, &kacc22, n, &ktop, &kbot, &ns, &wr[ks], 
+			&wi[ks], &h__[h_offset], ldh, iloz, ihiz, &z__[
+			z_offset], ldz, &work[1], &c__3, &h__[ku + h_dim1], 
+			ldh, &nve, &h__[kwv + h_dim1], ldh, &nho, &h__[ku + 
+			kwh * h_dim1], ldh);
+	    }
+
+/*           ==== Note progress (or the lack of it). ==== */
+
+	    if (ld > 0) {
+		ndfl = 1;
+	    } else {
+		++ndfl;
+	    }
+
+/*           ==== End of main loop ====   
+   L80: */
+	}
+
+/*        ==== Iteration limit exceeded.  Set INFO to show where   
+          .    the problem occurred and exit. ==== */
+
+	*info = kbot;
+L90:
+	;
+    }
+
+/*     ==== Return the optimal value of LWORK. ==== */
+
+    work[1] = (doublereal) lwkopt;
+
+/*     ==== End of DLAQR4 ==== */
+
+    return 0;
+} /* igraphdlaqr4_ */
+
diff --git a/igraph/src/dlaqr5.c b/igraph/src/dlaqr5.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaqr5.c
@@ -0,0 +1,1148 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b7 = 0.;
+static doublereal c_b8 = 1.;
+static integer c__3 = 3;
+static integer c__1 = 1;
+static integer c__2 = 2;
+
+/* > \brief \b DLAQR5 performs a single small-bulge multi-shift QR sweep.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAQR5 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaqr5.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaqr5.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaqr5.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAQR5( WANTT, WANTZ, KACC22, N, KTOP, KBOT, NSHFTS,   
+                            SR, SI, H, LDH, ILOZ, IHIZ, Z, LDZ, V, LDV, U,   
+                            LDU, NV, WV, LDWV, NH, WH, LDWH )   
+
+         INTEGER            IHIZ, ILOZ, KACC22, KBOT, KTOP, LDH, LDU, LDV,   
+        $                   LDWH, LDWV, LDZ, N, NH, NSHFTS, NV   
+         LOGICAL            WANTT, WANTZ   
+         DOUBLE PRECISION   H( LDH, * ), SI( * ), SR( * ), U( LDU, * ),   
+        $                   V( LDV, * ), WH( LDWH, * ), WV( LDWV, * ),   
+        $                   Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >    DLAQR5, called by DLAQR0, performs a   
+   >    single small-bulge multi-shift QR sweep.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] WANTT   
+   > \verbatim   
+   >          WANTT is logical scalar   
+   >             WANTT = .true. if the quasi-triangular Schur factor   
+   >             is being computed.  WANTT is set to .false. otherwise.   
+   > \endverbatim   
+   >   
+   > \param[in] WANTZ   
+   > \verbatim   
+   >          WANTZ is logical scalar   
+   >             WANTZ = .true. if the orthogonal Schur factor is being   
+   >             computed.  WANTZ is set to .false. otherwise.   
+   > \endverbatim   
+   >   
+   > \param[in] KACC22   
+   > \verbatim   
+   >          KACC22 is integer with value 0, 1, or 2.   
+   >             Specifies the computation mode of far-from-diagonal   
+   >             orthogonal updates.   
+   >        = 0: DLAQR5 does not accumulate reflections and does not   
+   >             use matrix-matrix multiply to update far-from-diagonal   
+   >             matrix entries.   
+   >        = 1: DLAQR5 accumulates reflections and uses matrix-matrix   
+   >             multiply to update the far-from-diagonal matrix entries.   
+   >        = 2: DLAQR5 accumulates reflections, uses matrix-matrix   
+   >             multiply to update the far-from-diagonal matrix entries,   
+   >             and takes advantage of 2-by-2 block structure during   
+   >             matrix multiplies.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is integer scalar   
+   >             N is the order of the Hessenberg matrix H upon which this   
+   >             subroutine operates.   
+   > \endverbatim   
+   >   
+   > \param[in] KTOP   
+   > \verbatim   
+   >          KTOP is integer scalar   
+   > \endverbatim   
+   >   
+   > \param[in] KBOT   
+   > \verbatim   
+   >          KBOT is integer scalar   
+   >             These are the first and last rows and columns of an   
+   >             isolated diagonal block upon which the QR sweep is to be   
+   >             applied. It is assumed without a check that   
+   >                       either KTOP = 1  or   H(KTOP,KTOP-1) = 0   
+   >             and   
+   >                       either KBOT = N  or   H(KBOT+1,KBOT) = 0.   
+   > \endverbatim   
+   >   
+   > \param[in] NSHFTS   
+   > \verbatim   
+   >          NSHFTS is integer scalar   
+   >             NSHFTS gives the number of simultaneous shifts.  NSHFTS   
+   >             must be positive and even.   
+   > \endverbatim   
+   >   
+   > \param[in,out] SR   
+   > \verbatim   
+   >          SR is DOUBLE PRECISION array of size (NSHFTS)   
+   > \endverbatim   
+   >   
+   > \param[in,out] SI   
+   > \verbatim   
+   >          SI is DOUBLE PRECISION array of size (NSHFTS)   
+   >             SR contains the real parts and SI contains the imaginary   
+   >             parts of the NSHFTS shifts of origin that define the   
+   >             multi-shift QR sweep.  On output SR and SI may be   
+   >             reordered.   
+   > \endverbatim   
+   >   
+   > \param[in,out] H   
+   > \verbatim   
+   >          H is DOUBLE PRECISION array of size (LDH,N)   
+   >             On input H contains a Hessenberg matrix.  On output a   
+   >             multi-shift QR sweep with shifts SR(J)+i*SI(J) is applied   
+   >             to the isolated diagonal block in rows and columns KTOP   
+   >             through KBOT.   
+   > \endverbatim   
+   >   
+   > \param[in] LDH   
+   > \verbatim   
+   >          LDH is integer scalar   
+   >             LDH is the leading dimension of H just as declared in the   
+   >             calling procedure.  LDH.GE.MAX(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] ILOZ   
+   > \verbatim   
+   >          ILOZ is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHIZ   
+   > \verbatim   
+   >          IHIZ is INTEGER   
+   >             Specify the rows of Z to which transformations must be   
+   >             applied if WANTZ is .TRUE.. 1 .LE. ILOZ .LE. IHIZ .LE. N   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array of size (LDZ,IHI)   
+   >             If WANTZ = .TRUE., then the QR Sweep orthogonal   
+   >             similarity transformation is accumulated into   
+   >             Z(ILOZ:IHIZ,ILO:IHI) from the right.   
+   >             If WANTZ = .FALSE., then Z is unreferenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is integer scalar   
+   >             LDA is the leading dimension of Z just as declared in   
+   >             the calling procedure. LDZ.GE.N.   
+   > \endverbatim   
+   >   
+   > \param[out] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array of size (LDV,NSHFTS/2)   
+   > \endverbatim   
+   >   
+   > \param[in] LDV   
+   > \verbatim   
+   >          LDV is integer scalar   
+   >             LDV is the leading dimension of V as declared in the   
+   >             calling procedure.  LDV.GE.3.   
+   > \endverbatim   
+   >   
+   > \param[out] U   
+   > \verbatim   
+   >          U is DOUBLE PRECISION array of size   
+   >             (LDU,3*NSHFTS-3)   
+   > \endverbatim   
+   >   
+   > \param[in] LDU   
+   > \verbatim   
+   >          LDU is integer scalar   
+   >             LDU is the leading dimension of U just as declared in the   
+   >             in the calling subroutine.  LDU.GE.3*NSHFTS-3.   
+   > \endverbatim   
+   >   
+   > \param[in] NH   
+   > \verbatim   
+   >          NH is integer scalar   
+   >             NH is the number of columns in array WH available for   
+   >             workspace. NH.GE.1.   
+   > \endverbatim   
+   >   
+   > \param[out] WH   
+   > \verbatim   
+   >          WH is DOUBLE PRECISION array of size (LDWH,NH)   
+   > \endverbatim   
+   >   
+   > \param[in] LDWH   
+   > \verbatim   
+   >          LDWH is integer scalar   
+   >             Leading dimension of WH just as declared in the   
+   >             calling procedure.  LDWH.GE.3*NSHFTS-3.   
+   > \endverbatim   
+   >   
+   > \param[in] NV   
+   > \verbatim   
+   >          NV is integer scalar   
+   >             NV is the number of rows in WV agailable for workspace.   
+   >             NV.GE.1.   
+   > \endverbatim   
+   >   
+   > \param[out] WV   
+   > \verbatim   
+   >          WV is DOUBLE PRECISION array of size   
+   >             (LDWV,3*NSHFTS-3)   
+   > \endverbatim   
+   >   
+   > \param[in] LDWV   
+   > \verbatim   
+   >          LDWV is integer scalar   
+   >             LDWV is the leading dimension of WV as declared in the   
+   >             in the calling subroutine.  LDWV.GE.NV.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >       Karen Braman and Ralph Byers, Department of Mathematics,   
+   >       University of Kansas, USA   
+
+   > \par References:   
+    ================   
+   >   
+   >       K. Braman, R. Byers and R. Mathias, The Multi-Shift QR   
+   >       Algorithm Part I: Maintaining Well Focused Shifts, and Level 3   
+   >       Performance, SIAM Journal of Matrix Analysis, volume 23, pages   
+   >       929--947, 2002.   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaqr5_(logical *wantt, logical *wantz, integer *kacc22, 
+	integer *n, integer *ktop, integer *kbot, integer *nshfts, doublereal 
+	*sr, doublereal *si, doublereal *h__, integer *ldh, integer *iloz, 
+	integer *ihiz, doublereal *z__, integer *ldz, doublereal *v, integer *
+	ldv, doublereal *u, integer *ldu, integer *nv, doublereal *wv, 
+	integer *ldwv, integer *nh, doublereal *wh, integer *ldwh)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, u_dim1, u_offset, v_dim1, v_offset, wh_dim1, 
+	    wh_offset, wv_dim1, wv_offset, z_dim1, z_offset, i__1, i__2, i__3,
+	     i__4, i__5, i__6, i__7;
+    doublereal d__1, d__2, d__3, d__4, d__5;
+
+    /* Local variables */
+    integer i__, j, k, m, i2, j2, i4, j4, k1;
+    doublereal h11, h12, h21, h22;
+    integer m22, ns, nu;
+    doublereal vt[3], scl;
+    integer kdu, kms;
+    doublereal ulp;
+    integer knz, kzs;
+    doublereal tst1, tst2, beta;
+    logical blk22, bmp22;
+    integer mend, jcol, jlen, jbot, mbot;
+    doublereal swap;
+    integer jtop, jrow, mtop;
+    doublereal alpha;
+    logical accum;
+    extern /* Subroutine */ int igraphdgemm_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *);
+    integer ndcol, incol, krcol, nbmps;
+    extern /* Subroutine */ int igraphdtrmm_(char *, char *, char *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdlaqr1_(
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *), igraphdlabad_(doublereal *, 
+	    doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlarfg_(integer *, doublereal *, doublereal *,
+	     integer *, doublereal *), igraphdlacpy_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *);
+    doublereal safmin;
+    extern /* Subroutine */ int igraphdlaset_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *);
+    doublereal safmax, refsum;
+    integer mstart;
+    doublereal smlnum;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    ================================================================   
+
+
+       ==== If there are no shifts, then there is nothing to do. ====   
+
+       Parameter adjustments */
+    --sr;
+    --si;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    u_dim1 = *ldu;
+    u_offset = 1 + u_dim1;
+    u -= u_offset;
+    wv_dim1 = *ldwv;
+    wv_offset = 1 + wv_dim1;
+    wv -= wv_offset;
+    wh_dim1 = *ldwh;
+    wh_offset = 1 + wh_dim1;
+    wh -= wh_offset;
+
+    /* Function Body */
+    if (*nshfts < 2) {
+	return 0;
+    }
+
+/*     ==== If the active block is empty or 1-by-1, then there   
+       .    is nothing to do. ==== */
+
+    if (*ktop >= *kbot) {
+	return 0;
+    }
+
+/*     ==== Shuffle shifts into pairs of real shifts and pairs   
+       .    of complex conjugate shifts assuming complex   
+       .    conjugate shifts are already adjacent to one   
+       .    another. ==== */
+
+    i__1 = *nshfts - 2;
+    for (i__ = 1; i__ <= i__1; i__ += 2) {
+	if (si[i__] != -si[i__ + 1]) {
+
+	    swap = sr[i__];
+	    sr[i__] = sr[i__ + 1];
+	    sr[i__ + 1] = sr[i__ + 2];
+	    sr[i__ + 2] = swap;
+
+	    swap = si[i__];
+	    si[i__] = si[i__ + 1];
+	    si[i__ + 1] = si[i__ + 2];
+	    si[i__ + 2] = swap;
+	}
+/* L10: */
+    }
+
+/*     ==== NSHFTS is supposed to be even, but if it is odd,   
+       .    then simply reduce it by one.  The shuffle above   
+       .    ensures that the dropped shift is real and that   
+       .    the remaining shifts are paired. ==== */
+
+    ns = *nshfts - *nshfts % 2;
+
+/*     ==== Machine constants for deflation ==== */
+
+    safmin = igraphdlamch_("SAFE MINIMUM");
+    safmax = 1. / safmin;
+    igraphdlabad_(&safmin, &safmax);
+    ulp = igraphdlamch_("PRECISION");
+    smlnum = safmin * ((doublereal) (*n) / ulp);
+
+/*     ==== Use accumulated reflections to update far-from-diagonal   
+       .    entries ? ==== */
+
+    accum = *kacc22 == 1 || *kacc22 == 2;
+
+/*     ==== If so, exploit the 2-by-2 block structure? ==== */
+
+    blk22 = ns > 2 && *kacc22 == 2;
+
+/*     ==== clear trash ==== */
+
+    if (*ktop + 2 <= *kbot) {
+	h__[*ktop + 2 + *ktop * h_dim1] = 0.;
+    }
+
+/*     ==== NBMPS = number of 2-shift bulges in the chain ==== */
+
+    nbmps = ns / 2;
+
+/*     ==== KDU = width of slab ==== */
+
+    kdu = nbmps * 6 - 3;
+
+/*     ==== Create and chase chains of NBMPS bulges ==== */
+
+    i__1 = *kbot - 2;
+    i__2 = nbmps * 3 - 2;
+    for (incol = (1 - nbmps) * 3 + *ktop - 1; i__2 < 0 ? incol >= i__1 : 
+	    incol <= i__1; incol += i__2) {
+	ndcol = incol + kdu;
+	if (accum) {
+	    igraphdlaset_("ALL", &kdu, &kdu, &c_b7, &c_b8, &u[u_offset], ldu);
+	}
+
+/*        ==== Near-the-diagonal bulge chase.  The following loop   
+          .    performs the near-the-diagonal part of a small bulge   
+          .    multi-shift QR sweep.  Each 6*NBMPS-2 column diagonal   
+          .    chunk extends from column INCOL to column NDCOL   
+          .    (including both column INCOL and column NDCOL). The   
+          .    following loop chases a 3*NBMPS column long chain of   
+          .    NBMPS bulges 3*NBMPS-2 columns to the right.  (INCOL   
+          .    may be less than KTOP and and NDCOL may be greater than   
+          .    KBOT indicating phantom columns from which to chase   
+          .    bulges before they are actually introduced or to which   
+          .    to chase bulges beyond column KBOT.)  ====   
+
+   Computing MIN */
+	i__4 = incol + nbmps * 3 - 3, i__5 = *kbot - 2;
+	i__3 = min(i__4,i__5);
+	for (krcol = incol; krcol <= i__3; ++krcol) {
+
+/*           ==== Bulges number MTOP to MBOT are active double implicit   
+             .    shift bulges.  There may or may not also be small   
+             .    2-by-2 bulge, if there is room.  The inactive bulges   
+             .    (if any) must wait until the active bulges have moved   
+             .    down the diagonal to make room.  The phantom matrix   
+             .    paradigm described above helps keep track.  ====   
+
+   Computing MAX */
+	    i__4 = 1, i__5 = (*ktop - 1 - krcol + 2) / 3 + 1;
+	    mtop = max(i__4,i__5);
+/* Computing MIN */
+	    i__4 = nbmps, i__5 = (*kbot - krcol) / 3;
+	    mbot = min(i__4,i__5);
+	    m22 = mbot + 1;
+	    bmp22 = mbot < nbmps && krcol + (m22 - 1) * 3 == *kbot - 2;
+
+/*           ==== Generate reflections to chase the chain right   
+             .    one column.  (The minimum value of K is KTOP-1.) ==== */
+
+	    i__4 = mbot;
+	    for (m = mtop; m <= i__4; ++m) {
+		k = krcol + (m - 1) * 3;
+		if (k == *ktop - 1) {
+		    igraphdlaqr1_(&c__3, &h__[*ktop + *ktop * h_dim1], ldh, &sr[(m 
+			    << 1) - 1], &si[(m << 1) - 1], &sr[m * 2], &si[m *
+			     2], &v[m * v_dim1 + 1]);
+		    alpha = v[m * v_dim1 + 1];
+		    igraphdlarfg_(&c__3, &alpha, &v[m * v_dim1 + 2], &c__1, &v[m * 
+			    v_dim1 + 1]);
+		} else {
+		    beta = h__[k + 1 + k * h_dim1];
+		    v[m * v_dim1 + 2] = h__[k + 2 + k * h_dim1];
+		    v[m * v_dim1 + 3] = h__[k + 3 + k * h_dim1];
+		    igraphdlarfg_(&c__3, &beta, &v[m * v_dim1 + 2], &c__1, &v[m * 
+			    v_dim1 + 1]);
+
+/*                 ==== A Bulge may collapse because of vigilant   
+                   .    deflation or destructive underflow.  In the   
+                   .    underflow case, try the two-small-subdiagonals   
+                   .    trick to try to reinflate the bulge.  ==== */
+
+		    if (h__[k + 3 + k * h_dim1] != 0. || h__[k + 3 + (k + 1) *
+			     h_dim1] != 0. || h__[k + 3 + (k + 2) * h_dim1] ==
+			     0.) {
+
+/*                    ==== Typical case: not collapsed (yet). ==== */
+
+			h__[k + 1 + k * h_dim1] = beta;
+			h__[k + 2 + k * h_dim1] = 0.;
+			h__[k + 3 + k * h_dim1] = 0.;
+		    } else {
+
+/*                    ==== Atypical case: collapsed.  Attempt to   
+                      .    reintroduce ignoring H(K+1,K) and H(K+2,K).   
+                      .    If the fill resulting from the new   
+                      .    reflector is too large, then abandon it.   
+                      .    Otherwise, use the new one. ==== */
+
+			igraphdlaqr1_(&c__3, &h__[k + 1 + (k + 1) * h_dim1], ldh, &
+				sr[(m << 1) - 1], &si[(m << 1) - 1], &sr[m * 
+				2], &si[m * 2], vt);
+			alpha = vt[0];
+			igraphdlarfg_(&c__3, &alpha, &vt[1], &c__1, vt);
+			refsum = vt[0] * (h__[k + 1 + k * h_dim1] + vt[1] * 
+				h__[k + 2 + k * h_dim1]);
+
+			if ((d__1 = h__[k + 2 + k * h_dim1] - refsum * vt[1], 
+				abs(d__1)) + (d__2 = refsum * vt[2], abs(d__2)
+				) > ulp * ((d__3 = h__[k + k * h_dim1], abs(
+				d__3)) + (d__4 = h__[k + 1 + (k + 1) * h_dim1]
+				, abs(d__4)) + (d__5 = h__[k + 2 + (k + 2) * 
+				h_dim1], abs(d__5)))) {
+
+/*                       ==== Starting a new bulge here would   
+                         .    create non-negligible fill.  Use   
+                         .    the old one with trepidation. ==== */
+
+			    h__[k + 1 + k * h_dim1] = beta;
+			    h__[k + 2 + k * h_dim1] = 0.;
+			    h__[k + 3 + k * h_dim1] = 0.;
+			} else {
+
+/*                       ==== Stating a new bulge here would   
+                         .    create only negligible fill.   
+                         .    Replace the old reflector with   
+                         .    the new one. ==== */
+
+			    h__[k + 1 + k * h_dim1] -= refsum;
+			    h__[k + 2 + k * h_dim1] = 0.;
+			    h__[k + 3 + k * h_dim1] = 0.;
+			    v[m * v_dim1 + 1] = vt[0];
+			    v[m * v_dim1 + 2] = vt[1];
+			    v[m * v_dim1 + 3] = vt[2];
+			}
+		    }
+		}
+/* L20: */
+	    }
+
+/*           ==== Generate a 2-by-2 reflection, if needed. ==== */
+
+	    k = krcol + (m22 - 1) * 3;
+	    if (bmp22) {
+		if (k == *ktop - 1) {
+		    igraphdlaqr1_(&c__2, &h__[k + 1 + (k + 1) * h_dim1], ldh, &sr[(
+			    m22 << 1) - 1], &si[(m22 << 1) - 1], &sr[m22 * 2],
+			     &si[m22 * 2], &v[m22 * v_dim1 + 1]);
+		    beta = v[m22 * v_dim1 + 1];
+		    igraphdlarfg_(&c__2, &beta, &v[m22 * v_dim1 + 2], &c__1, &v[m22 
+			    * v_dim1 + 1]);
+		} else {
+		    beta = h__[k + 1 + k * h_dim1];
+		    v[m22 * v_dim1 + 2] = h__[k + 2 + k * h_dim1];
+		    igraphdlarfg_(&c__2, &beta, &v[m22 * v_dim1 + 2], &c__1, &v[m22 
+			    * v_dim1 + 1]);
+		    h__[k + 1 + k * h_dim1] = beta;
+		    h__[k + 2 + k * h_dim1] = 0.;
+		}
+	    }
+
+/*           ==== Multiply H by reflections from the left ==== */
+
+	    if (accum) {
+		jbot = min(ndcol,*kbot);
+	    } else if (*wantt) {
+		jbot = *n;
+	    } else {
+		jbot = *kbot;
+	    }
+	    i__4 = jbot;
+	    for (j = max(*ktop,krcol); j <= i__4; ++j) {
+/* Computing MIN */
+		i__5 = mbot, i__6 = (j - krcol + 2) / 3;
+		mend = min(i__5,i__6);
+		i__5 = mend;
+		for (m = mtop; m <= i__5; ++m) {
+		    k = krcol + (m - 1) * 3;
+		    refsum = v[m * v_dim1 + 1] * (h__[k + 1 + j * h_dim1] + v[
+			    m * v_dim1 + 2] * h__[k + 2 + j * h_dim1] + v[m * 
+			    v_dim1 + 3] * h__[k + 3 + j * h_dim1]);
+		    h__[k + 1 + j * h_dim1] -= refsum;
+		    h__[k + 2 + j * h_dim1] -= refsum * v[m * v_dim1 + 2];
+		    h__[k + 3 + j * h_dim1] -= refsum * v[m * v_dim1 + 3];
+/* L30: */
+		}
+/* L40: */
+	    }
+	    if (bmp22) {
+		k = krcol + (m22 - 1) * 3;
+/* Computing MAX */
+		i__4 = k + 1;
+		i__5 = jbot;
+		for (j = max(i__4,*ktop); j <= i__5; ++j) {
+		    refsum = v[m22 * v_dim1 + 1] * (h__[k + 1 + j * h_dim1] + 
+			    v[m22 * v_dim1 + 2] * h__[k + 2 + j * h_dim1]);
+		    h__[k + 1 + j * h_dim1] -= refsum;
+		    h__[k + 2 + j * h_dim1] -= refsum * v[m22 * v_dim1 + 2];
+/* L50: */
+		}
+	    }
+
+/*           ==== Multiply H by reflections from the right.   
+             .    Delay filling in the last row until the   
+             .    vigilant deflation check is complete. ==== */
+
+	    if (accum) {
+		jtop = max(*ktop,incol);
+	    } else if (*wantt) {
+		jtop = 1;
+	    } else {
+		jtop = *ktop;
+	    }
+	    i__5 = mbot;
+	    for (m = mtop; m <= i__5; ++m) {
+		if (v[m * v_dim1 + 1] != 0.) {
+		    k = krcol + (m - 1) * 3;
+/* Computing MIN */
+		    i__6 = *kbot, i__7 = k + 3;
+		    i__4 = min(i__6,i__7);
+		    for (j = jtop; j <= i__4; ++j) {
+			refsum = v[m * v_dim1 + 1] * (h__[j + (k + 1) * 
+				h_dim1] + v[m * v_dim1 + 2] * h__[j + (k + 2) 
+				* h_dim1] + v[m * v_dim1 + 3] * h__[j + (k + 
+				3) * h_dim1]);
+			h__[j + (k + 1) * h_dim1] -= refsum;
+			h__[j + (k + 2) * h_dim1] -= refsum * v[m * v_dim1 + 
+				2];
+			h__[j + (k + 3) * h_dim1] -= refsum * v[m * v_dim1 + 
+				3];
+/* L60: */
+		    }
+
+		    if (accum) {
+
+/*                    ==== Accumulate U. (If necessary, update Z later   
+                      .    with with an efficient matrix-matrix   
+                      .    multiply.) ==== */
+
+			kms = k - incol;
+/* Computing MAX */
+			i__4 = 1, i__6 = *ktop - incol;
+			i__7 = kdu;
+			for (j = max(i__4,i__6); j <= i__7; ++j) {
+			    refsum = v[m * v_dim1 + 1] * (u[j + (kms + 1) * 
+				    u_dim1] + v[m * v_dim1 + 2] * u[j + (kms 
+				    + 2) * u_dim1] + v[m * v_dim1 + 3] * u[j 
+				    + (kms + 3) * u_dim1]);
+			    u[j + (kms + 1) * u_dim1] -= refsum;
+			    u[j + (kms + 2) * u_dim1] -= refsum * v[m * 
+				    v_dim1 + 2];
+			    u[j + (kms + 3) * u_dim1] -= refsum * v[m * 
+				    v_dim1 + 3];
+/* L70: */
+			}
+		    } else if (*wantz) {
+
+/*                    ==== U is not accumulated, so update Z   
+                      .    now by multiplying by reflections   
+                      .    from the right. ==== */
+
+			i__7 = *ihiz;
+			for (j = *iloz; j <= i__7; ++j) {
+			    refsum = v[m * v_dim1 + 1] * (z__[j + (k + 1) * 
+				    z_dim1] + v[m * v_dim1 + 2] * z__[j + (k 
+				    + 2) * z_dim1] + v[m * v_dim1 + 3] * z__[
+				    j + (k + 3) * z_dim1]);
+			    z__[j + (k + 1) * z_dim1] -= refsum;
+			    z__[j + (k + 2) * z_dim1] -= refsum * v[m * 
+				    v_dim1 + 2];
+			    z__[j + (k + 3) * z_dim1] -= refsum * v[m * 
+				    v_dim1 + 3];
+/* L80: */
+			}
+		    }
+		}
+/* L90: */
+	    }
+
+/*           ==== Special case: 2-by-2 reflection (if needed) ==== */
+
+	    k = krcol + (m22 - 1) * 3;
+	    if (bmp22) {
+		if (v[m22 * v_dim1 + 1] != 0.) {
+/* Computing MIN */
+		    i__7 = *kbot, i__4 = k + 3;
+		    i__5 = min(i__7,i__4);
+		    for (j = jtop; j <= i__5; ++j) {
+			refsum = v[m22 * v_dim1 + 1] * (h__[j + (k + 1) * 
+				h_dim1] + v[m22 * v_dim1 + 2] * h__[j + (k + 
+				2) * h_dim1]);
+			h__[j + (k + 1) * h_dim1] -= refsum;
+			h__[j + (k + 2) * h_dim1] -= refsum * v[m22 * v_dim1 
+				+ 2];
+/* L100: */
+		    }
+
+		    if (accum) {
+			kms = k - incol;
+/* Computing MAX */
+			i__5 = 1, i__7 = *ktop - incol;
+			i__4 = kdu;
+			for (j = max(i__5,i__7); j <= i__4; ++j) {
+			    refsum = v[m22 * v_dim1 + 1] * (u[j + (kms + 1) * 
+				    u_dim1] + v[m22 * v_dim1 + 2] * u[j + (
+				    kms + 2) * u_dim1]);
+			    u[j + (kms + 1) * u_dim1] -= refsum;
+			    u[j + (kms + 2) * u_dim1] -= refsum * v[m22 * 
+				    v_dim1 + 2];
+/* L110: */
+			}
+		    } else if (*wantz) {
+			i__4 = *ihiz;
+			for (j = *iloz; j <= i__4; ++j) {
+			    refsum = v[m22 * v_dim1 + 1] * (z__[j + (k + 1) * 
+				    z_dim1] + v[m22 * v_dim1 + 2] * z__[j + (
+				    k + 2) * z_dim1]);
+			    z__[j + (k + 1) * z_dim1] -= refsum;
+			    z__[j + (k + 2) * z_dim1] -= refsum * v[m22 * 
+				    v_dim1 + 2];
+/* L120: */
+			}
+		    }
+		}
+	    }
+
+/*           ==== Vigilant deflation check ==== */
+
+	    mstart = mtop;
+	    if (krcol + (mstart - 1) * 3 < *ktop) {
+		++mstart;
+	    }
+	    mend = mbot;
+	    if (bmp22) {
+		++mend;
+	    }
+	    if (krcol == *kbot - 2) {
+		++mend;
+	    }
+	    i__4 = mend;
+	    for (m = mstart; m <= i__4; ++m) {
+/* Computing MIN */
+		i__5 = *kbot - 1, i__7 = krcol + (m - 1) * 3;
+		k = min(i__5,i__7);
+
+/*              ==== The following convergence test requires that   
+                .    the tradition small-compared-to-nearby-diagonals   
+                .    criterion and the Ahues & Tisseur (LAWN 122, 1997)   
+                .    criteria both be satisfied.  The latter improves   
+                .    accuracy in some examples. Falling back on an   
+                .    alternate convergence criterion when TST1 or TST2   
+                .    is zero (as done here) is traditional but probably   
+                .    unnecessary. ==== */
+
+		if (h__[k + 1 + k * h_dim1] != 0.) {
+		    tst1 = (d__1 = h__[k + k * h_dim1], abs(d__1)) + (d__2 = 
+			    h__[k + 1 + (k + 1) * h_dim1], abs(d__2));
+		    if (tst1 == 0.) {
+			if (k >= *ktop + 1) {
+			    tst1 += (d__1 = h__[k + (k - 1) * h_dim1], abs(
+				    d__1));
+			}
+			if (k >= *ktop + 2) {
+			    tst1 += (d__1 = h__[k + (k - 2) * h_dim1], abs(
+				    d__1));
+			}
+			if (k >= *ktop + 3) {
+			    tst1 += (d__1 = h__[k + (k - 3) * h_dim1], abs(
+				    d__1));
+			}
+			if (k <= *kbot - 2) {
+			    tst1 += (d__1 = h__[k + 2 + (k + 1) * h_dim1], 
+				    abs(d__1));
+			}
+			if (k <= *kbot - 3) {
+			    tst1 += (d__1 = h__[k + 3 + (k + 1) * h_dim1], 
+				    abs(d__1));
+			}
+			if (k <= *kbot - 4) {
+			    tst1 += (d__1 = h__[k + 4 + (k + 1) * h_dim1], 
+				    abs(d__1));
+			}
+		    }
+/* Computing MAX */
+		    d__2 = smlnum, d__3 = ulp * tst1;
+		    if ((d__1 = h__[k + 1 + k * h_dim1], abs(d__1)) <= max(
+			    d__2,d__3)) {
+/* Computing MAX */
+			d__3 = (d__1 = h__[k + 1 + k * h_dim1], abs(d__1)), 
+				d__4 = (d__2 = h__[k + (k + 1) * h_dim1], abs(
+				d__2));
+			h12 = max(d__3,d__4);
+/* Computing MIN */
+			d__3 = (d__1 = h__[k + 1 + k * h_dim1], abs(d__1)), 
+				d__4 = (d__2 = h__[k + (k + 1) * h_dim1], abs(
+				d__2));
+			h21 = min(d__3,d__4);
+/* Computing MAX */
+			d__3 = (d__1 = h__[k + 1 + (k + 1) * h_dim1], abs(
+				d__1)), d__4 = (d__2 = h__[k + k * h_dim1] - 
+				h__[k + 1 + (k + 1) * h_dim1], abs(d__2));
+			h11 = max(d__3,d__4);
+/* Computing MIN */
+			d__3 = (d__1 = h__[k + 1 + (k + 1) * h_dim1], abs(
+				d__1)), d__4 = (d__2 = h__[k + k * h_dim1] - 
+				h__[k + 1 + (k + 1) * h_dim1], abs(d__2));
+			h22 = min(d__3,d__4);
+			scl = h11 + h12;
+			tst2 = h22 * (h11 / scl);
+
+/* Computing MAX */
+			d__1 = smlnum, d__2 = ulp * tst2;
+			if (tst2 == 0. || h21 * (h12 / scl) <= max(d__1,d__2))
+				 {
+			    h__[k + 1 + k * h_dim1] = 0.;
+			}
+		    }
+		}
+/* L130: */
+	    }
+
+/*           ==== Fill in the last row of each bulge. ====   
+
+   Computing MIN */
+	    i__4 = nbmps, i__5 = (*kbot - krcol - 1) / 3;
+	    mend = min(i__4,i__5);
+	    i__4 = mend;
+	    for (m = mtop; m <= i__4; ++m) {
+		k = krcol + (m - 1) * 3;
+		refsum = v[m * v_dim1 + 1] * v[m * v_dim1 + 3] * h__[k + 4 + (
+			k + 3) * h_dim1];
+		h__[k + 4 + (k + 1) * h_dim1] = -refsum;
+		h__[k + 4 + (k + 2) * h_dim1] = -refsum * v[m * v_dim1 + 2];
+		h__[k + 4 + (k + 3) * h_dim1] -= refsum * v[m * v_dim1 + 3];
+/* L140: */
+	    }
+
+/*           ==== End of near-the-diagonal bulge chase. ====   
+
+   L150: */
+	}
+
+/*        ==== Use U (if accumulated) to update far-from-diagonal   
+          .    entries in H.  If required, use U to update Z as   
+          .    well. ==== */
+
+	if (accum) {
+	    if (*wantt) {
+		jtop = 1;
+		jbot = *n;
+	    } else {
+		jtop = *ktop;
+		jbot = *kbot;
+	    }
+	    if (! blk22 || incol < *ktop || ndcol > *kbot || ns <= 2) {
+
+/*              ==== Updates not exploiting the 2-by-2 block   
+                .    structure of U.  K1 and NU keep track of   
+                .    the location and size of U in the special   
+                .    cases of introducing bulges and chasing   
+                .    bulges off the bottom.  In these special   
+                .    cases and in case the number of shifts   
+                .    is NS = 2, there is no 2-by-2 block   
+                .    structure to exploit.  ====   
+
+   Computing MAX */
+		i__3 = 1, i__4 = *ktop - incol;
+		k1 = max(i__3,i__4);
+/* Computing MAX */
+		i__3 = 0, i__4 = ndcol - *kbot;
+		nu = kdu - max(i__3,i__4) - k1 + 1;
+
+/*              ==== Horizontal Multiply ==== */
+
+		i__3 = jbot;
+		i__4 = *nh;
+		for (jcol = min(ndcol,*kbot) + 1; i__4 < 0 ? jcol >= i__3 : 
+			jcol <= i__3; jcol += i__4) {
+/* Computing MIN */
+		    i__5 = *nh, i__7 = jbot - jcol + 1;
+		    jlen = min(i__5,i__7);
+		    igraphdgemm_("C", "N", &nu, &jlen, &nu, &c_b8, &u[k1 + k1 * 
+			    u_dim1], ldu, &h__[incol + k1 + jcol * h_dim1], 
+			    ldh, &c_b7, &wh[wh_offset], ldwh);
+		    igraphdlacpy_("ALL", &nu, &jlen, &wh[wh_offset], ldwh, &h__[
+			    incol + k1 + jcol * h_dim1], ldh);
+/* L160: */
+		}
+
+/*              ==== Vertical multiply ==== */
+
+		i__4 = max(*ktop,incol) - 1;
+		i__3 = *nv;
+		for (jrow = jtop; i__3 < 0 ? jrow >= i__4 : jrow <= i__4; 
+			jrow += i__3) {
+/* Computing MIN */
+		    i__5 = *nv, i__7 = max(*ktop,incol) - jrow;
+		    jlen = min(i__5,i__7);
+		    igraphdgemm_("N", "N", &jlen, &nu, &nu, &c_b8, &h__[jrow + (
+			    incol + k1) * h_dim1], ldh, &u[k1 + k1 * u_dim1], 
+			    ldu, &c_b7, &wv[wv_offset], ldwv);
+		    igraphdlacpy_("ALL", &jlen, &nu, &wv[wv_offset], ldwv, &h__[
+			    jrow + (incol + k1) * h_dim1], ldh);
+/* L170: */
+		}
+
+/*              ==== Z multiply (also vertical) ==== */
+
+		if (*wantz) {
+		    i__3 = *ihiz;
+		    i__4 = *nv;
+		    for (jrow = *iloz; i__4 < 0 ? jrow >= i__3 : jrow <= i__3;
+			     jrow += i__4) {
+/* Computing MIN */
+			i__5 = *nv, i__7 = *ihiz - jrow + 1;
+			jlen = min(i__5,i__7);
+			igraphdgemm_("N", "N", &jlen, &nu, &nu, &c_b8, &z__[jrow + (
+				incol + k1) * z_dim1], ldz, &u[k1 + k1 * 
+				u_dim1], ldu, &c_b7, &wv[wv_offset], ldwv);
+			igraphdlacpy_("ALL", &jlen, &nu, &wv[wv_offset], ldwv, &z__[
+				jrow + (incol + k1) * z_dim1], ldz)
+				;
+/* L180: */
+		    }
+		}
+	    } else {
+
+/*              ==== Updates exploiting U's 2-by-2 block structure.   
+                .    (I2, I4, J2, J4 are the last rows and columns   
+                .    of the blocks.) ==== */
+
+		i2 = (kdu + 1) / 2;
+		i4 = kdu;
+		j2 = i4 - i2;
+		j4 = kdu;
+
+/*              ==== KZS and KNZ deal with the band of zeros   
+                .    along the diagonal of one of the triangular   
+                .    blocks. ==== */
+
+		kzs = j4 - j2 - (ns + 1);
+		knz = ns + 1;
+
+/*              ==== Horizontal multiply ==== */
+
+		i__4 = jbot;
+		i__3 = *nh;
+		for (jcol = min(ndcol,*kbot) + 1; i__3 < 0 ? jcol >= i__4 : 
+			jcol <= i__4; jcol += i__3) {
+/* Computing MIN */
+		    i__5 = *nh, i__7 = jbot - jcol + 1;
+		    jlen = min(i__5,i__7);
+
+/*                 ==== Copy bottom of H to top+KZS of scratch ====   
+                    (The first KZS rows get multiplied by zero.) ==== */
+
+		    igraphdlacpy_("ALL", &knz, &jlen, &h__[incol + 1 + j2 + jcol * 
+			    h_dim1], ldh, &wh[kzs + 1 + wh_dim1], ldwh);
+
+/*                 ==== Multiply by U21**T ==== */
+
+		    igraphdlaset_("ALL", &kzs, &jlen, &c_b7, &c_b7, &wh[wh_offset], 
+			    ldwh);
+		    igraphdtrmm_("L", "U", "C", "N", &knz, &jlen, &c_b8, &u[j2 + 1 
+			    + (kzs + 1) * u_dim1], ldu, &wh[kzs + 1 + wh_dim1]
+			    , ldwh);
+
+/*                 ==== Multiply top of H by U11**T ==== */
+
+		    igraphdgemm_("C", "N", &i2, &jlen, &j2, &c_b8, &u[u_offset], 
+			    ldu, &h__[incol + 1 + jcol * h_dim1], ldh, &c_b8, 
+			    &wh[wh_offset], ldwh);
+
+/*                 ==== Copy top of H to bottom of WH ==== */
+
+		    igraphdlacpy_("ALL", &j2, &jlen, &h__[incol + 1 + jcol * h_dim1]
+			    , ldh, &wh[i2 + 1 + wh_dim1], ldwh);
+
+/*                 ==== Multiply by U21**T ==== */
+
+		    igraphdtrmm_("L", "L", "C", "N", &j2, &jlen, &c_b8, &u[(i2 + 1) 
+			    * u_dim1 + 1], ldu, &wh[i2 + 1 + wh_dim1], ldwh);
+
+/*                 ==== Multiply by U22 ==== */
+
+		    i__5 = i4 - i2;
+		    i__7 = j4 - j2;
+		    igraphdgemm_("C", "N", &i__5, &jlen, &i__7, &c_b8, &u[j2 + 1 + (
+			    i2 + 1) * u_dim1], ldu, &h__[incol + 1 + j2 + 
+			    jcol * h_dim1], ldh, &c_b8, &wh[i2 + 1 + wh_dim1],
+			     ldwh);
+
+/*                 ==== Copy it back ==== */
+
+		    igraphdlacpy_("ALL", &kdu, &jlen, &wh[wh_offset], ldwh, &h__[
+			    incol + 1 + jcol * h_dim1], ldh);
+/* L190: */
+		}
+
+/*              ==== Vertical multiply ==== */
+
+		i__3 = max(incol,*ktop) - 1;
+		i__4 = *nv;
+		for (jrow = jtop; i__4 < 0 ? jrow >= i__3 : jrow <= i__3; 
+			jrow += i__4) {
+/* Computing MIN */
+		    i__5 = *nv, i__7 = max(incol,*ktop) - jrow;
+		    jlen = min(i__5,i__7);
+
+/*                 ==== Copy right of H to scratch (the first KZS   
+                   .    columns get multiplied by zero) ==== */
+
+		    igraphdlacpy_("ALL", &jlen, &knz, &h__[jrow + (incol + 1 + j2) *
+			     h_dim1], ldh, &wv[(kzs + 1) * wv_dim1 + 1], ldwv);
+
+/*                 ==== Multiply by U21 ==== */
+
+		    igraphdlaset_("ALL", &jlen, &kzs, &c_b7, &c_b7, &wv[wv_offset], 
+			    ldwv);
+		    igraphdtrmm_("R", "U", "N", "N", &jlen, &knz, &c_b8, &u[j2 + 1 
+			    + (kzs + 1) * u_dim1], ldu, &wv[(kzs + 1) * 
+			    wv_dim1 + 1], ldwv);
+
+/*                 ==== Multiply by U11 ==== */
+
+		    igraphdgemm_("N", "N", &jlen, &i2, &j2, &c_b8, &h__[jrow + (
+			    incol + 1) * h_dim1], ldh, &u[u_offset], ldu, &
+			    c_b8, &wv[wv_offset], ldwv);
+
+/*                 ==== Copy left of H to right of scratch ==== */
+
+		    igraphdlacpy_("ALL", &jlen, &j2, &h__[jrow + (incol + 1) * 
+			    h_dim1], ldh, &wv[(i2 + 1) * wv_dim1 + 1], ldwv);
+
+/*                 ==== Multiply by U21 ==== */
+
+		    i__5 = i4 - i2;
+		    igraphdtrmm_("R", "L", "N", "N", &jlen, &i__5, &c_b8, &u[(i2 + 
+			    1) * u_dim1 + 1], ldu, &wv[(i2 + 1) * wv_dim1 + 1]
+			    , ldwv);
+
+/*                 ==== Multiply by U22 ==== */
+
+		    i__5 = i4 - i2;
+		    i__7 = j4 - j2;
+		    igraphdgemm_("N", "N", &jlen, &i__5, &i__7, &c_b8, &h__[jrow + (
+			    incol + 1 + j2) * h_dim1], ldh, &u[j2 + 1 + (i2 + 
+			    1) * u_dim1], ldu, &c_b8, &wv[(i2 + 1) * wv_dim1 
+			    + 1], ldwv);
+
+/*                 ==== Copy it back ==== */
+
+		    igraphdlacpy_("ALL", &jlen, &kdu, &wv[wv_offset], ldwv, &h__[
+			    jrow + (incol + 1) * h_dim1], ldh);
+/* L200: */
+		}
+
+/*              ==== Multiply Z (also vertical) ==== */
+
+		if (*wantz) {
+		    i__4 = *ihiz;
+		    i__3 = *nv;
+		    for (jrow = *iloz; i__3 < 0 ? jrow >= i__4 : jrow <= i__4;
+			     jrow += i__3) {
+/* Computing MIN */
+			i__5 = *nv, i__7 = *ihiz - jrow + 1;
+			jlen = min(i__5,i__7);
+
+/*                    ==== Copy right of Z to left of scratch (first   
+                      .     KZS columns get multiplied by zero) ==== */
+
+			igraphdlacpy_("ALL", &jlen, &knz, &z__[jrow + (incol + 1 + 
+				j2) * z_dim1], ldz, &wv[(kzs + 1) * wv_dim1 + 
+				1], ldwv);
+
+/*                    ==== Multiply by U12 ==== */
+
+			igraphdlaset_("ALL", &jlen, &kzs, &c_b7, &c_b7, &wv[
+				wv_offset], ldwv);
+			igraphdtrmm_("R", "U", "N", "N", &jlen, &knz, &c_b8, &u[j2 
+				+ 1 + (kzs + 1) * u_dim1], ldu, &wv[(kzs + 1) 
+				* wv_dim1 + 1], ldwv);
+
+/*                    ==== Multiply by U11 ==== */
+
+			igraphdgemm_("N", "N", &jlen, &i2, &j2, &c_b8, &z__[jrow + (
+				incol + 1) * z_dim1], ldz, &u[u_offset], ldu, 
+				&c_b8, &wv[wv_offset], ldwv);
+
+/*                    ==== Copy left of Z to right of scratch ==== */
+
+			igraphdlacpy_("ALL", &jlen, &j2, &z__[jrow + (incol + 1) * 
+				z_dim1], ldz, &wv[(i2 + 1) * wv_dim1 + 1], 
+				ldwv);
+
+/*                    ==== Multiply by U21 ==== */
+
+			i__5 = i4 - i2;
+			igraphdtrmm_("R", "L", "N", "N", &jlen, &i__5, &c_b8, &u[(
+				i2 + 1) * u_dim1 + 1], ldu, &wv[(i2 + 1) * 
+				wv_dim1 + 1], ldwv);
+
+/*                    ==== Multiply by U22 ==== */
+
+			i__5 = i4 - i2;
+			i__7 = j4 - j2;
+			igraphdgemm_("N", "N", &jlen, &i__5, &i__7, &c_b8, &z__[
+				jrow + (incol + 1 + j2) * z_dim1], ldz, &u[j2 
+				+ 1 + (i2 + 1) * u_dim1], ldu, &c_b8, &wv[(i2 
+				+ 1) * wv_dim1 + 1], ldwv);
+
+/*                    ==== Copy the result back to Z ==== */
+
+			igraphdlacpy_("ALL", &jlen, &kdu, &wv[wv_offset], ldwv, &
+				z__[jrow + (incol + 1) * z_dim1], ldz);
+/* L210: */
+		    }
+		}
+	    }
+	}
+/* L220: */
+    }
+
+/*     ==== End of DLAQR5 ==== */
+
+    return 0;
+} /* igraphdlaqr5_ */
+
diff --git a/igraph/src/dlaqrb.c b/igraph/src/dlaqrb.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaqrb.c
@@ -0,0 +1,602 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dlaqrb   
+
+   \Description:   
+    Compute the eigenvalues and the Schur decomposition of an upper   
+    Hessenberg submatrix in rows and columns ILO to IHI.  Only the   
+    last component of the Schur vectors are computed.   
+
+    This is mostly a modification of the LAPACK routine dlahqr.   
+
+   \Usage:   
+    call dlaqrb   
+       ( WANTT, N, ILO, IHI, H, LDH, WR, WI,  Z, INFO )   
+
+   \Arguments   
+    WANTT   Logical variable.  (INPUT)   
+            = .TRUE. : the full Schur form T is required;   
+            = .FALSE.: only eigenvalues are required.   
+
+    N       Integer.  (INPUT)   
+            The order of the matrix H.  N >= 0.   
+
+    ILO     Integer.  (INPUT)   
+    IHI     Integer.  (INPUT)   
+            It is assumed that H is already upper quasi-triangular in   
+            rows and columns IHI+1:N, and that H(ILO,ILO-1) = 0 (unless   
+            ILO = 1). SLAQRB works primarily with the Hessenberg   
+            submatrix in rows and columns ILO to IHI, but applies   
+            transformations to all of H if WANTT is .TRUE..   
+            1 <= ILO <= max(1,IHI); IHI <= N.   
+
+    H       Double precision array, dimension (LDH,N).  (INPUT/OUTPUT)   
+            On entry, the upper Hessenberg matrix H.   
+            On exit, if WANTT is .TRUE., H is upper quasi-triangular in   
+            rows and columns ILO:IHI, with any 2-by-2 diagonal blocks in   
+            standard form. If WANTT is .FALSE., the contents of H are   
+            unspecified on exit.   
+
+    LDH     Integer.  (INPUT)   
+            The leading dimension of the array H. LDH >= max(1,N).   
+
+    WR      Double precision array, dimension (N).  (OUTPUT)   
+    WI      Double precision array, dimension (N).  (OUTPUT)   
+            The real and imaginary parts, respectively, of the computed   
+            eigenvalues ILO to IHI are stored in the corresponding   
+            elements of WR and WI. If two eigenvalues are computed as a   
+            complex conjugate pair, they are stored in consecutive   
+            elements of WR and WI, say the i-th and (i+1)th, with   
+            WI(i) > 0 and WI(i+1) < 0. If WANTT is .TRUE., the   
+            eigenvalues are stored in the same order as on the diagonal   
+            of the Schur form returned in H, with WR(i) = H(i,i), and, if   
+            H(i:i+1,i:i+1) is a 2-by-2 diagonal block,   
+            WI(i) = sqrt(H(i+1,i)*H(i,i+1)) and WI(i+1) = -WI(i).   
+
+    Z       Double precision array, dimension (N).  (OUTPUT)   
+            On exit Z contains the last components of the Schur vectors.   
+
+    INFO    Integer.  (OUPUT)   
+            = 0: successful exit   
+            > 0: SLAQRB failed to compute all the eigenvalues ILO to IHI   
+                 in a total of 30*(IHI-ILO+1) iterations; if INFO = i,   
+                 elements i+1:ihi of WR and WI contain those eigenvalues   
+                 which have been successfully computed.   
+
+   \Remarks   
+    1. None.   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \Routines called:   
+       dlabad  LAPACK routine that computes machine constants.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlanhs  LAPACK routine that computes various norms of a matrix.   
+       dlanv2  LAPACK routine that computes the Schur factorization of   
+               2 by 2 nonsymmetric matrix in standard form.   
+       dlarfg  LAPACK Householder reflection construction routine.   
+       dcopy   Level 1 BLAS that copies one vector to another.   
+       drot    Level 1 BLAS that applies a rotation to a 2 by 2 matrix.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/92: Version ' 2.4'   
+                 Modified from the LAPACK routine dlahqr so that only the   
+                 last component of the Schur vectors are computed.   
+
+   \SCCS Information: @(#)   
+   FILE: laqrb.F   SID: 2.2   DATE OF SID: 8/27/96   RELEASE: 2   
+
+   \Remarks   
+       1. None   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdlaqrb_(logical *wantt, integer *n, integer *ilo, 
+	integer *ihi, doublereal *h__, integer *ldh, doublereal *wr, 
+	doublereal *wi, doublereal *z__, integer *info)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, i__1, i__2, i__3, i__4;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    integer i__, j, k, l, m;
+    doublereal s, v[3];
+    integer i1, i2;
+    doublereal t1, t2, t3, v1, v2, v3, h00, h10, h11, h12, h21, h22, h33, h44;
+    integer nh;
+    doublereal cs;
+    integer nr;
+    doublereal sn, h33s, h44s;
+    integer itn, its;
+    doublereal ulp, sum, tst1, h43h34, unfl, ovfl;
+    extern /* Subroutine */ int igraphdrot_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *);
+    doublereal work[1];
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdlanv2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *), igraphdlabad_(
+	    doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlarfg_(integer *, doublereal *, doublereal *,
+	     integer *, doublereal *);
+    extern doublereal igraphdlanhs_(char *, integer *, doublereal *, integer *, 
+	    doublereal *);
+    doublereal smlnum;
+
+
+/*     %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %------------------------%   
+       | Local Scalars & Arrays |   
+       %------------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       Parameter adjustments */
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    --wr;
+    --wi;
+    --z__;
+
+    /* Function Body */
+    *info = 0;
+
+/*     %--------------------------%   
+       | Quick return if possible |   
+       %--------------------------% */
+
+    if (*n == 0) {
+	return 0;
+    }
+    if (*ilo == *ihi) {
+	wr[*ilo] = h__[*ilo + *ilo * h_dim1];
+	wi[*ilo] = 0.;
+	return 0;
+    }
+
+/*     %---------------------------------------------%   
+       | Initialize the vector of last components of |   
+       | the Schur vectors for accumulation.         |   
+       %---------------------------------------------% */
+
+    i__1 = *n - 1;
+    for (j = 1; j <= i__1; ++j) {
+	z__[j] = 0.;
+/* L5: */
+    }
+    z__[*n] = 1.;
+
+    nh = *ihi - *ilo + 1;
+
+/*     %-------------------------------------------------------------%   
+       | Set machine-dependent constants for the stopping criterion. |   
+       | If norm(H) <= sqrt(OVFL), overflow should not occur.        |   
+       %-------------------------------------------------------------% */
+
+    unfl = igraphdlamch_("safe minimum");
+    ovfl = 1. / unfl;
+    igraphdlabad_(&unfl, &ovfl);
+    ulp = igraphdlamch_("precision");
+    smlnum = unfl * (nh / ulp);
+
+/*     %---------------------------------------------------------------%   
+       | I1 and I2 are the indices of the first row and last column    |   
+       | of H to which transformations must be applied. If eigenvalues |   
+       | only are computed, I1 and I2 are set inside the main loop.    |   
+       | Zero out H(J+2,J) = ZERO for J=1:N if WANTT = .TRUE.          |   
+       | else H(J+2,J) for J=ILO:IHI-ILO-1 if WANTT = .FALSE.          |   
+       %---------------------------------------------------------------% */
+
+    if (*wantt) {
+	i1 = 1;
+	i2 = *n;
+	i__1 = i2 - 2;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    h__[i1 + i__ + 1 + i__ * h_dim1] = 0.;
+/* L8: */
+	}
+    } else {
+	i__1 = *ihi - *ilo - 1;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    h__[*ilo + i__ + 1 + (*ilo + i__ - 1) * h_dim1] = 0.;
+/* L9: */
+	}
+    }
+
+/*     %---------------------------------------------------%   
+       | ITN is the total number of QR iterations allowed. |   
+       %---------------------------------------------------% */
+
+    itn = nh * 30;
+
+/*     ------------------------------------------------------------------   
+       The main loop begins here. I is the loop index and decreases from   
+       IHI to ILO in steps of 1 or 2. Each iteration of the loop works   
+       with the active submatrix in rows and columns L to I.   
+       Eigenvalues I+1 to IHI have already converged. Either L = ILO or   
+       H(L,L-1) is negligible so that the matrix splits.   
+       ------------------------------------------------------------------ */
+
+    i__ = *ihi;
+L10:
+    l = *ilo;
+    if (i__ < *ilo) {
+	goto L150;
+    }
+/*     %--------------------------------------------------------------%   
+       | Perform QR iterations on rows and columns ILO to I until a   |   
+       | submatrix of order 1 or 2 splits off at the bottom because a |   
+       | subdiagonal element has become negligible.                   |   
+       %--------------------------------------------------------------% */
+    i__1 = itn;
+    for (its = 0; its <= i__1; ++its) {
+
+/*        %----------------------------------------------%   
+          | Look for a single small subdiagonal element. |   
+          %----------------------------------------------% */
+
+	i__2 = l + 1;
+	for (k = i__; k >= i__2; --k) {
+	    tst1 = (d__1 = h__[k - 1 + (k - 1) * h_dim1], abs(d__1)) + (d__2 =
+		     h__[k + k * h_dim1], abs(d__2));
+	    if (tst1 == 0.) {
+		i__3 = i__ - l + 1;
+		tst1 = igraphdlanhs_("1", &i__3, &h__[l + l * h_dim1], ldh, work);
+	    }
+/* Computing MAX */
+	    d__2 = ulp * tst1;
+	    if ((d__1 = h__[k + (k - 1) * h_dim1], abs(d__1)) <= max(d__2,
+		    smlnum)) {
+		goto L30;
+	    }
+/* L20: */
+	}
+L30:
+	l = k;
+	if (l > *ilo) {
+
+/*           %------------------------%   
+             | H(L,L-1) is negligible |   
+             %------------------------% */
+
+	    h__[l + (l - 1) * h_dim1] = 0.;
+	}
+
+/*        %-------------------------------------------------------------%   
+          | Exit from loop if a submatrix of order 1 or 2 has split off |   
+          %-------------------------------------------------------------% */
+
+	if (l >= i__ - 1) {
+	    goto L140;
+	}
+
+/*        %---------------------------------------------------------%   
+          | Now the active submatrix is in rows and columns L to I. |   
+          | If eigenvalues only are being computed, only the active |   
+          | submatrix need be transformed.                          |   
+          %---------------------------------------------------------% */
+
+	if (! (*wantt)) {
+	    i1 = l;
+	    i2 = i__;
+	}
+
+	if (its == 10 || its == 20) {
+
+/*           %-------------------%   
+             | Exceptional shift |   
+             %-------------------% */
+
+	    s = (d__1 = h__[i__ + (i__ - 1) * h_dim1], abs(d__1)) + (d__2 = 
+		    h__[i__ - 1 + (i__ - 2) * h_dim1], abs(d__2));
+	    h44 = s * .75;
+	    h33 = h44;
+	    h43h34 = s * -.4375 * s;
+
+	} else {
+
+/*           %-----------------------------------------%   
+             | Prepare to use Wilkinson's double shift |   
+             %-----------------------------------------% */
+
+	    h44 = h__[i__ + i__ * h_dim1];
+	    h33 = h__[i__ - 1 + (i__ - 1) * h_dim1];
+	    h43h34 = h__[i__ + (i__ - 1) * h_dim1] * h__[i__ - 1 + i__ * 
+		    h_dim1];
+	}
+
+/*        %-----------------------------------------------------%   
+          | Look for two consecutive small subdiagonal elements |   
+          %-----------------------------------------------------% */
+
+	i__2 = l;
+	for (m = i__ - 2; m >= i__2; --m) {
+
+/*           %---------------------------------------------------------%   
+             | Determine the effect of starting the double-shift QR    |   
+             | iteration at row M, and see if this would make H(M,M-1) |   
+             | negligible.                                             |   
+             %---------------------------------------------------------% */
+
+	    h11 = h__[m + m * h_dim1];
+	    h22 = h__[m + 1 + (m + 1) * h_dim1];
+	    h21 = h__[m + 1 + m * h_dim1];
+	    h12 = h__[m + (m + 1) * h_dim1];
+	    h44s = h44 - h11;
+	    h33s = h33 - h11;
+	    v1 = (h33s * h44s - h43h34) / h21 + h12;
+	    v2 = h22 - h11 - h33s - h44s;
+	    v3 = h__[m + 2 + (m + 1) * h_dim1];
+	    s = abs(v1) + abs(v2) + abs(v3);
+	    v1 /= s;
+	    v2 /= s;
+	    v3 /= s;
+	    v[0] = v1;
+	    v[1] = v2;
+	    v[2] = v3;
+	    if (m == l) {
+		goto L50;
+	    }
+	    h00 = h__[m - 1 + (m - 1) * h_dim1];
+	    h10 = h__[m + (m - 1) * h_dim1];
+	    tst1 = abs(v1) * (abs(h00) + abs(h11) + abs(h22));
+	    if (abs(h10) * (abs(v2) + abs(v3)) <= ulp * tst1) {
+		goto L50;
+	    }
+/* L40: */
+	}
+L50:
+
+/*        %----------------------%   
+          | Double-shift QR step |   
+          %----------------------% */
+
+	i__2 = i__ - 1;
+	for (k = m; k <= i__2; ++k) {
+
+/*           ------------------------------------------------------------   
+             The first iteration of this loop determines a reflection G   
+             from the vector V and applies it from left and right to H,   
+             thus creating a nonzero bulge below the subdiagonal.   
+
+             Each subsequent iteration determines a reflection G to   
+             restore the Hessenberg form in the (K-1)th column, and thus   
+             chases the bulge one step toward the bottom of the active   
+             submatrix. NR is the order of G.   
+             ------------------------------------------------------------   
+
+   Computing MIN */
+	    i__3 = 3, i__4 = i__ - k + 1;
+	    nr = min(i__3,i__4);
+	    if (k > m) {
+		igraphdcopy_(&nr, &h__[k + (k - 1) * h_dim1], &c__1, v, &c__1);
+	    }
+	    igraphdlarfg_(&nr, v, &v[1], &c__1, &t1);
+	    if (k > m) {
+		h__[k + (k - 1) * h_dim1] = v[0];
+		h__[k + 1 + (k - 1) * h_dim1] = 0.;
+		if (k < i__ - 1) {
+		    h__[k + 2 + (k - 1) * h_dim1] = 0.;
+		}
+	    } else if (m > l) {
+		h__[k + (k - 1) * h_dim1] = -h__[k + (k - 1) * h_dim1];
+	    }
+	    v2 = v[1];
+	    t2 = t1 * v2;
+	    if (nr == 3) {
+		v3 = v[2];
+		t3 = t1 * v3;
+
+/*              %------------------------------------------------%   
+                | Apply G from the left to transform the rows of |   
+                | the matrix in columns K to I2.                 |   
+                %------------------------------------------------% */
+
+		i__3 = i2;
+		for (j = k; j <= i__3; ++j) {
+		    sum = h__[k + j * h_dim1] + v2 * h__[k + 1 + j * h_dim1] 
+			    + v3 * h__[k + 2 + j * h_dim1];
+		    h__[k + j * h_dim1] -= sum * t1;
+		    h__[k + 1 + j * h_dim1] -= sum * t2;
+		    h__[k + 2 + j * h_dim1] -= sum * t3;
+/* L60: */
+		}
+
+/*              %----------------------------------------------------%   
+                | Apply G from the right to transform the columns of |   
+                | the matrix in rows I1 to min(K+3,I).               |   
+                %----------------------------------------------------%   
+
+   Computing MIN */
+		i__4 = k + 3;
+		i__3 = min(i__4,i__);
+		for (j = i1; j <= i__3; ++j) {
+		    sum = h__[j + k * h_dim1] + v2 * h__[j + (k + 1) * h_dim1]
+			     + v3 * h__[j + (k + 2) * h_dim1];
+		    h__[j + k * h_dim1] -= sum * t1;
+		    h__[j + (k + 1) * h_dim1] -= sum * t2;
+		    h__[j + (k + 2) * h_dim1] -= sum * t3;
+/* L70: */
+		}
+
+/*              %----------------------------------%   
+                | Accumulate transformations for Z |   
+                %----------------------------------% */
+
+		sum = z__[k] + v2 * z__[k + 1] + v3 * z__[k + 2];
+		z__[k] -= sum * t1;
+		z__[k + 1] -= sum * t2;
+		z__[k + 2] -= sum * t3;
+	    } else if (nr == 2) {
+
+/*              %------------------------------------------------%   
+                | Apply G from the left to transform the rows of |   
+                | the matrix in columns K to I2.                 |   
+                %------------------------------------------------% */
+
+		i__3 = i2;
+		for (j = k; j <= i__3; ++j) {
+		    sum = h__[k + j * h_dim1] + v2 * h__[k + 1 + j * h_dim1];
+		    h__[k + j * h_dim1] -= sum * t1;
+		    h__[k + 1 + j * h_dim1] -= sum * t2;
+/* L90: */
+		}
+
+/*              %----------------------------------------------------%   
+                | Apply G from the right to transform the columns of |   
+                | the matrix in rows I1 to min(K+3,I).               |   
+                %----------------------------------------------------% */
+
+		i__3 = i__;
+		for (j = i1; j <= i__3; ++j) {
+		    sum = h__[j + k * h_dim1] + v2 * h__[j + (k + 1) * h_dim1]
+			    ;
+		    h__[j + k * h_dim1] -= sum * t1;
+		    h__[j + (k + 1) * h_dim1] -= sum * t2;
+/* L100: */
+		}
+
+/*              %----------------------------------%   
+                | Accumulate transformations for Z |   
+                %----------------------------------% */
+
+		sum = z__[k] + v2 * z__[k + 1];
+		z__[k] -= sum * t1;
+		z__[k + 1] -= sum * t2;
+	    }
+/* L120: */
+	}
+/* L130: */
+    }
+
+/*     %-------------------------------------------------------%   
+       | Failure to converge in remaining number of iterations |   
+       %-------------------------------------------------------% */
+
+    *info = i__;
+    return 0;
+L140:
+    if (l == i__) {
+
+/*        %------------------------------------------------------%   
+          | H(I,I-1) is negligible: one eigenvalue has converged |   
+          %------------------------------------------------------% */
+
+	wr[i__] = h__[i__ + i__ * h_dim1];
+	wi[i__] = 0.;
+    } else if (l == i__ - 1) {
+
+/*        %--------------------------------------------------------%   
+          | H(I-1,I-2) is negligible;                              |   
+          | a pair of eigenvalues have converged.                  |   
+          |                                                        |   
+          | Transform the 2-by-2 submatrix to standard Schur form, |   
+          | and compute and store the eigenvalues.                 |   
+          %--------------------------------------------------------% */
+
+	igraphdlanv2_(&h__[i__ - 1 + (i__ - 1) * h_dim1], &h__[i__ - 1 + i__ * 
+		h_dim1], &h__[i__ + (i__ - 1) * h_dim1], &h__[i__ + i__ * 
+		h_dim1], &wr[i__ - 1], &wi[i__ - 1], &wr[i__], &wi[i__], &cs, 
+		&sn);
+	if (*wantt) {
+
+/*           %-----------------------------------------------------%   
+             | Apply the transformation to the rest of H and to Z, |   
+             | as required.                                        |   
+             %-----------------------------------------------------% */
+
+	    if (i2 > i__) {
+		i__1 = i2 - i__;
+		igraphdrot_(&i__1, &h__[i__ - 1 + (i__ + 1) * h_dim1], ldh, &h__[
+			i__ + (i__ + 1) * h_dim1], ldh, &cs, &sn);
+	    }
+	    i__1 = i__ - i1 - 1;
+	    igraphdrot_(&i__1, &h__[i1 + (i__ - 1) * h_dim1], &c__1, &h__[i1 + i__ *
+		     h_dim1], &c__1, &cs, &sn);
+	    sum = cs * z__[i__ - 1] + sn * z__[i__];
+	    z__[i__] = cs * z__[i__] - sn * z__[i__ - 1];
+	    z__[i__ - 1] = sum;
+	}
+    }
+
+/*     %---------------------------------------------------------%   
+       | Decrement number of remaining iterations, and return to |   
+       | start of the main loop with new value of I.             |   
+       %---------------------------------------------------------% */
+
+    itn -= its;
+    i__ = l - 1;
+    goto L10;
+L150:
+    return 0;
+
+/*     %---------------%   
+       | End of dlaqrb |   
+       %---------------% */
+
+} /* igraphdlaqrb_ */
+
diff --git a/igraph/src/dlaqtr.c b/igraph/src/dlaqtr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaqtr.c
@@ -0,0 +1,898 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static logical c_false = FALSE_;
+static integer c__2 = 2;
+static doublereal c_b21 = 1.;
+static doublereal c_b25 = 0.;
+static logical c_true = TRUE_;
+
+/* > \brief \b DLAQTR solves a real quasi-triangular system of equations, or a complex quasi-triangular system
+ of special form, in real arithmetic.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAQTR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaqtr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaqtr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaqtr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAQTR( LTRAN, LREAL, N, T, LDT, B, W, SCALE, X, WORK,   
+                            INFO )   
+
+         LOGICAL            LREAL, LTRAN   
+         INTEGER            INFO, LDT, N   
+         DOUBLE PRECISION   SCALE, W   
+         DOUBLE PRECISION   B( * ), T( LDT, * ), WORK( * ), X( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAQTR solves the real quasi-triangular system   
+   >   
+   >              op(T)*p = scale*c,               if LREAL = .TRUE.   
+   >   
+   > or the complex quasi-triangular systems   
+   >   
+   >            op(T + iB)*(p+iq) = scale*(c+id),  if LREAL = .FALSE.   
+   >   
+   > in real arithmetic, where T is upper quasi-triangular.   
+   > If LREAL = .FALSE., then the first diagonal block of T must be   
+   > 1 by 1, B is the specially structured matrix   
+   >   
+   >                B = [ b(1) b(2) ... b(n) ]   
+   >                    [       w            ]   
+   >                    [           w        ]   
+   >                    [              .     ]   
+   >                    [                 w  ]   
+   >   
+   > op(A) = A or A**T, A**T denotes the transpose of   
+   > matrix A.   
+   >   
+   > On input, X = [ c ].  On output, X = [ p ].   
+   >               [ d ]                  [ q ]   
+   >   
+   > This subroutine is designed for the condition number estimation   
+   > in routine DTRSNA.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] LTRAN   
+   > \verbatim   
+   >          LTRAN is LOGICAL   
+   >          On entry, LTRAN specifies the option of conjugate transpose:   
+   >             = .FALSE.,    op(T+i*B) = T+i*B,   
+   >             = .TRUE.,     op(T+i*B) = (T+i*B)**T.   
+   > \endverbatim   
+   >   
+   > \param[in] LREAL   
+   > \verbatim   
+   >          LREAL is LOGICAL   
+   >          On entry, LREAL specifies the input matrix structure:   
+   >             = .FALSE.,    the input is complex   
+   >             = .TRUE.,     the input is real   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          On entry, N specifies the order of T+i*B. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,N)   
+   >          On entry, T contains a matrix in Schur canonical form.   
+   >          If LREAL = .FALSE., then the first diagonal block of T mu   
+   >          be 1 by 1.   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is INTEGER   
+   >          The leading dimension of the matrix T. LDT >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, B contains the elements to form the matrix   
+   >          B as described above.   
+   >          If LREAL = .TRUE., B is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION   
+   >          On entry, W is the diagonal element of the matrix B.   
+   >          If LREAL = .TRUE., W is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[out] SCALE   
+   > \verbatim   
+   >          SCALE is DOUBLE PRECISION   
+   >          On exit, SCALE is the scale factor.   
+   > \endverbatim   
+   >   
+   > \param[in,out] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION array, dimension (2*N)   
+   >          On entry, X contains the right hand side of the system.   
+   >          On exit, X is overwritten by the solution.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          On exit, INFO is set to   
+   >             0: successful exit.   
+   >               1: the some diagonal 1 by 1 block has been perturbed by   
+   >                  a small number SMIN to keep nonsingularity.   
+   >               2: the some diagonal 2 by 2 block has been perturbed by   
+   >                  a small number in DLALN2 to keep nonsingularity.   
+   >          NOTE: In the interests of speed, this routine does not   
+   >                check the inputs for errors.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlaqtr_(logical *ltran, logical *lreal, integer *n, 
+	doublereal *t, integer *ldt, doublereal *b, doublereal *w, doublereal 
+	*scale, doublereal *x, doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer t_dim1, t_offset, i__1, i__2;
+    doublereal d__1, d__2, d__3, d__4, d__5, d__6;
+
+    /* Local variables */
+    doublereal d__[4]	/* was [2][2] */;
+    integer i__, j, k;
+    doublereal v[4]	/* was [2][2] */, z__;
+    integer j1, j2, n1, n2;
+    doublereal si, xj, sr, rec, eps, tjj, tmp;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    integer ierr;
+    doublereal smin, xmax;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    extern doublereal igraphdasum_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdaxpy_(integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    integer jnext;
+    doublereal sminw, xnorm;
+    extern /* Subroutine */ int igraphdlaln2_(logical *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *, doublereal *,
+	     doublereal *, doublereal *, integer *, doublereal *, doublereal *
+	    , doublereal *, integer *, doublereal *, doublereal *, integer *);
+    extern doublereal igraphdlamch_(char *), igraphdlange_(char *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    doublereal scaloc;
+    extern /* Subroutine */ int igraphdladiv_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *);
+    doublereal bignum;
+    logical notran;
+    doublereal smlnum;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Do not test the input parameters for errors   
+
+       Parameter adjustments */
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    --b;
+    --x;
+    --work;
+
+    /* Function Body */
+    notran = ! (*ltran);
+    *info = 0;
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+/*     Set constants to control overflow */
+
+    eps = igraphdlamch_("P");
+    smlnum = igraphdlamch_("S") / eps;
+    bignum = 1. / smlnum;
+
+    xnorm = igraphdlange_("M", n, n, &t[t_offset], ldt, d__);
+    if (! (*lreal)) {
+/* Computing MAX */
+	d__1 = xnorm, d__2 = abs(*w), d__1 = max(d__1,d__2), d__2 = igraphdlange_(
+		"M", n, &c__1, &b[1], n, d__);
+	xnorm = max(d__1,d__2);
+    }
+/* Computing MAX */
+    d__1 = smlnum, d__2 = eps * xnorm;
+    smin = max(d__1,d__2);
+
+/*     Compute 1-norm of each column of strictly upper triangular   
+       part of T to control overflow in triangular solver. */
+
+    work[1] = 0.;
+    i__1 = *n;
+    for (j = 2; j <= i__1; ++j) {
+	i__2 = j - 1;
+	work[j] = igraphdasum_(&i__2, &t[j * t_dim1 + 1], &c__1);
+/* L10: */
+    }
+
+    if (! (*lreal)) {
+	i__1 = *n;
+	for (i__ = 2; i__ <= i__1; ++i__) {
+	    work[i__] += (d__1 = b[i__], abs(d__1));
+/* L20: */
+	}
+    }
+
+    n2 = *n << 1;
+    n1 = *n;
+    if (! (*lreal)) {
+	n1 = n2;
+    }
+    k = igraphidamax_(&n1, &x[1], &c__1);
+    xmax = (d__1 = x[k], abs(d__1));
+    *scale = 1.;
+
+    if (xmax > bignum) {
+	*scale = bignum / xmax;
+	igraphdscal_(&n1, scale, &x[1], &c__1);
+	xmax = bignum;
+    }
+
+    if (*lreal) {
+
+	if (notran) {
+
+/*           Solve T*p = scale*c */
+
+	    jnext = *n;
+	    for (j = *n; j >= 1; --j) {
+		if (j > jnext) {
+		    goto L30;
+		}
+		j1 = j;
+		j2 = j;
+		jnext = j - 1;
+		if (j > 1) {
+		    if (t[j + (j - 1) * t_dim1] != 0.) {
+			j1 = j - 1;
+			jnext = j - 2;
+		    }
+		}
+
+		if (j1 == j2) {
+
+/*                 Meet 1 by 1 diagonal block   
+
+                   Scale to avoid overflow when computing   
+                       x(j) = b(j)/T(j,j) */
+
+		    xj = (d__1 = x[j1], abs(d__1));
+		    tjj = (d__1 = t[j1 + j1 * t_dim1], abs(d__1));
+		    tmp = t[j1 + j1 * t_dim1];
+		    if (tjj < smin) {
+			tmp = smin;
+			tjj = smin;
+			*info = 1;
+		    }
+
+		    if (xj == 0.) {
+			goto L30;
+		    }
+
+		    if (tjj < 1.) {
+			if (xj > bignum * tjj) {
+			    rec = 1. / xj;
+			    igraphdscal_(n, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			    xmax *= rec;
+			}
+		    }
+		    x[j1] /= tmp;
+		    xj = (d__1 = x[j1], abs(d__1));
+
+/*                 Scale x if necessary to avoid overflow when adding a   
+                   multiple of column j1 of T. */
+
+		    if (xj > 1.) {
+			rec = 1. / xj;
+			if (work[j1] > (bignum - xmax) * rec) {
+			    igraphdscal_(n, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			}
+		    }
+		    if (j1 > 1) {
+			i__1 = j1 - 1;
+			d__1 = -x[j1];
+			igraphdaxpy_(&i__1, &d__1, &t[j1 * t_dim1 + 1], &c__1, &x[1]
+				, &c__1);
+			i__1 = j1 - 1;
+			k = igraphidamax_(&i__1, &x[1], &c__1);
+			xmax = (d__1 = x[k], abs(d__1));
+		    }
+
+		} else {
+
+/*                 Meet 2 by 2 diagonal block   
+
+                   Call 2 by 2 linear system solve, to take   
+                   care of possible overflow by scaling factor. */
+
+		    d__[0] = x[j1];
+		    d__[1] = x[j2];
+		    igraphdlaln2_(&c_false, &c__2, &c__1, &smin, &c_b21, &t[j1 + j1 
+			    * t_dim1], ldt, &c_b21, &c_b21, d__, &c__2, &
+			    c_b25, &c_b25, v, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 2;
+		    }
+
+		    if (scaloc != 1.) {
+			igraphdscal_(n, &scaloc, &x[1], &c__1);
+			*scale *= scaloc;
+		    }
+		    x[j1] = v[0];
+		    x[j2] = v[1];
+
+/*                 Scale V(1,1) (= X(J1)) and/or V(2,1) (=X(J2))   
+                   to avoid overflow in updating right-hand side.   
+
+   Computing MAX */
+		    d__1 = abs(v[0]), d__2 = abs(v[1]);
+		    xj = max(d__1,d__2);
+		    if (xj > 1.) {
+			rec = 1. / xj;
+/* Computing MAX */
+			d__1 = work[j1], d__2 = work[j2];
+			if (max(d__1,d__2) > (bignum - xmax) * rec) {
+			    igraphdscal_(n, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			}
+		    }
+
+/*                 Update right-hand side */
+
+		    if (j1 > 1) {
+			i__1 = j1 - 1;
+			d__1 = -x[j1];
+			igraphdaxpy_(&i__1, &d__1, &t[j1 * t_dim1 + 1], &c__1, &x[1]
+				, &c__1);
+			i__1 = j1 - 1;
+			d__1 = -x[j2];
+			igraphdaxpy_(&i__1, &d__1, &t[j2 * t_dim1 + 1], &c__1, &x[1]
+				, &c__1);
+			i__1 = j1 - 1;
+			k = igraphidamax_(&i__1, &x[1], &c__1);
+			xmax = (d__1 = x[k], abs(d__1));
+		    }
+
+		}
+
+L30:
+		;
+	    }
+
+	} else {
+
+/*           Solve T**T*p = scale*c */
+
+	    jnext = 1;
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (j < jnext) {
+		    goto L40;
+		}
+		j1 = j;
+		j2 = j;
+		jnext = j + 1;
+		if (j < *n) {
+		    if (t[j + 1 + j * t_dim1] != 0.) {
+			j2 = j + 1;
+			jnext = j + 2;
+		    }
+		}
+
+		if (j1 == j2) {
+
+/*                 1 by 1 diagonal block   
+
+                   Scale if necessary to avoid overflow in forming the   
+                   right-hand side element by inner product. */
+
+		    xj = (d__1 = x[j1], abs(d__1));
+		    if (xmax > 1.) {
+			rec = 1. / xmax;
+			if (work[j1] > (bignum - xj) * rec) {
+			    igraphdscal_(n, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			    xmax *= rec;
+			}
+		    }
+
+		    i__2 = j1 - 1;
+		    x[j1] -= igraphddot_(&i__2, &t[j1 * t_dim1 + 1], &c__1, &x[1], &
+			    c__1);
+
+		    xj = (d__1 = x[j1], abs(d__1));
+		    tjj = (d__1 = t[j1 + j1 * t_dim1], abs(d__1));
+		    tmp = t[j1 + j1 * t_dim1];
+		    if (tjj < smin) {
+			tmp = smin;
+			tjj = smin;
+			*info = 1;
+		    }
+
+		    if (tjj < 1.) {
+			if (xj > bignum * tjj) {
+			    rec = 1. / xj;
+			    igraphdscal_(n, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			    xmax *= rec;
+			}
+		    }
+		    x[j1] /= tmp;
+/* Computing MAX */
+		    d__2 = xmax, d__3 = (d__1 = x[j1], abs(d__1));
+		    xmax = max(d__2,d__3);
+
+		} else {
+
+/*                 2 by 2 diagonal block   
+
+                   Scale if necessary to avoid overflow in forming the   
+                   right-hand side elements by inner product.   
+
+   Computing MAX */
+		    d__3 = (d__1 = x[j1], abs(d__1)), d__4 = (d__2 = x[j2], 
+			    abs(d__2));
+		    xj = max(d__3,d__4);
+		    if (xmax > 1.) {
+			rec = 1. / xmax;
+/* Computing MAX */
+			d__1 = work[j2], d__2 = work[j1];
+			if (max(d__1,d__2) > (bignum - xj) * rec) {
+			    igraphdscal_(n, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			    xmax *= rec;
+			}
+		    }
+
+		    i__2 = j1 - 1;
+		    d__[0] = x[j1] - igraphddot_(&i__2, &t[j1 * t_dim1 + 1], &c__1, 
+			    &x[1], &c__1);
+		    i__2 = j1 - 1;
+		    d__[1] = x[j2] - igraphddot_(&i__2, &t[j2 * t_dim1 + 1], &c__1, 
+			    &x[1], &c__1);
+
+		    igraphdlaln2_(&c_true, &c__2, &c__1, &smin, &c_b21, &t[j1 + j1 *
+			     t_dim1], ldt, &c_b21, &c_b21, d__, &c__2, &c_b25,
+			     &c_b25, v, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 2;
+		    }
+
+		    if (scaloc != 1.) {
+			igraphdscal_(n, &scaloc, &x[1], &c__1);
+			*scale *= scaloc;
+		    }
+		    x[j1] = v[0];
+		    x[j2] = v[1];
+/* Computing MAX */
+		    d__3 = (d__1 = x[j1], abs(d__1)), d__4 = (d__2 = x[j2], 
+			    abs(d__2)), d__3 = max(d__3,d__4);
+		    xmax = max(d__3,xmax);
+
+		}
+L40:
+		;
+	    }
+	}
+
+    } else {
+
+/* Computing MAX */
+	d__1 = eps * abs(*w);
+	sminw = max(d__1,smin);
+	if (notran) {
+
+/*           Solve (T + iB)*(p+iq) = c+id */
+
+	    jnext = *n;
+	    for (j = *n; j >= 1; --j) {
+		if (j > jnext) {
+		    goto L70;
+		}
+		j1 = j;
+		j2 = j;
+		jnext = j - 1;
+		if (j > 1) {
+		    if (t[j + (j - 1) * t_dim1] != 0.) {
+			j1 = j - 1;
+			jnext = j - 2;
+		    }
+		}
+
+		if (j1 == j2) {
+
+/*                 1 by 1 diagonal block   
+
+                   Scale if necessary to avoid overflow in division */
+
+		    z__ = *w;
+		    if (j1 == 1) {
+			z__ = b[1];
+		    }
+		    xj = (d__1 = x[j1], abs(d__1)) + (d__2 = x[*n + j1], abs(
+			    d__2));
+		    tjj = (d__1 = t[j1 + j1 * t_dim1], abs(d__1)) + abs(z__);
+		    tmp = t[j1 + j1 * t_dim1];
+		    if (tjj < sminw) {
+			tmp = sminw;
+			tjj = sminw;
+			*info = 1;
+		    }
+
+		    if (xj == 0.) {
+			goto L70;
+		    }
+
+		    if (tjj < 1.) {
+			if (xj > bignum * tjj) {
+			    rec = 1. / xj;
+			    igraphdscal_(&n2, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			    xmax *= rec;
+			}
+		    }
+		    igraphdladiv_(&x[j1], &x[*n + j1], &tmp, &z__, &sr, &si);
+		    x[j1] = sr;
+		    x[*n + j1] = si;
+		    xj = (d__1 = x[j1], abs(d__1)) + (d__2 = x[*n + j1], abs(
+			    d__2));
+
+/*                 Scale x if necessary to avoid overflow when adding a   
+                   multiple of column j1 of T. */
+
+		    if (xj > 1.) {
+			rec = 1. / xj;
+			if (work[j1] > (bignum - xmax) * rec) {
+			    igraphdscal_(&n2, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			}
+		    }
+
+		    if (j1 > 1) {
+			i__1 = j1 - 1;
+			d__1 = -x[j1];
+			igraphdaxpy_(&i__1, &d__1, &t[j1 * t_dim1 + 1], &c__1, &x[1]
+				, &c__1);
+			i__1 = j1 - 1;
+			d__1 = -x[*n + j1];
+			igraphdaxpy_(&i__1, &d__1, &t[j1 * t_dim1 + 1], &c__1, &x[*
+				n + 1], &c__1);
+
+			x[1] += b[j1] * x[*n + j1];
+			x[*n + 1] -= b[j1] * x[j1];
+
+			xmax = 0.;
+			i__1 = j1 - 1;
+			for (k = 1; k <= i__1; ++k) {
+/* Computing MAX */
+			    d__3 = xmax, d__4 = (d__1 = x[k], abs(d__1)) + (
+				    d__2 = x[k + *n], abs(d__2));
+			    xmax = max(d__3,d__4);
+/* L50: */
+			}
+		    }
+
+		} else {
+
+/*                 Meet 2 by 2 diagonal block */
+
+		    d__[0] = x[j1];
+		    d__[1] = x[j2];
+		    d__[2] = x[*n + j1];
+		    d__[3] = x[*n + j2];
+		    d__1 = -(*w);
+		    igraphdlaln2_(&c_false, &c__2, &c__2, &sminw, &c_b21, &t[j1 + 
+			    j1 * t_dim1], ldt, &c_b21, &c_b21, d__, &c__2, &
+			    c_b25, &d__1, v, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 2;
+		    }
+
+		    if (scaloc != 1.) {
+			i__1 = *n << 1;
+			igraphdscal_(&i__1, &scaloc, &x[1], &c__1);
+			*scale = scaloc * *scale;
+		    }
+		    x[j1] = v[0];
+		    x[j2] = v[1];
+		    x[*n + j1] = v[2];
+		    x[*n + j2] = v[3];
+
+/*                 Scale X(J1), .... to avoid overflow in   
+                   updating right hand side.   
+
+   Computing MAX */
+		    d__1 = abs(v[0]) + abs(v[2]), d__2 = abs(v[1]) + abs(v[3])
+			    ;
+		    xj = max(d__1,d__2);
+		    if (xj > 1.) {
+			rec = 1. / xj;
+/* Computing MAX */
+			d__1 = work[j1], d__2 = work[j2];
+			if (max(d__1,d__2) > (bignum - xmax) * rec) {
+			    igraphdscal_(&n2, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			}
+		    }
+
+/*                 Update the right-hand side. */
+
+		    if (j1 > 1) {
+			i__1 = j1 - 1;
+			d__1 = -x[j1];
+			igraphdaxpy_(&i__1, &d__1, &t[j1 * t_dim1 + 1], &c__1, &x[1]
+				, &c__1);
+			i__1 = j1 - 1;
+			d__1 = -x[j2];
+			igraphdaxpy_(&i__1, &d__1, &t[j2 * t_dim1 + 1], &c__1, &x[1]
+				, &c__1);
+
+			i__1 = j1 - 1;
+			d__1 = -x[*n + j1];
+			igraphdaxpy_(&i__1, &d__1, &t[j1 * t_dim1 + 1], &c__1, &x[*
+				n + 1], &c__1);
+			i__1 = j1 - 1;
+			d__1 = -x[*n + j2];
+			igraphdaxpy_(&i__1, &d__1, &t[j2 * t_dim1 + 1], &c__1, &x[*
+				n + 1], &c__1);
+
+			x[1] = x[1] + b[j1] * x[*n + j1] + b[j2] * x[*n + j2];
+			x[*n + 1] = x[*n + 1] - b[j1] * x[j1] - b[j2] * x[j2];
+
+			xmax = 0.;
+			i__1 = j1 - 1;
+			for (k = 1; k <= i__1; ++k) {
+/* Computing MAX */
+			    d__3 = (d__1 = x[k], abs(d__1)) + (d__2 = x[k + *
+				    n], abs(d__2));
+			    xmax = max(d__3,xmax);
+/* L60: */
+			}
+		    }
+
+		}
+L70:
+		;
+	    }
+
+	} else {
+
+/*           Solve (T + iB)**T*(p+iq) = c+id */
+
+	    jnext = 1;
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (j < jnext) {
+		    goto L80;
+		}
+		j1 = j;
+		j2 = j;
+		jnext = j + 1;
+		if (j < *n) {
+		    if (t[j + 1 + j * t_dim1] != 0.) {
+			j2 = j + 1;
+			jnext = j + 2;
+		    }
+		}
+
+		if (j1 == j2) {
+
+/*                 1 by 1 diagonal block   
+
+                   Scale if necessary to avoid overflow in forming the   
+                   right-hand side element by inner product. */
+
+		    xj = (d__1 = x[j1], abs(d__1)) + (d__2 = x[j1 + *n], abs(
+			    d__2));
+		    if (xmax > 1.) {
+			rec = 1. / xmax;
+			if (work[j1] > (bignum - xj) * rec) {
+			    igraphdscal_(&n2, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			    xmax *= rec;
+			}
+		    }
+
+		    i__2 = j1 - 1;
+		    x[j1] -= igraphddot_(&i__2, &t[j1 * t_dim1 + 1], &c__1, &x[1], &
+			    c__1);
+		    i__2 = j1 - 1;
+		    x[*n + j1] -= igraphddot_(&i__2, &t[j1 * t_dim1 + 1], &c__1, &x[
+			    *n + 1], &c__1);
+		    if (j1 > 1) {
+			x[j1] -= b[j1] * x[*n + 1];
+			x[*n + j1] += b[j1] * x[1];
+		    }
+		    xj = (d__1 = x[j1], abs(d__1)) + (d__2 = x[j1 + *n], abs(
+			    d__2));
+
+		    z__ = *w;
+		    if (j1 == 1) {
+			z__ = b[1];
+		    }
+
+/*                 Scale if necessary to avoid overflow in   
+                   complex division */
+
+		    tjj = (d__1 = t[j1 + j1 * t_dim1], abs(d__1)) + abs(z__);
+		    tmp = t[j1 + j1 * t_dim1];
+		    if (tjj < sminw) {
+			tmp = sminw;
+			tjj = sminw;
+			*info = 1;
+		    }
+
+		    if (tjj < 1.) {
+			if (xj > bignum * tjj) {
+			    rec = 1. / xj;
+			    igraphdscal_(&n2, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			    xmax *= rec;
+			}
+		    }
+		    d__1 = -z__;
+		    igraphdladiv_(&x[j1], &x[*n + j1], &tmp, &d__1, &sr, &si);
+		    x[j1] = sr;
+		    x[j1 + *n] = si;
+/* Computing MAX */
+		    d__3 = (d__1 = x[j1], abs(d__1)) + (d__2 = x[j1 + *n], 
+			    abs(d__2));
+		    xmax = max(d__3,xmax);
+
+		} else {
+
+/*                 2 by 2 diagonal block   
+
+                   Scale if necessary to avoid overflow in forming the   
+                   right-hand side element by inner product.   
+
+   Computing MAX */
+		    d__5 = (d__1 = x[j1], abs(d__1)) + (d__2 = x[*n + j1], 
+			    abs(d__2)), d__6 = (d__3 = x[j2], abs(d__3)) + (
+			    d__4 = x[*n + j2], abs(d__4));
+		    xj = max(d__5,d__6);
+		    if (xmax > 1.) {
+			rec = 1. / xmax;
+/* Computing MAX */
+			d__1 = work[j1], d__2 = work[j2];
+			if (max(d__1,d__2) > (bignum - xj) / xmax) {
+			    igraphdscal_(&n2, &rec, &x[1], &c__1);
+			    *scale *= rec;
+			    xmax *= rec;
+			}
+		    }
+
+		    i__2 = j1 - 1;
+		    d__[0] = x[j1] - igraphddot_(&i__2, &t[j1 * t_dim1 + 1], &c__1, 
+			    &x[1], &c__1);
+		    i__2 = j1 - 1;
+		    d__[1] = x[j2] - igraphddot_(&i__2, &t[j2 * t_dim1 + 1], &c__1, 
+			    &x[1], &c__1);
+		    i__2 = j1 - 1;
+		    d__[2] = x[*n + j1] - igraphddot_(&i__2, &t[j1 * t_dim1 + 1], &
+			    c__1, &x[*n + 1], &c__1);
+		    i__2 = j1 - 1;
+		    d__[3] = x[*n + j2] - igraphddot_(&i__2, &t[j2 * t_dim1 + 1], &
+			    c__1, &x[*n + 1], &c__1);
+		    d__[0] -= b[j1] * x[*n + 1];
+		    d__[1] -= b[j2] * x[*n + 1];
+		    d__[2] += b[j1] * x[1];
+		    d__[3] += b[j2] * x[1];
+
+		    igraphdlaln2_(&c_true, &c__2, &c__2, &sminw, &c_b21, &t[j1 + j1 
+			    * t_dim1], ldt, &c_b21, &c_b21, d__, &c__2, &
+			    c_b25, w, v, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 2;
+		    }
+
+		    if (scaloc != 1.) {
+			igraphdscal_(&n2, &scaloc, &x[1], &c__1);
+			*scale = scaloc * *scale;
+		    }
+		    x[j1] = v[0];
+		    x[j2] = v[1];
+		    x[*n + j1] = v[2];
+		    x[*n + j2] = v[3];
+/* Computing MAX */
+		    d__5 = (d__1 = x[j1], abs(d__1)) + (d__2 = x[*n + j1], 
+			    abs(d__2)), d__6 = (d__3 = x[j2], abs(d__3)) + (
+			    d__4 = x[*n + j2], abs(d__4)), d__5 = max(d__5,
+			    d__6);
+		    xmax = max(d__5,xmax);
+
+		}
+
+L80:
+		;
+	    }
+
+	}
+
+    }
+
+    return 0;
+
+/*     End of DLAQTR */
+
+} /* igraphdlaqtr_ */
+
diff --git a/igraph/src/dlar1v.c b/igraph/src/dlar1v.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlar1v.c
@@ -0,0 +1,545 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLAR1V computes the (scaled) r-th column of the inverse of the submatrix in rows b1 through bn 
+of the tridiagonal matrix LDLT - λI.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLAR1V + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlar1v.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlar1v.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlar1v.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLAR1V( N, B1, BN, LAMBDA, D, L, LD, LLD,   
+                    PIVMIN, GAPTOL, Z, WANTNC, NEGCNT, ZTZ, MINGMA,   
+                    R, ISUPPZ, NRMINV, RESID, RQCORR, WORK )   
+
+         LOGICAL            WANTNC   
+         INTEGER   B1, BN, N, NEGCNT, R   
+         DOUBLE PRECISION   GAPTOL, LAMBDA, MINGMA, NRMINV, PIVMIN, RESID,   
+        $                   RQCORR, ZTZ   
+         INTEGER            ISUPPZ( * )   
+         DOUBLE PRECISION   D( * ), L( * ), LD( * ), LLD( * ),   
+        $                  WORK( * )   
+         DOUBLE PRECISION Z( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLAR1V computes the (scaled) r-th column of the inverse of   
+   > the sumbmatrix in rows B1 through BN of the tridiagonal matrix   
+   > L D L**T - sigma I. When sigma is close to an eigenvalue, the   
+   > computed vector is an accurate eigenvector. Usually, r corresponds   
+   > to the index where the eigenvector is largest in magnitude.   
+   > The following steps accomplish this computation :   
+   > (a) Stationary qd transform,  L D L**T - sigma I = L(+) D(+) L(+)**T,   
+   > (b) Progressive qd transform, L D L**T - sigma I = U(-) D(-) U(-)**T,   
+   > (c) Computation of the diagonal elements of the inverse of   
+   >     L D L**T - sigma I by combining the above transforms, and choosing   
+   >     r as the index where the diagonal of the inverse is (one of the)   
+   >     largest in magnitude.   
+   > (d) Computation of the (scaled) r-th column of the inverse using the   
+   >     twisted factorization obtained by combining the top part of the   
+   >     the stationary and the bottom part of the progressive transform.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >           The order of the matrix L D L**T.   
+   > \endverbatim   
+   >   
+   > \param[in] B1   
+   > \verbatim   
+   >          B1 is INTEGER   
+   >           First index of the submatrix of L D L**T.   
+   > \endverbatim   
+   >   
+   > \param[in] BN   
+   > \verbatim   
+   >          BN is INTEGER   
+   >           Last index of the submatrix of L D L**T.   
+   > \endverbatim   
+   >   
+   > \param[in] LAMBDA   
+   > \verbatim   
+   >          LAMBDA is DOUBLE PRECISION   
+   >           The shift. In order to compute an accurate eigenvector,   
+   >           LAMBDA should be a good approximation to an eigenvalue   
+   >           of L D L**T.   
+   > \endverbatim   
+   >   
+   > \param[in] L   
+   > \verbatim   
+   >          L is DOUBLE PRECISION array, dimension (N-1)   
+   >           The (n-1) subdiagonal elements of the unit bidiagonal matrix   
+   >           L, in elements 1 to N-1.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >           The n diagonal elements of the diagonal matrix D.   
+   > \endverbatim   
+   >   
+   > \param[in] LD   
+   > \verbatim   
+   >          LD is DOUBLE PRECISION array, dimension (N-1)   
+   >           The n-1 elements L(i)*D(i).   
+   > \endverbatim   
+   >   
+   > \param[in] LLD   
+   > \verbatim   
+   >          LLD is DOUBLE PRECISION array, dimension (N-1)   
+   >           The n-1 elements L(i)*L(i)*D(i).   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >           The minimum pivot in the Sturm sequence.   
+   > \endverbatim   
+   >   
+   > \param[in] GAPTOL   
+   > \verbatim   
+   >          GAPTOL is DOUBLE PRECISION   
+   >           Tolerance that indicates when eigenvector entries are negligible   
+   >           w.r.t. their contribution to the residual.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (N)   
+   >           On input, all entries of Z must be set to 0.   
+   >           On output, Z contains the (scaled) r-th column of the   
+   >           inverse. The scaling is such that Z(R) equals 1.   
+   > \endverbatim   
+   >   
+   > \param[in] WANTNC   
+   > \verbatim   
+   >          WANTNC is LOGICAL   
+   >           Specifies whether NEGCNT has to be computed.   
+   > \endverbatim   
+   >   
+   > \param[out] NEGCNT   
+   > \verbatim   
+   >          NEGCNT is INTEGER   
+   >           If WANTNC is .TRUE. then NEGCNT = the number of pivots < pivmin   
+   >           in the  matrix factorization L D L**T, and NEGCNT = -1 otherwise.   
+   > \endverbatim   
+   >   
+   > \param[out] ZTZ   
+   > \verbatim   
+   >          ZTZ is DOUBLE PRECISION   
+   >           The square of the 2-norm of Z.   
+   > \endverbatim   
+   >   
+   > \param[out] MINGMA   
+   > \verbatim   
+   >          MINGMA is DOUBLE PRECISION   
+   >           The reciprocal of the largest (in magnitude) diagonal   
+   >           element of the inverse of L D L**T - sigma I.   
+   > \endverbatim   
+   >   
+   > \param[in,out] R   
+   > \verbatim   
+   >          R is INTEGER   
+   >           The twist index for the twisted factorization used to   
+   >           compute Z.   
+   >           On input, 0 <= R <= N. If R is input as 0, R is set to   
+   >           the index where (L D L**T - sigma I)^{-1} is largest   
+   >           in magnitude. If 1 <= R <= N, R is unchanged.   
+   >           On output, R contains the twist index used to compute Z.   
+   >           Ideally, R designates the position of the maximum entry in the   
+   >           eigenvector.   
+   > \endverbatim   
+   >   
+   > \param[out] ISUPPZ   
+   > \verbatim   
+   >          ISUPPZ is INTEGER array, dimension (2)   
+   >           The support of the vector in Z, i.e., the vector Z is   
+   >           nonzero only in elements ISUPPZ(1) through ISUPPZ( 2 ).   
+   > \endverbatim   
+   >   
+   > \param[out] NRMINV   
+   > \verbatim   
+   >          NRMINV is DOUBLE PRECISION   
+   >           NRMINV = 1/SQRT( ZTZ )   
+   > \endverbatim   
+   >   
+   > \param[out] RESID   
+   > \verbatim   
+   >          RESID is DOUBLE PRECISION   
+   >           The residual of the FP vector.   
+   >           RESID = ABS( MINGMA )/SQRT( ZTZ )   
+   > \endverbatim   
+   >   
+   > \param[out] RQCORR   
+   > \verbatim   
+   >          RQCORR is DOUBLE PRECISION   
+   >           The Rayleigh Quotient correction to LAMBDA.   
+   >           RQCORR = MINGMA*TMP   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (4*N)   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdlar1v_(integer *n, integer *b1, integer *bn, doublereal 
+	*lambda, doublereal *d__, doublereal *l, doublereal *ld, doublereal *
+	lld, doublereal *pivmin, doublereal *gaptol, doublereal *z__, logical 
+	*wantnc, integer *negcnt, doublereal *ztz, doublereal *mingma, 
+	integer *r__, integer *isuppz, doublereal *nrminv, doublereal *resid, 
+	doublereal *rqcorr, doublereal *work)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2, d__3;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__;
+    doublereal s;
+    integer r1, r2;
+    doublereal eps, tmp;
+    integer neg1, neg2, indp, inds;
+    doublereal dplus;
+    extern doublereal igraphdlamch_(char *);
+    extern logical igraphdisnan_(doublereal *);
+    integer indlpl, indumn;
+    doublereal dminus;
+    logical sawnan1, sawnan2;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --work;
+    --isuppz;
+    --z__;
+    --lld;
+    --ld;
+    --l;
+    --d__;
+
+    /* Function Body */
+    eps = igraphdlamch_("Precision");
+    if (*r__ == 0) {
+	r1 = *b1;
+	r2 = *bn;
+    } else {
+	r1 = *r__;
+	r2 = *r__;
+    }
+/*     Storage for LPLUS */
+    indlpl = 0;
+/*     Storage for UMINUS */
+    indumn = *n;
+    inds = (*n << 1) + 1;
+    indp = *n * 3 + 1;
+    if (*b1 == 1) {
+	work[inds] = 0.;
+    } else {
+	work[inds + *b1 - 1] = lld[*b1 - 1];
+    }
+
+/*     Compute the stationary transform (using the differential form)   
+       until the index R2. */
+
+    sawnan1 = FALSE_;
+    neg1 = 0;
+    s = work[inds + *b1 - 1] - *lambda;
+    i__1 = r1 - 1;
+    for (i__ = *b1; i__ <= i__1; ++i__) {
+	dplus = d__[i__] + s;
+	work[indlpl + i__] = ld[i__] / dplus;
+	if (dplus < 0.) {
+	    ++neg1;
+	}
+	work[inds + i__] = s * work[indlpl + i__] * l[i__];
+	s = work[inds + i__] - *lambda;
+/* L50: */
+    }
+    sawnan1 = igraphdisnan_(&s);
+    if (sawnan1) {
+	goto L60;
+    }
+    i__1 = r2 - 1;
+    for (i__ = r1; i__ <= i__1; ++i__) {
+	dplus = d__[i__] + s;
+	work[indlpl + i__] = ld[i__] / dplus;
+	work[inds + i__] = s * work[indlpl + i__] * l[i__];
+	s = work[inds + i__] - *lambda;
+/* L51: */
+    }
+    sawnan1 = igraphdisnan_(&s);
+
+L60:
+    if (sawnan1) {
+/*        Runs a slower version of the above loop if a NaN is detected */
+	neg1 = 0;
+	s = work[inds + *b1 - 1] - *lambda;
+	i__1 = r1 - 1;
+	for (i__ = *b1; i__ <= i__1; ++i__) {
+	    dplus = d__[i__] + s;
+	    if (abs(dplus) < *pivmin) {
+		dplus = -(*pivmin);
+	    }
+	    work[indlpl + i__] = ld[i__] / dplus;
+	    if (dplus < 0.) {
+		++neg1;
+	    }
+	    work[inds + i__] = s * work[indlpl + i__] * l[i__];
+	    if (work[indlpl + i__] == 0.) {
+		work[inds + i__] = lld[i__];
+	    }
+	    s = work[inds + i__] - *lambda;
+/* L70: */
+	}
+	i__1 = r2 - 1;
+	for (i__ = r1; i__ <= i__1; ++i__) {
+	    dplus = d__[i__] + s;
+	    if (abs(dplus) < *pivmin) {
+		dplus = -(*pivmin);
+	    }
+	    work[indlpl + i__] = ld[i__] / dplus;
+	    work[inds + i__] = s * work[indlpl + i__] * l[i__];
+	    if (work[indlpl + i__] == 0.) {
+		work[inds + i__] = lld[i__];
+	    }
+	    s = work[inds + i__] - *lambda;
+/* L71: */
+	}
+    }
+
+/*     Compute the progressive transform (using the differential form)   
+       until the index R1 */
+
+    sawnan2 = FALSE_;
+    neg2 = 0;
+    work[indp + *bn - 1] = d__[*bn] - *lambda;
+    i__1 = r1;
+    for (i__ = *bn - 1; i__ >= i__1; --i__) {
+	dminus = lld[i__] + work[indp + i__];
+	tmp = d__[i__] / dminus;
+	if (dminus < 0.) {
+	    ++neg2;
+	}
+	work[indumn + i__] = l[i__] * tmp;
+	work[indp + i__ - 1] = work[indp + i__] * tmp - *lambda;
+/* L80: */
+    }
+    tmp = work[indp + r1 - 1];
+    sawnan2 = igraphdisnan_(&tmp);
+    if (sawnan2) {
+/*        Runs a slower version of the above loop if a NaN is detected */
+	neg2 = 0;
+	i__1 = r1;
+	for (i__ = *bn - 1; i__ >= i__1; --i__) {
+	    dminus = lld[i__] + work[indp + i__];
+	    if (abs(dminus) < *pivmin) {
+		dminus = -(*pivmin);
+	    }
+	    tmp = d__[i__] / dminus;
+	    if (dminus < 0.) {
+		++neg2;
+	    }
+	    work[indumn + i__] = l[i__] * tmp;
+	    work[indp + i__ - 1] = work[indp + i__] * tmp - *lambda;
+	    if (tmp == 0.) {
+		work[indp + i__ - 1] = d__[i__] - *lambda;
+	    }
+/* L100: */
+	}
+    }
+
+/*     Find the index (from R1 to R2) of the largest (in magnitude)   
+       diagonal element of the inverse */
+
+    *mingma = work[inds + r1 - 1] + work[indp + r1 - 1];
+    if (*mingma < 0.) {
+	++neg1;
+    }
+    if (*wantnc) {
+	*negcnt = neg1 + neg2;
+    } else {
+	*negcnt = -1;
+    }
+    if (abs(*mingma) == 0.) {
+	*mingma = eps * work[inds + r1 - 1];
+    }
+    *r__ = r1;
+    i__1 = r2 - 1;
+    for (i__ = r1; i__ <= i__1; ++i__) {
+	tmp = work[inds + i__] + work[indp + i__];
+	if (tmp == 0.) {
+	    tmp = eps * work[inds + i__];
+	}
+	if (abs(tmp) <= abs(*mingma)) {
+	    *mingma = tmp;
+	    *r__ = i__ + 1;
+	}
+/* L110: */
+    }
+
+/*     Compute the FP vector: solve N^T v = e_r */
+
+    isuppz[1] = *b1;
+    isuppz[2] = *bn;
+    z__[*r__] = 1.;
+    *ztz = 1.;
+
+/*     Compute the FP vector upwards from R */
+
+    if (! sawnan1 && ! sawnan2) {
+	i__1 = *b1;
+	for (i__ = *r__ - 1; i__ >= i__1; --i__) {
+	    z__[i__] = -(work[indlpl + i__] * z__[i__ + 1]);
+	    if (((d__1 = z__[i__], abs(d__1)) + (d__2 = z__[i__ + 1], abs(
+		    d__2))) * (d__3 = ld[i__], abs(d__3)) < *gaptol) {
+		z__[i__] = 0.;
+		isuppz[1] = i__ + 1;
+		goto L220;
+	    }
+	    *ztz += z__[i__] * z__[i__];
+/* L210: */
+	}
+L220:
+	;
+    } else {
+/*        Run slower loop if NaN occurred. */
+	i__1 = *b1;
+	for (i__ = *r__ - 1; i__ >= i__1; --i__) {
+	    if (z__[i__ + 1] == 0.) {
+		z__[i__] = -(ld[i__ + 1] / ld[i__]) * z__[i__ + 2];
+	    } else {
+		z__[i__] = -(work[indlpl + i__] * z__[i__ + 1]);
+	    }
+	    if (((d__1 = z__[i__], abs(d__1)) + (d__2 = z__[i__ + 1], abs(
+		    d__2))) * (d__3 = ld[i__], abs(d__3)) < *gaptol) {
+		z__[i__] = 0.;
+		isuppz[1] = i__ + 1;
+		goto L240;
+	    }
+	    *ztz += z__[i__] * z__[i__];
+/* L230: */
+	}
+L240:
+	;
+    }
+/*     Compute the FP vector downwards from R in blocks of size BLKSIZ */
+    if (! sawnan1 && ! sawnan2) {
+	i__1 = *bn - 1;
+	for (i__ = *r__; i__ <= i__1; ++i__) {
+	    z__[i__ + 1] = -(work[indumn + i__] * z__[i__]);
+	    if (((d__1 = z__[i__], abs(d__1)) + (d__2 = z__[i__ + 1], abs(
+		    d__2))) * (d__3 = ld[i__], abs(d__3)) < *gaptol) {
+		z__[i__ + 1] = 0.;
+		isuppz[2] = i__;
+		goto L260;
+	    }
+	    *ztz += z__[i__ + 1] * z__[i__ + 1];
+/* L250: */
+	}
+L260:
+	;
+    } else {
+/*        Run slower loop if NaN occurred. */
+	i__1 = *bn - 1;
+	for (i__ = *r__; i__ <= i__1; ++i__) {
+	    if (z__[i__] == 0.) {
+		z__[i__ + 1] = -(ld[i__ - 1] / ld[i__]) * z__[i__ - 1];
+	    } else {
+		z__[i__ + 1] = -(work[indumn + i__] * z__[i__]);
+	    }
+	    if (((d__1 = z__[i__], abs(d__1)) + (d__2 = z__[i__ + 1], abs(
+		    d__2))) * (d__3 = ld[i__], abs(d__3)) < *gaptol) {
+		z__[i__ + 1] = 0.;
+		isuppz[2] = i__;
+		goto L280;
+	    }
+	    *ztz += z__[i__ + 1] * z__[i__ + 1];
+/* L270: */
+	}
+L280:
+	;
+    }
+
+/*     Compute quantities for convergence test */
+
+    tmp = 1. / *ztz;
+    *nrminv = sqrt(tmp);
+    *resid = abs(*mingma) * *nrminv;
+    *rqcorr = *mingma * tmp;
+
+
+    return 0;
+
+/*     End of DLAR1V */
+
+} /* igraphdlar1v_ */
+
diff --git a/igraph/src/dlarf.c b/igraph/src/dlarf.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarf.c
@@ -0,0 +1,255 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b4 = 1.;
+static doublereal c_b5 = 0.;
+static integer c__1 = 1;
+
+/* > \brief \b DLARF applies an elementary reflector to a general rectangular matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARF + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarf.f
+">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarf.f
+">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarf.f
+">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARF( SIDE, M, N, V, INCV, TAU, C, LDC, WORK )   
+
+         CHARACTER          SIDE   
+         INTEGER            INCV, LDC, M, N   
+         DOUBLE PRECISION   TAU   
+         DOUBLE PRECISION   C( LDC, * ), V( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARF applies a real elementary reflector H to a real m by n matrix   
+   > C, from either the left or the right. H is represented in the form   
+   >   
+   >       H = I - tau * v * v**T   
+   >   
+   > where tau is a real scalar and v is a real vector.   
+   >   
+   > If tau = 0, then H is taken to be the unit matrix.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': form  H * C   
+   >          = 'R': form  C * H   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C.   
+   > \endverbatim   
+   >   
+   > \param[in] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array, dimension   
+   >                     (1 + (M-1)*abs(INCV)) if SIDE = 'L'   
+   >                  or (1 + (N-1)*abs(INCV)) if SIDE = 'R'   
+   >          The vector v in the representation of H. V is not used if   
+   >          TAU = 0.   
+   > \endverbatim   
+   >   
+   > \param[in] INCV   
+   > \verbatim   
+   >          INCV is INTEGER   
+   >          The increment between elements of v. INCV <> 0.   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION   
+   >          The value tau in the representation of H.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the m by n matrix C.   
+   >          On exit, C is overwritten by the matrix H * C if SIDE = 'L',   
+   >          or C * H if SIDE = 'R'.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension   
+   >                         (N) if SIDE = 'L'   
+   >                      or (M) if SIDE = 'R'   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarf_(char *side, integer *m, integer *n, doublereal *v,
+	 integer *incv, doublereal *tau, doublereal *c__, integer *ldc, 
+	doublereal *work)
+{
+    /* System generated locals */
+    integer c_dim1, c_offset;
+    doublereal d__1;
+
+    /* Local variables */
+    integer i__;
+    logical applyleft;
+    extern /* Subroutine */ int igraphdger_(integer *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *);
+    integer lastc, lastv;
+    extern integer igraphiladlc_(integer *, integer *, doublereal *, integer *), 
+	    igraphiladlr_(integer *, integer *, doublereal *, integer *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --v;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    --work;
+
+    /* Function Body */
+    applyleft = igraphlsame_(side, "L");
+    lastv = 0;
+    lastc = 0;
+    if (*tau != 0.) {
+/*     Set up variables for scanning V.  LASTV begins pointing to the end   
+       of V. */
+	if (applyleft) {
+	    lastv = *m;
+	} else {
+	    lastv = *n;
+	}
+	if (*incv > 0) {
+	    i__ = (lastv - 1) * *incv + 1;
+	} else {
+	    i__ = 1;
+	}
+/*     Look for the last non-zero row in V. */
+	while(lastv > 0 && v[i__] == 0.) {
+	    --lastv;
+	    i__ -= *incv;
+	}
+	if (applyleft) {
+/*     Scan for the last non-zero column in C(1:lastv,:). */
+	    lastc = igraphiladlc_(&lastv, n, &c__[c_offset], ldc);
+	} else {
+/*     Scan for the last non-zero row in C(:,1:lastv). */
+	    lastc = igraphiladlr_(m, &lastv, &c__[c_offset], ldc);
+	}
+    }
+/*     Note that lastc.eq.0 renders the BLAS operations null; no special   
+       case is needed at this level. */
+    if (applyleft) {
+
+/*        Form  H * C */
+
+	if (lastv > 0) {
+
+/*           w(1:lastc,1) := C(1:lastv,1:lastc)**T * v(1:lastv,1) */
+
+	    igraphdgemv_("Transpose", &lastv, &lastc, &c_b4, &c__[c_offset], ldc, &
+		    v[1], incv, &c_b5, &work[1], &c__1);
+
+/*           C(1:lastv,1:lastc) := C(...) - v(1:lastv,1) * w(1:lastc,1)**T */
+
+	    d__1 = -(*tau);
+	    igraphdger_(&lastv, &lastc, &d__1, &v[1], incv, &work[1], &c__1, &c__[
+		    c_offset], ldc);
+	}
+    } else {
+
+/*        Form  C * H */
+
+	if (lastv > 0) {
+
+/*           w(1:lastc,1) := C(1:lastc,1:lastv) * v(1:lastv,1) */
+
+	    igraphdgemv_("No transpose", &lastc, &lastv, &c_b4, &c__[c_offset], ldc,
+		     &v[1], incv, &c_b5, &work[1], &c__1);
+
+/*           C(1:lastc,1:lastv) := C(...) - w(1:lastc,1) * v(1:lastv,1)**T */
+
+	    d__1 = -(*tau);
+	    igraphdger_(&lastc, &lastv, &d__1, &work[1], &c__1, &v[1], incv, &c__[
+		    c_offset], ldc);
+	}
+    }
+    return 0;
+
+/*     End of DLARF */
+
+} /* igraphdlarf_ */
+
diff --git a/igraph/src/dlarfb.c b/igraph/src/dlarfb.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarfb.c
@@ -0,0 +1,838 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b14 = 1.;
+static doublereal c_b25 = -1.;
+
+/* > \brief \b DLARFB applies a block reflector or its transpose to a general rectangular matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARFB + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarfb.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarfb.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarfb.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARFB( SIDE, TRANS, DIRECT, STOREV, M, N, K, V, LDV,   
+                            T, LDT, C, LDC, WORK, LDWORK )   
+
+         CHARACTER          DIRECT, SIDE, STOREV, TRANS   
+         INTEGER            K, LDC, LDT, LDV, LDWORK, M, N   
+         DOUBLE PRECISION   C( LDC, * ), T( LDT, * ), V( LDV, * ),   
+        $                   WORK( LDWORK, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARFB applies a real block reflector H or its transpose H**T to a   
+   > real m by n matrix C, from either the left or the right.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': apply H or H**T from the Left   
+   >          = 'R': apply H or H**T from the Right   
+   > \endverbatim   
+   >   
+   > \param[in] TRANS   
+   > \verbatim   
+   >          TRANS is CHARACTER*1   
+   >          = 'N': apply H (No transpose)   
+   >          = 'T': apply H**T (Transpose)   
+   > \endverbatim   
+   >   
+   > \param[in] DIRECT   
+   > \verbatim   
+   >          DIRECT is CHARACTER*1   
+   >          Indicates how H is formed from a product of elementary   
+   >          reflectors   
+   >          = 'F': H = H(1) H(2) . . . H(k) (Forward)   
+   >          = 'B': H = H(k) . . . H(2) H(1) (Backward)   
+   > \endverbatim   
+   >   
+   > \param[in] STOREV   
+   > \verbatim   
+   >          STOREV is CHARACTER*1   
+   >          Indicates how the vectors which define the elementary   
+   >          reflectors are stored:   
+   >          = 'C': Columnwise   
+   >          = 'R': Rowwise   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C.   
+   > \endverbatim   
+   >   
+   > \param[in] K   
+   > \verbatim   
+   >          K is INTEGER   
+   >          The order of the matrix T (= the number of elementary   
+   >          reflectors whose product defines the block reflector).   
+   > \endverbatim   
+   >   
+   > \param[in] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array, dimension   
+   >                                (LDV,K) if STOREV = 'C'   
+   >                                (LDV,M) if STOREV = 'R' and SIDE = 'L'   
+   >                                (LDV,N) if STOREV = 'R' and SIDE = 'R'   
+   >          The matrix V. See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[in] LDV   
+   > \verbatim   
+   >          LDV is INTEGER   
+   >          The leading dimension of the array V.   
+   >          If STOREV = 'C' and SIDE = 'L', LDV >= max(1,M);   
+   >          if STOREV = 'C' and SIDE = 'R', LDV >= max(1,N);   
+   >          if STOREV = 'R', LDV >= K.   
+   > \endverbatim   
+   >   
+   > \param[in] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,K)   
+   >          The triangular k by k matrix T in the representation of the   
+   >          block reflector.   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is INTEGER   
+   >          The leading dimension of the array T. LDT >= K.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the m by n matrix C.   
+   >          On exit, C is overwritten by H*C or H**T*C or C*H or C*H**T.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (LDWORK,K)   
+   > \endverbatim   
+   >   
+   > \param[in] LDWORK   
+   > \verbatim   
+   >          LDWORK is INTEGER   
+   >          The leading dimension of the array WORK.   
+   >          If SIDE = 'L', LDWORK >= max(1,N);   
+   >          if SIDE = 'R', LDWORK >= max(1,M).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date June 2013   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The shape of the matrix V and the storage of the vectors which define   
+   >  the H(i) is best illustrated by the following example with n = 5 and   
+   >  k = 3. The elements equal to 1 are not stored; the corresponding   
+   >  array elements are modified but restored on exit. The rest of the   
+   >  array is not used.   
+   >   
+   >  DIRECT = 'F' and STOREV = 'C':         DIRECT = 'F' and STOREV = 'R':   
+   >   
+   >               V = (  1       )                 V = (  1 v1 v1 v1 v1 )   
+   >                   ( v1  1    )                     (     1 v2 v2 v2 )   
+   >                   ( v1 v2  1 )                     (        1 v3 v3 )   
+   >                   ( v1 v2 v3 )   
+   >                   ( v1 v2 v3 )   
+   >   
+   >  DIRECT = 'B' and STOREV = 'C':         DIRECT = 'B' and STOREV = 'R':   
+   >   
+   >               V = ( v1 v2 v3 )                 V = ( v1 v1  1       )   
+   >                   ( v1 v2 v3 )                     ( v2 v2 v2  1    )   
+   >                   (  1 v2 v3 )                     ( v3 v3 v3 v3  1 )   
+   >                   (     1 v3 )   
+   >                   (        1 )   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlarfb_(char *side, char *trans, char *direct, char *
+	storev, integer *m, integer *n, integer *k, doublereal *v, integer *
+	ldv, doublereal *t, integer *ldt, doublereal *c__, integer *ldc, 
+	doublereal *work, integer *ldwork)
+{
+    /* System generated locals */
+    integer c_dim1, c_offset, t_dim1, t_offset, v_dim1, v_offset, work_dim1, 
+	    work_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j;
+    extern /* Subroutine */ int igraphdgemm_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdtrmm_(char *, char *, char *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    char transt[1];
+
+
+/*  -- LAPACK auxiliary routine (version 3.5.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       June 2013   
+
+
+    =====================================================================   
+
+
+       Quick return if possible   
+
+       Parameter adjustments */
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    work_dim1 = *ldwork;
+    work_offset = 1 + work_dim1;
+    work -= work_offset;
+
+    /* Function Body */
+    if (*m <= 0 || *n <= 0) {
+	return 0;
+    }
+
+    if (igraphlsame_(trans, "N")) {
+	*(unsigned char *)transt = 'T';
+    } else {
+	*(unsigned char *)transt = 'N';
+    }
+
+    if (igraphlsame_(storev, "C")) {
+
+	if (igraphlsame_(direct, "F")) {
+
+/*           Let  V =  ( V1 )    (first K rows)   
+                       ( V2 )   
+             where  V1  is unit lower triangular. */
+
+	    if (igraphlsame_(side, "L")) {
+
+/*              Form  H * C  or  H**T * C  where  C = ( C1 )   
+                                                      ( C2 )   
+
+                W := C**T * V  =  (C1**T * V1 + C2**T * V2)  (stored in WORK)   
+
+                W := C1**T */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    igraphdcopy_(n, &c__[j + c_dim1], ldc, &work[j * work_dim1 + 1],
+			     &c__1);
+/* L10: */
+		}
+
+/*              W := W * V1 */
+
+		igraphdtrmm_("Right", "Lower", "No transpose", "Unit", n, k, &c_b14,
+			 &v[v_offset], ldv, &work[work_offset], ldwork);
+		if (*m > *k) {
+
+/*                 W := W + C2**T * V2 */
+
+		    i__1 = *m - *k;
+		    igraphdgemm_("Transpose", "No transpose", n, k, &i__1, &c_b14, &
+			    c__[*k + 1 + c_dim1], ldc, &v[*k + 1 + v_dim1], 
+			    ldv, &c_b14, &work[work_offset], ldwork);
+		}
+
+/*              W := W * T**T  or  W * T */
+
+		igraphdtrmm_("Right", "Upper", transt, "Non-unit", n, k, &c_b14, &t[
+			t_offset], ldt, &work[work_offset], ldwork);
+
+/*              C := C - V * W**T */
+
+		if (*m > *k) {
+
+/*                 C2 := C2 - V2 * W**T */
+
+		    i__1 = *m - *k;
+		    igraphdgemm_("No transpose", "Transpose", &i__1, n, k, &c_b25, &
+			    v[*k + 1 + v_dim1], ldv, &work[work_offset], 
+			    ldwork, &c_b14, &c__[*k + 1 + c_dim1], ldc);
+		}
+
+/*              W := W * V1**T */
+
+		igraphdtrmm_("Right", "Lower", "Transpose", "Unit", n, k, &c_b14, &
+			v[v_offset], ldv, &work[work_offset], ldwork);
+
+/*              C1 := C1 - W**T */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *n;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[j + i__ * c_dim1] -= work[i__ + j * work_dim1];
+/* L20: */
+		    }
+/* L30: */
+		}
+
+	    } else if (igraphlsame_(side, "R")) {
+
+/*              Form  C * H  or  C * H**T  where  C = ( C1  C2 )   
+
+                W := C * V  =  (C1*V1 + C2*V2)  (stored in WORK)   
+
+                W := C1 */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    igraphdcopy_(m, &c__[j * c_dim1 + 1], &c__1, &work[j * 
+			    work_dim1 + 1], &c__1);
+/* L40: */
+		}
+
+/*              W := W * V1 */
+
+		igraphdtrmm_("Right", "Lower", "No transpose", "Unit", m, k, &c_b14,
+			 &v[v_offset], ldv, &work[work_offset], ldwork);
+		if (*n > *k) {
+
+/*                 W := W + C2 * V2 */
+
+		    i__1 = *n - *k;
+		    igraphdgemm_("No transpose", "No transpose", m, k, &i__1, &
+			    c_b14, &c__[(*k + 1) * c_dim1 + 1], ldc, &v[*k + 
+			    1 + v_dim1], ldv, &c_b14, &work[work_offset], 
+			    ldwork);
+		}
+
+/*              W := W * T  or  W * T**T */
+
+		igraphdtrmm_("Right", "Upper", trans, "Non-unit", m, k, &c_b14, &t[
+			t_offset], ldt, &work[work_offset], ldwork);
+
+/*              C := C - W * V**T */
+
+		if (*n > *k) {
+
+/*                 C2 := C2 - W * V2**T */
+
+		    i__1 = *n - *k;
+		    igraphdgemm_("No transpose", "Transpose", m, &i__1, k, &c_b25, &
+			    work[work_offset], ldwork, &v[*k + 1 + v_dim1], 
+			    ldv, &c_b14, &c__[(*k + 1) * c_dim1 + 1], ldc);
+		}
+
+/*              W := W * V1**T */
+
+		igraphdtrmm_("Right", "Lower", "Transpose", "Unit", m, k, &c_b14, &
+			v[v_offset], ldv, &work[work_offset], ldwork);
+
+/*              C1 := C1 - W */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] -= work[i__ + j * work_dim1];
+/* L50: */
+		    }
+/* L60: */
+		}
+	    }
+
+	} else {
+
+/*           Let  V =  ( V1 )   
+                       ( V2 )    (last K rows)   
+             where  V2  is unit upper triangular. */
+
+	    if (igraphlsame_(side, "L")) {
+
+/*              Form  H * C  or  H**T * C  where  C = ( C1 )   
+                                                      ( C2 )   
+
+                W := C**T * V  =  (C1**T * V1 + C2**T * V2)  (stored in WORK)   
+
+                W := C2**T */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    igraphdcopy_(n, &c__[*m - *k + j + c_dim1], ldc, &work[j * 
+			    work_dim1 + 1], &c__1);
+/* L70: */
+		}
+
+/*              W := W * V2 */
+
+		igraphdtrmm_("Right", "Upper", "No transpose", "Unit", n, k, &c_b14,
+			 &v[*m - *k + 1 + v_dim1], ldv, &work[work_offset], 
+			ldwork);
+		if (*m > *k) {
+
+/*                 W := W + C1**T * V1 */
+
+		    i__1 = *m - *k;
+		    igraphdgemm_("Transpose", "No transpose", n, k, &i__1, &c_b14, &
+			    c__[c_offset], ldc, &v[v_offset], ldv, &c_b14, &
+			    work[work_offset], ldwork);
+		}
+
+/*              W := W * T**T  or  W * T */
+
+		igraphdtrmm_("Right", "Lower", transt, "Non-unit", n, k, &c_b14, &t[
+			t_offset], ldt, &work[work_offset], ldwork);
+
+/*              C := C - V * W**T */
+
+		if (*m > *k) {
+
+/*                 C1 := C1 - V1 * W**T */
+
+		    i__1 = *m - *k;
+		    igraphdgemm_("No transpose", "Transpose", &i__1, n, k, &c_b25, &
+			    v[v_offset], ldv, &work[work_offset], ldwork, &
+			    c_b14, &c__[c_offset], ldc)
+			    ;
+		}
+
+/*              W := W * V2**T */
+
+		igraphdtrmm_("Right", "Upper", "Transpose", "Unit", n, k, &c_b14, &
+			v[*m - *k + 1 + v_dim1], ldv, &work[work_offset], 
+			ldwork);
+
+/*              C2 := C2 - W**T */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *n;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[*m - *k + j + i__ * c_dim1] -= work[i__ + j * 
+				work_dim1];
+/* L80: */
+		    }
+/* L90: */
+		}
+
+	    } else if (igraphlsame_(side, "R")) {
+
+/*              Form  C * H  or  C * H**T  where  C = ( C1  C2 )   
+
+                W := C * V  =  (C1*V1 + C2*V2)  (stored in WORK)   
+
+                W := C2 */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    igraphdcopy_(m, &c__[(*n - *k + j) * c_dim1 + 1], &c__1, &work[
+			    j * work_dim1 + 1], &c__1);
+/* L100: */
+		}
+
+/*              W := W * V2 */
+
+		igraphdtrmm_("Right", "Upper", "No transpose", "Unit", m, k, &c_b14,
+			 &v[*n - *k + 1 + v_dim1], ldv, &work[work_offset], 
+			ldwork);
+		if (*n > *k) {
+
+/*                 W := W + C1 * V1 */
+
+		    i__1 = *n - *k;
+		    igraphdgemm_("No transpose", "No transpose", m, k, &i__1, &
+			    c_b14, &c__[c_offset], ldc, &v[v_offset], ldv, &
+			    c_b14, &work[work_offset], ldwork);
+		}
+
+/*              W := W * T  or  W * T**T */
+
+		igraphdtrmm_("Right", "Lower", trans, "Non-unit", m, k, &c_b14, &t[
+			t_offset], ldt, &work[work_offset], ldwork);
+
+/*              C := C - W * V**T */
+
+		if (*n > *k) {
+
+/*                 C1 := C1 - W * V1**T */
+
+		    i__1 = *n - *k;
+		    igraphdgemm_("No transpose", "Transpose", m, &i__1, k, &c_b25, &
+			    work[work_offset], ldwork, &v[v_offset], ldv, &
+			    c_b14, &c__[c_offset], ldc)
+			    ;
+		}
+
+/*              W := W * V2**T */
+
+		igraphdtrmm_("Right", "Upper", "Transpose", "Unit", m, k, &c_b14, &
+			v[*n - *k + 1 + v_dim1], ldv, &work[work_offset], 
+			ldwork);
+
+/*              C2 := C2 - W */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + (*n - *k + j) * c_dim1] -= work[i__ + j * 
+				work_dim1];
+/* L110: */
+		    }
+/* L120: */
+		}
+	    }
+	}
+
+    } else if (igraphlsame_(storev, "R")) {
+
+	if (igraphlsame_(direct, "F")) {
+
+/*           Let  V =  ( V1  V2 )    (V1: first K columns)   
+             where  V1  is unit upper triangular. */
+
+	    if (igraphlsame_(side, "L")) {
+
+/*              Form  H * C  or  H**T * C  where  C = ( C1 )   
+                                                      ( C2 )   
+
+                W := C**T * V**T  =  (C1**T * V1**T + C2**T * V2**T) (stored in WORK)   
+
+                W := C1**T */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    igraphdcopy_(n, &c__[j + c_dim1], ldc, &work[j * work_dim1 + 1],
+			     &c__1);
+/* L130: */
+		}
+
+/*              W := W * V1**T */
+
+		igraphdtrmm_("Right", "Upper", "Transpose", "Unit", n, k, &c_b14, &
+			v[v_offset], ldv, &work[work_offset], ldwork);
+		if (*m > *k) {
+
+/*                 W := W + C2**T * V2**T */
+
+		    i__1 = *m - *k;
+		    igraphdgemm_("Transpose", "Transpose", n, k, &i__1, &c_b14, &
+			    c__[*k + 1 + c_dim1], ldc, &v[(*k + 1) * v_dim1 + 
+			    1], ldv, &c_b14, &work[work_offset], ldwork);
+		}
+
+/*              W := W * T**T  or  W * T */
+
+		igraphdtrmm_("Right", "Upper", transt, "Non-unit", n, k, &c_b14, &t[
+			t_offset], ldt, &work[work_offset], ldwork);
+
+/*              C := C - V**T * W**T */
+
+		if (*m > *k) {
+
+/*                 C2 := C2 - V2**T * W**T */
+
+		    i__1 = *m - *k;
+		    igraphdgemm_("Transpose", "Transpose", &i__1, n, k, &c_b25, &v[(
+			    *k + 1) * v_dim1 + 1], ldv, &work[work_offset], 
+			    ldwork, &c_b14, &c__[*k + 1 + c_dim1], ldc);
+		}
+
+/*              W := W * V1 */
+
+		igraphdtrmm_("Right", "Upper", "No transpose", "Unit", n, k, &c_b14,
+			 &v[v_offset], ldv, &work[work_offset], ldwork);
+
+/*              C1 := C1 - W**T */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *n;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[j + i__ * c_dim1] -= work[i__ + j * work_dim1];
+/* L140: */
+		    }
+/* L150: */
+		}
+
+	    } else if (igraphlsame_(side, "R")) {
+
+/*              Form  C * H  or  C * H**T  where  C = ( C1  C2 )   
+
+                W := C * V**T  =  (C1*V1**T + C2*V2**T)  (stored in WORK)   
+
+                W := C1 */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    igraphdcopy_(m, &c__[j * c_dim1 + 1], &c__1, &work[j * 
+			    work_dim1 + 1], &c__1);
+/* L160: */
+		}
+
+/*              W := W * V1**T */
+
+		igraphdtrmm_("Right", "Upper", "Transpose", "Unit", m, k, &c_b14, &
+			v[v_offset], ldv, &work[work_offset], ldwork);
+		if (*n > *k) {
+
+/*                 W := W + C2 * V2**T */
+
+		    i__1 = *n - *k;
+		    igraphdgemm_("No transpose", "Transpose", m, k, &i__1, &c_b14, &
+			    c__[(*k + 1) * c_dim1 + 1], ldc, &v[(*k + 1) * 
+			    v_dim1 + 1], ldv, &c_b14, &work[work_offset], 
+			    ldwork);
+		}
+
+/*              W := W * T  or  W * T**T */
+
+		igraphdtrmm_("Right", "Upper", trans, "Non-unit", m, k, &c_b14, &t[
+			t_offset], ldt, &work[work_offset], ldwork);
+
+/*              C := C - W * V */
+
+		if (*n > *k) {
+
+/*                 C2 := C2 - W * V2 */
+
+		    i__1 = *n - *k;
+		    igraphdgemm_("No transpose", "No transpose", m, &i__1, k, &
+			    c_b25, &work[work_offset], ldwork, &v[(*k + 1) * 
+			    v_dim1 + 1], ldv, &c_b14, &c__[(*k + 1) * c_dim1 
+			    + 1], ldc);
+		}
+
+/*              W := W * V1 */
+
+		igraphdtrmm_("Right", "Upper", "No transpose", "Unit", m, k, &c_b14,
+			 &v[v_offset], ldv, &work[work_offset], ldwork);
+
+/*              C1 := C1 - W */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] -= work[i__ + j * work_dim1];
+/* L170: */
+		    }
+/* L180: */
+		}
+
+	    }
+
+	} else {
+
+/*           Let  V =  ( V1  V2 )    (V2: last K columns)   
+             where  V2  is unit lower triangular. */
+
+	    if (igraphlsame_(side, "L")) {
+
+/*              Form  H * C  or  H**T * C  where  C = ( C1 )   
+                                                      ( C2 )   
+
+                W := C**T * V**T  =  (C1**T * V1**T + C2**T * V2**T) (stored in WORK)   
+
+                W := C2**T */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    igraphdcopy_(n, &c__[*m - *k + j + c_dim1], ldc, &work[j * 
+			    work_dim1 + 1], &c__1);
+/* L190: */
+		}
+
+/*              W := W * V2**T */
+
+		igraphdtrmm_("Right", "Lower", "Transpose", "Unit", n, k, &c_b14, &
+			v[(*m - *k + 1) * v_dim1 + 1], ldv, &work[work_offset]
+			, ldwork);
+		if (*m > *k) {
+
+/*                 W := W + C1**T * V1**T */
+
+		    i__1 = *m - *k;
+		    igraphdgemm_("Transpose", "Transpose", n, k, &i__1, &c_b14, &
+			    c__[c_offset], ldc, &v[v_offset], ldv, &c_b14, &
+			    work[work_offset], ldwork);
+		}
+
+/*              W := W * T**T  or  W * T */
+
+		igraphdtrmm_("Right", "Lower", transt, "Non-unit", n, k, &c_b14, &t[
+			t_offset], ldt, &work[work_offset], ldwork);
+
+/*              C := C - V**T * W**T */
+
+		if (*m > *k) {
+
+/*                 C1 := C1 - V1**T * W**T */
+
+		    i__1 = *m - *k;
+		    igraphdgemm_("Transpose", "Transpose", &i__1, n, k, &c_b25, &v[
+			    v_offset], ldv, &work[work_offset], ldwork, &
+			    c_b14, &c__[c_offset], ldc);
+		}
+
+/*              W := W * V2 */
+
+		igraphdtrmm_("Right", "Lower", "No transpose", "Unit", n, k, &c_b14,
+			 &v[(*m - *k + 1) * v_dim1 + 1], ldv, &work[
+			work_offset], ldwork);
+
+/*              C2 := C2 - W**T */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *n;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[*m - *k + j + i__ * c_dim1] -= work[i__ + j * 
+				work_dim1];
+/* L200: */
+		    }
+/* L210: */
+		}
+
+	    } else if (igraphlsame_(side, "R")) {
+
+/*              Form  C * H  or  C * H'  where  C = ( C1  C2 )   
+
+                W := C * V**T  =  (C1*V1**T + C2*V2**T)  (stored in WORK)   
+
+                W := C2 */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    igraphdcopy_(m, &c__[(*n - *k + j) * c_dim1 + 1], &c__1, &work[
+			    j * work_dim1 + 1], &c__1);
+/* L220: */
+		}
+
+/*              W := W * V2**T */
+
+		igraphdtrmm_("Right", "Lower", "Transpose", "Unit", m, k, &c_b14, &
+			v[(*n - *k + 1) * v_dim1 + 1], ldv, &work[work_offset]
+			, ldwork);
+		if (*n > *k) {
+
+/*                 W := W + C1 * V1**T */
+
+		    i__1 = *n - *k;
+		    igraphdgemm_("No transpose", "Transpose", m, k, &i__1, &c_b14, &
+			    c__[c_offset], ldc, &v[v_offset], ldv, &c_b14, &
+			    work[work_offset], ldwork);
+		}
+
+/*              W := W * T  or  W * T**T */
+
+		igraphdtrmm_("Right", "Lower", trans, "Non-unit", m, k, &c_b14, &t[
+			t_offset], ldt, &work[work_offset], ldwork);
+
+/*              C := C - W * V */
+
+		if (*n > *k) {
+
+/*                 C1 := C1 - W * V1 */
+
+		    i__1 = *n - *k;
+		    igraphdgemm_("No transpose", "No transpose", m, &i__1, k, &
+			    c_b25, &work[work_offset], ldwork, &v[v_offset], 
+			    ldv, &c_b14, &c__[c_offset], ldc);
+		}
+
+/*              W := W * V2 */
+
+		igraphdtrmm_("Right", "Lower", "No transpose", "Unit", m, k, &c_b14,
+			 &v[(*n - *k + 1) * v_dim1 + 1], ldv, &work[
+			work_offset], ldwork);
+
+/*              C1 := C1 - W */
+
+		i__1 = *k;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + (*n - *k + j) * c_dim1] -= work[i__ + j * 
+				work_dim1];
+/* L230: */
+		    }
+/* L240: */
+		}
+
+	    }
+
+	}
+    }
+
+    return 0;
+
+/*     End of DLARFB */
+
+} /* igraphdlarfb_ */
+
diff --git a/igraph/src/dlarfg.c b/igraph/src/dlarfg.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarfg.c
@@ -0,0 +1,217 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARFG generates an elementary reflector (Householder matrix).   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARFG + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarfg.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarfg.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarfg.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARFG( N, ALPHA, X, INCX, TAU )   
+
+         INTEGER            INCX, N   
+         DOUBLE PRECISION   ALPHA, TAU   
+         DOUBLE PRECISION   X( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARFG generates a real elementary reflector H of order n, such   
+   > that   
+   >   
+   >       H * ( alpha ) = ( beta ),   H**T * H = I.   
+   >           (   x   )   (   0  )   
+   >   
+   > where alpha and beta are scalars, and x is an (n-1)-element real   
+   > vector. H is represented in the form   
+   >   
+   >       H = I - tau * ( 1 ) * ( 1 v**T ) ,   
+   >                     ( v )   
+   >   
+   > where tau is a real scalar and v is a real (n-1)-element   
+   > vector.   
+   >   
+   > If the elements of x are all zero, then tau = 0 and H is taken to be   
+   > the unit matrix.   
+   >   
+   > Otherwise  1 <= tau <= 2.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the elementary reflector.   
+   > \endverbatim   
+   >   
+   > \param[in,out] ALPHA   
+   > \verbatim   
+   >          ALPHA is DOUBLE PRECISION   
+   >          On entry, the value alpha.   
+   >          On exit, it is overwritten with the value beta.   
+   > \endverbatim   
+   >   
+   > \param[in,out] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION array, dimension   
+   >                         (1+(N-2)*abs(INCX))   
+   >          On entry, the vector x.   
+   >          On exit, it is overwritten with the vector v.   
+   > \endverbatim   
+   >   
+   > \param[in] INCX   
+   > \verbatim   
+   >          INCX is INTEGER   
+   >          The increment between elements of X. INCX > 0.   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION   
+   >          The value tau.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarfg_(integer *n, doublereal *alpha, doublereal *x, 
+	integer *incx, doublereal *tau)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1;
+
+    /* Builtin functions */
+    double d_sign(doublereal *, doublereal *);
+
+    /* Local variables */
+    integer j, knt;
+    doublereal beta;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    doublereal xnorm;
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *), igraphdlamch_(char *);
+    doublereal safmin, rsafmn;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --x;
+
+    /* Function Body */
+    if (*n <= 1) {
+	*tau = 0.;
+	return 0;
+    }
+
+    i__1 = *n - 1;
+    xnorm = igraphdnrm2_(&i__1, &x[1], incx);
+
+    if (xnorm == 0.) {
+
+/*        H  =  I */
+
+	*tau = 0.;
+    } else {
+
+/*        general case */
+
+	d__1 = igraphdlapy2_(alpha, &xnorm);
+	beta = -d_sign(&d__1, alpha);
+	safmin = igraphdlamch_("S") / igraphdlamch_("E");
+	knt = 0;
+	if (abs(beta) < safmin) {
+
+/*           XNORM, BETA may be inaccurate; scale X and recompute them */
+
+	    rsafmn = 1. / safmin;
+L10:
+	    ++knt;
+	    i__1 = *n - 1;
+	    igraphdscal_(&i__1, &rsafmn, &x[1], incx);
+	    beta *= rsafmn;
+	    *alpha *= rsafmn;
+	    if (abs(beta) < safmin) {
+		goto L10;
+	    }
+
+/*           New BETA is at most 1, at least SAFMIN */
+
+	    i__1 = *n - 1;
+	    xnorm = igraphdnrm2_(&i__1, &x[1], incx);
+	    d__1 = igraphdlapy2_(alpha, &xnorm);
+	    beta = -d_sign(&d__1, alpha);
+	}
+	*tau = (beta - *alpha) / beta;
+	i__1 = *n - 1;
+	d__1 = 1. / (*alpha - beta);
+	igraphdscal_(&i__1, &d__1, &x[1], incx);
+
+/*        If ALPHA is subnormal, it may lose relative accuracy */
+
+	i__1 = knt;
+	for (j = 1; j <= i__1; ++j) {
+	    beta *= safmin;
+/* L20: */
+	}
+	*alpha = beta;
+    }
+
+    return 0;
+
+/*     End of DLARFG */
+
+} /* igraphdlarfg_ */
+
diff --git a/igraph/src/dlarft.c b/igraph/src/dlarft.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarft.c
@@ -0,0 +1,438 @@
+/* dlarft.f -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b8 = 1.;
+
+/* > \brief \b DLARFT forms the triangular factor T of a block reflector H = I - vtvH */
+
+/*  =========== DOCUMENTATION =========== */
+
+/* Online html documentation available at */
+/*            http://www.netlib.org/lapack/explore-html/ */
+
+/* > \htmlonly */
+/* > Download DLARFT + dependencies */
+/* > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarft.
+f"> */
+/* > [TGZ]</a> */
+/* > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarft.
+f"> */
+/* > [ZIP]</a> */
+/* > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarft.
+f"> */
+/* > [TXT]</a> */
+/* > \endhtmlonly */
+
+/*  Definition: */
+/*  =========== */
+
+/*       SUBROUTINE DLARFT( DIRECT, STOREV, N, K, V, LDV, TAU, T, LDT ) */
+
+/*       .. Scalar Arguments .. */
+/*       CHARACTER          DIRECT, STOREV */
+/*       INTEGER            K, LDT, LDV, N */
+/*       .. */
+/*       .. Array Arguments .. */
+/*       DOUBLE PRECISION   T( LDT, * ), TAU( * ), V( LDV, * ) */
+/*       .. */
+
+
+/* > \par Purpose: */
+/*  ============= */
+/* > */
+/* > \verbatim */
+/* > */
+/* > DLARFT forms the triangular factor T of a real block reflector H */
+/* > of order n, which is defined as a product of k elementary reflectors. */
+/* > */
+/* > If DIRECT = 'F', H = H(1) H(2) . . . H(k) and T is upper triangular; */
+/* > */
+/* > If DIRECT = 'B', H = H(k) . . . H(2) H(1) and T is lower triangular. */
+/* > */
+/* > If STOREV = 'C', the vector which defines the elementary reflector */
+/* > H(i) is stored in the i-th column of the array V, and */
+/* > */
+/* >    H  =  I - V * T * V**T */
+/* > */
+/* > If STOREV = 'R', the vector which defines the elementary reflector */
+/* > H(i) is stored in the i-th row of the array V, and */
+/* > */
+/* >    H  =  I - V**T * T * V */
+/* > \endverbatim */
+
+/*  Arguments: */
+/*  ========== */
+
+/* > \param[in] DIRECT */
+/* > \verbatim */
+/* >          DIRECT is CHARACTER*1 */
+/* >          Specifies the order in which the elementary reflectors are */
+/* >          multiplied to form the block reflector: */
+/* >          = 'F': H = H(1) H(2) . . . H(k) (Forward) */
+/* >          = 'B': H = H(k) . . . H(2) H(1) (Backward) */
+/* > \endverbatim */
+/* > */
+/* > \param[in] STOREV */
+/* > \verbatim */
+/* >          STOREV is CHARACTER*1 */
+/* >          Specifies how the vectors which define the elementary */
+/* >          reflectors are stored (see also Further Details): */
+/* >          = 'C': columnwise */
+/* >          = 'R': rowwise */
+/* > \endverbatim */
+/* > */
+/* > \param[in] N */
+/* > \verbatim */
+/* >          N is INTEGER */
+/* >          The order of the block reflector H. N >= 0. */
+/* > \endverbatim */
+/* > */
+/* > \param[in] K */
+/* > \verbatim */
+/* >          K is INTEGER */
+/* >          The order of the triangular factor T (= the number of */
+/* >          elementary reflectors). K >= 1. */
+/* > \endverbatim */
+/* > */
+/* > \param[in] V */
+/* > \verbatim */
+/* >          V is DOUBLE PRECISION array, dimension */
+/* >                               (LDV,K) if STOREV = 'C' */
+/* >                               (LDV,N) if STOREV = 'R' */
+/* >          The matrix V. See further details. */
+/* > \endverbatim */
+/* > */
+/* > \param[in] LDV */
+/* > \verbatim */
+/* >          LDV is INTEGER */
+/* >          The leading dimension of the array V. */
+/* >          If STOREV = 'C', LDV >= max(1,N); if STOREV = 'R', LDV >= K. */
+/* > \endverbatim */
+/* > */
+/* > \param[in] TAU */
+/* > \verbatim */
+/* >          TAU is DOUBLE PRECISION array, dimension (K) */
+/* >          TAU(i) must contain the scalar factor of the elementary */
+/* >          reflector H(i). */
+/* > \endverbatim */
+/* > */
+/* > \param[out] T */
+/* > \verbatim */
+/* >          T is DOUBLE PRECISION array, dimension (LDT,K) */
+/* >          The k by k triangular factor T of the block reflector. */
+/* >          If DIRECT = 'F', T is upper triangular; if DIRECT = 'B', T is */
+/* >          lower triangular. The rest of the array is not used. */
+/* > \endverbatim */
+/* > */
+/* > \param[in] LDT */
+/* > \verbatim */
+/* >          LDT is INTEGER */
+/* >          The leading dimension of the array T. LDT >= K. */
+/* > \endverbatim */
+
+/*  Authors: */
+/*  ======== */
+
+/* > \author Univ. of Tennessee */
+/* > \author Univ. of California Berkeley */
+/* > \author Univ. of Colorado Denver */
+/* > \author NAG Ltd. */
+
+/* > \date September 2012 */
+
+/* > \ingroup doubleOTHERauxiliary */
+
+/* > \par Further Details: */
+/*  ===================== */
+/* > */
+/* > \verbatim */
+/* > */
+/* >  The shape of the matrix V and the storage of the vectors which define */
+/* >  the H(i) is best illustrated by the following example with n = 5 and */
+/* >  k = 3. The elements equal to 1 are not stored. */
+/* > */
+/* >  DIRECT = 'F' and STOREV = 'C':         DIRECT = 'F' and STOREV = 'R': */
+/* > */
+/* >               V = (  1       )                 V = (  1 v1 v1 v1 v1 ) */
+/* >                   ( v1  1    )                     (     1 v2 v2 v2 ) */
+/* >                   ( v1 v2  1 )                     (        1 v3 v3 ) */
+/* >                   ( v1 v2 v3 ) */
+/* >                   ( v1 v2 v3 ) */
+/* > */
+/* >  DIRECT = 'B' and STOREV = 'C':         DIRECT = 'B' and STOREV = 'R': */
+/* > */
+/* >               V = ( v1 v2 v3 )                 V = ( v1 v1  1       ) */
+/* >                   ( v1 v2 v3 )                     ( v2 v2 v2  1    ) */
+/* >                   (  1 v2 v3 )                     ( v3 v3 v3 v3  1 ) */
+/* >                   (     1 v3 ) */
+/* >                   (        1 ) */
+/* > \endverbatim */
+/* > */
+/*  ===================================================================== */
+/* Subroutine */ int igraphdlarft_(char *direct, char *storev, integer *n, integer *
+	k, doublereal *v, integer *ldv, doublereal *tau, doublereal *t, 
+	integer *ldt)
+{
+    /* System generated locals */
+    integer t_dim1, t_offset, v_dim1, v_offset, i__1, i__2, i__3;
+    doublereal d__1;
+
+    /* Local variables */
+    integer i__, j, prevlastv;
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *);
+    integer lastv;
+    extern /* Subroutine */ int igraphdtrmv_(char *, char *, char *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) -- */
+/*  -- LAPACK is a software package provided by Univ. of Tennessee,    -- */
+/*  -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- */
+/*     September 2012 */
+
+/*     .. Scalar Arguments .. */
+/*     .. */
+/*     .. Array Arguments .. */
+/*     .. */
+
+/*  ===================================================================== */
+
+/*     .. Parameters .. */
+/*     .. */
+/*     .. Local Scalars .. */
+/*     .. */
+/*     .. External Subroutines .. */
+/*     .. */
+/*     .. External Functions .. */
+/*     .. */
+/*     .. Executable Statements .. */
+
+/*     Quick return if possible */
+
+    /* Parameter adjustments */
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    --tau;
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+
+    /* Function Body */
+    if (*n == 0) {
+	return 0;
+    }
+
+    if (igraphlsame_(direct, "F")) {
+	prevlastv = *n;
+	i__1 = *k;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    prevlastv = max(i__,prevlastv);
+	    if (tau[i__] == 0.) {
+
+/*              H(i)  =  I */
+
+		i__2 = i__;
+		for (j = 1; j <= i__2; ++j) {
+		    t[j + i__ * t_dim1] = 0.;
+		}
+	    } else {
+
+/*              general case */
+
+		if (igraphlsame_(storev, "C")) {
+/*                 Skip any trailing zeros. */
+		    lastv = *n;
+L14:
+		    if (v[lastv + i__ * v_dim1] != 0.) {
+			goto L15;
+		    }
+		    if (lastv == i__ + 1) {
+			goto L15;
+		    }
+		    --lastv;
+		    goto L14;
+L15:
+/*                 DO LASTV = N, I+1, -1 */
+/*                    IF( V( LASTV, I ).NE.ZERO ) EXIT */
+/*                 END DO */
+		    i__2 = i__ - 1;
+		    for (j = 1; j <= i__2; ++j) {
+			t[j + i__ * t_dim1] = -tau[i__] * v[i__ + j * v_dim1];
+		    }
+		    j = min(lastv,prevlastv);
+
+/*                 T(1:i-1,i) := - tau(i) * V(i:j,1:i-1)**T * V(i:j,i) */
+
+		    i__2 = j - i__;
+		    i__3 = i__ - 1;
+		    d__1 = -tau[i__];
+		    igraphdgemv_("Transpose", &i__2, &i__3, &d__1, &v[i__ + 1 + 
+			    v_dim1], ldv, &v[i__ + 1 + i__ * v_dim1], &c__1, &
+			    c_b8, &t[i__ * t_dim1 + 1], &c__1);
+		} else {
+/*                 Skip any trailing zeros. */
+		    lastv = *n;
+L16:
+		    if (v[i__ + lastv * v_dim1] != 0.) {
+			goto L17;
+		    }
+		    if (lastv == i__ + 1) {
+			goto L17;
+		    }
+		    --lastv;
+		    goto L16;
+L17:
+/*                 DO LASTV = N, I+1, -1 */
+/*                    IF( V( I, LASTV ).NE.ZERO ) EXIT */
+/*                 END DO */
+		    i__2 = i__ - 1;
+		    for (j = 1; j <= i__2; ++j) {
+			t[j + i__ * t_dim1] = -tau[i__] * v[j + i__ * v_dim1];
+		    }
+		    j = min(lastv,prevlastv);
+
+/*                 T(1:i-1,i) := - tau(i) * V(1:i-1,i:j) * V(i,i:j)**T */
+
+		    i__2 = i__ - 1;
+		    i__3 = j - i__;
+		    d__1 = -tau[i__];
+		    igraphdgemv_("No transpose", &i__2, &i__3, &d__1, &v[(i__ + 1) *
+			     v_dim1 + 1], ldv, &v[i__ + (i__ + 1) * v_dim1], 
+			    ldv, &c_b8, &t[i__ * t_dim1 + 1], &c__1);
+		}
+
+/*              T(1:i-1,i) := T(1:i-1,1:i-1) * T(1:i-1,i) */
+
+		i__2 = i__ - 1;
+		igraphdtrmv_("Upper", "No transpose", "Non-unit", &i__2, &t[
+			t_offset], ldt, &t[i__ * t_dim1 + 1], &c__1);
+		t[i__ + i__ * t_dim1] = tau[i__];
+		if (i__ > 1) {
+		    prevlastv = max(prevlastv,lastv);
+		} else {
+		    prevlastv = lastv;
+		}
+	    }
+	}
+    } else {
+	prevlastv = 1;
+	for (i__ = *k; i__ >= 1; --i__) {
+	    if (tau[i__] == 0.) {
+
+/*              H(i)  =  I */
+
+		i__1 = *k;
+		for (j = i__; j <= i__1; ++j) {
+		    t[j + i__ * t_dim1] = 0.;
+		}
+	    } else {
+
+/*              general case */
+
+		if (i__ < *k) {
+		    if (igraphlsame_(storev, "C")) {
+/*                    Skip any leading zeros. */
+			lastv = 1;
+L34:
+			if (v[lastv + i__ * v_dim1] != 0.) {
+			    goto L35;
+			}
+			if (lastv == i__ - 1) {
+			    goto L35;
+			}
+			++lastv;
+			goto L34;
+L35:
+/*                    DO LASTV = 1, I-1 */
+/*                       IF( V( LASTV, I ).NE.ZERO ) EXIT */
+/*                    END DO */
+			i__1 = *k;
+			for (j = i__ + 1; j <= i__1; ++j) {
+			    t[j + i__ * t_dim1] = -tau[i__] * v[*n - *k + i__ 
+				    + j * v_dim1];
+			}
+			j = max(lastv,prevlastv);
+
+/*                    T(i+1:k,i) = -tau(i) * V(j:n-k+i,i+1:k)**T * V(j:n-k+i,i) */
+
+			i__1 = *n - *k + i__ - j;
+			i__2 = *k - i__;
+			d__1 = -tau[i__];
+			igraphdgemv_("Transpose", &i__1, &i__2, &d__1, &v[j + (i__ 
+				+ 1) * v_dim1], ldv, &v[j + i__ * v_dim1], &
+				c__1, &c_b8, &t[i__ + 1 + i__ * t_dim1], &
+				c__1);
+		    } else {
+/*                    Skip any leading zeros. */
+			lastv = 1;
+/* L36: */
+			if (v[i__ + lastv * v_dim1] != 0.) {
+			    goto L37;
+			}
+			if (lastv == i__ - 1) {
+			    goto L37;
+			}
+			++lastv;
+L37:
+/*                    DO LASTV = 1, I-1 */
+/*                       IF( V( I, LASTV ).NE.ZERO ) EXIT */
+/*                    END DO */
+			i__1 = *k;
+			for (j = i__ + 1; j <= i__1; ++j) {
+			    t[j + i__ * t_dim1] = -tau[i__] * v[j + (*n - *k 
+				    + i__) * v_dim1];
+			}
+			j = max(lastv,prevlastv);
+
+/*                    T(i+1:k,i) = -tau(i) * V(i+1:k,j:n-k+i) * V(i,j:n-k+i)**T */
+
+			i__1 = *k - i__;
+			i__2 = *n - *k + i__ - j;
+			d__1 = -tau[i__];
+			igraphdgemv_("No transpose", &i__1, &i__2, &d__1, &v[i__ + 
+				1 + j * v_dim1], ldv, &v[i__ + j * v_dim1], 
+				ldv, &c_b8, &t[i__ + 1 + i__ * t_dim1], &c__1
+				 );
+		    }
+
+/*                 T(i+1:k,i) := T(i+1:k,i+1:k) * T(i+1:k,i) */
+
+		    i__1 = *k - i__;
+		    igraphdtrmv_("Lower", "No transpose", "Non-unit", &i__1, &t[i__ 
+			    + 1 + (i__ + 1) * t_dim1], ldt, &t[i__ + 1 + i__ *
+			     t_dim1], &c__1)
+			    ;
+		    if (i__ > 1) {
+			prevlastv = min(prevlastv,lastv);
+		    } else {
+			prevlastv = lastv;
+		    }
+		}
+		t[i__ + i__ * t_dim1] = tau[i__];
+	    }
+	}
+    }
+    return 0;
+
+/*     End of DLARFT */
+
+} /* dlarft_ */
+
diff --git a/igraph/src/dlarfx.c b/igraph/src/dlarfx.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarfx.c
@@ -0,0 +1,790 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DLARFX applies an elementary reflector to a general rectangular matrix, with loop unrolling whe
+n the reflector has order ≤ 10.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARFX + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarfx.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarfx.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarfx.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARFX( SIDE, M, N, V, TAU, C, LDC, WORK )   
+
+         CHARACTER          SIDE   
+         INTEGER            LDC, M, N   
+         DOUBLE PRECISION   TAU   
+         DOUBLE PRECISION   C( LDC, * ), V( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARFX applies a real elementary reflector H to a real m by n   
+   > matrix C, from either the left or the right. H is represented in the   
+   > form   
+   >   
+   >       H = I - tau * v * v**T   
+   >   
+   > where tau is a real scalar and v is a real vector.   
+   >   
+   > If tau = 0, then H is taken to be the unit matrix   
+   >   
+   > This version uses inline code if H has order < 11.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': form  H * C   
+   >          = 'R': form  C * H   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C.   
+   > \endverbatim   
+   >   
+   > \param[in] V   
+   > \verbatim   
+   >          V is DOUBLE PRECISION array, dimension (M) if SIDE = 'L'   
+   >                                     or (N) if SIDE = 'R'   
+   >          The vector v in the representation of H.   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION   
+   >          The value tau in the representation of H.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the m by n matrix C.   
+   >          On exit, C is overwritten by the matrix H * C if SIDE = 'L',   
+   >          or C * H if SIDE = 'R'.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDA >= (1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension   
+   >                      (N) if SIDE = 'L'   
+   >                      or (M) if SIDE = 'R'   
+   >          WORK is not referenced if H has order < 11.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarfx_(char *side, integer *m, integer *n, doublereal *
+	v, doublereal *tau, doublereal *c__, integer *ldc, doublereal *work)
+{
+    /* System generated locals */
+    integer c_dim1, c_offset, i__1;
+
+    /* Local variables */
+    integer j;
+    doublereal t1, t2, t3, t4, t5, t6, t7, t8, t9, v1, v2, v3, v4, v5, v6, v7,
+	     v8, v9, t10, v10, sum;
+    extern /* Subroutine */ int igraphdlarf_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *);
+    extern logical igraphlsame_(char *, char *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --v;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    --work;
+
+    /* Function Body */
+    if (*tau == 0.) {
+	return 0;
+    }
+    if (igraphlsame_(side, "L")) {
+
+/*        Form  H * C, where H has order m. */
+
+	switch (*m) {
+	    case 1:  goto L10;
+	    case 2:  goto L30;
+	    case 3:  goto L50;
+	    case 4:  goto L70;
+	    case 5:  goto L90;
+	    case 6:  goto L110;
+	    case 7:  goto L130;
+	    case 8:  goto L150;
+	    case 9:  goto L170;
+	    case 10:  goto L190;
+	}
+
+/*        Code for general M */
+
+	igraphdlarf_(side, m, n, &v[1], &c__1, tau, &c__[c_offset], ldc, &work[1]);
+	goto L410;
+L10:
+
+/*        Special code for 1 x 1 Householder */
+
+	t1 = 1. - *tau * v[1] * v[1];
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    c__[j * c_dim1 + 1] = t1 * c__[j * c_dim1 + 1];
+/* L20: */
+	}
+	goto L410;
+L30:
+
+/*        Special code for 2 x 2 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+/* L40: */
+	}
+	goto L410;
+L50:
+
+/*        Special code for 3 x 3 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2] + v3 * 
+		    c__[j * c_dim1 + 3];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+	    c__[j * c_dim1 + 3] -= sum * t3;
+/* L60: */
+	}
+	goto L410;
+L70:
+
+/*        Special code for 4 x 4 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2] + v3 * 
+		    c__[j * c_dim1 + 3] + v4 * c__[j * c_dim1 + 4];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+	    c__[j * c_dim1 + 3] -= sum * t3;
+	    c__[j * c_dim1 + 4] -= sum * t4;
+/* L80: */
+	}
+	goto L410;
+L90:
+
+/*        Special code for 5 x 5 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2] + v3 * 
+		    c__[j * c_dim1 + 3] + v4 * c__[j * c_dim1 + 4] + v5 * c__[
+		    j * c_dim1 + 5];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+	    c__[j * c_dim1 + 3] -= sum * t3;
+	    c__[j * c_dim1 + 4] -= sum * t4;
+	    c__[j * c_dim1 + 5] -= sum * t5;
+/* L100: */
+	}
+	goto L410;
+L110:
+
+/*        Special code for 6 x 6 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2] + v3 * 
+		    c__[j * c_dim1 + 3] + v4 * c__[j * c_dim1 + 4] + v5 * c__[
+		    j * c_dim1 + 5] + v6 * c__[j * c_dim1 + 6];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+	    c__[j * c_dim1 + 3] -= sum * t3;
+	    c__[j * c_dim1 + 4] -= sum * t4;
+	    c__[j * c_dim1 + 5] -= sum * t5;
+	    c__[j * c_dim1 + 6] -= sum * t6;
+/* L120: */
+	}
+	goto L410;
+L130:
+
+/*        Special code for 7 x 7 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	v7 = v[7];
+	t7 = *tau * v7;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2] + v3 * 
+		    c__[j * c_dim1 + 3] + v4 * c__[j * c_dim1 + 4] + v5 * c__[
+		    j * c_dim1 + 5] + v6 * c__[j * c_dim1 + 6] + v7 * c__[j * 
+		    c_dim1 + 7];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+	    c__[j * c_dim1 + 3] -= sum * t3;
+	    c__[j * c_dim1 + 4] -= sum * t4;
+	    c__[j * c_dim1 + 5] -= sum * t5;
+	    c__[j * c_dim1 + 6] -= sum * t6;
+	    c__[j * c_dim1 + 7] -= sum * t7;
+/* L140: */
+	}
+	goto L410;
+L150:
+
+/*        Special code for 8 x 8 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	v7 = v[7];
+	t7 = *tau * v7;
+	v8 = v[8];
+	t8 = *tau * v8;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2] + v3 * 
+		    c__[j * c_dim1 + 3] + v4 * c__[j * c_dim1 + 4] + v5 * c__[
+		    j * c_dim1 + 5] + v6 * c__[j * c_dim1 + 6] + v7 * c__[j * 
+		    c_dim1 + 7] + v8 * c__[j * c_dim1 + 8];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+	    c__[j * c_dim1 + 3] -= sum * t3;
+	    c__[j * c_dim1 + 4] -= sum * t4;
+	    c__[j * c_dim1 + 5] -= sum * t5;
+	    c__[j * c_dim1 + 6] -= sum * t6;
+	    c__[j * c_dim1 + 7] -= sum * t7;
+	    c__[j * c_dim1 + 8] -= sum * t8;
+/* L160: */
+	}
+	goto L410;
+L170:
+
+/*        Special code for 9 x 9 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	v7 = v[7];
+	t7 = *tau * v7;
+	v8 = v[8];
+	t8 = *tau * v8;
+	v9 = v[9];
+	t9 = *tau * v9;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2] + v3 * 
+		    c__[j * c_dim1 + 3] + v4 * c__[j * c_dim1 + 4] + v5 * c__[
+		    j * c_dim1 + 5] + v6 * c__[j * c_dim1 + 6] + v7 * c__[j * 
+		    c_dim1 + 7] + v8 * c__[j * c_dim1 + 8] + v9 * c__[j * 
+		    c_dim1 + 9];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+	    c__[j * c_dim1 + 3] -= sum * t3;
+	    c__[j * c_dim1 + 4] -= sum * t4;
+	    c__[j * c_dim1 + 5] -= sum * t5;
+	    c__[j * c_dim1 + 6] -= sum * t6;
+	    c__[j * c_dim1 + 7] -= sum * t7;
+	    c__[j * c_dim1 + 8] -= sum * t8;
+	    c__[j * c_dim1 + 9] -= sum * t9;
+/* L180: */
+	}
+	goto L410;
+L190:
+
+/*        Special code for 10 x 10 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	v7 = v[7];
+	t7 = *tau * v7;
+	v8 = v[8];
+	t8 = *tau * v8;
+	v9 = v[9];
+	t9 = *tau * v9;
+	v10 = v[10];
+	t10 = *tau * v10;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j * c_dim1 + 1] + v2 * c__[j * c_dim1 + 2] + v3 * 
+		    c__[j * c_dim1 + 3] + v4 * c__[j * c_dim1 + 4] + v5 * c__[
+		    j * c_dim1 + 5] + v6 * c__[j * c_dim1 + 6] + v7 * c__[j * 
+		    c_dim1 + 7] + v8 * c__[j * c_dim1 + 8] + v9 * c__[j * 
+		    c_dim1 + 9] + v10 * c__[j * c_dim1 + 10];
+	    c__[j * c_dim1 + 1] -= sum * t1;
+	    c__[j * c_dim1 + 2] -= sum * t2;
+	    c__[j * c_dim1 + 3] -= sum * t3;
+	    c__[j * c_dim1 + 4] -= sum * t4;
+	    c__[j * c_dim1 + 5] -= sum * t5;
+	    c__[j * c_dim1 + 6] -= sum * t6;
+	    c__[j * c_dim1 + 7] -= sum * t7;
+	    c__[j * c_dim1 + 8] -= sum * t8;
+	    c__[j * c_dim1 + 9] -= sum * t9;
+	    c__[j * c_dim1 + 10] -= sum * t10;
+/* L200: */
+	}
+	goto L410;
+    } else {
+
+/*        Form  C * H, where H has order n. */
+
+	switch (*n) {
+	    case 1:  goto L210;
+	    case 2:  goto L230;
+	    case 3:  goto L250;
+	    case 4:  goto L270;
+	    case 5:  goto L290;
+	    case 6:  goto L310;
+	    case 7:  goto L330;
+	    case 8:  goto L350;
+	    case 9:  goto L370;
+	    case 10:  goto L390;
+	}
+
+/*        Code for general N */
+
+	igraphdlarf_(side, m, n, &v[1], &c__1, tau, &c__[c_offset], ldc, &work[1]);
+	goto L410;
+L210:
+
+/*        Special code for 1 x 1 Householder */
+
+	t1 = 1. - *tau * v[1] * v[1];
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    c__[j + c_dim1] = t1 * c__[j + c_dim1];
+/* L220: */
+	}
+	goto L410;
+L230:
+
+/*        Special code for 2 x 2 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+/* L240: */
+	}
+	goto L410;
+L250:
+
+/*        Special code for 3 x 3 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)] + v3 * 
+		    c__[j + c_dim1 * 3];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+	    c__[j + c_dim1 * 3] -= sum * t3;
+/* L260: */
+	}
+	goto L410;
+L270:
+
+/*        Special code for 4 x 4 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)] + v3 * 
+		    c__[j + c_dim1 * 3] + v4 * c__[j + (c_dim1 << 2)];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+	    c__[j + c_dim1 * 3] -= sum * t3;
+	    c__[j + (c_dim1 << 2)] -= sum * t4;
+/* L280: */
+	}
+	goto L410;
+L290:
+
+/*        Special code for 5 x 5 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)] + v3 * 
+		    c__[j + c_dim1 * 3] + v4 * c__[j + (c_dim1 << 2)] + v5 * 
+		    c__[j + c_dim1 * 5];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+	    c__[j + c_dim1 * 3] -= sum * t3;
+	    c__[j + (c_dim1 << 2)] -= sum * t4;
+	    c__[j + c_dim1 * 5] -= sum * t5;
+/* L300: */
+	}
+	goto L410;
+L310:
+
+/*        Special code for 6 x 6 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)] + v3 * 
+		    c__[j + c_dim1 * 3] + v4 * c__[j + (c_dim1 << 2)] + v5 * 
+		    c__[j + c_dim1 * 5] + v6 * c__[j + c_dim1 * 6];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+	    c__[j + c_dim1 * 3] -= sum * t3;
+	    c__[j + (c_dim1 << 2)] -= sum * t4;
+	    c__[j + c_dim1 * 5] -= sum * t5;
+	    c__[j + c_dim1 * 6] -= sum * t6;
+/* L320: */
+	}
+	goto L410;
+L330:
+
+/*        Special code for 7 x 7 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	v7 = v[7];
+	t7 = *tau * v7;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)] + v3 * 
+		    c__[j + c_dim1 * 3] + v4 * c__[j + (c_dim1 << 2)] + v5 * 
+		    c__[j + c_dim1 * 5] + v6 * c__[j + c_dim1 * 6] + v7 * c__[
+		    j + c_dim1 * 7];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+	    c__[j + c_dim1 * 3] -= sum * t3;
+	    c__[j + (c_dim1 << 2)] -= sum * t4;
+	    c__[j + c_dim1 * 5] -= sum * t5;
+	    c__[j + c_dim1 * 6] -= sum * t6;
+	    c__[j + c_dim1 * 7] -= sum * t7;
+/* L340: */
+	}
+	goto L410;
+L350:
+
+/*        Special code for 8 x 8 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	v7 = v[7];
+	t7 = *tau * v7;
+	v8 = v[8];
+	t8 = *tau * v8;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)] + v3 * 
+		    c__[j + c_dim1 * 3] + v4 * c__[j + (c_dim1 << 2)] + v5 * 
+		    c__[j + c_dim1 * 5] + v6 * c__[j + c_dim1 * 6] + v7 * c__[
+		    j + c_dim1 * 7] + v8 * c__[j + (c_dim1 << 3)];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+	    c__[j + c_dim1 * 3] -= sum * t3;
+	    c__[j + (c_dim1 << 2)] -= sum * t4;
+	    c__[j + c_dim1 * 5] -= sum * t5;
+	    c__[j + c_dim1 * 6] -= sum * t6;
+	    c__[j + c_dim1 * 7] -= sum * t7;
+	    c__[j + (c_dim1 << 3)] -= sum * t8;
+/* L360: */
+	}
+	goto L410;
+L370:
+
+/*        Special code for 9 x 9 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	v7 = v[7];
+	t7 = *tau * v7;
+	v8 = v[8];
+	t8 = *tau * v8;
+	v9 = v[9];
+	t9 = *tau * v9;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)] + v3 * 
+		    c__[j + c_dim1 * 3] + v4 * c__[j + (c_dim1 << 2)] + v5 * 
+		    c__[j + c_dim1 * 5] + v6 * c__[j + c_dim1 * 6] + v7 * c__[
+		    j + c_dim1 * 7] + v8 * c__[j + (c_dim1 << 3)] + v9 * c__[
+		    j + c_dim1 * 9];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+	    c__[j + c_dim1 * 3] -= sum * t3;
+	    c__[j + (c_dim1 << 2)] -= sum * t4;
+	    c__[j + c_dim1 * 5] -= sum * t5;
+	    c__[j + c_dim1 * 6] -= sum * t6;
+	    c__[j + c_dim1 * 7] -= sum * t7;
+	    c__[j + (c_dim1 << 3)] -= sum * t8;
+	    c__[j + c_dim1 * 9] -= sum * t9;
+/* L380: */
+	}
+	goto L410;
+L390:
+
+/*        Special code for 10 x 10 Householder */
+
+	v1 = v[1];
+	t1 = *tau * v1;
+	v2 = v[2];
+	t2 = *tau * v2;
+	v3 = v[3];
+	t3 = *tau * v3;
+	v4 = v[4];
+	t4 = *tau * v4;
+	v5 = v[5];
+	t5 = *tau * v5;
+	v6 = v[6];
+	t6 = *tau * v6;
+	v7 = v[7];
+	t7 = *tau * v7;
+	v8 = v[8];
+	t8 = *tau * v8;
+	v9 = v[9];
+	t9 = *tau * v9;
+	v10 = v[10];
+	t10 = *tau * v10;
+	i__1 = *m;
+	for (j = 1; j <= i__1; ++j) {
+	    sum = v1 * c__[j + c_dim1] + v2 * c__[j + (c_dim1 << 1)] + v3 * 
+		    c__[j + c_dim1 * 3] + v4 * c__[j + (c_dim1 << 2)] + v5 * 
+		    c__[j + c_dim1 * 5] + v6 * c__[j + c_dim1 * 6] + v7 * c__[
+		    j + c_dim1 * 7] + v8 * c__[j + (c_dim1 << 3)] + v9 * c__[
+		    j + c_dim1 * 9] + v10 * c__[j + c_dim1 * 10];
+	    c__[j + c_dim1] -= sum * t1;
+	    c__[j + (c_dim1 << 1)] -= sum * t2;
+	    c__[j + c_dim1 * 3] -= sum * t3;
+	    c__[j + (c_dim1 << 2)] -= sum * t4;
+	    c__[j + c_dim1 * 5] -= sum * t5;
+	    c__[j + c_dim1 * 6] -= sum * t6;
+	    c__[j + c_dim1 * 7] -= sum * t7;
+	    c__[j + (c_dim1 << 3)] -= sum * t8;
+	    c__[j + c_dim1 * 9] -= sum * t9;
+	    c__[j + c_dim1 * 10] -= sum * t10;
+/* L400: */
+	}
+	goto L410;
+    }
+L410:
+    return 0;
+
+/*     End of DLARFX */
+
+} /* igraphdlarfx_ */
+
diff --git a/igraph/src/dlarnv.c b/igraph/src/dlarnv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarnv.c
@@ -0,0 +1,193 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARNV returns a vector of random numbers from a uniform or normal distribution.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARNV + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarnv.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarnv.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarnv.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARNV( IDIST, ISEED, N, X )   
+
+         INTEGER            IDIST, N   
+         INTEGER            ISEED( 4 )   
+         DOUBLE PRECISION   X( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARNV returns a vector of n random real numbers from a uniform or   
+   > normal distribution.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] IDIST   
+   > \verbatim   
+   >          IDIST is INTEGER   
+   >          Specifies the distribution of the random numbers:   
+   >          = 1:  uniform (0,1)   
+   >          = 2:  uniform (-1,1)   
+   >          = 3:  normal (0,1)   
+   > \endverbatim   
+   >   
+   > \param[in,out] ISEED   
+   > \verbatim   
+   >          ISEED is INTEGER array, dimension (4)   
+   >          On entry, the seed of the random number generator; the array   
+   >          elements must be between 0 and 4095, and ISEED(4) must be   
+   >          odd.   
+   >          On exit, the seed is updated.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of random numbers to be generated.   
+   > \endverbatim   
+   >   
+   > \param[out] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION array, dimension (N)   
+   >          The generated random numbers.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  This routine calls the auxiliary routine DLARUV to generate random   
+   >  real numbers from a uniform (0,1) distribution, in batches of up to   
+   >  128 using vectorisable code. The Box-Muller method is used to   
+   >  transform numbers from a uniform to a normal distribution.   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlarnv_(integer *idist, integer *iseed, integer *n, 
+	doublereal *x)
+{
+    /* System generated locals */
+    integer i__1, i__2, i__3;
+
+    /* Builtin functions */
+    double log(doublereal), sqrt(doublereal), cos(doublereal);
+
+    /* Local variables */
+    integer i__;
+    doublereal u[128];
+    integer il, iv, il2;
+    extern /* Subroutine */ int igraphdlaruv_(integer *, integer *, doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --x;
+    --iseed;
+
+    /* Function Body */
+    i__1 = *n;
+    for (iv = 1; iv <= i__1; iv += 64) {
+/* Computing MIN */
+	i__2 = 64, i__3 = *n - iv + 1;
+	il = min(i__2,i__3);
+	if (*idist == 3) {
+	    il2 = il << 1;
+	} else {
+	    il2 = il;
+	}
+
+/*        Call DLARUV to generate IL2 numbers from a uniform (0,1)   
+          distribution (IL2 <= LV) */
+
+	igraphdlaruv_(&iseed[1], &il2, u);
+
+	if (*idist == 1) {
+
+/*           Copy generated numbers */
+
+	    i__2 = il;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		x[iv + i__ - 1] = u[i__ - 1];
+/* L10: */
+	    }
+	} else if (*idist == 2) {
+
+/*           Convert generated numbers to uniform (-1,1) distribution */
+
+	    i__2 = il;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		x[iv + i__ - 1] = u[i__ - 1] * 2. - 1.;
+/* L20: */
+	    }
+	} else if (*idist == 3) {
+
+/*           Convert generated numbers to normal (0,1) distribution */
+
+	    i__2 = il;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		x[iv + i__ - 1] = sqrt(log(u[(i__ << 1) - 2]) * -2.) * cos(u[(
+			i__ << 1) - 1] * 6.2831853071795864769252867663);
+/* L30: */
+	    }
+	}
+/* L40: */
+    }
+    return 0;
+
+/*     End of DLARNV */
+
+} /* igraphdlarnv_ */
+
diff --git a/igraph/src/dlarra.c b/igraph/src/dlarra.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarra.c
@@ -0,0 +1,219 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARRA computes the splitting points with the specified threshold.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRA + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarra.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarra.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarra.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRA( N, D, E, E2, SPLTOL, TNRM,   
+                             NSPLIT, ISPLIT, INFO )   
+
+         INTEGER            INFO, N, NSPLIT   
+         DOUBLE PRECISION    SPLTOL, TNRM   
+         INTEGER            ISPLIT( * )   
+         DOUBLE PRECISION   D( * ), E( * ), E2( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > Compute the splitting points with threshold SPLTOL.   
+   > DLARRA sets any "small" off-diagonal elements to zero.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix. N > 0.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the N diagonal elements of the tridiagonal   
+   >          matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in,out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the first (N-1) entries contain the subdiagonal   
+   >          elements of the tridiagonal matrix T; E(N) need not be set.   
+   >          On exit, the entries E( ISPLIT( I ) ), 1 <= I <= NSPLIT,   
+   >          are set to zero, the other entries of E are untouched.   
+   > \endverbatim   
+   >   
+   > \param[in,out] E2   
+   > \verbatim   
+   >          E2 is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the first (N-1) entries contain the SQUARES of the   
+   >          subdiagonal elements of the tridiagonal matrix T;   
+   >          E2(N) need not be set.   
+   >          On exit, the entries E2( ISPLIT( I ) ),   
+   >          1 <= I <= NSPLIT, have been set to zero   
+   > \endverbatim   
+   >   
+   > \param[in] SPLTOL   
+   > \verbatim   
+   >          SPLTOL is DOUBLE PRECISION   
+   >          The threshold for splitting. Two criteria can be used:   
+   >          SPLTOL<0 : criterion based on absolute off-diagonal value   
+   >          SPLTOL>0 : criterion that preserves relative accuracy   
+   > \endverbatim   
+   >   
+   > \param[in] TNRM   
+   > \verbatim   
+   >          TNRM is DOUBLE PRECISION   
+   >          The norm of the matrix.   
+   > \endverbatim   
+   >   
+   > \param[out] NSPLIT   
+   > \verbatim   
+   >          NSPLIT is INTEGER   
+   >          The number of blocks T splits into. 1 <= NSPLIT <= N.   
+   > \endverbatim   
+   >   
+   > \param[out] ISPLIT   
+   > \verbatim   
+   >          ISPLIT is INTEGER array, dimension (N)   
+   >          The splitting points, at which T breaks up into blocks.   
+   >          The first block consists of rows/columns 1 to ISPLIT(1),   
+   >          the second of rows/columns ISPLIT(1)+1 through ISPLIT(2),   
+   >          etc., and the NSPLIT-th consists of rows/columns   
+   >          ISPLIT(NSPLIT-1)+1 through ISPLIT(NSPLIT)=N.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarra_(integer *n, doublereal *d__, doublereal *e, 
+	doublereal *e2, doublereal *spltol, doublereal *tnrm, integer *nsplit,
+	 integer *isplit, integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__;
+    doublereal tmp1, eabs;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --isplit;
+    --e2;
+    --e;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+/*     Compute splitting points */
+    *nsplit = 1;
+    if (*spltol < 0.) {
+/*        Criterion based on absolute off-diagonal value */
+	tmp1 = abs(*spltol) * *tnrm;
+	i__1 = *n - 1;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    eabs = (d__1 = e[i__], abs(d__1));
+	    if (eabs <= tmp1) {
+		e[i__] = 0.;
+		e2[i__] = 0.;
+		isplit[*nsplit] = i__;
+		++(*nsplit);
+	    }
+/* L9: */
+	}
+    } else {
+/*        Criterion that guarantees relative accuracy */
+	i__1 = *n - 1;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    eabs = (d__1 = e[i__], abs(d__1));
+	    if (eabs <= *spltol * sqrt((d__1 = d__[i__], abs(d__1))) * sqrt((
+		    d__2 = d__[i__ + 1], abs(d__2)))) {
+		e[i__] = 0.;
+		e2[i__] = 0.;
+		isplit[*nsplit] = i__;
+		++(*nsplit);
+	    }
+/* L10: */
+	}
+    }
+    isplit[*nsplit] = *n;
+    return 0;
+
+/*     End of DLARRA */
+
+} /* igraphdlarra_ */
+
diff --git a/igraph/src/dlarrb.c b/igraph/src/dlarrb.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarrb.c
@@ -0,0 +1,439 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARRB provides limited bisection to locate eigenvalues for more accuracy.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRB + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarrb.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarrb.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarrb.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRB( N, D, LLD, IFIRST, ILAST, RTOL1,   
+                            RTOL2, OFFSET, W, WGAP, WERR, WORK, IWORK,   
+                            PIVMIN, SPDIAM, TWIST, INFO )   
+
+         INTEGER            IFIRST, ILAST, INFO, N, OFFSET, TWIST   
+         DOUBLE PRECISION   PIVMIN, RTOL1, RTOL2, SPDIAM   
+         INTEGER            IWORK( * )   
+         DOUBLE PRECISION   D( * ), LLD( * ), W( * ),   
+        $                   WERR( * ), WGAP( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > Given the relatively robust representation(RRR) L D L^T, DLARRB   
+   > does "limited" bisection to refine the eigenvalues of L D L^T,   
+   > W( IFIRST-OFFSET ) through W( ILAST-OFFSET ), to more accuracy. Initial   
+   > guesses for these eigenvalues are input in W, the corresponding estimate   
+   > of the error in these guesses and their gaps are input in WERR   
+   > and WGAP, respectively. During bisection, intervals   
+   > [left, right] are maintained by storing their mid-points and   
+   > semi-widths in the arrays W and WERR respectively.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The N diagonal elements of the diagonal matrix D.   
+   > \endverbatim   
+   >   
+   > \param[in] LLD   
+   > \verbatim   
+   >          LLD is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (N-1) elements L(i)*L(i)*D(i).   
+   > \endverbatim   
+   >   
+   > \param[in] IFIRST   
+   > \verbatim   
+   >          IFIRST is INTEGER   
+   >          The index of the first eigenvalue to be computed.   
+   > \endverbatim   
+   >   
+   > \param[in] ILAST   
+   > \verbatim   
+   >          ILAST is INTEGER   
+   >          The index of the last eigenvalue to be computed.   
+   > \endverbatim   
+   >   
+   > \param[in] RTOL1   
+   > \verbatim   
+   >          RTOL1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] RTOL2   
+   > \verbatim   
+   >          RTOL2 is DOUBLE PRECISION   
+   >          Tolerance for the convergence of the bisection intervals.   
+   >          An interval [LEFT,RIGHT] has converged if   
+   >          RIGHT-LEFT.LT.MAX( RTOL1*GAP, RTOL2*MAX(|LEFT|,|RIGHT|) )   
+   >          where GAP is the (estimated) distance to the nearest   
+   >          eigenvalue.   
+   > \endverbatim   
+   >   
+   > \param[in] OFFSET   
+   > \verbatim   
+   >          OFFSET is INTEGER   
+   >          Offset for the arrays W, WGAP and WERR, i.e., the IFIRST-OFFSET   
+   >          through ILAST-OFFSET elements of these arrays are to be used.   
+   > \endverbatim   
+   >   
+   > \param[in,out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          On input, W( IFIRST-OFFSET ) through W( ILAST-OFFSET ) are   
+   >          estimates of the eigenvalues of L D L^T indexed IFIRST throug   
+   >          ILAST.   
+   >          On output, these estimates are refined.   
+   > \endverbatim   
+   >   
+   > \param[in,out] WGAP   
+   > \verbatim   
+   >          WGAP is DOUBLE PRECISION array, dimension (N-1)   
+   >          On input, the (estimated) gaps between consecutive   
+   >          eigenvalues of L D L^T, i.e., WGAP(I-OFFSET) is the gap between   
+   >          eigenvalues I and I+1. Note that if IFIRST.EQ.ILAST   
+   >          then WGAP(IFIRST-OFFSET) must be set to ZERO.   
+   >          On output, these gaps are refined.   
+   > \endverbatim   
+   >   
+   > \param[in,out] WERR   
+   > \verbatim   
+   >          WERR is DOUBLE PRECISION array, dimension (N)   
+   >          On input, WERR( IFIRST-OFFSET ) through WERR( ILAST-OFFSET ) are   
+   >          the errors in the estimates of the corresponding elements in W.   
+   >          On output, these errors are refined.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (2*N)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (2*N)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot in the Sturm sequence.   
+   > \endverbatim   
+   >   
+   > \param[in] SPDIAM   
+   > \verbatim   
+   >          SPDIAM is DOUBLE PRECISION   
+   >          The spectral diameter of the matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] TWIST   
+   > \verbatim   
+   >          TWIST is INTEGER   
+   >          The twist index for the twisted factorization that is used   
+   >          for the negcount.   
+   >          TWIST = N: Compute negcount from L D L^T - LAMBDA I = L+ D+ L+^T   
+   >          TWIST = 1: Compute negcount from L D L^T - LAMBDA I = U- D- U-^T   
+   >          TWIST = R: Compute negcount from L D L^T - LAMBDA I = N(r) D(r) N(r)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          Error flag.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarrb_(integer *n, doublereal *d__, doublereal *lld, 
+	integer *ifirst, integer *ilast, doublereal *rtol1, doublereal *rtol2,
+	 integer *offset, doublereal *w, doublereal *wgap, doublereal *werr, 
+	doublereal *work, integer *iwork, doublereal *pivmin, doublereal *
+	spdiam, integer *twist, integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double log(doublereal);
+
+    /* Local variables */
+    integer i__, k, r__, i1, ii, ip;
+    doublereal gap, mid, tmp, back, lgap, rgap, left;
+    integer iter, nint, prev, next;
+    doublereal cvrgd, right, width;
+    extern integer igraphdlaneg_(integer *, doublereal *, doublereal *, doublereal *
+	    , doublereal *, integer *);
+    integer negcnt;
+    doublereal mnwdth;
+    integer olnint, maxitr;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+
+       Parameter adjustments */
+    --iwork;
+    --work;
+    --werr;
+    --wgap;
+    --w;
+    --lld;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+
+    maxitr = (integer) ((log(*spdiam + *pivmin) - log(*pivmin)) / log(2.)) + 
+	    2;
+    mnwdth = *pivmin * 2.;
+
+    r__ = *twist;
+    if (r__ < 1 || r__ > *n) {
+	r__ = *n;
+    }
+
+/*     Initialize unconverged intervals in [ WORK(2*I-1), WORK(2*I) ].   
+       The Sturm Count, Count( WORK(2*I-1) ) is arranged to be I-1, while   
+       Count( WORK(2*I) ) is stored in IWORK( 2*I ). The integer IWORK( 2*I-1 )   
+       for an unconverged interval is set to the index of the next unconverged   
+       interval, and is -1 or 0 for a converged interval. Thus a linked   
+       list of unconverged intervals is set up. */
+
+    i1 = *ifirst;
+/*     The number of unconverged intervals */
+    nint = 0;
+/*     The last unconverged interval found */
+    prev = 0;
+    rgap = wgap[i1 - *offset];
+    i__1 = *ilast;
+    for (i__ = i1; i__ <= i__1; ++i__) {
+	k = i__ << 1;
+	ii = i__ - *offset;
+	left = w[ii] - werr[ii];
+	right = w[ii] + werr[ii];
+	lgap = rgap;
+	rgap = wgap[ii];
+	gap = min(lgap,rgap);
+/*        Make sure that [LEFT,RIGHT] contains the desired eigenvalue   
+          Compute negcount from dstqds facto L+D+L+^T = L D L^T - LEFT   
+
+          Do while( NEGCNT(LEFT).GT.I-1 ) */
+
+	back = werr[ii];
+L20:
+	negcnt = igraphdlaneg_(n, &d__[1], &lld[1], &left, pivmin, &r__);
+	if (negcnt > i__ - 1) {
+	    left -= back;
+	    back *= 2.;
+	    goto L20;
+	}
+
+/*        Do while( NEGCNT(RIGHT).LT.I )   
+          Compute negcount from dstqds facto L+D+L+^T = L D L^T - RIGHT */
+
+	back = werr[ii];
+L50:
+	negcnt = igraphdlaneg_(n, &d__[1], &lld[1], &right, pivmin, &r__);
+	if (negcnt < i__) {
+	    right += back;
+	    back *= 2.;
+	    goto L50;
+	}
+	width = (d__1 = left - right, abs(d__1)) * .5;
+/* Computing MAX */
+	d__1 = abs(left), d__2 = abs(right);
+	tmp = max(d__1,d__2);
+/* Computing MAX */
+	d__1 = *rtol1 * gap, d__2 = *rtol2 * tmp;
+	cvrgd = max(d__1,d__2);
+	if (width <= cvrgd || width <= mnwdth) {
+/*           This interval has already converged and does not need refinement.   
+             (Note that the gaps might change through refining the   
+              eigenvalues, however, they can only get bigger.)   
+             Remove it from the list. */
+	    iwork[k - 1] = -1;
+/*           Make sure that I1 always points to the first unconverged interval */
+	    if (i__ == i1 && i__ < *ilast) {
+		i1 = i__ + 1;
+	    }
+	    if (prev >= i1 && i__ <= *ilast) {
+		iwork[(prev << 1) - 1] = i__ + 1;
+	    }
+	} else {
+/*           unconverged interval found */
+	    prev = i__;
+	    ++nint;
+	    iwork[k - 1] = i__ + 1;
+	    iwork[k] = negcnt;
+	}
+	work[k - 1] = left;
+	work[k] = right;
+/* L75: */
+    }
+
+/*     Do while( NINT.GT.0 ), i.e. there are still unconverged intervals   
+       and while (ITER.LT.MAXITR) */
+
+    iter = 0;
+L80:
+    prev = i1 - 1;
+    i__ = i1;
+    olnint = nint;
+    i__1 = olnint;
+    for (ip = 1; ip <= i__1; ++ip) {
+	k = i__ << 1;
+	ii = i__ - *offset;
+	rgap = wgap[ii];
+	lgap = rgap;
+	if (ii > 1) {
+	    lgap = wgap[ii - 1];
+	}
+	gap = min(lgap,rgap);
+	next = iwork[k - 1];
+	left = work[k - 1];
+	right = work[k];
+	mid = (left + right) * .5;
+/*        semiwidth of interval */
+	width = right - mid;
+/* Computing MAX */
+	d__1 = abs(left), d__2 = abs(right);
+	tmp = max(d__1,d__2);
+/* Computing MAX */
+	d__1 = *rtol1 * gap, d__2 = *rtol2 * tmp;
+	cvrgd = max(d__1,d__2);
+	if (width <= cvrgd || width <= mnwdth || iter == maxitr) {
+/*           reduce number of unconverged intervals */
+	    --nint;
+/*           Mark interval as converged. */
+	    iwork[k - 1] = 0;
+	    if (i1 == i__) {
+		i1 = next;
+	    } else {
+/*              Prev holds the last unconverged interval previously examined */
+		if (prev >= i1) {
+		    iwork[(prev << 1) - 1] = next;
+		}
+	    }
+	    i__ = next;
+	    goto L100;
+	}
+	prev = i__;
+
+/*        Perform one bisection step */
+
+	negcnt = igraphdlaneg_(n, &d__[1], &lld[1], &mid, pivmin, &r__);
+	if (negcnt <= i__ - 1) {
+	    work[k - 1] = mid;
+	} else {
+	    work[k] = mid;
+	}
+	i__ = next;
+L100:
+	;
+    }
+    ++iter;
+/*     do another loop if there are still unconverged intervals   
+       However, in the last iteration, all intervals are accepted   
+       since this is the best we can do. */
+    if (nint > 0 && iter <= maxitr) {
+	goto L80;
+    }
+
+
+/*     At this point, all the intervals have converged */
+    i__1 = *ilast;
+    for (i__ = *ifirst; i__ <= i__1; ++i__) {
+	k = i__ << 1;
+	ii = i__ - *offset;
+/*        All intervals marked by '0' have been refined. */
+	if (iwork[k - 1] == 0) {
+	    w[ii] = (work[k - 1] + work[k]) * .5;
+	    werr[ii] = work[k] - w[ii];
+	}
+/* L110: */
+    }
+
+    i__1 = *ilast;
+    for (i__ = *ifirst + 1; i__ <= i__1; ++i__) {
+	k = i__ << 1;
+	ii = i__ - *offset;
+/* Computing MAX */
+	d__1 = 0., d__2 = w[ii] - werr[ii] - w[ii - 1] - werr[ii - 1];
+	wgap[ii - 1] = max(d__1,d__2);
+/* L111: */
+    }
+    return 0;
+
+/*     End of DLARRB */
+
+} /* igraphdlarrb_ */
+
diff --git a/igraph/src/dlarrc.c b/igraph/src/dlarrc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarrc.c
@@ -0,0 +1,255 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARRC computes the number of eigenvalues of the symmetric tridiagonal matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRC + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarrc.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarrc.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarrc.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRC( JOBT, N, VL, VU, D, E, PIVMIN,   
+                                     EIGCNT, LCNT, RCNT, INFO )   
+
+         CHARACTER          JOBT   
+         INTEGER            EIGCNT, INFO, LCNT, N, RCNT   
+         DOUBLE PRECISION   PIVMIN, VL, VU   
+         DOUBLE PRECISION   D( * ), E( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > Find the number of eigenvalues of the symmetric tridiagonal matrix T   
+   > that are in the interval (VL,VU] if JOBT = 'T', and of L D L^T   
+   > if JOBT = 'L'.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOBT   
+   > \verbatim   
+   >          JOBT is CHARACTER*1   
+   >          = 'T':  Compute Sturm count for matrix T.   
+   >          = 'L':  Compute Sturm count for matrix L D L^T.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix. N > 0.   
+   > \endverbatim   
+   >   
+   > \param[in] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] VU   
+   > \verbatim   
+   >          VU is DOUBLE PRECISION   
+   >          The lower and upper bounds for the eigenvalues.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          JOBT = 'T': The N diagonal elements of the tridiagonal matrix T.   
+   >          JOBT = 'L': The N diagonal elements of the diagonal matrix D.   
+   > \endverbatim   
+   >   
+   > \param[in] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N)   
+   >          JOBT = 'T': The N-1 offdiagonal elements of the matrix T.   
+   >          JOBT = 'L': The N-1 offdiagonal elements of the matrix L.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot in the Sturm sequence for T.   
+   > \endverbatim   
+   >   
+   > \param[out] EIGCNT   
+   > \verbatim   
+   >          EIGCNT is INTEGER   
+   >          The number of eigenvalues of the symmetric tridiagonal matrix T   
+   >          that are in the interval (VL,VU]   
+   > \endverbatim   
+   >   
+   > \param[out] LCNT   
+   > \verbatim   
+   >          LCNT is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[out] RCNT   
+   > \verbatim   
+   >          RCNT is INTEGER   
+   >          The left and right negcounts of the interval.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarrc_(char *jobt, integer *n, doublereal *vl, 
+	doublereal *vu, doublereal *d__, doublereal *e, doublereal *pivmin, 
+	integer *eigcnt, integer *lcnt, integer *rcnt, integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1;
+
+    /* Local variables */
+    integer i__;
+    doublereal sl, su, tmp, tmp2;
+    logical matt;
+    extern logical igraphlsame_(char *, char *);
+    doublereal lpivot, rpivot;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --e;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+    *lcnt = 0;
+    *rcnt = 0;
+    *eigcnt = 0;
+    matt = igraphlsame_(jobt, "T");
+    if (matt) {
+/*        Sturm sequence count on T */
+	lpivot = d__[1] - *vl;
+	rpivot = d__[1] - *vu;
+	if (lpivot <= 0.) {
+	    ++(*lcnt);
+	}
+	if (rpivot <= 0.) {
+	    ++(*rcnt);
+	}
+	i__1 = *n - 1;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+/* Computing 2nd power */
+	    d__1 = e[i__];
+	    tmp = d__1 * d__1;
+	    lpivot = d__[i__ + 1] - *vl - tmp / lpivot;
+	    rpivot = d__[i__ + 1] - *vu - tmp / rpivot;
+	    if (lpivot <= 0.) {
+		++(*lcnt);
+	    }
+	    if (rpivot <= 0.) {
+		++(*rcnt);
+	    }
+/* L10: */
+	}
+    } else {
+/*        Sturm sequence count on L D L^T */
+	sl = -(*vl);
+	su = -(*vu);
+	i__1 = *n - 1;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    lpivot = d__[i__] + sl;
+	    rpivot = d__[i__] + su;
+	    if (lpivot <= 0.) {
+		++(*lcnt);
+	    }
+	    if (rpivot <= 0.) {
+		++(*rcnt);
+	    }
+	    tmp = e[i__] * d__[i__] * e[i__];
+
+	    tmp2 = tmp / lpivot;
+	    if (tmp2 == 0.) {
+		sl = tmp - *vl;
+	    } else {
+		sl = sl * tmp2 - *vl;
+	    }
+
+	    tmp2 = tmp / rpivot;
+	    if (tmp2 == 0.) {
+		su = tmp - *vu;
+	    } else {
+		su = su * tmp2 - *vu;
+	    }
+/* L20: */
+	}
+	lpivot = d__[*n] + sl;
+	rpivot = d__[*n] + su;
+	if (lpivot <= 0.) {
+	    ++(*lcnt);
+	}
+	if (rpivot <= 0.) {
+	    ++(*rcnt);
+	}
+    }
+    *eigcnt = *rcnt - *lcnt;
+    return 0;
+
+/*     end of DLARRC */
+
+} /* igraphdlarrc_ */
+
diff --git a/igraph/src/dlarrd.c b/igraph/src/dlarrd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarrd.c
@@ -0,0 +1,912 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__3 = 3;
+static integer c__2 = 2;
+static integer c__0 = 0;
+
+/* > \brief \b DLARRD computes the eigenvalues of a symmetric tridiagonal matrix to suitable accuracy.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRD + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarrd.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarrd.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarrd.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRD( RANGE, ORDER, N, VL, VU, IL, IU, GERS,   
+                             RELTOL, D, E, E2, PIVMIN, NSPLIT, ISPLIT,   
+                             M, W, WERR, WL, WU, IBLOCK, INDEXW,   
+                             WORK, IWORK, INFO )   
+
+         CHARACTER          ORDER, RANGE   
+         INTEGER            IL, INFO, IU, M, N, NSPLIT   
+         DOUBLE PRECISION    PIVMIN, RELTOL, VL, VU, WL, WU   
+         INTEGER            IBLOCK( * ), INDEXW( * ),   
+        $                   ISPLIT( * ), IWORK( * )   
+         DOUBLE PRECISION   D( * ), E( * ), E2( * ),   
+        $                   GERS( * ), W( * ), WERR( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARRD computes the eigenvalues of a symmetric tridiagonal   
+   > matrix T to suitable accuracy. This is an auxiliary code to be   
+   > called from DSTEMR.   
+   > The user may ask for all eigenvalues, all eigenvalues   
+   > in the half-open interval (VL, VU], or the IL-th through IU-th   
+   > eigenvalues.   
+   >   
+   > To avoid overflow, the matrix must be scaled so that its   
+   > largest element is no greater than overflow**(1/2) * underflow**(1/4) in absolute value, and for greatest
+   
+   > accuracy, it should not be much smaller than that.   
+   >   
+   > See W. Kahan "Accurate Eigenvalues of a Symmetric Tridiagonal   
+   > Matrix", Report CS41, Computer Science Dept., Stanford   
+   > University, July 21, 1966.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] RANGE   
+   > \verbatim   
+   >          RANGE is CHARACTER*1   
+   >          = 'A': ("All")   all eigenvalues will be found.   
+   >          = 'V': ("Value") all eigenvalues in the half-open interval   
+   >                           (VL, VU] will be found.   
+   >          = 'I': ("Index") the IL-th through IU-th eigenvalues (of the   
+   >                           entire matrix) will be found.   
+   > \endverbatim   
+   >   
+   > \param[in] ORDER   
+   > \verbatim   
+   >          ORDER is CHARACTER*1   
+   >          = 'B': ("By Block") the eigenvalues will be grouped by   
+   >                              split-off block (see IBLOCK, ISPLIT) and   
+   >                              ordered from smallest to largest within   
+   >                              the block.   
+   >          = 'E': ("Entire matrix")   
+   >                              the eigenvalues for the entire matrix   
+   >                              will be ordered from smallest to   
+   >                              largest.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the tridiagonal matrix T.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] VU   
+   > \verbatim   
+   >          VU is DOUBLE PRECISION   
+   >          If RANGE='V', the lower and upper bounds of the interval to   
+   >          be searched for eigenvalues.  Eigenvalues less than or equal   
+   >          to VL, or greater than VU, will not be returned.  VL < VU.   
+   >          Not referenced if RANGE = 'A' or 'I'.   
+   > \endverbatim   
+   >   
+   > \param[in] IL   
+   > \verbatim   
+   >          IL is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IU   
+   > \verbatim   
+   >          IU is INTEGER   
+   >          If RANGE='I', the indices (in ascending order) of the   
+   >          smallest and largest eigenvalues to be returned.   
+   >          1 <= IL <= IU <= N, if N > 0; IL = 1 and IU = 0 if N = 0.   
+   >          Not referenced if RANGE = 'A' or 'V'.   
+   > \endverbatim   
+   >   
+   > \param[in] GERS   
+   > \verbatim   
+   >          GERS is DOUBLE PRECISION array, dimension (2*N)   
+   >          The N Gerschgorin intervals (the i-th Gerschgorin interval   
+   >          is (GERS(2*i-1), GERS(2*i)).   
+   > \endverbatim   
+   >   
+   > \param[in] RELTOL   
+   > \verbatim   
+   >          RELTOL is DOUBLE PRECISION   
+   >          The minimum relative width of an interval.  When an interval   
+   >          is narrower than RELTOL times the larger (in   
+   >          magnitude) endpoint, then it is considered to be   
+   >          sufficiently small, i.e., converged.  Note: this should   
+   >          always be at least radix*machine epsilon.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The n diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (n-1) off-diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] E2   
+   > \verbatim   
+   >          E2 is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (n-1) squared off-diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot allowed in the Sturm sequence for T.   
+   > \endverbatim   
+   >   
+   > \param[in] NSPLIT   
+   > \verbatim   
+   >          NSPLIT is INTEGER   
+   >          The number of diagonal blocks in the matrix T.   
+   >          1 <= NSPLIT <= N.   
+   > \endverbatim   
+   >   
+   > \param[in] ISPLIT   
+   > \verbatim   
+   >          ISPLIT is INTEGER array, dimension (N)   
+   >          The splitting points, at which T breaks up into submatrices.   
+   >          The first submatrix consists of rows/columns 1 to ISPLIT(1),   
+   >          the second of rows/columns ISPLIT(1)+1 through ISPLIT(2),   
+   >          etc., and the NSPLIT-th consists of rows/columns   
+   >          ISPLIT(NSPLIT-1)+1 through ISPLIT(NSPLIT)=N.   
+   >          (Only the first NSPLIT elements will actually be used, but   
+   >          since the user cannot know a priori what value NSPLIT will   
+   >          have, N words must be reserved for ISPLIT.)   
+   > \endverbatim   
+   >   
+   > \param[out] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The actual number of eigenvalues found. 0 <= M <= N.   
+   >          (See also the description of INFO=2,3.)   
+   > \endverbatim   
+   >   
+   > \param[out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          On exit, the first M elements of W will contain the   
+   >          eigenvalue approximations. DLARRD computes an interval   
+   >          I_j = (a_j, b_j] that includes eigenvalue j. The eigenvalue   
+   >          approximation is given as the interval midpoint   
+   >          W(j)= ( a_j + b_j)/2. The corresponding error is bounded by   
+   >          WERR(j) = abs( a_j - b_j)/2   
+   > \endverbatim   
+   >   
+   > \param[out] WERR   
+   > \verbatim   
+   >          WERR is DOUBLE PRECISION array, dimension (N)   
+   >          The error bound on the corresponding eigenvalue approximation   
+   >          in W.   
+   > \endverbatim   
+   >   
+   > \param[out] WL   
+   > \verbatim   
+   >          WL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[out] WU   
+   > \verbatim   
+   >          WU is DOUBLE PRECISION   
+   >          The interval (WL, WU] contains all the wanted eigenvalues.   
+   >          If RANGE='V', then WL=VL and WU=VU.   
+   >          If RANGE='A', then WL and WU are the global Gerschgorin bounds   
+   >                        on the spectrum.   
+   >          If RANGE='I', then WL and WU are computed by DLAEBZ from the   
+   >                        index range specified.   
+   > \endverbatim   
+   >   
+   > \param[out] IBLOCK   
+   > \verbatim   
+   >          IBLOCK is INTEGER array, dimension (N)   
+   >          At each row/column j where E(j) is zero or small, the   
+   >          matrix T is considered to split into a block diagonal   
+   >          matrix.  On exit, if INFO = 0, IBLOCK(i) specifies to which   
+   >          block (from 1 to the number of blocks) the eigenvalue W(i)   
+   >          belongs.  (DLARRD may use the remaining N-M elements as   
+   >          workspace.)   
+   > \endverbatim   
+   >   
+   > \param[out] INDEXW   
+   > \verbatim   
+   >          INDEXW is INTEGER array, dimension (N)   
+   >          The indices of the eigenvalues within each block (submatrix);   
+   >          for example, INDEXW(i)= j and IBLOCK(i)=k imply that the   
+   >          i-th eigenvalue W(i) is the j-th eigenvalue in block k.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (4*N)   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (3*N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  some or all of the eigenvalues failed to converge or   
+   >                were not computed:   
+   >                =1 or 3: Bisection failed to converge for some   
+   >                        eigenvalues; these eigenvalues are flagged by a   
+   >                        negative block number.  The effect is that the   
+   >                        eigenvalues may not be as accurate as the   
+   >                        absolute and relative tolerances.  This is   
+   >                        generally caused by unexpectedly inaccurate   
+   >                        arithmetic.   
+   >                =2 or 3: RANGE='I' only: Not all of the eigenvalues   
+   >                        IL:IU were found.   
+   >                        Effect: M < IU+1-IL   
+   >                        Cause:  non-monotonic arithmetic, causing the   
+   >                                Sturm sequence to be non-monotonic.   
+   >                        Cure:   recalculate, using RANGE='A', and pick   
+   >                                out eigenvalues IL:IU.  In some cases,   
+   >                                increasing the PARAMETER "FUDGE" may   
+   >                                make things work.   
+   >                = 4:    RANGE='I', and the Gershgorin interval   
+   >                        initially used was too small.  No eigenvalues   
+   >                        were computed.   
+   >                        Probable cause: your machine has sloppy   
+   >                                        floating-point arithmetic.   
+   >                        Cure: Increase the PARAMETER "FUDGE",   
+   >                              recompile, and try again.   
+   > \endverbatim   
+
+   > \par Internal Parameters:   
+    =========================   
+   >   
+   > \verbatim   
+   >  FUDGE   DOUBLE PRECISION, default = 2   
+   >          A "fudge factor" to widen the Gershgorin intervals.  Ideally,   
+   >          a value of 1 should work, but on machines with sloppy   
+   >          arithmetic, this needs to be larger.  The default for   
+   >          publicly released versions should be large enough to handle   
+   >          the worst machine around.  Note that this has no effect   
+   >          on accuracy of the solution.   
+   > \endverbatim   
+   >   
+   > \par Contributors:   
+    ==================   
+   >   
+   >     W. Kahan, University of California, Berkeley, USA \n   
+   >     Beresford Parlett, University of California, Berkeley, USA \n   
+   >     Jim Demmel, University of California, Berkeley, USA \n   
+   >     Inderjit Dhillon, University of Texas, Austin, USA \n   
+   >     Osni Marques, LBNL/NERSC, USA \n   
+   >     Christof Voemel, University of California, Berkeley, USA \n   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarrd_(char *range, char *order, integer *n, doublereal 
+	*vl, doublereal *vu, integer *il, integer *iu, doublereal *gers, 
+	doublereal *reltol, doublereal *d__, doublereal *e, doublereal *e2, 
+	doublereal *pivmin, integer *nsplit, integer *isplit, integer *m, 
+	doublereal *w, doublereal *werr, doublereal *wl, doublereal *wu, 
+	integer *iblock, integer *indexw, doublereal *work, integer *iwork, 
+	integer *info)
+{
+    /* System generated locals */
+    integer i__1, i__2, i__3;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double log(doublereal);
+
+    /* Local variables */
+    integer i__, j, ib, ie, je, nb;
+    doublereal gl;
+    integer im, in;
+    doublereal gu;
+    integer iw, jee;
+    doublereal eps;
+    integer nwl;
+    doublereal wlu, wul;
+    integer nwu;
+    doublereal tmp1, tmp2;
+    integer iend, jblk, ioff, iout, itmp1, itmp2, jdisc;
+    extern logical igraphlsame_(char *, char *);
+    integer iinfo;
+    doublereal atoli;
+    integer iwoff, itmax;
+    doublereal wkill, rtoli, uflow, tnorm;
+    extern doublereal igraphdlamch_(char *);
+    integer ibegin;
+    extern /* Subroutine */ int igraphdlaebz_(integer *, integer *, integer *, 
+	    integer *, integer *, integer *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, integer *,
+	     doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *);
+    integer irange, idiscl, idumma[1];
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    integer idiscu;
+    logical ncnvrg, toofew;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --iwork;
+    --work;
+    --indexw;
+    --iblock;
+    --werr;
+    --w;
+    --isplit;
+    --e2;
+    --e;
+    --d__;
+    --gers;
+
+    /* Function Body */
+    *info = 0;
+
+/*     Decode RANGE */
+
+    if (igraphlsame_(range, "A")) {
+	irange = 1;
+    } else if (igraphlsame_(range, "V")) {
+	irange = 2;
+    } else if (igraphlsame_(range, "I")) {
+	irange = 3;
+    } else {
+	irange = 0;
+    }
+
+/*     Check for Errors */
+
+    if (irange <= 0) {
+	*info = -1;
+    } else if (! (igraphlsame_(order, "B") || igraphlsame_(order, 
+	    "E"))) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -3;
+    } else if (irange == 2) {
+	if (*vl >= *vu) {
+	    *info = -5;
+	}
+    } else if (irange == 3 && (*il < 1 || *il > max(1,*n))) {
+	*info = -6;
+    } else if (irange == 3 && (*iu < min(*n,*il) || *iu > *n)) {
+	*info = -7;
+    }
+
+    if (*info != 0) {
+	return 0;
+    }
+/*     Initialize error flags */
+    *info = 0;
+    ncnvrg = FALSE_;
+    toofew = FALSE_;
+/*     Quick return if possible */
+    *m = 0;
+    if (*n == 0) {
+	return 0;
+    }
+/*     Simplification: */
+    if (irange == 3 && *il == 1 && *iu == *n) {
+	irange = 1;
+    }
+/*     Get machine constants */
+    eps = igraphdlamch_("P");
+    uflow = igraphdlamch_("U");
+/*     Special Case when N=1   
+       Treat case of 1x1 matrix for quick return */
+    if (*n == 1) {
+	if (irange == 1 || irange == 2 && d__[1] > *vl && d__[1] <= *vu || 
+		irange == 3 && *il == 1 && *iu == 1) {
+	    *m = 1;
+	    w[1] = d__[1];
+/*           The computation error of the eigenvalue is zero */
+	    werr[1] = 0.;
+	    iblock[1] = 1;
+	    indexw[1] = 1;
+	}
+	return 0;
+    }
+/*     NB is the minimum vector length for vector bisection, or 0   
+       if only scalar is to be done. */
+    nb = igraphilaenv_(&c__1, "DSTEBZ", " ", n, &c_n1, &c_n1, &c_n1, (ftnlen)6, (
+	    ftnlen)1);
+    if (nb <= 1) {
+	nb = 0;
+    }
+/*     Find global spectral radius */
+    gl = d__[1];
+    gu = d__[1];
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+/* Computing MIN */
+	d__1 = gl, d__2 = gers[(i__ << 1) - 1];
+	gl = min(d__1,d__2);
+/* Computing MAX */
+	d__1 = gu, d__2 = gers[i__ * 2];
+	gu = max(d__1,d__2);
+/* L5: */
+    }
+/*     Compute global Gerschgorin bounds and spectral diameter   
+   Computing MAX */
+    d__1 = abs(gl), d__2 = abs(gu);
+    tnorm = max(d__1,d__2);
+    gl = gl - tnorm * 2. * eps * *n - *pivmin * 4.;
+    gu = gu + tnorm * 2. * eps * *n + *pivmin * 4.;
+/*     [JAN/28/2009] remove the line below since SPDIAM variable not use   
+       SPDIAM = GU - GL   
+       Input arguments for DLAEBZ:   
+       The relative tolerance.  An interval (a,b] lies within   
+       "relative tolerance" if  b-a < RELTOL*max(|a|,|b|), */
+    rtoli = *reltol;
+/*     Set the absolute tolerance for interval convergence to zero to force   
+       interval convergence based on relative size of the interval.   
+       This is dangerous because intervals might not converge when RELTOL is   
+       small. But at least a very small number should be selected so that for   
+       strongly graded matrices, the code can get relatively accurate   
+       eigenvalues. */
+    atoli = uflow * 4. + *pivmin * 4.;
+    if (irange == 3) {
+/*        RANGE='I': Compute an interval containing eigenvalues   
+          IL through IU. The initial interval [GL,GU] from the global   
+          Gerschgorin bounds GL and GU is refined by DLAEBZ. */
+	itmax = (integer) ((log(tnorm + *pivmin) - log(*pivmin)) / log(2.)) + 
+		2;
+	work[*n + 1] = gl;
+	work[*n + 2] = gl;
+	work[*n + 3] = gu;
+	work[*n + 4] = gu;
+	work[*n + 5] = gl;
+	work[*n + 6] = gu;
+	iwork[1] = -1;
+	iwork[2] = -1;
+	iwork[3] = *n + 1;
+	iwork[4] = *n + 1;
+	iwork[5] = *il - 1;
+	iwork[6] = *iu;
+
+	igraphdlaebz_(&c__3, &itmax, n, &c__2, &c__2, &nb, &atoli, &rtoli, pivmin, &
+		d__[1], &e[1], &e2[1], &iwork[5], &work[*n + 1], &work[*n + 5]
+		, &iout, &iwork[1], &w[1], &iblock[1], &iinfo);
+	if (iinfo != 0) {
+	    *info = iinfo;
+	    return 0;
+	}
+/*        On exit, output intervals may not be ordered by ascending negcount */
+	if (iwork[6] == *iu) {
+	    *wl = work[*n + 1];
+	    wlu = work[*n + 3];
+	    nwl = iwork[1];
+	    *wu = work[*n + 4];
+	    wul = work[*n + 2];
+	    nwu = iwork[4];
+	} else {
+	    *wl = work[*n + 2];
+	    wlu = work[*n + 4];
+	    nwl = iwork[2];
+	    *wu = work[*n + 3];
+	    wul = work[*n + 1];
+	    nwu = iwork[3];
+	}
+/*        On exit, the interval [WL, WLU] contains a value with negcount NWL,   
+          and [WUL, WU] contains a value with negcount NWU. */
+	if (nwl < 0 || nwl >= *n || nwu < 1 || nwu > *n) {
+	    *info = 4;
+	    return 0;
+	}
+    } else if (irange == 2) {
+	*wl = *vl;
+	*wu = *vu;
+    } else if (irange == 1) {
+	*wl = gl;
+	*wu = gu;
+    }
+/*     Find Eigenvalues -- Loop Over blocks and recompute NWL and NWU.   
+       NWL accumulates the number of eigenvalues .le. WL,   
+       NWU accumulates the number of eigenvalues .le. WU */
+    *m = 0;
+    iend = 0;
+    *info = 0;
+    nwl = 0;
+    nwu = 0;
+
+    i__1 = *nsplit;
+    for (jblk = 1; jblk <= i__1; ++jblk) {
+	ioff = iend;
+	ibegin = ioff + 1;
+	iend = isplit[jblk];
+	in = iend - ioff;
+
+	if (in == 1) {
+/*           1x1 block */
+	    if (*wl >= d__[ibegin] - *pivmin) {
+		++nwl;
+	    }
+	    if (*wu >= d__[ibegin] - *pivmin) {
+		++nwu;
+	    }
+	    if (irange == 1 || *wl < d__[ibegin] - *pivmin && *wu >= d__[
+		    ibegin] - *pivmin) {
+		++(*m);
+		w[*m] = d__[ibegin];
+		werr[*m] = 0.;
+/*              The gap for a single block doesn't matter for the later   
+                algorithm and is assigned an arbitrary large value */
+		iblock[*m] = jblk;
+		indexw[*m] = 1;
+	    }
+/*        Disabled 2x2 case because of a failure on the following matrix   
+          RANGE = 'I', IL = IU = 4   
+            Original Tridiagonal, d = [   
+             -0.150102010615740E+00   
+             -0.849897989384260E+00   
+             -0.128208148052635E-15   
+              0.128257718286320E-15   
+            ];   
+            e = [   
+             -0.357171383266986E+00   
+             -0.180411241501588E-15   
+             -0.175152352710251E-15   
+            ];   
+
+           ELSE IF( IN.EQ.2 ) THEN   
+   *           2x2 block   
+              DISC = SQRT( (HALF*(D(IBEGIN)-D(IEND)))**2 + E(IBEGIN)**2 )   
+              TMP1 = HALF*(D(IBEGIN)+D(IEND))   
+              L1 = TMP1 - DISC   
+              IF( WL.GE. L1-PIVMIN )   
+       $         NWL = NWL + 1   
+              IF( WU.GE. L1-PIVMIN )   
+       $         NWU = NWU + 1   
+              IF( IRANGE.EQ.ALLRNG .OR. ( WL.LT.L1-PIVMIN .AND. WU.GE.   
+       $          L1-PIVMIN ) ) THEN   
+                 M = M + 1   
+                 W( M ) = L1   
+   *              The uncertainty of eigenvalues of a 2x2 matrix is very small   
+                 WERR( M ) = EPS * ABS( W( M ) ) * TWO   
+                 IBLOCK( M ) = JBLK   
+                 INDEXW( M ) = 1   
+              ENDIF   
+              L2 = TMP1 + DISC   
+              IF( WL.GE. L2-PIVMIN )   
+       $         NWL = NWL + 1   
+              IF( WU.GE. L2-PIVMIN )   
+       $         NWU = NWU + 1   
+              IF( IRANGE.EQ.ALLRNG .OR. ( WL.LT.L2-PIVMIN .AND. WU.GE.   
+       $          L2-PIVMIN ) ) THEN   
+                 M = M + 1   
+                 W( M ) = L2   
+   *              The uncertainty of eigenvalues of a 2x2 matrix is very small   
+                 WERR( M ) = EPS * ABS( W( M ) ) * TWO   
+                 IBLOCK( M ) = JBLK   
+                 INDEXW( M ) = 2   
+              ENDIF */
+	} else {
+/*           General Case - block of size IN >= 2   
+             Compute local Gerschgorin interval and use it as the initial   
+             interval for DLAEBZ */
+	    gu = d__[ibegin];
+	    gl = d__[ibegin];
+	    tmp1 = 0.;
+	    i__2 = iend;
+	    for (j = ibegin; j <= i__2; ++j) {
+/* Computing MIN */
+		d__1 = gl, d__2 = gers[(j << 1) - 1];
+		gl = min(d__1,d__2);
+/* Computing MAX */
+		d__1 = gu, d__2 = gers[j * 2];
+		gu = max(d__1,d__2);
+/* L40: */
+	    }
+/*           [JAN/28/2009]   
+             change SPDIAM by TNORM in lines 2 and 3 thereafter   
+             line 1: remove computation of SPDIAM (not useful anymore)   
+             SPDIAM = GU - GL   
+             GL = GL - FUDGE*SPDIAM*EPS*IN - FUDGE*PIVMIN   
+             GU = GU + FUDGE*SPDIAM*EPS*IN + FUDGE*PIVMIN */
+	    gl = gl - tnorm * 2. * eps * in - *pivmin * 2.;
+	    gu = gu + tnorm * 2. * eps * in + *pivmin * 2.;
+
+	    if (irange > 1) {
+		if (gu < *wl) {
+/*                 the local block contains none of the wanted eigenvalues */
+		    nwl += in;
+		    nwu += in;
+		    goto L70;
+		}
+/*              refine search interval if possible, only range (WL,WU] matters */
+		gl = max(gl,*wl);
+		gu = min(gu,*wu);
+		if (gl >= gu) {
+		    goto L70;
+		}
+	    }
+/*           Find negcount of initial interval boundaries GL and GU */
+	    work[*n + 1] = gl;
+	    work[*n + in + 1] = gu;
+	    igraphdlaebz_(&c__1, &c__0, &in, &in, &c__1, &nb, &atoli, &rtoli, 
+		    pivmin, &d__[ibegin], &e[ibegin], &e2[ibegin], idumma, &
+		    work[*n + 1], &work[*n + (in << 1) + 1], &im, &iwork[1], &
+		    w[*m + 1], &iblock[*m + 1], &iinfo);
+	    if (iinfo != 0) {
+		*info = iinfo;
+		return 0;
+	    }
+
+	    nwl += iwork[1];
+	    nwu += iwork[in + 1];
+	    iwoff = *m - iwork[1];
+/*           Compute Eigenvalues */
+	    itmax = (integer) ((log(gu - gl + *pivmin) - log(*pivmin)) / log(
+		    2.)) + 2;
+	    igraphdlaebz_(&c__2, &itmax, &in, &in, &c__1, &nb, &atoli, &rtoli, 
+		    pivmin, &d__[ibegin], &e[ibegin], &e2[ibegin], idumma, &
+		    work[*n + 1], &work[*n + (in << 1) + 1], &iout, &iwork[1],
+		     &w[*m + 1], &iblock[*m + 1], &iinfo);
+	    if (iinfo != 0) {
+		*info = iinfo;
+		return 0;
+	    }
+
+/*           Copy eigenvalues into W and IBLOCK   
+             Use -JBLK for block number for unconverged eigenvalues.   
+             Loop over the number of output intervals from DLAEBZ */
+	    i__2 = iout;
+	    for (j = 1; j <= i__2; ++j) {
+/*              eigenvalue approximation is middle point of interval */
+		tmp1 = (work[j + *n] + work[j + in + *n]) * .5;
+/*              semi length of error interval */
+		tmp2 = (d__1 = work[j + *n] - work[j + in + *n], abs(d__1)) * 
+			.5;
+		if (j > iout - iinfo) {
+/*                 Flag non-convergence. */
+		    ncnvrg = TRUE_;
+		    ib = -jblk;
+		} else {
+		    ib = jblk;
+		}
+		i__3 = iwork[j + in] + iwoff;
+		for (je = iwork[j] + 1 + iwoff; je <= i__3; ++je) {
+		    w[je] = tmp1;
+		    werr[je] = tmp2;
+		    indexw[je] = je - iwoff;
+		    iblock[je] = ib;
+/* L50: */
+		}
+/* L60: */
+	    }
+
+	    *m += im;
+	}
+L70:
+	;
+    }
+/*     If RANGE='I', then (WL,WU) contains eigenvalues NWL+1,...,NWU   
+       If NWL+1 < IL or NWU > IU, discard extra eigenvalues. */
+    if (irange == 3) {
+	idiscl = *il - 1 - nwl;
+	idiscu = nwu - *iu;
+
+	if (idiscl > 0) {
+	    im = 0;
+	    i__1 = *m;
+	    for (je = 1; je <= i__1; ++je) {
+/*              Remove some of the smallest eigenvalues from the left so that   
+                at the end IDISCL =0. Move all eigenvalues up to the left. */
+		if (w[je] <= wlu && idiscl > 0) {
+		    --idiscl;
+		} else {
+		    ++im;
+		    w[im] = w[je];
+		    werr[im] = werr[je];
+		    indexw[im] = indexw[je];
+		    iblock[im] = iblock[je];
+		}
+/* L80: */
+	    }
+	    *m = im;
+	}
+	if (idiscu > 0) {
+/*           Remove some of the largest eigenvalues from the right so that   
+             at the end IDISCU =0. Move all eigenvalues up to the left. */
+	    im = *m + 1;
+	    for (je = *m; je >= 1; --je) {
+		if (w[je] >= wul && idiscu > 0) {
+		    --idiscu;
+		} else {
+		    --im;
+		    w[im] = w[je];
+		    werr[im] = werr[je];
+		    indexw[im] = indexw[je];
+		    iblock[im] = iblock[je];
+		}
+/* L81: */
+	    }
+	    jee = 0;
+	    i__1 = *m;
+	    for (je = im; je <= i__1; ++je) {
+		++jee;
+		w[jee] = w[je];
+		werr[jee] = werr[je];
+		indexw[jee] = indexw[je];
+		iblock[jee] = iblock[je];
+/* L82: */
+	    }
+	    *m = *m - im + 1;
+	}
+	if (idiscl > 0 || idiscu > 0) {
+/*           Code to deal with effects of bad arithmetic. (If N(w) is   
+             monotone non-decreasing, this should never happen.)   
+             Some low eigenvalues to be discarded are not in (WL,WLU],   
+             or high eigenvalues to be discarded are not in (WUL,WU]   
+             so just kill off the smallest IDISCL/largest IDISCU   
+             eigenvalues, by marking the corresponding IBLOCK = 0 */
+	    if (idiscl > 0) {
+		wkill = *wu;
+		i__1 = idiscl;
+		for (jdisc = 1; jdisc <= i__1; ++jdisc) {
+		    iw = 0;
+		    i__2 = *m;
+		    for (je = 1; je <= i__2; ++je) {
+			if (iblock[je] != 0 && (w[je] < wkill || iw == 0)) {
+			    iw = je;
+			    wkill = w[je];
+			}
+/* L90: */
+		    }
+		    iblock[iw] = 0;
+/* L100: */
+		}
+	    }
+	    if (idiscu > 0) {
+		wkill = *wl;
+		i__1 = idiscu;
+		for (jdisc = 1; jdisc <= i__1; ++jdisc) {
+		    iw = 0;
+		    i__2 = *m;
+		    for (je = 1; je <= i__2; ++je) {
+			if (iblock[je] != 0 && (w[je] >= wkill || iw == 0)) {
+			    iw = je;
+			    wkill = w[je];
+			}
+/* L110: */
+		    }
+		    iblock[iw] = 0;
+/* L120: */
+		}
+	    }
+/*           Now erase all eigenvalues with IBLOCK set to zero */
+	    im = 0;
+	    i__1 = *m;
+	    for (je = 1; je <= i__1; ++je) {
+		if (iblock[je] != 0) {
+		    ++im;
+		    w[im] = w[je];
+		    werr[im] = werr[je];
+		    indexw[im] = indexw[je];
+		    iblock[im] = iblock[je];
+		}
+/* L130: */
+	    }
+	    *m = im;
+	}
+	if (idiscl < 0 || idiscu < 0) {
+	    toofew = TRUE_;
+	}
+    }
+
+    if (irange == 1 && *m != *n || irange == 3 && *m != *iu - *il + 1) {
+	toofew = TRUE_;
+    }
+/*     If ORDER='B', do nothing the eigenvalues are already sorted by   
+          block.   
+       If ORDER='E', sort the eigenvalues from smallest to largest */
+    if (igraphlsame_(order, "E") && *nsplit > 1) {
+	i__1 = *m - 1;
+	for (je = 1; je <= i__1; ++je) {
+	    ie = 0;
+	    tmp1 = w[je];
+	    i__2 = *m;
+	    for (j = je + 1; j <= i__2; ++j) {
+		if (w[j] < tmp1) {
+		    ie = j;
+		    tmp1 = w[j];
+		}
+/* L140: */
+	    }
+	    if (ie != 0) {
+		tmp2 = werr[ie];
+		itmp1 = iblock[ie];
+		itmp2 = indexw[ie];
+		w[ie] = w[je];
+		werr[ie] = werr[je];
+		iblock[ie] = iblock[je];
+		indexw[ie] = indexw[je];
+		w[je] = tmp1;
+		werr[je] = tmp2;
+		iblock[je] = itmp1;
+		indexw[je] = itmp2;
+	    }
+/* L150: */
+	}
+    }
+
+    *info = 0;
+    if (ncnvrg) {
+	++(*info);
+    }
+    if (toofew) {
+	*info += 2;
+    }
+    return 0;
+
+/*     End of DLARRD */
+
+} /* igraphdlarrd_ */
+
diff --git a/igraph/src/dlarre.c b/igraph/src/dlarre.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarre.c
@@ -0,0 +1,986 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c__2 = 2;
+
+/* > \brief \b DLARRE given the tridiagonal matrix T, sets small off-diagonal elements to zero and for each un
+reduced block Ti, finds base representations and eigenvalues.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRE + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarre.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarre.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarre.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRE( RANGE, N, VL, VU, IL, IU, D, E, E2,   
+                             RTOL1, RTOL2, SPLTOL, NSPLIT, ISPLIT, M,   
+                             W, WERR, WGAP, IBLOCK, INDEXW, GERS, PIVMIN,   
+                             WORK, IWORK, INFO )   
+
+         CHARACTER          RANGE   
+         INTEGER            IL, INFO, IU, M, N, NSPLIT   
+         DOUBLE PRECISION  PIVMIN, RTOL1, RTOL2, SPLTOL, VL, VU   
+         INTEGER            IBLOCK( * ), ISPLIT( * ), IWORK( * ),   
+        $                   INDEXW( * )   
+         DOUBLE PRECISION   D( * ), E( * ), E2( * ), GERS( * ),   
+        $                   W( * ),WERR( * ), WGAP( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > To find the desired eigenvalues of a given real symmetric   
+   > tridiagonal matrix T, DLARRE sets any "small" off-diagonal   
+   > elements to zero, and for each unreduced block T_i, it finds   
+   > (a) a suitable shift at one end of the block's spectrum,   
+   > (b) the base representation, T_i - sigma_i I = L_i D_i L_i^T, and   
+   > (c) eigenvalues of each L_i D_i L_i^T.   
+   > The representations and eigenvalues found are then used by   
+   > DSTEMR to compute the eigenvectors of T.   
+   > The accuracy varies depending on whether bisection is used to   
+   > find a few eigenvalues or the dqds algorithm (subroutine DLASQ2) to   
+   > conpute all and then discard any unwanted one.   
+   > As an added benefit, DLARRE also outputs the n   
+   > Gerschgorin intervals for the matrices L_i D_i L_i^T.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] RANGE   
+   > \verbatim   
+   >          RANGE is CHARACTER*1   
+   >          = 'A': ("All")   all eigenvalues will be found.   
+   >          = 'V': ("Value") all eigenvalues in the half-open interval   
+   >                           (VL, VU] will be found.   
+   >          = 'I': ("Index") the IL-th through IU-th eigenvalues (of the   
+   >                           entire matrix) will be found.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix. N > 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] VU   
+   > \verbatim   
+   >          VU is DOUBLE PRECISION   
+   >          If RANGE='V', the lower and upper bounds for the eigenvalues.   
+   >          Eigenvalues less than or equal to VL, or greater than VU,   
+   >          will not be returned.  VL < VU.   
+   >          If RANGE='I' or ='A', DLARRE computes bounds on the desired   
+   >          part of the spectrum.   
+   > \endverbatim   
+   >   
+   > \param[in] IL   
+   > \verbatim   
+   >          IL is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IU   
+   > \verbatim   
+   >          IU is INTEGER   
+   >          If RANGE='I', the indices (in ascending order) of the   
+   >          smallest and largest eigenvalues to be returned.   
+   >          1 <= IL <= IU <= N.   
+   > \endverbatim   
+   >   
+   > \param[in,out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the N diagonal elements of the tridiagonal   
+   >          matrix T.   
+   >          On exit, the N diagonal elements of the diagonal   
+   >          matrices D_i.   
+   > \endverbatim   
+   >   
+   > \param[in,out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the first (N-1) entries contain the subdiagonal   
+   >          elements of the tridiagonal matrix T; E(N) need not be set.   
+   >          On exit, E contains the subdiagonal elements of the unit   
+   >          bidiagonal matrices L_i. The entries E( ISPLIT( I ) ),   
+   >          1 <= I <= NSPLIT, contain the base points sigma_i on output.   
+   > \endverbatim   
+   >   
+   > \param[in,out] E2   
+   > \verbatim   
+   >          E2 is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the first (N-1) entries contain the SQUARES of the   
+   >          subdiagonal elements of the tridiagonal matrix T;   
+   >          E2(N) need not be set.   
+   >          On exit, the entries E2( ISPLIT( I ) ),   
+   >          1 <= I <= NSPLIT, have been set to zero   
+   > \endverbatim   
+   >   
+   > \param[in] RTOL1   
+   > \verbatim   
+   >          RTOL1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] RTOL2   
+   > \verbatim   
+   >          RTOL2 is DOUBLE PRECISION   
+   >           Parameters for bisection.   
+   >           An interval [LEFT,RIGHT] has converged if   
+   >           RIGHT-LEFT.LT.MAX( RTOL1*GAP, RTOL2*MAX(|LEFT|,|RIGHT|) )   
+   > \endverbatim   
+   >   
+   > \param[in] SPLTOL   
+   > \verbatim   
+   >          SPLTOL is DOUBLE PRECISION   
+   >          The threshold for splitting.   
+   > \endverbatim   
+   >   
+   > \param[out] NSPLIT   
+   > \verbatim   
+   >          NSPLIT is INTEGER   
+   >          The number of blocks T splits into. 1 <= NSPLIT <= N.   
+   > \endverbatim   
+   >   
+   > \param[out] ISPLIT   
+   > \verbatim   
+   >          ISPLIT is INTEGER array, dimension (N)   
+   >          The splitting points, at which T breaks up into blocks.   
+   >          The first block consists of rows/columns 1 to ISPLIT(1),   
+   >          the second of rows/columns ISPLIT(1)+1 through ISPLIT(2),   
+   >          etc., and the NSPLIT-th consists of rows/columns   
+   >          ISPLIT(NSPLIT-1)+1 through ISPLIT(NSPLIT)=N.   
+   > \endverbatim   
+   >   
+   > \param[out] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The total number of eigenvalues (of all L_i D_i L_i^T)   
+   >          found.   
+   > \endverbatim   
+   >   
+   > \param[out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          The first M elements contain the eigenvalues. The   
+   >          eigenvalues of each of the blocks, L_i D_i L_i^T, are   
+   >          sorted in ascending order ( DLARRE may use the   
+   >          remaining N-M elements as workspace).   
+   > \endverbatim   
+   >   
+   > \param[out] WERR   
+   > \verbatim   
+   >          WERR is DOUBLE PRECISION array, dimension (N)   
+   >          The error bound on the corresponding eigenvalue in W.   
+   > \endverbatim   
+   >   
+   > \param[out] WGAP   
+   > \verbatim   
+   >          WGAP is DOUBLE PRECISION array, dimension (N)   
+   >          The separation from the right neighbor eigenvalue in W.   
+   >          The gap is only with respect to the eigenvalues of the same block   
+   >          as each block has its own representation tree.   
+   >          Exception: at the right end of a block we store the left gap   
+   > \endverbatim   
+   >   
+   > \param[out] IBLOCK   
+   > \verbatim   
+   >          IBLOCK is INTEGER array, dimension (N)   
+   >          The indices of the blocks (submatrices) associated with the   
+   >          corresponding eigenvalues in W; IBLOCK(i)=1 if eigenvalue   
+   >          W(i) belongs to the first block from the top, =2 if W(i)   
+   >          belongs to the second block, etc.   
+   > \endverbatim   
+   >   
+   > \param[out] INDEXW   
+   > \verbatim   
+   >          INDEXW is INTEGER array, dimension (N)   
+   >          The indices of the eigenvalues within each block (submatrix);   
+   >          for example, INDEXW(i)= 10 and IBLOCK(i)=2 imply that the   
+   >          i-th eigenvalue W(i) is the 10-th eigenvalue in block 2   
+   > \endverbatim   
+   >   
+   > \param[out] GERS   
+   > \verbatim   
+   >          GERS is DOUBLE PRECISION array, dimension (2*N)   
+   >          The N Gerschgorin intervals (the i-th Gerschgorin interval   
+   >          is (GERS(2*i-1), GERS(2*i)).   
+   > \endverbatim   
+   >   
+   > \param[out] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot in the Sturm sequence for T.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (6*N)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (5*N)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          > 0:  A problem occured in DLARRE.   
+   >          < 0:  One of the called subroutines signaled an internal problem.   
+   >                Needs inspection of the corresponding parameter IINFO   
+   >                for further information.   
+   >   
+   >          =-1:  Problem in DLARRD.   
+   >          = 2:  No base representation could be found in MAXTRY iterations.   
+   >                Increasing MAXTRY and recompilation might be a remedy.   
+   >          =-3:  Problem in DLARRB when computing the refined root   
+   >                representation for DLASQ2.   
+   >          =-4:  Problem in DLARRB when preforming bisection on the   
+   >                desired part of the spectrum.   
+   >          =-5:  Problem in DLASQ2.   
+   >          =-6:  Problem in DLASQ2.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The base representations are required to suffer very little   
+   >  element growth and consequently define all their eigenvalues to   
+   >  high relative accuracy.   
+   > \endverbatim   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >     Beresford Parlett, University of California, Berkeley, USA \n   
+   >     Jim Demmel, University of California, Berkeley, USA \n   
+   >     Inderjit Dhillon, University of Texas, Austin, USA \n   
+   >     Osni Marques, LBNL/NERSC, USA \n   
+   >     Christof Voemel, University of California, Berkeley, USA \n   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlarre_(char *range, integer *n, doublereal *vl, 
+	doublereal *vu, integer *il, integer *iu, doublereal *d__, doublereal 
+	*e, doublereal *e2, doublereal *rtol1, doublereal *rtol2, doublereal *
+	spltol, integer *nsplit, integer *isplit, integer *m, doublereal *w, 
+	doublereal *werr, doublereal *wgap, integer *iblock, integer *indexw, 
+	doublereal *gers, doublereal *pivmin, doublereal *work, integer *
+	iwork, integer *info)
+{
+    /* System generated locals */
+    integer i__1, i__2;
+    doublereal d__1, d__2, d__3;
+
+    /* Builtin functions */
+    double sqrt(doublereal), log(doublereal);
+
+    /* Local variables */
+    integer i__, j;
+    doublereal s1, s2;
+    integer mb;
+    doublereal gl;
+    integer in, mm;
+    doublereal gu;
+    integer cnt;
+    doublereal eps, tau, tmp, rtl;
+    integer cnt1, cnt2;
+    doublereal tmp1, eabs;
+    integer iend, jblk;
+    doublereal eold;
+    integer indl;
+    doublereal dmax__, emax;
+    integer wend, idum, indu;
+    doublereal rtol;
+    integer iseed[4];
+    doublereal avgap, sigma;
+    extern logical igraphlsame_(char *, char *);
+    integer iinfo;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    logical norep;
+    extern /* Subroutine */ int igraphdlasq2_(integer *, doublereal *, integer *);
+    extern doublereal igraphdlamch_(char *);
+    integer ibegin;
+    logical forceb;
+    integer irange;
+    doublereal sgndef;
+    extern /* Subroutine */ int igraphdlarra_(integer *, doublereal *, doublereal *,
+	     doublereal *, doublereal *, doublereal *, integer *, integer *, 
+	    integer *), igraphdlarrb_(integer *, doublereal *, doublereal *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, integer *,
+	     doublereal *, doublereal *, integer *, integer *), igraphdlarrc_(char *
+	    , integer *, doublereal *, doublereal *, doublereal *, doublereal 
+	    *, doublereal *, integer *, integer *, integer *, integer *);
+    integer wbegin;
+    extern /* Subroutine */ int igraphdlarrd_(char *, char *, integer *, doublereal 
+	    *, doublereal *, integer *, integer *, doublereal *, doublereal *,
+	     doublereal *, doublereal *, doublereal *, doublereal *, integer *
+	    , integer *, integer *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *);
+    doublereal safmin, spdiam;
+    extern /* Subroutine */ int igraphdlarrk_(integer *, integer *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, integer *);
+    logical usedqd;
+    doublereal clwdth, isleft;
+    extern /* Subroutine */ int igraphdlarnv_(integer *, integer *, integer *, 
+	    doublereal *);
+    doublereal isrght, bsrtol, dpivot;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --iwork;
+    --work;
+    --gers;
+    --indexw;
+    --iblock;
+    --wgap;
+    --werr;
+    --w;
+    --isplit;
+    --e2;
+    --e;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+
+/*     Decode RANGE */
+
+    if (igraphlsame_(range, "A")) {
+	irange = 1;
+    } else if (igraphlsame_(range, "V")) {
+	irange = 3;
+    } else if (igraphlsame_(range, "I")) {
+	irange = 2;
+    }
+    *m = 0;
+/*     Get machine constants */
+    safmin = igraphdlamch_("S");
+    eps = igraphdlamch_("P");
+/*     Set parameters */
+    rtl = sqrt(eps);
+    bsrtol = sqrt(eps);
+/*     Treat case of 1x1 matrix for quick return */
+    if (*n == 1) {
+	if (irange == 1 || irange == 3 && d__[1] > *vl && d__[1] <= *vu || 
+		irange == 2 && *il == 1 && *iu == 1) {
+	    *m = 1;
+	    w[1] = d__[1];
+/*           The computation error of the eigenvalue is zero */
+	    werr[1] = 0.;
+	    wgap[1] = 0.;
+	    iblock[1] = 1;
+	    indexw[1] = 1;
+	    gers[1] = d__[1];
+	    gers[2] = d__[1];
+	}
+/*        store the shift for the initial RRR, which is zero in this case */
+	e[1] = 0.;
+	return 0;
+    }
+/*     General case: tridiagonal matrix of order > 1   
+
+       Init WERR, WGAP. Compute Gerschgorin intervals and spectral diameter.   
+       Compute maximum off-diagonal entry and pivmin. */
+    gl = d__[1];
+    gu = d__[1];
+    eold = 0.;
+    emax = 0.;
+    e[*n] = 0.;
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	werr[i__] = 0.;
+	wgap[i__] = 0.;
+	eabs = (d__1 = e[i__], abs(d__1));
+	if (eabs >= emax) {
+	    emax = eabs;
+	}
+	tmp1 = eabs + eold;
+	gers[(i__ << 1) - 1] = d__[i__] - tmp1;
+/* Computing MIN */
+	d__1 = gl, d__2 = gers[(i__ << 1) - 1];
+	gl = min(d__1,d__2);
+	gers[i__ * 2] = d__[i__] + tmp1;
+/* Computing MAX */
+	d__1 = gu, d__2 = gers[i__ * 2];
+	gu = max(d__1,d__2);
+	eold = eabs;
+/* L5: */
+    }
+/*     The minimum pivot allowed in the Sturm sequence for T   
+   Computing MAX   
+   Computing 2nd power */
+    d__3 = emax;
+    d__1 = 1., d__2 = d__3 * d__3;
+    *pivmin = safmin * max(d__1,d__2);
+/*     Compute spectral diameter. The Gerschgorin bounds give an   
+       estimate that is wrong by at most a factor of SQRT(2) */
+    spdiam = gu - gl;
+/*     Compute splitting points */
+    igraphdlarra_(n, &d__[1], &e[1], &e2[1], spltol, &spdiam, nsplit, &isplit[1], &
+	    iinfo);
+/*     Can force use of bisection instead of faster DQDS.   
+       Option left in the code for future multisection work. */
+    forceb = FALSE_;
+/*     Initialize USEDQD, DQDS should be used for ALLRNG unless someone   
+       explicitly wants bisection. */
+    usedqd = irange == 1 && ! forceb;
+    if (irange == 1 && ! forceb) {
+/*        Set interval [VL,VU] that contains all eigenvalues */
+	*vl = gl;
+	*vu = gu;
+    } else {
+/*        We call DLARRD to find crude approximations to the eigenvalues   
+          in the desired range. In case IRANGE = INDRNG, we also obtain the   
+          interval (VL,VU] that contains all the wanted eigenvalues.   
+          An interval [LEFT,RIGHT] has converged if   
+          RIGHT-LEFT.LT.RTOL*MAX(ABS(LEFT),ABS(RIGHT))   
+          DLARRD needs a WORK of size 4*N, IWORK of size 3*N */
+	igraphdlarrd_(range, "B", n, vl, vu, il, iu, &gers[1], &bsrtol, &d__[1], &e[
+		1], &e2[1], pivmin, nsplit, &isplit[1], &mm, &w[1], &werr[1], 
+		vl, vu, &iblock[1], &indexw[1], &work[1], &iwork[1], &iinfo);
+	if (iinfo != 0) {
+	    *info = -1;
+	    return 0;
+	}
+/*        Make sure that the entries M+1 to N in W, WERR, IBLOCK, INDEXW are 0 */
+	i__1 = *n;
+	for (i__ = mm + 1; i__ <= i__1; ++i__) {
+	    w[i__] = 0.;
+	    werr[i__] = 0.;
+	    iblock[i__] = 0;
+	    indexw[i__] = 0;
+/* L14: */
+	}
+    }
+/* **   
+       Loop over unreduced blocks */
+    ibegin = 1;
+    wbegin = 1;
+    i__1 = *nsplit;
+    for (jblk = 1; jblk <= i__1; ++jblk) {
+	iend = isplit[jblk];
+	in = iend - ibegin + 1;
+/*        1 X 1 block */
+	if (in == 1) {
+	    if (irange == 1 || irange == 3 && d__[ibegin] > *vl && d__[ibegin]
+		     <= *vu || irange == 2 && iblock[wbegin] == jblk) {
+		++(*m);
+		w[*m] = d__[ibegin];
+		werr[*m] = 0.;
+/*              The gap for a single block doesn't matter for the later   
+                algorithm and is assigned an arbitrary large value */
+		wgap[*m] = 0.;
+		iblock[*m] = jblk;
+		indexw[*m] = 1;
+		++wbegin;
+	    }
+/*           E( IEND ) holds the shift for the initial RRR */
+	    e[iend] = 0.;
+	    ibegin = iend + 1;
+	    goto L170;
+	}
+
+/*        Blocks of size larger than 1x1   
+
+          E( IEND ) will hold the shift for the initial RRR, for now set it =0 */
+	e[iend] = 0.;
+
+/*        Find local outer bounds GL,GU for the block */
+	gl = d__[ibegin];
+	gu = d__[ibegin];
+	i__2 = iend;
+	for (i__ = ibegin; i__ <= i__2; ++i__) {
+/* Computing MIN */
+	    d__1 = gers[(i__ << 1) - 1];
+	    gl = min(d__1,gl);
+/* Computing MAX */
+	    d__1 = gers[i__ * 2];
+	    gu = max(d__1,gu);
+/* L15: */
+	}
+	spdiam = gu - gl;
+	if (! (irange == 1 && ! forceb)) {
+/*           Count the number of eigenvalues in the current block. */
+	    mb = 0;
+	    i__2 = mm;
+	    for (i__ = wbegin; i__ <= i__2; ++i__) {
+		if (iblock[i__] == jblk) {
+		    ++mb;
+		} else {
+		    goto L21;
+		}
+/* L20: */
+	    }
+L21:
+	    if (mb == 0) {
+/*              No eigenvalue in the current block lies in the desired range   
+                E( IEND ) holds the shift for the initial RRR */
+		e[iend] = 0.;
+		ibegin = iend + 1;
+		goto L170;
+	    } else {
+/*              Decide whether dqds or bisection is more efficient */
+		usedqd = (doublereal) mb > in * .5 && ! forceb;
+		wend = wbegin + mb - 1;
+/*              Calculate gaps for the current block   
+                In later stages, when representations for individual   
+                eigenvalues are different, we use SIGMA = E( IEND ). */
+		sigma = 0.;
+		i__2 = wend - 1;
+		for (i__ = wbegin; i__ <= i__2; ++i__) {
+/* Computing MAX */
+		    d__1 = 0., d__2 = w[i__ + 1] - werr[i__ + 1] - (w[i__] + 
+			    werr[i__]);
+		    wgap[i__] = max(d__1,d__2);
+/* L30: */
+		}
+/* Computing MAX */
+		d__1 = 0., d__2 = *vu - sigma - (w[wend] + werr[wend]);
+		wgap[wend] = max(d__1,d__2);
+/*              Find local index of the first and last desired evalue. */
+		indl = indexw[wbegin];
+		indu = indexw[wend];
+	    }
+	}
+	if (irange == 1 && ! forceb || usedqd) {
+/*           Case of DQDS   
+             Find approximations to the extremal eigenvalues of the block */
+	    igraphdlarrk_(&in, &c__1, &gl, &gu, &d__[ibegin], &e2[ibegin], pivmin, &
+		    rtl, &tmp, &tmp1, &iinfo);
+	    if (iinfo != 0) {
+		*info = -1;
+		return 0;
+	    }
+/* Computing MAX */
+	    d__2 = gl, d__3 = tmp - tmp1 - eps * 100. * (d__1 = tmp - tmp1, 
+		    abs(d__1));
+	    isleft = max(d__2,d__3);
+	    igraphdlarrk_(&in, &in, &gl, &gu, &d__[ibegin], &e2[ibegin], pivmin, &
+		    rtl, &tmp, &tmp1, &iinfo);
+	    if (iinfo != 0) {
+		*info = -1;
+		return 0;
+	    }
+/* Computing MIN */
+	    d__2 = gu, d__3 = tmp + tmp1 + eps * 100. * (d__1 = tmp + tmp1, 
+		    abs(d__1));
+	    isrght = min(d__2,d__3);
+/*           Improve the estimate of the spectral diameter */
+	    spdiam = isrght - isleft;
+	} else {
+/*           Case of bisection   
+             Find approximations to the wanted extremal eigenvalues   
+   Computing MAX */
+	    d__2 = gl, d__3 = w[wbegin] - werr[wbegin] - eps * 100. * (d__1 = 
+		    w[wbegin] - werr[wbegin], abs(d__1));
+	    isleft = max(d__2,d__3);
+/* Computing MIN */
+	    d__2 = gu, d__3 = w[wend] + werr[wend] + eps * 100. * (d__1 = w[
+		    wend] + werr[wend], abs(d__1));
+	    isrght = min(d__2,d__3);
+	}
+/*        Decide whether the base representation for the current block   
+          L_JBLK D_JBLK L_JBLK^T = T_JBLK - sigma_JBLK I   
+          should be on the left or the right end of the current block.   
+          The strategy is to shift to the end which is "more populated"   
+          Furthermore, decide whether to use DQDS for the computation of   
+          the eigenvalue approximations at the end of DLARRE or bisection.   
+          dqds is chosen if all eigenvalues are desired or the number of   
+          eigenvalues to be computed is large compared to the blocksize. */
+	if (irange == 1 && ! forceb) {
+/*           If all the eigenvalues have to be computed, we use dqd */
+	    usedqd = TRUE_;
+/*           INDL is the local index of the first eigenvalue to compute */
+	    indl = 1;
+	    indu = in;
+/*           MB =  number of eigenvalues to compute */
+	    mb = in;
+	    wend = wbegin + mb - 1;
+/*           Define 1/4 and 3/4 points of the spectrum */
+	    s1 = isleft + spdiam * .25;
+	    s2 = isrght - spdiam * .25;
+	} else {
+/*           DLARRD has computed IBLOCK and INDEXW for each eigenvalue   
+             approximation.   
+             choose sigma */
+	    if (usedqd) {
+		s1 = isleft + spdiam * .25;
+		s2 = isrght - spdiam * .25;
+	    } else {
+		tmp = min(isrght,*vu) - max(isleft,*vl);
+		s1 = max(isleft,*vl) + tmp * .25;
+		s2 = min(isrght,*vu) - tmp * .25;
+	    }
+	}
+/*        Compute the negcount at the 1/4 and 3/4 points */
+	if (mb > 1) {
+	    igraphdlarrc_("T", &in, &s1, &s2, &d__[ibegin], &e[ibegin], pivmin, &
+		    cnt, &cnt1, &cnt2, &iinfo);
+	}
+	if (mb == 1) {
+	    sigma = gl;
+	    sgndef = 1.;
+	} else if (cnt1 - indl >= indu - cnt2) {
+	    if (irange == 1 && ! forceb) {
+		sigma = max(isleft,gl);
+	    } else if (usedqd) {
+/*              use Gerschgorin bound as shift to get pos def matrix   
+                for dqds */
+		sigma = isleft;
+	    } else {
+/*              use approximation of the first desired eigenvalue of the   
+                block as shift */
+		sigma = max(isleft,*vl);
+	    }
+	    sgndef = 1.;
+	} else {
+	    if (irange == 1 && ! forceb) {
+		sigma = min(isrght,gu);
+	    } else if (usedqd) {
+/*              use Gerschgorin bound as shift to get neg def matrix   
+                for dqds */
+		sigma = isrght;
+	    } else {
+/*              use approximation of the first desired eigenvalue of the   
+                block as shift */
+		sigma = min(isrght,*vu);
+	    }
+	    sgndef = -1.;
+	}
+/*        An initial SIGMA has been chosen that will be used for computing   
+          T - SIGMA I = L D L^T   
+          Define the increment TAU of the shift in case the initial shift   
+          needs to be refined to obtain a factorization with not too much   
+          element growth. */
+	if (usedqd) {
+/*           The initial SIGMA was to the outer end of the spectrum   
+             the matrix is definite and we need not retreat. */
+	    tau = spdiam * eps * *n + *pivmin * 2.;
+/* Computing MAX */
+	    d__1 = tau, d__2 = eps * 2. * abs(sigma);
+	    tau = max(d__1,d__2);
+	} else {
+	    if (mb > 1) {
+		clwdth = w[wend] + werr[wend] - w[wbegin] - werr[wbegin];
+		avgap = (d__1 = clwdth / (doublereal) (wend - wbegin), abs(
+			d__1));
+		if (sgndef == 1.) {
+/* Computing MAX */
+		    d__1 = wgap[wbegin];
+		    tau = max(d__1,avgap) * .5;
+/* Computing MAX */
+		    d__1 = tau, d__2 = werr[wbegin];
+		    tau = max(d__1,d__2);
+		} else {
+/* Computing MAX */
+		    d__1 = wgap[wend - 1];
+		    tau = max(d__1,avgap) * .5;
+/* Computing MAX */
+		    d__1 = tau, d__2 = werr[wend];
+		    tau = max(d__1,d__2);
+		}
+	    } else {
+		tau = werr[wbegin];
+	    }
+	}
+
+	for (idum = 1; idum <= 6; ++idum) {
+/*           Compute L D L^T factorization of tridiagonal matrix T - sigma I.   
+             Store D in WORK(1:IN), L in WORK(IN+1:2*IN), and reciprocals of   
+             pivots in WORK(2*IN+1:3*IN) */
+	    dpivot = d__[ibegin] - sigma;
+	    work[1] = dpivot;
+	    dmax__ = abs(work[1]);
+	    j = ibegin;
+	    i__2 = in - 1;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		work[(in << 1) + i__] = 1. / work[i__];
+		tmp = e[j] * work[(in << 1) + i__];
+		work[in + i__] = tmp;
+		dpivot = d__[j + 1] - sigma - tmp * e[j];
+		work[i__ + 1] = dpivot;
+/* Computing MAX */
+		d__1 = dmax__, d__2 = abs(dpivot);
+		dmax__ = max(d__1,d__2);
+		++j;
+/* L70: */
+	    }
+/*           check for element growth */
+	    if (dmax__ > spdiam * 64.) {
+		norep = TRUE_;
+	    } else {
+		norep = FALSE_;
+	    }
+	    if (usedqd && ! norep) {
+/*              Ensure the definiteness of the representation   
+                All entries of D (of L D L^T) must have the same sign */
+		i__2 = in;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    tmp = sgndef * work[i__];
+		    if (tmp < 0.) {
+			norep = TRUE_;
+		    }
+/* L71: */
+		}
+	    }
+	    if (norep) {
+/*              Note that in the case of IRANGE=ALLRNG, we use the Gerschgorin   
+                shift which makes the matrix definite. So we should end up   
+                here really only in the case of IRANGE = VALRNG or INDRNG. */
+		if (idum == 5) {
+		    if (sgndef == 1.) {
+/*                    The fudged Gerschgorin shift should succeed */
+			sigma = gl - spdiam * 2. * eps * *n - *pivmin * 4.;
+		    } else {
+			sigma = gu + spdiam * 2. * eps * *n + *pivmin * 4.;
+		    }
+		} else {
+		    sigma -= sgndef * tau;
+		    tau *= 2.;
+		}
+	    } else {
+/*              an initial RRR is found */
+		goto L83;
+	    }
+/* L80: */
+	}
+/*        if the program reaches this point, no base representation could be   
+          found in MAXTRY iterations. */
+	*info = 2;
+	return 0;
+L83:
+/*        At this point, we have found an initial base representation   
+          T - SIGMA I = L D L^T with not too much element growth.   
+          Store the shift. */
+	e[iend] = sigma;
+/*        Store D and L. */
+	igraphdcopy_(&in, &work[1], &c__1, &d__[ibegin], &c__1);
+	i__2 = in - 1;
+	igraphdcopy_(&i__2, &work[in + 1], &c__1, &e[ibegin], &c__1);
+	if (mb > 1) {
+
+/*           Perturb each entry of the base representation by a small   
+             (but random) relative amount to overcome difficulties with   
+             glued matrices. */
+
+	    for (i__ = 1; i__ <= 4; ++i__) {
+		iseed[i__ - 1] = 1;
+/* L122: */
+	    }
+	    i__2 = (in << 1) - 1;
+	    igraphdlarnv_(&c__2, iseed, &i__2, &work[1]);
+	    i__2 = in - 1;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		d__[ibegin + i__ - 1] *= eps * 8. * work[i__] + 1.;
+		e[ibegin + i__ - 1] *= eps * 8. * work[in + i__] + 1.;
+/* L125: */
+	    }
+	    d__[iend] *= eps * 4. * work[in] + 1.;
+
+	}
+
+/*        Don't update the Gerschgorin intervals because keeping track   
+          of the updates would be too much work in DLARRV.   
+          We update W instead and use it to locate the proper Gerschgorin   
+          intervals.   
+          Compute the required eigenvalues of L D L' by bisection or dqds */
+	if (! usedqd) {
+/*           If DLARRD has been used, shift the eigenvalue approximations   
+             according to their representation. This is necessary for   
+             a uniform DLARRV since dqds computes eigenvalues of the   
+             shifted representation. In DLARRV, W will always hold the   
+             UNshifted eigenvalue approximation. */
+	    i__2 = wend;
+	    for (j = wbegin; j <= i__2; ++j) {
+		w[j] -= sigma;
+		werr[j] += (d__1 = w[j], abs(d__1)) * eps;
+/* L134: */
+	    }
+/*           call DLARRB to reduce eigenvalue error of the approximations   
+             from DLARRD */
+	    i__2 = iend - 1;
+	    for (i__ = ibegin; i__ <= i__2; ++i__) {
+/* Computing 2nd power */
+		d__1 = e[i__];
+		work[i__] = d__[i__] * (d__1 * d__1);
+/* L135: */
+	    }
+/*           use bisection to find EV from INDL to INDU */
+	    i__2 = indl - 1;
+	    igraphdlarrb_(&in, &d__[ibegin], &work[ibegin], &indl, &indu, rtol1, 
+		    rtol2, &i__2, &w[wbegin], &wgap[wbegin], &werr[wbegin], &
+		    work[(*n << 1) + 1], &iwork[1], pivmin, &spdiam, &in, &
+		    iinfo);
+	    if (iinfo != 0) {
+		*info = -4;
+		return 0;
+	    }
+/*           DLARRB computes all gaps correctly except for the last one   
+             Record distance to VU/GU   
+   Computing MAX */
+	    d__1 = 0., d__2 = *vu - sigma - (w[wend] + werr[wend]);
+	    wgap[wend] = max(d__1,d__2);
+	    i__2 = indu;
+	    for (i__ = indl; i__ <= i__2; ++i__) {
+		++(*m);
+		iblock[*m] = jblk;
+		indexw[*m] = i__;
+/* L138: */
+	    }
+	} else {
+/*           Call dqds to get all eigs (and then possibly delete unwanted   
+             eigenvalues).   
+             Note that dqds finds the eigenvalues of the L D L^T representation   
+             of T to high relative accuracy. High relative accuracy   
+             might be lost when the shift of the RRR is subtracted to obtain   
+             the eigenvalues of T. However, T is not guaranteed to define its   
+             eigenvalues to high relative accuracy anyway.   
+             Set RTOL to the order of the tolerance used in DLASQ2   
+             This is an ESTIMATED error, the worst case bound is 4*N*EPS   
+             which is usually too large and requires unnecessary work to be   
+             done by bisection when computing the eigenvectors */
+	    rtol = log((doublereal) in) * 4. * eps;
+	    j = ibegin;
+	    i__2 = in - 1;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		work[(i__ << 1) - 1] = (d__1 = d__[j], abs(d__1));
+		work[i__ * 2] = e[j] * e[j] * work[(i__ << 1) - 1];
+		++j;
+/* L140: */
+	    }
+	    work[(in << 1) - 1] = (d__1 = d__[iend], abs(d__1));
+	    work[in * 2] = 0.;
+	    igraphdlasq2_(&in, &work[1], &iinfo);
+	    if (iinfo != 0) {
+/*              If IINFO = -5 then an index is part of a tight cluster   
+                and should be changed. The index is in IWORK(1) and the   
+                gap is in WORK(N+1) */
+		*info = -5;
+		return 0;
+	    } else {
+/*              Test that all eigenvalues are positive as expected */
+		i__2 = in;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    if (work[i__] < 0.) {
+			*info = -6;
+			return 0;
+		    }
+/* L149: */
+		}
+	    }
+	    if (sgndef > 0.) {
+		i__2 = indu;
+		for (i__ = indl; i__ <= i__2; ++i__) {
+		    ++(*m);
+		    w[*m] = work[in - i__ + 1];
+		    iblock[*m] = jblk;
+		    indexw[*m] = i__;
+/* L150: */
+		}
+	    } else {
+		i__2 = indu;
+		for (i__ = indl; i__ <= i__2; ++i__) {
+		    ++(*m);
+		    w[*m] = -work[i__];
+		    iblock[*m] = jblk;
+		    indexw[*m] = i__;
+/* L160: */
+		}
+	    }
+	    i__2 = *m;
+	    for (i__ = *m - mb + 1; i__ <= i__2; ++i__) {
+/*              the value of RTOL below should be the tolerance in DLASQ2 */
+		werr[i__] = rtol * (d__1 = w[i__], abs(d__1));
+/* L165: */
+	    }
+	    i__2 = *m - 1;
+	    for (i__ = *m - mb + 1; i__ <= i__2; ++i__) {
+/*              compute the right gap between the intervals   
+   Computing MAX */
+		d__1 = 0., d__2 = w[i__ + 1] - werr[i__ + 1] - (w[i__] + werr[
+			i__]);
+		wgap[i__] = max(d__1,d__2);
+/* L166: */
+	    }
+/* Computing MAX */
+	    d__1 = 0., d__2 = *vu - sigma - (w[*m] + werr[*m]);
+	    wgap[*m] = max(d__1,d__2);
+	}
+/*        proceed with next block */
+	ibegin = iend + 1;
+	wbegin = wend + 1;
+L170:
+	;
+    }
+
+    return 0;
+
+/*     end of DLARRE */
+
+} /* igraphdlarre_ */
+
diff --git a/igraph/src/dlarrf.c b/igraph/src/dlarrf.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarrf.c
@@ -0,0 +1,523 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DLARRF finds a new relatively robust representation such that at least one of the eigenvalues i
+s relatively isolated.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRF + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarrf.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarrf.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarrf.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRF( N, D, L, LD, CLSTRT, CLEND,   
+                            W, WGAP, WERR,   
+                            SPDIAM, CLGAPL, CLGAPR, PIVMIN, SIGMA,   
+                            DPLUS, LPLUS, WORK, INFO )   
+
+         INTEGER            CLSTRT, CLEND, INFO, N   
+         DOUBLE PRECISION   CLGAPL, CLGAPR, PIVMIN, SIGMA, SPDIAM   
+         DOUBLE PRECISION   D( * ), DPLUS( * ), L( * ), LD( * ),   
+        $          LPLUS( * ), W( * ), WGAP( * ), WERR( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > Given the initial representation L D L^T and its cluster of close   
+   > eigenvalues (in a relative measure), W( CLSTRT ), W( CLSTRT+1 ), ...   
+   > W( CLEND ), DLARRF finds a new relatively robust representation   
+   > L D L^T - SIGMA I = L(+) D(+) L(+)^T such that at least one of the   
+   > eigenvalues of L(+) D(+) L(+)^T is relatively isolated.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix (subblock, if the matrix splitted).   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The N diagonal elements of the diagonal matrix D.   
+   > \endverbatim   
+   >   
+   > \param[in] L   
+   > \verbatim   
+   >          L is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (N-1) subdiagonal elements of the unit bidiagonal   
+   >          matrix L.   
+   > \endverbatim   
+   >   
+   > \param[in] LD   
+   > \verbatim   
+   >          LD is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (N-1) elements L(i)*D(i).   
+   > \endverbatim   
+   >   
+   > \param[in] CLSTRT   
+   > \verbatim   
+   >          CLSTRT is INTEGER   
+   >          The index of the first eigenvalue in the cluster.   
+   > \endverbatim   
+   >   
+   > \param[in] CLEND   
+   > \verbatim   
+   >          CLEND is INTEGER   
+   >          The index of the last eigenvalue in the cluster.   
+   > \endverbatim   
+   >   
+   > \param[in] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension   
+   >          dimension is >=  (CLEND-CLSTRT+1)   
+   >          The eigenvalue APPROXIMATIONS of L D L^T in ascending order.   
+   >          W( CLSTRT ) through W( CLEND ) form the cluster of relatively   
+   >          close eigenalues.   
+   > \endverbatim   
+   >   
+   > \param[in,out] WGAP   
+   > \verbatim   
+   >          WGAP is DOUBLE PRECISION array, dimension   
+   >          dimension is >=  (CLEND-CLSTRT+1)   
+   >          The separation from the right neighbor eigenvalue in W.   
+   > \endverbatim   
+   >   
+   > \param[in] WERR   
+   > \verbatim   
+   >          WERR is DOUBLE PRECISION array, dimension   
+   >          dimension is  >=  (CLEND-CLSTRT+1)   
+   >          WERR contain the semiwidth of the uncertainty   
+   >          interval of the corresponding eigenvalue APPROXIMATION in W   
+   > \endverbatim   
+   >   
+   > \param[in] SPDIAM   
+   > \verbatim   
+   >          SPDIAM is DOUBLE PRECISION   
+   >          estimate of the spectral diameter obtained from the   
+   >          Gerschgorin intervals   
+   > \endverbatim   
+   >   
+   > \param[in] CLGAPL   
+   > \verbatim   
+   >          CLGAPL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] CLGAPR   
+   > \verbatim   
+   >          CLGAPR is DOUBLE PRECISION   
+   >          absolute gap on each end of the cluster.   
+   >          Set by the calling routine to protect against shifts too close   
+   >          to eigenvalues outside the cluster.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot allowed in the Sturm sequence.   
+   > \endverbatim   
+   >   
+   > \param[out] SIGMA   
+   > \verbatim   
+   >          SIGMA is DOUBLE PRECISION   
+   >          The shift used to form L(+) D(+) L(+)^T.   
+   > \endverbatim   
+   >   
+   > \param[out] DPLUS   
+   > \verbatim   
+   >          DPLUS is DOUBLE PRECISION array, dimension (N)   
+   >          The N diagonal elements of the diagonal matrix D(+).   
+   > \endverbatim   
+   >   
+   > \param[out] LPLUS   
+   > \verbatim   
+   >          LPLUS is DOUBLE PRECISION array, dimension (N-1)   
+   >          The first (N-1) elements of LPLUS contain the subdiagonal   
+   >          elements of the unit bidiagonal matrix L(+).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (2*N)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          Signals processing OK (=0) or failure (=1)   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarrf_(integer *n, doublereal *d__, doublereal *l, 
+	doublereal *ld, integer *clstrt, integer *clend, doublereal *w, 
+	doublereal *wgap, doublereal *werr, doublereal *spdiam, doublereal *
+	clgapl, doublereal *clgapr, doublereal *pivmin, doublereal *sigma, 
+	doublereal *dplus, doublereal *lplus, doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2, d__3;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__;
+    doublereal s, bestshift, smlgrowth, eps, tmp, max1, max2, rrr1, rrr2, 
+	    znm2, growthbound, fail, fact, oldp;
+    integer indx;
+    doublereal prod;
+    integer ktry;
+    doublereal fail2, avgap, ldmax, rdmax;
+    integer shift;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    logical dorrr1;
+    extern doublereal igraphdlamch_(char *);
+    doublereal ldelta;
+    logical nofail;
+    doublereal mingap, lsigma, rdelta;
+    extern logical igraphdisnan_(doublereal *);
+    logical forcer;
+    doublereal rsigma, clwdth;
+    logical sawnan1, sawnan2, tryrrr1;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --work;
+    --lplus;
+    --dplus;
+    --werr;
+    --wgap;
+    --w;
+    --ld;
+    --l;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+    fact = 2.;
+    eps = igraphdlamch_("Precision");
+    shift = 0;
+    forcer = FALSE_;
+/*     Note that we cannot guarantee that for any of the shifts tried,   
+       the factorization has a small or even moderate element growth.   
+       There could be Ritz values at both ends of the cluster and despite   
+       backing off, there are examples where all factorizations tried   
+       (in IEEE mode, allowing zero pivots & infinities) have INFINITE   
+       element growth.   
+       For this reason, we should use PIVMIN in this subroutine so that at   
+       least the L D L^T factorization exists. It can be checked afterwards   
+       whether the element growth caused bad residuals/orthogonality.   
+       Decide whether the code should accept the best among all   
+       representations despite large element growth or signal INFO=1 */
+    nofail = TRUE_;
+
+/*     Compute the average gap length of the cluster */
+    clwdth = (d__1 = w[*clend] - w[*clstrt], abs(d__1)) + werr[*clend] + werr[
+	    *clstrt];
+    avgap = clwdth / (doublereal) (*clend - *clstrt);
+    mingap = min(*clgapl,*clgapr);
+/*     Initial values for shifts to both ends of cluster   
+   Computing MIN */
+    d__1 = w[*clstrt], d__2 = w[*clend];
+    lsigma = min(d__1,d__2) - werr[*clstrt];
+/* Computing MAX */
+    d__1 = w[*clstrt], d__2 = w[*clend];
+    rsigma = max(d__1,d__2) + werr[*clend];
+/*     Use a small fudge to make sure that we really shift to the outside */
+    lsigma -= abs(lsigma) * 4. * eps;
+    rsigma += abs(rsigma) * 4. * eps;
+/*     Compute upper bounds for how much to back off the initial shifts */
+    ldmax = mingap * .25 + *pivmin * 2.;
+    rdmax = mingap * .25 + *pivmin * 2.;
+/* Computing MAX */
+    d__1 = avgap, d__2 = wgap[*clstrt];
+    ldelta = max(d__1,d__2) / fact;
+/* Computing MAX */
+    d__1 = avgap, d__2 = wgap[*clend - 1];
+    rdelta = max(d__1,d__2) / fact;
+
+/*     Initialize the record of the best representation found */
+
+    s = igraphdlamch_("S");
+    smlgrowth = 1. / s;
+    fail = (doublereal) (*n - 1) * mingap / (*spdiam * eps);
+    fail2 = (doublereal) (*n - 1) * mingap / (*spdiam * sqrt(eps));
+    bestshift = lsigma;
+
+/*     while (KTRY <= KTRYMAX) */
+    ktry = 0;
+    growthbound = *spdiam * 8.;
+L5:
+    sawnan1 = FALSE_;
+    sawnan2 = FALSE_;
+/*     Ensure that we do not back off too much of the initial shifts */
+    ldelta = min(ldmax,ldelta);
+    rdelta = min(rdmax,rdelta);
+/*     Compute the element growth when shifting to both ends of the cluster   
+       accept the shift if there is no element growth at one of the two ends   
+       Left end */
+    s = -lsigma;
+    dplus[1] = d__[1] + s;
+    if (abs(dplus[1]) < *pivmin) {
+	dplus[1] = -(*pivmin);
+/*        Need to set SAWNAN1 because refined RRR test should not be used   
+          in this case */
+	sawnan1 = TRUE_;
+    }
+    max1 = abs(dplus[1]);
+    i__1 = *n - 1;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	lplus[i__] = ld[i__] / dplus[i__];
+	s = s * lplus[i__] * l[i__] - lsigma;
+	dplus[i__ + 1] = d__[i__ + 1] + s;
+	if ((d__1 = dplus[i__ + 1], abs(d__1)) < *pivmin) {
+	    dplus[i__ + 1] = -(*pivmin);
+/*           Need to set SAWNAN1 because refined RRR test should not be used   
+             in this case */
+	    sawnan1 = TRUE_;
+	}
+/* Computing MAX */
+	d__2 = max1, d__3 = (d__1 = dplus[i__ + 1], abs(d__1));
+	max1 = max(d__2,d__3);
+/* L6: */
+    }
+    sawnan1 = sawnan1 || igraphdisnan_(&max1);
+    if (forcer || max1 <= growthbound && ! sawnan1) {
+	*sigma = lsigma;
+	shift = 1;
+	goto L100;
+    }
+/*     Right end */
+    s = -rsigma;
+    work[1] = d__[1] + s;
+    if (abs(work[1]) < *pivmin) {
+	work[1] = -(*pivmin);
+/*        Need to set SAWNAN2 because refined RRR test should not be used   
+          in this case */
+	sawnan2 = TRUE_;
+    }
+    max2 = abs(work[1]);
+    i__1 = *n - 1;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	work[*n + i__] = ld[i__] / work[i__];
+	s = s * work[*n + i__] * l[i__] - rsigma;
+	work[i__ + 1] = d__[i__ + 1] + s;
+	if ((d__1 = work[i__ + 1], abs(d__1)) < *pivmin) {
+	    work[i__ + 1] = -(*pivmin);
+/*           Need to set SAWNAN2 because refined RRR test should not be used   
+             in this case */
+	    sawnan2 = TRUE_;
+	}
+/* Computing MAX */
+	d__2 = max2, d__3 = (d__1 = work[i__ + 1], abs(d__1));
+	max2 = max(d__2,d__3);
+/* L7: */
+    }
+    sawnan2 = sawnan2 || igraphdisnan_(&max2);
+    if (forcer || max2 <= growthbound && ! sawnan2) {
+	*sigma = rsigma;
+	shift = 2;
+	goto L100;
+    }
+/*     If we are at this point, both shifts led to too much element growth   
+       Record the better of the two shifts (provided it didn't lead to NaN) */
+    if (sawnan1 && sawnan2) {
+/*        both MAX1 and MAX2 are NaN */
+	goto L50;
+    } else {
+	if (! sawnan1) {
+	    indx = 1;
+	    if (max1 <= smlgrowth) {
+		smlgrowth = max1;
+		bestshift = lsigma;
+	    }
+	}
+	if (! sawnan2) {
+	    if (sawnan1 || max2 <= max1) {
+		indx = 2;
+	    }
+	    if (max2 <= smlgrowth) {
+		smlgrowth = max2;
+		bestshift = rsigma;
+	    }
+	}
+    }
+/*     If we are here, both the left and the right shift led to   
+       element growth. If the element growth is moderate, then   
+       we may still accept the representation, if it passes a   
+       refined test for RRR. This test supposes that no NaN occurred.   
+       Moreover, we use the refined RRR test only for isolated clusters. */
+    if (clwdth < mingap / 128. && min(max1,max2) < fail2 && ! sawnan1 && ! 
+	    sawnan2) {
+	dorrr1 = TRUE_;
+    } else {
+	dorrr1 = FALSE_;
+    }
+    tryrrr1 = TRUE_;
+    if (tryrrr1 && dorrr1) {
+	if (indx == 1) {
+	    tmp = (d__1 = dplus[*n], abs(d__1));
+	    znm2 = 1.;
+	    prod = 1.;
+	    oldp = 1.;
+	    for (i__ = *n - 1; i__ >= 1; --i__) {
+		if (prod <= eps) {
+		    prod = dplus[i__ + 1] * work[*n + i__ + 1] / (dplus[i__] *
+			     work[*n + i__]) * oldp;
+		} else {
+		    prod *= (d__1 = work[*n + i__], abs(d__1));
+		}
+		oldp = prod;
+/* Computing 2nd power */
+		d__1 = prod;
+		znm2 += d__1 * d__1;
+/* Computing MAX */
+		d__2 = tmp, d__3 = (d__1 = dplus[i__] * prod, abs(d__1));
+		tmp = max(d__2,d__3);
+/* L15: */
+	    }
+	    rrr1 = tmp / (*spdiam * sqrt(znm2));
+	    if (rrr1 <= 8.) {
+		*sigma = lsigma;
+		shift = 1;
+		goto L100;
+	    }
+	} else if (indx == 2) {
+	    tmp = (d__1 = work[*n], abs(d__1));
+	    znm2 = 1.;
+	    prod = 1.;
+	    oldp = 1.;
+	    for (i__ = *n - 1; i__ >= 1; --i__) {
+		if (prod <= eps) {
+		    prod = work[i__ + 1] * lplus[i__ + 1] / (work[i__] * 
+			    lplus[i__]) * oldp;
+		} else {
+		    prod *= (d__1 = lplus[i__], abs(d__1));
+		}
+		oldp = prod;
+/* Computing 2nd power */
+		d__1 = prod;
+		znm2 += d__1 * d__1;
+/* Computing MAX */
+		d__2 = tmp, d__3 = (d__1 = work[i__] * prod, abs(d__1));
+		tmp = max(d__2,d__3);
+/* L16: */
+	    }
+	    rrr2 = tmp / (*spdiam * sqrt(znm2));
+	    if (rrr2 <= 8.) {
+		*sigma = rsigma;
+		shift = 2;
+		goto L100;
+	    }
+	}
+    }
+L50:
+    if (ktry < 1) {
+/*        If we are here, both shifts failed also the RRR test.   
+          Back off to the outside   
+   Computing MAX */
+	d__1 = lsigma - ldelta, d__2 = lsigma - ldmax;
+	lsigma = max(d__1,d__2);
+/* Computing MIN */
+	d__1 = rsigma + rdelta, d__2 = rsigma + rdmax;
+	rsigma = min(d__1,d__2);
+	ldelta *= 2.;
+	rdelta *= 2.;
+	++ktry;
+	goto L5;
+    } else {
+/*        None of the representations investigated satisfied our   
+          criteria. Take the best one we found. */
+	if (smlgrowth < fail || nofail) {
+	    lsigma = bestshift;
+	    rsigma = bestshift;
+	    forcer = TRUE_;
+	    goto L5;
+	} else {
+	    *info = 1;
+	    return 0;
+	}
+    }
+L100:
+    if (shift == 1) {
+    } else if (shift == 2) {
+/*        store new L and D back into DPLUS, LPLUS */
+	igraphdcopy_(n, &work[1], &c__1, &dplus[1], &c__1);
+	i__1 = *n - 1;
+	igraphdcopy_(&i__1, &work[*n + 1], &c__1, &lplus[1], &c__1);
+    }
+    return 0;
+
+/*     End of DLARRF */
+
+} /* igraphdlarrf_ */
+
diff --git a/igraph/src/dlarrj.c b/igraph/src/dlarrj.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarrj.c
@@ -0,0 +1,419 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARRJ performs refinement of the initial estimates of the eigenvalues of the matrix T.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRJ + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarrj.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarrj.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarrj.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRJ( N, D, E2, IFIRST, ILAST,   
+                            RTOL, OFFSET, W, WERR, WORK, IWORK,   
+                            PIVMIN, SPDIAM, INFO )   
+
+         INTEGER            IFIRST, ILAST, INFO, N, OFFSET   
+         DOUBLE PRECISION   PIVMIN, RTOL, SPDIAM   
+         INTEGER            IWORK( * )   
+         DOUBLE PRECISION   D( * ), E2( * ), W( * ),   
+        $                   WERR( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > Given the initial eigenvalue approximations of T, DLARRJ   
+   > does  bisection to refine the eigenvalues of T,   
+   > W( IFIRST-OFFSET ) through W( ILAST-OFFSET ), to more accuracy. Initial   
+   > guesses for these eigenvalues are input in W, the corresponding estimate   
+   > of the error in these guesses in WERR. During bisection, intervals   
+   > [left, right] are maintained by storing their mid-points and   
+   > semi-widths in the arrays W and WERR respectively.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The N diagonal elements of T.   
+   > \endverbatim   
+   >   
+   > \param[in] E2   
+   > \verbatim   
+   >          E2 is DOUBLE PRECISION array, dimension (N-1)   
+   >          The Squares of the (N-1) subdiagonal elements of T.   
+   > \endverbatim   
+   >   
+   > \param[in] IFIRST   
+   > \verbatim   
+   >          IFIRST is INTEGER   
+   >          The index of the first eigenvalue to be computed.   
+   > \endverbatim   
+   >   
+   > \param[in] ILAST   
+   > \verbatim   
+   >          ILAST is INTEGER   
+   >          The index of the last eigenvalue to be computed.   
+   > \endverbatim   
+   >   
+   > \param[in] RTOL   
+   > \verbatim   
+   >          RTOL is DOUBLE PRECISION   
+   >          Tolerance for the convergence of the bisection intervals.   
+   >          An interval [LEFT,RIGHT] has converged if   
+   >          RIGHT-LEFT.LT.RTOL*MAX(|LEFT|,|RIGHT|).   
+   > \endverbatim   
+   >   
+   > \param[in] OFFSET   
+   > \verbatim   
+   >          OFFSET is INTEGER   
+   >          Offset for the arrays W and WERR, i.e., the IFIRST-OFFSET   
+   >          through ILAST-OFFSET elements of these arrays are to be used.   
+   > \endverbatim   
+   >   
+   > \param[in,out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          On input, W( IFIRST-OFFSET ) through W( ILAST-OFFSET ) are   
+   >          estimates of the eigenvalues of L D L^T indexed IFIRST through   
+   >          ILAST.   
+   >          On output, these estimates are refined.   
+   > \endverbatim   
+   >   
+   > \param[in,out] WERR   
+   > \verbatim   
+   >          WERR is DOUBLE PRECISION array, dimension (N)   
+   >          On input, WERR( IFIRST-OFFSET ) through WERR( ILAST-OFFSET ) are   
+   >          the errors in the estimates of the corresponding elements in W.   
+   >          On output, these errors are refined.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (2*N)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (2*N)   
+   >          Workspace.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot in the Sturm sequence for T.   
+   > \endverbatim   
+   >   
+   > \param[in] SPDIAM   
+   > \verbatim   
+   >          SPDIAM is DOUBLE PRECISION   
+   >          The spectral diameter of T.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          Error flag.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarrj_(integer *n, doublereal *d__, doublereal *e2, 
+	integer *ifirst, integer *ilast, doublereal *rtol, integer *offset, 
+	doublereal *w, doublereal *werr, doublereal *work, integer *iwork, 
+	doublereal *pivmin, doublereal *spdiam, integer *info)
+{
+    /* System generated locals */
+    integer i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double log(doublereal);
+
+    /* Local variables */
+    integer i__, j, k, p;
+    doublereal s;
+    integer i1, i2, ii;
+    doublereal fac, mid;
+    integer cnt;
+    doublereal tmp, left;
+    integer iter, nint, prev, next, savi1;
+    doublereal right, width, dplus;
+    integer olnint, maxitr;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+
+       Parameter adjustments */
+    --iwork;
+    --work;
+    --werr;
+    --w;
+    --e2;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+
+    maxitr = (integer) ((log(*spdiam + *pivmin) - log(*pivmin)) / log(2.)) + 
+	    2;
+
+/*     Initialize unconverged intervals in [ WORK(2*I-1), WORK(2*I) ].   
+       The Sturm Count, Count( WORK(2*I-1) ) is arranged to be I-1, while   
+       Count( WORK(2*I) ) is stored in IWORK( 2*I ). The integer IWORK( 2*I-1 )   
+       for an unconverged interval is set to the index of the next unconverged   
+       interval, and is -1 or 0 for a converged interval. Thus a linked   
+       list of unconverged intervals is set up. */
+
+    i1 = *ifirst;
+    i2 = *ilast;
+/*     The number of unconverged intervals */
+    nint = 0;
+/*     The last unconverged interval found */
+    prev = 0;
+    i__1 = i2;
+    for (i__ = i1; i__ <= i__1; ++i__) {
+	k = i__ << 1;
+	ii = i__ - *offset;
+	left = w[ii] - werr[ii];
+	mid = w[ii];
+	right = w[ii] + werr[ii];
+	width = right - mid;
+/* Computing MAX */
+	d__1 = abs(left), d__2 = abs(right);
+	tmp = max(d__1,d__2);
+/*        The following test prevents the test of converged intervals */
+	if (width < *rtol * tmp) {
+/*           This interval has already converged and does not need refinement.   
+             (Note that the gaps might change through refining the   
+              eigenvalues, however, they can only get bigger.)   
+             Remove it from the list. */
+	    iwork[k - 1] = -1;
+/*           Make sure that I1 always points to the first unconverged interval */
+	    if (i__ == i1 && i__ < i2) {
+		i1 = i__ + 1;
+	    }
+	    if (prev >= i1 && i__ <= i2) {
+		iwork[(prev << 1) - 1] = i__ + 1;
+	    }
+	} else {
+/*           unconverged interval found */
+	    prev = i__;
+/*           Make sure that [LEFT,RIGHT] contains the desired eigenvalue   
+
+             Do while( CNT(LEFT).GT.I-1 ) */
+
+	    fac = 1.;
+L20:
+	    cnt = 0;
+	    s = left;
+	    dplus = d__[1] - s;
+	    if (dplus < 0.) {
+		++cnt;
+	    }
+	    i__2 = *n;
+	    for (j = 2; j <= i__2; ++j) {
+		dplus = d__[j] - s - e2[j - 1] / dplus;
+		if (dplus < 0.) {
+		    ++cnt;
+		}
+/* L30: */
+	    }
+	    if (cnt > i__ - 1) {
+		left -= werr[ii] * fac;
+		fac *= 2.;
+		goto L20;
+	    }
+
+/*           Do while( CNT(RIGHT).LT.I ) */
+
+	    fac = 1.;
+L50:
+	    cnt = 0;
+	    s = right;
+	    dplus = d__[1] - s;
+	    if (dplus < 0.) {
+		++cnt;
+	    }
+	    i__2 = *n;
+	    for (j = 2; j <= i__2; ++j) {
+		dplus = d__[j] - s - e2[j - 1] / dplus;
+		if (dplus < 0.) {
+		    ++cnt;
+		}
+/* L60: */
+	    }
+	    if (cnt < i__) {
+		right += werr[ii] * fac;
+		fac *= 2.;
+		goto L50;
+	    }
+	    ++nint;
+	    iwork[k - 1] = i__ + 1;
+	    iwork[k] = cnt;
+	}
+	work[k - 1] = left;
+	work[k] = right;
+/* L75: */
+    }
+    savi1 = i1;
+
+/*     Do while( NINT.GT.0 ), i.e. there are still unconverged intervals   
+       and while (ITER.LT.MAXITR) */
+
+    iter = 0;
+L80:
+    prev = i1 - 1;
+    i__ = i1;
+    olnint = nint;
+    i__1 = olnint;
+    for (p = 1; p <= i__1; ++p) {
+	k = i__ << 1;
+	ii = i__ - *offset;
+	next = iwork[k - 1];
+	left = work[k - 1];
+	right = work[k];
+	mid = (left + right) * .5;
+/*        semiwidth of interval */
+	width = right - mid;
+/* Computing MAX */
+	d__1 = abs(left), d__2 = abs(right);
+	tmp = max(d__1,d__2);
+	if (width < *rtol * tmp || iter == maxitr) {
+/*           reduce number of unconverged intervals */
+	    --nint;
+/*           Mark interval as converged. */
+	    iwork[k - 1] = 0;
+	    if (i1 == i__) {
+		i1 = next;
+	    } else {
+/*              Prev holds the last unconverged interval previously examined */
+		if (prev >= i1) {
+		    iwork[(prev << 1) - 1] = next;
+		}
+	    }
+	    i__ = next;
+	    goto L100;
+	}
+	prev = i__;
+
+/*        Perform one bisection step */
+
+	cnt = 0;
+	s = mid;
+	dplus = d__[1] - s;
+	if (dplus < 0.) {
+	    ++cnt;
+	}
+	i__2 = *n;
+	for (j = 2; j <= i__2; ++j) {
+	    dplus = d__[j] - s - e2[j - 1] / dplus;
+	    if (dplus < 0.) {
+		++cnt;
+	    }
+/* L90: */
+	}
+	if (cnt <= i__ - 1) {
+	    work[k - 1] = mid;
+	} else {
+	    work[k] = mid;
+	}
+	i__ = next;
+L100:
+	;
+    }
+    ++iter;
+/*     do another loop if there are still unconverged intervals   
+       However, in the last iteration, all intervals are accepted   
+       since this is the best we can do. */
+    if (nint > 0 && iter <= maxitr) {
+	goto L80;
+    }
+
+
+/*     At this point, all the intervals have converged */
+    i__1 = *ilast;
+    for (i__ = savi1; i__ <= i__1; ++i__) {
+	k = i__ << 1;
+	ii = i__ - *offset;
+/*        All intervals marked by '0' have been refined. */
+	if (iwork[k - 1] == 0) {
+	    w[ii] = (work[k - 1] + work[k]) * .5;
+	    werr[ii] = work[k] - w[ii];
+	}
+/* L110: */
+    }
+
+    return 0;
+
+/*     End of DLARRJ */
+
+} /* igraphdlarrj_ */
+
diff --git a/igraph/src/dlarrk.c b/igraph/src/dlarrk.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarrk.c
@@ -0,0 +1,264 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARRK computes one eigenvalue of a symmetric tridiagonal matrix T to suitable accuracy.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRK + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarrk.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarrk.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarrk.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRK( N, IW, GL, GU,   
+                             D, E2, PIVMIN, RELTOL, W, WERR, INFO)   
+
+         INTEGER   INFO, IW, N   
+         DOUBLE PRECISION    PIVMIN, RELTOL, GL, GU, W, WERR   
+         DOUBLE PRECISION   D( * ), E2( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARRK computes one eigenvalue of a symmetric tridiagonal   
+   > matrix T to suitable accuracy. This is an auxiliary code to be   
+   > called from DSTEMR.   
+   >   
+   > To avoid overflow, the matrix must be scaled so that its   
+   > largest element is no greater than overflow**(1/2) * underflow**(1/4) in absolute value, and for greatest
+   
+   > accuracy, it should not be much smaller than that.   
+   >   
+   > See W. Kahan "Accurate Eigenvalues of a Symmetric Tridiagonal   
+   > Matrix", Report CS41, Computer Science Dept., Stanford   
+   > University, July 21, 1966.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the tridiagonal matrix T.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] IW   
+   > \verbatim   
+   >          IW is INTEGER   
+   >          The index of the eigenvalues to be returned.   
+   > \endverbatim   
+   >   
+   > \param[in] GL   
+   > \verbatim   
+   >          GL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] GU   
+   > \verbatim   
+   >          GU is DOUBLE PRECISION   
+   >          An upper and a lower bound on the eigenvalue.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The n diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] E2   
+   > \verbatim   
+   >          E2 is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (n-1) squared off-diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot allowed in the Sturm sequence for T.   
+   > \endverbatim   
+   >   
+   > \param[in] RELTOL   
+   > \verbatim   
+   >          RELTOL is DOUBLE PRECISION   
+   >          The minimum relative width of an interval.  When an interval   
+   >          is narrower than RELTOL times the larger (in   
+   >          magnitude) endpoint, then it is considered to be   
+   >          sufficiently small, i.e., converged.  Note: this should   
+   >          always be at least radix*machine epsilon.   
+   > \endverbatim   
+   >   
+   > \param[out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[out] WERR   
+   > \verbatim   
+   >          WERR is DOUBLE PRECISION   
+   >          The error bound on the corresponding eigenvalue approximation   
+   >          in W.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:       Eigenvalue converged   
+   >          = -1:      Eigenvalue did NOT converge   
+   > \endverbatim   
+
+   > \par Internal Parameters:   
+    =========================   
+   >   
+   > \verbatim   
+   >  FUDGE   DOUBLE PRECISION, default = 2   
+   >          A "fudge factor" to widen the Gershgorin intervals.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarrk_(integer *n, integer *iw, doublereal *gl, 
+	doublereal *gu, doublereal *d__, doublereal *e2, doublereal *pivmin, 
+	doublereal *reltol, doublereal *w, doublereal *werr, integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double log(doublereal);
+
+    /* Local variables */
+    integer i__, it;
+    doublereal mid, eps, tmp1, tmp2, left, atoli, right;
+    integer itmax;
+    doublereal rtoli, tnorm;
+    extern doublereal igraphdlamch_(char *);
+    integer negcnt;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Get machine constants   
+       Parameter adjustments */
+    --e2;
+    --d__;
+
+    /* Function Body */
+    eps = igraphdlamch_("P");
+/* Computing MAX */
+    d__1 = abs(*gl), d__2 = abs(*gu);
+    tnorm = max(d__1,d__2);
+    rtoli = *reltol;
+    atoli = *pivmin * 4.;
+    itmax = (integer) ((log(tnorm + *pivmin) - log(*pivmin)) / log(2.)) + 2;
+    *info = -1;
+    left = *gl - tnorm * 2. * eps * *n - *pivmin * 4.;
+    right = *gu + tnorm * 2. * eps * *n + *pivmin * 4.;
+    it = 0;
+L10:
+
+/*     Check if interval converged or maximum number of iterations reached */
+
+    tmp1 = (d__1 = right - left, abs(d__1));
+/* Computing MAX */
+    d__1 = abs(right), d__2 = abs(left);
+    tmp2 = max(d__1,d__2);
+/* Computing MAX */
+    d__1 = max(atoli,*pivmin), d__2 = rtoli * tmp2;
+    if (tmp1 < max(d__1,d__2)) {
+	*info = 0;
+	goto L30;
+    }
+    if (it > itmax) {
+	goto L30;
+    }
+
+/*     Count number of negative pivots for mid-point */
+
+    ++it;
+    mid = (left + right) * .5;
+    negcnt = 0;
+    tmp1 = d__[1] - mid;
+    if (abs(tmp1) < *pivmin) {
+	tmp1 = -(*pivmin);
+    }
+    if (tmp1 <= 0.) {
+	++negcnt;
+    }
+
+    i__1 = *n;
+    for (i__ = 2; i__ <= i__1; ++i__) {
+	tmp1 = d__[i__] - e2[i__ - 1] / tmp1 - mid;
+	if (abs(tmp1) < *pivmin) {
+	    tmp1 = -(*pivmin);
+	}
+	if (tmp1 <= 0.) {
+	    ++negcnt;
+	}
+/* L20: */
+    }
+    if (negcnt >= *iw) {
+	right = mid;
+    } else {
+	left = mid;
+    }
+    goto L10;
+L30:
+
+/*     Converged or maximum number of iterations reached */
+
+    *w = (left + right) * .5;
+    *werr = (d__1 = right - left, abs(d__1)) * .5;
+    return 0;
+
+/*     End of DLARRK */
+
+} /* igraphdlarrk_ */
+
diff --git a/igraph/src/dlarrr.c b/igraph/src/dlarrr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarrr.c
@@ -0,0 +1,222 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARRR performs tests to decide whether the symmetric tridiagonal matrix T warrants expensive c
+omputations which guarantee high relative accuracy in the eigenvalues.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarrr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarrr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarrr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRR( N, D, E, INFO )   
+
+         INTEGER            N, INFO   
+         DOUBLE PRECISION   D( * ), E( * )   
+
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > Perform tests to decide whether the symmetric tridiagonal matrix T   
+   > warrants expensive computations which guarantee high relative accuracy   
+   > in the eigenvalues.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix. N > 0.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The N diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in,out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the first (N-1) entries contain the subdiagonal   
+   >          elements of the tridiagonal matrix T; E(N) is set to ZERO.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          INFO = 0(default) : the matrix warrants computations preserving   
+   >                              relative accuracy.   
+   >          INFO = 1          : the matrix warrants computations guaranteeing   
+   >                              only absolute accuracy.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarrr_(integer *n, doublereal *d__, doublereal *e, 
+	integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__;
+    doublereal eps, tmp, tmp2, rmin;
+    extern doublereal igraphdlamch_(char *);
+    doublereal offdig, safmin;
+    logical yesrel;
+    doublereal smlnum, offdig2;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+
+    =====================================================================   
+
+
+       As a default, do NOT go for relative-accuracy preserving computations.   
+       Parameter adjustments */
+    --e;
+    --d__;
+
+    /* Function Body */
+    *info = 1;
+    safmin = igraphdlamch_("Safe minimum");
+    eps = igraphdlamch_("Precision");
+    smlnum = safmin / eps;
+    rmin = sqrt(smlnum);
+/*     Tests for relative accuracy   
+
+       Test for scaled diagonal dominance   
+       Scale the diagonal entries to one and check whether the sum of the   
+       off-diagonals is less than one   
+
+       The sdd relative error bounds have a 1/(1- 2*x) factor in them,   
+       x = max(OFFDIG + OFFDIG2), so when x is close to 1/2, no relative   
+       accuracy is promised.  In the notation of the code fragment below,   
+       1/(1 - (OFFDIG + OFFDIG2)) is the condition number.   
+       We don't think it is worth going into "sdd mode" unless the relative   
+       condition number is reasonable, not 1/macheps.   
+       The threshold should be compatible with other thresholds used in the   
+       code. We set  OFFDIG + OFFDIG2 <= .999 =: RELCOND, it corresponds   
+       to losing at most 3 decimal digits: 1 / (1 - (OFFDIG + OFFDIG2)) <= 1000   
+       instead of the current OFFDIG + OFFDIG2 < 1 */
+
+    yesrel = TRUE_;
+    offdig = 0.;
+    tmp = sqrt((abs(d__[1])));
+    if (tmp < rmin) {
+	yesrel = FALSE_;
+    }
+    if (! yesrel) {
+	goto L11;
+    }
+    i__1 = *n;
+    for (i__ = 2; i__ <= i__1; ++i__) {
+	tmp2 = sqrt((d__1 = d__[i__], abs(d__1)));
+	if (tmp2 < rmin) {
+	    yesrel = FALSE_;
+	}
+	if (! yesrel) {
+	    goto L11;
+	}
+	offdig2 = (d__1 = e[i__ - 1], abs(d__1)) / (tmp * tmp2);
+	if (offdig + offdig2 >= .999) {
+	    yesrel = FALSE_;
+	}
+	if (! yesrel) {
+	    goto L11;
+	}
+	tmp = tmp2;
+	offdig = offdig2;
+/* L10: */
+    }
+L11:
+    if (yesrel) {
+	*info = 0;
+	return 0;
+    } else {
+    }
+
+
+/*     *** MORE TO BE IMPLEMENTED ***   
+
+
+       Test if the lower bidiagonal matrix L from T = L D L^T   
+       (zero shift facto) is well conditioned   
+
+
+       Test if the upper bidiagonal matrix U from T = U D U^T   
+       (zero shift facto) is well conditioned.   
+       In this case, the matrix needs to be flipped and, at the end   
+       of the eigenvector computation, the flip needs to be applied   
+       to the computed eigenvectors (and the support) */
+
+
+    return 0;
+
+/*     END OF DLARRR */
+
+} /* igraphdlarrr_ */
+
diff --git a/igraph/src/dlarrv.c b/igraph/src/dlarrv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlarrv.c
@@ -0,0 +1,1105 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b5 = 0.;
+static integer c__1 = 1;
+static integer c__2 = 2;
+
+/* > \brief \b DLARRV computes the eigenvectors of the tridiagonal matrix T = L D LT given L, D and the eigenv
+alues of L D LT.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARRV + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlarrv.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlarrv.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlarrv.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARRV( N, VL, VU, D, L, PIVMIN,   
+                            ISPLIT, M, DOL, DOU, MINRGP,   
+                            RTOL1, RTOL2, W, WERR, WGAP,   
+                            IBLOCK, INDEXW, GERS, Z, LDZ, ISUPPZ,   
+                            WORK, IWORK, INFO )   
+
+         INTEGER            DOL, DOU, INFO, LDZ, M, N   
+         DOUBLE PRECISION   MINRGP, PIVMIN, RTOL1, RTOL2, VL, VU   
+         INTEGER            IBLOCK( * ), INDEXW( * ), ISPLIT( * ),   
+        $                   ISUPPZ( * ), IWORK( * )   
+         DOUBLE PRECISION   D( * ), GERS( * ), L( * ), W( * ), WERR( * ),   
+        $                   WGAP( * ), WORK( * )   
+         DOUBLE PRECISION  Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARRV computes the eigenvectors of the tridiagonal matrix   
+   > T = L D L**T given L, D and APPROXIMATIONS to the eigenvalues of L D L**T.   
+   > The input eigenvalues should have been computed by DLARRE.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] VU   
+   > \verbatim   
+   >          VU is DOUBLE PRECISION   
+   >          Lower and upper bounds of the interval that contains the desired   
+   >          eigenvalues. VL < VU. Needed to compute gaps on the left or right   
+   >          end of the extremal eigenvalues in the desired RANGE.   
+   > \endverbatim   
+   >   
+   > \param[in,out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the N diagonal elements of the diagonal matrix D.   
+   >          On exit, D may be overwritten.   
+   > \endverbatim   
+   >   
+   > \param[in,out] L   
+   > \verbatim   
+   >          L is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the (N-1) subdiagonal elements of the unit   
+   >          bidiagonal matrix L are in elements 1 to N-1 of L   
+   >          (if the matrix is not splitted.) At the end of each block   
+   >          is stored the corresponding shift as given by DLARRE.   
+   >          On exit, L is overwritten.   
+   > \endverbatim   
+   >   
+   > \param[in] PIVMIN   
+   > \verbatim   
+   >          PIVMIN is DOUBLE PRECISION   
+   >          The minimum pivot allowed in the Sturm sequence.   
+   > \endverbatim   
+   >   
+   > \param[in] ISPLIT   
+   > \verbatim   
+   >          ISPLIT is INTEGER array, dimension (N)   
+   >          The splitting points, at which T breaks up into blocks.   
+   >          The first block consists of rows/columns 1 to   
+   >          ISPLIT( 1 ), the second of rows/columns ISPLIT( 1 )+1   
+   >          through ISPLIT( 2 ), etc.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The total number of input eigenvalues.  0 <= M <= N.   
+   > \endverbatim   
+   >   
+   > \param[in] DOL   
+   > \verbatim   
+   >          DOL is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] DOU   
+   > \verbatim   
+   >          DOU is INTEGER   
+   >          If the user wants to compute only selected eigenvectors from all   
+   >          the eigenvalues supplied, he can specify an index range DOL:DOU.   
+   >          Or else the setting DOL=1, DOU=M should be applied.   
+   >          Note that DOL and DOU refer to the order in which the eigenvalues   
+   >          are stored in W.   
+   >          If the user wants to compute only selected eigenpairs, then   
+   >          the columns DOL-1 to DOU+1 of the eigenvector space Z contain the   
+   >          computed eigenvectors. All other columns of Z are set to zero.   
+   > \endverbatim   
+   >   
+   > \param[in] MINRGP   
+   > \verbatim   
+   >          MINRGP is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] RTOL1   
+   > \verbatim   
+   >          RTOL1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] RTOL2   
+   > \verbatim   
+   >          RTOL2 is DOUBLE PRECISION   
+   >           Parameters for bisection.   
+   >           An interval [LEFT,RIGHT] has converged if   
+   >           RIGHT-LEFT.LT.MAX( RTOL1*GAP, RTOL2*MAX(|LEFT|,|RIGHT|) )   
+   > \endverbatim   
+   >   
+   > \param[in,out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          The first M elements of W contain the APPROXIMATE eigenvalues for   
+   >          which eigenvectors are to be computed.  The eigenvalues   
+   >          should be grouped by split-off block and ordered from   
+   >          smallest to largest within the block ( The output array   
+   >          W from DLARRE is expected here ). Furthermore, they are with   
+   >          respect to the shift of the corresponding root representation   
+   >          for their block. On exit, W holds the eigenvalues of the   
+   >          UNshifted matrix.   
+   > \endverbatim   
+   >   
+   > \param[in,out] WERR   
+   > \verbatim   
+   >          WERR is DOUBLE PRECISION array, dimension (N)   
+   >          The first M elements contain the semiwidth of the uncertainty   
+   >          interval of the corresponding eigenvalue in W   
+   > \endverbatim   
+   >   
+   > \param[in,out] WGAP   
+   > \verbatim   
+   >          WGAP is DOUBLE PRECISION array, dimension (N)   
+   >          The separation from the right neighbor eigenvalue in W.   
+   > \endverbatim   
+   >   
+   > \param[in] IBLOCK   
+   > \verbatim   
+   >          IBLOCK is INTEGER array, dimension (N)   
+   >          The indices of the blocks (submatrices) associated with the   
+   >          corresponding eigenvalues in W; IBLOCK(i)=1 if eigenvalue   
+   >          W(i) belongs to the first block from the top, =2 if W(i)   
+   >          belongs to the second block, etc.   
+   > \endverbatim   
+   >   
+   > \param[in] INDEXW   
+   > \verbatim   
+   >          INDEXW is INTEGER array, dimension (N)   
+   >          The indices of the eigenvalues within each block (submatrix);   
+   >          for example, INDEXW(i)= 10 and IBLOCK(i)=2 imply that the   
+   >          i-th eigenvalue W(i) is the 10-th eigenvalue in the second block.   
+   > \endverbatim   
+   >   
+   > \param[in] GERS   
+   > \verbatim   
+   >          GERS is DOUBLE PRECISION array, dimension (2*N)   
+   >          The N Gerschgorin intervals (the i-th Gerschgorin interval   
+   >          is (GERS(2*i-1), GERS(2*i)). The Gerschgorin intervals should   
+   >          be computed from the original UNshifted matrix.   
+   > \endverbatim   
+   >   
+   > \param[out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ, max(1,M) )   
+   >          If INFO = 0, the first M columns of Z contain the   
+   >          orthonormal eigenvectors of the matrix T   
+   >          corresponding to the input eigenvalues, with the i-th   
+   >          column of Z holding the eigenvector associated with W(i).   
+   >          Note: the user must ensure that at least max(1,M) columns are   
+   >          supplied in the array Z.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >          The leading dimension of the array Z.  LDZ >= 1, and if   
+   >          JOBZ = 'V', LDZ >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] ISUPPZ   
+   > \verbatim   
+   >          ISUPPZ is INTEGER array, dimension ( 2*max(1,M) )   
+   >          The support of the eigenvectors in Z, i.e., the indices   
+   >          indicating the nonzero elements in Z. The I-th eigenvector   
+   >          is nonzero only in elements ISUPPZ( 2*I-1 ) through   
+   >          ISUPPZ( 2*I ).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (12*N)   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (7*N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >   
+   >          > 0:  A problem occured in DLARRV.   
+   >          < 0:  One of the called subroutines signaled an internal problem.   
+   >                Needs inspection of the corresponding parameter IINFO   
+   >                for further information.   
+   >   
+   >          =-1:  Problem in DLARRB when refining a child's eigenvalues.   
+   >          =-2:  Problem in DLARRF when computing the RRR of a child.   
+   >                When a child is inside a tight cluster, it can be difficult   
+   >                to find an RRR. A partial remedy from the user's point of   
+   >                view is to make the parameter MINRGP smaller and recompile.   
+   >                However, as the orthogonality of the computed vectors is   
+   >                proportional to 1/MINRGP, the user should be aware that   
+   >                he might be trading in precision when he decreases MINRGP.   
+   >          =-3:  Problem in DLARRB when refining a single eigenvalue   
+   >                after the Rayleigh correction was rejected.   
+   >          = 5:  The Rayleigh Quotient Iteration failed to converge to   
+   >                full accuracy in MAXITR steps.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdlarrv_(integer *n, doublereal *vl, doublereal *vu, 
+	doublereal *d__, doublereal *l, doublereal *pivmin, integer *isplit, 
+	integer *m, integer *dol, integer *dou, doublereal *minrgp, 
+	doublereal *rtol1, doublereal *rtol2, doublereal *w, doublereal *werr,
+	 doublereal *wgap, integer *iblock, integer *indexw, doublereal *gers,
+	 doublereal *z__, integer *ldz, integer *isuppz, doublereal *work, 
+	integer *iwork, integer *info)
+{
+    /* System generated locals */
+    integer z_dim1, z_offset, i__1, i__2, i__3, i__4, i__5;
+    doublereal d__1, d__2;
+    logical L__1;
+
+    /* Builtin functions */
+    double log(doublereal);
+
+    /* Local variables */
+    integer minwsize, i__, j, k, p, q, miniwsize, ii;
+    doublereal gl;
+    integer im, in;
+    doublereal gu, gap, eps, tau, tol, tmp;
+    integer zto;
+    doublereal ztz;
+    integer iend, jblk;
+    doublereal lgap;
+    integer done;
+    doublereal rgap, left;
+    integer wend, iter;
+    doublereal bstw;
+    integer itmp1;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    integer indld;
+    doublereal fudge;
+    integer idone;
+    doublereal sigma;
+    integer iinfo, iindr;
+    doublereal resid;
+    logical eskip;
+    doublereal right;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    integer nclus, zfrom;
+    doublereal rqtol;
+    integer iindc1, iindc2;
+    extern /* Subroutine */ int igraphdlar1v_(integer *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, logical *,
+	     integer *, doublereal *, doublereal *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *);
+    logical stp2ii;
+    doublereal lambda;
+    extern doublereal igraphdlamch_(char *);
+    integer ibegin, indeig;
+    logical needbs;
+    integer indlld;
+    doublereal sgndef, mingma;
+    extern /* Subroutine */ int igraphdlarrb_(integer *, doublereal *, doublereal *,
+	     integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, integer *,
+	     doublereal *, doublereal *, integer *, integer *);
+    integer oldien, oldncl, wbegin;
+    doublereal spdiam;
+    integer negcnt;
+    extern /* Subroutine */ int igraphdlarrf_(integer *, doublereal *, doublereal *,
+	     doublereal *, integer *, integer *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, integer *);
+    integer oldcls;
+    doublereal savgap;
+    integer ndepth;
+    doublereal ssigma;
+    extern /* Subroutine */ int igraphdlaset_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *);
+    logical usedbs;
+    integer iindwk, offset;
+    doublereal gaptol;
+    integer newcls, oldfst, indwrk, windex, oldlst;
+    logical usedrq;
+    integer newfst, newftt, parity, windmn, windpl, isupmn, newlst, zusedl;
+    doublereal bstres;
+    integer newsiz, zusedu, zusedw;
+    doublereal nrminv, rqcorr;
+    logical tryrqc;
+    integer isupmx;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+       The first N entries of WORK are reserved for the eigenvalues   
+       Parameter adjustments */
+    --d__;
+    --l;
+    --isplit;
+    --w;
+    --werr;
+    --wgap;
+    --iblock;
+    --indexw;
+    --gers;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --isuppz;
+    --work;
+    --iwork;
+
+    /* Function Body */
+    indld = *n + 1;
+    indlld = (*n << 1) + 1;
+    indwrk = *n * 3 + 1;
+    minwsize = *n * 12;
+    i__1 = minwsize;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	work[i__] = 0.;
+/* L5: */
+    }
+/*     IWORK(IINDR+1:IINDR+N) hold the twist indices R for the   
+       factorization used to compute the FP vector */
+    iindr = 0;
+/*     IWORK(IINDC1+1:IINC2+N) are used to store the clusters of the current   
+       layer and the one above. */
+    iindc1 = *n;
+    iindc2 = *n << 1;
+    iindwk = *n * 3 + 1;
+    miniwsize = *n * 7;
+    i__1 = miniwsize;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	iwork[i__] = 0;
+/* L10: */
+    }
+    zusedl = 1;
+    if (*dol > 1) {
+/*        Set lower bound for use of Z */
+	zusedl = *dol - 1;
+    }
+    zusedu = *m;
+    if (*dou < *m) {
+/*        Set lower bound for use of Z */
+	zusedu = *dou + 1;
+    }
+/*     The width of the part of Z that is used */
+    zusedw = zusedu - zusedl + 1;
+    igraphdlaset_("Full", n, &zusedw, &c_b5, &c_b5, &z__[zusedl * z_dim1 + 1], ldz);
+    eps = igraphdlamch_("Precision");
+    rqtol = eps * 2.;
+
+/*     Set expert flags for standard code. */
+    tryrqc = TRUE_;
+    if (*dol == 1 && *dou == *m) {
+    } else {
+/*        Only selected eigenpairs are computed. Since the other evalues   
+          are not refined by RQ iteration, bisection has to compute to full   
+          accuracy. */
+	*rtol1 = eps * 4.;
+	*rtol2 = eps * 4.;
+    }
+/*     The entries WBEGIN:WEND in W, WERR, WGAP correspond to the   
+       desired eigenvalues. The support of the nonzero eigenvector   
+       entries is contained in the interval IBEGIN:IEND.   
+       Remark that if k eigenpairs are desired, then the eigenvectors   
+       are stored in k contiguous columns of Z.   
+       DONE is the number of eigenvectors already computed */
+    done = 0;
+    ibegin = 1;
+    wbegin = 1;
+    i__1 = iblock[*m];
+    for (jblk = 1; jblk <= i__1; ++jblk) {
+	iend = isplit[jblk];
+	sigma = l[iend];
+/*        Find the eigenvectors of the submatrix indexed IBEGIN   
+          through IEND. */
+	wend = wbegin - 1;
+L15:
+	if (wend < *m) {
+	    if (iblock[wend + 1] == jblk) {
+		++wend;
+		goto L15;
+	    }
+	}
+	if (wend < wbegin) {
+	    ibegin = iend + 1;
+	    goto L170;
+	} else if (wend < *dol || wbegin > *dou) {
+	    ibegin = iend + 1;
+	    wbegin = wend + 1;
+	    goto L170;
+	}
+/*        Find local spectral diameter of the block */
+	gl = gers[(ibegin << 1) - 1];
+	gu = gers[ibegin * 2];
+	i__2 = iend;
+	for (i__ = ibegin + 1; i__ <= i__2; ++i__) {
+/* Computing MIN */
+	    d__1 = gers[(i__ << 1) - 1];
+	    gl = min(d__1,gl);
+/* Computing MAX */
+	    d__1 = gers[i__ * 2];
+	    gu = max(d__1,gu);
+/* L20: */
+	}
+	spdiam = gu - gl;
+/*        OLDIEN is the last index of the previous block */
+	oldien = ibegin - 1;
+/*        Calculate the size of the current block */
+	in = iend - ibegin + 1;
+/*        The number of eigenvalues in the current block */
+	im = wend - wbegin + 1;
+/*        This is for a 1x1 block */
+	if (ibegin == iend) {
+	    ++done;
+	    z__[ibegin + wbegin * z_dim1] = 1.;
+	    isuppz[(wbegin << 1) - 1] = ibegin;
+	    isuppz[wbegin * 2] = ibegin;
+	    w[wbegin] += sigma;
+	    work[wbegin] = w[wbegin];
+	    ibegin = iend + 1;
+	    ++wbegin;
+	    goto L170;
+	}
+/*        The desired (shifted) eigenvalues are stored in W(WBEGIN:WEND)   
+          Note that these can be approximations, in this case, the corresp.   
+          entries of WERR give the size of the uncertainty interval.   
+          The eigenvalue approximations will be refined when necessary as   
+          high relative accuracy is required for the computation of the   
+          corresponding eigenvectors. */
+	igraphdcopy_(&im, &w[wbegin], &c__1, &work[wbegin], &c__1);
+/*        We store in W the eigenvalue approximations w.r.t. the original   
+          matrix T. */
+	i__2 = im;
+	for (i__ = 1; i__ <= i__2; ++i__) {
+	    w[wbegin + i__ - 1] += sigma;
+/* L30: */
+	}
+/*        NDEPTH is the current depth of the representation tree */
+	ndepth = 0;
+/*        PARITY is either 1 or 0 */
+	parity = 1;
+/*        NCLUS is the number of clusters for the next level of the   
+          representation tree, we start with NCLUS = 1 for the root */
+	nclus = 1;
+	iwork[iindc1 + 1] = 1;
+	iwork[iindc1 + 2] = im;
+/*        IDONE is the number of eigenvectors already computed in the current   
+          block */
+	idone = 0;
+/*        loop while( IDONE.LT.IM )   
+          generate the representation tree for the current block and   
+          compute the eigenvectors */
+L40:
+	if (idone < im) {
+/*           This is a crude protection against infinitely deep trees */
+	    if (ndepth > *m) {
+		*info = -2;
+		return 0;
+	    }
+/*           breadth first processing of the current level of the representation   
+             tree: OLDNCL = number of clusters on current level */
+	    oldncl = nclus;
+/*           reset NCLUS to count the number of child clusters */
+	    nclus = 0;
+
+	    parity = 1 - parity;
+	    if (parity == 0) {
+		oldcls = iindc1;
+		newcls = iindc2;
+	    } else {
+		oldcls = iindc2;
+		newcls = iindc1;
+	    }
+/*           Process the clusters on the current level */
+	    i__2 = oldncl;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		j = oldcls + (i__ << 1);
+/*              OLDFST, OLDLST = first, last index of current cluster.   
+                                 cluster indices start with 1 and are relative   
+                                 to WBEGIN when accessing W, WGAP, WERR, Z */
+		oldfst = iwork[j - 1];
+		oldlst = iwork[j];
+		if (ndepth > 0) {
+/*                 Retrieve relatively robust representation (RRR) of cluster   
+                   that has been computed at the previous level   
+                   The RRR is stored in Z and overwritten once the eigenvectors   
+                   have been computed or when the cluster is refined */
+		    if (*dol == 1 && *dou == *m) {
+/*                    Get representation from location of the leftmost evalue   
+                      of the cluster */
+			j = wbegin + oldfst - 1;
+		    } else {
+			if (wbegin + oldfst - 1 < *dol) {
+/*                       Get representation from the left end of Z array */
+			    j = *dol - 1;
+			} else if (wbegin + oldfst - 1 > *dou) {
+/*                       Get representation from the right end of Z array */
+			    j = *dou;
+			} else {
+			    j = wbegin + oldfst - 1;
+			}
+		    }
+		    igraphdcopy_(&in, &z__[ibegin + j * z_dim1], &c__1, &d__[ibegin]
+			    , &c__1);
+		    i__3 = in - 1;
+		    igraphdcopy_(&i__3, &z__[ibegin + (j + 1) * z_dim1], &c__1, &l[
+			    ibegin], &c__1);
+		    sigma = z__[iend + (j + 1) * z_dim1];
+/*                 Set the corresponding entries in Z to zero */
+		    igraphdlaset_("Full", &in, &c__2, &c_b5, &c_b5, &z__[ibegin + j 
+			    * z_dim1], ldz);
+		}
+/*              Compute DL and DLL of current RRR */
+		i__3 = iend - 1;
+		for (j = ibegin; j <= i__3; ++j) {
+		    tmp = d__[j] * l[j];
+		    work[indld - 1 + j] = tmp;
+		    work[indlld - 1 + j] = tmp * l[j];
+/* L50: */
+		}
+		if (ndepth > 0) {
+/*                 P and Q are index of the first and last eigenvalue to compute   
+                   within the current block */
+		    p = indexw[wbegin - 1 + oldfst];
+		    q = indexw[wbegin - 1 + oldlst];
+/*                 Offset for the arrays WORK, WGAP and WERR, i.e., the P-OFFSET   
+                   through the Q-OFFSET elements of these arrays are to be used.   
+                    OFFSET = P-OLDFST */
+		    offset = indexw[wbegin] - 1;
+/*                 perform limited bisection (if necessary) to get approximate   
+                   eigenvalues to the precision needed. */
+		    igraphdlarrb_(&in, &d__[ibegin], &work[indlld + ibegin - 1], &p,
+			     &q, rtol1, rtol2, &offset, &work[wbegin], &wgap[
+			    wbegin], &werr[wbegin], &work[indwrk], &iwork[
+			    iindwk], pivmin, &spdiam, &in, &iinfo);
+		    if (iinfo != 0) {
+			*info = -1;
+			return 0;
+		    }
+/*                 We also recompute the extremal gaps. W holds all eigenvalues   
+                   of the unshifted matrix and must be used for computation   
+                   of WGAP, the entries of WORK might stem from RRRs with   
+                   different shifts. The gaps from WBEGIN-1+OLDFST to   
+                   WBEGIN-1+OLDLST are correctly computed in DLARRB.   
+                   However, we only allow the gaps to become greater since   
+                   this is what should happen when we decrease WERR */
+		    if (oldfst > 1) {
+/* Computing MAX */
+			d__1 = wgap[wbegin + oldfst - 2], d__2 = w[wbegin + 
+				oldfst - 1] - werr[wbegin + oldfst - 1] - w[
+				wbegin + oldfst - 2] - werr[wbegin + oldfst - 
+				2];
+			wgap[wbegin + oldfst - 2] = max(d__1,d__2);
+		    }
+		    if (wbegin + oldlst - 1 < wend) {
+/* Computing MAX */
+			d__1 = wgap[wbegin + oldlst - 1], d__2 = w[wbegin + 
+				oldlst] - werr[wbegin + oldlst] - w[wbegin + 
+				oldlst - 1] - werr[wbegin + oldlst - 1];
+			wgap[wbegin + oldlst - 1] = max(d__1,d__2);
+		    }
+/*                 Each time the eigenvalues in WORK get refined, we store   
+                   the newly found approximation with all shifts applied in W */
+		    i__3 = oldlst;
+		    for (j = oldfst; j <= i__3; ++j) {
+			w[wbegin + j - 1] = work[wbegin + j - 1] + sigma;
+/* L53: */
+		    }
+		}
+/*              Process the current node. */
+		newfst = oldfst;
+		i__3 = oldlst;
+		for (j = oldfst; j <= i__3; ++j) {
+		    if (j == oldlst) {
+/*                    we are at the right end of the cluster, this is also the   
+                      boundary of the child cluster */
+			newlst = j;
+		    } else if (wgap[wbegin + j - 1] >= *minrgp * (d__1 = work[
+			    wbegin + j - 1], abs(d__1))) {
+/*                    the right relative gap is big enough, the child cluster   
+                      (NEWFST,..,NEWLST) is well separated from the following */
+			newlst = j;
+		    } else {
+/*                    inside a child cluster, the relative gap is not   
+                      big enough. */
+			goto L140;
+		    }
+/*                 Compute size of child cluster found */
+		    newsiz = newlst - newfst + 1;
+/*                 NEWFTT is the place in Z where the new RRR or the computed   
+                   eigenvector is to be stored */
+		    if (*dol == 1 && *dou == *m) {
+/*                    Store representation at location of the leftmost evalue   
+                      of the cluster */
+			newftt = wbegin + newfst - 1;
+		    } else {
+			if (wbegin + newfst - 1 < *dol) {
+/*                       Store representation at the left end of Z array */
+			    newftt = *dol - 1;
+			} else if (wbegin + newfst - 1 > *dou) {
+/*                       Store representation at the right end of Z array */
+			    newftt = *dou;
+			} else {
+			    newftt = wbegin + newfst - 1;
+			}
+		    }
+		    if (newsiz > 1) {
+
+/*                    Current child is not a singleton but a cluster.   
+                      Compute and store new representation of child.   
+
+
+                      Compute left and right cluster gap.   
+
+                      LGAP and RGAP are not computed from WORK because   
+                      the eigenvalue approximations may stem from RRRs   
+                      different shifts. However, W hold all eigenvalues   
+                      of the unshifted matrix. Still, the entries in WGAP   
+                      have to be computed from WORK since the entries   
+                      in W might be of the same order so that gaps are not   
+                      exhibited correctly for very close eigenvalues. */
+			if (newfst == 1) {
+/* Computing MAX */
+			    d__1 = 0., d__2 = w[wbegin] - werr[wbegin] - *vl;
+			    lgap = max(d__1,d__2);
+			} else {
+			    lgap = wgap[wbegin + newfst - 2];
+			}
+			rgap = wgap[wbegin + newlst - 1];
+
+/*                    Compute left- and rightmost eigenvalue of child   
+                      to high precision in order to shift as close   
+                      as possible and obtain as large relative gaps   
+                      as possible */
+
+			for (k = 1; k <= 2; ++k) {
+			    if (k == 1) {
+				p = indexw[wbegin - 1 + newfst];
+			    } else {
+				p = indexw[wbegin - 1 + newlst];
+			    }
+			    offset = indexw[wbegin] - 1;
+			    igraphdlarrb_(&in, &d__[ibegin], &work[indlld + ibegin 
+				    - 1], &p, &p, &rqtol, &rqtol, &offset, &
+				    work[wbegin], &wgap[wbegin], &werr[wbegin]
+				    , &work[indwrk], &iwork[iindwk], pivmin, &
+				    spdiam, &in, &iinfo);
+/* L55: */
+			}
+
+			if (wbegin + newlst - 1 < *dol || wbegin + newfst - 1 
+				> *dou) {
+/*                       if the cluster contains no desired eigenvalues   
+                         skip the computation of that branch of the rep. tree   
+
+                         We could skip before the refinement of the extremal   
+                         eigenvalues of the child, but then the representation   
+                         tree could be different from the one when nothing is   
+                         skipped. For this reason we skip at this place. */
+			    idone = idone + newlst - newfst + 1;
+			    goto L139;
+			}
+
+/*                    Compute RRR of child cluster.   
+                      Note that the new RRR is stored in Z   
+
+                      DLARRF needs LWORK = 2*N */
+			igraphdlarrf_(&in, &d__[ibegin], &l[ibegin], &work[indld + 
+				ibegin - 1], &newfst, &newlst, &work[wbegin], 
+				&wgap[wbegin], &werr[wbegin], &spdiam, &lgap, 
+				&rgap, pivmin, &tau, &z__[ibegin + newftt * 
+				z_dim1], &z__[ibegin + (newftt + 1) * z_dim1],
+				 &work[indwrk], &iinfo);
+			if (iinfo == 0) {
+/*                       a new RRR for the cluster was found by DLARRF   
+                         update shift and store it */
+			    ssigma = sigma + tau;
+			    z__[iend + (newftt + 1) * z_dim1] = ssigma;
+/*                       WORK() are the midpoints and WERR() the semi-width   
+                         Note that the entries in W are unchanged. */
+			    i__4 = newlst;
+			    for (k = newfst; k <= i__4; ++k) {
+				fudge = eps * 3. * (d__1 = work[wbegin + k - 
+					1], abs(d__1));
+				work[wbegin + k - 1] -= tau;
+				fudge += eps * 4. * (d__1 = work[wbegin + k - 
+					1], abs(d__1));
+/*                          Fudge errors */
+				werr[wbegin + k - 1] += fudge;
+/*                          Gaps are not fudged. Provided that WERR is small   
+                            when eigenvalues are close, a zero gap indicates   
+                            that a new representation is needed for resolving   
+                            the cluster. A fudge could lead to a wrong decision   
+                            of judging eigenvalues 'separated' which in   
+                            reality are not. This could have a negative impact   
+                            on the orthogonality of the computed eigenvectors.   
+   L116: */
+			    }
+			    ++nclus;
+			    k = newcls + (nclus << 1);
+			    iwork[k - 1] = newfst;
+			    iwork[k] = newlst;
+			} else {
+			    *info = -2;
+			    return 0;
+			}
+		    } else {
+
+/*                    Compute eigenvector of singleton */
+
+			iter = 0;
+
+			tol = log((doublereal) in) * 4. * eps;
+
+			k = newfst;
+			windex = wbegin + k - 1;
+/* Computing MAX */
+			i__4 = windex - 1;
+			windmn = max(i__4,1);
+/* Computing MIN */
+			i__4 = windex + 1;
+			windpl = min(i__4,*m);
+			lambda = work[windex];
+			++done;
+/*                    Check if eigenvector computation is to be skipped */
+			if (windex < *dol || windex > *dou) {
+			    eskip = TRUE_;
+			    goto L125;
+			} else {
+			    eskip = FALSE_;
+			}
+			left = work[windex] - werr[windex];
+			right = work[windex] + werr[windex];
+			indeig = indexw[windex];
+/*                    Note that since we compute the eigenpairs for a child,   
+                      all eigenvalue approximations are w.r.t the same shift.   
+                      In this case, the entries in WORK should be used for   
+                      computing the gaps since they exhibit even very small   
+                      differences in the eigenvalues, as opposed to the   
+                      entries in W which might "look" the same. */
+			if (k == 1) {
+/*                       In the case RANGE='I' and with not much initial   
+                         accuracy in LAMBDA and VL, the formula   
+                         LGAP = MAX( ZERO, (SIGMA - VL) + LAMBDA )   
+                         can lead to an overestimation of the left gap and   
+                         thus to inadequately early RQI 'convergence'.   
+                         Prevent this by forcing a small left gap.   
+   Computing MAX */
+			    d__1 = abs(left), d__2 = abs(right);
+			    lgap = eps * max(d__1,d__2);
+			} else {
+			    lgap = wgap[windmn];
+			}
+			if (k == im) {
+/*                       In the case RANGE='I' and with not much initial   
+                         accuracy in LAMBDA and VU, the formula   
+                         can lead to an overestimation of the right gap and   
+                         thus to inadequately early RQI 'convergence'.   
+                         Prevent this by forcing a small right gap.   
+   Computing MAX */
+			    d__1 = abs(left), d__2 = abs(right);
+			    rgap = eps * max(d__1,d__2);
+			} else {
+			    rgap = wgap[windex];
+			}
+			gap = min(lgap,rgap);
+			if (k == 1 || k == im) {
+/*                       The eigenvector support can become wrong   
+                         because significant entries could be cut off due to a   
+                         large GAPTOL parameter in LAR1V. Prevent this. */
+			    gaptol = 0.;
+			} else {
+			    gaptol = gap * eps;
+			}
+			isupmn = in;
+			isupmx = 1;
+/*                    Update WGAP so that it holds the minimum gap   
+                      to the left or the right. This is crucial in the   
+                      case where bisection is used to ensure that the   
+                      eigenvalue is refined up to the required precision.   
+                      The correct value is restored afterwards. */
+			savgap = wgap[windex];
+			wgap[windex] = gap;
+/*                    We want to use the Rayleigh Quotient Correction   
+                      as often as possible since it converges quadratically   
+                      when we are close enough to the desired eigenvalue.   
+                      However, the Rayleigh Quotient can have the wrong sign   
+                      and lead us away from the desired eigenvalue. In this   
+                      case, the best we can do is to use bisection. */
+			usedbs = FALSE_;
+			usedrq = FALSE_;
+/*                    Bisection is initially turned off unless it is forced */
+			needbs = ! tryrqc;
+L120:
+/*                    Check if bisection should be used to refine eigenvalue */
+			if (needbs) {
+/*                       Take the bisection as new iterate */
+			    usedbs = TRUE_;
+			    itmp1 = iwork[iindr + windex];
+			    offset = indexw[wbegin] - 1;
+			    d__1 = eps * 2.;
+			    igraphdlarrb_(&in, &d__[ibegin], &work[indlld + ibegin 
+				    - 1], &indeig, &indeig, &c_b5, &d__1, &
+				    offset, &work[wbegin], &wgap[wbegin], &
+				    werr[wbegin], &work[indwrk], &iwork[
+				    iindwk], pivmin, &spdiam, &itmp1, &iinfo);
+			    if (iinfo != 0) {
+				*info = -3;
+				return 0;
+			    }
+			    lambda = work[windex];
+/*                       Reset twist index from inaccurate LAMBDA to   
+                         force computation of true MINGMA */
+			    iwork[iindr + windex] = 0;
+			}
+/*                    Given LAMBDA, compute the eigenvector. */
+			L__1 = ! usedbs;
+			igraphdlar1v_(&in, &c__1, &in, &lambda, &d__[ibegin], &l[
+				ibegin], &work[indld + ibegin - 1], &work[
+				indlld + ibegin - 1], pivmin, &gaptol, &z__[
+				ibegin + windex * z_dim1], &L__1, &negcnt, &
+				ztz, &mingma, &iwork[iindr + windex], &isuppz[
+				(windex << 1) - 1], &nrminv, &resid, &rqcorr, 
+				&work[indwrk]);
+			if (iter == 0) {
+			    bstres = resid;
+			    bstw = lambda;
+			} else if (resid < bstres) {
+			    bstres = resid;
+			    bstw = lambda;
+			}
+/* Computing MIN */
+			i__4 = isupmn, i__5 = isuppz[(windex << 1) - 1];
+			isupmn = min(i__4,i__5);
+/* Computing MAX */
+			i__4 = isupmx, i__5 = isuppz[windex * 2];
+			isupmx = max(i__4,i__5);
+			++iter;
+/*                    sin alpha <= |resid|/gap   
+                      Note that both the residual and the gap are   
+                      proportional to the matrix, so ||T|| doesn't play   
+                      a role in the quotient   
+
+                      Convergence test for Rayleigh-Quotient iteration   
+                      (omitted when Bisection has been used) */
+
+			if (resid > tol * gap && abs(rqcorr) > rqtol * abs(
+				lambda) && ! usedbs) {
+/*                       We need to check that the RQCORR update doesn't   
+                         move the eigenvalue away from the desired one and   
+                         towards a neighbor. -> protection with bisection */
+			    if (indeig <= negcnt) {
+/*                          The wanted eigenvalue lies to the left */
+				sgndef = -1.;
+			    } else {
+/*                          The wanted eigenvalue lies to the right */
+				sgndef = 1.;
+			    }
+/*                       We only use the RQCORR if it improves the   
+                         the iterate reasonably. */
+			    if (rqcorr * sgndef >= 0. && lambda + rqcorr <= 
+				    right && lambda + rqcorr >= left) {
+				usedrq = TRUE_;
+/*                          Store new midpoint of bisection interval in WORK */
+				if (sgndef == 1.) {
+/*                             The current LAMBDA is on the left of the true   
+                               eigenvalue */
+				    left = lambda;
+/*                             We prefer to assume that the error estimate   
+                               is correct. We could make the interval not   
+                               as a bracket but to be modified if the RQCORR   
+                               chooses to. In this case, the RIGHT side should   
+                               be modified as follows:   
+                                RIGHT = MAX(RIGHT, LAMBDA + RQCORR) */
+				} else {
+/*                             The current LAMBDA is on the right of the true   
+                               eigenvalue */
+				    right = lambda;
+/*                             See comment about assuming the error estimate is   
+                               correct above.   
+                                LEFT = MIN(LEFT, LAMBDA + RQCORR) */
+				}
+				work[windex] = (right + left) * .5;
+/*                          Take RQCORR since it has the correct sign and   
+                            improves the iterate reasonably */
+				lambda += rqcorr;
+/*                          Update width of error interval */
+				werr[windex] = (right - left) * .5;
+			    } else {
+				needbs = TRUE_;
+			    }
+			    if (right - left < rqtol * abs(lambda)) {
+/*                             The eigenvalue is computed to bisection accuracy   
+                               compute eigenvector and stop */
+				usedbs = TRUE_;
+				goto L120;
+			    } else if (iter < 10) {
+				goto L120;
+			    } else if (iter == 10) {
+				needbs = TRUE_;
+				goto L120;
+			    } else {
+				*info = 5;
+				return 0;
+			    }
+			} else {
+			    stp2ii = FALSE_;
+			    if (usedrq && usedbs && bstres <= resid) {
+				lambda = bstw;
+				stp2ii = TRUE_;
+			    }
+			    if (stp2ii) {
+/*                          improve error angle by second step */
+				L__1 = ! usedbs;
+				igraphdlar1v_(&in, &c__1, &in, &lambda, &d__[ibegin]
+					, &l[ibegin], &work[indld + ibegin - 
+					1], &work[indlld + ibegin - 1], 
+					pivmin, &gaptol, &z__[ibegin + windex 
+					* z_dim1], &L__1, &negcnt, &ztz, &
+					mingma, &iwork[iindr + windex], &
+					isuppz[(windex << 1) - 1], &nrminv, &
+					resid, &rqcorr, &work[indwrk]);
+			    }
+			    work[windex] = lambda;
+			}
+
+/*                    Compute FP-vector support w.r.t. whole matrix */
+
+			isuppz[(windex << 1) - 1] += oldien;
+			isuppz[windex * 2] += oldien;
+			zfrom = isuppz[(windex << 1) - 1];
+			zto = isuppz[windex * 2];
+			isupmn += oldien;
+			isupmx += oldien;
+/*                    Ensure vector is ok if support in the RQI has changed */
+			if (isupmn < zfrom) {
+			    i__4 = zfrom - 1;
+			    for (ii = isupmn; ii <= i__4; ++ii) {
+				z__[ii + windex * z_dim1] = 0.;
+/* L122: */
+			    }
+			}
+			if (isupmx > zto) {
+			    i__4 = isupmx;
+			    for (ii = zto + 1; ii <= i__4; ++ii) {
+				z__[ii + windex * z_dim1] = 0.;
+/* L123: */
+			    }
+			}
+			i__4 = zto - zfrom + 1;
+			igraphdscal_(&i__4, &nrminv, &z__[zfrom + windex * z_dim1], 
+				&c__1);
+L125:
+/*                    Update W */
+			w[windex] = lambda + sigma;
+/*                    Recompute the gaps on the left and right   
+                      But only allow them to become larger and not   
+                      smaller (which can only happen through "bad"   
+                      cancellation and doesn't reflect the theory   
+                      where the initial gaps are underestimated due   
+                      to WERR being too crude.) */
+			if (! eskip) {
+			    if (k > 1) {
+/* Computing MAX */
+				d__1 = wgap[windmn], d__2 = w[windex] - werr[
+					windex] - w[windmn] - werr[windmn];
+				wgap[windmn] = max(d__1,d__2);
+			    }
+			    if (windex < wend) {
+/* Computing MAX */
+				d__1 = savgap, d__2 = w[windpl] - werr[windpl]
+					 - w[windex] - werr[windex];
+				wgap[windex] = max(d__1,d__2);
+			    }
+			}
+			++idone;
+		    }
+/*                 here ends the code for the current child */
+
+L139:
+/*                 Proceed to any remaining child nodes */
+		    newfst = j + 1;
+L140:
+		    ;
+		}
+/* L150: */
+	    }
+	    ++ndepth;
+	    goto L40;
+	}
+	ibegin = iend + 1;
+	wbegin = wend + 1;
+L170:
+	;
+    }
+
+    return 0;
+
+/*     End of DLARRV */
+
+} /* igraphdlarrv_ */
+
diff --git a/igraph/src/dlartg.c b/igraph/src/dlartg.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlartg.c
@@ -0,0 +1,235 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARTG generates a plane rotation with real cosine and real sine.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARTG + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlartg.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlartg.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlartg.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARTG( F, G, CS, SN, R )   
+
+         DOUBLE PRECISION   CS, F, G, R, SN   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARTG generate a plane rotation so that   
+   >   
+   >    [  CS  SN  ]  .  [ F ]  =  [ R ]   where CS**2 + SN**2 = 1.   
+   >    [ -SN  CS  ]     [ G ]     [ 0 ]   
+   >   
+   > This is a slower, more accurate version of the BLAS1 routine DROTG,   
+   > with the following other differences:   
+   >    F and G are unchanged on return.   
+   >    If G=0, then CS=1 and SN=0.   
+   >    If F=0 and (G .ne. 0), then CS=0 and SN=1 without doing any   
+   >       floating point operations (saves work in DBDSQR when   
+   >       there are zeros on the diagonal).   
+   >   
+   > If F exceeds G in magnitude, CS will be positive.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] F   
+   > \verbatim   
+   >          F is DOUBLE PRECISION   
+   >          The first component of vector to be rotated.   
+   > \endverbatim   
+   >   
+   > \param[in] G   
+   > \verbatim   
+   >          G is DOUBLE PRECISION   
+   >          The second component of vector to be rotated.   
+   > \endverbatim   
+   >   
+   > \param[out] CS   
+   > \verbatim   
+   >          CS is DOUBLE PRECISION   
+   >          The cosine of the rotation.   
+   > \endverbatim   
+   >   
+   > \param[out] SN   
+   > \verbatim   
+   >          SN is DOUBLE PRECISION   
+   >          The sine of the rotation.   
+   > \endverbatim   
+   >   
+   > \param[out] R   
+   > \verbatim   
+   >          R is DOUBLE PRECISION   
+   >          The nonzero component of the rotated vector.   
+   >   
+   >  This version has a few statements commented out for thread safety   
+   >  (machine parameters are computed on each entry). 10 feb 03, SJH.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlartg_(doublereal *f, doublereal *g, doublereal *cs, 
+	doublereal *sn, doublereal *r__)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double log(doublereal), pow_di(doublereal *, integer *), sqrt(doublereal);
+
+    /* Local variables */
+    integer i__;
+    doublereal f1, g1, eps, scale;
+    integer count;
+    doublereal safmn2, safmx2;
+    extern doublereal igraphdlamch_(char *);
+    doublereal safmin;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+       LOGICAL            FIRST   
+       SAVE               FIRST, SAFMX2, SAFMIN, SAFMN2   
+       DATA               FIRST / .TRUE. /   
+
+       IF( FIRST ) THEN */
+    safmin = igraphdlamch_("S");
+    eps = igraphdlamch_("E");
+    d__1 = igraphdlamch_("B");
+    i__1 = (integer) (log(safmin / eps) / log(igraphdlamch_("B")) / 2.);
+    safmn2 = pow_di(&d__1, &i__1);
+    safmx2 = 1. / safmn2;
+/*        FIRST = .FALSE.   
+       END IF */
+    if (*g == 0.) {
+	*cs = 1.;
+	*sn = 0.;
+	*r__ = *f;
+    } else if (*f == 0.) {
+	*cs = 0.;
+	*sn = 1.;
+	*r__ = *g;
+    } else {
+	f1 = *f;
+	g1 = *g;
+/* Computing MAX */
+	d__1 = abs(f1), d__2 = abs(g1);
+	scale = max(d__1,d__2);
+	if (scale >= safmx2) {
+	    count = 0;
+L10:
+	    ++count;
+	    f1 *= safmn2;
+	    g1 *= safmn2;
+/* Computing MAX */
+	    d__1 = abs(f1), d__2 = abs(g1);
+	    scale = max(d__1,d__2);
+	    if (scale >= safmx2) {
+		goto L10;
+	    }
+/* Computing 2nd power */
+	    d__1 = f1;
+/* Computing 2nd power */
+	    d__2 = g1;
+	    *r__ = sqrt(d__1 * d__1 + d__2 * d__2);
+	    *cs = f1 / *r__;
+	    *sn = g1 / *r__;
+	    i__1 = count;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		*r__ *= safmx2;
+/* L20: */
+	    }
+	} else if (scale <= safmn2) {
+	    count = 0;
+L30:
+	    ++count;
+	    f1 *= safmx2;
+	    g1 *= safmx2;
+/* Computing MAX */
+	    d__1 = abs(f1), d__2 = abs(g1);
+	    scale = max(d__1,d__2);
+	    if (scale <= safmn2) {
+		goto L30;
+	    }
+/* Computing 2nd power */
+	    d__1 = f1;
+/* Computing 2nd power */
+	    d__2 = g1;
+	    *r__ = sqrt(d__1 * d__1 + d__2 * d__2);
+	    *cs = f1 / *r__;
+	    *sn = g1 / *r__;
+	    i__1 = count;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		*r__ *= safmn2;
+/* L40: */
+	    }
+	} else {
+/* Computing 2nd power */
+	    d__1 = f1;
+/* Computing 2nd power */
+	    d__2 = g1;
+	    *r__ = sqrt(d__1 * d__1 + d__2 * d__2);
+	    *cs = f1 / *r__;
+	    *sn = g1 / *r__;
+	}
+	if (abs(*f) > abs(*g) && *cs < 0.) {
+	    *cs = -(*cs);
+	    *sn = -(*sn);
+	    *r__ = -(*r__);
+	}
+    }
+    return 0;
+
+/*     End of DLARTG */
+
+} /* igraphdlartg_ */
+
diff --git a/igraph/src/dlaruv.c b/igraph/src/dlaruv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaruv.c
@@ -0,0 +1,236 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLARUV returns a vector of n random real numbers from a uniform distribution.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLARUV + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaruv.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaruv.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaruv.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLARUV( ISEED, N, X )   
+
+         INTEGER            N   
+         INTEGER            ISEED( 4 )   
+         DOUBLE PRECISION   X( N )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLARUV returns a vector of n random real numbers from a uniform (0,1)   
+   > distribution (n <= 128).   
+   >   
+   > This is an auxiliary routine called by DLARNV and ZLARNV.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in,out] ISEED   
+   > \verbatim   
+   >          ISEED is INTEGER array, dimension (4)   
+   >          On entry, the seed of the random number generator; the array   
+   >          elements must be between 0 and 4095, and ISEED(4) must be   
+   >          odd.   
+   >          On exit, the seed is updated.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of random numbers to be generated. N <= 128.   
+   > \endverbatim   
+   >   
+   > \param[out] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION array, dimension (N)   
+   >          The generated random numbers.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  This routine uses a multiplicative congruential method with modulus   
+   >  2**48 and multiplier 33952834046453 (see G.S.Fishman,   
+   >  'Multiplicative congruential random number generators with modulus   
+   >  2**b: an exhaustive analysis for b = 32 and a partial analysis for   
+   >  b = 48', Math. Comp. 189, pp 331-344, 1990).   
+   >   
+   >  48-bit integers are stored in 4 integer array elements with 12 bits   
+   >  per element. Hence the routine is portable across machines with   
+   >  integers of 32 bits or more.   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaruv_(integer *iseed, integer *n, doublereal *x)
+{
+    /* Initialized data */
+
+    static integer mm[512]	/* was [128][4] */ = { 494,2637,255,2008,1253,
+	    3344,4084,1739,3143,3468,688,1657,1238,3166,1292,3422,1270,2016,
+	    154,2862,697,1706,491,931,1444,444,3577,3944,2184,1661,3482,657,
+	    3023,3618,1267,1828,164,3798,3087,2400,2870,3876,1905,1593,1797,
+	    1234,3460,328,2861,1950,617,2070,3331,769,1558,2412,2800,189,287,
+	    2045,1227,2838,209,2770,3654,3993,192,2253,3491,2889,2857,2094,
+	    1818,688,1407,634,3231,815,3524,1914,516,164,303,2144,3480,119,
+	    3357,837,2826,2332,2089,3780,1700,3712,150,2000,3375,1621,3090,
+	    3765,1149,3146,33,3082,2741,359,3316,1749,185,2784,2202,2199,1364,
+	    1244,2020,3160,2785,2772,1217,1822,1245,2252,3904,2774,997,2573,
+	    1148,545,322,789,1440,752,2859,123,1848,643,2405,2638,2344,46,
+	    3814,913,3649,339,3808,822,2832,3078,3633,2970,637,2249,2081,4019,
+	    1478,242,481,2075,4058,622,3376,812,234,641,4005,1122,3135,2640,
+	    2302,40,1832,2247,2034,2637,1287,1691,496,1597,2394,2584,1843,336,
+	    1472,2407,433,2096,1761,2810,566,442,41,1238,1086,603,840,3168,
+	    1499,1084,3438,2408,1589,2391,288,26,512,1456,171,1677,2657,2270,
+	    2587,2961,1970,1817,676,1410,3723,2803,3185,184,663,499,3784,1631,
+	    1925,3912,1398,1349,1441,2224,2411,1907,3192,2786,382,37,759,2948,
+	    1862,3802,2423,2051,2295,1332,1832,2405,3638,3661,327,3660,716,
+	    1842,3987,1368,1848,2366,2508,3754,1766,3572,2893,307,1297,3966,
+	    758,2598,3406,2922,1038,2934,2091,2451,1580,1958,2055,1507,1078,
+	    3273,17,854,2916,3971,2889,3831,2621,1541,893,736,3992,787,2125,
+	    2364,2460,257,1574,3912,1216,3248,3401,2124,2762,149,2245,166,466,
+	    4018,1399,190,2879,153,2320,18,712,2159,2318,2091,3443,1510,449,
+	    1956,2201,3137,3399,1321,2271,3667,2703,629,2365,2431,1113,3922,
+	    2554,184,2099,3228,4012,1921,3452,3901,572,3309,3171,817,3039,
+	    1696,1256,3715,2077,3019,1497,1101,717,51,981,1978,1813,3881,76,
+	    3846,3694,1682,124,1660,3997,479,1141,886,3514,1301,3604,1888,
+	    1836,1990,2058,692,1194,20,3285,2046,2107,3508,3525,3801,2549,
+	    1145,2253,305,3301,1065,3133,2913,3285,1241,1197,3729,2501,1673,
+	    541,2753,949,2361,1165,4081,2725,3305,3069,3617,3733,409,2157,
+	    1361,3973,1865,2525,1409,3445,3577,77,3761,2149,1449,3005,225,85,
+	    3673,3117,3089,1349,2057,413,65,1845,697,3085,3441,1573,3689,2941,
+	    929,533,2841,4077,721,2821,2249,2397,2817,245,1913,1997,3121,997,
+	    1833,2877,1633,981,2009,941,2449,197,2441,285,1473,2741,3129,909,
+	    2801,421,4073,2813,2337,1429,1177,1901,81,1669,2633,2269,129,1141,
+	    249,3917,2481,3941,2217,2749,3041,1877,345,2861,1809,3141,2825,
+	    157,2881,3637,1465,2829,2161,3365,361,2685,3745,2325,3609,3821,
+	    3537,517,3017,2141,1537 };
+
+    /* System generated locals */
+    integer i__1;
+
+    /* Local variables */
+    integer i__, i1, i2, i3, i4, it1, it2, it3, it4;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --iseed;
+    --x;
+
+    /* Function Body */
+
+    i1 = iseed[1];
+    i2 = iseed[2];
+    i3 = iseed[3];
+    i4 = iseed[4];
+
+    i__1 = min(*n,128);
+    for (i__ = 1; i__ <= i__1; ++i__) {
+
+L20:
+
+/*        Multiply the seed by i-th power of the multiplier modulo 2**48 */
+
+	it4 = i4 * mm[i__ + 383];
+	it3 = it4 / 4096;
+	it4 -= it3 << 12;
+	it3 = it3 + i3 * mm[i__ + 383] + i4 * mm[i__ + 255];
+	it2 = it3 / 4096;
+	it3 -= it2 << 12;
+	it2 = it2 + i2 * mm[i__ + 383] + i3 * mm[i__ + 255] + i4 * mm[i__ + 
+		127];
+	it1 = it2 / 4096;
+	it2 -= it1 << 12;
+	it1 = it1 + i1 * mm[i__ + 383] + i2 * mm[i__ + 255] + i3 * mm[i__ + 
+		127] + i4 * mm[i__ - 1];
+	it1 %= 4096;
+
+/*        Convert 48-bit integer to a real number in the interval (0,1) */
+
+	x[i__] = ((doublereal) it1 + ((doublereal) it2 + ((doublereal) it3 + (
+		doublereal) it4 * 2.44140625e-4) * 2.44140625e-4) * 
+		2.44140625e-4) * 2.44140625e-4;
+
+	if (x[i__] == 1.) {
+/*           If a real number has n bits of precision, and the first   
+             n bits of the 48-bit integer above happen to be all 1 (which   
+             will occur about once every 2**n calls), then X( I ) will   
+             be rounded to exactly 1.0.   
+             Since X( I ) is not supposed to return exactly 0.0 or 1.0,   
+             the statistically correct thing to do in this situation is   
+             simply to iterate again.   
+             N.B. the case X( I ) = 0.0 should not be possible. */
+	    i1 += 2;
+	    i2 += 2;
+	    i3 += 2;
+	    i4 += 2;
+	    goto L20;
+	}
+
+/* L10: */
+    }
+
+/*     Return final value of seed */
+
+    iseed[1] = it1;
+    iseed[2] = it2;
+    iseed[3] = it3;
+    iseed[4] = it4;
+    return 0;
+
+/*     End of DLARUV */
+
+} /* igraphdlaruv_ */
+
diff --git a/igraph/src/dlascl.c b/igraph/src/dlascl.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlascl.c
@@ -0,0 +1,419 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASCL multiplies a general rectangular matrix by a real scalar defined as cto/cfrom.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASCL + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlascl.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlascl.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlascl.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASCL( TYPE, KL, KU, CFROM, CTO, M, N, A, LDA, INFO )   
+
+         CHARACTER          TYPE   
+         INTEGER            INFO, KL, KU, LDA, M, N   
+         DOUBLE PRECISION   CFROM, CTO   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASCL multiplies the M by N real matrix A by the real scalar   
+   > CTO/CFROM.  This is done without over/underflow as long as the final   
+   > result CTO*A(I,J)/CFROM does not over/underflow. TYPE specifies that   
+   > A may be full, upper triangular, lower triangular, upper Hessenberg,   
+   > or banded.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] TYPE   
+   > \verbatim   
+   >          TYPE is CHARACTER*1   
+   >          TYPE indices the storage type of the input matrix.   
+   >          = 'G':  A is a full matrix.   
+   >          = 'L':  A is a lower triangular matrix.   
+   >          = 'U':  A is an upper triangular matrix.   
+   >          = 'H':  A is an upper Hessenberg matrix.   
+   >          = 'B':  A is a symmetric band matrix with lower bandwidth KL   
+   >                  and upper bandwidth KU and with the only the lower   
+   >                  half stored.   
+   >          = 'Q':  A is a symmetric band matrix with lower bandwidth KL   
+   >                  and upper bandwidth KU and with the only the upper   
+   >                  half stored.   
+   >          = 'Z':  A is a band matrix with lower bandwidth KL and upper   
+   >                  bandwidth KU. See DGBTRF for storage details.   
+   > \endverbatim   
+   >   
+   > \param[in] KL   
+   > \verbatim   
+   >          KL is INTEGER   
+   >          The lower bandwidth of A.  Referenced only if TYPE = 'B',   
+   >          'Q' or 'Z'.   
+   > \endverbatim   
+   >   
+   > \param[in] KU   
+   > \verbatim   
+   >          KU is INTEGER   
+   >          The upper bandwidth of A.  Referenced only if TYPE = 'B',   
+   >          'Q' or 'Z'.   
+   > \endverbatim   
+   >   
+   > \param[in] CFROM   
+   > \verbatim   
+   >          CFROM is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] CTO   
+   > \verbatim   
+   >          CTO is DOUBLE PRECISION   
+   >   
+   >          The matrix A is multiplied by CTO/CFROM. A(I,J) is computed   
+   >          without over/underflow if the final result CTO*A(I,J)/CFROM   
+   >          can be represented without over/underflow.  CFROM must be   
+   >          nonzero.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.  M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The matrix to be multiplied by CTO/CFROM.  See TYPE for the   
+   >          storage type.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          0  - successful exit   
+   >          <0 - if INFO = -i, the i-th argument had an illegal value.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlascl_(char *type__, integer *kl, integer *ku, 
+	doublereal *cfrom, doublereal *cto, integer *m, integer *n, 
+	doublereal *a, integer *lda, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3, i__4, i__5;
+
+    /* Local variables */
+    integer i__, j, k1, k2, k3, k4;
+    doublereal mul, cto1;
+    logical done;
+    doublereal ctoc;
+    extern logical igraphlsame_(char *, char *);
+    integer itype;
+    doublereal cfrom1;
+    extern doublereal igraphdlamch_(char *);
+    doublereal cfromc;
+    extern logical igraphdisnan_(doublereal *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    doublereal bignum, smlnum;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    *info = 0;
+
+    if (igraphlsame_(type__, "G")) {
+	itype = 0;
+    } else if (igraphlsame_(type__, "L")) {
+	itype = 1;
+    } else if (igraphlsame_(type__, "U")) {
+	itype = 2;
+    } else if (igraphlsame_(type__, "H")) {
+	itype = 3;
+    } else if (igraphlsame_(type__, "B")) {
+	itype = 4;
+    } else if (igraphlsame_(type__, "Q")) {
+	itype = 5;
+    } else if (igraphlsame_(type__, "Z")) {
+	itype = 6;
+    } else {
+	itype = -1;
+    }
+
+    if (itype == -1) {
+	*info = -1;
+    } else if (*cfrom == 0. || igraphdisnan_(cfrom)) {
+	*info = -4;
+    } else if (igraphdisnan_(cto)) {
+	*info = -5;
+    } else if (*m < 0) {
+	*info = -6;
+    } else if (*n < 0 || itype == 4 && *n != *m || itype == 5 && *n != *m) {
+	*info = -7;
+    } else if (itype <= 3 && *lda < max(1,*m)) {
+	*info = -9;
+    } else if (itype >= 4) {
+/* Computing MAX */
+	i__1 = *m - 1;
+	if (*kl < 0 || *kl > max(i__1,0)) {
+	    *info = -2;
+	} else /* if(complicated condition) */ {
+/* Computing MAX */
+	    i__1 = *n - 1;
+	    if (*ku < 0 || *ku > max(i__1,0) || (itype == 4 || itype == 5) && 
+		    *kl != *ku) {
+		*info = -3;
+	    } else if (itype == 4 && *lda < *kl + 1 || itype == 5 && *lda < *
+		    ku + 1 || itype == 6 && *lda < (*kl << 1) + *ku + 1) {
+		*info = -9;
+	    }
+	}
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DLASCL", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0 || *m == 0) {
+	return 0;
+    }
+
+/*     Get machine parameters */
+
+    smlnum = igraphdlamch_("S");
+    bignum = 1. / smlnum;
+
+    cfromc = *cfrom;
+    ctoc = *cto;
+
+L10:
+    cfrom1 = cfromc * smlnum;
+    if (cfrom1 == cfromc) {
+/*        CFROMC is an inf.  Multiply by a correctly signed zero for   
+          finite CTOC, or a NaN if CTOC is infinite. */
+	mul = ctoc / cfromc;
+	done = TRUE_;
+	cto1 = ctoc;
+    } else {
+	cto1 = ctoc / bignum;
+	if (cto1 == ctoc) {
+/*           CTOC is either 0 or an inf.  In both cases, CTOC itself   
+             serves as the correct multiplication factor. */
+	    mul = ctoc;
+	    done = TRUE_;
+	    cfromc = 1.;
+	} else if (abs(cfrom1) > abs(ctoc) && ctoc != 0.) {
+	    mul = smlnum;
+	    done = FALSE_;
+	    cfromc = cfrom1;
+	} else if (abs(cto1) > abs(cfromc)) {
+	    mul = bignum;
+	    done = FALSE_;
+	    ctoc = cto1;
+	} else {
+	    mul = ctoc / cfromc;
+	    done = TRUE_;
+	}
+    }
+
+    if (itype == 0) {
+
+/*        Full matrix */
+
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] *= mul;
+/* L20: */
+	    }
+/* L30: */
+	}
+
+    } else if (itype == 1) {
+
+/*        Lower triangular matrix */
+
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = j; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] *= mul;
+/* L40: */
+	    }
+/* L50: */
+	}
+
+    } else if (itype == 2) {
+
+/*        Upper triangular matrix */
+
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = min(j,*m);
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] *= mul;
+/* L60: */
+	    }
+/* L70: */
+	}
+
+    } else if (itype == 3) {
+
+/*        Upper Hessenberg matrix */
+
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MIN */
+	    i__3 = j + 1;
+	    i__2 = min(i__3,*m);
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] *= mul;
+/* L80: */
+	    }
+/* L90: */
+	}
+
+    } else if (itype == 4) {
+
+/*        Lower half of a symmetric band matrix */
+
+	k3 = *kl + 1;
+	k4 = *n + 1;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MIN */
+	    i__3 = k3, i__4 = k4 - j;
+	    i__2 = min(i__3,i__4);
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] *= mul;
+/* L100: */
+	    }
+/* L110: */
+	}
+
+    } else if (itype == 5) {
+
+/*        Upper half of a symmetric band matrix */
+
+	k1 = *ku + 2;
+	k3 = *ku + 1;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MAX */
+	    i__2 = k1 - j;
+	    i__3 = k3;
+	    for (i__ = max(i__2,1); i__ <= i__3; ++i__) {
+		a[i__ + j * a_dim1] *= mul;
+/* L120: */
+	    }
+/* L130: */
+	}
+
+    } else if (itype == 6) {
+
+/*        Band matrix */
+
+	k1 = *kl + *ku + 2;
+	k2 = *kl + 1;
+	k3 = (*kl << 1) + *ku + 1;
+	k4 = *kl + *ku + 1 + *m;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MAX */
+	    i__3 = k1 - j;
+/* Computing MIN */
+	    i__4 = k3, i__5 = k4 - j;
+	    i__2 = min(i__4,i__5);
+	    for (i__ = max(i__3,k2); i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] *= mul;
+/* L140: */
+	    }
+/* L150: */
+	}
+
+    }
+
+    if (! done) {
+	goto L10;
+    }
+
+    return 0;
+
+/*     End of DLASCL */
+
+} /* igraphdlascl_ */
+
diff --git a/igraph/src/dlaset.c b/igraph/src/dlaset.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaset.c
@@ -0,0 +1,211 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASET initializes the off-diagonal elements and the diagonal elements of a matrix to given val
+ues.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASET + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaset.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaset.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaset.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASET( UPLO, M, N, ALPHA, BETA, A, LDA )   
+
+         CHARACTER          UPLO   
+         INTEGER            LDA, M, N   
+         DOUBLE PRECISION   ALPHA, BETA   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASET initializes an m-by-n matrix A to BETA on the diagonal and   
+   > ALPHA on the offdiagonals.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          Specifies the part of the matrix A to be set.   
+   >          = 'U':      Upper triangular part is set; the strictly lower   
+   >                      triangular part of A is not changed.   
+   >          = 'L':      Lower triangular part is set; the strictly upper   
+   >                      triangular part of A is not changed.   
+   >          Otherwise:  All of the matrix A is set.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.  M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ALPHA   
+   > \verbatim   
+   >          ALPHA is DOUBLE PRECISION   
+   >          The constant to which the offdiagonal elements are to be set.   
+   > \endverbatim   
+   >   
+   > \param[in] BETA   
+   > \verbatim   
+   >          BETA is DOUBLE PRECISION   
+   >          The constant to which the diagonal elements are to be set.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On exit, the leading m-by-n submatrix of A is set as follows:   
+   >   
+   >          if UPLO = 'U', A(i,j) = ALPHA, 1<=i<=j-1, 1<=j<=n,   
+   >          if UPLO = 'L', A(i,j) = ALPHA, j+1<=i<=m, 1<=j<=n,   
+   >          otherwise,     A(i,j) = ALPHA, 1<=i<=m, 1<=j<=n, i.ne.j,   
+   >   
+   >          and, for all UPLO, A(i,i) = BETA, 1<=i<=min(m,n).   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,M).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlaset_(char *uplo, integer *m, integer *n, doublereal *
+	alpha, doublereal *beta, doublereal *a, integer *lda)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__, j;
+    extern logical igraphlsame_(char *, char *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    if (igraphlsame_(uplo, "U")) {
+
+/*        Set the strictly upper triangular or trapezoidal part of the   
+          array to ALPHA. */
+
+	i__1 = *n;
+	for (j = 2; j <= i__1; ++j) {
+/* Computing MIN */
+	    i__3 = j - 1;
+	    i__2 = min(i__3,*m);
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] = *alpha;
+/* L10: */
+	    }
+/* L20: */
+	}
+
+    } else if (igraphlsame_(uplo, "L")) {
+
+/*        Set the strictly lower triangular or trapezoidal part of the   
+          array to ALPHA. */
+
+	i__1 = min(*m,*n);
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = j + 1; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] = *alpha;
+/* L30: */
+	    }
+/* L40: */
+	}
+
+    } else {
+
+/*        Set the leading m-by-n submatrix to ALPHA. */
+
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] = *alpha;
+/* L50: */
+	    }
+/* L60: */
+	}
+    }
+
+/*     Set the first min(M,N) diagonal elements to BETA. */
+
+    i__1 = min(*m,*n);
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	a[i__ + i__ * a_dim1] = *beta;
+/* L70: */
+    }
+
+    return 0;
+
+/*     End of DLASET */
+
+} /* igraphdlaset_ */
+
diff --git a/igraph/src/dlasq2.c b/igraph/src/dlasq2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlasq2.c
@@ -0,0 +1,688 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c__2 = 2;
+static integer c__10 = 10;
+static integer c__3 = 3;
+static integer c__4 = 4;
+static integer c__11 = 11;
+
+/* > \brief \b DLASQ2 computes all the eigenvalues of the symmetric positive definite tridiagonal matrix assoc
+iated with the qd Array Z to high relative accuracy. Used by sbdsqr and sstegr.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASQ2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlasq2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlasq2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlasq2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASQ2( N, Z, INFO )   
+
+         INTEGER            INFO, N   
+         DOUBLE PRECISION   Z( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASQ2 computes all the eigenvalues of the symmetric positive   
+   > definite tridiagonal matrix associated with the qd array Z to high   
+   > relative accuracy are computed to high relative accuracy, in the   
+   > absence of denormalization, underflow and overflow.   
+   >   
+   > To see the relation of Z to the tridiagonal matrix, let L be a   
+   > unit lower bidiagonal matrix with subdiagonals Z(2,4,6,,..) and   
+   > let U be an upper bidiagonal matrix with 1's above and diagonal   
+   > Z(1,3,5,,..). The tridiagonal is L*U or, if you prefer, the   
+   > symmetric tridiagonal to which it is similar.   
+   >   
+   > Note : DLASQ2 defines a logical variable, IEEE, which is true   
+   > on machines which follow ieee-754 floating-point standard in their   
+   > handling of infinities and NaNs, and false otherwise. This variable   
+   > is passed to DLASQ3.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >        The number of rows and columns in the matrix. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension ( 4*N )   
+   >        On entry Z holds the qd array. On exit, entries 1 to N hold   
+   >        the eigenvalues in decreasing order, Z( 2*N+1 ) holds the   
+   >        trace, and Z( 2*N+2 ) holds the sum of the eigenvalues. If   
+   >        N > 2, then Z( 2*N+3 ) holds the iteration count, Z( 2*N+4 )   
+   >        holds NDIVS/NIN^2, and Z( 2*N+5 ) holds the percentage of   
+   >        shifts that failed.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >        = 0: successful exit   
+   >        < 0: if the i-th argument is a scalar and had an illegal   
+   >             value, then INFO = -i, if the i-th argument is an   
+   >             array and the j-entry had an illegal value, then   
+   >             INFO = -(i*100+j)   
+   >        > 0: the algorithm failed   
+   >              = 1, a split was marked by a positive value in E   
+   >              = 2, current block of Z not diagonalized after 100*N   
+   >                   iterations (in inner while loop).  On exit Z holds   
+   >                   a qd array with the same eigenvalues as the given Z.   
+   >              = 3, termination criterion of outer while loop not met   
+   >                   (program created more than N unreduced blocks)   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  Local Variables: I0:N0 defines a current unreduced segment of Z.   
+   >  The shifts are accumulated in SIGMA. Iteration count is in ITER.   
+   >  Ping-pong is controlled by PP (alternates between 0 and 1).   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlasq2_(integer *n, doublereal *z__, integer *info)
+{
+    /* System generated locals */
+    integer i__1, i__2, i__3;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    doublereal d__, e, g;
+    integer k;
+    doublereal s, t;
+    integer i0, i1, i4, n0, n1;
+    doublereal dn;
+    integer pp;
+    doublereal dn1, dn2, dee, eps, tau, tol;
+    integer ipn4;
+    doublereal tol2;
+    logical ieee;
+    integer nbig;
+    doublereal dmin__, emin, emax;
+    integer kmin, ndiv, iter;
+    doublereal qmin, temp, qmax, zmax;
+    integer splt;
+    doublereal dmin1, dmin2;
+    integer nfail;
+    doublereal desig, trace, sigma;
+    integer iinfo;
+    doublereal tempe, tempq;
+    integer ttype;
+    extern /* Subroutine */ int igraphdlasq3_(integer *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, doublereal *, doublereal *,
+	     integer *, integer *, integer *, logical *, integer *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    doublereal deemin;
+    integer iwhila, iwhilb;
+    doublereal oldemn, safmin;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    extern /* Subroutine */ int igraphdlasrt_(char *, integer *, doublereal *, 
+	    integer *);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input arguments.   
+       (in case DLASQ2 is not called by DLASQ1)   
+
+       Parameter adjustments */
+    --z__;
+
+    /* Function Body */
+    *info = 0;
+    eps = igraphdlamch_("Precision");
+    safmin = igraphdlamch_("Safe minimum");
+    tol = eps * 100.;
+/* Computing 2nd power */
+    d__1 = tol;
+    tol2 = d__1 * d__1;
+
+    if (*n < 0) {
+	*info = -1;
+	igraphxerbla_("DLASQ2", &c__1, (ftnlen)6);
+	return 0;
+    } else if (*n == 0) {
+	return 0;
+    } else if (*n == 1) {
+
+/*        1-by-1 case. */
+
+	if (z__[1] < 0.) {
+	    *info = -201;
+	    igraphxerbla_("DLASQ2", &c__2, (ftnlen)6);
+	}
+	return 0;
+    } else if (*n == 2) {
+
+/*        2-by-2 case. */
+
+	if (z__[2] < 0. || z__[3] < 0.) {
+	    *info = -2;
+	    igraphxerbla_("DLASQ2", &c__2, (ftnlen)6);
+	    return 0;
+	} else if (z__[3] > z__[1]) {
+	    d__ = z__[3];
+	    z__[3] = z__[1];
+	    z__[1] = d__;
+	}
+	z__[5] = z__[1] + z__[2] + z__[3];
+	if (z__[2] > z__[3] * tol2) {
+	    t = (z__[1] - z__[3] + z__[2]) * .5;
+	    s = z__[3] * (z__[2] / t);
+	    if (s <= t) {
+		s = z__[3] * (z__[2] / (t * (sqrt(s / t + 1.) + 1.)));
+	    } else {
+		s = z__[3] * (z__[2] / (t + sqrt(t) * sqrt(t + s)));
+	    }
+	    t = z__[1] + (s + z__[2]);
+	    z__[3] *= z__[1] / t;
+	    z__[1] = t;
+	}
+	z__[2] = z__[3];
+	z__[6] = z__[2] + z__[1];
+	return 0;
+    }
+
+/*     Check for negative data and compute sums of q's and e's. */
+
+    z__[*n * 2] = 0.;
+    emin = z__[2];
+    qmax = 0.;
+    zmax = 0.;
+    d__ = 0.;
+    e = 0.;
+
+    i__1 = *n - 1 << 1;
+    for (k = 1; k <= i__1; k += 2) {
+	if (z__[k] < 0.) {
+	    *info = -(k + 200);
+	    igraphxerbla_("DLASQ2", &c__2, (ftnlen)6);
+	    return 0;
+	} else if (z__[k + 1] < 0.) {
+	    *info = -(k + 201);
+	    igraphxerbla_("DLASQ2", &c__2, (ftnlen)6);
+	    return 0;
+	}
+	d__ += z__[k];
+	e += z__[k + 1];
+/* Computing MAX */
+	d__1 = qmax, d__2 = z__[k];
+	qmax = max(d__1,d__2);
+/* Computing MIN */
+	d__1 = emin, d__2 = z__[k + 1];
+	emin = min(d__1,d__2);
+/* Computing MAX */
+	d__1 = max(qmax,zmax), d__2 = z__[k + 1];
+	zmax = max(d__1,d__2);
+/* L10: */
+    }
+    if (z__[(*n << 1) - 1] < 0.) {
+	*info = -((*n << 1) + 199);
+	igraphxerbla_("DLASQ2", &c__2, (ftnlen)6);
+	return 0;
+    }
+    d__ += z__[(*n << 1) - 1];
+/* Computing MAX */
+    d__1 = qmax, d__2 = z__[(*n << 1) - 1];
+    qmax = max(d__1,d__2);
+    zmax = max(qmax,zmax);
+
+/*     Check for diagonality. */
+
+    if (e == 0.) {
+	i__1 = *n;
+	for (k = 2; k <= i__1; ++k) {
+	    z__[k] = z__[(k << 1) - 1];
+/* L20: */
+	}
+	igraphdlasrt_("D", n, &z__[1], &iinfo);
+	z__[(*n << 1) - 1] = d__;
+	return 0;
+    }
+
+    trace = d__ + e;
+
+/*     Check for zero data. */
+
+    if (trace == 0.) {
+	z__[(*n << 1) - 1] = 0.;
+	return 0;
+    }
+
+/*     Check whether the machine is IEEE conformable. */
+
+    ieee = igraphilaenv_(&c__10, "DLASQ2", "N", &c__1, &c__2, &c__3, &c__4, (ftnlen)
+	    6, (ftnlen)1) == 1 && igraphilaenv_(&c__11, "DLASQ2", "N", &c__1, &c__2,
+	     &c__3, &c__4, (ftnlen)6, (ftnlen)1) == 1;
+
+/*     Rearrange data for locality: Z=(q1,qq1,e1,ee1,q2,qq2,e2,ee2,...). */
+
+    for (k = *n << 1; k >= 2; k += -2) {
+	z__[k * 2] = 0.;
+	z__[(k << 1) - 1] = z__[k];
+	z__[(k << 1) - 2] = 0.;
+	z__[(k << 1) - 3] = z__[k - 1];
+/* L30: */
+    }
+
+    i0 = 1;
+    n0 = *n;
+
+/*     Reverse the qd-array, if warranted. */
+
+    if (z__[(i0 << 2) - 3] * 1.5 < z__[(n0 << 2) - 3]) {
+	ipn4 = i0 + n0 << 2;
+	i__1 = i0 + n0 - 1 << 1;
+	for (i4 = i0 << 2; i4 <= i__1; i4 += 4) {
+	    temp = z__[i4 - 3];
+	    z__[i4 - 3] = z__[ipn4 - i4 - 3];
+	    z__[ipn4 - i4 - 3] = temp;
+	    temp = z__[i4 - 1];
+	    z__[i4 - 1] = z__[ipn4 - i4 - 5];
+	    z__[ipn4 - i4 - 5] = temp;
+/* L40: */
+	}
+    }
+
+/*     Initial split checking via dqd and Li's test. */
+
+    pp = 0;
+
+    for (k = 1; k <= 2; ++k) {
+
+	d__ = z__[(n0 << 2) + pp - 3];
+	i__1 = (i0 << 2) + pp;
+	for (i4 = (n0 - 1 << 2) + pp; i4 >= i__1; i4 += -4) {
+	    if (z__[i4 - 1] <= tol2 * d__) {
+		z__[i4 - 1] = -0.;
+		d__ = z__[i4 - 3];
+	    } else {
+		d__ = z__[i4 - 3] * (d__ / (d__ + z__[i4 - 1]));
+	    }
+/* L50: */
+	}
+
+/*        dqd maps Z to ZZ plus Li's test. */
+
+	emin = z__[(i0 << 2) + pp + 1];
+	d__ = z__[(i0 << 2) + pp - 3];
+	i__1 = (n0 - 1 << 2) + pp;
+	for (i4 = (i0 << 2) + pp; i4 <= i__1; i4 += 4) {
+	    z__[i4 - (pp << 1) - 2] = d__ + z__[i4 - 1];
+	    if (z__[i4 - 1] <= tol2 * d__) {
+		z__[i4 - 1] = -0.;
+		z__[i4 - (pp << 1) - 2] = d__;
+		z__[i4 - (pp << 1)] = 0.;
+		d__ = z__[i4 + 1];
+	    } else if (safmin * z__[i4 + 1] < z__[i4 - (pp << 1) - 2] && 
+		    safmin * z__[i4 - (pp << 1) - 2] < z__[i4 + 1]) {
+		temp = z__[i4 + 1] / z__[i4 - (pp << 1) - 2];
+		z__[i4 - (pp << 1)] = z__[i4 - 1] * temp;
+		d__ *= temp;
+	    } else {
+		z__[i4 - (pp << 1)] = z__[i4 + 1] * (z__[i4 - 1] / z__[i4 - (
+			pp << 1) - 2]);
+		d__ = z__[i4 + 1] * (d__ / z__[i4 - (pp << 1) - 2]);
+	    }
+/* Computing MIN */
+	    d__1 = emin, d__2 = z__[i4 - (pp << 1)];
+	    emin = min(d__1,d__2);
+/* L60: */
+	}
+	z__[(n0 << 2) - pp - 2] = d__;
+
+/*        Now find qmax. */
+
+	qmax = z__[(i0 << 2) - pp - 2];
+	i__1 = (n0 << 2) - pp - 2;
+	for (i4 = (i0 << 2) - pp + 2; i4 <= i__1; i4 += 4) {
+/* Computing MAX */
+	    d__1 = qmax, d__2 = z__[i4];
+	    qmax = max(d__1,d__2);
+/* L70: */
+	}
+
+/*        Prepare for the next iteration on K. */
+
+	pp = 1 - pp;
+/* L80: */
+    }
+
+/*     Initialise variables to pass to DLASQ3. */
+
+    ttype = 0;
+    dmin1 = 0.;
+    dmin2 = 0.;
+    dn = 0.;
+    dn1 = 0.;
+    dn2 = 0.;
+    g = 0.;
+    tau = 0.;
+
+    iter = 2;
+    nfail = 0;
+    ndiv = n0 - i0 << 1;
+
+    i__1 = *n + 1;
+    for (iwhila = 1; iwhila <= i__1; ++iwhila) {
+	if (n0 < 1) {
+	    goto L170;
+	}
+
+/*        While array unfinished do   
+
+          E(N0) holds the value of SIGMA when submatrix in I0:N0   
+          splits from the rest of the array, but is negated. */
+
+	desig = 0.;
+	if (n0 == *n) {
+	    sigma = 0.;
+	} else {
+	    sigma = -z__[(n0 << 2) - 1];
+	}
+	if (sigma < 0.) {
+	    *info = 1;
+	    return 0;
+	}
+
+/*        Find last unreduced submatrix's top index I0, find QMAX and   
+          EMIN. Find Gershgorin-type bound if Q's much greater than E's. */
+
+	emax = 0.;
+	if (n0 > i0) {
+	    emin = (d__1 = z__[(n0 << 2) - 5], abs(d__1));
+	} else {
+	    emin = 0.;
+	}
+	qmin = z__[(n0 << 2) - 3];
+	qmax = qmin;
+	for (i4 = n0 << 2; i4 >= 8; i4 += -4) {
+	    if (z__[i4 - 5] <= 0.) {
+		goto L100;
+	    }
+	    if (qmin >= emax * 4.) {
+/* Computing MIN */
+		d__1 = qmin, d__2 = z__[i4 - 3];
+		qmin = min(d__1,d__2);
+/* Computing MAX */
+		d__1 = emax, d__2 = z__[i4 - 5];
+		emax = max(d__1,d__2);
+	    }
+/* Computing MAX */
+	    d__1 = qmax, d__2 = z__[i4 - 7] + z__[i4 - 5];
+	    qmax = max(d__1,d__2);
+/* Computing MIN */
+	    d__1 = emin, d__2 = z__[i4 - 5];
+	    emin = min(d__1,d__2);
+/* L90: */
+	}
+	i4 = 4;
+
+L100:
+	i0 = i4 / 4;
+	pp = 0;
+
+	if (n0 - i0 > 1) {
+	    dee = z__[(i0 << 2) - 3];
+	    deemin = dee;
+	    kmin = i0;
+	    i__2 = (n0 << 2) - 3;
+	    for (i4 = (i0 << 2) + 1; i4 <= i__2; i4 += 4) {
+		dee = z__[i4] * (dee / (dee + z__[i4 - 2]));
+		if (dee <= deemin) {
+		    deemin = dee;
+		    kmin = (i4 + 3) / 4;
+		}
+/* L110: */
+	    }
+	    if (kmin - i0 << 1 < n0 - kmin && deemin <= z__[(n0 << 2) - 3] * 
+		    .5) {
+		ipn4 = i0 + n0 << 2;
+		pp = 2;
+		i__2 = i0 + n0 - 1 << 1;
+		for (i4 = i0 << 2; i4 <= i__2; i4 += 4) {
+		    temp = z__[i4 - 3];
+		    z__[i4 - 3] = z__[ipn4 - i4 - 3];
+		    z__[ipn4 - i4 - 3] = temp;
+		    temp = z__[i4 - 2];
+		    z__[i4 - 2] = z__[ipn4 - i4 - 2];
+		    z__[ipn4 - i4 - 2] = temp;
+		    temp = z__[i4 - 1];
+		    z__[i4 - 1] = z__[ipn4 - i4 - 5];
+		    z__[ipn4 - i4 - 5] = temp;
+		    temp = z__[i4];
+		    z__[i4] = z__[ipn4 - i4 - 4];
+		    z__[ipn4 - i4 - 4] = temp;
+/* L120: */
+		}
+	    }
+	}
+
+/*        Put -(initial shift) into DMIN.   
+
+   Computing MAX */
+	d__1 = 0., d__2 = qmin - sqrt(qmin) * 2. * sqrt(emax);
+	dmin__ = -max(d__1,d__2);
+
+/*        Now I0:N0 is unreduced.   
+          PP = 0 for ping, PP = 1 for pong.   
+          PP = 2 indicates that flipping was applied to the Z array and   
+                 and that the tests for deflation upon entry in DLASQ3   
+                 should not be performed. */
+
+	nbig = (n0 - i0 + 1) * 100;
+	i__2 = nbig;
+	for (iwhilb = 1; iwhilb <= i__2; ++iwhilb) {
+	    if (i0 > n0) {
+		goto L150;
+	    }
+
+/*           While submatrix unfinished take a good dqds step. */
+
+	    igraphdlasq3_(&i0, &n0, &z__[1], &pp, &dmin__, &sigma, &desig, &qmax, &
+		    nfail, &iter, &ndiv, &ieee, &ttype, &dmin1, &dmin2, &dn, &
+		    dn1, &dn2, &g, &tau);
+
+	    pp = 1 - pp;
+
+/*           When EMIN is very small check for splits. */
+
+	    if (pp == 0 && n0 - i0 >= 3) {
+		if (z__[n0 * 4] <= tol2 * qmax || z__[(n0 << 2) - 1] <= tol2 *
+			 sigma) {
+		    splt = i0 - 1;
+		    qmax = z__[(i0 << 2) - 3];
+		    emin = z__[(i0 << 2) - 1];
+		    oldemn = z__[i0 * 4];
+		    i__3 = n0 - 3 << 2;
+		    for (i4 = i0 << 2; i4 <= i__3; i4 += 4) {
+			if (z__[i4] <= tol2 * z__[i4 - 3] || z__[i4 - 1] <= 
+				tol2 * sigma) {
+			    z__[i4 - 1] = -sigma;
+			    splt = i4 / 4;
+			    qmax = 0.;
+			    emin = z__[i4 + 3];
+			    oldemn = z__[i4 + 4];
+			} else {
+/* Computing MAX */
+			    d__1 = qmax, d__2 = z__[i4 + 1];
+			    qmax = max(d__1,d__2);
+/* Computing MIN */
+			    d__1 = emin, d__2 = z__[i4 - 1];
+			    emin = min(d__1,d__2);
+/* Computing MIN */
+			    d__1 = oldemn, d__2 = z__[i4];
+			    oldemn = min(d__1,d__2);
+			}
+/* L130: */
+		    }
+		    z__[(n0 << 2) - 1] = emin;
+		    z__[n0 * 4] = oldemn;
+		    i0 = splt + 1;
+		}
+	    }
+
+/* L140: */
+	}
+
+	*info = 2;
+
+/*        Maximum number of iterations exceeded, restore the shift   
+          SIGMA and place the new d's and e's in a qd array.   
+          This might need to be done for several blocks */
+
+	i1 = i0;
+	n1 = n0;
+L145:
+	tempq = z__[(i0 << 2) - 3];
+	z__[(i0 << 2) - 3] += sigma;
+	i__2 = n0;
+	for (k = i0 + 1; k <= i__2; ++k) {
+	    tempe = z__[(k << 2) - 5];
+	    z__[(k << 2) - 5] *= tempq / z__[(k << 2) - 7];
+	    tempq = z__[(k << 2) - 3];
+	    z__[(k << 2) - 3] = z__[(k << 2) - 3] + sigma + tempe - z__[(k << 
+		    2) - 5];
+	}
+
+/*        Prepare to do this on the previous block if there is one */
+
+	if (i1 > 1) {
+	    n1 = i1 - 1;
+	    while(i1 >= 2 && z__[(i1 << 2) - 5] >= 0.) {
+		--i1;
+	    }
+	    sigma = -z__[(n1 << 2) - 1];
+	    goto L145;
+	}
+	i__2 = *n;
+	for (k = 1; k <= i__2; ++k) {
+	    z__[(k << 1) - 1] = z__[(k << 2) - 3];
+
+/*        Only the block 1..N0 is unfinished.  The rest of the e's   
+          must be essentially zero, although sometimes other data   
+          has been stored in them. */
+
+	    if (k < n0) {
+		z__[k * 2] = z__[(k << 2) - 1];
+	    } else {
+		z__[k * 2] = 0.;
+	    }
+	}
+	return 0;
+
+/*        end IWHILB */
+
+L150:
+
+/* L160: */
+	;
+    }
+
+    *info = 3;
+    return 0;
+
+/*     end IWHILA */
+
+L170:
+
+/*     Move q's to the front. */
+
+    i__1 = *n;
+    for (k = 2; k <= i__1; ++k) {
+	z__[k] = z__[(k << 2) - 3];
+/* L180: */
+    }
+
+/*     Sort and compute sum of eigenvalues. */
+
+    igraphdlasrt_("D", n, &z__[1], &iinfo);
+
+    e = 0.;
+    for (k = *n; k >= 1; --k) {
+	e += z__[k];
+/* L190: */
+    }
+
+/*     Store trace, sum(eigenvalues) and information on performance. */
+
+    z__[(*n << 1) + 1] = trace;
+    z__[(*n << 1) + 2] = e;
+    z__[(*n << 1) + 3] = (doublereal) iter;
+/* Computing 2nd power */
+    i__1 = *n;
+    z__[(*n << 1) + 4] = (doublereal) ndiv / (doublereal) (i__1 * i__1);
+    z__[(*n << 1) + 5] = nfail * 100. / (doublereal) iter;
+    return 0;
+
+/*     End of DLASQ2 */
+
+} /* igraphdlasq2_ */
+
diff --git a/igraph/src/dlasq3.c b/igraph/src/dlasq3.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlasq3.c
@@ -0,0 +1,453 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASQ3 checks for deflation, computes a shift and calls dqds. Used by sbdsqr.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASQ3 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlasq3.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlasq3.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlasq3.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASQ3( I0, N0, Z, PP, DMIN, SIGMA, DESIG, QMAX, NFAIL,   
+                            ITER, NDIV, IEEE, TTYPE, DMIN1, DMIN2, DN, DN1,   
+                            DN2, G, TAU )   
+
+         LOGICAL            IEEE   
+         INTEGER            I0, ITER, N0, NDIV, NFAIL, PP   
+         DOUBLE PRECISION   DESIG, DMIN, DMIN1, DMIN2, DN, DN1, DN2, G,   
+        $                   QMAX, SIGMA, TAU   
+         DOUBLE PRECISION   Z( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASQ3 checks for deflation, computes a shift (TAU) and calls dqds.   
+   > In case of failure it changes shifts, and tries again until output   
+   > is positive.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] I0   
+   > \verbatim   
+   >          I0 is INTEGER   
+   >         First index.   
+   > \endverbatim   
+   >   
+   > \param[in,out] N0   
+   > \verbatim   
+   >          N0 is INTEGER   
+   >         Last index.   
+   > \endverbatim   
+   >   
+   > \param[in] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension ( 4*N )   
+   >         Z holds the qd array.   
+   > \endverbatim   
+   >   
+   > \param[in,out] PP   
+   > \verbatim   
+   >          PP is INTEGER   
+   >         PP=0 for ping, PP=1 for pong.   
+   >         PP=2 indicates that flipping was applied to the Z array   
+   >         and that the initial tests for deflation should not be   
+   >         performed.   
+   > \endverbatim   
+   >   
+   > \param[out] DMIN   
+   > \verbatim   
+   >          DMIN is DOUBLE PRECISION   
+   >         Minimum value of d.   
+   > \endverbatim   
+   >   
+   > \param[out] SIGMA   
+   > \verbatim   
+   >          SIGMA is DOUBLE PRECISION   
+   >         Sum of shifts used in current segment.   
+   > \endverbatim   
+   >   
+   > \param[in,out] DESIG   
+   > \verbatim   
+   >          DESIG is DOUBLE PRECISION   
+   >         Lower order part of SIGMA   
+   > \endverbatim   
+   >   
+   > \param[in] QMAX   
+   > \verbatim   
+   >          QMAX is DOUBLE PRECISION   
+   >         Maximum value of q.   
+   > \endverbatim   
+   >   
+   > \param[out] NFAIL   
+   > \verbatim   
+   >          NFAIL is INTEGER   
+   >         Number of times shift was too big.   
+   > \endverbatim   
+   >   
+   > \param[out] ITER   
+   > \verbatim   
+   >          ITER is INTEGER   
+   >         Number of iterations.   
+   > \endverbatim   
+   >   
+   > \param[out] NDIV   
+   > \verbatim   
+   >          NDIV is INTEGER   
+   >         Number of divisions.   
+   > \endverbatim   
+   >   
+   > \param[in] IEEE   
+   > \verbatim   
+   >          IEEE is LOGICAL   
+   >         Flag for IEEE or non IEEE arithmetic (passed to DLASQ5).   
+   > \endverbatim   
+   >   
+   > \param[in,out] TTYPE   
+   > \verbatim   
+   >          TTYPE is INTEGER   
+   >         Shift type.   
+   > \endverbatim   
+   >   
+   > \param[in,out] DMIN1   
+   > \verbatim   
+   >          DMIN1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] DMIN2   
+   > \verbatim   
+   >          DMIN2 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] DN   
+   > \verbatim   
+   >          DN is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] DN1   
+   > \verbatim   
+   >          DN1 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] DN2   
+   > \verbatim   
+   >          DN2 is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] G   
+   > \verbatim   
+   >          G is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in,out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION   
+   >   
+   >         These are passed as arguments in order to save their values   
+   >         between calls to DLASQ3.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdlasq3_(integer *i0, integer *n0, doublereal *z__, 
+	integer *pp, doublereal *dmin__, doublereal *sigma, doublereal *desig,
+	 doublereal *qmax, integer *nfail, integer *iter, integer *ndiv, 
+	logical *ieee, integer *ttype, doublereal *dmin1, doublereal *dmin2, 
+	doublereal *dn, doublereal *dn1, doublereal *dn2, doublereal *g, 
+	doublereal *tau)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    doublereal s, t;
+    integer j4, nn;
+    doublereal eps, tol;
+    integer n0in, ipn4;
+    doublereal tol2, temp;
+    extern /* Subroutine */ int igraphdlasq4_(integer *, integer *, doublereal *, 
+	    integer *, integer *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, integer *,
+	     doublereal *), igraphdlasq5_(integer *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, doublereal *, doublereal *,
+	     doublereal *, doublereal *, doublereal *, doublereal *, logical *
+	    , doublereal *), igraphdlasq6_(integer *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, doublereal *, doublereal *,
+	     doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern logical igraphdisnan_(doublereal *);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --z__;
+
+    /* Function Body */
+    n0in = *n0;
+    eps = igraphdlamch_("Precision");
+    tol = eps * 100.;
+/* Computing 2nd power */
+    d__1 = tol;
+    tol2 = d__1 * d__1;
+
+/*     Check for deflation. */
+
+L10:
+
+    if (*n0 < *i0) {
+	return 0;
+    }
+    if (*n0 == *i0) {
+	goto L20;
+    }
+    nn = (*n0 << 2) + *pp;
+    if (*n0 == *i0 + 1) {
+	goto L40;
+    }
+
+/*     Check whether E(N0-1) is negligible, 1 eigenvalue. */
+
+    if (z__[nn - 5] > tol2 * (*sigma + z__[nn - 3]) && z__[nn - (*pp << 1) - 
+	    4] > tol2 * z__[nn - 7]) {
+	goto L30;
+    }
+
+L20:
+
+    z__[(*n0 << 2) - 3] = z__[(*n0 << 2) + *pp - 3] + *sigma;
+    --(*n0);
+    goto L10;
+
+/*     Check  whether E(N0-2) is negligible, 2 eigenvalues. */
+
+L30:
+
+    if (z__[nn - 9] > tol2 * *sigma && z__[nn - (*pp << 1) - 8] > tol2 * z__[
+	    nn - 11]) {
+	goto L50;
+    }
+
+L40:
+
+    if (z__[nn - 3] > z__[nn - 7]) {
+	s = z__[nn - 3];
+	z__[nn - 3] = z__[nn - 7];
+	z__[nn - 7] = s;
+    }
+    t = (z__[nn - 7] - z__[nn - 3] + z__[nn - 5]) * .5;
+    if (z__[nn - 5] > z__[nn - 3] * tol2 && t != 0.) {
+	s = z__[nn - 3] * (z__[nn - 5] / t);
+	if (s <= t) {
+	    s = z__[nn - 3] * (z__[nn - 5] / (t * (sqrt(s / t + 1.) + 1.)));
+	} else {
+	    s = z__[nn - 3] * (z__[nn - 5] / (t + sqrt(t) * sqrt(t + s)));
+	}
+	t = z__[nn - 7] + (s + z__[nn - 5]);
+	z__[nn - 3] *= z__[nn - 7] / t;
+	z__[nn - 7] = t;
+    }
+    z__[(*n0 << 2) - 7] = z__[nn - 7] + *sigma;
+    z__[(*n0 << 2) - 3] = z__[nn - 3] + *sigma;
+    *n0 += -2;
+    goto L10;
+
+L50:
+    if (*pp == 2) {
+	*pp = 0;
+    }
+
+/*     Reverse the qd-array, if warranted. */
+
+    if (*dmin__ <= 0. || *n0 < n0in) {
+	if (z__[(*i0 << 2) + *pp - 3] * 1.5 < z__[(*n0 << 2) + *pp - 3]) {
+	    ipn4 = *i0 + *n0 << 2;
+	    i__1 = *i0 + *n0 - 1 << 1;
+	    for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		temp = z__[j4 - 3];
+		z__[j4 - 3] = z__[ipn4 - j4 - 3];
+		z__[ipn4 - j4 - 3] = temp;
+		temp = z__[j4 - 2];
+		z__[j4 - 2] = z__[ipn4 - j4 - 2];
+		z__[ipn4 - j4 - 2] = temp;
+		temp = z__[j4 - 1];
+		z__[j4 - 1] = z__[ipn4 - j4 - 5];
+		z__[ipn4 - j4 - 5] = temp;
+		temp = z__[j4];
+		z__[j4] = z__[ipn4 - j4 - 4];
+		z__[ipn4 - j4 - 4] = temp;
+/* L60: */
+	    }
+	    if (*n0 - *i0 <= 4) {
+		z__[(*n0 << 2) + *pp - 1] = z__[(*i0 << 2) + *pp - 1];
+		z__[(*n0 << 2) - *pp] = z__[(*i0 << 2) - *pp];
+	    }
+/* Computing MIN */
+	    d__1 = *dmin2, d__2 = z__[(*n0 << 2) + *pp - 1];
+	    *dmin2 = min(d__1,d__2);
+/* Computing MIN */
+	    d__1 = z__[(*n0 << 2) + *pp - 1], d__2 = z__[(*i0 << 2) + *pp - 1]
+		    , d__1 = min(d__1,d__2), d__2 = z__[(*i0 << 2) + *pp + 3];
+	    z__[(*n0 << 2) + *pp - 1] = min(d__1,d__2);
+/* Computing MIN */
+	    d__1 = z__[(*n0 << 2) - *pp], d__2 = z__[(*i0 << 2) - *pp], d__1 =
+		     min(d__1,d__2), d__2 = z__[(*i0 << 2) - *pp + 4];
+	    z__[(*n0 << 2) - *pp] = min(d__1,d__2);
+/* Computing MAX */
+	    d__1 = *qmax, d__2 = z__[(*i0 << 2) + *pp - 3], d__1 = max(d__1,
+		    d__2), d__2 = z__[(*i0 << 2) + *pp + 1];
+	    *qmax = max(d__1,d__2);
+	    *dmin__ = -0.;
+	}
+    }
+
+/*     Choose a shift. */
+
+    igraphdlasq4_(i0, n0, &z__[1], pp, &n0in, dmin__, dmin1, dmin2, dn, dn1, dn2, 
+	    tau, ttype, g);
+
+/*     Call dqds until DMIN > 0. */
+
+L70:
+
+    igraphdlasq5_(i0, n0, &z__[1], pp, tau, sigma, dmin__, dmin1, dmin2, dn, dn1, 
+	    dn2, ieee, &eps);
+
+    *ndiv += *n0 - *i0 + 2;
+    ++(*iter);
+
+/*     Check status. */
+
+    if (*dmin__ >= 0. && *dmin1 >= 0.) {
+
+/*        Success. */
+
+	goto L90;
+
+    } else if (*dmin__ < 0. && *dmin1 > 0. && z__[(*n0 - 1 << 2) - *pp] < tol 
+	    * (*sigma + *dn1) && abs(*dn) < tol * *sigma) {
+
+/*        Convergence hidden by negative DN. */
+
+	z__[(*n0 - 1 << 2) - *pp + 2] = 0.;
+	*dmin__ = 0.;
+	goto L90;
+    } else if (*dmin__ < 0.) {
+
+/*        TAU too big. Select new TAU and try again. */
+
+	++(*nfail);
+	if (*ttype < -22) {
+
+/*           Failed twice. Play it safe. */
+
+	    *tau = 0.;
+	} else if (*dmin1 > 0.) {
+
+/*           Late failure. Gives excellent shift. */
+
+	    *tau = (*tau + *dmin__) * (1. - eps * 2.);
+	    *ttype += -11;
+	} else {
+
+/*           Early failure. Divide by 4. */
+
+	    *tau *= .25;
+	    *ttype += -12;
+	}
+	goto L70;
+    } else if (igraphdisnan_(dmin__)) {
+
+/*        NaN. */
+
+	if (*tau == 0.) {
+	    goto L80;
+	} else {
+	    *tau = 0.;
+	    goto L70;
+	}
+    } else {
+
+/*        Possible underflow. Play it safe. */
+
+	goto L80;
+    }
+
+/*     Risk of underflow. */
+
+L80:
+    igraphdlasq6_(i0, n0, &z__[1], pp, dmin__, dmin1, dmin2, dn, dn1, dn2);
+    *ndiv += *n0 - *i0 + 2;
+    ++(*iter);
+    *tau = 0.;
+
+L90:
+    if (*tau < *sigma) {
+	*desig += *tau;
+	t = *sigma + *desig;
+	*desig -= t - *sigma;
+    } else {
+	t = *sigma + *tau;
+	*desig = *sigma - (t - *tau) + *desig;
+    }
+    *sigma = t;
+
+    return 0;
+
+/*     End of DLASQ3 */
+
+} /* igraphdlasq3_ */
+
diff --git a/igraph/src/dlasq4.c b/igraph/src/dlasq4.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlasq4.c
@@ -0,0 +1,484 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASQ4 computes an approximation to the smallest eigenvalue using values of d from the previous
+ transform. Used by sbdsqr.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASQ4 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlasq4.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlasq4.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlasq4.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASQ4( I0, N0, Z, PP, N0IN, DMIN, DMIN1, DMIN2, DN,   
+                            DN1, DN2, TAU, TTYPE, G )   
+
+         INTEGER            I0, N0, N0IN, PP, TTYPE   
+         DOUBLE PRECISION   DMIN, DMIN1, DMIN2, DN, DN1, DN2, G, TAU   
+         DOUBLE PRECISION   Z( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASQ4 computes an approximation TAU to the smallest eigenvalue   
+   > using values of d from the previous transform.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] I0   
+   > \verbatim   
+   >          I0 is INTEGER   
+   >        First index.   
+   > \endverbatim   
+   >   
+   > \param[in] N0   
+   > \verbatim   
+   >          N0 is INTEGER   
+   >        Last index.   
+   > \endverbatim   
+   >   
+   > \param[in] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension ( 4*N )   
+   >        Z holds the qd array.   
+   > \endverbatim   
+   >   
+   > \param[in] PP   
+   > \verbatim   
+   >          PP is INTEGER   
+   >        PP=0 for ping, PP=1 for pong.   
+   > \endverbatim   
+   >   
+   > \param[in] N0IN   
+   > \verbatim   
+   >          N0IN is INTEGER   
+   >        The value of N0 at start of EIGTEST.   
+   > \endverbatim   
+   >   
+   > \param[in] DMIN   
+   > \verbatim   
+   >          DMIN is DOUBLE PRECISION   
+   >        Minimum value of d.   
+   > \endverbatim   
+   >   
+   > \param[in] DMIN1   
+   > \verbatim   
+   >          DMIN1 is DOUBLE PRECISION   
+   >        Minimum value of d, excluding D( N0 ).   
+   > \endverbatim   
+   >   
+   > \param[in] DMIN2   
+   > \verbatim   
+   >          DMIN2 is DOUBLE PRECISION   
+   >        Minimum value of d, excluding D( N0 ) and D( N0-1 ).   
+   > \endverbatim   
+   >   
+   > \param[in] DN   
+   > \verbatim   
+   >          DN is DOUBLE PRECISION   
+   >        d(N)   
+   > \endverbatim   
+   >   
+   > \param[in] DN1   
+   > \verbatim   
+   >          DN1 is DOUBLE PRECISION   
+   >        d(N-1)   
+   > \endverbatim   
+   >   
+   > \param[in] DN2   
+   > \verbatim   
+   >          DN2 is DOUBLE PRECISION   
+   >        d(N-2)   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION   
+   >        This is the shift.   
+   > \endverbatim   
+   >   
+   > \param[out] TTYPE   
+   > \verbatim   
+   >          TTYPE is INTEGER   
+   >        Shift type.   
+   > \endverbatim   
+   >   
+   > \param[in,out] G   
+   > \verbatim   
+   >          G is REAL   
+   >        G is passed as an argument in order to save its value between   
+   >        calls to DLASQ4.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  CNST1 = 9/16   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlasq4_(integer *i0, integer *n0, doublereal *z__, 
+	integer *pp, integer *n0in, doublereal *dmin__, doublereal *dmin1, 
+	doublereal *dmin2, doublereal *dn, doublereal *dn1, doublereal *dn2, 
+	doublereal *tau, integer *ttype, doublereal *g)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    doublereal s = 0., a2, b1, b2;
+    integer i4, nn, np;
+    doublereal gam, gap1, gap2;
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       A negative DMIN forces the shift to take that absolute value   
+       TTYPE records the type of shift.   
+
+       Parameter adjustments */
+    --z__;
+
+    /* Function Body */
+    if (*dmin__ <= 0.) {
+	*tau = -(*dmin__);
+	*ttype = -1;
+	return 0;
+    }
+
+    nn = (*n0 << 2) + *pp;
+    if (*n0in == *n0) {
+
+/*        No eigenvalues deflated. */
+
+	if (*dmin__ == *dn || *dmin__ == *dn1) {
+
+	    b1 = sqrt(z__[nn - 3]) * sqrt(z__[nn - 5]);
+	    b2 = sqrt(z__[nn - 7]) * sqrt(z__[nn - 9]);
+	    a2 = z__[nn - 7] + z__[nn - 5];
+
+/*           Cases 2 and 3. */
+
+	    if (*dmin__ == *dn && *dmin1 == *dn1) {
+		gap2 = *dmin2 - a2 - *dmin2 * .25;
+		if (gap2 > 0. && gap2 > b2) {
+		    gap1 = a2 - *dn - b2 / gap2 * b2;
+		} else {
+		    gap1 = a2 - *dn - (b1 + b2);
+		}
+		if (gap1 > 0. && gap1 > b1) {
+/* Computing MAX */
+		    d__1 = *dn - b1 / gap1 * b1, d__2 = *dmin__ * .5;
+		    s = max(d__1,d__2);
+		    *ttype = -2;
+		} else {
+		    s = 0.;
+		    if (*dn > b1) {
+			s = *dn - b1;
+		    }
+		    if (a2 > b1 + b2) {
+/* Computing MIN */
+			d__1 = s, d__2 = a2 - (b1 + b2);
+			s = min(d__1,d__2);
+		    }
+/* Computing MAX */
+		    d__1 = s, d__2 = *dmin__ * .333;
+		    s = max(d__1,d__2);
+		    *ttype = -3;
+		}
+	    } else {
+
+/*              Case 4. */
+
+		*ttype = -4;
+		s = *dmin__ * .25;
+		if (*dmin__ == *dn) {
+		    gam = *dn;
+		    a2 = 0.;
+		    if (z__[nn - 5] > z__[nn - 7]) {
+			return 0;
+		    }
+		    b2 = z__[nn - 5] / z__[nn - 7];
+		    np = nn - 9;
+		} else {
+		    np = nn - (*pp << 1);
+		    b2 = z__[np - 2];
+		    gam = *dn1;
+		    if (z__[np - 4] > z__[np - 2]) {
+			return 0;
+		    }
+		    a2 = z__[np - 4] / z__[np - 2];
+		    if (z__[nn - 9] > z__[nn - 11]) {
+			return 0;
+		    }
+		    b2 = z__[nn - 9] / z__[nn - 11];
+		    np = nn - 13;
+		}
+
+/*              Approximate contribution to norm squared from I < NN-1. */
+
+		a2 += b2;
+		i__1 = (*i0 << 2) - 1 + *pp;
+		for (i4 = np; i4 >= i__1; i4 += -4) {
+		    if (b2 == 0.) {
+			goto L20;
+		    }
+		    b1 = b2;
+		    if (z__[i4] > z__[i4 - 2]) {
+			return 0;
+		    }
+		    b2 *= z__[i4] / z__[i4 - 2];
+		    a2 += b2;
+		    if (max(b2,b1) * 100. < a2 || .563 < a2) {
+			goto L20;
+		    }
+/* L10: */
+		}
+L20:
+		a2 *= 1.05;
+
+/*              Rayleigh quotient residual bound. */
+
+		if (a2 < .563) {
+		    s = gam * (1. - sqrt(a2)) / (a2 + 1.);
+		}
+	    }
+	} else if (*dmin__ == *dn2) {
+
+/*           Case 5. */
+
+	    *ttype = -5;
+	    s = *dmin__ * .25;
+
+/*           Compute contribution to norm squared from I > NN-2. */
+
+	    np = nn - (*pp << 1);
+	    b1 = z__[np - 2];
+	    b2 = z__[np - 6];
+	    gam = *dn2;
+	    if (z__[np - 8] > b2 || z__[np - 4] > b1) {
+		return 0;
+	    }
+	    a2 = z__[np - 8] / b2 * (z__[np - 4] / b1 + 1.);
+
+/*           Approximate contribution to norm squared from I < NN-2. */
+
+	    if (*n0 - *i0 > 2) {
+		b2 = z__[nn - 13] / z__[nn - 15];
+		a2 += b2;
+		i__1 = (*i0 << 2) - 1 + *pp;
+		for (i4 = nn - 17; i4 >= i__1; i4 += -4) {
+		    if (b2 == 0.) {
+			goto L40;
+		    }
+		    b1 = b2;
+		    if (z__[i4] > z__[i4 - 2]) {
+			return 0;
+		    }
+		    b2 *= z__[i4] / z__[i4 - 2];
+		    a2 += b2;
+		    if (max(b2,b1) * 100. < a2 || .563 < a2) {
+			goto L40;
+		    }
+/* L30: */
+		}
+L40:
+		a2 *= 1.05;
+	    }
+
+	    if (a2 < .563) {
+		s = gam * (1. - sqrt(a2)) / (a2 + 1.);
+	    }
+	} else {
+
+/*           Case 6, no information to guide us. */
+
+	    if (*ttype == -6) {
+		*g += (1. - *g) * .333;
+	    } else if (*ttype == -18) {
+		*g = .083250000000000005;
+	    } else {
+		*g = .25;
+	    }
+	    s = *g * *dmin__;
+	    *ttype = -6;
+	}
+
+    } else if (*n0in == *n0 + 1) {
+
+/*        One eigenvalue just deflated. Use DMIN1, DN1 for DMIN and DN. */
+
+	if (*dmin1 == *dn1 && *dmin2 == *dn2) {
+
+/*           Cases 7 and 8. */
+
+	    *ttype = -7;
+	    s = *dmin1 * .333;
+	    if (z__[nn - 5] > z__[nn - 7]) {
+		return 0;
+	    }
+	    b1 = z__[nn - 5] / z__[nn - 7];
+	    b2 = b1;
+	    if (b2 == 0.) {
+		goto L60;
+	    }
+	    i__1 = (*i0 << 2) - 1 + *pp;
+	    for (i4 = (*n0 << 2) - 9 + *pp; i4 >= i__1; i4 += -4) {
+		a2 = b1;
+		if (z__[i4] > z__[i4 - 2]) {
+		    return 0;
+		}
+		b1 *= z__[i4] / z__[i4 - 2];
+		b2 += b1;
+		if (max(b1,a2) * 100. < b2) {
+		    goto L60;
+		}
+/* L50: */
+	    }
+L60:
+	    b2 = sqrt(b2 * 1.05);
+/* Computing 2nd power */
+	    d__1 = b2;
+	    a2 = *dmin1 / (d__1 * d__1 + 1.);
+	    gap2 = *dmin2 * .5 - a2;
+	    if (gap2 > 0. && gap2 > b2 * a2) {
+/* Computing MAX */
+		d__1 = s, d__2 = a2 * (1. - a2 * 1.01 * (b2 / gap2) * b2);
+		s = max(d__1,d__2);
+	    } else {
+/* Computing MAX */
+		d__1 = s, d__2 = a2 * (1. - b2 * 1.01);
+		s = max(d__1,d__2);
+		*ttype = -8;
+	    }
+	} else {
+
+/*           Case 9. */
+
+	    s = *dmin1 * .25;
+	    if (*dmin1 == *dn1) {
+		s = *dmin1 * .5;
+	    }
+	    *ttype = -9;
+	}
+
+    } else if (*n0in == *n0 + 2) {
+
+/*        Two eigenvalues deflated. Use DMIN2, DN2 for DMIN and DN.   
+
+          Cases 10 and 11. */
+
+	if (*dmin2 == *dn2 && z__[nn - 5] * 2. < z__[nn - 7]) {
+	    *ttype = -10;
+	    s = *dmin2 * .333;
+	    if (z__[nn - 5] > z__[nn - 7]) {
+		return 0;
+	    }
+	    b1 = z__[nn - 5] / z__[nn - 7];
+	    b2 = b1;
+	    if (b2 == 0.) {
+		goto L80;
+	    }
+	    i__1 = (*i0 << 2) - 1 + *pp;
+	    for (i4 = (*n0 << 2) - 9 + *pp; i4 >= i__1; i4 += -4) {
+		if (z__[i4] > z__[i4 - 2]) {
+		    return 0;
+		}
+		b1 *= z__[i4] / z__[i4 - 2];
+		b2 += b1;
+		if (b1 * 100. < b2) {
+		    goto L80;
+		}
+/* L70: */
+	    }
+L80:
+	    b2 = sqrt(b2 * 1.05);
+/* Computing 2nd power */
+	    d__1 = b2;
+	    a2 = *dmin2 / (d__1 * d__1 + 1.);
+	    gap2 = z__[nn - 7] + z__[nn - 9] - sqrt(z__[nn - 11]) * sqrt(z__[
+		    nn - 9]) - a2;
+	    if (gap2 > 0. && gap2 > b2 * a2) {
+/* Computing MAX */
+		d__1 = s, d__2 = a2 * (1. - a2 * 1.01 * (b2 / gap2) * b2);
+		s = max(d__1,d__2);
+	    } else {
+/* Computing MAX */
+		d__1 = s, d__2 = a2 * (1. - b2 * 1.01);
+		s = max(d__1,d__2);
+	    }
+	} else {
+	    s = *dmin2 * .25;
+	    *ttype = -11;
+	}
+    } else if (*n0in > *n0 + 2) {
+
+/*        Case 12, more than two eigenvalues deflated. No information. */
+
+	s = 0.;
+	*ttype = -12;
+    }
+
+    *tau = s;
+    return 0;
+
+/*     End of DLASQ4 */
+
+} /* igraphdlasq4_ */
+
diff --git a/igraph/src/dlasq5.c b/igraph/src/dlasq5.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlasq5.c
@@ -0,0 +1,462 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASQ5 computes one dqds transform in ping-pong form. Used by sbdsqr and sstegr.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASQ5 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlasq5.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlasq5.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlasq5.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASQ5( I0, N0, Z, PP, TAU, SIGMA, DMIN, DMIN1, DMIN2, DN,   
+                            DNM1, DNM2, IEEE, EPS )   
+
+         LOGICAL            IEEE   
+         INTEGER            I0, N0, PP   
+         DOUBLE PRECISION   DMIN, DMIN1, DMIN2, DN, DNM1, DNM2, TAU, SIGMA, EPS   
+         DOUBLE PRECISION   Z( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASQ5 computes one dqds transform in ping-pong form, one   
+   > version for IEEE machines another for non IEEE machines.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] I0   
+   > \verbatim   
+   >          I0 is INTEGER   
+   >        First index.   
+   > \endverbatim   
+   >   
+   > \param[in] N0   
+   > \verbatim   
+   >          N0 is INTEGER   
+   >        Last index.   
+   > \endverbatim   
+   >   
+   > \param[in] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension ( 4*N )   
+   >        Z holds the qd array. EMIN is stored in Z(4*N0) to avoid   
+   >        an extra argument.   
+   > \endverbatim   
+   >   
+   > \param[in] PP   
+   > \verbatim   
+   >          PP is INTEGER   
+   >        PP=0 for ping, PP=1 for pong.   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION   
+   >        This is the shift.   
+   > \endverbatim   
+   >   
+   > \param[in] SIGMA   
+   > \verbatim   
+   >          SIGMA is DOUBLE PRECISION   
+   >        This is the accumulated shift up to this step.   
+   > \endverbatim   
+   >   
+   > \param[out] DMIN   
+   > \verbatim   
+   >          DMIN is DOUBLE PRECISION   
+   >        Minimum value of d.   
+   > \endverbatim   
+   >   
+   > \param[out] DMIN1   
+   > \verbatim   
+   >          DMIN1 is DOUBLE PRECISION   
+   >        Minimum value of d, excluding D( N0 ).   
+   > \endverbatim   
+   >   
+   > \param[out] DMIN2   
+   > \verbatim   
+   >          DMIN2 is DOUBLE PRECISION   
+   >        Minimum value of d, excluding D( N0 ) and D( N0-1 ).   
+   > \endverbatim   
+   >   
+   > \param[out] DN   
+   > \verbatim   
+   >          DN is DOUBLE PRECISION   
+   >        d(N0), the last value of d.   
+   > \endverbatim   
+   >   
+   > \param[out] DNM1   
+   > \verbatim   
+   >          DNM1 is DOUBLE PRECISION   
+   >        d(N0-1).   
+   > \endverbatim   
+   >   
+   > \param[out] DNM2   
+   > \verbatim   
+   >          DNM2 is DOUBLE PRECISION   
+   >        d(N0-2).   
+   > \endverbatim   
+   >   
+   > \param[in] IEEE   
+   > \verbatim   
+   >          IEEE is LOGICAL   
+   >        Flag for IEEE or non IEEE arithmetic.   
+   > \endverbatim   
+
+   > \param[in] EPS   
+   > \verbatim   
+   >          EPS is DOUBLE PRECISION   
+   >        This is the value of epsilon used.   
+   > \endverbatim   
+   >   
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdlasq5_(integer *i0, integer *n0, doublereal *z__, 
+	integer *pp, doublereal *tau, doublereal *sigma, doublereal *dmin__, 
+	doublereal *dmin1, doublereal *dmin2, doublereal *dn, doublereal *
+	dnm1, doublereal *dnm2, logical *ieee, doublereal *eps)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    doublereal d__;
+    integer j4, j4p2;
+    doublereal emin, temp, dthresh;
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --z__;
+
+    /* Function Body */
+    if (*n0 - *i0 - 1 <= 0) {
+	return 0;
+    }
+
+    dthresh = *eps * (*sigma + *tau);
+    if (*tau < dthresh * .5) {
+	*tau = 0.;
+    }
+    if (*tau != 0.) {
+	j4 = (*i0 << 2) + *pp - 3;
+	emin = z__[j4 + 4];
+	d__ = z__[j4] - *tau;
+	*dmin__ = d__;
+	*dmin1 = -z__[j4];
+
+	if (*ieee) {
+
+/*        Code for IEEE arithmetic. */
+
+	    if (*pp == 0) {
+		i__1 = *n0 - 3 << 2;
+		for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		    z__[j4 - 2] = d__ + z__[j4 - 1];
+		    temp = z__[j4 + 1] / z__[j4 - 2];
+		    d__ = d__ * temp - *tau;
+		    *dmin__ = min(*dmin__,d__);
+		    z__[j4] = z__[j4 - 1] * temp;
+/* Computing MIN */
+		    d__1 = z__[j4];
+		    emin = min(d__1,emin);
+/* L10: */
+		}
+	    } else {
+		i__1 = *n0 - 3 << 2;
+		for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		    z__[j4 - 3] = d__ + z__[j4];
+		    temp = z__[j4 + 2] / z__[j4 - 3];
+		    d__ = d__ * temp - *tau;
+		    *dmin__ = min(*dmin__,d__);
+		    z__[j4 - 1] = z__[j4] * temp;
+/* Computing MIN */
+		    d__1 = z__[j4 - 1];
+		    emin = min(d__1,emin);
+/* L20: */
+		}
+	    }
+
+/*        Unroll last two steps. */
+
+	    *dnm2 = d__;
+	    *dmin2 = *dmin__;
+	    j4 = (*n0 - 2 << 2) - *pp;
+	    j4p2 = j4 + (*pp << 1) - 1;
+	    z__[j4 - 2] = *dnm2 + z__[j4p2];
+	    z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+	    *dnm1 = z__[j4p2 + 2] * (*dnm2 / z__[j4 - 2]) - *tau;
+	    *dmin__ = min(*dmin__,*dnm1);
+
+	    *dmin1 = *dmin__;
+	    j4 += 4;
+	    j4p2 = j4 + (*pp << 1) - 1;
+	    z__[j4 - 2] = *dnm1 + z__[j4p2];
+	    z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+	    *dn = z__[j4p2 + 2] * (*dnm1 / z__[j4 - 2]) - *tau;
+	    *dmin__ = min(*dmin__,*dn);
+
+	} else {
+
+/*        Code for non IEEE arithmetic. */
+
+	    if (*pp == 0) {
+		i__1 = *n0 - 3 << 2;
+		for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		    z__[j4 - 2] = d__ + z__[j4 - 1];
+		    if (d__ < 0.) {
+			return 0;
+		    } else {
+			z__[j4] = z__[j4 + 1] * (z__[j4 - 1] / z__[j4 - 2]);
+			d__ = z__[j4 + 1] * (d__ / z__[j4 - 2]) - *tau;
+		    }
+		    *dmin__ = min(*dmin__,d__);
+/* Computing MIN */
+		    d__1 = emin, d__2 = z__[j4];
+		    emin = min(d__1,d__2);
+/* L30: */
+		}
+	    } else {
+		i__1 = *n0 - 3 << 2;
+		for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		    z__[j4 - 3] = d__ + z__[j4];
+		    if (d__ < 0.) {
+			return 0;
+		    } else {
+			z__[j4 - 1] = z__[j4 + 2] * (z__[j4] / z__[j4 - 3]);
+			d__ = z__[j4 + 2] * (d__ / z__[j4 - 3]) - *tau;
+		    }
+		    *dmin__ = min(*dmin__,d__);
+/* Computing MIN */
+		    d__1 = emin, d__2 = z__[j4 - 1];
+		    emin = min(d__1,d__2);
+/* L40: */
+		}
+	    }
+
+/*        Unroll last two steps. */
+
+	    *dnm2 = d__;
+	    *dmin2 = *dmin__;
+	    j4 = (*n0 - 2 << 2) - *pp;
+	    j4p2 = j4 + (*pp << 1) - 1;
+	    z__[j4 - 2] = *dnm2 + z__[j4p2];
+	    if (*dnm2 < 0.) {
+		return 0;
+	    } else {
+		z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+		*dnm1 = z__[j4p2 + 2] * (*dnm2 / z__[j4 - 2]) - *tau;
+	    }
+	    *dmin__ = min(*dmin__,*dnm1);
+
+	    *dmin1 = *dmin__;
+	    j4 += 4;
+	    j4p2 = j4 + (*pp << 1) - 1;
+	    z__[j4 - 2] = *dnm1 + z__[j4p2];
+	    if (*dnm1 < 0.) {
+		return 0;
+	    } else {
+		z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+		*dn = z__[j4p2 + 2] * (*dnm1 / z__[j4 - 2]) - *tau;
+	    }
+	    *dmin__ = min(*dmin__,*dn);
+
+	}
+    } else {
+/*     This is the version that sets d's to zero if they are small enough */
+	j4 = (*i0 << 2) + *pp - 3;
+	emin = z__[j4 + 4];
+	d__ = z__[j4] - *tau;
+	*dmin__ = d__;
+	*dmin1 = -z__[j4];
+	if (*ieee) {
+
+/*     Code for IEEE arithmetic. */
+
+	    if (*pp == 0) {
+		i__1 = *n0 - 3 << 2;
+		for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		    z__[j4 - 2] = d__ + z__[j4 - 1];
+		    temp = z__[j4 + 1] / z__[j4 - 2];
+		    d__ = d__ * temp - *tau;
+		    if (d__ < dthresh) {
+			d__ = 0.;
+		    }
+		    *dmin__ = min(*dmin__,d__);
+		    z__[j4] = z__[j4 - 1] * temp;
+/* Computing MIN */
+		    d__1 = z__[j4];
+		    emin = min(d__1,emin);
+/* L50: */
+		}
+	    } else {
+		i__1 = *n0 - 3 << 2;
+		for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		    z__[j4 - 3] = d__ + z__[j4];
+		    temp = z__[j4 + 2] / z__[j4 - 3];
+		    d__ = d__ * temp - *tau;
+		    if (d__ < dthresh) {
+			d__ = 0.;
+		    }
+		    *dmin__ = min(*dmin__,d__);
+		    z__[j4 - 1] = z__[j4] * temp;
+/* Computing MIN */
+		    d__1 = z__[j4 - 1];
+		    emin = min(d__1,emin);
+/* L60: */
+		}
+	    }
+
+/*     Unroll last two steps. */
+
+	    *dnm2 = d__;
+	    *dmin2 = *dmin__;
+	    j4 = (*n0 - 2 << 2) - *pp;
+	    j4p2 = j4 + (*pp << 1) - 1;
+	    z__[j4 - 2] = *dnm2 + z__[j4p2];
+	    z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+	    *dnm1 = z__[j4p2 + 2] * (*dnm2 / z__[j4 - 2]) - *tau;
+	    *dmin__ = min(*dmin__,*dnm1);
+
+	    *dmin1 = *dmin__;
+	    j4 += 4;
+	    j4p2 = j4 + (*pp << 1) - 1;
+	    z__[j4 - 2] = *dnm1 + z__[j4p2];
+	    z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+	    *dn = z__[j4p2 + 2] * (*dnm1 / z__[j4 - 2]) - *tau;
+	    *dmin__ = min(*dmin__,*dn);
+
+	} else {
+
+/*     Code for non IEEE arithmetic. */
+
+	    if (*pp == 0) {
+		i__1 = *n0 - 3 << 2;
+		for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		    z__[j4 - 2] = d__ + z__[j4 - 1];
+		    if (d__ < 0.) {
+			return 0;
+		    } else {
+			z__[j4] = z__[j4 + 1] * (z__[j4 - 1] / z__[j4 - 2]);
+			d__ = z__[j4 + 1] * (d__ / z__[j4 - 2]) - *tau;
+		    }
+		    if (d__ < dthresh) {
+			d__ = 0.;
+		    }
+		    *dmin__ = min(*dmin__,d__);
+/* Computing MIN */
+		    d__1 = emin, d__2 = z__[j4];
+		    emin = min(d__1,d__2);
+/* L70: */
+		}
+	    } else {
+		i__1 = *n0 - 3 << 2;
+		for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+		    z__[j4 - 3] = d__ + z__[j4];
+		    if (d__ < 0.) {
+			return 0;
+		    } else {
+			z__[j4 - 1] = z__[j4 + 2] * (z__[j4] / z__[j4 - 3]);
+			d__ = z__[j4 + 2] * (d__ / z__[j4 - 3]) - *tau;
+		    }
+		    if (d__ < dthresh) {
+			d__ = 0.;
+		    }
+		    *dmin__ = min(*dmin__,d__);
+/* Computing MIN */
+		    d__1 = emin, d__2 = z__[j4 - 1];
+		    emin = min(d__1,d__2);
+/* L80: */
+		}
+	    }
+
+/*     Unroll last two steps. */
+
+	    *dnm2 = d__;
+	    *dmin2 = *dmin__;
+	    j4 = (*n0 - 2 << 2) - *pp;
+	    j4p2 = j4 + (*pp << 1) - 1;
+	    z__[j4 - 2] = *dnm2 + z__[j4p2];
+	    if (*dnm2 < 0.) {
+		return 0;
+	    } else {
+		z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+		*dnm1 = z__[j4p2 + 2] * (*dnm2 / z__[j4 - 2]) - *tau;
+	    }
+	    *dmin__ = min(*dmin__,*dnm1);
+
+	    *dmin1 = *dmin__;
+	    j4 += 4;
+	    j4p2 = j4 + (*pp << 1) - 1;
+	    z__[j4 - 2] = *dnm1 + z__[j4p2];
+	    if (*dnm1 < 0.) {
+		return 0;
+	    } else {
+		z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+		*dn = z__[j4p2 + 2] * (*dnm1 / z__[j4 - 2]) - *tau;
+	    }
+	    *dmin__ = min(*dmin__,*dn);
+
+	}
+    }
+
+    z__[j4 + 2] = *dn;
+    z__[(*n0 << 2) - *pp] = emin;
+    return 0;
+
+/*     End of DLASQ5 */
+
+} /* igraphdlasq5_ */
+
diff --git a/igraph/src/dlasq6.c b/igraph/src/dlasq6.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlasq6.c
@@ -0,0 +1,271 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASQ6 computes one dqd transform in ping-pong form. Used by sbdsqr and sstegr.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASQ6 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlasq6.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlasq6.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlasq6.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASQ6( I0, N0, Z, PP, DMIN, DMIN1, DMIN2, DN,   
+                            DNM1, DNM2 )   
+
+         INTEGER            I0, N0, PP   
+         DOUBLE PRECISION   DMIN, DMIN1, DMIN2, DN, DNM1, DNM2   
+         DOUBLE PRECISION   Z( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASQ6 computes one dqd (shift equal to zero) transform in   
+   > ping-pong form, with protection against underflow and overflow.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] I0   
+   > \verbatim   
+   >          I0 is INTEGER   
+   >        First index.   
+   > \endverbatim   
+   >   
+   > \param[in] N0   
+   > \verbatim   
+   >          N0 is INTEGER   
+   >        Last index.   
+   > \endverbatim   
+   >   
+   > \param[in] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension ( 4*N )   
+   >        Z holds the qd array. EMIN is stored in Z(4*N0) to avoid   
+   >        an extra argument.   
+   > \endverbatim   
+   >   
+   > \param[in] PP   
+   > \verbatim   
+   >          PP is INTEGER   
+   >        PP=0 for ping, PP=1 for pong.   
+   > \endverbatim   
+   >   
+   > \param[out] DMIN   
+   > \verbatim   
+   >          DMIN is DOUBLE PRECISION   
+   >        Minimum value of d.   
+   > \endverbatim   
+   >   
+   > \param[out] DMIN1   
+   > \verbatim   
+   >          DMIN1 is DOUBLE PRECISION   
+   >        Minimum value of d, excluding D( N0 ).   
+   > \endverbatim   
+   >   
+   > \param[out] DMIN2   
+   > \verbatim   
+   >          DMIN2 is DOUBLE PRECISION   
+   >        Minimum value of d, excluding D( N0 ) and D( N0-1 ).   
+   > \endverbatim   
+   >   
+   > \param[out] DN   
+   > \verbatim   
+   >          DN is DOUBLE PRECISION   
+   >        d(N0), the last value of d.   
+   > \endverbatim   
+   >   
+   > \param[out] DNM1   
+   > \verbatim   
+   >          DNM1 is DOUBLE PRECISION   
+   >        d(N0-1).   
+   > \endverbatim   
+   >   
+   > \param[out] DNM2   
+   > \verbatim   
+   >          DNM2 is DOUBLE PRECISION   
+   >        d(N0-2).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdlasq6_(integer *i0, integer *n0, doublereal *z__, 
+	integer *pp, doublereal *dmin__, doublereal *dmin1, doublereal *dmin2,
+	 doublereal *dn, doublereal *dnm1, doublereal *dnm2)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    doublereal d__;
+    integer j4, j4p2;
+    doublereal emin, temp;
+    extern doublereal igraphdlamch_(char *);
+    doublereal safmin;
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --z__;
+
+    /* Function Body */
+    if (*n0 - *i0 - 1 <= 0) {
+	return 0;
+    }
+
+    safmin = igraphdlamch_("Safe minimum");
+    j4 = (*i0 << 2) + *pp - 3;
+    emin = z__[j4 + 4];
+    d__ = z__[j4];
+    *dmin__ = d__;
+
+    if (*pp == 0) {
+	i__1 = *n0 - 3 << 2;
+	for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+	    z__[j4 - 2] = d__ + z__[j4 - 1];
+	    if (z__[j4 - 2] == 0.) {
+		z__[j4] = 0.;
+		d__ = z__[j4 + 1];
+		*dmin__ = d__;
+		emin = 0.;
+	    } else if (safmin * z__[j4 + 1] < z__[j4 - 2] && safmin * z__[j4 
+		    - 2] < z__[j4 + 1]) {
+		temp = z__[j4 + 1] / z__[j4 - 2];
+		z__[j4] = z__[j4 - 1] * temp;
+		d__ *= temp;
+	    } else {
+		z__[j4] = z__[j4 + 1] * (z__[j4 - 1] / z__[j4 - 2]);
+		d__ = z__[j4 + 1] * (d__ / z__[j4 - 2]);
+	    }
+	    *dmin__ = min(*dmin__,d__);
+/* Computing MIN */
+	    d__1 = emin, d__2 = z__[j4];
+	    emin = min(d__1,d__2);
+/* L10: */
+	}
+    } else {
+	i__1 = *n0 - 3 << 2;
+	for (j4 = *i0 << 2; j4 <= i__1; j4 += 4) {
+	    z__[j4 - 3] = d__ + z__[j4];
+	    if (z__[j4 - 3] == 0.) {
+		z__[j4 - 1] = 0.;
+		d__ = z__[j4 + 2];
+		*dmin__ = d__;
+		emin = 0.;
+	    } else if (safmin * z__[j4 + 2] < z__[j4 - 3] && safmin * z__[j4 
+		    - 3] < z__[j4 + 2]) {
+		temp = z__[j4 + 2] / z__[j4 - 3];
+		z__[j4 - 1] = z__[j4] * temp;
+		d__ *= temp;
+	    } else {
+		z__[j4 - 1] = z__[j4 + 2] * (z__[j4] / z__[j4 - 3]);
+		d__ = z__[j4 + 2] * (d__ / z__[j4 - 3]);
+	    }
+	    *dmin__ = min(*dmin__,d__);
+/* Computing MIN */
+	    d__1 = emin, d__2 = z__[j4 - 1];
+	    emin = min(d__1,d__2);
+/* L20: */
+	}
+    }
+
+/*     Unroll last two steps. */
+
+    *dnm2 = d__;
+    *dmin2 = *dmin__;
+    j4 = (*n0 - 2 << 2) - *pp;
+    j4p2 = j4 + (*pp << 1) - 1;
+    z__[j4 - 2] = *dnm2 + z__[j4p2];
+    if (z__[j4 - 2] == 0.) {
+	z__[j4] = 0.;
+	*dnm1 = z__[j4p2 + 2];
+	*dmin__ = *dnm1;
+	emin = 0.;
+    } else if (safmin * z__[j4p2 + 2] < z__[j4 - 2] && safmin * z__[j4 - 2] < 
+	    z__[j4p2 + 2]) {
+	temp = z__[j4p2 + 2] / z__[j4 - 2];
+	z__[j4] = z__[j4p2] * temp;
+	*dnm1 = *dnm2 * temp;
+    } else {
+	z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+	*dnm1 = z__[j4p2 + 2] * (*dnm2 / z__[j4 - 2]);
+    }
+    *dmin__ = min(*dmin__,*dnm1);
+
+    *dmin1 = *dmin__;
+    j4 += 4;
+    j4p2 = j4 + (*pp << 1) - 1;
+    z__[j4 - 2] = *dnm1 + z__[j4p2];
+    if (z__[j4 - 2] == 0.) {
+	z__[j4] = 0.;
+	*dn = z__[j4p2 + 2];
+	*dmin__ = *dn;
+	emin = 0.;
+    } else if (safmin * z__[j4p2 + 2] < z__[j4 - 2] && safmin * z__[j4 - 2] < 
+	    z__[j4p2 + 2]) {
+	temp = z__[j4p2 + 2] / z__[j4 - 2];
+	z__[j4] = z__[j4p2] * temp;
+	*dn = *dnm1 * temp;
+    } else {
+	z__[j4] = z__[j4p2 + 2] * (z__[j4p2] / z__[j4 - 2]);
+	*dn = z__[j4p2 + 2] * (*dnm1 / z__[j4 - 2]);
+    }
+    *dmin__ = min(*dmin__,*dn);
+
+    z__[j4 + 2] = *dn;
+    z__[(*n0 << 2) - *pp] = emin;
+    return 0;
+
+/*     End of DLASQ6 */
+
+} /* igraphdlasq6_ */
+
diff --git a/igraph/src/dlasr.c b/igraph/src/dlasr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlasr.c
@@ -0,0 +1,512 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASR applies a sequence of plane rotations to a general rectangular matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlasr.f
+">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlasr.f
+">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlasr.f
+">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASR( SIDE, PIVOT, DIRECT, M, N, C, S, A, LDA )   
+
+         CHARACTER          DIRECT, PIVOT, SIDE   
+         INTEGER            LDA, M, N   
+         DOUBLE PRECISION   A( LDA, * ), C( * ), S( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASR applies a sequence of plane rotations to a real matrix A,   
+   > from either the left or the right.   
+   >   
+   > When SIDE = 'L', the transformation takes the form   
+   >   
+   >    A := P*A   
+   >   
+   > and when SIDE = 'R', the transformation takes the form   
+   >   
+   >    A := A*P**T   
+   >   
+   > where P is an orthogonal matrix consisting of a sequence of z plane   
+   > rotations, with z = M when SIDE = 'L' and z = N when SIDE = 'R',   
+   > and P**T is the transpose of P.   
+   >   
+   > When DIRECT = 'F' (Forward sequence), then   
+   >   
+   >    P = P(z-1) * ... * P(2) * P(1)   
+   >   
+   > and when DIRECT = 'B' (Backward sequence), then   
+   >   
+   >    P = P(1) * P(2) * ... * P(z-1)   
+   >   
+   > where P(k) is a plane rotation matrix defined by the 2-by-2 rotation   
+   >   
+   >    R(k) = (  c(k)  s(k) )   
+   >         = ( -s(k)  c(k) ).   
+   >   
+   > When PIVOT = 'V' (Variable pivot), the rotation is performed   
+   > for the plane (k,k+1), i.e., P(k) has the form   
+   >   
+   >    P(k) = (  1                                            )   
+   >           (       ...                                     )   
+   >           (              1                                )   
+   >           (                   c(k)  s(k)                  )   
+   >           (                  -s(k)  c(k)                  )   
+   >           (                                1              )   
+   >           (                                     ...       )   
+   >           (                                            1  )   
+   >   
+   > where R(k) appears as a rank-2 modification to the identity matrix in   
+   > rows and columns k and k+1.   
+   >   
+   > When PIVOT = 'T' (Top pivot), the rotation is performed for the   
+   > plane (1,k+1), so P(k) has the form   
+   >   
+   >    P(k) = (  c(k)                    s(k)                 )   
+   >           (         1                                     )   
+   >           (              ...                              )   
+   >           (                     1                         )   
+   >           ( -s(k)                    c(k)                 )   
+   >           (                                 1             )   
+   >           (                                      ...      )   
+   >           (                                             1 )   
+   >   
+   > where R(k) appears in rows and columns 1 and k+1.   
+   >   
+   > Similarly, when PIVOT = 'B' (Bottom pivot), the rotation is   
+   > performed for the plane (k,z), giving P(k) the form   
+   >   
+   >    P(k) = ( 1                                             )   
+   >           (      ...                                      )   
+   >           (             1                                 )   
+   >           (                  c(k)                    s(k) )   
+   >           (                         1                     )   
+   >           (                              ...              )   
+   >           (                                     1         )   
+   >           (                 -s(k)                    c(k) )   
+   >   
+   > where R(k) appears in rows and columns k and z.  The rotations are   
+   > performed without ever forming P(k) explicitly.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          Specifies whether the plane rotation matrix P is applied to   
+   >          A on the left or the right.   
+   >          = 'L':  Left, compute A := P*A   
+   >          = 'R':  Right, compute A:= A*P**T   
+   > \endverbatim   
+   >   
+   > \param[in] PIVOT   
+   > \verbatim   
+   >          PIVOT is CHARACTER*1   
+   >          Specifies the plane for which P(k) is a plane rotation   
+   >          matrix.   
+   >          = 'V':  Variable pivot, the plane (k,k+1)   
+   >          = 'T':  Top pivot, the plane (1,k+1)   
+   >          = 'B':  Bottom pivot, the plane (k,z)   
+   > \endverbatim   
+   >   
+   > \param[in] DIRECT   
+   > \verbatim   
+   >          DIRECT is CHARACTER*1   
+   >          Specifies whether P is a forward or backward sequence of   
+   >          plane rotations.   
+   >          = 'F':  Forward, P = P(z-1)*...*P(2)*P(1)   
+   >          = 'B':  Backward, P = P(1)*P(2)*...*P(z-1)   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.  If m <= 1, an immediate   
+   >          return is effected.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.  If n <= 1, an   
+   >          immediate return is effected.   
+   > \endverbatim   
+   >   
+   > \param[in] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension   
+   >                  (M-1) if SIDE = 'L'   
+   >                  (N-1) if SIDE = 'R'   
+   >          The cosines c(k) of the plane rotations.   
+   > \endverbatim   
+   >   
+   > \param[in] S   
+   > \verbatim   
+   >          S is DOUBLE PRECISION array, dimension   
+   >                  (M-1) if SIDE = 'L'   
+   >                  (N-1) if SIDE = 'R'   
+   >          The sines s(k) of the plane rotations.  The 2-by-2 plane   
+   >          rotation part of the matrix P(k), R(k), has the form   
+   >          R(k) = (  c(k)  s(k) )   
+   >                 ( -s(k)  c(k) ).   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The M-by-N matrix A.  On exit, A is overwritten by P*A if   
+   >          SIDE = 'R' or by A*P**T if SIDE = 'L'.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,M).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlasr_(char *side, char *pivot, char *direct, integer *m,
+	 integer *n, doublereal *c__, doublereal *s, doublereal *a, integer *
+	lda)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j, info;
+    doublereal temp;
+    extern logical igraphlsame_(char *, char *);
+    doublereal ctemp, stemp;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input parameters   
+
+       Parameter adjustments */
+    --c__;
+    --s;
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    info = 0;
+    if (! (igraphlsame_(side, "L") || igraphlsame_(side, "R"))) {
+	info = 1;
+    } else if (! (igraphlsame_(pivot, "V") || igraphlsame_(pivot, 
+	    "T") || igraphlsame_(pivot, "B"))) {
+	info = 2;
+    } else if (! (igraphlsame_(direct, "F") || igraphlsame_(direct, 
+	    "B"))) {
+	info = 3;
+    } else if (*m < 0) {
+	info = 4;
+    } else if (*n < 0) {
+	info = 5;
+    } else if (*lda < max(1,*m)) {
+	info = 9;
+    }
+    if (info != 0) {
+	igraphxerbla_("DLASR ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0) {
+	return 0;
+    }
+    if (igraphlsame_(side, "L")) {
+
+/*        Form  P * A */
+
+	if (igraphlsame_(pivot, "V")) {
+	    if (igraphlsame_(direct, "F")) {
+		i__1 = *m - 1;
+		for (j = 1; j <= i__1; ++j) {
+		    ctemp = c__[j];
+		    stemp = s[j];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__2 = *n;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    temp = a[j + 1 + i__ * a_dim1];
+			    a[j + 1 + i__ * a_dim1] = ctemp * temp - stemp * 
+				    a[j + i__ * a_dim1];
+			    a[j + i__ * a_dim1] = stemp * temp + ctemp * a[j 
+				    + i__ * a_dim1];
+/* L10: */
+			}
+		    }
+/* L20: */
+		}
+	    } else if (igraphlsame_(direct, "B")) {
+		for (j = *m - 1; j >= 1; --j) {
+		    ctemp = c__[j];
+		    stemp = s[j];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__1 = *n;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    temp = a[j + 1 + i__ * a_dim1];
+			    a[j + 1 + i__ * a_dim1] = ctemp * temp - stemp * 
+				    a[j + i__ * a_dim1];
+			    a[j + i__ * a_dim1] = stemp * temp + ctemp * a[j 
+				    + i__ * a_dim1];
+/* L30: */
+			}
+		    }
+/* L40: */
+		}
+	    }
+	} else if (igraphlsame_(pivot, "T")) {
+	    if (igraphlsame_(direct, "F")) {
+		i__1 = *m;
+		for (j = 2; j <= i__1; ++j) {
+		    ctemp = c__[j - 1];
+		    stemp = s[j - 1];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__2 = *n;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    temp = a[j + i__ * a_dim1];
+			    a[j + i__ * a_dim1] = ctemp * temp - stemp * a[
+				    i__ * a_dim1 + 1];
+			    a[i__ * a_dim1 + 1] = stemp * temp + ctemp * a[
+				    i__ * a_dim1 + 1];
+/* L50: */
+			}
+		    }
+/* L60: */
+		}
+	    } else if (igraphlsame_(direct, "B")) {
+		for (j = *m; j >= 2; --j) {
+		    ctemp = c__[j - 1];
+		    stemp = s[j - 1];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__1 = *n;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    temp = a[j + i__ * a_dim1];
+			    a[j + i__ * a_dim1] = ctemp * temp - stemp * a[
+				    i__ * a_dim1 + 1];
+			    a[i__ * a_dim1 + 1] = stemp * temp + ctemp * a[
+				    i__ * a_dim1 + 1];
+/* L70: */
+			}
+		    }
+/* L80: */
+		}
+	    }
+	} else if (igraphlsame_(pivot, "B")) {
+	    if (igraphlsame_(direct, "F")) {
+		i__1 = *m - 1;
+		for (j = 1; j <= i__1; ++j) {
+		    ctemp = c__[j];
+		    stemp = s[j];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__2 = *n;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    temp = a[j + i__ * a_dim1];
+			    a[j + i__ * a_dim1] = stemp * a[*m + i__ * a_dim1]
+				     + ctemp * temp;
+			    a[*m + i__ * a_dim1] = ctemp * a[*m + i__ * 
+				    a_dim1] - stemp * temp;
+/* L90: */
+			}
+		    }
+/* L100: */
+		}
+	    } else if (igraphlsame_(direct, "B")) {
+		for (j = *m - 1; j >= 1; --j) {
+		    ctemp = c__[j];
+		    stemp = s[j];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__1 = *n;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    temp = a[j + i__ * a_dim1];
+			    a[j + i__ * a_dim1] = stemp * a[*m + i__ * a_dim1]
+				     + ctemp * temp;
+			    a[*m + i__ * a_dim1] = ctemp * a[*m + i__ * 
+				    a_dim1] - stemp * temp;
+/* L110: */
+			}
+		    }
+/* L120: */
+		}
+	    }
+	}
+    } else if (igraphlsame_(side, "R")) {
+
+/*        Form A * P**T */
+
+	if (igraphlsame_(pivot, "V")) {
+	    if (igraphlsame_(direct, "F")) {
+		i__1 = *n - 1;
+		for (j = 1; j <= i__1; ++j) {
+		    ctemp = c__[j];
+		    stemp = s[j];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    temp = a[i__ + (j + 1) * a_dim1];
+			    a[i__ + (j + 1) * a_dim1] = ctemp * temp - stemp *
+				     a[i__ + j * a_dim1];
+			    a[i__ + j * a_dim1] = stemp * temp + ctemp * a[
+				    i__ + j * a_dim1];
+/* L130: */
+			}
+		    }
+/* L140: */
+		}
+	    } else if (igraphlsame_(direct, "B")) {
+		for (j = *n - 1; j >= 1; --j) {
+		    ctemp = c__[j];
+		    stemp = s[j];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__1 = *m;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    temp = a[i__ + (j + 1) * a_dim1];
+			    a[i__ + (j + 1) * a_dim1] = ctemp * temp - stemp *
+				     a[i__ + j * a_dim1];
+			    a[i__ + j * a_dim1] = stemp * temp + ctemp * a[
+				    i__ + j * a_dim1];
+/* L150: */
+			}
+		    }
+/* L160: */
+		}
+	    }
+	} else if (igraphlsame_(pivot, "T")) {
+	    if (igraphlsame_(direct, "F")) {
+		i__1 = *n;
+		for (j = 2; j <= i__1; ++j) {
+		    ctemp = c__[j - 1];
+		    stemp = s[j - 1];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    temp = a[i__ + j * a_dim1];
+			    a[i__ + j * a_dim1] = ctemp * temp - stemp * a[
+				    i__ + a_dim1];
+			    a[i__ + a_dim1] = stemp * temp + ctemp * a[i__ + 
+				    a_dim1];
+/* L170: */
+			}
+		    }
+/* L180: */
+		}
+	    } else if (igraphlsame_(direct, "B")) {
+		for (j = *n; j >= 2; --j) {
+		    ctemp = c__[j - 1];
+		    stemp = s[j - 1];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__1 = *m;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    temp = a[i__ + j * a_dim1];
+			    a[i__ + j * a_dim1] = ctemp * temp - stemp * a[
+				    i__ + a_dim1];
+			    a[i__ + a_dim1] = stemp * temp + ctemp * a[i__ + 
+				    a_dim1];
+/* L190: */
+			}
+		    }
+/* L200: */
+		}
+	    }
+	} else if (igraphlsame_(pivot, "B")) {
+	    if (igraphlsame_(direct, "F")) {
+		i__1 = *n - 1;
+		for (j = 1; j <= i__1; ++j) {
+		    ctemp = c__[j];
+		    stemp = s[j];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    temp = a[i__ + j * a_dim1];
+			    a[i__ + j * a_dim1] = stemp * a[i__ + *n * a_dim1]
+				     + ctemp * temp;
+			    a[i__ + *n * a_dim1] = ctemp * a[i__ + *n * 
+				    a_dim1] - stemp * temp;
+/* L210: */
+			}
+		    }
+/* L220: */
+		}
+	    } else if (igraphlsame_(direct, "B")) {
+		for (j = *n - 1; j >= 1; --j) {
+		    ctemp = c__[j];
+		    stemp = s[j];
+		    if (ctemp != 1. || stemp != 0.) {
+			i__1 = *m;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    temp = a[i__ + j * a_dim1];
+			    a[i__ + j * a_dim1] = stemp * a[i__ + *n * a_dim1]
+				     + ctemp * temp;
+			    a[i__ + *n * a_dim1] = ctemp * a[i__ + *n * 
+				    a_dim1] - stemp * temp;
+/* L230: */
+			}
+		    }
+/* L240: */
+		}
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DLASR */
+
+} /* igraphdlasr_ */
+
diff --git a/igraph/src/dlasrt.c b/igraph/src/dlasrt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlasrt.c
@@ -0,0 +1,330 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASRT sorts numbers in increasing or decreasing order.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASRT + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlasrt.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlasrt.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlasrt.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASRT( ID, N, D, INFO )   
+
+         CHARACTER          ID   
+         INTEGER            INFO, N   
+         DOUBLE PRECISION   D( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > Sort the numbers in D in increasing order (if ID = 'I') or   
+   > in decreasing order (if ID = 'D' ).   
+   >   
+   > Use Quick Sort, reverting to Insertion sort on arrays of   
+   > size <= 20. Dimension of STACK limits N to about 2**32.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] ID   
+   > \verbatim   
+   >          ID is CHARACTER*1   
+   >          = 'I': sort D in increasing order;   
+   >          = 'D': sort D in decreasing order.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The length of the array D.   
+   > \endverbatim   
+   >   
+   > \param[in,out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the array to be sorted.   
+   >          On exit, D has been sorted into increasing order   
+   >          (D(1) <= ... <= D(N) ) or into decreasing order   
+   >          (D(1) >= ... >= D(N) ), depending on ID.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdlasrt_(char *id, integer *n, doublereal *d__, integer *
+	info)
+{
+    /* System generated locals */
+    integer i__1, i__2;
+
+    /* Local variables */
+    integer i__, j;
+    doublereal d1, d2, d3;
+    integer dir;
+    doublereal tmp;
+    integer endd;
+    extern logical igraphlsame_(char *, char *);
+    integer stack[64]	/* was [2][32] */;
+    doublereal dmnmx;
+    integer start;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    integer stkpnt;
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input paramters.   
+
+       Parameter adjustments */
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+    dir = -1;
+    if (igraphlsame_(id, "D")) {
+	dir = 0;
+    } else if (igraphlsame_(id, "I")) {
+	dir = 1;
+    }
+    if (dir == -1) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DLASRT", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n <= 1) {
+	return 0;
+    }
+
+    stkpnt = 1;
+    stack[0] = 1;
+    stack[1] = *n;
+L10:
+    start = stack[(stkpnt << 1) - 2];
+    endd = stack[(stkpnt << 1) - 1];
+    --stkpnt;
+    if (endd - start <= 20 && endd - start > 0) {
+
+/*        Do Insertion sort on D( START:ENDD ) */
+
+	if (dir == 0) {
+
+/*           Sort into decreasing order */
+
+	    i__1 = endd;
+	    for (i__ = start + 1; i__ <= i__1; ++i__) {
+		i__2 = start + 1;
+		for (j = i__; j >= i__2; --j) {
+		    if (d__[j] > d__[j - 1]) {
+			dmnmx = d__[j];
+			d__[j] = d__[j - 1];
+			d__[j - 1] = dmnmx;
+		    } else {
+			goto L30;
+		    }
+/* L20: */
+		}
+L30:
+		;
+	    }
+
+	} else {
+
+/*           Sort into increasing order */
+
+	    i__1 = endd;
+	    for (i__ = start + 1; i__ <= i__1; ++i__) {
+		i__2 = start + 1;
+		for (j = i__; j >= i__2; --j) {
+		    if (d__[j] < d__[j - 1]) {
+			dmnmx = d__[j];
+			d__[j] = d__[j - 1];
+			d__[j - 1] = dmnmx;
+		    } else {
+			goto L50;
+		    }
+/* L40: */
+		}
+L50:
+		;
+	    }
+
+	}
+
+    } else if (endd - start > 20) {
+
+/*        Partition D( START:ENDD ) and stack parts, largest one first   
+
+          Choose partition entry as median of 3 */
+
+	d1 = d__[start];
+	d2 = d__[endd];
+	i__ = (start + endd) / 2;
+	d3 = d__[i__];
+	if (d1 < d2) {
+	    if (d3 < d1) {
+		dmnmx = d1;
+	    } else if (d3 < d2) {
+		dmnmx = d3;
+	    } else {
+		dmnmx = d2;
+	    }
+	} else {
+	    if (d3 < d2) {
+		dmnmx = d2;
+	    } else if (d3 < d1) {
+		dmnmx = d3;
+	    } else {
+		dmnmx = d1;
+	    }
+	}
+
+	if (dir == 0) {
+
+/*           Sort into decreasing order */
+
+	    i__ = start - 1;
+	    j = endd + 1;
+L60:
+L70:
+	    --j;
+	    if (d__[j] < dmnmx) {
+		goto L70;
+	    }
+L80:
+	    ++i__;
+	    if (d__[i__] > dmnmx) {
+		goto L80;
+	    }
+	    if (i__ < j) {
+		tmp = d__[i__];
+		d__[i__] = d__[j];
+		d__[j] = tmp;
+		goto L60;
+	    }
+	    if (j - start > endd - j - 1) {
+		++stkpnt;
+		stack[(stkpnt << 1) - 2] = start;
+		stack[(stkpnt << 1) - 1] = j;
+		++stkpnt;
+		stack[(stkpnt << 1) - 2] = j + 1;
+		stack[(stkpnt << 1) - 1] = endd;
+	    } else {
+		++stkpnt;
+		stack[(stkpnt << 1) - 2] = j + 1;
+		stack[(stkpnt << 1) - 1] = endd;
+		++stkpnt;
+		stack[(stkpnt << 1) - 2] = start;
+		stack[(stkpnt << 1) - 1] = j;
+	    }
+	} else {
+
+/*           Sort into increasing order */
+
+	    i__ = start - 1;
+	    j = endd + 1;
+L90:
+L100:
+	    --j;
+	    if (d__[j] > dmnmx) {
+		goto L100;
+	    }
+L110:
+	    ++i__;
+	    if (d__[i__] < dmnmx) {
+		goto L110;
+	    }
+	    if (i__ < j) {
+		tmp = d__[i__];
+		d__[i__] = d__[j];
+		d__[j] = tmp;
+		goto L90;
+	    }
+	    if (j - start > endd - j - 1) {
+		++stkpnt;
+		stack[(stkpnt << 1) - 2] = start;
+		stack[(stkpnt << 1) - 1] = j;
+		++stkpnt;
+		stack[(stkpnt << 1) - 2] = j + 1;
+		stack[(stkpnt << 1) - 1] = endd;
+	    } else {
+		++stkpnt;
+		stack[(stkpnt << 1) - 2] = j + 1;
+		stack[(stkpnt << 1) - 1] = endd;
+		++stkpnt;
+		stack[(stkpnt << 1) - 2] = start;
+		stack[(stkpnt << 1) - 1] = j;
+	    }
+	}
+    }
+    if (stkpnt > 0) {
+	goto L10;
+    }
+    return 0;
+
+/*     End of DLASRT */
+
+} /* igraphdlasrt_ */
+
diff --git a/igraph/src/dlassq.c b/igraph/src/dlassq.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlassq.c
@@ -0,0 +1,168 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASSQ updates a sum of squares represented in scaled form.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASSQ + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlassq.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlassq.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlassq.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASSQ( N, X, INCX, SCALE, SUMSQ )   
+
+         INTEGER            INCX, N   
+         DOUBLE PRECISION   SCALE, SUMSQ   
+         DOUBLE PRECISION   X( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASSQ  returns the values  scl  and  smsq  such that   
+   >   
+   >    ( scl**2 )*smsq = x( 1 )**2 +...+ x( n )**2 + ( scale**2 )*sumsq,   
+   >   
+   > where  x( i ) = X( 1 + ( i - 1 )*INCX ). The value of  sumsq  is   
+   > assumed to be non-negative and  scl  returns the value   
+   >   
+   >    scl = max( scale, abs( x( i ) ) ).   
+   >   
+   > scale and sumsq must be supplied in SCALE and SUMSQ and   
+   > scl and smsq are overwritten on SCALE and SUMSQ respectively.   
+   >   
+   > The routine makes only one pass through the vector x.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of elements to be used from the vector X.   
+   > \endverbatim   
+   >   
+   > \param[in] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION array, dimension (N)   
+   >          The vector for which a scaled sum of squares is computed.   
+   >             x( i )  = X( 1 + ( i - 1 )*INCX ), 1 <= i <= n.   
+   > \endverbatim   
+   >   
+   > \param[in] INCX   
+   > \verbatim   
+   >          INCX is INTEGER   
+   >          The increment between successive values of the vector X.   
+   >          INCX > 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] SCALE   
+   > \verbatim   
+   >          SCALE is DOUBLE PRECISION   
+   >          On entry, the value  scale  in the equation above.   
+   >          On exit, SCALE is overwritten with  scl , the scaling factor   
+   >          for the sum of squares.   
+   > \endverbatim   
+   >   
+   > \param[in,out] SUMSQ   
+   > \verbatim   
+   >          SUMSQ is DOUBLE PRECISION   
+   >          On entry, the value  sumsq  in the equation above.   
+   >          On exit, SUMSQ is overwritten with  smsq , the basic sum of   
+   >          squares from which  scl  has been factored out.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlassq_(integer *n, doublereal *x, integer *incx, 
+	doublereal *scale, doublereal *sumsq)
+{
+    /* System generated locals */
+    integer i__1, i__2;
+    doublereal d__1;
+
+    /* Local variables */
+    integer ix;
+    doublereal absxi;
+    extern logical igraphdisnan_(doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Parameter adjustments */
+    --x;
+
+    /* Function Body */
+    if (*n > 0) {
+	i__1 = (*n - 1) * *incx + 1;
+	i__2 = *incx;
+	for (ix = 1; i__2 < 0 ? ix >= i__1 : ix <= i__1; ix += i__2) {
+	    absxi = (d__1 = x[ix], abs(d__1));
+	    if (absxi > 0. || igraphdisnan_(&absxi)) {
+		if (*scale < absxi) {
+/* Computing 2nd power */
+		    d__1 = *scale / absxi;
+		    *sumsq = *sumsq * (d__1 * d__1) + 1;
+		    *scale = absxi;
+		} else {
+/* Computing 2nd power */
+		    d__1 = absxi / *scale;
+		    *sumsq += d__1 * d__1;
+		}
+	    }
+/* L10: */
+	}
+    }
+    return 0;
+
+/*     End of DLASSQ */
+
+} /* igraphdlassq_ */
+
diff --git a/igraph/src/dlaswp.c b/igraph/src/dlaswp.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlaswp.c
@@ -0,0 +1,222 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b DLASWP performs a series of row interchanges on a general rectangular matrix.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASWP + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlaswp.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlaswp.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlaswp.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASWP( N, A, LDA, K1, K2, IPIV, INCX )   
+
+         INTEGER            INCX, K1, K2, LDA, N   
+         INTEGER            IPIV( * )   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASWP performs a series of row interchanges on the matrix A.   
+   > One row interchange is initiated for each of rows K1 through K2 of A.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the matrix of column dimension N to which the row   
+   >          interchanges will be applied.   
+   >          On exit, the permuted matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.   
+   > \endverbatim   
+   >   
+   > \param[in] K1   
+   > \verbatim   
+   >          K1 is INTEGER   
+   >          The first element of IPIV for which a row interchange will   
+   >          be done.   
+   > \endverbatim   
+   >   
+   > \param[in] K2   
+   > \verbatim   
+   >          K2 is INTEGER   
+   >          The last element of IPIV for which a row interchange will   
+   >          be done.   
+   > \endverbatim   
+   >   
+   > \param[in] IPIV   
+   > \verbatim   
+   >          IPIV is INTEGER array, dimension (K2*abs(INCX))   
+   >          The vector of pivot indices.  Only the elements in positions   
+   >          K1 through K2 of IPIV are accessed.   
+   >          IPIV(K) = L implies rows K and L are to be interchanged.   
+   > \endverbatim   
+   >   
+   > \param[in] INCX   
+   > \verbatim   
+   >          INCX is INTEGER   
+   >          The increment between successive values of IPIV.  If IPIV   
+   >          is negative, the pivots are applied in reverse order.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  Modified by   
+   >   R. C. Whaley, Computer Science Dept., Univ. of Tenn., Knoxville, USA   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlaswp_(integer *n, doublereal *a, integer *lda, integer 
+	*k1, integer *k2, integer *ipiv, integer *incx)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3, i__4;
+
+    /* Local variables */
+    integer i__, j, k, i1, i2, n32, ip, ix, ix0, inc;
+    doublereal temp;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Interchange row I with row IPIV(I) for each of rows K1 through K2.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --ipiv;
+
+    /* Function Body */
+    if (*incx > 0) {
+	ix0 = *k1;
+	i1 = *k1;
+	i2 = *k2;
+	inc = 1;
+    } else if (*incx < 0) {
+	ix0 = (1 - *k2) * *incx + 1;
+	i1 = *k2;
+	i2 = *k1;
+	inc = -1;
+    } else {
+	return 0;
+    }
+
+    n32 = *n / 32 << 5;
+    if (n32 != 0) {
+	i__1 = n32;
+	for (j = 1; j <= i__1; j += 32) {
+	    ix = ix0;
+	    i__2 = i2;
+	    i__3 = inc;
+	    for (i__ = i1; i__3 < 0 ? i__ >= i__2 : i__ <= i__2; i__ += i__3) 
+		    {
+		ip = ipiv[ix];
+		if (ip != i__) {
+		    i__4 = j + 31;
+		    for (k = j; k <= i__4; ++k) {
+			temp = a[i__ + k * a_dim1];
+			a[i__ + k * a_dim1] = a[ip + k * a_dim1];
+			a[ip + k * a_dim1] = temp;
+/* L10: */
+		    }
+		}
+		ix += *incx;
+/* L20: */
+	    }
+/* L30: */
+	}
+    }
+    if (n32 != *n) {
+	++n32;
+	ix = ix0;
+	i__1 = i2;
+	i__3 = inc;
+	for (i__ = i1; i__3 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__3) {
+	    ip = ipiv[ix];
+	    if (ip != i__) {
+		i__2 = *n;
+		for (k = n32; k <= i__2; ++k) {
+		    temp = a[i__ + k * a_dim1];
+		    a[i__ + k * a_dim1] = a[ip + k * a_dim1];
+		    a[ip + k * a_dim1] = temp;
+/* L40: */
+		}
+	    }
+	    ix += *incx;
+/* L50: */
+	}
+    }
+
+    return 0;
+
+/*     End of DLASWP */
+
+} /* igraphdlaswp_ */
+
diff --git a/igraph/src/dlasy2.c b/igraph/src/dlasy2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlasy2.c
@@ -0,0 +1,557 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__4 = 4;
+static integer c__1 = 1;
+static integer c__16 = 16;
+static integer c__0 = 0;
+
+/* > \brief \b DLASY2 solves the Sylvester matrix equation where the matrices are of order 1 or 2.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLASY2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlasy2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlasy2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlasy2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLASY2( LTRANL, LTRANR, ISGN, N1, N2, TL, LDTL, TR,   
+                            LDTR, B, LDB, SCALE, X, LDX, XNORM, INFO )   
+
+         LOGICAL            LTRANL, LTRANR   
+         INTEGER            INFO, ISGN, LDB, LDTL, LDTR, LDX, N1, N2   
+         DOUBLE PRECISION   SCALE, XNORM   
+         DOUBLE PRECISION   B( LDB, * ), TL( LDTL, * ), TR( LDTR, * ),   
+        $                   X( LDX, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLASY2 solves for the N1 by N2 matrix X, 1 <= N1,N2 <= 2, in   
+   >   
+   >        op(TL)*X + ISGN*X*op(TR) = SCALE*B,   
+   >   
+   > where TL is N1 by N1, TR is N2 by N2, B is N1 by N2, and ISGN = 1 or   
+   > -1.  op(T) = T or T**T, where T**T denotes the transpose of T.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] LTRANL   
+   > \verbatim   
+   >          LTRANL is LOGICAL   
+   >          On entry, LTRANL specifies the op(TL):   
+   >             = .FALSE., op(TL) = TL,   
+   >             = .TRUE., op(TL) = TL**T.   
+   > \endverbatim   
+   >   
+   > \param[in] LTRANR   
+   > \verbatim   
+   >          LTRANR is LOGICAL   
+   >          On entry, LTRANR specifies the op(TR):   
+   >            = .FALSE., op(TR) = TR,   
+   >            = .TRUE., op(TR) = TR**T.   
+   > \endverbatim   
+   >   
+   > \param[in] ISGN   
+   > \verbatim   
+   >          ISGN is INTEGER   
+   >          On entry, ISGN specifies the sign of the equation   
+   >          as described before. ISGN may only be 1 or -1.   
+   > \endverbatim   
+   >   
+   > \param[in] N1   
+   > \verbatim   
+   >          N1 is INTEGER   
+   >          On entry, N1 specifies the order of matrix TL.   
+   >          N1 may only be 0, 1 or 2.   
+   > \endverbatim   
+   >   
+   > \param[in] N2   
+   > \verbatim   
+   >          N2 is INTEGER   
+   >          On entry, N2 specifies the order of matrix TR.   
+   >          N2 may only be 0, 1 or 2.   
+   > \endverbatim   
+   >   
+   > \param[in] TL   
+   > \verbatim   
+   >          TL is DOUBLE PRECISION array, dimension (LDTL,2)   
+   >          On entry, TL contains an N1 by N1 matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] LDTL   
+   > \verbatim   
+   >          LDTL is INTEGER   
+   >          The leading dimension of the matrix TL. LDTL >= max(1,N1).   
+   > \endverbatim   
+   >   
+   > \param[in] TR   
+   > \verbatim   
+   >          TR is DOUBLE PRECISION array, dimension (LDTR,2)   
+   >          On entry, TR contains an N2 by N2 matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] LDTR   
+   > \verbatim   
+   >          LDTR is INTEGER   
+   >          The leading dimension of the matrix TR. LDTR >= max(1,N2).   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (LDB,2)   
+   >          On entry, the N1 by N2 matrix B contains the right-hand   
+   >          side of the equation.   
+   > \endverbatim   
+   >   
+   > \param[in] LDB   
+   > \verbatim   
+   >          LDB is INTEGER   
+   >          The leading dimension of the matrix B. LDB >= max(1,N1).   
+   > \endverbatim   
+   >   
+   > \param[out] SCALE   
+   > \verbatim   
+   >          SCALE is DOUBLE PRECISION   
+   >          On exit, SCALE contains the scale factor. SCALE is chosen   
+   >          less than or equal to 1 to prevent the solution overflowing.   
+   > \endverbatim   
+   >   
+   > \param[out] X   
+   > \verbatim   
+   >          X is DOUBLE PRECISION array, dimension (LDX,2)   
+   >          On exit, X contains the N1 by N2 solution.   
+   > \endverbatim   
+   >   
+   > \param[in] LDX   
+   > \verbatim   
+   >          LDX is INTEGER   
+   >          The leading dimension of the matrix X. LDX >= max(1,N1).   
+   > \endverbatim   
+   >   
+   > \param[out] XNORM   
+   > \verbatim   
+   >          XNORM is DOUBLE PRECISION   
+   >          On exit, XNORM is the infinity-norm of the solution.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          On exit, INFO is set to   
+   >             0: successful exit.   
+   >             1: TL and TR have too close eigenvalues, so TL or   
+   >                TR is perturbed to get a nonsingular equation.   
+   >          NOTE: In the interests of speed, this routine does not   
+   >                check the inputs for errors.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleSYauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphdlasy2_(logical *ltranl, logical *ltranr, integer *isgn, 
+	integer *n1, integer *n2, doublereal *tl, integer *ldtl, doublereal *
+	tr, integer *ldtr, doublereal *b, integer *ldb, doublereal *scale, 
+	doublereal *x, integer *ldx, doublereal *xnorm, integer *info)
+{
+    /* Initialized data */
+
+    static integer locu12[4] = { 3,4,1,2 };
+    static integer locl21[4] = { 2,1,4,3 };
+    static integer locu22[4] = { 4,3,2,1 };
+    static logical xswpiv[4] = { FALSE_,FALSE_,TRUE_,TRUE_ };
+    static logical bswpiv[4] = { FALSE_,TRUE_,FALSE_,TRUE_ };
+
+    /* System generated locals */
+    integer b_dim1, b_offset, tl_dim1, tl_offset, tr_dim1, tr_offset, x_dim1, 
+	    x_offset;
+    doublereal d__1, d__2, d__3, d__4, d__5, d__6, d__7, d__8;
+
+    /* Local variables */
+    integer i__, j, k;
+    doublereal x2[2], l21, u11, u12;
+    integer ip, jp;
+    doublereal u22, t16[16]	/* was [4][4] */, gam, bet, eps, sgn, tmp[4], 
+	    tau1, btmp[4], smin;
+    integer ipiv;
+    doublereal temp;
+    integer jpiv[4];
+    doublereal xmax;
+    integer ipsv, jpsv;
+    logical bswap;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdswap_(integer *, doublereal *, integer 
+	    *, doublereal *, integer *);
+    logical xswap;
+    extern doublereal igraphdlamch_(char *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    doublereal smlnum;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+       Parameter adjustments */
+    tl_dim1 = *ldtl;
+    tl_offset = 1 + tl_dim1;
+    tl -= tl_offset;
+    tr_dim1 = *ldtr;
+    tr_offset = 1 + tr_dim1;
+    tr -= tr_offset;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+    x_dim1 = *ldx;
+    x_offset = 1 + x_dim1;
+    x -= x_offset;
+
+    /* Function Body   
+
+       Do not check the input parameters for errors */
+
+    *info = 0;
+
+/*     Quick return if possible */
+
+    if (*n1 == 0 || *n2 == 0) {
+	return 0;
+    }
+
+/*     Set constants to control overflow */
+
+    eps = igraphdlamch_("P");
+    smlnum = igraphdlamch_("S") / eps;
+    sgn = (doublereal) (*isgn);
+
+    k = *n1 + *n1 + *n2 - 2;
+    switch (k) {
+	case 1:  goto L10;
+	case 2:  goto L20;
+	case 3:  goto L30;
+	case 4:  goto L50;
+    }
+
+/*     1 by 1: TL11*X + SGN*X*TR11 = B11 */
+
+L10:
+    tau1 = tl[tl_dim1 + 1] + sgn * tr[tr_dim1 + 1];
+    bet = abs(tau1);
+    if (bet <= smlnum) {
+	tau1 = smlnum;
+	bet = smlnum;
+	*info = 1;
+    }
+
+    *scale = 1.;
+    gam = (d__1 = b[b_dim1 + 1], abs(d__1));
+    if (smlnum * gam > bet) {
+	*scale = 1. / gam;
+    }
+
+    x[x_dim1 + 1] = b[b_dim1 + 1] * *scale / tau1;
+    *xnorm = (d__1 = x[x_dim1 + 1], abs(d__1));
+    return 0;
+
+/*     1 by 2:   
+       TL11*[X11 X12] + ISGN*[X11 X12]*op[TR11 TR12]  = [B11 B12]   
+                                         [TR21 TR22] */
+
+L20:
+
+/* Computing MAX   
+   Computing MAX */
+    d__7 = (d__1 = tl[tl_dim1 + 1], abs(d__1)), d__8 = (d__2 = tr[tr_dim1 + 1]
+	    , abs(d__2)), d__7 = max(d__7,d__8), d__8 = (d__3 = tr[(tr_dim1 <<
+	     1) + 1], abs(d__3)), d__7 = max(d__7,d__8), d__8 = (d__4 = tr[
+	    tr_dim1 + 2], abs(d__4)), d__7 = max(d__7,d__8), d__8 = (d__5 = 
+	    tr[(tr_dim1 << 1) + 2], abs(d__5));
+    d__6 = eps * max(d__7,d__8);
+    smin = max(d__6,smlnum);
+    tmp[0] = tl[tl_dim1 + 1] + sgn * tr[tr_dim1 + 1];
+    tmp[3] = tl[tl_dim1 + 1] + sgn * tr[(tr_dim1 << 1) + 2];
+    if (*ltranr) {
+	tmp[1] = sgn * tr[tr_dim1 + 2];
+	tmp[2] = sgn * tr[(tr_dim1 << 1) + 1];
+    } else {
+	tmp[1] = sgn * tr[(tr_dim1 << 1) + 1];
+	tmp[2] = sgn * tr[tr_dim1 + 2];
+    }
+    btmp[0] = b[b_dim1 + 1];
+    btmp[1] = b[(b_dim1 << 1) + 1];
+    goto L40;
+
+/*     2 by 1:   
+            op[TL11 TL12]*[X11] + ISGN* [X11]*TR11  = [B11]   
+              [TL21 TL22] [X21]         [X21]         [B21] */
+
+L30:
+/* Computing MAX   
+   Computing MAX */
+    d__7 = (d__1 = tr[tr_dim1 + 1], abs(d__1)), d__8 = (d__2 = tl[tl_dim1 + 1]
+	    , abs(d__2)), d__7 = max(d__7,d__8), d__8 = (d__3 = tl[(tl_dim1 <<
+	     1) + 1], abs(d__3)), d__7 = max(d__7,d__8), d__8 = (d__4 = tl[
+	    tl_dim1 + 2], abs(d__4)), d__7 = max(d__7,d__8), d__8 = (d__5 = 
+	    tl[(tl_dim1 << 1) + 2], abs(d__5));
+    d__6 = eps * max(d__7,d__8);
+    smin = max(d__6,smlnum);
+    tmp[0] = tl[tl_dim1 + 1] + sgn * tr[tr_dim1 + 1];
+    tmp[3] = tl[(tl_dim1 << 1) + 2] + sgn * tr[tr_dim1 + 1];
+    if (*ltranl) {
+	tmp[1] = tl[(tl_dim1 << 1) + 1];
+	tmp[2] = tl[tl_dim1 + 2];
+    } else {
+	tmp[1] = tl[tl_dim1 + 2];
+	tmp[2] = tl[(tl_dim1 << 1) + 1];
+    }
+    btmp[0] = b[b_dim1 + 1];
+    btmp[1] = b[b_dim1 + 2];
+L40:
+
+/*     Solve 2 by 2 system using complete pivoting.   
+       Set pivots less than SMIN to SMIN. */
+
+    ipiv = igraphidamax_(&c__4, tmp, &c__1);
+    u11 = tmp[ipiv - 1];
+    if (abs(u11) <= smin) {
+	*info = 1;
+	u11 = smin;
+    }
+    u12 = tmp[locu12[ipiv - 1] - 1];
+    l21 = tmp[locl21[ipiv - 1] - 1] / u11;
+    u22 = tmp[locu22[ipiv - 1] - 1] - u12 * l21;
+    xswap = xswpiv[ipiv - 1];
+    bswap = bswpiv[ipiv - 1];
+    if (abs(u22) <= smin) {
+	*info = 1;
+	u22 = smin;
+    }
+    if (bswap) {
+	temp = btmp[1];
+	btmp[1] = btmp[0] - l21 * temp;
+	btmp[0] = temp;
+    } else {
+	btmp[1] -= l21 * btmp[0];
+    }
+    *scale = 1.;
+    if (smlnum * 2. * abs(btmp[1]) > abs(u22) || smlnum * 2. * abs(btmp[0]) > 
+	    abs(u11)) {
+/* Computing MAX */
+	d__1 = abs(btmp[0]), d__2 = abs(btmp[1]);
+	*scale = .5 / max(d__1,d__2);
+	btmp[0] *= *scale;
+	btmp[1] *= *scale;
+    }
+    x2[1] = btmp[1] / u22;
+    x2[0] = btmp[0] / u11 - u12 / u11 * x2[1];
+    if (xswap) {
+	temp = x2[1];
+	x2[1] = x2[0];
+	x2[0] = temp;
+    }
+    x[x_dim1 + 1] = x2[0];
+    if (*n1 == 1) {
+	x[(x_dim1 << 1) + 1] = x2[1];
+	*xnorm = (d__1 = x[x_dim1 + 1], abs(d__1)) + (d__2 = x[(x_dim1 << 1) 
+		+ 1], abs(d__2));
+    } else {
+	x[x_dim1 + 2] = x2[1];
+/* Computing MAX */
+	d__3 = (d__1 = x[x_dim1 + 1], abs(d__1)), d__4 = (d__2 = x[x_dim1 + 2]
+		, abs(d__2));
+	*xnorm = max(d__3,d__4);
+    }
+    return 0;
+
+/*     2 by 2:   
+       op[TL11 TL12]*[X11 X12] +ISGN* [X11 X12]*op[TR11 TR12] = [B11 B12]   
+         [TL21 TL22] [X21 X22]        [X21 X22]   [TR21 TR22]   [B21 B22]   
+
+       Solve equivalent 4 by 4 system using complete pivoting.   
+       Set pivots less than SMIN to SMIN. */
+
+L50:
+/* Computing MAX */
+    d__5 = (d__1 = tr[tr_dim1 + 1], abs(d__1)), d__6 = (d__2 = tr[(tr_dim1 << 
+	    1) + 1], abs(d__2)), d__5 = max(d__5,d__6), d__6 = (d__3 = tr[
+	    tr_dim1 + 2], abs(d__3)), d__5 = max(d__5,d__6), d__6 = (d__4 = 
+	    tr[(tr_dim1 << 1) + 2], abs(d__4));
+    smin = max(d__5,d__6);
+/* Computing MAX */
+    d__5 = smin, d__6 = (d__1 = tl[tl_dim1 + 1], abs(d__1)), d__5 = max(d__5,
+	    d__6), d__6 = (d__2 = tl[(tl_dim1 << 1) + 1], abs(d__2)), d__5 = 
+	    max(d__5,d__6), d__6 = (d__3 = tl[tl_dim1 + 2], abs(d__3)), d__5 =
+	     max(d__5,d__6), d__6 = (d__4 = tl[(tl_dim1 << 1) + 2], abs(d__4))
+	    ;
+    smin = max(d__5,d__6);
+/* Computing MAX */
+    d__1 = eps * smin;
+    smin = max(d__1,smlnum);
+    btmp[0] = 0.;
+    igraphdcopy_(&c__16, btmp, &c__0, t16, &c__1);
+    t16[0] = tl[tl_dim1 + 1] + sgn * tr[tr_dim1 + 1];
+    t16[5] = tl[(tl_dim1 << 1) + 2] + sgn * tr[tr_dim1 + 1];
+    t16[10] = tl[tl_dim1 + 1] + sgn * tr[(tr_dim1 << 1) + 2];
+    t16[15] = tl[(tl_dim1 << 1) + 2] + sgn * tr[(tr_dim1 << 1) + 2];
+    if (*ltranl) {
+	t16[4] = tl[tl_dim1 + 2];
+	t16[1] = tl[(tl_dim1 << 1) + 1];
+	t16[14] = tl[tl_dim1 + 2];
+	t16[11] = tl[(tl_dim1 << 1) + 1];
+    } else {
+	t16[4] = tl[(tl_dim1 << 1) + 1];
+	t16[1] = tl[tl_dim1 + 2];
+	t16[14] = tl[(tl_dim1 << 1) + 1];
+	t16[11] = tl[tl_dim1 + 2];
+    }
+    if (*ltranr) {
+	t16[8] = sgn * tr[(tr_dim1 << 1) + 1];
+	t16[13] = sgn * tr[(tr_dim1 << 1) + 1];
+	t16[2] = sgn * tr[tr_dim1 + 2];
+	t16[7] = sgn * tr[tr_dim1 + 2];
+    } else {
+	t16[8] = sgn * tr[tr_dim1 + 2];
+	t16[13] = sgn * tr[tr_dim1 + 2];
+	t16[2] = sgn * tr[(tr_dim1 << 1) + 1];
+	t16[7] = sgn * tr[(tr_dim1 << 1) + 1];
+    }
+    btmp[0] = b[b_dim1 + 1];
+    btmp[1] = b[b_dim1 + 2];
+    btmp[2] = b[(b_dim1 << 1) + 1];
+    btmp[3] = b[(b_dim1 << 1) + 2];
+
+/*     Perform elimination */
+
+    for (i__ = 1; i__ <= 3; ++i__) {
+	xmax = 0.;
+	for (ip = i__; ip <= 4; ++ip) {
+	    for (jp = i__; jp <= 4; ++jp) {
+		if ((d__1 = t16[ip + (jp << 2) - 5], abs(d__1)) >= xmax) {
+		    xmax = (d__1 = t16[ip + (jp << 2) - 5], abs(d__1));
+		    ipsv = ip;
+		    jpsv = jp;
+		}
+/* L60: */
+	    }
+/* L70: */
+	}
+	if (ipsv != i__) {
+	    igraphdswap_(&c__4, &t16[ipsv - 1], &c__4, &t16[i__ - 1], &c__4);
+	    temp = btmp[i__ - 1];
+	    btmp[i__ - 1] = btmp[ipsv - 1];
+	    btmp[ipsv - 1] = temp;
+	}
+	if (jpsv != i__) {
+	    igraphdswap_(&c__4, &t16[(jpsv << 2) - 4], &c__1, &t16[(i__ << 2) - 4], 
+		    &c__1);
+	}
+	jpiv[i__ - 1] = jpsv;
+	if ((d__1 = t16[i__ + (i__ << 2) - 5], abs(d__1)) < smin) {
+	    *info = 1;
+	    t16[i__ + (i__ << 2) - 5] = smin;
+	}
+	for (j = i__ + 1; j <= 4; ++j) {
+	    t16[j + (i__ << 2) - 5] /= t16[i__ + (i__ << 2) - 5];
+	    btmp[j - 1] -= t16[j + (i__ << 2) - 5] * btmp[i__ - 1];
+	    for (k = i__ + 1; k <= 4; ++k) {
+		t16[j + (k << 2) - 5] -= t16[j + (i__ << 2) - 5] * t16[i__ + (
+			k << 2) - 5];
+/* L80: */
+	    }
+/* L90: */
+	}
+/* L100: */
+    }
+    if (abs(t16[15]) < smin) {
+	t16[15] = smin;
+    }
+    *scale = 1.;
+    if (smlnum * 8. * abs(btmp[0]) > abs(t16[0]) || smlnum * 8. * abs(btmp[1])
+	     > abs(t16[5]) || smlnum * 8. * abs(btmp[2]) > abs(t16[10]) || 
+	    smlnum * 8. * abs(btmp[3]) > abs(t16[15])) {
+/* Computing MAX */
+	d__1 = abs(btmp[0]), d__2 = abs(btmp[1]), d__1 = max(d__1,d__2), d__2 
+		= abs(btmp[2]), d__1 = max(d__1,d__2), d__2 = abs(btmp[3]);
+	*scale = .125 / max(d__1,d__2);
+	btmp[0] *= *scale;
+	btmp[1] *= *scale;
+	btmp[2] *= *scale;
+	btmp[3] *= *scale;
+    }
+    for (i__ = 1; i__ <= 4; ++i__) {
+	k = 5 - i__;
+	temp = 1. / t16[k + (k << 2) - 5];
+	tmp[k - 1] = btmp[k - 1] * temp;
+	for (j = k + 1; j <= 4; ++j) {
+	    tmp[k - 1] -= temp * t16[k + (j << 2) - 5] * tmp[j - 1];
+/* L110: */
+	}
+/* L120: */
+    }
+    for (i__ = 1; i__ <= 3; ++i__) {
+	if (jpiv[4 - i__ - 1] != 4 - i__) {
+	    temp = tmp[4 - i__ - 1];
+	    tmp[4 - i__ - 1] = tmp[jpiv[4 - i__ - 1] - 1];
+	    tmp[jpiv[4 - i__ - 1] - 1] = temp;
+	}
+/* L130: */
+    }
+    x[x_dim1 + 1] = tmp[0];
+    x[x_dim1 + 2] = tmp[1];
+    x[(x_dim1 << 1) + 1] = tmp[2];
+    x[(x_dim1 << 1) + 2] = tmp[3];
+/* Computing MAX */
+    d__1 = abs(tmp[0]) + abs(tmp[2]), d__2 = abs(tmp[1]) + abs(tmp[3]);
+    *xnorm = max(d__1,d__2);
+    return 0;
+
+/*     End of DLASY2 */
+
+} /* igraphdlasy2_ */
+
diff --git a/igraph/src/dlatrd.c b/igraph/src/dlatrd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dlatrd.c
@@ -0,0 +1,418 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b5 = -1.;
+static doublereal c_b6 = 1.;
+static integer c__1 = 1;
+static doublereal c_b16 = 0.;
+
+/* > \brief \b DLATRD reduces the first nb rows and columns of a symmetric/Hermitian matrix A to real tridiago
+nal form by an orthogonal similarity transformation.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DLATRD + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dlatrd.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dlatrd.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dlatrd.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DLATRD( UPLO, N, NB, A, LDA, E, TAU, W, LDW )   
+
+         CHARACTER          UPLO   
+         INTEGER            LDA, LDW, N, NB   
+         DOUBLE PRECISION   A( LDA, * ), E( * ), TAU( * ), W( LDW, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DLATRD reduces NB rows and columns of a real symmetric matrix A to   
+   > symmetric tridiagonal form by an orthogonal similarity   
+   > transformation Q**T * A * Q, and returns the matrices V and W which are   
+   > needed to apply the transformation to the unreduced part of A.   
+   >   
+   > If UPLO = 'U', DLATRD reduces the last NB rows and columns of a   
+   > matrix, of which the upper triangle is supplied;   
+   > if UPLO = 'L', DLATRD reduces the first NB rows and columns of a   
+   > matrix, of which the lower triangle is supplied.   
+   >   
+   > This is an auxiliary routine called by DSYTRD.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          Specifies whether the upper or lower triangular part of the   
+   >          symmetric matrix A is stored:   
+   >          = 'U': Upper triangular   
+   >          = 'L': Lower triangular   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] NB   
+   > \verbatim   
+   >          NB is INTEGER   
+   >          The number of rows and columns to be reduced.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the symmetric matrix A.  If UPLO = 'U', the leading   
+   >          n-by-n upper triangular part of A contains the upper   
+   >          triangular part of the matrix A, and the strictly lower   
+   >          triangular part of A is not referenced.  If UPLO = 'L', the   
+   >          leading n-by-n lower triangular part of A contains the lower   
+   >          triangular part of the matrix A, and the strictly upper   
+   >          triangular part of A is not referenced.   
+   >          On exit:   
+   >          if UPLO = 'U', the last NB columns have been reduced to   
+   >            tridiagonal form, with the diagonal elements overwriting   
+   >            the diagonal elements of A; the elements above the diagonal   
+   >            with the array TAU, represent the orthogonal matrix Q as a   
+   >            product of elementary reflectors;   
+   >          if UPLO = 'L', the first NB columns have been reduced to   
+   >            tridiagonal form, with the diagonal elements overwriting   
+   >            the diagonal elements of A; the elements below the diagonal   
+   >            with the array TAU, represent the  orthogonal matrix Q as a   
+   >            product of elementary reflectors.   
+   >          See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= (1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          If UPLO = 'U', E(n-nb:n-1) contains the superdiagonal   
+   >          elements of the last NB columns of the reduced matrix;   
+   >          if UPLO = 'L', E(1:nb) contains the subdiagonal elements of   
+   >          the first NB columns of the reduced matrix.   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (N-1)   
+   >          The scalar factors of the elementary reflectors, stored in   
+   >          TAU(n-nb:n-1) if UPLO = 'U', and in TAU(1:nb) if UPLO = 'L'.   
+   >          See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (LDW,NB)   
+   >          The n-by-nb matrix W required to update the unreduced part   
+   >          of A.   
+   > \endverbatim   
+   >   
+   > \param[in] LDW   
+   > \verbatim   
+   >          LDW is INTEGER   
+   >          The leading dimension of the array W. LDW >= max(1,N).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  If UPLO = 'U', the matrix Q is represented as a product of elementary   
+   >  reflectors   
+   >   
+   >     Q = H(n) H(n-1) . . . H(n-nb+1).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(i:n) = 0 and v(i-1) = 1; v(1:i-1) is stored on exit in A(1:i-1,i),   
+   >  and tau in TAU(i-1).   
+   >   
+   >  If UPLO = 'L', the matrix Q is represented as a product of elementary   
+   >  reflectors   
+   >   
+   >     Q = H(1) H(2) . . . H(nb).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(1:i) = 0 and v(i+1) = 1; v(i+1:n) is stored on exit in A(i+1:n,i),   
+   >  and tau in TAU(i).   
+   >   
+   >  The elements of the vectors v together form the n-by-nb matrix V   
+   >  which is needed, with W, to apply the transformation to the unreduced   
+   >  part of the matrix, using a symmetric rank-2k update of the form:   
+   >  A := A - V*W**T - W*V**T.   
+   >   
+   >  The contents of A on exit are illustrated by the following examples   
+   >  with n = 5 and nb = 2:   
+   >   
+   >  if UPLO = 'U':                       if UPLO = 'L':   
+   >   
+   >    (  a   a   a   v4  v5 )              (  d                  )   
+   >    (      a   a   v4  v5 )              (  1   d              )   
+   >    (          a   1   v5 )              (  v1  1   a          )   
+   >    (              d   1  )              (  v1  v2  a   a      )   
+   >    (                  d  )              (  v1  v2  a   a   a  )   
+   >   
+   >  where d denotes a diagonal element of the reduced matrix, a denotes   
+   >  an element of the original matrix that is unchanged, and vi denotes   
+   >  an element of the vector defining H(i).   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdlatrd_(char *uplo, integer *n, integer *nb, doublereal *
+	a, integer *lda, doublereal *e, doublereal *tau, doublereal *w, 
+	integer *ldw)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, w_dim1, w_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__, iw;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    doublereal alpha;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *), igraphdaxpy_(integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *), 
+	    igraphdsymv_(char *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *), igraphdlarfg_(integer *, doublereal *, doublereal *, integer *,
+	     doublereal *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Quick return if possible   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --e;
+    --tau;
+    w_dim1 = *ldw;
+    w_offset = 1 + w_dim1;
+    w -= w_offset;
+
+    /* Function Body */
+    if (*n <= 0) {
+	return 0;
+    }
+
+    if (igraphlsame_(uplo, "U")) {
+
+/*        Reduce last NB columns of upper triangle */
+
+	i__1 = *n - *nb + 1;
+	for (i__ = *n; i__ >= i__1; --i__) {
+	    iw = i__ - *n + *nb;
+	    if (i__ < *n) {
+
+/*              Update A(1:i,i) */
+
+		i__2 = *n - i__;
+		igraphdgemv_("No transpose", &i__, &i__2, &c_b5, &a[(i__ + 1) * 
+			a_dim1 + 1], lda, &w[i__ + (iw + 1) * w_dim1], ldw, &
+			c_b6, &a[i__ * a_dim1 + 1], &c__1);
+		i__2 = *n - i__;
+		igraphdgemv_("No transpose", &i__, &i__2, &c_b5, &w[(iw + 1) * 
+			w_dim1 + 1], ldw, &a[i__ + (i__ + 1) * a_dim1], lda, &
+			c_b6, &a[i__ * a_dim1 + 1], &c__1);
+	    }
+	    if (i__ > 1) {
+
+/*              Generate elementary reflector H(i) to annihilate   
+                A(1:i-2,i) */
+
+		i__2 = i__ - 1;
+		igraphdlarfg_(&i__2, &a[i__ - 1 + i__ * a_dim1], &a[i__ * a_dim1 + 
+			1], &c__1, &tau[i__ - 1]);
+		e[i__ - 1] = a[i__ - 1 + i__ * a_dim1];
+		a[i__ - 1 + i__ * a_dim1] = 1.;
+
+/*              Compute W(1:i-1,i) */
+
+		i__2 = i__ - 1;
+		igraphdsymv_("Upper", &i__2, &c_b6, &a[a_offset], lda, &a[i__ * 
+			a_dim1 + 1], &c__1, &c_b16, &w[iw * w_dim1 + 1], &
+			c__1);
+		if (i__ < *n) {
+		    i__2 = i__ - 1;
+		    i__3 = *n - i__;
+		    igraphdgemv_("Transpose", &i__2, &i__3, &c_b6, &w[(iw + 1) * 
+			    w_dim1 + 1], ldw, &a[i__ * a_dim1 + 1], &c__1, &
+			    c_b16, &w[i__ + 1 + iw * w_dim1], &c__1);
+		    i__2 = i__ - 1;
+		    i__3 = *n - i__;
+		    igraphdgemv_("No transpose", &i__2, &i__3, &c_b5, &a[(i__ + 1) *
+			     a_dim1 + 1], lda, &w[i__ + 1 + iw * w_dim1], &
+			    c__1, &c_b6, &w[iw * w_dim1 + 1], &c__1);
+		    i__2 = i__ - 1;
+		    i__3 = *n - i__;
+		    igraphdgemv_("Transpose", &i__2, &i__3, &c_b6, &a[(i__ + 1) * 
+			    a_dim1 + 1], lda, &a[i__ * a_dim1 + 1], &c__1, &
+			    c_b16, &w[i__ + 1 + iw * w_dim1], &c__1);
+		    i__2 = i__ - 1;
+		    i__3 = *n - i__;
+		    igraphdgemv_("No transpose", &i__2, &i__3, &c_b5, &w[(iw + 1) * 
+			    w_dim1 + 1], ldw, &w[i__ + 1 + iw * w_dim1], &
+			    c__1, &c_b6, &w[iw * w_dim1 + 1], &c__1);
+		}
+		i__2 = i__ - 1;
+		igraphdscal_(&i__2, &tau[i__ - 1], &w[iw * w_dim1 + 1], &c__1);
+		i__2 = i__ - 1;
+		alpha = tau[i__ - 1] * -.5 * igraphddot_(&i__2, &w[iw * w_dim1 + 1],
+			 &c__1, &a[i__ * a_dim1 + 1], &c__1);
+		i__2 = i__ - 1;
+		igraphdaxpy_(&i__2, &alpha, &a[i__ * a_dim1 + 1], &c__1, &w[iw * 
+			w_dim1 + 1], &c__1);
+	    }
+
+/* L10: */
+	}
+    } else {
+
+/*        Reduce first NB columns of lower triangle */
+
+	i__1 = *nb;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+
+/*           Update A(i:n,i) */
+
+	    i__2 = *n - i__ + 1;
+	    i__3 = i__ - 1;
+	    igraphdgemv_("No transpose", &i__2, &i__3, &c_b5, &a[i__ + a_dim1], lda,
+		     &w[i__ + w_dim1], ldw, &c_b6, &a[i__ + i__ * a_dim1], &
+		    c__1);
+	    i__2 = *n - i__ + 1;
+	    i__3 = i__ - 1;
+	    igraphdgemv_("No transpose", &i__2, &i__3, &c_b5, &w[i__ + w_dim1], ldw,
+		     &a[i__ + a_dim1], lda, &c_b6, &a[i__ + i__ * a_dim1], &
+		    c__1);
+	    if (i__ < *n) {
+
+/*              Generate elementary reflector H(i) to annihilate   
+                A(i+2:n,i) */
+
+		i__2 = *n - i__;
+/* Computing MIN */
+		i__3 = i__ + 2;
+		igraphdlarfg_(&i__2, &a[i__ + 1 + i__ * a_dim1], &a[min(i__3,*n) + 
+			i__ * a_dim1], &c__1, &tau[i__]);
+		e[i__] = a[i__ + 1 + i__ * a_dim1];
+		a[i__ + 1 + i__ * a_dim1] = 1.;
+
+/*              Compute W(i+1:n,i) */
+
+		i__2 = *n - i__;
+		igraphdsymv_("Lower", &i__2, &c_b6, &a[i__ + 1 + (i__ + 1) * a_dim1]
+			, lda, &a[i__ + 1 + i__ * a_dim1], &c__1, &c_b16, &w[
+			i__ + 1 + i__ * w_dim1], &c__1);
+		i__2 = *n - i__;
+		i__3 = i__ - 1;
+		igraphdgemv_("Transpose", &i__2, &i__3, &c_b6, &w[i__ + 1 + w_dim1],
+			 ldw, &a[i__ + 1 + i__ * a_dim1], &c__1, &c_b16, &w[
+			i__ * w_dim1 + 1], &c__1);
+		i__2 = *n - i__;
+		i__3 = i__ - 1;
+		igraphdgemv_("No transpose", &i__2, &i__3, &c_b5, &a[i__ + 1 + 
+			a_dim1], lda, &w[i__ * w_dim1 + 1], &c__1, &c_b6, &w[
+			i__ + 1 + i__ * w_dim1], &c__1);
+		i__2 = *n - i__;
+		i__3 = i__ - 1;
+		igraphdgemv_("Transpose", &i__2, &i__3, &c_b6, &a[i__ + 1 + a_dim1],
+			 lda, &a[i__ + 1 + i__ * a_dim1], &c__1, &c_b16, &w[
+			i__ * w_dim1 + 1], &c__1);
+		i__2 = *n - i__;
+		i__3 = i__ - 1;
+		igraphdgemv_("No transpose", &i__2, &i__3, &c_b5, &w[i__ + 1 + 
+			w_dim1], ldw, &w[i__ * w_dim1 + 1], &c__1, &c_b6, &w[
+			i__ + 1 + i__ * w_dim1], &c__1);
+		i__2 = *n - i__;
+		igraphdscal_(&i__2, &tau[i__], &w[i__ + 1 + i__ * w_dim1], &c__1);
+		i__2 = *n - i__;
+		alpha = tau[i__] * -.5 * igraphddot_(&i__2, &w[i__ + 1 + i__ * 
+			w_dim1], &c__1, &a[i__ + 1 + i__ * a_dim1], &c__1);
+		i__2 = *n - i__;
+		igraphdaxpy_(&i__2, &alpha, &a[i__ + 1 + i__ * a_dim1], &c__1, &w[
+			i__ + 1 + i__ * w_dim1], &c__1);
+	    }
+
+/* L20: */
+	}
+    }
+
+    return 0;
+
+/*     End of DLATRD */
+
+} /* igraphdlatrd_ */
+
diff --git a/igraph/src/dmout.c b/igraph/src/dmout.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dmout.c
@@ -0,0 +1,393 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c__3 = 3;
+
+/* -----------------------------------------------------------------------   
+    Routine:    DMOUT   
+
+    Purpose:    Real matrix output routine.   
+
+    Usage:      CALL DMOUT (LOUT, M, N, A, LDA, IDIGIT, IFMT)   
+
+    Arguments   
+       M      - Number of rows of A.  (Input)   
+       N      - Number of columns of A.  (Input)   
+       A      - Real M by N matrix to be printed.  (Input)   
+       LDA    - Leading dimension of A exactly as specified in the   
+                dimension statement of the calling program.  (Input)   
+       IFMT   - Format to be used in printing matrix A.  (Input)   
+       IDIGIT - Print up to IABS(IDIGIT) decimal digits per number.  (In)   
+                If IDIGIT .LT. 0, printing is done with 72 columns.   
+                If IDIGIT .GT. 0, printing is done with 132 columns.   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdmout_(integer *lout, integer *m, integer *n, doublereal 
+	*a, integer *lda, integer *idigit, char *ifmt, ftnlen ifmt_len)
+{
+    /* Initialized data */
+
+    static char icol[1*3] = "C" "o" "l";
+
+    /* Format strings */
+    static char fmt_9999[] = "(/1x,a,/1x,a)";
+    static char fmt_9998[] = "(10x,10(4x,3a1,i4,1x))";
+    static char fmt_9994[] = "(1x,\002 Row\002,i4,\002:\002,1x,1p,10d12.3)";
+    static char fmt_9997[] = "(10x,8(5x,3a1,i4,2x))";
+    static char fmt_9993[] = "(1x,\002 Row\002,i4,\002:\002,1x,1p,8d14.5)";
+    static char fmt_9996[] = "(10x,6(7x,3a1,i4,4x))";
+    static char fmt_9992[] = "(1x,\002 Row\002,i4,\002:\002,1x,1p,6d18.9)";
+    static char fmt_9995[] = "(10x,5(9x,3a1,i4,6x))";
+    static char fmt_9991[] = "(1x,\002 Row\002,i4,\002:\002,1x,1p,5d22.13)";
+    static char fmt_9990[] = "(1x,\002 \002)";
+
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+
+    /* Builtin functions */
+    integer i_len(char *, ftnlen), s_wsfe(cilist *), do_fio(integer *, char *,
+	     ftnlen), e_wsfe(void);
+
+    /* Local variables */
+    integer i__, j, k1, k2, lll;
+    char line[80];
+    integer ndigit;
+
+    /* Fortran I/O blocks */
+    static cilist io___5 = { 0, 0, 0, fmt_9999, 0 };
+    static cilist io___9 = { 0, 0, 0, fmt_9998, 0 };
+    static cilist io___10 = { 0, 0, 0, fmt_9994, 0 };
+    static cilist io___12 = { 0, 0, 0, fmt_9997, 0 };
+    static cilist io___13 = { 0, 0, 0, fmt_9993, 0 };
+    static cilist io___14 = { 0, 0, 0, fmt_9996, 0 };
+    static cilist io___15 = { 0, 0, 0, fmt_9992, 0 };
+    static cilist io___16 = { 0, 0, 0, fmt_9995, 0 };
+    static cilist io___17 = { 0, 0, 0, fmt_9991, 0 };
+    static cilist io___18 = { 0, 0, 0, fmt_9998, 0 };
+    static cilist io___19 = { 0, 0, 0, fmt_9994, 0 };
+    static cilist io___20 = { 0, 0, 0, fmt_9997, 0 };
+    static cilist io___21 = { 0, 0, 0, fmt_9993, 0 };
+    static cilist io___22 = { 0, 0, 0, fmt_9996, 0 };
+    static cilist io___23 = { 0, 0, 0, fmt_9992, 0 };
+    static cilist io___24 = { 0, 0, 0, fmt_9995, 0 };
+    static cilist io___25 = { 0, 0, 0, fmt_9991, 0 };
+    static cilist io___26 = { 0, 0, 0, fmt_9990, 0 };
+
+
+/*     ...   
+       ... SPECIFICATIONS FOR ARGUMENTS   
+       ...   
+       ... SPECIFICATIONS FOR LOCAL VARIABLES   
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body   
+       ...   
+       ... FIRST EXECUTABLE STATEMENT   
+
+   Computing MIN */
+    i__1 = i_len(ifmt, ifmt_len);
+    lll = min(i__1,80);
+    i__1 = lll;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	*(unsigned char *)&line[i__ - 1] = '-';
+/* L10: */
+    }
+
+    for (i__ = lll + 1; i__ <= 80; ++i__) {
+	*(unsigned char *)&line[i__ - 1] = ' ';
+/* L20: */
+    }
+
+    io___5.ciunit = *lout;
+    s_wsfe(&io___5);
+    do_fio(&c__1, ifmt, ifmt_len);
+    do_fio(&c__1, line, lll);
+    e_wsfe();
+
+    if (*m <= 0 || *n <= 0 || *lda <= 0) {
+	return 0;
+    }
+    ndigit = *idigit;
+    if (*idigit == 0) {
+	ndigit = 4;
+    }
+
+/* =======================================================================   
+               CODE FOR OUTPUT USING 72 COLUMNS FORMAT   
+   ======================================================================= */
+
+    if (*idigit < 0) {
+	ndigit = -(*idigit);
+	if (ndigit <= 4) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 5) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 4;
+		k2 = min(i__2,i__3);
+		io___9.ciunit = *lout;
+		s_wsfe(&io___9);
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__3, icol, (ftnlen)1);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    io___10.ciunit = *lout;
+		    s_wsfe(&io___10);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		    i__3 = k2;
+		    for (j = k1; j <= i__3; ++j) {
+			do_fio(&c__1, (char *)&a[i__ + j * a_dim1], (ftnlen)
+				sizeof(doublereal));
+		    }
+		    e_wsfe();
+/* L30: */
+		}
+/* L40: */
+	    }
+
+	} else if (ndigit <= 6) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 4) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 3;
+		k2 = min(i__2,i__3);
+		io___12.ciunit = *lout;
+		s_wsfe(&io___12);
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__3, icol, (ftnlen)1);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    io___13.ciunit = *lout;
+		    s_wsfe(&io___13);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		    i__3 = k2;
+		    for (j = k1; j <= i__3; ++j) {
+			do_fio(&c__1, (char *)&a[i__ + j * a_dim1], (ftnlen)
+				sizeof(doublereal));
+		    }
+		    e_wsfe();
+/* L50: */
+		}
+/* L60: */
+	    }
+
+	} else if (ndigit <= 10) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 3) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 2;
+		k2 = min(i__2,i__3);
+		io___14.ciunit = *lout;
+		s_wsfe(&io___14);
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__3, icol, (ftnlen)1);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    io___15.ciunit = *lout;
+		    s_wsfe(&io___15);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		    i__3 = k2;
+		    for (j = k1; j <= i__3; ++j) {
+			do_fio(&c__1, (char *)&a[i__ + j * a_dim1], (ftnlen)
+				sizeof(doublereal));
+		    }
+		    e_wsfe();
+/* L70: */
+		}
+/* L80: */
+	    }
+
+	} else {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 2) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 1;
+		k2 = min(i__2,i__3);
+		io___16.ciunit = *lout;
+		s_wsfe(&io___16);
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__3, icol, (ftnlen)1);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    io___17.ciunit = *lout;
+		    s_wsfe(&io___17);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		    i__3 = k2;
+		    for (j = k1; j <= i__3; ++j) {
+			do_fio(&c__1, (char *)&a[i__ + j * a_dim1], (ftnlen)
+				sizeof(doublereal));
+		    }
+		    e_wsfe();
+/* L90: */
+		}
+/* L100: */
+	    }
+	}
+
+/* =======================================================================   
+               CODE FOR OUTPUT USING 132 COLUMNS FORMAT   
+   ======================================================================= */
+
+    } else {
+	if (ndigit <= 4) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 10) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 9;
+		k2 = min(i__2,i__3);
+		io___18.ciunit = *lout;
+		s_wsfe(&io___18);
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__3, icol, (ftnlen)1);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    io___19.ciunit = *lout;
+		    s_wsfe(&io___19);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		    i__3 = k2;
+		    for (j = k1; j <= i__3; ++j) {
+			do_fio(&c__1, (char *)&a[i__ + j * a_dim1], (ftnlen)
+				sizeof(doublereal));
+		    }
+		    e_wsfe();
+/* L110: */
+		}
+/* L120: */
+	    }
+
+	} else if (ndigit <= 6) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 8) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 7;
+		k2 = min(i__2,i__3);
+		io___20.ciunit = *lout;
+		s_wsfe(&io___20);
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__3, icol, (ftnlen)1);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    io___21.ciunit = *lout;
+		    s_wsfe(&io___21);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		    i__3 = k2;
+		    for (j = k1; j <= i__3; ++j) {
+			do_fio(&c__1, (char *)&a[i__ + j * a_dim1], (ftnlen)
+				sizeof(doublereal));
+		    }
+		    e_wsfe();
+/* L130: */
+		}
+/* L140: */
+	    }
+
+	} else if (ndigit <= 10) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 6) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 5;
+		k2 = min(i__2,i__3);
+		io___22.ciunit = *lout;
+		s_wsfe(&io___22);
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__3, icol, (ftnlen)1);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    io___23.ciunit = *lout;
+		    s_wsfe(&io___23);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		    i__3 = k2;
+		    for (j = k1; j <= i__3; ++j) {
+			do_fio(&c__1, (char *)&a[i__ + j * a_dim1], (ftnlen)
+				sizeof(doublereal));
+		    }
+		    e_wsfe();
+/* L150: */
+		}
+/* L160: */
+	    }
+
+	} else {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 5) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 4;
+		k2 = min(i__2,i__3);
+		io___24.ciunit = *lout;
+		s_wsfe(&io___24);
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__3, icol, (ftnlen)1);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+		i__2 = *m;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    io___25.ciunit = *lout;
+		    s_wsfe(&io___25);
+		    do_fio(&c__1, (char *)&i__, (ftnlen)sizeof(integer));
+		    i__3 = k2;
+		    for (j = k1; j <= i__3; ++j) {
+			do_fio(&c__1, (char *)&a[i__ + j * a_dim1], (ftnlen)
+				sizeof(doublereal));
+		    }
+		    e_wsfe();
+/* L170: */
+		}
+/* L180: */
+	    }
+	}
+    }
+    io___26.ciunit = *lout;
+    s_wsfe(&io___26);
+    e_wsfe();
+
+
+    return 0;
+} /* igraphdmout_ */
+
diff --git a/igraph/src/dnaitr.c b/igraph/src/dnaitr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dnaitr.c
@@ -0,0 +1,950 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static logical c_false = FALSE_;
+static doublereal c_b25 = 1.;
+static doublereal c_b47 = 0.;
+static doublereal c_b50 = -1.;
+static integer c__2 = 2;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dnaitr   
+
+   \Description:   
+    Reverse communication interface for applying NP additional steps to   
+    a K step nonsymmetric Arnoldi factorization.   
+
+    Input:  OP*V_{k}  -  V_{k}*H = r_{k}*e_{k}^T   
+
+            with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0.   
+
+    Output: OP*V_{k+p}  -  V_{k+p}*H = r_{k+p}*e_{k+p}^T   
+
+            with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0.   
+
+    where OP and B are as in dnaupd.  The B-norm of r_{k+p} is also   
+    computed and returned.   
+
+   \Usage:   
+    call dnaitr   
+       ( IDO, BMAT, N, K, NP, NB, RESID, RNORM, V, LDV, H, LDH,   
+         IPNTR, WORKD, INFO )   
+
+   \Arguments   
+    IDO     Integer.  (INPUT/OUTPUT)   
+            Reverse communication flag.   
+            -------------------------------------------------------------   
+            IDO =  0: first call to the reverse communication interface   
+            IDO = -1: compute  Y = OP * X  where   
+                      IPNTR(1) is the pointer into WORK for X,   
+                      IPNTR(2) is the pointer into WORK for Y.   
+                      This is for the restart phase to force the new   
+                      starting vector into the range of OP.   
+            IDO =  1: compute  Y = OP * X  where   
+                      IPNTR(1) is the pointer into WORK for X,   
+                      IPNTR(2) is the pointer into WORK for Y,   
+                      IPNTR(3) is the pointer into WORK for B * X.   
+            IDO =  2: compute  Y = B * X  where   
+                      IPNTR(1) is the pointer into WORK for X,   
+                      IPNTR(2) is the pointer into WORK for Y.   
+            IDO = 99: done   
+            -------------------------------------------------------------   
+            When the routine is used in the "shift-and-invert" mode, the   
+            vector B * Q is already available and do not need to be   
+            recompute in forming OP * Q.   
+
+    BMAT    Character*1.  (INPUT)   
+            BMAT specifies the type of the matrix B that defines the   
+            semi-inner product for the operator OP.  See dnaupd.   
+            B = 'I' -> standard eigenvalue problem A*x = lambda*x   
+            B = 'G' -> generalized eigenvalue problem A*x = lambda*M**x   
+
+    N       Integer.  (INPUT)   
+            Dimension of the eigenproblem.   
+
+    K       Integer.  (INPUT)   
+            Current size of V and H.   
+
+    NP      Integer.  (INPUT)   
+            Number of additional Arnoldi steps to take.   
+
+    NB      Integer.  (INPUT)   
+            Blocksize to be used in the recurrence.   
+            Only work for NB = 1 right now.  The goal is to have a   
+            program that implement both the block and non-block method.   
+
+    RESID   Double precision array of length N.  (INPUT/OUTPUT)   
+            On INPUT:  RESID contains the residual vector r_{k}.   
+            On OUTPUT: RESID contains the residual vector r_{k+p}.   
+
+    RNORM   Double precision scalar.  (INPUT/OUTPUT)   
+            B-norm of the starting residual on input.   
+            B-norm of the updated residual r_{k+p} on output.   
+
+    V       Double precision N by K+NP array.  (INPUT/OUTPUT)   
+            On INPUT:  V contains the Arnoldi vectors in the first K   
+            columns.   
+            On OUTPUT: V contains the new NP Arnoldi vectors in the next   
+            NP columns.  The first K columns are unchanged.   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling   
+            program.   
+
+    H       Double precision (K+NP) by (K+NP) array.  (INPUT/OUTPUT)   
+            H is used to store the generated upper Hessenberg matrix.   
+
+    LDH     Integer.  (INPUT)   
+            Leading dimension of H exactly as declared in the calling   
+            program.   
+
+    IPNTR   Integer array of length 3.  (OUTPUT)   
+            Pointer to mark the starting locations in the WORK for   
+            vectors used by the Arnoldi iteration.   
+            -------------------------------------------------------------   
+            IPNTR(1): pointer to the current operand vector X.   
+            IPNTR(2): pointer to the current result vector Y.   
+            IPNTR(3): pointer to the vector B * X when used in the   
+                      shift-and-invert mode.  X is the current operand.   
+            -------------------------------------------------------------   
+
+    WORKD   Double precision work array of length 3*N.  (REVERSE COMMUNICATION)   
+            Distributed array to be used in the basic Arnoldi iteration   
+            for reverse communication.  The calling program should not   
+            use WORKD as temporary workspace during the iteration !!!!!!   
+            On input, WORKD(1:N) = B*RESID and is used to save some   
+            computation at the first step.   
+
+    INFO    Integer.  (OUTPUT)   
+            = 0: Normal exit.   
+            > 0: Size of the spanning invariant subspace of OP found.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+
+   \Routines called:   
+       dgetv0  ARPACK routine to generate the initial vector.   
+       ivout   ARPACK utility routine that prints integers.   
+       second  ARPACK utility routine for timing.   
+       dmout   ARPACK utility routine that prints matrices   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlabad  LAPACK routine that computes machine constants.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlascl  LAPACK routine for careful scaling of a matrix.   
+       dlanhs  LAPACK routine that computes various norms of a matrix.   
+       dgemv   Level 2 BLAS routine for matrix vector multiplication.   
+       daxpy   Level 1 BLAS that computes a vector triad.   
+       dscal   Level 1 BLAS that scales a vector.   
+       dcopy   Level 1 BLAS that copies one vector to another .   
+       ddot    Level 1 BLAS that computes the scalar product of two vectors.   
+       dnrm2   Level 1 BLAS that computes the norm of a vector.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/92: Version ' 2.4'   
+
+   \SCCS Information: @(#)   
+   FILE: naitr.F   SID: 2.4   DATE OF SID: 8/27/96   RELEASE: 2   
+
+   \Remarks   
+    The algorithm implemented is:   
+
+    restart = .false.   
+    Given V_{k} = [v_{1}, ..., v_{k}], r_{k};   
+    r_{k} contains the initial residual vector even for k = 0;   
+    Also assume that rnorm = || B*r_{k} || and B*r_{k} are already   
+    computed by the calling program.   
+
+    betaj = rnorm ; p_{k+1} = B*r_{k} ;   
+    For  j = k+1, ..., k+np  Do   
+       1) if ( betaj < tol ) stop or restart depending on j.   
+          ( At present tol is zero )   
+          if ( restart ) generate a new starting vector.   
+       2) v_{j} = r(j-1)/betaj;  V_{j} = [V_{j-1}, v_{j}];   
+          p_{j} = p_{j}/betaj   
+       3) r_{j} = OP*v_{j} where OP is defined as in dnaupd   
+          For shift-invert mode p_{j} = B*v_{j} is already available.   
+          wnorm = || OP*v_{j} ||   
+       4) Compute the j-th step residual vector.   
+          w_{j} =  V_{j}^T * B * OP * v_{j}   
+          r_{j} =  OP*v_{j} - V_{j} * w_{j}   
+          H(:,j) = w_{j};   
+          H(j,j-1) = rnorm   
+          rnorm = || r_(j) ||   
+          If (rnorm > 0.717*wnorm) accept step and go back to 1)   
+       5) Re-orthogonalization step:   
+          s = V_{j}'*B*r_{j}   
+          r_{j} = r_{j} - V_{j}*s;  rnorm1 = || r_{j} ||   
+          alphaj = alphaj + s_{j};   
+       6) Iterative refinement step:   
+          If (rnorm1 > 0.717*rnorm) then   
+             rnorm = rnorm1   
+             accept step and go back to 1)   
+          Else   
+             rnorm = rnorm1   
+             If this is the first time in step 6), go to 5)   
+             Else r_{j} lies in the span of V_{j} numerically.   
+                Set r_{j} = 0 and rnorm = 0; go to 1)   
+          EndIf   
+    End Do   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdnaitr_(integer *ido, char *bmat, integer *n, integer *k,
+	 integer *np, integer *nb, doublereal *resid, doublereal *rnorm, 
+	doublereal *v, integer *ldv, doublereal *h__, integer *ldh, integer *
+	ipntr, doublereal *workd, integer *info)
+{
+    /* Initialized data */
+
+    IGRAPH_F77_SAVE logical first = TRUE_;
+
+    /* System generated locals */
+    integer h_dim1, h_offset, v_dim1, v_offset, i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__;
+    IGRAPH_F77_SAVE integer j;
+    real t0, t1, t2 = 0, t3, t4, t5;
+    integer jj;
+    IGRAPH_F77_SAVE integer ipj, irj;
+    integer nbx = 0;
+    IGRAPH_F77_SAVE integer ivj;
+    IGRAPH_F77_SAVE doublereal ulp;
+    doublereal tst1;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    IGRAPH_F77_SAVE integer ierr, iter;
+    IGRAPH_F77_SAVE doublereal unfl, ovfl;
+    integer nopx = 0;
+    IGRAPH_F77_SAVE integer itry;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    doublereal temp1;
+    IGRAPH_F77_SAVE logical orth1, orth2, step3, step4;
+    IGRAPH_F77_SAVE doublereal betaj;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *), igraphdgemv_(char *, integer *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *);
+    integer infol;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdaxpy_(integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *), igraphdmout_(integer 
+	    *, integer *, integer *, doublereal *, integer *, integer *, char 
+	    *, ftnlen);
+    doublereal xtemp[2];
+    real tmvbx = 0;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen);
+    IGRAPH_F77_SAVE doublereal wnorm;
+    extern /* Subroutine */ int igraphivout_(integer *, integer *, integer *, 
+	    integer *, char *, ftnlen), igraphdgetv0_(integer *, char *, integer *, 
+	    logical *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *), igraphdlabad_(doublereal *, doublereal *);
+    IGRAPH_F77_SAVE doublereal rnorm1;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlascl_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *);
+    extern doublereal igraphdlanhs_(char *, integer *, doublereal *, integer *, 
+	    doublereal *);
+    extern /* Subroutine */ int igraphsecond_(real *);
+    integer logfil, ndigit, nitref = 0, mnaitr = 0;
+    real titref = 0, tnaitr = 0;
+    IGRAPH_F77_SAVE integer msglvl;
+    IGRAPH_F77_SAVE doublereal smlnum;
+    integer nrorth = 0;
+    IGRAPH_F77_SAVE logical rstart;
+    integer nrstrt = 0;
+    real tmvopx = 0;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %-----------------------%   
+       | Local Array Arguments |   
+       %-----------------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------%   
+       | Data statements |   
+       %-----------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    --ipntr;
+
+    /* Function Body   
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------% */
+
+    if (first) {
+
+/*        %-----------------------------------------%   
+          | Set machine-dependent constants for the |   
+          | the splitting and deflation criterion.  |   
+          | If norm(H) <= sqrt(OVFL),               |   
+          | overflow should not occur.              |   
+          | REFERENCE: LAPACK subroutine dlahqr     |   
+          %-----------------------------------------% */
+
+	unfl = igraphdlamch_("safe minimum");
+	ovfl = 1. / unfl;
+	igraphdlabad_(&unfl, &ovfl);
+	ulp = igraphdlamch_("precision");
+	smlnum = unfl * (*n / ulp);
+	first = FALSE_;
+    }
+
+    if (*ido == 0) {
+
+/*        %-------------------------------%   
+          | Initialize timing statistics  |   
+          | & message level for debugging |   
+          %-------------------------------% */
+
+	igraphsecond_(&t0);
+	msglvl = mnaitr;
+
+/*        %------------------------------%   
+          | Initial call to this routine |   
+          %------------------------------% */
+
+	*info = 0;
+	step3 = FALSE_;
+	step4 = FALSE_;
+	rstart = FALSE_;
+	orth1 = FALSE_;
+	orth2 = FALSE_;
+	j = *k + 1;
+	ipj = 1;
+	irj = ipj + *n;
+	ivj = irj + *n;
+    }
+
+/*     %-------------------------------------------------%   
+       | When in reverse communication mode one of:      |   
+       | STEP3, STEP4, ORTH1, ORTH2, RSTART              |   
+       | will be .true. when ....                        |   
+       | STEP3: return from computing OP*v_{j}.          |   
+       | STEP4: return from computing B-norm of OP*v_{j} |   
+       | ORTH1: return from computing B-norm of r_{j+1}  |   
+       | ORTH2: return from computing B-norm of          |   
+       |        correction to the residual vector.       |   
+       | RSTART: return from OP computations needed by   |   
+       |         dgetv0.                                 |   
+       %-------------------------------------------------% */
+
+    if (step3) {
+	goto L50;
+    }
+    if (step4) {
+	goto L60;
+    }
+    if (orth1) {
+	goto L70;
+    }
+    if (orth2) {
+	goto L90;
+    }
+    if (rstart) {
+	goto L30;
+    }
+
+/*     %-----------------------------%   
+       | Else this is the first step |   
+       %-----------------------------%   
+
+       %--------------------------------------------------------------%   
+       |                                                              |   
+       |        A R N O L D I     I T E R A T I O N     L O O P       |   
+       |                                                              |   
+       | Note:  B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) |   
+       %--------------------------------------------------------------% */
+L1000:
+
+    if (msglvl > 1) {
+	igraphivout_(&logfil, &c__1, &j, &ndigit, "_naitr: generating Arnoldi vect"
+		"or number", (ftnlen)40);
+	igraphdvout_(&logfil, &c__1, rnorm, &ndigit, "_naitr: B-norm of the curren"
+		"t residual is", (ftnlen)41);
+    }
+
+/*        %---------------------------------------------------%   
+          | STEP 1: Check if the B norm of j-th residual      |   
+          | vector is zero. Equivalent to determing whether   |   
+          | an exact j-step Arnoldi factorization is present. |   
+          %---------------------------------------------------% */
+
+    betaj = *rnorm;
+    if (*rnorm > 0.) {
+	goto L40;
+    }
+
+/*           %---------------------------------------------------%   
+             | Invariant subspace found, generate a new starting |   
+             | vector which is orthogonal to the current Arnoldi |   
+             | basis and continue the iteration.                 |   
+             %---------------------------------------------------% */
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, &j, &ndigit, "_naitr: ****** RESTART AT STEP "
+		"******", (ftnlen)37);
+    }
+
+/*           %---------------------------------------------%   
+             | ITRY is the loop variable that controls the |   
+             | maximum amount of times that a restart is   |   
+             | attempted. NRSTRT is used by stat.h         |   
+             %---------------------------------------------% */
+
+    betaj = 0.;
+    ++nrstrt;
+    itry = 1;
+L20:
+    rstart = TRUE_;
+    *ido = 0;
+L30:
+
+/*           %--------------------------------------%   
+             | If in reverse communication mode and |   
+             | RSTART = .true. flow returns here.   |   
+             %--------------------------------------% */
+
+    igraphdgetv0_(ido, bmat, &itry, &c_false, n, &j, &v[v_offset], ldv, &resid[1], 
+	    rnorm, &ipntr[1], &workd[1], &ierr);
+    if (*ido != 99) {
+	goto L9000;
+    }
+    if (ierr < 0) {
+	++itry;
+	if (itry <= 3) {
+	    goto L20;
+	}
+
+/*              %------------------------------------------------%   
+                | Give up after several restart attempts.        |   
+                | Set INFO to the size of the invariant subspace |   
+                | which spans OP and exit.                       |   
+                %------------------------------------------------% */
+
+	*info = j - 1;
+	igraphsecond_(&t1);
+	tnaitr += t1 - t0;
+	*ido = 99;
+	goto L9000;
+    }
+
+L40:
+
+/*        %---------------------------------------------------------%   
+          | STEP 2:  v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm  |   
+          | Note that p_{j} = B*r_{j-1}. In order to avoid overflow |   
+          | when reciprocating a small RNORM, test against lower    |   
+          | machine bound.                                          |   
+          %---------------------------------------------------------% */
+
+    igraphdcopy_(n, &resid[1], &c__1, &v[j * v_dim1 + 1], &c__1);
+    if (*rnorm >= unfl) {
+	temp1 = 1. / *rnorm;
+	igraphdscal_(n, &temp1, &v[j * v_dim1 + 1], &c__1);
+	igraphdscal_(n, &temp1, &workd[ipj], &c__1);
+    } else {
+
+/*            %-----------------------------------------%   
+              | To scale both v_{j} and p_{j} carefully |   
+              | use LAPACK routine SLASCL               |   
+              %-----------------------------------------% */
+
+	igraphdlascl_("General", &i__, &i__, rnorm, &c_b25, n, &c__1, &v[j * v_dim1 
+		+ 1], n, &infol);
+	igraphdlascl_("General", &i__, &i__, rnorm, &c_b25, n, &c__1, &workd[ipj], 
+		n, &infol);
+    }
+
+/*        %------------------------------------------------------%   
+          | STEP 3:  r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} |   
+          | Note that this is not quite yet r_{j}. See STEP 4    |   
+          %------------------------------------------------------% */
+
+    step3 = TRUE_;
+    ++nopx;
+    igraphsecond_(&t2);
+    igraphdcopy_(n, &v[j * v_dim1 + 1], &c__1, &workd[ivj], &c__1);
+    ipntr[1] = ivj;
+    ipntr[2] = irj;
+    ipntr[3] = ipj;
+    *ido = 1;
+
+/*        %-----------------------------------%   
+          | Exit in order to compute OP*v_{j} |   
+          %-----------------------------------% */
+
+    goto L9000;
+L50:
+
+/*        %----------------------------------%   
+          | Back from reverse communication; |   
+          | WORKD(IRJ:IRJ+N-1) := OP*v_{j}   |   
+          | if step3 = .true.                |   
+          %----------------------------------% */
+
+    igraphsecond_(&t3);
+    tmvopx += t3 - t2;
+    step3 = FALSE_;
+
+/*        %------------------------------------------%   
+          | Put another copy of OP*v_{j} into RESID. |   
+          %------------------------------------------% */
+
+    igraphdcopy_(n, &workd[irj], &c__1, &resid[1], &c__1);
+
+/*        %---------------------------------------%   
+          | STEP 4:  Finish extending the Arnoldi |   
+          |          factorization to length j.   |   
+          %---------------------------------------% */
+
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	step4 = TRUE_;
+	ipntr[1] = irj;
+	ipntr[2] = ipj;
+	*ido = 2;
+
+/*           %-------------------------------------%   
+             | Exit in order to compute B*OP*v_{j} |   
+             %-------------------------------------% */
+
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+    }
+L60:
+
+/*        %----------------------------------%   
+          | Back from reverse communication; |   
+          | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j} |   
+          | if step4 = .true.                |   
+          %----------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+    step4 = FALSE_;
+
+/*        %-------------------------------------%   
+          | The following is needed for STEP 5. |   
+          | Compute the B-norm of OP*v_{j}.     |   
+          %-------------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	wnorm = igraphddot_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+	wnorm = sqrt((abs(wnorm)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	wnorm = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+
+/*        %-----------------------------------------%   
+          | Compute the j-th residual corresponding |   
+          | to the j step factorization.            |   
+          | Use Classical Gram Schmidt and compute: |   
+          | w_{j} <-  V_{j}^T * B * OP * v_{j}      |   
+          | r_{j} <-  OP*v_{j} - V_{j} * w_{j}      |   
+          %-----------------------------------------%   
+
+
+          %------------------------------------------%   
+          | Compute the j Fourier coefficients w_{j} |   
+          | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}.  |   
+          %------------------------------------------% */
+
+    igraphdgemv_("T", n, &j, &c_b25, &v[v_offset], ldv, &workd[ipj], &c__1, &c_b47, 
+	    &h__[j * h_dim1 + 1], &c__1);
+
+/*        %--------------------------------------%   
+          | Orthogonalize r_{j} against V_{j}.   |   
+          | RESID contains OP*v_{j}. See STEP 3. |   
+          %--------------------------------------% */
+
+    igraphdgemv_("N", n, &j, &c_b50, &v[v_offset], ldv, &h__[j * h_dim1 + 1], &c__1,
+	     &c_b25, &resid[1], &c__1);
+
+    if (j > 1) {
+	h__[j + (j - 1) * h_dim1] = betaj;
+    }
+
+    igraphsecond_(&t4);
+
+    orth1 = TRUE_;
+
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	igraphdcopy_(n, &resid[1], &c__1, &workd[irj], &c__1);
+	ipntr[1] = irj;
+	ipntr[2] = ipj;
+	*ido = 2;
+
+/*           %----------------------------------%   
+             | Exit in order to compute B*r_{j} |   
+             %----------------------------------% */
+
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+    }
+L70:
+
+/*        %---------------------------------------------------%   
+          | Back from reverse communication if ORTH1 = .true. |   
+          | WORKD(IPJ:IPJ+N-1) := B*r_{j}.                    |   
+          %---------------------------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+    orth1 = FALSE_;
+
+/*        %------------------------------%   
+          | Compute the B-norm of r_{j}. |   
+          %------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	*rnorm = igraphddot_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+	*rnorm = sqrt((abs(*rnorm)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	*rnorm = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+
+/*        %-----------------------------------------------------------%   
+          | STEP 5: Re-orthogonalization / Iterative refinement phase |   
+          | Maximum NITER_ITREF tries.                                |   
+          |                                                           |   
+          |          s      = V_{j}^T * B * r_{j}                     |   
+          |          r_{j}  = r_{j} - V_{j}*s                         |   
+          |          alphaj = alphaj + s_{j}                          |   
+          |                                                           |   
+          | The stopping criteria used for iterative refinement is    |   
+          | discussed in Parlett's book SEP, page 107 and in Gragg &  |   
+          | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990.         |   
+          | Determine if we need to correct the residual. The goal is |   
+          | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} ||  |   
+          | The following test determines whether the sine of the     |   
+          | angle between  OP*x and the computed residual is less     |   
+          | than or equal to 0.717.                                   |   
+          %-----------------------------------------------------------% */
+
+    if (*rnorm > wnorm * .717f) {
+	goto L100;
+    }
+    iter = 0;
+    ++nrorth;
+
+/*        %---------------------------------------------------%   
+          | Enter the Iterative refinement phase. If further  |   
+          | refinement is necessary, loop back here. The loop |   
+          | variable is ITER. Perform a step of Classical     |   
+          | Gram-Schmidt using all the Arnoldi vectors V_{j}  |   
+          %---------------------------------------------------% */
+
+L80:
+
+    if (msglvl > 2) {
+	xtemp[0] = wnorm;
+	xtemp[1] = *rnorm;
+	igraphdvout_(&logfil, &c__2, xtemp, &ndigit, "_naitr: re-orthonalization; "
+		"wnorm and rnorm are", (ftnlen)47);
+	igraphdvout_(&logfil, &j, &h__[j * h_dim1 + 1], &ndigit, "_naitr: j-th col"
+		"umn of H", (ftnlen)24);
+    }
+
+/*        %----------------------------------------------------%   
+          | Compute V_{j}^T * B * r_{j}.                       |   
+          | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). |   
+          %----------------------------------------------------% */
+
+    igraphdgemv_("T", n, &j, &c_b25, &v[v_offset], ldv, &workd[ipj], &c__1, &c_b47, 
+	    &workd[irj], &c__1);
+
+/*        %---------------------------------------------%   
+          | Compute the correction to the residual:     |   
+          | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1). |   
+          | The correction to H is v(:,1:J)*H(1:J,1:J)  |   
+          | + v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j.         |   
+          %---------------------------------------------% */
+
+    igraphdgemv_("N", n, &j, &c_b50, &v[v_offset], ldv, &workd[irj], &c__1, &c_b25, 
+	    &resid[1], &c__1);
+    igraphdaxpy_(&j, &c_b25, &workd[irj], &c__1, &h__[j * h_dim1 + 1], &c__1);
+
+    orth2 = TRUE_;
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	igraphdcopy_(n, &resid[1], &c__1, &workd[irj], &c__1);
+	ipntr[1] = irj;
+	ipntr[2] = ipj;
+	*ido = 2;
+
+/*           %-----------------------------------%   
+             | Exit in order to compute B*r_{j}. |   
+             | r_{j} is the corrected residual.  |   
+             %-----------------------------------% */
+
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+    }
+L90:
+
+/*        %---------------------------------------------------%   
+          | Back from reverse communication if ORTH2 = .true. |   
+          %---------------------------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+/*        %-----------------------------------------------------%   
+          | Compute the B-norm of the corrected residual r_{j}. |   
+          %-----------------------------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	rnorm1 = igraphddot_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+	rnorm1 = sqrt((abs(rnorm1)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	rnorm1 = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+
+    if (msglvl > 0 && iter > 0) {
+	igraphivout_(&logfil, &c__1, &j, &ndigit, "_naitr: Iterative refinement fo"
+		"r Arnoldi residual", (ftnlen)49);
+	if (msglvl > 2) {
+	    xtemp[0] = *rnorm;
+	    xtemp[1] = rnorm1;
+	    igraphdvout_(&logfil, &c__2, xtemp, &ndigit, "_naitr: iterative refine"
+		    "ment ; rnorm and rnorm1 are", (ftnlen)51);
+	}
+    }
+
+/*        %-----------------------------------------%   
+          | Determine if we need to perform another |   
+          | step of re-orthogonalization.           |   
+          %-----------------------------------------% */
+
+    if (rnorm1 > *rnorm * .717f) {
+
+/*           %---------------------------------------%   
+             | No need for further refinement.       |   
+             | The cosine of the angle between the   |   
+             | corrected residual vector and the old |   
+             | residual vector is greater than 0.717 |   
+             | In other words the corrected residual |   
+             | and the old residual vector share an  |   
+             | angle of less than arcCOS(0.717)      |   
+             %---------------------------------------% */
+
+	*rnorm = rnorm1;
+
+    } else {
+
+/*           %-------------------------------------------%   
+             | Another step of iterative refinement step |   
+             | is required. NITREF is used by stat.h     |   
+             %-------------------------------------------% */
+
+	++nitref;
+	*rnorm = rnorm1;
+	++iter;
+	if (iter <= 1) {
+	    goto L80;
+	}
+
+/*           %-------------------------------------------------%   
+             | Otherwise RESID is numerically in the span of V |   
+             %-------------------------------------------------% */
+
+	i__1 = *n;
+	for (jj = 1; jj <= i__1; ++jj) {
+	    resid[jj] = 0.;
+/* L95: */
+	}
+	*rnorm = 0.;
+    }
+
+/*        %----------------------------------------------%   
+          | Branch here directly if iterative refinement |   
+          | wasn't necessary or after at most NITER_REF  |   
+          | steps of iterative refinement.               |   
+          %----------------------------------------------% */
+
+L100:
+
+    rstart = FALSE_;
+    orth2 = FALSE_;
+
+    igraphsecond_(&t5);
+    titref += t5 - t4;
+
+/*        %------------------------------------%   
+          | STEP 6: Update  j = j+1;  Continue |   
+          %------------------------------------% */
+
+    ++j;
+    if (j > *k + *np) {
+	igraphsecond_(&t1);
+	tnaitr += t1 - t0;
+	*ido = 99;
+	i__1 = *k + *np - 1;
+	for (i__ = max(1,*k); i__ <= i__1; ++i__) {
+
+/*              %--------------------------------------------%   
+                | Check for splitting and deflation.         |   
+                | Use a standard test as in the QR algorithm |   
+                | REFERENCE: LAPACK subroutine dlahqr        |   
+                %--------------------------------------------% */
+
+	    tst1 = (d__1 = h__[i__ + i__ * h_dim1], abs(d__1)) + (d__2 = h__[
+		    i__ + 1 + (i__ + 1) * h_dim1], abs(d__2));
+	    if (tst1 == 0.) {
+		i__2 = *k + *np;
+		tst1 = igraphdlanhs_("1", &i__2, &h__[h_offset], ldh, &workd[*n + 1]
+			);
+	    }
+/* Computing MAX */
+	    d__2 = ulp * tst1;
+	    if ((d__1 = h__[i__ + 1 + i__ * h_dim1], abs(d__1)) <= max(d__2,
+		    smlnum)) {
+		h__[i__ + 1 + i__ * h_dim1] = 0.;
+	    }
+/* L110: */
+	}
+
+	if (msglvl > 2) {
+	    i__1 = *k + *np;
+	    i__2 = *k + *np;
+	    igraphdmout_(&logfil, &i__1, &i__2, &h__[h_offset], ldh, &ndigit, "_na"
+		    "itr: Final upper Hessenberg matrix H of order K+NP", (
+		    ftnlen)53);
+	}
+
+	goto L9000;
+    }
+
+/*        %--------------------------------------------------------%   
+          | Loop back to extend the factorization by another step. |   
+          %--------------------------------------------------------% */
+
+    goto L1000;
+
+/*     %---------------------------------------------------------------%   
+       |                                                               |   
+       |  E N D     O F     M A I N     I T E R A T I O N     L O O P  |   
+       |                                                               |   
+       %---------------------------------------------------------------% */
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dnaitr |   
+       %---------------% */
+
+} /* igraphdnaitr_ */
+
diff --git a/igraph/src/dnapps.c b/igraph/src/dnapps.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dnapps.c
@@ -0,0 +1,795 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b5 = 0.;
+static doublereal c_b6 = 1.;
+static integer c__1 = 1;
+static doublereal c_b43 = -1.;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dnapps   
+
+   \Description:   
+    Given the Arnoldi factorization   
+
+       A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T,   
+
+    apply NP implicit shifts resulting in   
+
+       A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q   
+
+    where Q is an orthogonal matrix which is the product of rotations   
+    and reflections resulting from the NP bulge chage sweeps.   
+    The updated Arnoldi factorization becomes:   
+
+       A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T.   
+
+   \Usage:   
+    call dnapps   
+       ( N, KEV, NP, SHIFTR, SHIFTI, V, LDV, H, LDH, RESID, Q, LDQ,   
+         WORKL, WORKD )   
+
+   \Arguments   
+    N       Integer.  (INPUT)   
+            Problem size, i.e. size of matrix A.   
+
+    KEV     Integer.  (INPUT/OUTPUT)   
+            KEV+NP is the size of the input matrix H.   
+            KEV is the size of the updated matrix HNEW.  KEV is only   
+            updated on ouput when fewer than NP shifts are applied in   
+            order to keep the conjugate pair together.   
+
+    NP      Integer.  (INPUT)   
+            Number of implicit shifts to be applied.   
+
+    SHIFTR, Double precision array of length NP.  (INPUT)   
+    SHIFTI  Real and imaginary part of the shifts to be applied.   
+            Upon, entry to dnapps, the shifts must be sorted so that the   
+            conjugate pairs are in consecutive locations.   
+
+    V       Double precision N by (KEV+NP) array.  (INPUT/OUTPUT)   
+            On INPUT, V contains the current KEV+NP Arnoldi vectors.   
+            On OUTPUT, V contains the updated KEV Arnoldi vectors   
+            in the first KEV columns of V.   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling   
+            program.   
+
+    H       Double precision (KEV+NP) by (KEV+NP) array.  (INPUT/OUTPUT)   
+            On INPUT, H contains the current KEV+NP by KEV+NP upper   
+            Hessenber matrix of the Arnoldi factorization.   
+            On OUTPUT, H contains the updated KEV by KEV upper Hessenberg   
+            matrix in the KEV leading submatrix.   
+
+    LDH     Integer.  (INPUT)   
+            Leading dimension of H exactly as declared in the calling   
+            program.   
+
+    RESID   Double precision array of length N.  (INPUT/OUTPUT)   
+            On INPUT, RESID contains the the residual vector r_{k+p}.   
+            On OUTPUT, RESID is the update residual vector rnew_{k}   
+            in the first KEV locations.   
+
+    Q       Double precision KEV+NP by KEV+NP work array.  (WORKSPACE)   
+            Work array used to accumulate the rotations and reflections   
+            during the bulge chase sweep.   
+
+    LDQ     Integer.  (INPUT)   
+            Leading dimension of Q exactly as declared in the calling   
+            program.   
+
+    WORKL   Double precision work array of length (KEV+NP).  (WORKSPACE)   
+            Private (replicated) array on each PE or array allocated on   
+            the front end.   
+
+    WORKD   Double precision work array of length 2*N.  (WORKSPACE)   
+            Distributed array used in the application of the accumulated   
+            orthogonal matrix Q.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+
+   \Routines called:   
+       ivout   ARPACK utility routine that prints integers.   
+       second  ARPACK utility routine for timing.   
+       dmout   ARPACK utility routine that prints matrices.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlabad  LAPACK routine that computes machine constants.   
+       dlacpy  LAPACK matrix copy routine.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlanhs  LAPACK routine that computes various norms of a matrix.   
+       dlapy2  LAPACK routine to compute sqrt(x**2+y**2) carefully.   
+       dlarf   LAPACK routine that applies Householder reflection to   
+               a matrix.   
+       dlarfg  LAPACK Householder reflection construction routine.   
+       dlartg  LAPACK Givens rotation construction routine.   
+       dlaset  LAPACK matrix initialization routine.   
+       dgemv   Level 2 BLAS routine for matrix vector multiplication.   
+       daxpy   Level 1 BLAS that computes a vector triad.   
+       dcopy   Level 1 BLAS that copies one vector to another .   
+       dscal   Level 1 BLAS that scales a vector.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/92: Version ' 2.1'   
+
+   \SCCS Information: @(#)   
+   FILE: napps.F   SID: 2.3   DATE OF SID: 4/20/96   RELEASE: 2   
+
+   \Remarks   
+    1. In this version, each shift is applied to all the sublocks of   
+       the Hessenberg matrix H and not just to the submatrix that it   
+       comes from. Deflation as in LAPACK routine dlahqr (QR algorithm   
+       for upper Hessenberg matrices ) is used.   
+       The subdiagonals of H are enforced to be non-negative.   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdnapps_(integer *n, integer *kev, integer *np, 
+	doublereal *shiftr, doublereal *shifti, doublereal *v, integer *ldv, 
+	doublereal *h__, integer *ldh, doublereal *resid, doublereal *q, 
+	integer *ldq, doublereal *workl, doublereal *workd)
+{
+    /* Initialized data */
+
+    IGRAPH_F77_SAVE logical first = TRUE_;
+
+    /* System generated locals */
+    integer h_dim1, h_offset, v_dim1, v_offset, q_dim1, q_offset, i__1, i__2, 
+	    i__3, i__4;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    doublereal c__, f, g;
+    integer i__, j;
+    doublereal r__, s, t, u[3];
+    real t0, t1;
+    doublereal h11, h12, h21, h22, h32;
+    integer jj, ir, nr;
+    doublereal tau;
+    IGRAPH_F77_SAVE doublereal ulp;
+    doublereal tst1;
+    integer iend;
+    IGRAPH_F77_SAVE doublereal unfl, ovfl;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *), igraphdlarf_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *);
+    logical cconj;
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *), igraphdcopy_(integer *, 
+	    doublereal *, integer *, doublereal *, integer *), igraphdaxpy_(integer 
+	    *, doublereal *, doublereal *, integer *, doublereal *, integer *)
+	    , igraphdmout_(integer *, integer *, integer *, doublereal *, integer *,
+	     integer *, char *, ftnlen), igraphdvout_(integer *, integer *, 
+	    doublereal *, integer *, char *, ftnlen), igraphivout_(integer *, 
+	    integer *, integer *, integer *, char *, ftnlen);
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlabad_(doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlarfg_(integer *, doublereal *, doublereal *,
+	     integer *, doublereal *);
+    doublereal sigmai;
+    extern doublereal igraphdlanhs_(char *, integer *, doublereal *, integer *, 
+	    doublereal *);
+    extern /* Subroutine */ int igraphsecond_(real *), igraphdlacpy_(char *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *), igraphdlaset_(char *, integer *, integer *, doublereal *, 
+	    doublereal *, doublereal *, integer *), igraphdlartg_(
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *);
+    integer logfil, ndigit;
+    doublereal sigmar;
+    integer mnapps = 0, msglvl;
+    real tnapps = 0.;
+    integer istart;
+    IGRAPH_F77_SAVE doublereal smlnum;
+    integer kplusp;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %------------------------%   
+       | Local Scalars & Arrays |   
+       %------------------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %----------------------%   
+       | Intrinsics Functions |   
+       %----------------------%   
+
+
+       %----------------%   
+       | Data statments |   
+       %----------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    --workl;
+    --shifti;
+    --shiftr;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    q_dim1 = *ldq;
+    q_offset = 1 + q_dim1;
+    q -= q_offset;
+
+    /* Function Body   
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------% */
+
+    if (first) {
+
+/*        %-----------------------------------------------%   
+          | Set machine-dependent constants for the       |   
+          | stopping criterion. If norm(H) <= sqrt(OVFL), |   
+          | overflow should not occur.                    |   
+          | REFERENCE: LAPACK subroutine dlahqr           |   
+          %-----------------------------------------------% */
+
+	unfl = igraphdlamch_("safe minimum");
+	ovfl = 1. / unfl;
+	igraphdlabad_(&unfl, &ovfl);
+	ulp = igraphdlamch_("precision");
+	smlnum = unfl * (*n / ulp);
+	first = FALSE_;
+    }
+
+/*     %-------------------------------%   
+       | Initialize timing statistics  |   
+       | & message level for debugging |   
+       %-------------------------------% */
+
+    igraphsecond_(&t0);
+    msglvl = mnapps;
+    kplusp = *kev + *np;
+
+/*     %--------------------------------------------%   
+       | Initialize Q to the identity to accumulate |   
+       | the rotations and reflections              |   
+       %--------------------------------------------% */
+
+    igraphdlaset_("All", &kplusp, &kplusp, &c_b5, &c_b6, &q[q_offset], ldq);
+
+/*     %----------------------------------------------%   
+       | Quick return if there are no shifts to apply |   
+       %----------------------------------------------% */
+
+    if (*np == 0) {
+	goto L9000;
+    }
+
+/*     %----------------------------------------------%   
+       | Chase the bulge with the application of each |   
+       | implicit shift. Each shift is applied to the |   
+       | whole matrix including each block.           |   
+       %----------------------------------------------% */
+
+    cconj = FALSE_;
+    i__1 = *np;
+    for (jj = 1; jj <= i__1; ++jj) {
+	sigmar = shiftr[jj];
+	sigmai = shifti[jj];
+
+	if (msglvl > 2) {
+	    igraphivout_(&logfil, &c__1, &jj, &ndigit, "_napps: shift number.", (
+		    ftnlen)21);
+	    igraphdvout_(&logfil, &c__1, &sigmar, &ndigit, "_napps: The real part "
+		    "of the shift ", (ftnlen)35);
+	    igraphdvout_(&logfil, &c__1, &sigmai, &ndigit, "_napps: The imaginary "
+		    "part of the shift ", (ftnlen)40);
+	}
+
+/*        %-------------------------------------------------%   
+          | The following set of conditionals is necessary  |   
+          | in order that complex conjugate pairs of shifts |   
+          | are applied together or not at all.             |   
+          %-------------------------------------------------% */
+
+	if (cconj) {
+
+/*           %-----------------------------------------%   
+             | cconj = .true. means the previous shift |   
+             | had non-zero imaginary part.            |   
+             %-----------------------------------------% */
+
+	    cconj = FALSE_;
+	    goto L110;
+	} else if (jj < *np && abs(sigmai) > 0.) {
+
+/*           %------------------------------------%   
+             | Start of a complex conjugate pair. |   
+             %------------------------------------% */
+
+	    cconj = TRUE_;
+	} else if (jj == *np && abs(sigmai) > 0.) {
+
+/*           %----------------------------------------------%   
+             | The last shift has a nonzero imaginary part. |   
+             | Don't apply it; thus the order of the        |   
+             | compressed H is order KEV+1 since only np-1  |   
+             | were applied.                                |   
+             %----------------------------------------------% */
+
+	    ++(*kev);
+	    goto L110;
+	}
+	istart = 1;
+L20:
+
+/*        %--------------------------------------------------%   
+          | if sigmai = 0 then                               |   
+          |    Apply the jj-th shift ...                     |   
+          | else                                             |   
+          |    Apply the jj-th and (jj+1)-th together ...    |   
+          |    (Note that jj < np at this point in the code) |   
+          | end                                              |   
+          | to the current block of H. The next do loop      |   
+          | determines the current block ;                   |   
+          %--------------------------------------------------% */
+
+	i__2 = kplusp - 1;
+	for (i__ = istart; i__ <= i__2; ++i__) {
+
+/*           %----------------------------------------%   
+             | Check for splitting and deflation. Use |   
+             | a standard test as in the QR algorithm |   
+             | REFERENCE: LAPACK subroutine dlahqr    |   
+             %----------------------------------------% */
+
+	    tst1 = (d__1 = h__[i__ + i__ * h_dim1], abs(d__1)) + (d__2 = h__[
+		    i__ + 1 + (i__ + 1) * h_dim1], abs(d__2));
+	    if (tst1 == 0.) {
+		i__3 = kplusp - jj + 1;
+		tst1 = igraphdlanhs_("1", &i__3, &h__[h_offset], ldh, &workl[1]);
+	    }
+/* Computing MAX */
+	    d__2 = ulp * tst1;
+	    if ((d__1 = h__[i__ + 1 + i__ * h_dim1], abs(d__1)) <= max(d__2,
+		    smlnum)) {
+		if (msglvl > 0) {
+		    igraphivout_(&logfil, &c__1, &i__, &ndigit, "_napps: matrix sp"
+			    "litting at row/column no.", (ftnlen)42);
+		    igraphivout_(&logfil, &c__1, &jj, &ndigit, "_napps: matrix spl"
+			    "itting with shift number.", (ftnlen)43);
+		    igraphdvout_(&logfil, &c__1, &h__[i__ + 1 + i__ * h_dim1], &
+			    ndigit, "_napps: off diagonal element.", (ftnlen)
+			    29);
+		}
+		iend = i__;
+		h__[i__ + 1 + i__ * h_dim1] = 0.;
+		goto L40;
+	    }
+/* L30: */
+	}
+	iend = kplusp;
+L40:
+
+	if (msglvl > 2) {
+	    igraphivout_(&logfil, &c__1, &istart, &ndigit, "_napps: Start of curre"
+		    "nt block ", (ftnlen)31);
+	    igraphivout_(&logfil, &c__1, &iend, &ndigit, "_napps: End of current b"
+		    "lock ", (ftnlen)29);
+	}
+
+/*        %------------------------------------------------%   
+          | No reason to apply a shift to block of order 1 |   
+          %------------------------------------------------% */
+
+	if (istart == iend) {
+	    goto L100;
+	}
+
+/*        %------------------------------------------------------%   
+          | If istart + 1 = iend then no reason to apply a       |   
+          | complex conjugate pair of shifts on a 2 by 2 matrix. |   
+          %------------------------------------------------------% */
+
+	if (istart + 1 == iend && abs(sigmai) > 0.) {
+	    goto L100;
+	}
+
+	h11 = h__[istart + istart * h_dim1];
+	h21 = h__[istart + 1 + istart * h_dim1];
+	if (abs(sigmai) <= 0.) {
+
+/*           %---------------------------------------------%   
+             | Real-valued shift ==> apply single shift QR |   
+             %---------------------------------------------% */
+
+	    f = h11 - sigmar;
+	    g = h21;
+
+	    i__2 = iend - 1;
+	    for (i__ = istart; i__ <= i__2; ++i__) {
+
+/*              %-----------------------------------------------------%   
+                | Contruct the plane rotation G to zero out the bulge |   
+                %-----------------------------------------------------% */
+
+		igraphdlartg_(&f, &g, &c__, &s, &r__);
+		if (i__ > istart) {
+
+/*                 %-------------------------------------------%   
+                   | The following ensures that h(1:iend-1,1), |   
+                   | the first iend-2 off diagonal of elements |   
+                   | H, remain non negative.                   |   
+                   %-------------------------------------------% */
+
+		    if (r__ < 0.) {
+			r__ = -r__;
+			c__ = -c__;
+			s = -s;
+		    }
+		    h__[i__ + (i__ - 1) * h_dim1] = r__;
+		    h__[i__ + 1 + (i__ - 1) * h_dim1] = 0.;
+		}
+
+/*              %---------------------------------------------%   
+                | Apply rotation to the left of H;  H <- G'*H |   
+                %---------------------------------------------% */
+
+		i__3 = kplusp;
+		for (j = i__; j <= i__3; ++j) {
+		    t = c__ * h__[i__ + j * h_dim1] + s * h__[i__ + 1 + j * 
+			    h_dim1];
+		    h__[i__ + 1 + j * h_dim1] = -s * h__[i__ + j * h_dim1] + 
+			    c__ * h__[i__ + 1 + j * h_dim1];
+		    h__[i__ + j * h_dim1] = t;
+/* L50: */
+		}
+
+/*              %---------------------------------------------%   
+                | Apply rotation to the right of H;  H <- H*G |   
+                %---------------------------------------------%   
+
+   Computing MIN */
+		i__4 = i__ + 2;
+		i__3 = min(i__4,iend);
+		for (j = 1; j <= i__3; ++j) {
+		    t = c__ * h__[j + i__ * h_dim1] + s * h__[j + (i__ + 1) * 
+			    h_dim1];
+		    h__[j + (i__ + 1) * h_dim1] = -s * h__[j + i__ * h_dim1] 
+			    + c__ * h__[j + (i__ + 1) * h_dim1];
+		    h__[j + i__ * h_dim1] = t;
+/* L60: */
+		}
+
+/*              %----------------------------------------------------%   
+                | Accumulate the rotation in the matrix Q;  Q <- Q*G |   
+                %----------------------------------------------------%   
+
+   Computing MIN */
+		i__4 = j + jj;
+		i__3 = min(i__4,kplusp);
+		for (j = 1; j <= i__3; ++j) {
+		    t = c__ * q[j + i__ * q_dim1] + s * q[j + (i__ + 1) * 
+			    q_dim1];
+		    q[j + (i__ + 1) * q_dim1] = -s * q[j + i__ * q_dim1] + 
+			    c__ * q[j + (i__ + 1) * q_dim1];
+		    q[j + i__ * q_dim1] = t;
+/* L70: */
+		}
+
+/*              %---------------------------%   
+                | Prepare for next rotation |   
+                %---------------------------% */
+
+		if (i__ < iend - 1) {
+		    f = h__[i__ + 1 + i__ * h_dim1];
+		    g = h__[i__ + 2 + i__ * h_dim1];
+		}
+/* L80: */
+	    }
+
+/*           %-----------------------------------%   
+             | Finished applying the real shift. |   
+             %-----------------------------------% */
+
+	} else {
+
+/*           %----------------------------------------------------%   
+             | Complex conjugate shifts ==> apply double shift QR |   
+             %----------------------------------------------------% */
+
+	    h12 = h__[istart + (istart + 1) * h_dim1];
+	    h22 = h__[istart + 1 + (istart + 1) * h_dim1];
+	    h32 = h__[istart + 2 + (istart + 1) * h_dim1];
+
+/*           %---------------------------------------------------------%   
+             | Compute 1st column of (H - shift*I)*(H - conj(shift)*I) |   
+             %---------------------------------------------------------% */
+
+	    s = sigmar * 2.f;
+	    t = igraphdlapy2_(&sigmar, &sigmai);
+	    u[0] = (h11 * (h11 - s) + t * t) / h21 + h12;
+	    u[1] = h11 + h22 - s;
+	    u[2] = h32;
+
+	    i__2 = iend - 1;
+	    for (i__ = istart; i__ <= i__2; ++i__) {
+
+/* Computing MIN */
+		i__3 = 3, i__4 = iend - i__ + 1;
+		nr = min(i__3,i__4);
+
+/*              %-----------------------------------------------------%   
+                | Construct Householder reflector G to zero out u(1). |   
+                | G is of the form I - tau*( 1 u )' * ( 1 u' ).       |   
+                %-----------------------------------------------------% */
+
+		igraphdlarfg_(&nr, u, &u[1], &c__1, &tau);
+
+		if (i__ > istart) {
+		    h__[i__ + (i__ - 1) * h_dim1] = u[0];
+		    h__[i__ + 1 + (i__ - 1) * h_dim1] = 0.;
+		    if (i__ < iend - 1) {
+			h__[i__ + 2 + (i__ - 1) * h_dim1] = 0.;
+		    }
+		}
+		u[0] = 1.;
+
+/*              %--------------------------------------%   
+                | Apply the reflector to the left of H |   
+                %--------------------------------------% */
+
+		i__3 = kplusp - i__ + 1;
+		igraphdlarf_("Left", &nr, &i__3, u, &c__1, &tau, &h__[i__ + i__ * 
+			h_dim1], ldh, &workl[1]);
+
+/*              %---------------------------------------%   
+                | Apply the reflector to the right of H |   
+                %---------------------------------------%   
+
+   Computing MIN */
+		i__3 = i__ + 3;
+		ir = min(i__3,iend);
+		igraphdlarf_("Right", &ir, &nr, u, &c__1, &tau, &h__[i__ * h_dim1 + 
+			1], ldh, &workl[1]);
+
+/*              %-----------------------------------------------------%   
+                | Accumulate the reflector in the matrix Q;  Q <- Q*G |   
+                %-----------------------------------------------------% */
+
+		igraphdlarf_("Right", &kplusp, &nr, u, &c__1, &tau, &q[i__ * q_dim1 
+			+ 1], ldq, &workl[1]);
+
+/*              %----------------------------%   
+                | Prepare for next reflector |   
+                %----------------------------% */
+
+		if (i__ < iend - 1) {
+		    u[0] = h__[i__ + 1 + i__ * h_dim1];
+		    u[1] = h__[i__ + 2 + i__ * h_dim1];
+		    if (i__ < iend - 2) {
+			u[2] = h__[i__ + 3 + i__ * h_dim1];
+		    }
+		}
+
+/* L90: */
+	    }
+
+/*           %--------------------------------------------%   
+             | Finished applying a complex pair of shifts |   
+             | to the current block                       |   
+             %--------------------------------------------% */
+
+	}
+
+L100:
+
+/*        %---------------------------------------------------------%   
+          | Apply the same shift to the next block if there is any. |   
+          %---------------------------------------------------------% */
+
+	istart = iend + 1;
+	if (iend < kplusp) {
+	    goto L20;
+	}
+
+/*        %---------------------------------------------%   
+          | Loop back to the top to get the next shift. |   
+          %---------------------------------------------% */
+
+L110:
+	;
+    }
+
+/*     %--------------------------------------------------%   
+       | Perform a similarity transformation that makes   |   
+       | sure that H will have non negative sub diagonals |   
+       %--------------------------------------------------% */
+
+    i__1 = *kev;
+    for (j = 1; j <= i__1; ++j) {
+	if (h__[j + 1 + j * h_dim1] < 0.) {
+	    i__2 = kplusp - j + 1;
+	    igraphdscal_(&i__2, &c_b43, &h__[j + 1 + j * h_dim1], ldh);
+/* Computing MIN */
+	    i__3 = j + 2;
+	    i__2 = min(i__3,kplusp);
+	    igraphdscal_(&i__2, &c_b43, &h__[(j + 1) * h_dim1 + 1], &c__1);
+/* Computing MIN */
+	    i__3 = j + *np + 1;
+	    i__2 = min(i__3,kplusp);
+	    igraphdscal_(&i__2, &c_b43, &q[(j + 1) * q_dim1 + 1], &c__1);
+	}
+/* L120: */
+    }
+
+    i__1 = *kev;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+
+/*        %--------------------------------------------%   
+          | Final check for splitting and deflation.   |   
+          | Use a standard test as in the QR algorithm |   
+          | REFERENCE: LAPACK subroutine dlahqr        |   
+          %--------------------------------------------% */
+
+	tst1 = (d__1 = h__[i__ + i__ * h_dim1], abs(d__1)) + (d__2 = h__[i__ 
+		+ 1 + (i__ + 1) * h_dim1], abs(d__2));
+	if (tst1 == 0.) {
+	    tst1 = igraphdlanhs_("1", kev, &h__[h_offset], ldh, &workl[1]);
+	}
+/* Computing MAX */
+	d__1 = ulp * tst1;
+	if (h__[i__ + 1 + i__ * h_dim1] <= max(d__1,smlnum)) {
+	    h__[i__ + 1 + i__ * h_dim1] = 0.;
+	}
+/* L130: */
+    }
+
+/*     %-------------------------------------------------%   
+       | Compute the (kev+1)-st column of (V*Q) and      |   
+       | temporarily store the result in WORKD(N+1:2*N). |   
+       | This is needed in the residual update since we  |   
+       | cannot GUARANTEE that the corresponding entry   |   
+       | of H would be zero as in exact arithmetic.      |   
+       %-------------------------------------------------% */
+
+    if (h__[*kev + 1 + *kev * h_dim1] > 0.) {
+	igraphdgemv_("N", n, &kplusp, &c_b6, &v[v_offset], ldv, &q[(*kev + 1) * 
+		q_dim1 + 1], &c__1, &c_b5, &workd[*n + 1], &c__1);
+    }
+
+/*     %----------------------------------------------------------%   
+       | Compute column 1 to kev of (V*Q) in backward order       |   
+       | taking advantage of the upper Hessenberg structure of Q. |   
+       %----------------------------------------------------------% */
+
+    i__1 = *kev;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	i__2 = kplusp - i__ + 1;
+	igraphdgemv_("N", n, &i__2, &c_b6, &v[v_offset], ldv, &q[(*kev - i__ + 1) * 
+		q_dim1 + 1], &c__1, &c_b5, &workd[1], &c__1);
+	igraphdcopy_(n, &workd[1], &c__1, &v[(kplusp - i__ + 1) * v_dim1 + 1], &
+		c__1);
+/* L140: */
+    }
+
+/*     %-------------------------------------------------%   
+       |  Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). |   
+       %-------------------------------------------------% */
+
+    igraphdlacpy_("A", n, kev, &v[(kplusp - *kev + 1) * v_dim1 + 1], ldv, &v[
+	    v_offset], ldv);
+
+/*     %--------------------------------------------------------------%   
+       | Copy the (kev+1)-st column of (V*Q) in the appropriate place |   
+       %--------------------------------------------------------------% */
+
+    if (h__[*kev + 1 + *kev * h_dim1] > 0.) {
+	igraphdcopy_(n, &workd[*n + 1], &c__1, &v[(*kev + 1) * v_dim1 + 1], &c__1);
+    }
+
+/*     %-------------------------------------%   
+       | Update the residual vector:         |   
+       |    r <- sigmak*r + betak*v(:,kev+1) |   
+       | where                               |   
+       |    sigmak = (e_{kplusp}'*Q)*e_{kev} |   
+       |    betak = e_{kev+1}'*H*e_{kev}     |   
+       %-------------------------------------% */
+
+    igraphdscal_(n, &q[kplusp + *kev * q_dim1], &resid[1], &c__1);
+    if (h__[*kev + 1 + *kev * h_dim1] > 0.) {
+	igraphdaxpy_(n, &h__[*kev + 1 + *kev * h_dim1], &v[(*kev + 1) * v_dim1 + 1],
+		 &c__1, &resid[1], &c__1);
+    }
+
+    if (msglvl > 1) {
+	igraphdvout_(&logfil, &c__1, &q[kplusp + *kev * q_dim1], &ndigit, "_napps:"
+		" sigmak = (e_{kev+p}^T*Q)*e_{kev}", (ftnlen)40);
+	igraphdvout_(&logfil, &c__1, &h__[*kev + 1 + *kev * h_dim1], &ndigit, "_na"
+		"pps: betak = e_{kev+1}^T*H*e_{kev}", (ftnlen)37);
+	igraphivout_(&logfil, &c__1, kev, &ndigit, "_napps: Order of the final Hes"
+		"senberg matrix ", (ftnlen)45);
+	if (msglvl > 2) {
+	    igraphdmout_(&logfil, kev, kev, &h__[h_offset], ldh, &ndigit, "_napps:"
+		    " updated Hessenberg matrix H for next iteration", (ftnlen)
+		    54);
+	}
+
+    }
+
+L9000:
+    igraphsecond_(&t1);
+    tnapps += t1 - t0;
+
+    return 0;
+
+/*     %---------------%   
+       | End of dnapps |   
+       %---------------% */
+
+} /* igraphdnapps_ */
+
diff --git a/igraph/src/dnaup2.c b/igraph/src/dnaup2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dnaup2.c
@@ -0,0 +1,978 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b3 = .66666666666666663;
+static integer c__1 = 1;
+static integer c__0 = 0;
+static integer c__4 = 4;
+static logical c_true = TRUE_;
+static integer c__2 = 2;
+
+/* \BeginDoc   
+
+   \Name: dnaup2   
+
+   \Description:   
+    Intermediate level interface called by dnaupd.   
+
+   \Usage:   
+    call dnaup2   
+       ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD,   
+         ISHIFT, MXITER, V, LDV, H, LDH, RITZR, RITZI, BOUNDS,   
+         Q, LDQ, WORKL, IPNTR, WORKD, INFO )   
+
+   \Arguments   
+
+    IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in dnaupd.   
+    MODE, ISHIFT, MXITER: see the definition of IPARAM in dnaupd.   
+
+    NP      Integer.  (INPUT/OUTPUT)   
+            Contains the number of implicit shifts to apply during   
+            each Arnoldi iteration.   
+            If ISHIFT=1, NP is adjusted dynamically at each iteration   
+            to accelerate convergence and prevent stagnation.   
+            This is also roughly equal to the number of matrix-vector   
+            products (involving the operator OP) per Arnoldi iteration.   
+            The logic for adjusting is contained within the current   
+            subroutine.   
+            If ISHIFT=0, NP is the number of shifts the user needs   
+            to provide via reverse comunication. 0 < NP < NCV-NEV.   
+            NP may be less than NCV-NEV for two reasons. The first, is   
+            to keep complex conjugate pairs of "wanted" Ritz values   
+            together. The second, is that a leading block of the current   
+            upper Hessenberg matrix has split off and contains "unwanted"   
+            Ritz values.   
+            Upon termination of the IRA iteration, NP contains the number   
+            of "converged" wanted Ritz values.   
+
+    IUPD    Integer.  (INPUT)   
+            IUPD .EQ. 0: use explicit restart instead implicit update.   
+            IUPD .NE. 0: use implicit update.   
+
+    V       Double precision N by (NEV+NP) array.  (INPUT/OUTPUT)   
+            The Arnoldi basis vectors are returned in the first NEV   
+            columns of V.   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling   
+            program.   
+
+    H       Double precision (NEV+NP) by (NEV+NP) array.  (OUTPUT)   
+            H is used to store the generated upper Hessenberg matrix   
+
+    LDH     Integer.  (INPUT)   
+            Leading dimension of H exactly as declared in the calling   
+            program.   
+
+    RITZR,  Double precision arrays of length NEV+NP.  (OUTPUT)   
+    RITZI   RITZR(1:NEV) (resp. RITZI(1:NEV)) contains the real (resp.   
+            imaginary) part of the computed Ritz values of OP.   
+
+    BOUNDS  Double precision array of length NEV+NP.  (OUTPUT)   
+            BOUNDS(1:NEV) contain the error bounds corresponding to   
+            the computed Ritz values.   
+
+    Q       Double precision (NEV+NP) by (NEV+NP) array.  (WORKSPACE)   
+            Private (replicated) work array used to accumulate the   
+            rotation in the shift application step.   
+
+    LDQ     Integer.  (INPUT)   
+            Leading dimension of Q exactly as declared in the calling   
+            program.   
+
+    WORKL   Double precision work array of length at least   
+            (NEV+NP)**2 + 3*(NEV+NP).  (INPUT/WORKSPACE)   
+            Private (replicated) array on each PE or array allocated on   
+            the front end.  It is used in shifts calculation, shifts   
+            application and convergence checking.   
+
+            On exit, the last 3*(NEV+NP) locations of WORKL contain   
+            the Ritz values (real,imaginary) and associated Ritz   
+            estimates of the current Hessenberg matrix.  They are   
+            listed in the same order as returned from dneigh.   
+
+            If ISHIFT .EQ. O and IDO .EQ. 3, the first 2*NP locations   
+            of WORKL are used in reverse communication to hold the user   
+            supplied shifts.   
+
+    IPNTR   Integer array of length 3.  (OUTPUT)   
+            Pointer to mark the starting locations in the WORKD for   
+            vectors used by the Arnoldi iteration.   
+            -------------------------------------------------------------   
+            IPNTR(1): pointer to the current operand vector X.   
+            IPNTR(2): pointer to the current result vector Y.   
+            IPNTR(3): pointer to the vector B * X when used in the   
+                      shift-and-invert mode.  X is the current operand.   
+            -------------------------------------------------------------   
+
+    WORKD   Double precision work array of length 3*N.  (WORKSPACE)   
+            Distributed array to be used in the basic Arnoldi iteration   
+            for reverse communication.  The user should not use WORKD   
+            as temporary workspace during the iteration !!!!!!!!!!   
+            See Data Distribution Note in DNAUPD.   
+
+    INFO    Integer.  (INPUT/OUTPUT)   
+            If INFO .EQ. 0, a randomly initial residual vector is used.   
+            If INFO .NE. 0, RESID contains the initial residual vector,   
+                            possibly from a previous run.   
+            Error flag on output.   
+            =     0: Normal return.   
+            =     1: Maximum number of iterations taken.   
+                     All possible eigenvalues of OP has been found.   
+                     NP returns the number of converged Ritz values.   
+            =     2: No shifts could be applied.   
+            =    -8: Error return from LAPACK eigenvalue calculation;   
+                     This should never happen.   
+            =    -9: Starting vector is zero.   
+            = -9999: Could not build an Arnoldi factorization.   
+                     Size that was built in returned in NP.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+
+   \Routines called:   
+       dgetv0  ARPACK initial vector generation routine.   
+       dnaitr  ARPACK Arnoldi factorization routine.   
+       dnapps  ARPACK application of implicit shifts routine.   
+       dnconv  ARPACK convergence of Ritz values routine.   
+       dneigh  ARPACK compute Ritz values and error bounds routine.   
+       dngets  ARPACK reorder Ritz values and error bounds routine.   
+       dsortc  ARPACK sorting routine.   
+       ivout   ARPACK utility routine that prints integers.   
+       second  ARPACK utility routine for timing.   
+       dmout   ARPACK utility routine that prints matrices   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlapy2  LAPACK routine to compute sqrt(x**2+y**2) carefully.   
+       dcopy   Level 1 BLAS that copies one vector to another .   
+       ddot    Level 1 BLAS that computes the scalar product of two vectors.   
+       dnrm2   Level 1 BLAS that computes the norm of a vector.   
+       dswap   Level 1 BLAS that swaps two vectors.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \SCCS Information: @(#)   
+   FILE: naup2.F   SID: 2.4   DATE OF SID: 7/30/96   RELEASE: 2   
+
+   \Remarks   
+       1. None   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdnaup2_(integer *ido, char *bmat, integer *n, char *
+	which, integer *nev, integer *np, doublereal *tol, doublereal *resid, 
+	integer *mode, integer *iupd, integer *ishift, integer *mxiter, 
+	doublereal *v, integer *ldv, doublereal *h__, integer *ldh, 
+	doublereal *ritzr, doublereal *ritzi, doublereal *bounds, doublereal *
+	q, integer *ldq, doublereal *workl, integer *ipntr, doublereal *workd,
+	 integer *info)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, q_dim1, q_offset, v_dim1, v_offset, i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double pow_dd(doublereal *, doublereal *);
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+    /* Subroutine */ int s_copy(char *, char *, ftnlen, ftnlen);
+    double sqrt(doublereal);
+
+    /* Local variables */
+    IGRAPH_F77_SAVE integer j;
+    IGRAPH_F77_SAVE real t0, t1, t2, t3;
+    IGRAPH_F77_SAVE integer kp[4], np0, nbx, nev0;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    IGRAPH_F77_SAVE doublereal eps23;
+    IGRAPH_F77_SAVE integer ierr, iter;
+    IGRAPH_F77_SAVE doublereal temp;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    IGRAPH_F77_SAVE logical getv0, cnorm;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    IGRAPH_F77_SAVE integer nconv;
+    extern /* Subroutine */ int igraphdmout_(integer *, integer *, integer *, 
+	    doublereal *, integer *, integer *, char *, ftnlen);
+    IGRAPH_F77_SAVE logical initv;
+    IGRAPH_F77_SAVE doublereal rnorm;
+    IGRAPH_F77_SAVE real tmvbx;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen), igraphivout_(integer *, integer *, integer *
+	    , integer *, char *, ftnlen), igraphdgetv0_(integer *, char *, integer *
+	    , logical *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *);
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *);
+    IGRAPH_F77_SAVE integer mnaup2;
+    IGRAPH_F77_SAVE real tnaup2;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdneigh_(doublereal *, integer *, doublereal *,
+	     integer *, doublereal *, doublereal *, doublereal *, doublereal *
+	    , integer *, doublereal *, integer *);
+    IGRAPH_F77_SAVE integer nevbef;
+    extern /* Subroutine */ int igraphsecond_(real *);
+    IGRAPH_F77_SAVE integer logfil, ndigit;
+    extern /* Subroutine */ int igraphdnaitr_(integer *, char *, integer *, integer 
+	    *, integer *, integer *, doublereal *, doublereal *, doublereal *,
+	     integer *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *);
+    IGRAPH_F77_SAVE logical update;
+    extern /* Subroutine */ int igraphdngets_(integer *, char *, integer *, integer 
+	    *, doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *), igraphdnapps_(integer *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *, doublereal *,
+	     integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    doublereal *), igraphdnconv_(integer *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, integer *), igraphdsortc_(char *, logical *,
+	     integer *, doublereal *, doublereal *, doublereal *);
+    IGRAPH_F77_SAVE logical ushift;
+    IGRAPH_F77_SAVE char wprime[2];
+    IGRAPH_F77_SAVE integer msglvl, nptemp, numcnv, kplusp;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %-----------------------%   
+       | Local array arguments |   
+       %-----------------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    --workl;
+    --bounds;
+    --ritzi;
+    --ritzr;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    q_dim1 = *ldq;
+    q_offset = 1 + q_dim1;
+    q -= q_offset;
+    --ipntr;
+
+    /* Function Body */
+    if (*ido == 0) {
+
+	igraphsecond_(&t0);
+
+	msglvl = mnaup2;
+
+/*        %-------------------------------------%   
+          | Get the machine dependent constant. |   
+          %-------------------------------------% */
+
+	eps23 = igraphdlamch_("Epsilon-Machine");
+	eps23 = pow_dd(&eps23, &c_b3);
+
+	nev0 = *nev;
+	np0 = *np;
+
+/*        %-------------------------------------%   
+          | kplusp is the bound on the largest  |   
+          |        Lanczos factorization built. |   
+          | nconv is the current number of      |   
+          |        "converged" eigenvlues.      |   
+          | iter is the counter on the current  |   
+          |      iteration step.                |   
+          %-------------------------------------% */
+
+	kplusp = *nev + *np;
+	nconv = 0;
+	iter = 0;
+
+/*        %---------------------------------------%   
+          | Set flags for computing the first NEV |   
+          | steps of the Arnoldi factorization.   |   
+          %---------------------------------------% */
+
+	getv0 = TRUE_;
+	update = FALSE_;
+	ushift = FALSE_;
+	cnorm = FALSE_;
+
+	if (*info != 0) {
+
+/*           %--------------------------------------------%   
+             | User provides the initial residual vector. |   
+             %--------------------------------------------% */
+
+	    initv = TRUE_;
+	    *info = 0;
+	} else {
+	    initv = FALSE_;
+	}
+    }
+
+/*     %---------------------------------------------%   
+       | Get a possibly random starting vector and   |   
+       | force it into the range of the operator OP. |   
+       %---------------------------------------------%   
+
+   L10: */
+
+    if (getv0) {
+	igraphdgetv0_(ido, bmat, &c__1, &initv, n, &c__1, &v[v_offset], ldv, &resid[
+		1], &rnorm, &ipntr[1], &workd[1], info);
+
+	if (*ido != 99) {
+	    goto L9000;
+	}
+
+	if (rnorm == 0.) {
+
+/*           %-----------------------------------------%   
+             | The initial vector is zero. Error exit. |   
+             %-----------------------------------------% */
+
+	    *info = -9;
+	    goto L1100;
+	}
+	getv0 = FALSE_;
+	*ido = 0;
+    }
+
+/*     %-----------------------------------%   
+       | Back from reverse communication : |   
+       | continue with update step         |   
+       %-----------------------------------% */
+
+    if (update) {
+	goto L20;
+    }
+
+/*     %-------------------------------------------%   
+       | Back from computing user specified shifts |   
+       %-------------------------------------------% */
+
+    if (ushift) {
+	goto L50;
+    }
+
+/*     %-------------------------------------%   
+       | Back from computing residual norm   |   
+       | at the end of the current iteration |   
+       %-------------------------------------% */
+
+    if (cnorm) {
+	goto L100;
+    }
+
+/*     %----------------------------------------------------------%   
+       | Compute the first NEV steps of the Arnoldi factorization |   
+       %----------------------------------------------------------% */
+
+    igraphdnaitr_(ido, bmat, n, &c__0, nev, mode, &resid[1], &rnorm, &v[v_offset], 
+	    ldv, &h__[h_offset], ldh, &ipntr[1], &workd[1], info);
+
+/*     %---------------------------------------------------%   
+       | ido .ne. 99 implies use of reverse communication  |   
+       | to compute operations involving OP and possibly B |   
+       %---------------------------------------------------% */
+
+    if (*ido != 99) {
+	goto L9000;
+    }
+
+    if (*info > 0) {
+	*np = *info;
+	*mxiter = iter;
+	*info = -9999;
+	goto L1200;
+    }
+
+/*     %--------------------------------------------------------------%   
+       |                                                              |   
+       |           M A I N  ARNOLDI  I T E R A T I O N  L O O P       |   
+       |           Each iteration implicitly restarts the Arnoldi     |   
+       |           factorization in place.                            |   
+       |                                                              |   
+       %--------------------------------------------------------------% */
+
+L1000:
+
+    ++iter;
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, &iter, &ndigit, "_naup2: **** Start of major "
+		"iteration number ****", (ftnlen)49);
+    }
+
+/*        %-----------------------------------------------------------%   
+          | Compute NP additional steps of the Arnoldi factorization. |   
+          | Adjust NP since NEV might have been updated by last call  |   
+          | to the shift application routine dnapps.                  |   
+          %-----------------------------------------------------------% */
+
+    *np = kplusp - *nev;
+
+    if (msglvl > 1) {
+	igraphivout_(&logfil, &c__1, nev, &ndigit, "_naup2: The length of the curr"
+		"ent Arnoldi factorization", (ftnlen)55);
+	igraphivout_(&logfil, &c__1, np, &ndigit, "_naup2: Extend the Arnoldi fact"
+		"orization by", (ftnlen)43);
+    }
+
+/*        %-----------------------------------------------------------%   
+          | Compute NP additional steps of the Arnoldi factorization. |   
+          %-----------------------------------------------------------% */
+
+    *ido = 0;
+L20:
+    update = TRUE_;
+
+    igraphdnaitr_(ido, bmat, n, nev, np, mode, &resid[1], &rnorm, &v[v_offset], ldv,
+	     &h__[h_offset], ldh, &ipntr[1], &workd[1], info);
+
+/*        %---------------------------------------------------%   
+          | ido .ne. 99 implies use of reverse communication  |   
+          | to compute operations involving OP and possibly B |   
+          %---------------------------------------------------% */
+
+    if (*ido != 99) {
+	goto L9000;
+    }
+
+    if (*info > 0) {
+	*np = *info;
+	*mxiter = iter;
+	*info = -9999;
+	goto L1200;
+    }
+    update = FALSE_;
+
+    if (msglvl > 1) {
+	igraphdvout_(&logfil, &c__1, &rnorm, &ndigit, "_naup2: Corresponding B-nor"
+		"m of the residual", (ftnlen)44);
+    }
+
+/*        %--------------------------------------------------------%   
+          | Compute the eigenvalues and corresponding error bounds |   
+          | of the current upper Hessenberg matrix.                |   
+          %--------------------------------------------------------% */
+
+    igraphdneigh_(&rnorm, &kplusp, &h__[h_offset], ldh, &ritzr[1], &ritzi[1], &
+	    bounds[1], &q[q_offset], ldq, &workl[1], &ierr);
+
+    if (ierr != 0) {
+	*info = -8;
+	goto L1200;
+    }
+
+/*        %----------------------------------------------------%   
+          | Make a copy of eigenvalues and corresponding error |   
+          | bounds obtained from dneigh.                       |   
+          %----------------------------------------------------%   
+
+   Computing 2nd power */
+    i__1 = kplusp;
+    igraphdcopy_(&kplusp, &ritzr[1], &c__1, &workl[i__1 * i__1 + 1], &c__1);
+/* Computing 2nd power */
+    i__1 = kplusp;
+    igraphdcopy_(&kplusp, &ritzi[1], &c__1, &workl[i__1 * i__1 + kplusp + 1], &c__1)
+	    ;
+/* Computing 2nd power */
+    i__1 = kplusp;
+    igraphdcopy_(&kplusp, &bounds[1], &c__1, &workl[i__1 * i__1 + (kplusp << 1) + 1]
+	    , &c__1);
+
+/*        %---------------------------------------------------%   
+          | Select the wanted Ritz values and their bounds    |   
+          | to be used in the convergence test.               |   
+          | The wanted part of the spectrum and corresponding |   
+          | error bounds are in the last NEV loc. of RITZR,   |   
+          | RITZI and BOUNDS respectively. The variables NEV  |   
+          | and NP may be updated if the NEV-th wanted Ritz   |   
+          | value has a non zero imaginary part. In this case |   
+          | NEV is increased by one and NP decreased by one.  |   
+          | NOTE: The last two arguments of dngets are no     |   
+          | longer used as of version 2.1.                    |   
+          %---------------------------------------------------% */
+
+    *nev = nev0;
+    *np = np0;
+    numcnv = *nev;
+    igraphdngets_(ishift, which, nev, np, &ritzr[1], &ritzi[1], &bounds[1], &workl[
+	    1], &workl[*np + 1]);
+    if (*nev == nev0 + 1) {
+	numcnv = nev0 + 1;
+    }
+
+/*        %-------------------%   
+          | Convergence test. |   
+          %-------------------% */
+
+    igraphdcopy_(nev, &bounds[*np + 1], &c__1, &workl[(*np << 1) + 1], &c__1);
+    igraphdnconv_(nev, &ritzr[*np + 1], &ritzi[*np + 1], &workl[(*np << 1) + 1], 
+	    tol, &nconv);
+
+    if (msglvl > 2) {
+	kp[0] = *nev;
+	kp[1] = *np;
+	kp[2] = numcnv;
+	kp[3] = nconv;
+	igraphivout_(&logfil, &c__4, kp, &ndigit, "_naup2: NEV, NP, NUMCNV, NCONV "
+		"are", (ftnlen)34);
+	igraphdvout_(&logfil, &kplusp, &ritzr[1], &ndigit, "_naup2: Real part of t"
+		"he eigenvalues of H", (ftnlen)41);
+	igraphdvout_(&logfil, &kplusp, &ritzi[1], &ndigit, "_naup2: Imaginary part"
+		" of the eigenvalues of H", (ftnlen)46);
+	igraphdvout_(&logfil, &kplusp, &bounds[1], &ndigit, "_naup2: Ritz estimate"
+		"s of the current NCV Ritz values", (ftnlen)53);
+    }
+
+/*        %---------------------------------------------------------%   
+          | Count the number of unwanted Ritz values that have zero |   
+          | Ritz estimates. If any Ritz estimates are equal to zero |   
+          | then a leading block of H of order equal to at least    |   
+          | the number of Ritz values with zero Ritz estimates has  |   
+          | split off. None of these Ritz values may be removed by  |   
+          | shifting. Decrease NP the number of shifts to apply. If |   
+          | no shifts may be applied, then prepare to exit          |   
+          %---------------------------------------------------------% */
+
+    nptemp = *np;
+    i__1 = nptemp;
+    for (j = 1; j <= i__1; ++j) {
+	if (bounds[j] == 0.) {
+	    --(*np);
+	    ++(*nev);
+	}
+/* L30: */
+    }
+
+    if (nconv >= numcnv || iter > *mxiter || *np == 0) {
+
+	if (msglvl > 4) {
+/* Computing 2nd power */
+	    i__1 = kplusp;
+	    igraphdvout_(&logfil, &kplusp, &workl[i__1 * i__1 + 1], &ndigit, "_nau"
+		    "p2: Real part of the eig computed by _neigh:", (ftnlen)48)
+		    ;
+/* Computing 2nd power */
+	    i__1 = kplusp;
+	    igraphdvout_(&logfil, &kplusp, &workl[i__1 * i__1 + kplusp + 1], &
+		    ndigit, "_naup2: Imag part of the eig computed by _neigh:"
+		    , (ftnlen)48);
+/* Computing 2nd power */
+	    i__1 = kplusp;
+	    igraphdvout_(&logfil, &kplusp, &workl[i__1 * i__1 + (kplusp << 1) + 1], 
+		    &ndigit, "_naup2: Ritz eistmates computed by _neigh:", (
+		    ftnlen)42);
+	}
+
+/*           %------------------------------------------------%   
+             | Prepare to exit. Put the converged Ritz values |   
+             | and corresponding bounds in RITZ(1:NCONV) and  |   
+             | BOUNDS(1:NCONV) respectively. Then sort. Be    |   
+             | careful when NCONV > NP                        |   
+             %------------------------------------------------%   
+
+             %------------------------------------------%   
+             |  Use h( 3,1 ) as storage to communicate  |   
+             |  rnorm to _neupd if needed               |   
+             %------------------------------------------% */
+	h__[h_dim1 + 3] = rnorm;
+
+/*           %----------------------------------------------%   
+             | To be consistent with dngets, we first do a  |   
+             | pre-processing sort in order to keep complex |   
+             | conjugate pairs together.  This is similar   |   
+             | to the pre-processing sort used in dngets    |   
+             | except that the sort is done in the opposite |   
+             | order.                                       |   
+             %----------------------------------------------% */
+
+	if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "SR", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "LR", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "LR", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "SM", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "SR", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "LM", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "LI", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "SM", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "SI", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "LM", (ftnlen)2, (ftnlen)2);
+	}
+
+	igraphdsortc_(wprime, &c_true, &kplusp, &ritzr[1], &ritzi[1], &bounds[1]);
+
+/*           %----------------------------------------------%   
+             | Now sort Ritz values so that converged Ritz  |   
+             | values appear within the first NEV locations |   
+             | of ritzr, ritzi and bounds, and the most     |   
+             | desired one appears at the front.            |   
+             %----------------------------------------------% */
+
+	if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "SM", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "LM", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "LR", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "SR", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "SR", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "LR", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "LI", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "SI", (ftnlen)2, (ftnlen)2);
+	}
+	if (s_cmp(which, "SI", (ftnlen)2, (ftnlen)2) == 0) {
+	    s_copy(wprime, "LI", (ftnlen)2, (ftnlen)2);
+	}
+
+	igraphdsortc_(wprime, &c_true, &kplusp, &ritzr[1], &ritzi[1], &bounds[1]);
+
+/*           %--------------------------------------------------%   
+             | Scale the Ritz estimate of each Ritz value       |   
+             | by 1 / max(eps23,magnitude of the Ritz value).   |   
+             %--------------------------------------------------% */
+
+	i__1 = nev0;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MAX */
+	    d__1 = eps23, d__2 = igraphdlapy2_(&ritzr[j], &ritzi[j]);
+	    temp = max(d__1,d__2);
+	    bounds[j] /= temp;
+/* L35: */
+	}
+
+/*           %----------------------------------------------------%   
+             | Sort the Ritz values according to the scaled Ritz  |   
+             | esitmates.  This will push all the converged ones  |   
+             | towards the front of ritzr, ritzi, bounds          |   
+             | (in the case when NCONV < NEV.)                    |   
+             %----------------------------------------------------% */
+
+	s_copy(wprime, "LR", (ftnlen)2, (ftnlen)2);
+	igraphdsortc_(wprime, &c_true, &nev0, &bounds[1], &ritzr[1], &ritzi[1]);
+
+/*           %----------------------------------------------%   
+             | Scale the Ritz estimate back to its original |   
+             | value.                                       |   
+             %----------------------------------------------% */
+
+	i__1 = nev0;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MAX */
+	    d__1 = eps23, d__2 = igraphdlapy2_(&ritzr[j], &ritzi[j]);
+	    temp = max(d__1,d__2);
+	    bounds[j] *= temp;
+/* L40: */
+	}
+
+/*           %------------------------------------------------%   
+             | Sort the converged Ritz values again so that   |   
+             | the "threshold" value appears at the front of  |   
+             | ritzr, ritzi and bound.                        |   
+             %------------------------------------------------% */
+
+	igraphdsortc_(which, &c_true, &nconv, &ritzr[1], &ritzi[1], &bounds[1]);
+
+	if (msglvl > 1) {
+	    igraphdvout_(&logfil, &kplusp, &ritzr[1], &ndigit, "_naup2: Sorted rea"
+		    "l part of the eigenvalues", (ftnlen)43);
+	    igraphdvout_(&logfil, &kplusp, &ritzi[1], &ndigit, "_naup2: Sorted ima"
+		    "ginary part of the eigenvalues", (ftnlen)48);
+	    igraphdvout_(&logfil, &kplusp, &bounds[1], &ndigit, "_naup2: Sorted ri"
+		    "tz estimates.", (ftnlen)30);
+	}
+
+/*           %------------------------------------%   
+             | Max iterations have been exceeded. |   
+             %------------------------------------% */
+
+	if (iter > *mxiter && nconv < numcnv) {
+	    *info = 1;
+	}
+
+/*           %---------------------%   
+             | No shifts to apply. |   
+             %---------------------% */
+
+	if (*np == 0 && nconv < numcnv) {
+	    *info = 2;
+	}
+
+	*np = nconv;
+	goto L1100;
+
+    } else if (nconv < numcnv && *ishift == 1) {
+
+/*           %-------------------------------------------------%   
+             | Do not have all the requested eigenvalues yet.  |   
+             | To prevent possible stagnation, adjust the size |   
+             | of NEV.                                         |   
+             %-------------------------------------------------% */
+
+	nevbef = *nev;
+/* Computing MIN */
+	i__1 = nconv, i__2 = *np / 2;
+	*nev += min(i__1,i__2);
+	if (*nev == 1 && kplusp >= 6) {
+	    *nev = kplusp / 2;
+	} else if (*nev == 1 && kplusp > 3) {
+	    *nev = 2;
+	}
+	*np = kplusp - *nev;
+
+/*           %---------------------------------------%   
+             | If the size of NEV was just increased |   
+             | resort the eigenvalues.               |   
+             %---------------------------------------% */
+
+	if (nevbef < *nev) {
+	    igraphdngets_(ishift, which, nev, np, &ritzr[1], &ritzi[1], &bounds[1], 
+		    &workl[1], &workl[*np + 1]);
+	}
+
+    }
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, &nconv, &ndigit, "_naup2: no. of \"converge"
+		"d\" Ritz values at this iter.", (ftnlen)52);
+	if (msglvl > 1) {
+	    kp[0] = *nev;
+	    kp[1] = *np;
+	    igraphivout_(&logfil, &c__2, kp, &ndigit, "_naup2: NEV and NP are", (
+		    ftnlen)22);
+	    igraphdvout_(&logfil, nev, &ritzr[*np + 1], &ndigit, "_naup2: \"wante"
+		    "d\" Ritz values -- real part", (ftnlen)41);
+	    igraphdvout_(&logfil, nev, &ritzi[*np + 1], &ndigit, "_naup2: \"wante"
+		    "d\" Ritz values -- imag part", (ftnlen)41);
+	    igraphdvout_(&logfil, nev, &bounds[*np + 1], &ndigit, "_naup2: Ritz es"
+		    "timates of the \"wanted\" values ", (ftnlen)46);
+	}
+    }
+
+    if (*ishift == 0) {
+
+/*           %-------------------------------------------------------%   
+             | User specified shifts: reverse comminucation to       |   
+             | compute the shifts. They are returned in the first    |   
+             | 2*NP locations of WORKL.                              |   
+             %-------------------------------------------------------% */
+
+	ushift = TRUE_;
+	*ido = 3;
+	goto L9000;
+    }
+
+L50:
+
+/*        %------------------------------------%   
+          | Back from reverse communication;   |   
+          | User specified shifts are returned |   
+          | in WORKL(1:2*NP)                   |   
+          %------------------------------------% */
+
+    ushift = FALSE_;
+
+    if (*ishift == 0) {
+
+/*            %----------------------------------%   
+              | Move the NP shifts from WORKL to |   
+              | RITZR, RITZI to free up WORKL    |   
+              | for non-exact shift case.        |   
+              %----------------------------------% */
+
+	igraphdcopy_(np, &workl[1], &c__1, &ritzr[1], &c__1);
+	igraphdcopy_(np, &workl[*np + 1], &c__1, &ritzi[1], &c__1);
+    }
+
+    if (msglvl > 2) {
+	igraphivout_(&logfil, &c__1, np, &ndigit, "_naup2: The number of shifts to"
+		" apply ", (ftnlen)38);
+	igraphdvout_(&logfil, np, &ritzr[1], &ndigit, "_naup2: Real part of the sh"
+		"ifts", (ftnlen)31);
+	igraphdvout_(&logfil, np, &ritzi[1], &ndigit, "_naup2: Imaginary part of t"
+		"he shifts", (ftnlen)36);
+	if (*ishift == 1) {
+	    igraphdvout_(&logfil, np, &bounds[1], &ndigit, "_naup2: Ritz estimates"
+		    " of the shifts", (ftnlen)36);
+	}
+    }
+
+/*        %---------------------------------------------------------%   
+          | Apply the NP implicit shifts by QR bulge chasing.       |   
+          | Each shift is applied to the whole upper Hessenberg     |   
+          | matrix H.                                               |   
+          | The first 2*N locations of WORKD are used as workspace. |   
+          %---------------------------------------------------------% */
+
+    igraphdnapps_(n, nev, np, &ritzr[1], &ritzi[1], &v[v_offset], ldv, &h__[
+	    h_offset], ldh, &resid[1], &q[q_offset], ldq, &workl[1], &workd[1]
+	    );
+
+/*        %---------------------------------------------%   
+          | Compute the B-norm of the updated residual. |   
+          | Keep B*RESID in WORKD(1:N) to be used in    |   
+          | the first step of the next call to dnaitr.  |   
+          %---------------------------------------------% */
+
+    cnorm = TRUE_;
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	igraphdcopy_(n, &resid[1], &c__1, &workd[*n + 1], &c__1);
+	ipntr[1] = *n + 1;
+	ipntr[2] = 1;
+	*ido = 2;
+
+/*           %----------------------------------%   
+             | Exit in order to compute B*RESID |   
+             %----------------------------------% */
+
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[1], &c__1);
+    }
+
+L100:
+
+/*        %----------------------------------%   
+          | Back from reverse communication; |   
+          | WORKD(1:N) := B*RESID            |   
+          %----------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+    if (*(unsigned char *)bmat == 'G') {
+	rnorm = igraphddot_(n, &resid[1], &c__1, &workd[1], &c__1);
+	rnorm = sqrt((abs(rnorm)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	rnorm = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+    cnorm = FALSE_;
+
+    if (msglvl > 2) {
+	igraphdvout_(&logfil, &c__1, &rnorm, &ndigit, "_naup2: B-norm of residual "
+		"for compressed factorization", (ftnlen)55);
+	igraphdmout_(&logfil, nev, nev, &h__[h_offset], ldh, &ndigit, "_naup2: Com"
+		"pressed upper Hessenberg matrix H", (ftnlen)44);
+    }
+
+    goto L1000;
+
+/*     %---------------------------------------------------------------%   
+       |                                                               |   
+       |  E N D     O F     M A I N     I T E R A T I O N     L O O P  |   
+       |                                                               |   
+       %---------------------------------------------------------------% */
+
+L1100:
+
+    *mxiter = iter;
+    *nev = numcnv;
+
+L1200:
+    *ido = 99;
+
+/*     %------------%   
+       | Error Exit |   
+       %------------% */
+
+    igraphsecond_(&t1);
+    tnaup2 = t1 - t0;
+
+L9000:
+
+/*     %---------------%   
+       | End of dnaup2 |   
+       %---------------% */
+
+    return 0;
+} /* igraphdnaup2_ */
+
diff --git a/igraph/src/dnaupd.c b/igraph/src/dnaupd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dnaupd.c
@@ -0,0 +1,794 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* \BeginDoc   
+
+   \Name: dnaupd   
+
+   \Description:   
+    Reverse communication interface for the Implicitly Restarted Arnoldi   
+    iteration. This subroutine computes approximations to a few eigenpairs   
+    of a linear operator "OP" with respect to a semi-inner product defined by   
+    a symmetric positive semi-definite real matrix B. B may be the identity   
+    matrix. NOTE: If the linear operator "OP" is real and symmetric   
+    with respect to the real positive semi-definite symmetric matrix B,   
+    i.e. B*OP = (OP')*B, then subroutine ssaupd should be used instead.   
+
+    The computed approximate eigenvalues are called Ritz values and   
+    the corresponding approximate eigenvectors are called Ritz vectors.   
+
+    dnaupd is usually called iteratively to solve one of the   
+    following problems:   
+
+    Mode 1:  A*x = lambda*x.   
+             ===> OP = A  and  B = I.   
+
+    Mode 2:  A*x = lambda*M*x, M symmetric positive definite   
+             ===> OP = inv[M]*A  and  B = M.   
+             ===> (If M can be factored see remark 3 below)   
+
+    Mode 3:  A*x = lambda*M*x, M symmetric semi-definite   
+             ===> OP = Real_Part{ inv[A - sigma*M]*M }  and  B = M.   
+             ===> shift-and-invert mode (in real arithmetic)   
+             If OP*x = amu*x, then   
+             amu = 1/2 * [ 1/(lambda-sigma) + 1/(lambda-conjg(sigma)) ].   
+             Note: If sigma is real, i.e. imaginary part of sigma is zero;   
+                   Real_Part{ inv[A - sigma*M]*M } == inv[A - sigma*M]*M   
+                   amu == 1/(lambda-sigma).   
+
+    Mode 4:  A*x = lambda*M*x, M symmetric semi-definite   
+             ===> OP = Imaginary_Part{ inv[A - sigma*M]*M }  and  B = M.   
+             ===> shift-and-invert mode (in real arithmetic)   
+             If OP*x = amu*x, then   
+             amu = 1/2i * [ 1/(lambda-sigma) - 1/(lambda-conjg(sigma)) ].   
+
+    Both mode 3 and 4 give the same enhancement to eigenvalues close to   
+    the (complex) shift sigma.  However, as lambda goes to infinity,   
+    the operator OP in mode 4 dampens the eigenvalues more strongly than   
+    does OP defined in mode 3.   
+
+    NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v   
+          should be accomplished either by a direct method   
+          using a sparse matrix factorization and solving   
+
+             [A - sigma*M]*w = v  or M*w = v,   
+
+          or through an iterative method for solving these   
+          systems.  If an iterative method is used, the   
+          convergence test must be more stringent than   
+          the accuracy requirements for the eigenvalue   
+          approximations.   
+
+   \Usage:   
+    call dnaupd   
+       ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM,   
+         IPNTR, WORKD, WORKL, LWORKL, INFO )   
+
+   \Arguments   
+    IDO     Integer.  (INPUT/OUTPUT)   
+            Reverse communication flag.  IDO must be zero on the first   
+            call to dnaupd.  IDO will be set internally to   
+            indicate the type of operation to be performed.  Control is   
+            then given back to the calling routine which has the   
+            responsibility to carry out the requested operation and call   
+            dnaupd with the result.  The operand is given in   
+            WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)).   
+            -------------------------------------------------------------   
+            IDO =  0: first call to the reverse communication interface   
+            IDO = -1: compute  Y = OP * X  where   
+                      IPNTR(1) is the pointer into WORKD for X,   
+                      IPNTR(2) is the pointer into WORKD for Y.   
+                      This is for the initialization phase to force the   
+                      starting vector into the range of OP.   
+            IDO =  1: compute  Y = OP * X  where   
+                      IPNTR(1) is the pointer into WORKD for X,   
+                      IPNTR(2) is the pointer into WORKD for Y.   
+                      In mode 3 and 4, the vector B * X is already   
+                      available in WORKD(ipntr(3)).  It does not   
+                      need to be recomputed in forming OP * X.   
+            IDO =  2: compute  Y = B * X  where   
+                      IPNTR(1) is the pointer into WORKD for X,   
+                      IPNTR(2) is the pointer into WORKD for Y.   
+            IDO =  3: compute the IPARAM(8) real and imaginary parts   
+                      of the shifts where INPTR(14) is the pointer   
+                      into WORKL for placing the shifts. See Remark   
+                      5 below.   
+            IDO = 99: done   
+            -------------------------------------------------------------   
+
+    BMAT    Character*1.  (INPUT)   
+            BMAT specifies the type of the matrix B that defines the   
+            semi-inner product for the operator OP.   
+            BMAT = 'I' -> standard eigenvalue problem A*x = lambda*x   
+            BMAT = 'G' -> generalized eigenvalue problem A*x = lambda*B*x   
+
+    N       Integer.  (INPUT)   
+            Dimension of the eigenproblem.   
+
+    WHICH   Character*2.  (INPUT)   
+            'LM' -> want the NEV eigenvalues of largest magnitude.   
+            'SM' -> want the NEV eigenvalues of smallest magnitude.   
+            'LR' -> want the NEV eigenvalues of largest real part.   
+            'SR' -> want the NEV eigenvalues of smallest real part.   
+            'LI' -> want the NEV eigenvalues of largest imaginary part.   
+            'SI' -> want the NEV eigenvalues of smallest imaginary part.   
+
+    NEV     Integer.  (INPUT)   
+            Number of eigenvalues of OP to be computed. 0 < NEV < N-1.   
+
+    TOL     Double precision scalar.  (INPUT)   
+            Stopping criterion: the relative accuracy of the Ritz value   
+            is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I))   
+            where ABS(RITZ(I)) is the magnitude when RITZ(I) is complex.   
+            DEFAULT = DLAMCH('EPS')  (machine precision as computed   
+                      by the LAPACK auxiliary subroutine DLAMCH).   
+
+    RESID   Double precision array of length N.  (INPUT/OUTPUT)   
+            On INPUT:   
+            If INFO .EQ. 0, a random initial residual vector is used.   
+            If INFO .NE. 0, RESID contains the initial residual vector,   
+                            possibly from a previous run.   
+            On OUTPUT:   
+            RESID contains the final residual vector.   
+
+    NCV     Integer.  (INPUT)   
+            Number of columns of the matrix V. NCV must satisfy the two   
+            inequalities 2 <= NCV-NEV and NCV <= N.   
+            This will indicate how many Arnoldi vectors are generated   
+            at each iteration.  After the startup phase in which NEV   
+            Arnoldi vectors are generated, the algorithm generates   
+            approximately NCV-NEV Arnoldi vectors at each subsequent update   
+            iteration. Most of the cost in generating each Arnoldi vector is   
+            in the matrix-vector operation OP*x.   
+            NOTE: 2 <= NCV-NEV in order that complex conjugate pairs of Ritz   
+            values are kept together. (See remark 4 below)   
+
+    V       Double precision array N by NCV.  (OUTPUT)   
+            Contains the final set of Arnoldi basis vectors.   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling program.   
+
+    IPARAM  Integer array of length 11.  (INPUT/OUTPUT)   
+            IPARAM(1) = ISHIFT: method for selecting the implicit shifts.   
+            The shifts selected at each iteration are used to restart   
+            the Arnoldi iteration in an implicit fashion.   
+            -------------------------------------------------------------   
+            ISHIFT = 0: the shifts are provided by the user via   
+                        reverse communication.  The real and imaginary   
+                        parts of the NCV eigenvalues of the Hessenberg   
+                        matrix H are returned in the part of the WORKL   
+                        array corresponding to RITZR and RITZI. See remark   
+                        5 below.   
+            ISHIFT = 1: exact shifts with respect to the current   
+                        Hessenberg matrix H.  This is equivalent to   
+                        restarting the iteration with a starting vector   
+                        that is a linear combination of approximate Schur   
+                        vectors associated with the "wanted" Ritz values.   
+            -------------------------------------------------------------   
+
+            IPARAM(2) = No longer referenced.   
+
+            IPARAM(3) = MXITER   
+            On INPUT:  maximum number of Arnoldi update iterations allowed.   
+            On OUTPUT: actual number of Arnoldi update iterations taken.   
+
+            IPARAM(4) = NB: blocksize to be used in the recurrence.   
+            The code currently works only for NB = 1.   
+
+            IPARAM(5) = NCONV: number of "converged" Ritz values.   
+            This represents the number of Ritz values that satisfy   
+            the convergence criterion.   
+
+            IPARAM(6) = IUPD   
+            No longer referenced. Implicit restarting is ALWAYS used.   
+
+            IPARAM(7) = MODE   
+            On INPUT determines what type of eigenproblem is being solved.   
+            Must be 1,2,3,4; See under \Description of dnaupd for the   
+            four modes available.   
+
+            IPARAM(8) = NP   
+            When ido = 3 and the user provides shifts through reverse   
+            communication (IPARAM(1)=0), dnaupd returns NP, the number   
+            of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark   
+            5 below.   
+
+            IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO,   
+            OUTPUT: NUMOP  = total number of OP*x operations,   
+                    NUMOPB = total number of B*x operations if BMAT='G',   
+                    NUMREO = total number of steps of re-orthogonalization.   
+
+    IPNTR   Integer array of length 14.  (OUTPUT)   
+            Pointer to mark the starting locations in the WORKD and WORKL   
+            arrays for matrices/vectors used by the Arnoldi iteration.   
+            -------------------------------------------------------------   
+            IPNTR(1): pointer to the current operand vector X in WORKD.   
+            IPNTR(2): pointer to the current result vector Y in WORKD.   
+            IPNTR(3): pointer to the vector B * X in WORKD when used in   
+                      the shift-and-invert mode.   
+            IPNTR(4): pointer to the next available location in WORKL   
+                      that is untouched by the program.   
+            IPNTR(5): pointer to the NCV by NCV upper Hessenberg matrix   
+                      H in WORKL.   
+            IPNTR(6): pointer to the real part of the ritz value array   
+                      RITZR in WORKL.   
+            IPNTR(7): pointer to the imaginary part of the ritz value array   
+                      RITZI in WORKL.   
+            IPNTR(8): pointer to the Ritz estimates in array WORKL associated   
+                      with the Ritz values located in RITZR and RITZI in WORKL.   
+
+            IPNTR(14): pointer to the NP shifts in WORKL. See Remark 5 below.   
+
+            Note: IPNTR(9:13) is only referenced by dneupd. See Remark 2 below.   
+
+            IPNTR(9):  pointer to the real part of the NCV RITZ values of the   
+                       original system.   
+            IPNTR(10): pointer to the imaginary part of the NCV RITZ values of   
+                       the original system.   
+            IPNTR(11): pointer to the NCV corresponding error bounds.   
+            IPNTR(12): pointer to the NCV by NCV upper quasi-triangular   
+                       Schur matrix for H.   
+            IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors   
+                       of the upper Hessenberg matrix H. Only referenced by   
+                       dneupd if RVEC = .TRUE. See Remark 2 below.   
+            -------------------------------------------------------------   
+
+    WORKD   Double precision work array of length 3*N.  (REVERSE COMMUNICATION)   
+            Distributed array to be used in the basic Arnoldi iteration   
+            for reverse communication.  The user should not use WORKD   
+            as temporary workspace during the iteration. Upon termination   
+            WORKD(1:N) contains B*RESID(1:N). If an invariant subspace   
+            associated with the converged Ritz values is desired, see remark   
+            2 below, subroutine dneupd uses this output.   
+            See Data Distribution Note below.   
+
+    WORKL   Double precision work array of length LWORKL.  (OUTPUT/WORKSPACE)   
+            Private (replicated) array on each PE or array allocated on   
+            the front end.  See Data Distribution Note below.   
+
+    LWORKL  Integer.  (INPUT)   
+            LWORKL must be at least 3*NCV**2 + 6*NCV.   
+
+    INFO    Integer.  (INPUT/OUTPUT)   
+            If INFO .EQ. 0, a randomly initial residual vector is used.   
+            If INFO .NE. 0, RESID contains the initial residual vector,   
+                            possibly from a previous run.   
+            Error flag on output.   
+            =  0: Normal exit.   
+            =  1: Maximum number of iterations taken.   
+                  All possible eigenvalues of OP has been found. IPARAM(5)   
+                  returns the number of wanted converged Ritz values.   
+            =  2: No longer an informational error. Deprecated starting   
+                  with release 2 of ARPACK.   
+            =  3: No shifts could be applied during a cycle of the   
+                  Implicitly restarted Arnoldi iteration. One possibility   
+                  is to increase the size of NCV relative to NEV.   
+                  See remark 4 below.   
+            = -1: N must be positive.   
+            = -2: NEV must be positive.   
+            = -3: NCV-NEV >= 2 and less than or equal to N.   
+            = -4: The maximum number of Arnoldi update iteration   
+                  must be greater than zero.   
+            = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'   
+            = -6: BMAT must be one of 'I' or 'G'.   
+            = -7: Length of private work array is not sufficient.   
+            = -8: Error return from LAPACK eigenvalue calculation;   
+            = -9: Starting vector is zero.   
+            = -10: IPARAM(7) must be 1,2,3,4.   
+            = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable.   
+            = -12: IPARAM(1) must be equal to 0 or 1.   
+            = -9999: Could not build an Arnoldi factorization.   
+                     IPARAM(5) returns the size of the current Arnoldi   
+                     factorization.   
+
+   \Remarks   
+    1. The computed Ritz values are approximate eigenvalues of OP. The   
+       selection of WHICH should be made with this in mind when   
+       Mode = 3 and 4.  After convergence, approximate eigenvalues of the   
+       original problem may be obtained with the ARPACK subroutine dneupd.   
+
+    2. If a basis for the invariant subspace corresponding to the converged Ritz   
+       values is needed, the user must call dneupd immediately following   
+       completion of dnaupd. This is new starting with release 2 of ARPACK.   
+
+    3. If M can be factored into a Cholesky factorization M = LL'   
+       then Mode = 2 should not be selected.  Instead one should use   
+       Mode = 1 with  OP = inv(L)*A*inv(L').  Appropriate triangular   
+       linear systems should be solved with L and L' rather   
+       than computing inverses.  After convergence, an approximate   
+       eigenvector z of the original problem is recovered by solving   
+       L'z = x  where x is a Ritz vector of OP.   
+
+    4. At present there is no a-priori analysis to guide the selection   
+       of NCV relative to NEV.  The only formal requrement is that NCV > NEV + 2.   
+       However, it is recommended that NCV .ge. 2*NEV+1.  If many problems of   
+       the same type are to be solved, one should experiment with increasing   
+       NCV while keeping NEV fixed for a given test problem.  This will   
+       usually decrease the required number of OP*x operations but it   
+       also increases the work and storage required to maintain the orthogonal   
+       basis vectors.  The optimal "cross-over" with respect to CPU time   
+       is problem dependent and must be determined empirically.   
+       See Chapter 8 of Reference 2 for further information.   
+
+    5. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the   
+       NP = IPARAM(8) real and imaginary parts of the shifts in locations   
+           real part                  imaginary part   
+           -----------------------    --------------   
+       1   WORKL(IPNTR(14))           WORKL(IPNTR(14)+NP)   
+       2   WORKL(IPNTR(14)+1)         WORKL(IPNTR(14)+NP+1)   
+                          .                          .   
+                          .                          .   
+                          .                          .   
+       NP  WORKL(IPNTR(14)+NP-1)      WORKL(IPNTR(14)+2*NP-1).   
+
+       Only complex conjugate pairs of shifts may be applied and the pairs   
+       must be placed in consecutive locations. The real part of the   
+       eigenvalues of the current upper Hessenberg matrix are located in   
+       WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1) and the imaginary part   
+       in WORKL(IPNTR(7)) through WORKL(IPNTR(7)+NCV-1). They are ordered   
+       according to the order defined by WHICH. The complex conjugate   
+       pairs are kept together and the associated Ritz estimates are located in   
+       WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1).   
+
+   -----------------------------------------------------------------------   
+
+   \Data Distribution Note:   
+
+    Fortran-D syntax:   
+    ================   
+    Double precision resid(n), v(ldv,ncv), workd(3*n), workl(lworkl)   
+    decompose  d1(n), d2(n,ncv)   
+    align      resid(i) with d1(i)   
+    align      v(i,j)   with d2(i,j)   
+    align      workd(i) with d1(i)     range (1:n)   
+    align      workd(i) with d1(i-n)   range (n+1:2*n)   
+    align      workd(i) with d1(i-2*n) range (2*n+1:3*n)   
+    distribute d1(block), d2(block,:)   
+    replicated workl(lworkl)   
+
+    Cray MPP syntax:   
+    ===============   
+    Double precision  resid(n), v(ldv,ncv), workd(n,3), workl(lworkl)   
+    shared     resid(block), v(block,:), workd(block,:)   
+    replicated workl(lworkl)   
+
+    CM2/CM5 syntax:   
+    ==============   
+
+   -----------------------------------------------------------------------   
+
+       include   'ex-nonsym.doc'   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+    3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for   
+       Real Matrices", Linear Algebra and its Applications, vol 88/89,   
+       pp 575-595, (1987).   
+
+   \Routines called:   
+       dnaup2  ARPACK routine that implements the Implicitly Restarted   
+               Arnoldi Iteration.   
+       ivout   ARPACK utility routine that prints integers.   
+       second  ARPACK utility routine for timing.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlamch  LAPACK routine that determines machine constants.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       12/16/93: Version '1.1'   
+
+   \SCCS Information: @(#)   
+   FILE: naupd.F   SID: 2.5   DATE OF SID: 8/27/96   RELEASE: 2   
+
+   \Remarks   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdnaupd_(integer *ido, char *bmat, integer *n, char *
+	which, integer *nev, doublereal *tol, doublereal *resid, integer *ncv,
+	 doublereal *v, integer *ldv, integer *iparam, integer *ipntr, 
+	doublereal *workd, doublereal *workl, integer *lworkl, integer *info)
+{
+    /* Format strings */
+    static char fmt_1000[] = "(//,5x,\002==================================="
+	    "==========\002,/5x,\002= Nonsymmetric implicit Arnoldi update co"
+	    "de =\002,/5x,\002= Version Number: \002,\002 2.4\002,21x,\002 "
+	    "=\002,/5x,\002= Version Date:   \002,\002 07/31/96\002,16x,\002 ="
+	    "\002,/5x,\002=============================================\002,/"
+	    "5x,\002= Summary of timing statistics              =\002,/5x,"
+	    "\002=============================================\002,//)";
+    static char fmt_1100[] = "(5x,\002Total number update iterations        "
+	    "     = \002,i5,/5x,\002Total number of OP*x operations          "
+	    "  = \002,i5,/5x,\002Total number of B*x operations             = "
+	    "\002,i5,/5x,\002Total number of reorthogonalization steps  = "
+	    "\002,i5,/5x,\002Total number of iterative refinement steps = "
+	    "\002,i5,/5x,\002Total number of restart steps              = "
+	    "\002,i5,/5x,\002Total time in user OP*x operation          = "
+	    "\002,f12.6,/5x,\002Total time in user B*x operation           ="
+	    " \002,f12.6,/5x,\002Total time in Arnoldi update routine       = "
+	    "\002,f12.6,/5x,\002Total time in naup2 routine                ="
+	    " \002,f12.6,/5x,\002Total time in basic Arnoldi iteration loop = "
+	    "\002,f12.6,/5x,\002Total time in reorthogonalization phase    ="
+	    " \002,f12.6,/5x,\002Total time in (re)start vector generation  = "
+	    "\002,f12.6,/5x,\002Total time in Hessenberg eig. subproblem   ="
+	    " \002,f12.6,/5x,\002Total time in getting the shifts           = "
+	    "\002,f12.6,/5x,\002Total time in applying the shifts          ="
+	    " \002,f12.6,/5x,\002Total time in convergence testing          = "
+	    "\002,f12.6,/5x,\002Total time in computing final Ritz vectors ="
+	    " \002,f12.6/)";
+
+    /* System generated locals */
+    integer v_dim1, v_offset, i__1, i__2;
+
+    /* Builtin functions */
+    integer s_cmp(char *, char *, ftnlen, ftnlen), s_wsfe(cilist *), e_wsfe(
+	    void), do_fio(integer *, char *, ftnlen);
+
+    /* Local variables */
+    integer j;
+    real t0, t1;
+    IGRAPH_F77_SAVE integer nb, ih, iq, np, iw, ldh, ldq;
+    integer nbx = 0;
+    IGRAPH_F77_SAVE integer nev0, mode;
+    integer ierr;
+    IGRAPH_F77_SAVE integer iupd, next;
+    integer nopx = 0;
+    IGRAPH_F77_SAVE integer levec;
+    real trvec, tmvbx;
+    IGRAPH_F77_SAVE integer ritzi;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen), igraphivout_(integer *, integer *, integer *
+	    , integer *, char *, ftnlen);
+    IGRAPH_F77_SAVE integer ritzr;
+    extern /* Subroutine */ int igraphdnaup2_(integer *, char *, integer *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, doublereal *,
+	     doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    real tnaup2, tgetv0;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphsecond_(real *);
+    integer logfil, ndigit;
+    real tneigh;
+    integer mnaupd = 0;
+    IGRAPH_F77_SAVE integer ishift;
+    integer nitref;
+    IGRAPH_F77_SAVE integer bounds;
+    real tnaupd;
+    extern /* Subroutine */ int igraphdstatn_(void);
+    real titref, tnaitr;
+    IGRAPH_F77_SAVE integer msglvl;
+    real tngets, tnapps, tnconv;
+    IGRAPH_F77_SAVE integer mxiter;
+    integer nrorth = 0, nrstrt = 0;
+    real tmvopx;
+
+    /* Fortran I/O blocks */
+    static cilist io___30 = { 0, 6, 0, fmt_1000, 0 };
+    static cilist io___31 = { 0, 6, 0, fmt_1100, 0 };
+
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    --iparam;
+    --ipntr;
+    --workl;
+
+    /* Function Body */
+    if (*ido == 0) {
+
+/*        %-------------------------------%   
+          | Initialize timing statistics  |   
+          | & message level for debugging |   
+          %-------------------------------% */
+
+	igraphdstatn_();
+	igraphsecond_(&t0);
+	msglvl = mnaupd;
+
+/*        %----------------%   
+          | Error checking |   
+          %----------------% */
+
+	ierr = 0;
+	ishift = iparam[1];
+	levec = iparam[2];
+	mxiter = iparam[3];
+	nb = iparam[4];
+
+/*        %--------------------------------------------%   
+          | Revision 2 performs only implicit restart. |   
+          %--------------------------------------------% */
+
+	iupd = 1;
+	mode = iparam[7];
+
+	if (*n <= 0) {
+	    ierr = -1;
+	} else if (*nev <= 0) {
+	    ierr = -2;
+	} else if (*ncv <= *nev + 1 || *ncv > *n) {
+	    ierr = -3;
+	} else if (mxiter <= 0) {
+	    ierr = -4;
+	} else if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) != 0 && s_cmp(
+		which, "SM", (ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, "LR", 
+		(ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, "SR", (ftnlen)2, (
+		ftnlen)2) != 0 && s_cmp(which, "LI", (ftnlen)2, (ftnlen)2) != 
+		0 && s_cmp(which, "SI", (ftnlen)2, (ftnlen)2) != 0) {
+	    ierr = -5;
+	} else if (*(unsigned char *)bmat != 'I' && *(unsigned char *)bmat != 
+		'G') {
+	    ierr = -6;
+	} else /* if(complicated condition) */ {
+/* Computing 2nd power */
+	    i__1 = *ncv;
+	    if (*lworkl < i__1 * i__1 * 3 + *ncv * 6) {
+		ierr = -7;
+	    } else if (mode < 1 || mode > 5) {
+		ierr = -10;
+	    } else if (mode == 1 && *(unsigned char *)bmat == 'G') {
+		ierr = -11;
+	    } else if (ishift < 0 || ishift > 1) {
+		ierr = -12;
+	    }
+	}
+
+/*        %------------%   
+          | Error Exit |   
+          %------------% */
+
+	if (ierr != 0) {
+	    *info = ierr;
+	    *ido = 99;
+	    goto L9000;
+	}
+
+/*        %------------------------%   
+          | Set default parameters |   
+          %------------------------% */
+
+	if (nb <= 0) {
+	    nb = 1;
+	}
+	if (*tol <= 0.) {
+	    *tol = igraphdlamch_("EpsMach");
+	}
+
+/*        %----------------------------------------------%   
+          | NP is the number of additional steps to      |   
+          | extend the length NEV Lanczos factorization. |   
+          | NEV0 is the local variable designating the   |   
+          | size of the invariant subspace desired.      |   
+          %----------------------------------------------% */
+
+	np = *ncv - *nev;
+	nev0 = *nev;
+
+/*        %-----------------------------%   
+          | Zero out internal workspace |   
+          %-----------------------------%   
+
+   Computing 2nd power */
+	i__2 = *ncv;
+	i__1 = i__2 * i__2 * 3 + *ncv * 6;
+	for (j = 1; j <= i__1; ++j) {
+	    workl[j] = 0.;
+/* L10: */
+	}
+
+/*        %-------------------------------------------------------------%   
+          | Pointer into WORKL for address of H, RITZ, BOUNDS, Q        |   
+          | etc... and the remaining workspace.                         |   
+          | Also update pointer to be used on output.                   |   
+          | Memory is laid out as follows:                              |   
+          | workl(1:ncv*ncv) := generated Hessenberg matrix             |   
+          | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary        |   
+          |                                   parts of ritz values      |   
+          | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds        |   
+          | workl(ncv*ncv+3*ncv+1:2*ncv*ncv+3*ncv) := rotation matrix Q |   
+          | workl(2*ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) := workspace       |   
+          | The final workspace is needed by subroutine dneigh called   |   
+          | by dnaup2. Subroutine dneigh calls LAPACK routines for      |   
+          | calculating eigenvalues and the last row of the eigenvector |   
+          | matrix.                                                     |   
+          %-------------------------------------------------------------% */
+
+	ldh = *ncv;
+	ldq = *ncv;
+	ih = 1;
+	ritzr = ih + ldh * *ncv;
+	ritzi = ritzr + *ncv;
+	bounds = ritzi + *ncv;
+	iq = bounds + *ncv;
+	iw = iq + ldq * *ncv;
+/* Computing 2nd power */
+	i__1 = *ncv;
+	next = iw + i__1 * i__1 + *ncv * 3;
+
+	ipntr[4] = next;
+	ipntr[5] = ih;
+	ipntr[6] = ritzr;
+	ipntr[7] = ritzi;
+	ipntr[8] = bounds;
+	ipntr[14] = iw;
+
+    }
+
+/*     %-------------------------------------------------------%   
+       | Carry out the Implicitly restarted Arnoldi Iteration. |   
+       %-------------------------------------------------------% */
+
+    igraphdnaup2_(ido, bmat, n, which, &nev0, &np, tol, &resid[1], &mode, &iupd, &
+	    ishift, &mxiter, &v[v_offset], ldv, &workl[ih], &ldh, &workl[
+	    ritzr], &workl[ritzi], &workl[bounds], &workl[iq], &ldq, &workl[
+	    iw], &ipntr[1], &workd[1], info);
+
+/*     %--------------------------------------------------%   
+       | ido .ne. 99 implies use of reverse communication |   
+       | to compute operations involving OP or shifts.    |   
+       %--------------------------------------------------% */
+
+    if (*ido == 3) {
+	iparam[8] = np;
+    }
+    if (*ido != 99) {
+	goto L9000;
+    }
+
+    iparam[3] = mxiter;
+    iparam[5] = np;
+    iparam[9] = nopx;
+    iparam[10] = nbx;
+    iparam[11] = nrorth;
+
+/*     %------------------------------------%   
+       | Exit if there was an informational |   
+       | error within dnaup2.               |   
+       %------------------------------------% */
+
+    if (*info < 0) {
+	goto L9000;
+    }
+    if (*info == 2) {
+	*info = 3;
+    }
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, &mxiter, &ndigit, "_naupd: Number of update i"
+		"terations taken", (ftnlen)41);
+	igraphivout_(&logfil, &c__1, &np, &ndigit, "_naupd: Number of wanted \"con"
+		"verged\" Ritz values", (ftnlen)48);
+	igraphdvout_(&logfil, &np, &workl[ritzr], &ndigit, "_naupd: Real part of t"
+		"he final Ritz values", (ftnlen)42);
+	igraphdvout_(&logfil, &np, &workl[ritzi], &ndigit, "_naupd: Imaginary part"
+		" of the final Ritz values", (ftnlen)47);
+	igraphdvout_(&logfil, &np, &workl[bounds], &ndigit, "_naupd: Associated Ri"
+		"tz estimates", (ftnlen)33);
+    }
+
+    igraphsecond_(&t1);
+    tnaupd = t1 - t0;
+
+    if (msglvl > 0) {
+
+/*        %--------------------------------------------------------%   
+          | Version Number & Version Date are defined in version.h |   
+          %--------------------------------------------------------% */
+
+	s_wsfe(&io___30);
+	e_wsfe();
+	s_wsfe(&io___31);
+	do_fio(&c__1, (char *)&mxiter, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nopx, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nbx, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nrorth, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nitref, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nrstrt, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&tmvopx, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tmvbx, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tnaupd, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tnaup2, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tnaitr, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&titref, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tgetv0, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tneigh, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tngets, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tnapps, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tnconv, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&trvec, (ftnlen)sizeof(real));
+	e_wsfe();
+    }
+
+L9000:
+
+    return 0;
+
+/*     %---------------%   
+       | End of dnaupd |   
+       %---------------% */
+
+} /* igraphdnaupd_ */
+
diff --git a/igraph/src/dnconv.c b/igraph/src/dnconv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dnconv.c
@@ -0,0 +1,178 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b3 = .66666666666666663;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dnconv   
+
+   \Description:   
+    Convergence testing for the nonsymmetric Arnoldi eigenvalue routine.   
+
+   \Usage:   
+    call dnconv   
+       ( N, RITZR, RITZI, BOUNDS, TOL, NCONV )   
+
+   \Arguments   
+    N       Integer.  (INPUT)   
+            Number of Ritz values to check for convergence.   
+
+    RITZR,  Double precision arrays of length N.  (INPUT)   
+    RITZI   Real and imaginary parts of the Ritz values to be checked   
+            for convergence.   
+    BOUNDS  Double precision array of length N.  (INPUT)   
+            Ritz estimates for the Ritz values in RITZR and RITZI.   
+
+    TOL     Double precision scalar.  (INPUT)   
+            Desired backward error for a Ritz value to be considered   
+            "converged".   
+
+    NCONV   Integer scalar.  (OUTPUT)   
+            Number of "converged" Ritz values.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \Routines called:   
+       second  ARPACK utility routine for timing.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlapy2  LAPACK routine to compute sqrt(x**2+y**2) carefully.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/92: Version ' 2.1'   
+
+   \SCCS Information: @(#)   
+   FILE: nconv.F   SID: 2.3   DATE OF SID: 4/20/96   RELEASE: 2   
+
+   \Remarks   
+       1. xxxx   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdnconv_(integer *n, doublereal *ritzr, doublereal *ritzi,
+	 doublereal *bounds, doublereal *tol, integer *nconv)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double pow_dd(doublereal *, doublereal *);
+
+    /* Local variables */
+    integer i__;
+    real t0, t1;
+    doublereal eps23, temp;
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *), igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphsecond_(real *);
+    real tnconv = 0.;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       %-------------------------------------------------------------%   
+       | Convergence test: unlike in the symmetric code, I am not    |   
+       | using things like refined error bounds and gap condition    |   
+       | because I don't know the exact equivalent concept.          |   
+       |                                                             |   
+       | Instead the i-th Ritz value is considered "converged" when: |   
+       |                                                             |   
+       |     bounds(i) .le. ( TOL * | ritz | )                       |   
+       |                                                             |   
+       | for some appropriate choice of norm.                        |   
+       %-------------------------------------------------------------%   
+
+       Parameter adjustments */
+    --bounds;
+    --ritzi;
+    --ritzr;
+
+    /* Function Body */
+    igraphsecond_(&t0);
+
+/*     %---------------------------------%   
+       | Get machine dependent constant. |   
+       %---------------------------------% */
+
+    eps23 = igraphdlamch_("Epsilon-Machine");
+    eps23 = pow_dd(&eps23, &c_b3);
+
+    *nconv = 0;
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+/* Computing MAX */
+	d__1 = eps23, d__2 = igraphdlapy2_(&ritzr[i__], &ritzi[i__]);
+	temp = max(d__1,d__2);
+	if (bounds[i__] <= *tol * temp) {
+	    ++(*nconv);
+	}
+/* L20: */
+    }
+
+    igraphsecond_(&t1);
+    tnconv += t1 - t0;
+
+    return 0;
+
+/*     %---------------%   
+       | End of dnconv |   
+       %---------------% */
+
+} /* igraphdnconv_ */
+
diff --git a/igraph/src/dneigh.c b/igraph/src/dneigh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dneigh.c
@@ -0,0 +1,377 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static logical c_true = TRUE_;
+static integer c__1 = 1;
+static doublereal c_b18 = 1.;
+static doublereal c_b20 = 0.;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dneigh   
+
+   \Description:   
+    Compute the eigenvalues of the current upper Hessenberg matrix   
+    and the corresponding Ritz estimates given the current residual norm.   
+
+   \Usage:   
+    call dneigh   
+       ( RNORM, N, H, LDH, RITZR, RITZI, BOUNDS, Q, LDQ, WORKL, IERR )   
+
+   \Arguments   
+    RNORM   Double precision scalar.  (INPUT)   
+            Residual norm corresponding to the current upper Hessenberg   
+            matrix H.   
+
+    N       Integer.  (INPUT)   
+            Size of the matrix H.   
+
+    H       Double precision N by N array.  (INPUT)   
+            H contains the current upper Hessenberg matrix.   
+
+    LDH     Integer.  (INPUT)   
+            Leading dimension of H exactly as declared in the calling   
+            program.   
+
+    RITZR,  Double precision arrays of length N.  (OUTPUT)   
+    RITZI   On output, RITZR(1:N) (resp. RITZI(1:N)) contains the real   
+            (respectively imaginary) parts of the eigenvalues of H.   
+
+    BOUNDS  Double precision array of length N.  (OUTPUT)   
+            On output, BOUNDS contains the Ritz estimates associated with   
+            the eigenvalues RITZR and RITZI.  This is equal to RNORM   
+            times the last components of the eigenvectors corresponding   
+            to the eigenvalues in RITZR and RITZI.   
+
+    Q       Double precision N by N array.  (WORKSPACE)   
+            Workspace needed to store the eigenvectors of H.   
+
+    LDQ     Integer.  (INPUT)   
+            Leading dimension of Q exactly as declared in the calling   
+            program.   
+
+    WORKL   Double precision work array of length N**2 + 3*N.  (WORKSPACE)   
+            Private (replicated) array on each PE or array allocated on   
+            the front end.  This is needed to keep the full Schur form   
+            of H and also in the calculation of the eigenvectors of H.   
+
+    IERR    Integer.  (OUTPUT)   
+            Error exit flag from dlaqrb or dtrevc.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \Routines called:   
+       dlaqrb  ARPACK routine to compute the real Schur form of an   
+               upper Hessenberg matrix and last row of the Schur vectors.   
+       second  ARPACK utility routine for timing.   
+       dmout   ARPACK utility routine that prints matrices   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlacpy  LAPACK matrix copy routine.   
+       dlapy2  LAPACK routine to compute sqrt(x**2+y**2) carefully.   
+       dtrevc  LAPACK routine to compute the eigenvectors of a matrix   
+               in upper quasi-triangular form   
+       dgemv   Level 2 BLAS routine for matrix vector multiplication.   
+       dcopy   Level 1 BLAS that copies one vector to another .   
+       dnrm2   Level 1 BLAS that computes the norm of a vector.   
+       dscal   Level 1 BLAS that scales a vector.   
+
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/92: Version ' 2.1'   
+
+   \SCCS Information: @(#)   
+   FILE: neigh.F   SID: 2.3   DATE OF SID: 4/20/96   RELEASE: 2   
+
+   \Remarks   
+       None   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdneigh_(doublereal *rnorm, integer *n, doublereal *h__, 
+	integer *ldh, doublereal *ritzr, doublereal *ritzi, doublereal *
+	bounds, doublereal *q, integer *ldq, doublereal *workl, integer *ierr)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, q_dim1, q_offset, i__1;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    integer i__;
+    real t0, t1;
+    doublereal vl[1], temp;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    integer iconj;
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *), igraphdmout_(integer *, 
+	    integer *, integer *, doublereal *, integer *, integer *, char *, 
+	    ftnlen), igraphdvout_(integer *, integer *, doublereal *, integer *, 
+	    char *, ftnlen);
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlaqrb_(logical *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    doublereal *, integer *);
+    integer mneigh = 0;
+    extern /* Subroutine */ int igraphsecond_(real *), igraphdlacpy_(char *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *);
+    integer logfil, ndigit;
+    logical select[1];
+    real tneigh = 0.;
+    extern /* Subroutine */ int igraphdtrevc_(char *, char *, logical *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, integer *, integer *, doublereal *, integer *);
+    integer msglvl;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %------------------------%   
+       | Local Scalars & Arrays |   
+       %------------------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+
+       %-------------------------------%   
+       | Initialize timing statistics  |   
+       | & message level for debugging |   
+       %-------------------------------%   
+
+       Parameter adjustments */
+    --workl;
+    --bounds;
+    --ritzi;
+    --ritzr;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    q_dim1 = *ldq;
+    q_offset = 1 + q_dim1;
+    q -= q_offset;
+
+    /* Function Body */
+    igraphsecond_(&t0);
+    msglvl = mneigh;
+
+    if (msglvl > 2) {
+	igraphdmout_(&logfil, n, n, &h__[h_offset], ldh, &ndigit, "_neigh: Enterin"
+		"g upper Hessenberg matrix H ", (ftnlen)43);
+    }
+
+/*     %-----------------------------------------------------------%   
+       | 1. Compute the eigenvalues, the last components of the    |   
+       |    corresponding Schur vectors and the full Schur form T  |   
+       |    of the current upper Hessenberg matrix H.              |   
+       | dlaqrb returns the full Schur form of H in WORKL(1:N**2)  |   
+       | and the last components of the Schur vectors in BOUNDS.   |   
+       %-----------------------------------------------------------% */
+
+    igraphdlacpy_("All", n, n, &h__[h_offset], ldh, &workl[1], n);
+    igraphdlaqrb_(&c_true, n, &c__1, n, &workl[1], n, &ritzr[1], &ritzi[1], &bounds[
+	    1], ierr);
+    if (*ierr != 0) {
+	goto L9000;
+    }
+
+    if (msglvl > 1) {
+	igraphdvout_(&logfil, n, &bounds[1], &ndigit, "_neigh: last row of the Sch"
+		"ur matrix for H", (ftnlen)42);
+    }
+
+/*     %-----------------------------------------------------------%   
+       | 2. Compute the eigenvectors of the full Schur form T and  |   
+       |    apply the last components of the Schur vectors to get  |   
+       |    the last components of the corresponding eigenvectors. |   
+       | Remember that if the i-th and (i+1)-st eigenvalues are    |   
+       | complex conjugate pairs, then the real & imaginary part   |   
+       | of the eigenvector components are split across adjacent   |   
+       | columns of Q.                                             |   
+       %-----------------------------------------------------------% */
+
+    igraphdtrevc_("R", "A", select, n, &workl[1], n, vl, n, &q[q_offset], ldq, n, n,
+	     &workl[*n * *n + 1], ierr);
+
+    if (*ierr != 0) {
+	goto L9000;
+    }
+
+/*     %------------------------------------------------%   
+       | Scale the returning eigenvectors so that their |   
+       | euclidean norms are all one. LAPACK subroutine |   
+       | dtrevc returns each eigenvector normalized so  |   
+       | that the element of largest magnitude has      |   
+       | magnitude 1; here the magnitude of a complex   |   
+       | number (x,y) is taken to be |x| + |y|.         |   
+       %------------------------------------------------% */
+
+    iconj = 0;
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	if ((d__1 = ritzi[i__], abs(d__1)) <= 0.) {
+
+/*           %----------------------%   
+             | Real eigenvalue case |   
+             %----------------------% */
+
+	    temp = igraphdnrm2_(n, &q[i__ * q_dim1 + 1], &c__1);
+	    d__1 = 1. / temp;
+	    igraphdscal_(n, &d__1, &q[i__ * q_dim1 + 1], &c__1);
+	} else {
+
+/*           %-------------------------------------------%   
+             | Complex conjugate pair case. Note that    |   
+             | since the real and imaginary part of      |   
+             | the eigenvector are stored in consecutive |   
+             | columns, we further normalize by the      |   
+             | square root of two.                       |   
+             %-------------------------------------------% */
+
+	    if (iconj == 0) {
+		d__1 = igraphdnrm2_(n, &q[i__ * q_dim1 + 1], &c__1);
+		d__2 = igraphdnrm2_(n, &q[(i__ + 1) * q_dim1 + 1], &c__1);
+		temp = igraphdlapy2_(&d__1, &d__2);
+		d__1 = 1. / temp;
+		igraphdscal_(n, &d__1, &q[i__ * q_dim1 + 1], &c__1);
+		d__1 = 1. / temp;
+		igraphdscal_(n, &d__1, &q[(i__ + 1) * q_dim1 + 1], &c__1);
+		iconj = 1;
+	    } else {
+		iconj = 0;
+	    }
+	}
+/* L10: */
+    }
+
+    igraphdgemv_("T", n, n, &c_b18, &q[q_offset], ldq, &bounds[1], &c__1, &c_b20, &
+	    workl[1], &c__1);
+
+    if (msglvl > 1) {
+	igraphdvout_(&logfil, n, &workl[1], &ndigit, "_neigh: Last row of the eige"
+		"nvector matrix for H", (ftnlen)48);
+    }
+
+/*     %----------------------------%   
+       | Compute the Ritz estimates |   
+       %----------------------------% */
+
+    iconj = 0;
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	if ((d__1 = ritzi[i__], abs(d__1)) <= 0.) {
+
+/*           %----------------------%   
+             | Real eigenvalue case |   
+             %----------------------% */
+
+	    bounds[i__] = *rnorm * (d__1 = workl[i__], abs(d__1));
+	} else {
+
+/*           %-------------------------------------------%   
+             | Complex conjugate pair case. Note that    |   
+             | since the real and imaginary part of      |   
+             | the eigenvector are stored in consecutive |   
+             | columns, we need to take the magnitude    |   
+             | of the last components of the two vectors |   
+             %-------------------------------------------% */
+
+	    if (iconj == 0) {
+		bounds[i__] = *rnorm * igraphdlapy2_(&workl[i__], &workl[i__ + 1]);
+		bounds[i__ + 1] = bounds[i__];
+		iconj = 1;
+	    } else {
+		iconj = 0;
+	    }
+	}
+/* L20: */
+    }
+
+    if (msglvl > 2) {
+	igraphdvout_(&logfil, n, &ritzr[1], &ndigit, "_neigh: Real part of the eig"
+		"envalues of H", (ftnlen)41);
+	igraphdvout_(&logfil, n, &ritzi[1], &ndigit, "_neigh: Imaginary part of th"
+		"e eigenvalues of H", (ftnlen)46);
+	igraphdvout_(&logfil, n, &bounds[1], &ndigit, "_neigh: Ritz estimates for "
+		"the eigenvalues of H", (ftnlen)47);
+    }
+
+    igraphsecond_(&t1);
+    tneigh += t1 - t0;
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dneigh |   
+       %---------------% */
+
+} /* igraphdneigh_ */
+
diff --git a/igraph/src/dneupd.c b/igraph/src/dneupd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dneupd.c
@@ -0,0 +1,1195 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b3 = .66666666666666663;
+static integer c__1 = 1;
+static doublereal c_b44 = 0.;
+static doublereal c_b45 = 1.;
+static logical c_true = TRUE_;
+static doublereal c_b71 = -1.;
+
+/* \BeginDoc   
+
+   \Name: dneupd   
+
+   \Description:   
+
+    This subroutine returns the converged approximations to eigenvalues   
+    of A*z = lambda*B*z and (optionally):   
+
+        (1) The corresponding approximate eigenvectors;   
+
+        (2) An orthonormal basis for the associated approximate   
+            invariant subspace;   
+
+        (3) Both.   
+
+    There is negligible additional cost to obtain eigenvectors.  An orthonormal   
+    basis is always computed.  There is an additional storage cost of n*nev   
+    if both are requested (in this case a separate array Z must be supplied).   
+
+    The approximate eigenvalues and eigenvectors of  A*z = lambda*B*z   
+    are derived from approximate eigenvalues and eigenvectors of   
+    of the linear operator OP prescribed by the MODE selection in the   
+    call to DNAUPD.  DNAUPD must be called before this routine is called.   
+    These approximate eigenvalues and vectors are commonly called Ritz   
+    values and Ritz vectors respectively.  They are referred to as such   
+    in the comments that follow.  The computed orthonormal basis for the   
+    invariant subspace corresponding to these Ritz values is referred to as a   
+    Schur basis.   
+
+    See documentation in the header of the subroutine DNAUPD for   
+    definition of OP as well as other terms and the relation of computed   
+    Ritz values and Ritz vectors of OP with respect to the given problem   
+    A*z = lambda*B*z.  For a brief description, see definitions of   
+    IPARAM(7), MODE and WHICH in the documentation of DNAUPD.   
+
+   \Usage:   
+    call dneupd   
+       ( RVEC, HOWMNY, SELECT, DR, DI, Z, LDZ, SIGMAR, SIGMAI, WORKEV, BMAT,   
+         N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL,   
+         LWORKL, INFO )   
+
+   \Arguments:   
+    RVEC    LOGICAL  (INPUT)   
+            Specifies whether a basis for the invariant subspace corresponding   
+            to the converged Ritz value approximations for the eigenproblem   
+            A*z = lambda*B*z is computed.   
+
+               RVEC = .FALSE.     Compute Ritz values only.   
+
+               RVEC = .TRUE.      Compute the Ritz vectors or Schur vectors.   
+                                  See Remarks below.   
+
+    HOWMNY  Character*1  (INPUT)   
+            Specifies the form of the basis for the invariant subspace   
+            corresponding to the converged Ritz values that is to be computed.   
+
+            = 'A': Compute NEV Ritz vectors;   
+            = 'P': Compute NEV Schur vectors;   
+            = 'S': compute some of the Ritz vectors, specified   
+                   by the logical array SELECT.   
+
+    SELECT  Logical array of dimension NCV.  (INPUT)   
+            If HOWMNY = 'S', SELECT specifies the Ritz vectors to be   
+            computed. To select the Ritz vector corresponding to a   
+            Ritz value (DR(j), DI(j)), SELECT(j) must be set to .TRUE..   
+            If HOWMNY = 'A' or 'P', SELECT is used as internal workspace.   
+
+    DR      Double precision array of dimension NEV+1.  (OUTPUT)   
+            If IPARAM(7) = 1,2 or 3 and SIGMAI=0.0  then on exit: DR contains   
+            the real part of the Ritz  approximations to the eigenvalues of   
+            A*z = lambda*B*z.   
+            If IPARAM(7) = 3, 4 and SIGMAI is not equal to zero, then on exit:   
+            DR contains the real part of the Ritz values of OP computed by   
+            DNAUPD. A further computation must be performed by the user   
+            to transform the Ritz values computed for OP by DNAUPD to those   
+            of the original system A*z = lambda*B*z. See remark 3 below.   
+
+    DI      Double precision array of dimension NEV+1.  (OUTPUT)   
+            On exit, DI contains the imaginary part of the Ritz value   
+            approximations to the eigenvalues of A*z = lambda*B*z associated   
+            with DR.   
+
+            NOTE: When Ritz values are complex, they will come in complex   
+                  conjugate pairs.  If eigenvectors are requested, the   
+                  corresponding Ritz vectors will also come in conjugate   
+                  pairs and the real and imaginary parts of these are   
+                  represented in two consecutive columns of the array Z   
+                  (see below).   
+
+    Z       Double precision N by NEV+1 array if RVEC = .TRUE. and HOWMNY = 'A'. (OUTPUT)   
+            On exit, if RVEC = .TRUE. and HOWMNY = 'A', then the columns of   
+            Z represent approximate eigenvectors (Ritz vectors) corresponding   
+            to the NCONV=IPARAM(5) Ritz values for eigensystem   
+            A*z = lambda*B*z.   
+
+            The complex Ritz vector associated with the Ritz value   
+            with positive imaginary part is stored in two consecutive   
+            columns.  The first column holds the real part of the Ritz   
+            vector and the second column holds the imaginary part.  The   
+            Ritz vector associated with the Ritz value with negative   
+            imaginary part is simply the complex conjugate of the Ritz vector   
+            associated with the positive imaginary part.   
+
+            If  RVEC = .FALSE. or HOWMNY = 'P', then Z is not referenced.   
+
+            NOTE: If if RVEC = .TRUE. and a Schur basis is not required,   
+            the array Z may be set equal to first NEV+1 columns of the Arnoldi   
+            basis array V computed by DNAUPD.  In this case the Arnoldi basis   
+            will be destroyed and overwritten with the eigenvector basis.   
+
+    LDZ     Integer.  (INPUT)   
+            The leading dimension of the array Z.  If Ritz vectors are   
+            desired, then  LDZ >= max( 1, N ).  In any case,  LDZ >= 1.   
+
+    SIGMAR  Double precision  (INPUT)   
+            If IPARAM(7) = 3 or 4, represents the real part of the shift.   
+            Not referenced if IPARAM(7) = 1 or 2.   
+
+    SIGMAI  Double precision  (INPUT)   
+            If IPARAM(7) = 3 or 4, represents the imaginary part of the shift.   
+            Not referenced if IPARAM(7) = 1 or 2. See remark 3 below.   
+
+    WORKEV  Double precision work array of dimension 3*NCV.  (WORKSPACE)   
+
+    **** The remaining arguments MUST be the same as for the   ****   
+    **** call to DNAUPD that was just completed.               ****   
+
+    NOTE: The remaining arguments   
+
+             BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR,   
+             WORKD, WORKL, LWORKL, INFO   
+
+           must be passed directly to DNEUPD following the last call   
+           to DNAUPD.  These arguments MUST NOT BE MODIFIED between   
+           the the last call to DNAUPD and the call to DNEUPD.   
+
+    Three of these parameters (V, WORKL, INFO) are also output parameters:   
+
+    V       Double precision N by NCV array.  (INPUT/OUTPUT)   
+
+            Upon INPUT: the NCV columns of V contain the Arnoldi basis   
+                        vectors for OP as constructed by DNAUPD .   
+
+            Upon OUTPUT: If RVEC = .TRUE. the first NCONV=IPARAM(5) columns   
+                         contain approximate Schur vectors that span the   
+                         desired invariant subspace.  See Remark 2 below.   
+
+            NOTE: If the array Z has been set equal to first NEV+1 columns   
+            of the array V and RVEC=.TRUE. and HOWMNY= 'A', then the   
+            Arnoldi basis held by V has been overwritten by the desired   
+            Ritz vectors.  If a separate array Z has been passed then   
+            the first NCONV=IPARAM(5) columns of V will contain approximate   
+            Schur vectors that span the desired invariant subspace.   
+
+    WORKL   Double precision work array of length LWORKL.  (OUTPUT/WORKSPACE)   
+            WORKL(1:ncv*ncv+3*ncv) contains information obtained in   
+            dnaupd.  They are not changed by dneupd.   
+            WORKL(ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv) holds the   
+            real and imaginary part of the untransformed Ritz values,   
+            the upper quasi-triangular matrix for H, and the   
+            associated matrix representation of the invariant subspace for H.   
+
+            Note: IPNTR(9:13) contains the pointer into WORKL for addresses   
+            of the above information computed by dneupd.   
+            -------------------------------------------------------------   
+            IPNTR(9):  pointer to the real part of the NCV RITZ values of the   
+                       original system.   
+            IPNTR(10): pointer to the imaginary part of the NCV RITZ values of   
+                       the original system.   
+            IPNTR(11): pointer to the NCV corresponding error bounds.   
+            IPNTR(12): pointer to the NCV by NCV upper quasi-triangular   
+                       Schur matrix for H.   
+            IPNTR(13): pointer to the NCV by NCV matrix of eigenvectors   
+                       of the upper Hessenberg matrix H. Only referenced by   
+                       dneupd if RVEC = .TRUE. See Remark 2 below.   
+            -------------------------------------------------------------   
+
+    INFO    Integer.  (OUTPUT)   
+            Error flag on output.   
+
+            =  0: Normal exit.   
+
+            =  1: The Schur form computed by LAPACK routine dlahqr   
+                  could not be reordered by LAPACK routine dtrsen.   
+                  Re-enter subroutine dneupd with IPARAM(5)=NCV and   
+                  increase the size of the arrays DR and DI to have   
+                  dimension at least dimension NCV and allocate at least NCV   
+                  columns for Z. NOTE: Not necessary if Z and V share   
+                  the same space. Please notify the authors if this error   
+                  occurs.   
+
+            = -1: N must be positive.   
+            = -2: NEV must be positive.   
+            = -3: NCV-NEV >= 2 and less than or equal to N.   
+            = -5: WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'   
+            = -6: BMAT must be one of 'I' or 'G'.   
+            = -7: Length of private work WORKL array is not sufficient.   
+            = -8: Error return from calculation of a real Schur form.   
+                  Informational error from LAPACK routine dlahqr.   
+            = -9: Error return from calculation of eigenvectors.   
+                  Informational error from LAPACK routine dtrevc.   
+            = -10: IPARAM(7) must be 1,2,3,4.   
+            = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible.   
+            = -12: HOWMNY = 'S' not yet implemented   
+            = -13: HOWMNY must be one of 'A' or 'P' if RVEC = .true.   
+            = -14: DNAUPD did not find any eigenvalues to sufficient   
+                   accuracy.   
+
+   \BeginLib   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+    3. B.N. Parlett & Y. Saad, "Complex Shift and Invert Strategies for   
+       Real Matrices", Linear Algebra and its Applications, vol 88/89,   
+       pp 575-595, (1987).   
+
+   \Routines called:   
+       ivout   ARPACK utility routine that prints integers.   
+       dmout   ARPACK utility routine that prints matrices   
+       dvout   ARPACK utility routine that prints vectors.   
+       dgeqr2  LAPACK routine that computes the QR factorization of   
+               a matrix.   
+       dlacpy  LAPACK matrix copy routine.   
+       dlahqr  LAPACK routine to compute the real Schur form of an   
+               upper Hessenberg matrix.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlapy2  LAPACK routine to compute sqrt(x**2+y**2) carefully.   
+       dlaset  LAPACK matrix initialization routine.   
+       dorm2r  LAPACK routine that applies an orthogonal matrix in   
+               factored form.   
+       dtrevc  LAPACK routine to compute the eigenvectors of a matrix   
+               in upper quasi-triangular form.   
+       dtrsen  LAPACK routine that re-orders the Schur form.   
+       dtrmm   Level 3 BLAS matrix times an upper triangular matrix.   
+       dger    Level 2 BLAS rank one update to a matrix.   
+       dcopy   Level 1 BLAS that copies one vector to another .   
+       ddot    Level 1 BLAS that computes the scalar product of two vectors.   
+       dnrm2   Level 1 BLAS that computes the norm of a vector.   
+       dscal   Level 1 BLAS that scales a vector.   
+
+   \Remarks   
+
+    1. Currently only HOWMNY = 'A' and 'P' are implemented.   
+
+       Let X' denote the transpose of X.   
+
+    2. Schur vectors are an orthogonal representation for the basis of   
+       Ritz vectors. Thus, their numerical properties are often superior.   
+       If RVEC = .TRUE. then the relationship   
+               A * V(:,1:IPARAM(5)) = V(:,1:IPARAM(5)) * T, and   
+       V(:,1:IPARAM(5))' * V(:,1:IPARAM(5)) = I are approximately satisfied.   
+       Here T is the leading submatrix of order IPARAM(5) of the real   
+       upper quasi-triangular matrix stored workl(ipntr(12)). That is,   
+       T is block upper triangular with 1-by-1 and 2-by-2 diagonal blocks;   
+       each 2-by-2 diagonal block has its diagonal elements equal and its   
+       off-diagonal elements of opposite sign.  Corresponding to each 2-by-2   
+       diagonal block is a complex conjugate pair of Ritz values. The real   
+       Ritz values are stored on the diagonal of T.   
+
+    3. If IPARAM(7) = 3 or 4 and SIGMAI is not equal zero, then the user must   
+       form the IPARAM(5) Rayleigh quotients in order to transform the Ritz   
+       values computed by DNAUPD for OP to those of A*z = lambda*B*z.   
+       Set RVEC = .true. and HOWMNY = 'A', and   
+       compute   
+             Z(:,I)' * A * Z(:,I) if DI(I) = 0.   
+       If DI(I) is not equal to zero and DI(I+1) = - D(I),   
+       then the desired real and imaginary parts of the Ritz value are   
+             Z(:,I)' * A * Z(:,I) +  Z(:,I+1)' * A * Z(:,I+1),   
+             Z(:,I)' * A * Z(:,I+1) -  Z(:,I+1)' * A * Z(:,I), respectively.   
+       Another possibility is to set RVEC = .true. and HOWMNY = 'P' and   
+       compute V(:,1:IPARAM(5))' * A * V(:,1:IPARAM(5)) and then an upper   
+       quasi-triangular matrix of order IPARAM(5) is computed. See remark   
+       2 above.   
+
+   \Authors   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Chao Yang                    Houston, Texas   
+       Dept. of Computational &   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \SCCS Information: @(#)   
+   FILE: neupd.F   SID: 2.5   DATE OF SID: 7/31/96   RELEASE: 2   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+   Subroutine */ int igraphdneupd_(logical *rvec, char *howmny, logical *select, 
+	doublereal *dr, doublereal *di, doublereal *z__, integer *ldz, 
+	doublereal *sigmar, doublereal *sigmai, doublereal *workev, char *
+	bmat, integer *n, char *which, integer *nev, doublereal *tol, 
+	doublereal *resid, integer *ncv, doublereal *v, integer *ldv, integer 
+	*iparam, integer *ipntr, doublereal *workd, doublereal *workl, 
+	integer *lworkl, integer *info)
+{
+    /* System generated locals */
+    integer v_dim1, v_offset, z_dim1, z_offset, i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double pow_dd(doublereal *, doublereal *);
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+    /* Subroutine */ int s_copy(char *, char *, ftnlen, ftnlen);
+
+    /* Local variables */
+    integer j, k, ih;
+    doublereal vl[1]	/* was [1][1] */;
+    integer ibd, ldh, ldq, iri;
+    doublereal sep;
+    integer irr, wri, wrr;
+    extern /* Subroutine */ int igraphdger_(integer *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    integer mode;
+    doublereal eps23;
+    integer ierr;
+    doublereal temp;
+    integer iwev;
+    char type__[6];
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    doublereal temp1;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    integer ihbds, iconj;
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *);
+    doublereal conds;
+    logical reord;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    integer nconv;
+    extern /* Subroutine */ int igraphdtrmm_(char *, char *, char *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    doublereal thres;
+    extern /* Subroutine */ int igraphdmout_(integer *, integer *, integer *, 
+	    doublereal *, integer *, integer *, char *, ftnlen);
+    integer iwork[1];
+    doublereal rnorm;
+    integer ritzi;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen), igraphivout_(integer *, integer *, integer *
+	    , integer *, char *, ftnlen);
+    integer ritzr;
+    extern /* Subroutine */ int igraphdgeqr2_(integer *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *);
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdorm2r_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    extern doublereal igraphdlamch_(char *);
+    integer iheigi, iheigr;
+    extern /* Subroutine */ int igraphdlahqr_(logical *, logical *, integer *, 
+	    integer *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *), igraphdlacpy_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *), igraphdlaset_(char *, 
+	    integer *, integer *, doublereal *, doublereal *, doublereal *, 
+	    integer *);
+    integer logfil, ndigit;
+    extern /* Subroutine */ int igraphdtrevc_(char *, char *, logical *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, integer *, integer *, doublereal *, integer *);
+    integer mneupd = 0, bounds;
+    extern /* Subroutine */ int igraphdtrsen_(char *, char *, logical *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, doublereal *, doublereal *,
+	     integer *, integer *, integer *, integer *);
+    integer msglvl, ktrord, invsub, iuptri, outncv;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       %------------------------%   
+       | Set default parameters |   
+       %------------------------%   
+
+       Parameter adjustments */
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --workd;
+    --resid;
+    --di;
+    --dr;
+    --workev;
+    --select;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    --iparam;
+    --ipntr;
+    --workl;
+
+    /* Function Body */
+    msglvl = mneupd;
+    mode = iparam[7];
+    nconv = iparam[5];
+    *info = 0;
+
+/*     %---------------------------------%   
+       | Get machine dependent constant. |   
+       %---------------------------------% */
+
+    eps23 = igraphdlamch_("Epsilon-Machine");
+    eps23 = pow_dd(&eps23, &c_b3);
+
+/*     %--------------%   
+       | Quick return |   
+       %--------------% */
+
+    ierr = 0;
+
+    if (nconv <= 0) {
+	ierr = -14;
+    } else if (*n <= 0) {
+	ierr = -1;
+    } else if (*nev <= 0) {
+	ierr = -2;
+    } else if (*ncv <= *nev + 1 || *ncv > *n) {
+	ierr = -3;
+    } else if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, 
+	    "SM", (ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, "LR", (ftnlen)2, 
+	    (ftnlen)2) != 0 && s_cmp(which, "SR", (ftnlen)2, (ftnlen)2) != 0 
+	    && s_cmp(which, "LI", (ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, 
+	    "SI", (ftnlen)2, (ftnlen)2) != 0) {
+	ierr = -5;
+    } else if (*(unsigned char *)bmat != 'I' && *(unsigned char *)bmat != 'G')
+	     {
+	ierr = -6;
+    } else /* if(complicated condition) */ {
+/* Computing 2nd power */
+	i__1 = *ncv;
+	if (*lworkl < i__1 * i__1 * 3 + *ncv * 6) {
+	    ierr = -7;
+	} else if (*(unsigned char *)howmny != 'A' && *(unsigned char *)
+		howmny != 'P' && *(unsigned char *)howmny != 'S' && *rvec) {
+	    ierr = -13;
+	} else if (*(unsigned char *)howmny == 'S') {
+	    ierr = -12;
+	}
+    }
+
+    if (mode == 1 || mode == 2) {
+	s_copy(type__, "REGULR", (ftnlen)6, (ftnlen)6);
+    } else if (mode == 3 && *sigmai == 0.) {
+	s_copy(type__, "SHIFTI", (ftnlen)6, (ftnlen)6);
+    } else if (mode == 3) {
+	s_copy(type__, "REALPT", (ftnlen)6, (ftnlen)6);
+    } else if (mode == 4) {
+	s_copy(type__, "IMAGPT", (ftnlen)6, (ftnlen)6);
+    } else {
+	ierr = -10;
+    }
+    if (mode == 1 && *(unsigned char *)bmat == 'G') {
+	ierr = -11;
+    }
+
+/*     %------------%   
+       | Error Exit |   
+       %------------% */
+
+    if (ierr != 0) {
+	*info = ierr;
+	goto L9000;
+    }
+
+/*     %--------------------------------------------------------%   
+       | Pointer into WORKL for address of H, RITZ, BOUNDS, Q   |   
+       | etc... and the remaining workspace.                    |   
+       | Also update pointer to be used on output.              |   
+       | Memory is laid out as follows:                         |   
+       | workl(1:ncv*ncv) := generated Hessenberg matrix        |   
+       | workl(ncv*ncv+1:ncv*ncv+2*ncv) := real and imaginary   |   
+       |                                   parts of ritz values |   
+       | workl(ncv*ncv+2*ncv+1:ncv*ncv+3*ncv) := error bounds   |   
+       %--------------------------------------------------------%   
+
+       %-----------------------------------------------------------%   
+       | The following is used and set by DNEUPD.                  |   
+       | workl(ncv*ncv+3*ncv+1:ncv*ncv+4*ncv) := The untransformed |   
+       |                             real part of the Ritz values. |   
+       | workl(ncv*ncv+4*ncv+1:ncv*ncv+5*ncv) := The untransformed |   
+       |                        imaginary part of the Ritz values. |   
+       | workl(ncv*ncv+5*ncv+1:ncv*ncv+6*ncv) := The untransformed |   
+       |                           error bounds of the Ritz values |   
+       | workl(ncv*ncv+6*ncv+1:2*ncv*ncv+6*ncv) := Holds the upper |   
+       |                             quasi-triangular matrix for H |   
+       | workl(2*ncv*ncv+6*ncv+1: 3*ncv*ncv+6*ncv) := Holds the    |   
+       |       associated matrix representation of the invariant   |   
+       |       subspace for H.                                     |   
+       | GRAND total of NCV * ( 3 * NCV + 6 ) locations.           |   
+       %-----------------------------------------------------------% */
+
+    ih = ipntr[5];
+    ritzr = ipntr[6];
+    ritzi = ipntr[7];
+    bounds = ipntr[8];
+    ldh = *ncv;
+    ldq = *ncv;
+    iheigr = bounds + ldh;
+    iheigi = iheigr + ldh;
+    ihbds = iheigi + ldh;
+    iuptri = ihbds + ldh;
+    invsub = iuptri + ldh * *ncv;
+    ipntr[9] = iheigr;
+    ipntr[10] = iheigi;
+    ipntr[11] = ihbds;
+    ipntr[12] = iuptri;
+    ipntr[13] = invsub;
+    wrr = 1;
+    wri = *ncv + 1;
+    iwev = wri + *ncv;
+
+/*     %-----------------------------------------%   
+       | irr points to the REAL part of the Ritz |   
+       |     values computed by _neigh before    |   
+       |     exiting _naup2.                     |   
+       | iri points to the IMAGINARY part of the |   
+       |     Ritz values computed by _neigh      |   
+       |     before exiting _naup2.              |   
+       | ibd points to the Ritz estimates        |   
+       |     computed by _neigh before exiting   |   
+       |     _naup2.                             |   
+       %-----------------------------------------% */
+
+    irr = ipntr[14] + *ncv * *ncv;
+    iri = irr + *ncv;
+    ibd = iri + *ncv;
+
+/*     %------------------------------------%   
+       | RNORM is B-norm of the RESID(1:N). |   
+       %------------------------------------% */
+
+    rnorm = workl[ih + 2];
+    workl[ih + 2] = 0.;
+
+    if (*rvec) {
+
+/*        %-------------------------------------------%   
+          | Get converged Ritz value on the boundary. |   
+          | Note: converged Ritz values have been     |   
+          | placed in the first NCONV locations in    |   
+          | workl(ritzr) and workl(ritzi).  They have |   
+          | been sorted (in _naup2) according to the  |   
+          | WHICH selection criterion.                |   
+          %-------------------------------------------% */
+
+	if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(which, 
+		"SM", (ftnlen)2, (ftnlen)2) == 0) {
+	    thres = igraphdlapy2_(&workl[ritzr], &workl[ritzi]);
+	} else if (s_cmp(which, "LR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		which, "SR", (ftnlen)2, (ftnlen)2) == 0) {
+	    thres = workl[ritzr];
+	} else if (s_cmp(which, "LI", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		which, "SI", (ftnlen)2, (ftnlen)2) == 0) {
+	    thres = (d__1 = workl[ritzi], abs(d__1));
+	}
+
+	if (msglvl > 2) {
+	    igraphdvout_(&logfil, &c__1, &thres, &ndigit, "_neupd: Threshold eigen"
+		    "value used for re-ordering", (ftnlen)49);
+	}
+
+/*        %----------------------------------------------------------%   
+          | Check to see if all converged Ritz values appear at the  |   
+          | top of the upper quasi-triangular matrix computed by     |   
+          | _neigh in _naup2.  This is done in the following way:    |   
+          |                                                          |   
+          | 1) For each Ritz value obtained from _neigh, compare it  |   
+          |    with the threshold Ritz value computed above to       |   
+          |    determine whether it is a wanted one.                 |   
+          |                                                          |   
+          | 2) If it is wanted, then check the corresponding Ritz    |   
+          |    estimate to see if it has converged.  If it has, set  |   
+          |    correponding entry in the logical array SELECT to     |   
+          |    .TRUE..                                               |   
+          |                                                          |   
+          | If SELECT(j) = .TRUE. and j > NCONV, then there is a     |   
+          | converged Ritz value that does not appear at the top of  |   
+          | the upper quasi-triangular matrix computed by _neigh in  |   
+          | _naup2.  Reordering is needed.                           |   
+          %----------------------------------------------------------% */
+
+	reord = FALSE_;
+	ktrord = 0;
+	i__1 = *ncv - 1;
+	for (j = 0; j <= i__1; ++j) {
+	    select[j + 1] = FALSE_;
+	    if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+		if (igraphdlapy2_(&workl[irr + j], &workl[iri + j]) >= thres) {
+/* Computing MAX */
+		    d__1 = eps23, d__2 = igraphdlapy2_(&workl[irr + j], &workl[iri 
+			    + j]);
+		    temp1 = max(d__1,d__2);
+		    if (workl[ibd + j] <= *tol * temp1) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+		if (igraphdlapy2_(&workl[irr + j], &workl[iri + j]) <= thres) {
+/* Computing MAX */
+		    d__1 = eps23, d__2 = igraphdlapy2_(&workl[irr + j], &workl[iri 
+			    + j]);
+		    temp1 = max(d__1,d__2);
+		    if (workl[ibd + j] <= *tol * temp1) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "LR", (ftnlen)2, (ftnlen)2) == 0) {
+		if (workl[irr + j] >= thres) {
+/* Computing MAX */
+		    d__1 = eps23, d__2 = igraphdlapy2_(&workl[irr + j], &workl[iri 
+			    + j]);
+		    temp1 = max(d__1,d__2);
+		    if (workl[ibd + j] <= *tol * temp1) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "SR", (ftnlen)2, (ftnlen)2) == 0) {
+		if (workl[irr + j] <= thres) {
+/* Computing MAX */
+		    d__1 = eps23, d__2 = igraphdlapy2_(&workl[irr + j], &workl[iri 
+			    + j]);
+		    temp1 = max(d__1,d__2);
+		    if (workl[ibd + j] <= *tol * temp1) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "LI", (ftnlen)2, (ftnlen)2) == 0) {
+		if ((d__1 = workl[iri + j], abs(d__1)) >= thres) {
+/* Computing MAX */
+		    d__1 = eps23, d__2 = igraphdlapy2_(&workl[irr + j], &workl[iri 
+			    + j]);
+		    temp1 = max(d__1,d__2);
+		    if (workl[ibd + j] <= *tol * temp1) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "SI", (ftnlen)2, (ftnlen)2) == 0) {
+		if ((d__1 = workl[iri + j], abs(d__1)) <= thres) {
+/* Computing MAX */
+		    d__1 = eps23, d__2 = igraphdlapy2_(&workl[irr + j], &workl[iri 
+			    + j]);
+		    temp1 = max(d__1,d__2);
+		    if (workl[ibd + j] <= *tol * temp1) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    }
+	    if (j + 1 > nconv) {
+		reord = select[j + 1] || reord;
+	    }
+	    if (select[j + 1]) {
+		++ktrord;
+	    }
+/* L10: */
+	}
+
+	if (msglvl > 2) {
+	    igraphivout_(&logfil, &c__1, &ktrord, &ndigit, "_neupd: Number of spec"
+		    "ified eigenvalues", (ftnlen)39);
+	    igraphivout_(&logfil, &c__1, &nconv, &ndigit, "_neupd: Number of \"con"
+		    "verged\" eigenvalues", (ftnlen)41);
+	}
+
+/*        %-----------------------------------------------------------%   
+          | Call LAPACK routine dlahqr to compute the real Schur form |   
+          | of the upper Hessenberg matrix returned by DNAUPD.        |   
+          | Make a copy of the upper Hessenberg matrix.               |   
+          | Initialize the Schur vector matrix Q to the identity.     |   
+          %-----------------------------------------------------------% */
+
+	i__1 = ldh * *ncv;
+	igraphdcopy_(&i__1, &workl[ih], &c__1, &workl[iuptri], &c__1);
+	igraphdlaset_("All", ncv, ncv, &c_b44, &c_b45, &workl[invsub], &ldq);
+	igraphdlahqr_(&c_true, &c_true, ncv, &c__1, ncv, &workl[iuptri], &ldh, &
+		workl[iheigr], &workl[iheigi], &c__1, ncv, &workl[invsub], &
+		ldq, &ierr);
+	igraphdcopy_(ncv, &workl[invsub + *ncv - 1], &ldq, &workl[ihbds], &c__1);
+
+	if (ierr != 0) {
+	    *info = -8;
+	    goto L9000;
+	}
+
+	if (msglvl > 1) {
+	    igraphdvout_(&logfil, ncv, &workl[iheigr], &ndigit, "_neupd: Real part"
+		    " of the eigenvalues of H", (ftnlen)41);
+	    igraphdvout_(&logfil, ncv, &workl[iheigi], &ndigit, "_neupd: Imaginary"
+		    " part of the Eigenvalues of H", (ftnlen)46);
+	    igraphdvout_(&logfil, ncv, &workl[ihbds], &ndigit, "_neupd: Last row o"
+		    "f the Schur vector matrix", (ftnlen)43);
+	    if (msglvl > 3) {
+		igraphdmout_(&logfil, ncv, ncv, &workl[iuptri], &ldh, &ndigit, 
+			"_neupd: The upper quasi-triangular matrix ", (ftnlen)
+			42);
+	    }
+	}
+
+	if (reord) {
+
+/*           %-----------------------------------------------------%   
+             | Reorder the computed upper quasi-triangular matrix. |   
+             %-----------------------------------------------------% */
+
+	    igraphdtrsen_("None", "V", &select[1], ncv, &workl[iuptri], &ldh, &
+		    workl[invsub], &ldq, &workl[iheigr], &workl[iheigi], &
+		    nconv, &conds, &sep, &workl[ihbds], ncv, iwork, &c__1, &
+		    ierr);
+
+	    if (ierr == 1) {
+		*info = 1;
+		goto L9000;
+	    }
+
+	    if (msglvl > 2) {
+		igraphdvout_(&logfil, ncv, &workl[iheigr], &ndigit, "_neupd: Real "
+			"part of the eigenvalues of H--reordered", (ftnlen)52);
+		igraphdvout_(&logfil, ncv, &workl[iheigi], &ndigit, "_neupd: Imag "
+			"part of the eigenvalues of H--reordered", (ftnlen)52);
+		if (msglvl > 3) {
+		    igraphdmout_(&logfil, ncv, ncv, &workl[iuptri], &ldq, &ndigit, 
+			    "_neupd: Quasi-triangular matrix after re-orderi"
+			    "ng", (ftnlen)49);
+		}
+	    }
+
+	}
+
+/*        %---------------------------------------%   
+          | Copy the last row of the Schur vector |   
+          | into workl(ihbds).  This will be used |   
+          | to compute the Ritz estimates of      |   
+          | converged Ritz values.                |   
+          %---------------------------------------% */
+
+	igraphdcopy_(ncv, &workl[invsub + *ncv - 1], &ldq, &workl[ihbds], &c__1);
+
+/*        %----------------------------------------------------%   
+          | Place the computed eigenvalues of H into DR and DI |   
+          | if a spectral transformation was not used.         |   
+          %----------------------------------------------------% */
+
+	if (s_cmp(type__, "REGULR", (ftnlen)6, (ftnlen)6) == 0) {
+	    igraphdcopy_(&nconv, &workl[iheigr], &c__1, &dr[1], &c__1);
+	    igraphdcopy_(&nconv, &workl[iheigi], &c__1, &di[1], &c__1);
+	}
+
+/*        %----------------------------------------------------------%   
+          | Compute the QR factorization of the matrix representing  |   
+          | the wanted invariant subspace located in the first NCONV |   
+          | columns of workl(invsub,ldq).                            |   
+          %----------------------------------------------------------% */
+
+	igraphdgeqr2_(ncv, &nconv, &workl[invsub], &ldq, &workev[1], &workev[*ncv + 
+		1], &ierr);
+
+/*        %---------------------------------------------------------%   
+          | * Postmultiply V by Q using dorm2r.                     |   
+          | * Copy the first NCONV columns of VQ into Z.            |   
+          | * Postmultiply Z by R.                                  |   
+          | The N by NCONV matrix Z is now a matrix representation  |   
+          | of the approximate invariant subspace associated with   |   
+          | the Ritz values in workl(iheigr) and workl(iheigi)      |   
+          | The first NCONV columns of V are now approximate Schur  |   
+          | vectors associated with the real upper quasi-triangular |   
+          | matrix of order NCONV in workl(iuptri)                  |   
+          %---------------------------------------------------------% */
+
+	igraphdorm2r_("Right", "Notranspose", n, ncv, &nconv, &workl[invsub], &ldq, 
+		&workev[1], &v[v_offset], ldv, &workd[*n + 1], &ierr);
+	igraphdlacpy_("All", n, &nconv, &v[v_offset], ldv, &z__[z_offset], ldz);
+
+	i__1 = nconv;
+	for (j = 1; j <= i__1; ++j) {
+
+/*           %---------------------------------------------------%   
+             | Perform both a column and row scaling if the      |   
+             | diagonal element of workl(invsub,ldq) is negative |   
+             | I'm lazy and don't take advantage of the upper    |   
+             | quasi-triangular form of workl(iuptri,ldq)        |   
+             | Note that since Q is orthogonal, R is a diagonal  |   
+             | matrix consisting of plus or minus ones           |   
+             %---------------------------------------------------% */
+
+	    if (workl[invsub + (j - 1) * ldq + j - 1] < 0.) {
+		igraphdscal_(&nconv, &c_b71, &workl[iuptri + j - 1], &ldq);
+		igraphdscal_(&nconv, &c_b71, &workl[iuptri + (j - 1) * ldq], &c__1);
+	    }
+
+/* L20: */
+	}
+
+	if (*(unsigned char *)howmny == 'A') {
+
+/*           %--------------------------------------------%   
+             | Compute the NCONV wanted eigenvectors of T |   
+             | located in workl(iuptri,ldq).              |   
+             %--------------------------------------------% */
+
+	    i__1 = *ncv;
+	    for (j = 1; j <= i__1; ++j) {
+		if (j <= nconv) {
+		    select[j] = TRUE_;
+		} else {
+		    select[j] = FALSE_;
+		}
+/* L30: */
+	    }
+
+	    igraphdtrevc_("Right", "Select", &select[1], ncv, &workl[iuptri], &ldq, 
+		    vl, &c__1, &workl[invsub], &ldq, ncv, &outncv, &workev[1],
+		     &ierr);
+
+	    if (ierr != 0) {
+		*info = -9;
+		goto L9000;
+	    }
+
+/*           %------------------------------------------------%   
+             | Scale the returning eigenvectors so that their |   
+             | Euclidean norms are all one. LAPACK subroutine |   
+             | dtrevc returns each eigenvector normalized so  |   
+             | that the element of largest magnitude has      |   
+             | magnitude 1;                                   |   
+             %------------------------------------------------% */
+
+	    iconj = 0;
+	    i__1 = nconv;
+	    for (j = 1; j <= i__1; ++j) {
+
+		if (workl[iheigi + j - 1] == 0.) {
+
+/*                 %----------------------%   
+                   | real eigenvalue case |   
+                   %----------------------% */
+
+		    temp = igraphdnrm2_(ncv, &workl[invsub + (j - 1) * ldq], &c__1);
+		    d__1 = 1. / temp;
+		    igraphdscal_(ncv, &d__1, &workl[invsub + (j - 1) * ldq], &c__1);
+
+		} else {
+
+/*                 %-------------------------------------------%   
+                   | Complex conjugate pair case. Note that    |   
+                   | since the real and imaginary part of      |   
+                   | the eigenvector are stored in consecutive |   
+                   | columns, we further normalize by the      |   
+                   | square root of two.                       |   
+                   %-------------------------------------------% */
+
+		    if (iconj == 0) {
+			d__1 = igraphdnrm2_(ncv, &workl[invsub + (j - 1) * ldq], &
+				c__1);
+			d__2 = igraphdnrm2_(ncv, &workl[invsub + j * ldq], &c__1);
+			temp = igraphdlapy2_(&d__1, &d__2);
+			d__1 = 1. / temp;
+			igraphdscal_(ncv, &d__1, &workl[invsub + (j - 1) * ldq], &
+				c__1);
+			d__1 = 1. / temp;
+			igraphdscal_(ncv, &d__1, &workl[invsub + j * ldq], &c__1);
+			iconj = 1;
+		    } else {
+			iconj = 0;
+		    }
+
+		}
+
+/* L40: */
+	    }
+
+	    igraphdgemv_("T", ncv, &nconv, &c_b45, &workl[invsub], &ldq, &workl[
+		    ihbds], &c__1, &c_b44, &workev[1], &c__1);
+
+	    iconj = 0;
+	    i__1 = nconv;
+	    for (j = 1; j <= i__1; ++j) {
+		if (workl[iheigi + j - 1] != 0.) {
+
+/*                 %-------------------------------------------%   
+                   | Complex conjugate pair case. Note that    |   
+                   | since the real and imaginary part of      |   
+                   | the eigenvector are stored in consecutive |   
+                   %-------------------------------------------% */
+
+		    if (iconj == 0) {
+			workev[j] = igraphdlapy2_(&workev[j], &workev[j + 1]);
+			workev[j + 1] = workev[j];
+			iconj = 1;
+		    } else {
+			iconj = 0;
+		    }
+		}
+/* L45: */
+	    }
+
+	    if (msglvl > 2) {
+		igraphdcopy_(ncv, &workl[invsub + *ncv - 1], &ldq, &workl[ihbds], &
+			c__1);
+		igraphdvout_(&logfil, ncv, &workl[ihbds], &ndigit, "_neupd: Last r"
+			"ow of the eigenvector matrix for T", (ftnlen)48);
+		if (msglvl > 3) {
+		    igraphdmout_(&logfil, ncv, ncv, &workl[invsub], &ldq, &ndigit, 
+			    "_neupd: The eigenvector matrix for T", (ftnlen)
+			    36);
+		}
+	    }
+
+/*           %---------------------------------------%   
+             | Copy Ritz estimates into workl(ihbds) |   
+             %---------------------------------------% */
+
+	    igraphdcopy_(&nconv, &workev[1], &c__1, &workl[ihbds], &c__1);
+
+/*           %---------------------------------------------------------%   
+             | Compute the QR factorization of the eigenvector matrix  |   
+             | associated with leading portion of T in the first NCONV |   
+             | columns of workl(invsub,ldq).                           |   
+             %---------------------------------------------------------% */
+
+	    igraphdgeqr2_(ncv, &nconv, &workl[invsub], &ldq, &workev[1], &workev[*
+		    ncv + 1], &ierr);
+
+/*           %----------------------------------------------%   
+             | * Postmultiply Z by Q.                       |   
+             | * Postmultiply Z by R.                       |   
+             | The N by NCONV matrix Z is now contains the  |   
+             | Ritz vectors associated with the Ritz values |   
+             | in workl(iheigr) and workl(iheigi).          |   
+             %----------------------------------------------% */
+
+	    igraphdorm2r_("Right", "Notranspose", n, ncv, &nconv, &workl[invsub], &
+		    ldq, &workev[1], &z__[z_offset], ldz, &workd[*n + 1], &
+		    ierr);
+
+	    igraphdtrmm_("Right", "Upper", "No transpose", "Non-unit", n, &nconv, &
+		    c_b45, &workl[invsub], &ldq, &z__[z_offset], ldz);
+
+	}
+
+    } else {
+
+/*        %------------------------------------------------------%   
+          | An approximate invariant subspace is not needed.     |   
+          | Place the Ritz values computed DNAUPD into DR and DI |   
+          %------------------------------------------------------% */
+
+	igraphdcopy_(&nconv, &workl[ritzr], &c__1, &dr[1], &c__1);
+	igraphdcopy_(&nconv, &workl[ritzi], &c__1, &di[1], &c__1);
+	igraphdcopy_(&nconv, &workl[ritzr], &c__1, &workl[iheigr], &c__1);
+	igraphdcopy_(&nconv, &workl[ritzi], &c__1, &workl[iheigi], &c__1);
+	igraphdcopy_(&nconv, &workl[bounds], &c__1, &workl[ihbds], &c__1);
+    }
+
+/*     %------------------------------------------------%   
+       | Transform the Ritz values and possibly vectors |   
+       | and corresponding error bounds of OP to those  |   
+       | of A*x = lambda*B*x.                           |   
+       %------------------------------------------------% */
+
+    if (s_cmp(type__, "REGULR", (ftnlen)6, (ftnlen)6) == 0) {
+
+	if (*rvec) {
+	    igraphdscal_(ncv, &rnorm, &workl[ihbds], &c__1);
+	}
+
+    } else {
+
+/*        %---------------------------------------%   
+          |   A spectral transformation was used. |   
+          | * Determine the Ritz estimates of the |   
+          |   Ritz values in the original system. |   
+          %---------------------------------------% */
+
+	if (s_cmp(type__, "SHIFTI", (ftnlen)6, (ftnlen)6) == 0) {
+
+	    if (*rvec) {
+		igraphdscal_(ncv, &rnorm, &workl[ihbds], &c__1);
+	    }
+
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+		temp = igraphdlapy2_(&workl[iheigr + k - 1], &workl[iheigi + k - 1])
+			;
+		workl[ihbds + k - 1] = (d__1 = workl[ihbds + k - 1], abs(d__1)
+			) / temp / temp;
+/* L50: */
+	    }
+
+	} else if (s_cmp(type__, "REALPT", (ftnlen)6, (ftnlen)6) == 0) {
+
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+/* L60: */
+	    }
+
+	} else if (s_cmp(type__, "IMAGPT", (ftnlen)6, (ftnlen)6) == 0) {
+
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+/* L70: */
+	    }
+
+	}
+
+/*        %-----------------------------------------------------------%   
+          | *  Transform the Ritz values back to the original system. |   
+          |    For TYPE = 'SHIFTI' the transformation is              |   
+          |             lambda = 1/theta + sigma                      |   
+          |    For TYPE = 'REALPT' or 'IMAGPT' the user must from     |   
+          |    Rayleigh quotients or a projection. See remark 3 above.|   
+          | NOTES:                                                    |   
+          | *The Ritz vectors are not affected by the transformation. |   
+          %-----------------------------------------------------------% */
+
+	if (s_cmp(type__, "SHIFTI", (ftnlen)6, (ftnlen)6) == 0) {
+
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+		temp = igraphdlapy2_(&workl[iheigr + k - 1], &workl[iheigi + k - 1])
+			;
+		workl[iheigr + k - 1] = workl[iheigr + k - 1] / temp / temp + 
+			*sigmar;
+		workl[iheigi + k - 1] = -workl[iheigi + k - 1] / temp / temp 
+			+ *sigmai;
+/* L80: */
+	    }
+
+	    igraphdcopy_(&nconv, &workl[iheigr], &c__1, &dr[1], &c__1);
+	    igraphdcopy_(&nconv, &workl[iheigi], &c__1, &di[1], &c__1);
+
+	} else if (s_cmp(type__, "REALPT", (ftnlen)6, (ftnlen)6) == 0 || 
+		s_cmp(type__, "IMAGPT", (ftnlen)6, (ftnlen)6) == 0) {
+
+	    igraphdcopy_(&nconv, &workl[iheigr], &c__1, &dr[1], &c__1);
+	    igraphdcopy_(&nconv, &workl[iheigi], &c__1, &di[1], &c__1);
+
+	}
+
+    }
+
+    if (s_cmp(type__, "SHIFTI", (ftnlen)6, (ftnlen)6) == 0 && msglvl > 1) {
+	igraphdvout_(&logfil, &nconv, &dr[1], &ndigit, "_neupd: Untransformed real"
+		" part of the Ritz valuess.", (ftnlen)52);
+	igraphdvout_(&logfil, &nconv, &di[1], &ndigit, "_neupd: Untransformed imag"
+		" part of the Ritz valuess.", (ftnlen)52);
+	igraphdvout_(&logfil, &nconv, &workl[ihbds], &ndigit, "_neupd: Ritz estima"
+		"tes of untransformed Ritz values.", (ftnlen)52);
+    } else if (s_cmp(type__, "REGULR", (ftnlen)6, (ftnlen)6) == 0 && msglvl > 
+	    1) {
+	igraphdvout_(&logfil, &nconv, &dr[1], &ndigit, "_neupd: Real parts of conv"
+		"erged Ritz values.", (ftnlen)44);
+	igraphdvout_(&logfil, &nconv, &di[1], &ndigit, "_neupd: Imag parts of conv"
+		"erged Ritz values.", (ftnlen)44);
+	igraphdvout_(&logfil, &nconv, &workl[ihbds], &ndigit, "_neupd: Associated "
+		"Ritz estimates.", (ftnlen)34);
+    }
+
+/*     %-------------------------------------------------%   
+       | Eigenvector Purification step. Formally perform |   
+       | one of inverse subspace iteration. Only used    |   
+       | for MODE = 2.                                   |   
+       %-------------------------------------------------% */
+
+    if (*rvec && *(unsigned char *)howmny == 'A' && s_cmp(type__, "SHIFTI", (
+	    ftnlen)6, (ftnlen)6) == 0) {
+
+/*        %------------------------------------------------%   
+          | Purify the computed Ritz vectors by adding a   |   
+          | little bit of the residual vector:             |   
+          |                      T                         |   
+          |          resid(:)*( e    s ) / theta           |   
+          |                      NCV                       |   
+          | where H s = s theta. Remember that when theta  |   
+          | has nonzero imaginary part, the corresponding  |   
+          | Ritz vector is stored across two columns of Z. |   
+          %------------------------------------------------% */
+
+	iconj = 0;
+	i__1 = nconv;
+	for (j = 1; j <= i__1; ++j) {
+	    if (workl[iheigi + j - 1] == 0.) {
+		workev[j] = workl[invsub + (j - 1) * ldq + *ncv - 1] / workl[
+			iheigr + j - 1];
+	    } else if (iconj == 0) {
+		temp = igraphdlapy2_(&workl[iheigr + j - 1], &workl[iheigi + j - 1])
+			;
+		workev[j] = (workl[invsub + (j - 1) * ldq + *ncv - 1] * workl[
+			iheigr + j - 1] + workl[invsub + j * ldq + *ncv - 1] *
+			 workl[iheigi + j - 1]) / temp / temp;
+		workev[j + 1] = (workl[invsub + j * ldq + *ncv - 1] * workl[
+			iheigr + j - 1] - workl[invsub + (j - 1) * ldq + *ncv 
+			- 1] * workl[iheigi + j - 1]) / temp / temp;
+		iconj = 1;
+	    } else {
+		iconj = 0;
+	    }
+/* L110: */
+	}
+
+/*        %---------------------------------------%   
+          | Perform a rank one update to Z and    |   
+          | purify all the Ritz vectors together. |   
+          %---------------------------------------% */
+
+	igraphdger_(n, &nconv, &c_b45, &resid[1], &c__1, &workev[1], &c__1, &z__[
+		z_offset], ldz);
+
+    }
+
+L9000:
+
+    return 0;
+
+/*     %---------------%   
+       | End of DNEUPD |   
+       %---------------% */
+
+} /* igraphdneupd_ */
+
diff --git a/igraph/src/dngets.c b/igraph/src/dngets.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dngets.c
@@ -0,0 +1,275 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static logical c_true = TRUE_;
+static integer c__1 = 1;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dngets   
+
+   \Description:   
+    Given the eigenvalues of the upper Hessenberg matrix H,   
+    computes the NP shifts AMU that are zeros of the polynomial of   
+    degree NP which filters out components of the unwanted eigenvectors   
+    corresponding to the AMU's based on some given criteria.   
+
+    NOTE: call this even in the case of user specified shifts in order   
+    to sort the eigenvalues, and error bounds of H for later use.   
+
+   \Usage:   
+    call dngets   
+       ( ISHIFT, WHICH, KEV, NP, RITZR, RITZI, BOUNDS, SHIFTR, SHIFTI )   
+
+   \Arguments   
+    ISHIFT  Integer.  (INPUT)   
+            Method for selecting the implicit shifts at each iteration.   
+            ISHIFT = 0: user specified shifts   
+            ISHIFT = 1: exact shift with respect to the matrix H.   
+
+    WHICH   Character*2.  (INPUT)   
+            Shift selection criteria.   
+            'LM' -> want the KEV eigenvalues of largest magnitude.   
+            'SM' -> want the KEV eigenvalues of smallest magnitude.   
+            'LR' -> want the KEV eigenvalues of largest real part.   
+            'SR' -> want the KEV eigenvalues of smallest real part.   
+            'LI' -> want the KEV eigenvalues of largest imaginary part.   
+            'SI' -> want the KEV eigenvalues of smallest imaginary part.   
+
+    KEV      Integer.  (INPUT/OUTPUT)   
+             INPUT: KEV+NP is the size of the matrix H.   
+             OUTPUT: Possibly increases KEV by one to keep complex conjugate   
+             pairs together.   
+
+    NP       Integer.  (INPUT/OUTPUT)   
+             Number of implicit shifts to be computed.   
+             OUTPUT: Possibly decreases NP by one to keep complex conjugate   
+             pairs together.   
+
+    RITZR,  Double precision array of length KEV+NP.  (INPUT/OUTPUT)   
+    RITZI   On INPUT, RITZR and RITZI contain the real and imaginary   
+            parts of the eigenvalues of H.   
+            On OUTPUT, RITZR and RITZI are sorted so that the unwanted   
+            eigenvalues are in the first NP locations and the wanted   
+            portion is in the last KEV locations.  When exact shifts are   
+            selected, the unwanted part corresponds to the shifts to   
+            be applied. Also, if ISHIFT .eq. 1, the unwanted eigenvalues   
+            are further sorted so that the ones with largest Ritz values   
+            are first.   
+
+    BOUNDS  Double precision array of length KEV+NP.  (INPUT/OUTPUT)   
+            Error bounds corresponding to the ordering in RITZ.   
+
+    SHIFTR, SHIFTI  *** USE deprecated as of version 2.1. ***   
+
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \Routines called:   
+       dsortc  ARPACK sorting routine.   
+       dcopy   Level 1 BLAS that copies one vector to another .   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/92: Version ' 2.1'   
+
+   \SCCS Information: @(#)   
+   FILE: ngets.F   SID: 2.3   DATE OF SID: 4/20/96   RELEASE: 2   
+
+   \Remarks   
+       1. xxxx   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdngets_(integer *ishift, char *which, integer *kev, 
+	integer *np, doublereal *ritzr, doublereal *ritzi, doublereal *bounds,
+	 doublereal *shiftr, doublereal *shifti)
+{
+    /* System generated locals */
+    integer i__1;
+
+    /* Builtin functions */
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+
+    /* Local variables */
+    real t0, t1;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen), igraphivout_(integer *, integer *, integer *
+	    , integer *, char *, ftnlen), igraphsecond_(real *);
+    integer logfil, ndigit, mngets = 0;
+    extern /* Subroutine */ int igraphdsortc_(char *, logical *, integer *, 
+	    doublereal *, doublereal *, doublereal *);
+    integer msglvl;
+    real tngets = 0.;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %----------------------%   
+       | Intrinsics Functions |   
+       %----------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       %-------------------------------%   
+       | Initialize timing statistics  |   
+       | & message level for debugging |   
+       %-------------------------------%   
+
+       Parameter adjustments */
+    --bounds;
+    --ritzi;
+    --ritzr;
+    --shiftr;
+    --shifti;
+
+    /* Function Body */
+    igraphsecond_(&t0);
+    msglvl = mngets;
+
+/*     %----------------------------------------------------%   
+       | LM, SM, LR, SR, LI, SI case.                       |   
+       | Sort the eigenvalues of H into the desired order   |   
+       | and apply the resulting order to BOUNDS.           |   
+       | The eigenvalues are sorted so that the wanted part |   
+       | are always in the last KEV locations.              |   
+       | We first do a pre-processing sort in order to keep |   
+       | complex conjugate pairs together                   |   
+       %----------------------------------------------------% */
+
+    if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+	i__1 = *kev + *np;
+	igraphdsortc_("LR", &c_true, &i__1, &ritzr[1], &ritzi[1], &bounds[1]);
+    } else if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+	i__1 = *kev + *np;
+	igraphdsortc_("SR", &c_true, &i__1, &ritzr[1], &ritzi[1], &bounds[1]);
+    } else if (s_cmp(which, "LR", (ftnlen)2, (ftnlen)2) == 0) {
+	i__1 = *kev + *np;
+	igraphdsortc_("LM", &c_true, &i__1, &ritzr[1], &ritzi[1], &bounds[1]);
+    } else if (s_cmp(which, "SR", (ftnlen)2, (ftnlen)2) == 0) {
+	i__1 = *kev + *np;
+	igraphdsortc_("SM", &c_true, &i__1, &ritzr[1], &ritzi[1], &bounds[1]);
+    } else if (s_cmp(which, "LI", (ftnlen)2, (ftnlen)2) == 0) {
+	i__1 = *kev + *np;
+	igraphdsortc_("LM", &c_true, &i__1, &ritzr[1], &ritzi[1], &bounds[1]);
+    } else if (s_cmp(which, "SI", (ftnlen)2, (ftnlen)2) == 0) {
+	i__1 = *kev + *np;
+	igraphdsortc_("SM", &c_true, &i__1, &ritzr[1], &ritzi[1], &bounds[1]);
+    }
+
+    i__1 = *kev + *np;
+    igraphdsortc_(which, &c_true, &i__1, &ritzr[1], &ritzi[1], &bounds[1]);
+
+/*     %-------------------------------------------------------%   
+       | Increase KEV by one if the ( ritzr(np),ritzi(np) )    |   
+       | = ( ritzr(np+1),-ritzi(np+1) ) and ritz(np) .ne. zero |   
+       | Accordingly decrease NP by one. In other words keep   |   
+       | complex conjugate pairs together.                     |   
+       %-------------------------------------------------------% */
+
+    if (ritzr[*np + 1] - ritzr[*np] == 0. && ritzi[*np + 1] + ritzi[*np] == 
+	    0.) {
+	--(*np);
+	++(*kev);
+    }
+
+    if (*ishift == 1) {
+
+/*        %-------------------------------------------------------%   
+          | Sort the unwanted Ritz values used as shifts so that  |   
+          | the ones with largest Ritz estimates are first        |   
+          | This will tend to minimize the effects of the         |   
+          | forward instability of the iteration when they shifts |   
+          | are applied in subroutine dnapps.                     |   
+          | Be careful and use 'SR' since we want to sort BOUNDS! |   
+          %-------------------------------------------------------% */
+
+	igraphdsortc_("SR", &c_true, np, &bounds[1], &ritzr[1], &ritzi[1]);
+    }
+
+    igraphsecond_(&t1);
+    tngets += t1 - t0;
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, kev, &ndigit, "_ngets: KEV is", (ftnlen)14);
+	igraphivout_(&logfil, &c__1, np, &ndigit, "_ngets: NP is", (ftnlen)13);
+	i__1 = *kev + *np;
+	igraphdvout_(&logfil, &i__1, &ritzr[1], &ndigit, "_ngets: Eigenvalues of c"
+		"urrent H matrix -- real part", (ftnlen)52);
+	i__1 = *kev + *np;
+	igraphdvout_(&logfil, &i__1, &ritzi[1], &ndigit, "_ngets: Eigenvalues of c"
+		"urrent H matrix -- imag part", (ftnlen)52);
+	i__1 = *kev + *np;
+	igraphdvout_(&logfil, &i__1, &bounds[1], &ndigit, "_ngets: Ritz estimates "
+		"of the current KEV+NP Ritz values", (ftnlen)56);
+    }
+
+    return 0;
+
+/*     %---------------%   
+       | End of dngets |   
+       %---------------% */
+
+} /* igraphdngets_ */
+
diff --git a/igraph/src/dnrm2.c b/igraph/src/dnrm2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dnrm2.c
@@ -0,0 +1,88 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+doublereal igraphdnrm2_(integer *n, doublereal *x, integer *incx)
+{
+    /* System generated locals */
+    integer i__1, i__2;
+    doublereal ret_val, d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer ix;
+    doublereal ssq, norm, scale, absxi;
+
+
+/*  Purpose   
+    =======   
+
+    DNRM2 returns the euclidean norm of a vector via the function   
+    name, so that   
+
+       DNRM2 := sqrt( x'*x )   
+
+    Further Details   
+    ===============   
+
+    -- This version written on 25-October-1982.   
+       Modified on 14-October-1993 to inline the call to DLASSQ.   
+       Sven Hammarling, Nag Ltd.   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --x;
+
+    /* Function Body */
+    if (*n < 1 || *incx < 1) {
+	norm = 0.;
+    } else if (*n == 1) {
+	norm = abs(x[1]);
+    } else {
+	scale = 0.;
+	ssq = 1.;
+/*        The following loop is equivalent to this call to the LAPACK   
+          auxiliary routine:   
+          CALL DLASSQ( N, X, INCX, SCALE, SSQ ) */
+
+	i__1 = (*n - 1) * *incx + 1;
+	i__2 = *incx;
+	for (ix = 1; i__2 < 0 ? ix >= i__1 : ix <= i__1; ix += i__2) {
+	    if (x[ix] != 0.) {
+		absxi = (d__1 = x[ix], abs(d__1));
+		if (scale < absxi) {
+/* Computing 2nd power */
+		    d__1 = scale / absxi;
+		    ssq = ssq * (d__1 * d__1) + 1.;
+		    scale = absxi;
+		} else {
+/* Computing 2nd power */
+		    d__1 = absxi / scale;
+		    ssq += d__1 * d__1;
+		}
+	    }
+/* L10: */
+	}
+	norm = scale * sqrt(ssq);
+    }
+
+    ret_val = norm;
+    return ret_val;
+
+/*     End of DNRM2. */
+
+} /* igraphdnrm2_ */
+
diff --git a/igraph/src/dolio.c b/igraph/src/dolio.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dolio.c
@@ -0,0 +1,26 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef KR_headers
+extern int (*f__lioproc)();
+
+integer do_lio(type,number,ptr,len) ftnint *number,*type; char *ptr; ftnlen len;
+#else
+extern int (*f__lioproc)(ftnint*, char*, ftnlen, ftnint);
+
+integer do_lio(ftnint *type, ftnint *number, char *ptr, ftnlen len)
+#endif
+{
+	return((*f__lioproc)(number,ptr,len,*type));
+}
+#ifdef __cplusplus
+	}
+#endif
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/dorg2r.c b/igraph/src/dorg2r.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dorg2r.c
@@ -0,0 +1,233 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DORG2R generates all or part of the orthogonal matrix Q from a QR factorization determined by s
+geqrf (unblocked algorithm).   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORG2R + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dorg2r.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dorg2r.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dorg2r.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORG2R( M, N, K, A, LDA, TAU, WORK, INFO )   
+
+         INTEGER            INFO, K, LDA, M, N   
+         DOUBLE PRECISION   A( LDA, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORG2R generates an m by n real matrix Q with orthonormal columns,   
+   > which is defined as the first n columns of a product of k elementary   
+   > reflectors of order m   
+   >   
+   >       Q  =  H(1) H(2) . . . H(k)   
+   >   
+   > as returned by DGEQRF.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix Q. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix Q. M >= N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] K   
+   > \verbatim   
+   >          K is INTEGER   
+   >          The number of elementary reflectors whose product defines the   
+   >          matrix Q. N >= K >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the i-th column must contain the vector which   
+   >          defines the elementary reflector H(i), for i = 1,2,...,k, as   
+   >          returned by DGEQRF in the first k columns of its array   
+   >          argument A.   
+   >          On exit, the m-by-n matrix Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The first dimension of the array A. LDA >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (K)   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DGEQRF.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -i, the i-th argument has an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdorg2r_(integer *m, integer *n, integer *k, doublereal *
+	a, integer *lda, doublereal *tau, doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+    doublereal d__1;
+
+    /* Local variables */
+    integer i__, j, l;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *), igraphdlarf_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *), igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    if (*m < 0) {
+	*info = -1;
+    } else if (*n < 0 || *n > *m) {
+	*info = -2;
+    } else if (*k < 0 || *k > *n) {
+	*info = -3;
+    } else if (*lda < max(1,*m)) {
+	*info = -5;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DORG2R", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n <= 0) {
+	return 0;
+    }
+
+/*     Initialise columns k+1:n to columns of the unit matrix */
+
+    i__1 = *n;
+    for (j = *k + 1; j <= i__1; ++j) {
+	i__2 = *m;
+	for (l = 1; l <= i__2; ++l) {
+	    a[l + j * a_dim1] = 0.;
+/* L10: */
+	}
+	a[j + j * a_dim1] = 1.;
+/* L20: */
+    }
+
+    for (i__ = *k; i__ >= 1; --i__) {
+
+/*        Apply H(i) to A(i:m,i:n) from the left */
+
+	if (i__ < *n) {
+	    a[i__ + i__ * a_dim1] = 1.;
+	    i__1 = *m - i__ + 1;
+	    i__2 = *n - i__;
+	    igraphdlarf_("Left", &i__1, &i__2, &a[i__ + i__ * a_dim1], &c__1, &tau[
+		    i__], &a[i__ + (i__ + 1) * a_dim1], lda, &work[1]);
+	}
+	if (i__ < *m) {
+	    i__1 = *m - i__;
+	    d__1 = -tau[i__];
+	    igraphdscal_(&i__1, &d__1, &a[i__ + 1 + i__ * a_dim1], &c__1);
+	}
+	a[i__ + i__ * a_dim1] = 1. - tau[i__];
+
+/*        Set A(1:i-1,i) to zero */
+
+	i__1 = i__ - 1;
+	for (l = 1; l <= i__1; ++l) {
+	    a[l + i__ * a_dim1] = 0.;
+/* L30: */
+	}
+/* L40: */
+    }
+    return 0;
+
+/*     End of DORG2R */
+
+} /* igraphdorg2r_ */
+
diff --git a/igraph/src/dorghr.c b/igraph/src/dorghr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dorghr.c
@@ -0,0 +1,277 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+
+/* > \brief \b DORGHR   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORGHR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dorghr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dorghr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dorghr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORGHR( N, ILO, IHI, A, LDA, TAU, WORK, LWORK, INFO )   
+
+         INTEGER            IHI, ILO, INFO, LDA, LWORK, N   
+         DOUBLE PRECISION   A( LDA, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORGHR generates a real orthogonal matrix Q which is defined as the   
+   > product of IHI-ILO elementary reflectors of order N, as returned by   
+   > DGEHRD:   
+   >   
+   > Q = H(ilo) H(ilo+1) . . . H(ihi-1).   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix Q. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >   
+   >          ILO and IHI must have the same values as in the previous call   
+   >          of DGEHRD. Q is equal to the unit matrix except in the   
+   >          submatrix Q(ilo+1:ihi,ilo+1:ihi).   
+   >          1 <= ILO <= IHI <= N, if N > 0; ILO=1 and IHI=0, if N=0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the vectors which define the elementary reflectors,   
+   >          as returned by DGEHRD.   
+   >          On exit, the N-by-N orthogonal matrix Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A. LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (N-1)   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DGEHRD.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK. LWORK >= IHI-ILO.   
+   >          For optimum performance LWORK >= (IHI-ILO)*NB, where NB is   
+   >          the optimal blocksize.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdorghr_(integer *n, integer *ilo, integer *ihi, 
+	doublereal *a, integer *lda, doublereal *tau, doublereal *work, 
+	integer *lwork, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j, nb, nh, iinfo;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    extern /* Subroutine */ int igraphdorgqr_(integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    integer *);
+    integer lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    nh = *ihi - *ilo;
+    lquery = *lwork == -1;
+    if (*n < 0) {
+	*info = -1;
+    } else if (*ilo < 1 || *ilo > max(1,*n)) {
+	*info = -2;
+    } else if (*ihi < min(*ilo,*n) || *ihi > *n) {
+	*info = -3;
+    } else if (*lda < max(1,*n)) {
+	*info = -5;
+    } else if (*lwork < max(1,nh) && ! lquery) {
+	*info = -8;
+    }
+
+    if (*info == 0) {
+	nb = igraphilaenv_(&c__1, "DORGQR", " ", &nh, &nh, &nh, &c_n1, (ftnlen)6, (
+		ftnlen)1);
+	lwkopt = max(1,nh) * nb;
+	work[1] = (doublereal) lwkopt;
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DORGHR", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	work[1] = 1.;
+	return 0;
+    }
+
+/*     Shift the vectors which define the elementary reflectors one   
+       column to the right, and set the first ilo and the last n-ihi   
+       rows and columns to those of the unit matrix */
+
+    i__1 = *ilo + 1;
+    for (j = *ihi; j >= i__1; --j) {
+	i__2 = j - 1;
+	for (i__ = 1; i__ <= i__2; ++i__) {
+	    a[i__ + j * a_dim1] = 0.;
+/* L10: */
+	}
+	i__2 = *ihi;
+	for (i__ = j + 1; i__ <= i__2; ++i__) {
+	    a[i__ + j * a_dim1] = a[i__ + (j - 1) * a_dim1];
+/* L20: */
+	}
+	i__2 = *n;
+	for (i__ = *ihi + 1; i__ <= i__2; ++i__) {
+	    a[i__ + j * a_dim1] = 0.;
+/* L30: */
+	}
+/* L40: */
+    }
+    i__1 = *ilo;
+    for (j = 1; j <= i__1; ++j) {
+	i__2 = *n;
+	for (i__ = 1; i__ <= i__2; ++i__) {
+	    a[i__ + j * a_dim1] = 0.;
+/* L50: */
+	}
+	a[j + j * a_dim1] = 1.;
+/* L60: */
+    }
+    i__1 = *n;
+    for (j = *ihi + 1; j <= i__1; ++j) {
+	i__2 = *n;
+	for (i__ = 1; i__ <= i__2; ++i__) {
+	    a[i__ + j * a_dim1] = 0.;
+/* L70: */
+	}
+	a[j + j * a_dim1] = 1.;
+/* L80: */
+    }
+
+    if (nh > 0) {
+
+/*        Generate Q(ilo+1:ihi,ilo+1:ihi) */
+
+	igraphdorgqr_(&nh, &nh, &nh, &a[*ilo + 1 + (*ilo + 1) * a_dim1], lda, &tau[*
+		ilo], &work[1], lwork, &iinfo);
+    }
+    work[1] = (doublereal) lwkopt;
+    return 0;
+
+/*     End of DORGHR */
+
+} /* igraphdorghr_ */
+
diff --git a/igraph/src/dorgqr.c b/igraph/src/dorgqr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dorgqr.c
@@ -0,0 +1,341 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__3 = 3;
+static integer c__2 = 2;
+
+/* > \brief \b DORGQR   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORGQR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dorgqr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dorgqr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dorgqr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORGQR( M, N, K, A, LDA, TAU, WORK, LWORK, INFO )   
+
+         INTEGER            INFO, K, LDA, LWORK, M, N   
+         DOUBLE PRECISION   A( LDA, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORGQR generates an M-by-N real matrix Q with orthonormal columns,   
+   > which is defined as the first N columns of a product of K elementary   
+   > reflectors of order M   
+   >   
+   >       Q  =  H(1) H(2) . . . H(k)   
+   >   
+   > as returned by DGEQRF.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix Q. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix Q. M >= N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] K   
+   > \verbatim   
+   >          K is INTEGER   
+   >          The number of elementary reflectors whose product defines the   
+   >          matrix Q. N >= K >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the i-th column must contain the vector which   
+   >          defines the elementary reflector H(i), for i = 1,2,...,k, as   
+   >          returned by DGEQRF in the first k columns of its array   
+   >          argument A.   
+   >          On exit, the M-by-N matrix Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The first dimension of the array A. LDA >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (K)   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DGEQRF.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK. LWORK >= max(1,N).   
+   >          For optimum performance LWORK >= N*NB, where NB is the   
+   >          optimal blocksize.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument has an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdorgqr_(integer *m, integer *n, integer *k, doublereal *
+	a, integer *lda, doublereal *tau, doublereal *work, integer *lwork, 
+	integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__, j, l, ib, nb, ki, kk, nx, iws, nbmin, iinfo;
+    extern /* Subroutine */ int igraphdorg2r_(integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *), 
+	    igraphdlarfb_(char *, char *, char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), igraphdlarft_(char *, char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *), igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    integer ldwork, lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    nb = igraphilaenv_(&c__1, "DORGQR", " ", m, n, k, &c_n1, (ftnlen)6, (ftnlen)1);
+    lwkopt = max(1,*n) * nb;
+    work[1] = (doublereal) lwkopt;
+    lquery = *lwork == -1;
+    if (*m < 0) {
+	*info = -1;
+    } else if (*n < 0 || *n > *m) {
+	*info = -2;
+    } else if (*k < 0 || *k > *n) {
+	*info = -3;
+    } else if (*lda < max(1,*m)) {
+	*info = -5;
+    } else if (*lwork < max(1,*n) && ! lquery) {
+	*info = -8;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DORGQR", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n <= 0) {
+	work[1] = 1.;
+	return 0;
+    }
+
+    nbmin = 2;
+    nx = 0;
+    iws = *n;
+    if (nb > 1 && nb < *k) {
+
+/*        Determine when to cross over from blocked to unblocked code.   
+
+   Computing MAX */
+	i__1 = 0, i__2 = igraphilaenv_(&c__3, "DORGQR", " ", m, n, k, &c_n1, (
+		ftnlen)6, (ftnlen)1);
+	nx = max(i__1,i__2);
+	if (nx < *k) {
+
+/*           Determine if workspace is large enough for blocked code. */
+
+	    ldwork = *n;
+	    iws = ldwork * nb;
+	    if (*lwork < iws) {
+
+/*              Not enough workspace to use optimal NB:  reduce NB and   
+                determine the minimum value of NB. */
+
+		nb = *lwork / ldwork;
+/* Computing MAX */
+		i__1 = 2, i__2 = igraphilaenv_(&c__2, "DORGQR", " ", m, n, k, &c_n1,
+			 (ftnlen)6, (ftnlen)1);
+		nbmin = max(i__1,i__2);
+	    }
+	}
+    }
+
+    if (nb >= nbmin && nb < *k && nx < *k) {
+
+/*        Use blocked code after the last block.   
+          The first kk columns are handled by the block method. */
+
+	ki = (*k - nx - 1) / nb * nb;
+/* Computing MIN */
+	i__1 = *k, i__2 = ki + nb;
+	kk = min(i__1,i__2);
+
+/*        Set A(1:kk,kk+1:n) to zero. */
+
+	i__1 = *n;
+	for (j = kk + 1; j <= i__1; ++j) {
+	    i__2 = kk;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		a[i__ + j * a_dim1] = 0.;
+/* L10: */
+	    }
+/* L20: */
+	}
+    } else {
+	kk = 0;
+    }
+
+/*     Use unblocked code for the last or only block. */
+
+    if (kk < *n) {
+	i__1 = *m - kk;
+	i__2 = *n - kk;
+	i__3 = *k - kk;
+	igraphdorg2r_(&i__1, &i__2, &i__3, &a[kk + 1 + (kk + 1) * a_dim1], lda, &
+		tau[kk + 1], &work[1], &iinfo);
+    }
+
+    if (kk > 0) {
+
+/*        Use blocked code */
+
+	i__1 = -nb;
+	for (i__ = ki + 1; i__1 < 0 ? i__ >= 1 : i__ <= 1; i__ += i__1) {
+/* Computing MIN */
+	    i__2 = nb, i__3 = *k - i__ + 1;
+	    ib = min(i__2,i__3);
+	    if (i__ + ib <= *n) {
+
+/*              Form the triangular factor of the block reflector   
+                H = H(i) H(i+1) . . . H(i+ib-1) */
+
+		i__2 = *m - i__ + 1;
+		igraphdlarft_("Forward", "Columnwise", &i__2, &ib, &a[i__ + i__ * 
+			a_dim1], lda, &tau[i__], &work[1], &ldwork);
+
+/*              Apply H to A(i:m,i+ib:n) from the left */
+
+		i__2 = *m - i__ + 1;
+		i__3 = *n - i__ - ib + 1;
+		igraphdlarfb_("Left", "No transpose", "Forward", "Columnwise", &
+			i__2, &i__3, &ib, &a[i__ + i__ * a_dim1], lda, &work[
+			1], &ldwork, &a[i__ + (i__ + ib) * a_dim1], lda, &
+			work[ib + 1], &ldwork);
+	    }
+
+/*           Apply H to rows i:m of current block */
+
+	    i__2 = *m - i__ + 1;
+	    igraphdorg2r_(&i__2, &ib, &ib, &a[i__ + i__ * a_dim1], lda, &tau[i__], &
+		    work[1], &iinfo);
+
+/*           Set rows 1:i-1 of current block to zero */
+
+	    i__2 = i__ + ib - 1;
+	    for (j = i__; j <= i__2; ++j) {
+		i__3 = i__ - 1;
+		for (l = 1; l <= i__3; ++l) {
+		    a[l + j * a_dim1] = 0.;
+/* L30: */
+		}
+/* L40: */
+	    }
+/* L50: */
+	}
+    }
+
+    work[1] = (doublereal) iws;
+    return 0;
+
+/*     End of DORGQR */
+
+} /* igraphdorgqr_ */
+
diff --git a/igraph/src/dorm2l.c b/igraph/src/dorm2l.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dorm2l.c
@@ -0,0 +1,301 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DORM2L multiplies a general matrix by the orthogonal matrix from a QL factorization determined 
+by sgeqlf (unblocked algorithm).   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORM2L + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dorm2l.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dorm2l.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dorm2l.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORM2L( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC,   
+                            WORK, INFO )   
+
+         CHARACTER          SIDE, TRANS   
+         INTEGER            INFO, K, LDA, LDC, M, N   
+         DOUBLE PRECISION   A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORM2L overwrites the general real m by n matrix C with   
+   >   
+   >       Q * C  if SIDE = 'L' and TRANS = 'N', or   
+   >   
+   >       Q**T * C  if SIDE = 'L' and TRANS = 'T', or   
+   >   
+   >       C * Q  if SIDE = 'R' and TRANS = 'N', or   
+   >   
+   >       C * Q**T if SIDE = 'R' and TRANS = 'T',   
+   >   
+   > where Q is a real orthogonal matrix defined as the product of k   
+   > elementary reflectors   
+   >   
+   >       Q = H(k) . . . H(2) H(1)   
+   >   
+   > as returned by DGEQLF. Q is of order m if SIDE = 'L' and of order n   
+   > if SIDE = 'R'.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': apply Q or Q**T from the Left   
+   >          = 'R': apply Q or Q**T from the Right   
+   > \endverbatim   
+   >   
+   > \param[in] TRANS   
+   > \verbatim   
+   >          TRANS is CHARACTER*1   
+   >          = 'N': apply Q  (No transpose)   
+   >          = 'T': apply Q**T (Transpose)   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] K   
+   > \verbatim   
+   >          K is INTEGER   
+   >          The number of elementary reflectors whose product defines   
+   >          the matrix Q.   
+   >          If SIDE = 'L', M >= K >= 0;   
+   >          if SIDE = 'R', N >= K >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,K)   
+   >          The i-th column must contain the vector which defines the   
+   >          elementary reflector H(i), for i = 1,2,...,k, as returned by   
+   >          DGEQLF in the last k columns of its array argument A.   
+   >          A is modified by the routine but restored on exit.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.   
+   >          If SIDE = 'L', LDA >= max(1,M);   
+   >          if SIDE = 'R', LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (K)   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DGEQLF.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the m by n matrix C.   
+   >          On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension   
+   >                                   (N) if SIDE = 'L',   
+   >                                   (M) if SIDE = 'R'   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdorm2l_(char *side, char *trans, integer *m, integer *n, 
+	integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal *
+	c__, integer *ldc, doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, c_dim1, c_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, i1, i2, i3, mi, ni, nq;
+    doublereal aii;
+    logical left;
+    extern /* Subroutine */ int igraphdlarf_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical notran;
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    left = igraphlsame_(side, "L");
+    notran = igraphlsame_(trans, "N");
+
+/*     NQ is the order of Q */
+
+    if (left) {
+	nq = *m;
+    } else {
+	nq = *n;
+    }
+    if (! left && ! igraphlsame_(side, "R")) {
+	*info = -1;
+    } else if (! notran && ! igraphlsame_(trans, "T")) {
+	*info = -2;
+    } else if (*m < 0) {
+	*info = -3;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*k < 0 || *k > nq) {
+	*info = -5;
+    } else if (*lda < max(1,nq)) {
+	*info = -7;
+    } else if (*ldc < max(1,*m)) {
+	*info = -10;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DORM2L", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0 || *k == 0) {
+	return 0;
+    }
+
+    if (left && notran || ! left && ! notran) {
+	i1 = 1;
+	i2 = *k;
+	i3 = 1;
+    } else {
+	i1 = *k;
+	i2 = 1;
+	i3 = -1;
+    }
+
+    if (left) {
+	ni = *n;
+    } else {
+	mi = *m;
+    }
+
+    i__1 = i2;
+    i__2 = i3;
+    for (i__ = i1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) {
+	if (left) {
+
+/*           H(i) is applied to C(1:m-k+i,1:n) */
+
+	    mi = *m - *k + i__;
+	} else {
+
+/*           H(i) is applied to C(1:m,1:n-k+i) */
+
+	    ni = *n - *k + i__;
+	}
+
+/*        Apply H(i) */
+
+	aii = a[nq - *k + i__ + i__ * a_dim1];
+	a[nq - *k + i__ + i__ * a_dim1] = 1.;
+	igraphdlarf_(side, &mi, &ni, &a[i__ * a_dim1 + 1], &c__1, &tau[i__], &c__[
+		c_offset], ldc, &work[1]);
+	a[nq - *k + i__ + i__ * a_dim1] = aii;
+/* L10: */
+    }
+    return 0;
+
+/*     End of DORM2L */
+
+} /* igraphdorm2l_ */
+
diff --git a/igraph/src/dorm2r.c b/igraph/src/dorm2r.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dorm2r.c
@@ -0,0 +1,305 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b DORM2R multiplies a general matrix by the orthogonal matrix from a QR factorization determined 
+by sgeqrf (unblocked algorithm).   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORM2R + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dorm2r.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dorm2r.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dorm2r.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORM2R( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC,   
+                            WORK, INFO )   
+
+         CHARACTER          SIDE, TRANS   
+         INTEGER            INFO, K, LDA, LDC, M, N   
+         DOUBLE PRECISION   A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORM2R overwrites the general real m by n matrix C with   
+   >   
+   >       Q * C  if SIDE = 'L' and TRANS = 'N', or   
+   >   
+   >       Q**T* C  if SIDE = 'L' and TRANS = 'T', or   
+   >   
+   >       C * Q  if SIDE = 'R' and TRANS = 'N', or   
+   >   
+   >       C * Q**T if SIDE = 'R' and TRANS = 'T',   
+   >   
+   > where Q is a real orthogonal matrix defined as the product of k   
+   > elementary reflectors   
+   >   
+   >       Q = H(1) H(2) . . . H(k)   
+   >   
+   > as returned by DGEQRF. Q is of order m if SIDE = 'L' and of order n   
+   > if SIDE = 'R'.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': apply Q or Q**T from the Left   
+   >          = 'R': apply Q or Q**T from the Right   
+   > \endverbatim   
+   >   
+   > \param[in] TRANS   
+   > \verbatim   
+   >          TRANS is CHARACTER*1   
+   >          = 'N': apply Q  (No transpose)   
+   >          = 'T': apply Q**T (Transpose)   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] K   
+   > \verbatim   
+   >          K is INTEGER   
+   >          The number of elementary reflectors whose product defines   
+   >          the matrix Q.   
+   >          If SIDE = 'L', M >= K >= 0;   
+   >          if SIDE = 'R', N >= K >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,K)   
+   >          The i-th column must contain the vector which defines the   
+   >          elementary reflector H(i), for i = 1,2,...,k, as returned by   
+   >          DGEQRF in the first k columns of its array argument A.   
+   >          A is modified by the routine but restored on exit.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.   
+   >          If SIDE = 'L', LDA >= max(1,M);   
+   >          if SIDE = 'R', LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (K)   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DGEQRF.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the m by n matrix C.   
+   >          On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension   
+   >                                   (N) if SIDE = 'L',   
+   >                                   (M) if SIDE = 'R'   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdorm2r_(char *side, char *trans, integer *m, integer *n, 
+	integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal *
+	c__, integer *ldc, doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, c_dim1, c_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, i1, i2, i3, ic, jc, mi, ni, nq;
+    doublereal aii;
+    logical left;
+    extern /* Subroutine */ int igraphdlarf_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical notran;
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    left = igraphlsame_(side, "L");
+    notran = igraphlsame_(trans, "N");
+
+/*     NQ is the order of Q */
+
+    if (left) {
+	nq = *m;
+    } else {
+	nq = *n;
+    }
+    if (! left && ! igraphlsame_(side, "R")) {
+	*info = -1;
+    } else if (! notran && ! igraphlsame_(trans, "T")) {
+	*info = -2;
+    } else if (*m < 0) {
+	*info = -3;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*k < 0 || *k > nq) {
+	*info = -5;
+    } else if (*lda < max(1,nq)) {
+	*info = -7;
+    } else if (*ldc < max(1,*m)) {
+	*info = -10;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DORM2R", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0 || *k == 0) {
+	return 0;
+    }
+
+    if (left && ! notran || ! left && notran) {
+	i1 = 1;
+	i2 = *k;
+	i3 = 1;
+    } else {
+	i1 = *k;
+	i2 = 1;
+	i3 = -1;
+    }
+
+    if (left) {
+	ni = *n;
+	jc = 1;
+    } else {
+	mi = *m;
+	ic = 1;
+    }
+
+    i__1 = i2;
+    i__2 = i3;
+    for (i__ = i1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) {
+	if (left) {
+
+/*           H(i) is applied to C(i:m,1:n) */
+
+	    mi = *m - i__ + 1;
+	    ic = i__;
+	} else {
+
+/*           H(i) is applied to C(1:m,i:n) */
+
+	    ni = *n - i__ + 1;
+	    jc = i__;
+	}
+
+/*        Apply H(i) */
+
+	aii = a[i__ + i__ * a_dim1];
+	a[i__ + i__ * a_dim1] = 1.;
+	igraphdlarf_(side, &mi, &ni, &a[i__ + i__ * a_dim1], &c__1, &tau[i__], &c__[
+		ic + jc * c_dim1], ldc, &work[1]);
+	a[i__ + i__ * a_dim1] = aii;
+/* L10: */
+    }
+    return 0;
+
+/*     End of DORM2R */
+
+} /* igraphdorm2r_ */
+
diff --git a/igraph/src/dormhr.c b/igraph/src/dormhr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dormhr.c
@@ -0,0 +1,338 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__2 = 2;
+
+/* > \brief \b DORMHR   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORMHR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dormhr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dormhr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dormhr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORMHR( SIDE, TRANS, M, N, ILO, IHI, A, LDA, TAU, C,   
+                            LDC, WORK, LWORK, INFO )   
+
+         CHARACTER          SIDE, TRANS   
+         INTEGER            IHI, ILO, INFO, LDA, LDC, LWORK, M, N   
+         DOUBLE PRECISION   A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORMHR overwrites the general real M-by-N matrix C with   
+   >   
+   >                 SIDE = 'L'     SIDE = 'R'   
+   > TRANS = 'N':      Q * C          C * Q   
+   > TRANS = 'T':      Q**T * C       C * Q**T   
+   >   
+   > where Q is a real orthogonal matrix of order nq, with nq = m if   
+   > SIDE = 'L' and nq = n if SIDE = 'R'. Q is defined as the product of   
+   > IHI-ILO elementary reflectors, as returned by DGEHRD:   
+   >   
+   > Q = H(ilo) H(ilo+1) . . . H(ihi-1).   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': apply Q or Q**T from the Left;   
+   >          = 'R': apply Q or Q**T from the Right.   
+   > \endverbatim   
+   >   
+   > \param[in] TRANS   
+   > \verbatim   
+   >          TRANS is CHARACTER*1   
+   >          = 'N':  No transpose, apply Q;   
+   >          = 'T':  Transpose, apply Q**T.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >   
+   >          ILO and IHI must have the same values as in the previous call   
+   >          of DGEHRD. Q is equal to the unit matrix except in the   
+   >          submatrix Q(ilo+1:ihi,ilo+1:ihi).   
+   >          If SIDE = 'L', then 1 <= ILO <= IHI <= M, if M > 0, and   
+   >          ILO = 1 and IHI = 0, if M = 0;   
+   >          if SIDE = 'R', then 1 <= ILO <= IHI <= N, if N > 0, and   
+   >          ILO = 1 and IHI = 0, if N = 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension   
+   >                               (LDA,M) if SIDE = 'L'   
+   >                               (LDA,N) if SIDE = 'R'   
+   >          The vectors which define the elementary reflectors, as   
+   >          returned by DGEHRD.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.   
+   >          LDA >= max(1,M) if SIDE = 'L'; LDA >= max(1,N) if SIDE = 'R'.   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension   
+   >                               (M-1) if SIDE = 'L'   
+   >                               (N-1) if SIDE = 'R'   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DGEHRD.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the M-by-N matrix C.   
+   >          On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.   
+   >          If SIDE = 'L', LWORK >= max(1,N);   
+   >          if SIDE = 'R', LWORK >= max(1,M).   
+   >          For optimum performance LWORK >= N*NB if SIDE = 'L', and   
+   >          LWORK >= M*NB if SIDE = 'R', where NB is the optimal   
+   >          blocksize.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdormhr_(char *side, char *trans, integer *m, integer *n, 
+	integer *ilo, integer *ihi, doublereal *a, integer *lda, doublereal *
+	tau, doublereal *c__, integer *ldc, doublereal *work, integer *lwork, 
+	integer *info)
+{
+    /* System generated locals */
+    address a__1[2];
+    integer a_dim1, a_offset, c_dim1, c_offset, i__1[2], i__2;
+    char ch__1[2];
+
+    /* Builtin functions   
+       Subroutine */ int s_cat(char *, char **, integer *, integer *, ftnlen);
+
+    /* Local variables */
+    integer i1, i2, nb, mi, nh, ni, nq, nw;
+    logical left;
+    extern logical igraphlsame_(char *, char *);
+    integer iinfo;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    extern /* Subroutine */ int igraphdormqr_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *);
+    integer lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    nh = *ihi - *ilo;
+    left = igraphlsame_(side, "L");
+    lquery = *lwork == -1;
+
+/*     NQ is the order of Q and NW is the minimum dimension of WORK */
+
+    if (left) {
+	nq = *m;
+	nw = *n;
+    } else {
+	nq = *n;
+	nw = *m;
+    }
+    if (! left && ! igraphlsame_(side, "R")) {
+	*info = -1;
+    } else if (! igraphlsame_(trans, "N") && ! igraphlsame_(trans, 
+	    "T")) {
+	*info = -2;
+    } else if (*m < 0) {
+	*info = -3;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*ilo < 1 || *ilo > max(1,nq)) {
+	*info = -5;
+    } else if (*ihi < min(*ilo,nq) || *ihi > nq) {
+	*info = -6;
+    } else if (*lda < max(1,nq)) {
+	*info = -8;
+    } else if (*ldc < max(1,*m)) {
+	*info = -11;
+    } else if (*lwork < max(1,nw) && ! lquery) {
+	*info = -13;
+    }
+
+    if (*info == 0) {
+	if (left) {
+/* Writing concatenation */
+	    i__1[0] = 1, a__1[0] = side;
+	    i__1[1] = 1, a__1[1] = trans;
+	    s_cat(ch__1, a__1, i__1, &c__2, (ftnlen)2);
+	    nb = igraphilaenv_(&c__1, "DORMQR", ch__1, &nh, n, &nh, &c_n1, (ftnlen)
+		    6, (ftnlen)2);
+	} else {
+/* Writing concatenation */
+	    i__1[0] = 1, a__1[0] = side;
+	    i__1[1] = 1, a__1[1] = trans;
+	    s_cat(ch__1, a__1, i__1, &c__2, (ftnlen)2);
+	    nb = igraphilaenv_(&c__1, "DORMQR", ch__1, m, &nh, &nh, &c_n1, (ftnlen)
+		    6, (ftnlen)2);
+	}
+	lwkopt = max(1,nw) * nb;
+	work[1] = (doublereal) lwkopt;
+    }
+
+    if (*info != 0) {
+	i__2 = -(*info);
+	igraphxerbla_("DORMHR", &i__2, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0 || nh == 0) {
+	work[1] = 1.;
+	return 0;
+    }
+
+    if (left) {
+	mi = nh;
+	ni = *n;
+	i1 = *ilo + 1;
+	i2 = 1;
+    } else {
+	mi = *m;
+	ni = nh;
+	i1 = 1;
+	i2 = *ilo + 1;
+    }
+
+    igraphdormqr_(side, trans, &mi, &ni, &nh, &a[*ilo + 1 + *ilo * a_dim1], lda, &
+	    tau[*ilo], &c__[i1 + i2 * c_dim1], ldc, &work[1], lwork, &iinfo);
+
+    work[1] = (doublereal) lwkopt;
+    return 0;
+
+/*     End of DORMHR */
+
+} /* igraphdormhr_ */
+
diff --git a/igraph/src/dormql.c b/igraph/src/dormql.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dormql.c
@@ -0,0 +1,398 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__2 = 2;
+static integer c__65 = 65;
+
+/* > \brief \b DORMQL   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORMQL + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dormql.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dormql.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dormql.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORMQL( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC,   
+                            WORK, LWORK, INFO )   
+
+         CHARACTER          SIDE, TRANS   
+         INTEGER            INFO, K, LDA, LDC, LWORK, M, N   
+         DOUBLE PRECISION   A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORMQL overwrites the general real M-by-N matrix C with   
+   >   
+   >                 SIDE = 'L'     SIDE = 'R'   
+   > TRANS = 'N':      Q * C          C * Q   
+   > TRANS = 'T':      Q**T * C       C * Q**T   
+   >   
+   > where Q is a real orthogonal matrix defined as the product of k   
+   > elementary reflectors   
+   >   
+   >       Q = H(k) . . . H(2) H(1)   
+   >   
+   > as returned by DGEQLF. Q is of order M if SIDE = 'L' and of order N   
+   > if SIDE = 'R'.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': apply Q or Q**T from the Left;   
+   >          = 'R': apply Q or Q**T from the Right.   
+   > \endverbatim   
+   >   
+   > \param[in] TRANS   
+   > \verbatim   
+   >          TRANS is CHARACTER*1   
+   >          = 'N':  No transpose, apply Q;   
+   >          = 'T':  Transpose, apply Q**T.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] K   
+   > \verbatim   
+   >          K is INTEGER   
+   >          The number of elementary reflectors whose product defines   
+   >          the matrix Q.   
+   >          If SIDE = 'L', M >= K >= 0;   
+   >          if SIDE = 'R', N >= K >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,K)   
+   >          The i-th column must contain the vector which defines the   
+   >          elementary reflector H(i), for i = 1,2,...,k, as returned by   
+   >          DGEQLF in the last k columns of its array argument A.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.   
+   >          If SIDE = 'L', LDA >= max(1,M);   
+   >          if SIDE = 'R', LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (K)   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DGEQLF.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the M-by-N matrix C.   
+   >          On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.   
+   >          If SIDE = 'L', LWORK >= max(1,N);   
+   >          if SIDE = 'R', LWORK >= max(1,M).   
+   >          For optimum performance LWORK >= N*NB if SIDE = 'L', and   
+   >          LWORK >= M*NB if SIDE = 'R', where NB is the optimal   
+   >          blocksize.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdormql_(char *side, char *trans, integer *m, integer *n, 
+	integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal *
+	c__, integer *ldc, doublereal *work, integer *lwork, integer *info)
+{
+    /* System generated locals */
+    address a__1[2];
+    integer a_dim1, a_offset, c_dim1, c_offset, i__1, i__2, i__3[2], i__4, 
+	    i__5;
+    char ch__1[2];
+
+    /* Builtin functions   
+       Subroutine */ int s_cat(char *, char **, integer *, integer *, ftnlen);
+
+    /* Local variables */
+    integer i__;
+    doublereal t[4160]	/* was [65][64] */;
+    integer i1, i2, i3, ib, nb, mi, ni, nq, nw, iws;
+    logical left;
+    extern logical igraphlsame_(char *, char *);
+    integer nbmin, iinfo;
+    extern /* Subroutine */ int igraphdorm2l_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *), igraphdlarfb_(char 
+	    *, char *, char *, char *, integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *), igraphdlarft_(char *, char *, integer *, integer *, doublereal 
+	    *, integer *, doublereal *, doublereal *, integer *), igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    logical notran;
+    integer ldwork, lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    left = igraphlsame_(side, "L");
+    notran = igraphlsame_(trans, "N");
+    lquery = *lwork == -1;
+
+/*     NQ is the order of Q and NW is the minimum dimension of WORK */
+
+    if (left) {
+	nq = *m;
+	nw = max(1,*n);
+    } else {
+	nq = *n;
+	nw = max(1,*m);
+    }
+    if (! left && ! igraphlsame_(side, "R")) {
+	*info = -1;
+    } else if (! notran && ! igraphlsame_(trans, "T")) {
+	*info = -2;
+    } else if (*m < 0) {
+	*info = -3;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*k < 0 || *k > nq) {
+	*info = -5;
+    } else if (*lda < max(1,nq)) {
+	*info = -7;
+    } else if (*ldc < max(1,*m)) {
+	*info = -10;
+    }
+
+    if (*info == 0) {
+	if (*m == 0 || *n == 0) {
+	    lwkopt = 1;
+	} else {
+
+/*           Determine the block size.  NB may be at most NBMAX, where   
+             NBMAX is used to define the local array T.   
+
+   Computing MIN   
+   Writing concatenation */
+	    i__3[0] = 1, a__1[0] = side;
+	    i__3[1] = 1, a__1[1] = trans;
+	    s_cat(ch__1, a__1, i__3, &c__2, (ftnlen)2);
+	    i__1 = 64, i__2 = igraphilaenv_(&c__1, "DORMQL", ch__1, m, n, k, &c_n1, 
+		    (ftnlen)6, (ftnlen)2);
+	    nb = min(i__1,i__2);
+	    lwkopt = nw * nb;
+	}
+	work[1] = (doublereal) lwkopt;
+
+	if (*lwork < nw && ! lquery) {
+	    *info = -12;
+	}
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DORMQL", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0) {
+	return 0;
+    }
+
+    nbmin = 2;
+    ldwork = nw;
+    if (nb > 1 && nb < *k) {
+	iws = nw * nb;
+	if (*lwork < iws) {
+	    nb = *lwork / ldwork;
+/* Computing MAX   
+   Writing concatenation */
+	    i__3[0] = 1, a__1[0] = side;
+	    i__3[1] = 1, a__1[1] = trans;
+	    s_cat(ch__1, a__1, i__3, &c__2, (ftnlen)2);
+	    i__1 = 2, i__2 = igraphilaenv_(&c__2, "DORMQL", ch__1, m, n, k, &c_n1, (
+		    ftnlen)6, (ftnlen)2);
+	    nbmin = max(i__1,i__2);
+	}
+    } else {
+	iws = nw;
+    }
+
+    if (nb < nbmin || nb >= *k) {
+
+/*        Use unblocked code */
+
+	igraphdorm2l_(side, trans, m, n, k, &a[a_offset], lda, &tau[1], &c__[
+		c_offset], ldc, &work[1], &iinfo);
+    } else {
+
+/*        Use blocked code */
+
+	if (left && notran || ! left && ! notran) {
+	    i1 = 1;
+	    i2 = *k;
+	    i3 = nb;
+	} else {
+	    i1 = (*k - 1) / nb * nb + 1;
+	    i2 = 1;
+	    i3 = -nb;
+	}
+
+	if (left) {
+	    ni = *n;
+	} else {
+	    mi = *m;
+	}
+
+	i__1 = i2;
+	i__2 = i3;
+	for (i__ = i1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) {
+/* Computing MIN */
+	    i__4 = nb, i__5 = *k - i__ + 1;
+	    ib = min(i__4,i__5);
+
+/*           Form the triangular factor of the block reflector   
+             H = H(i+ib-1) . . . H(i+1) H(i) */
+
+	    i__4 = nq - *k + i__ + ib - 1;
+	    igraphdlarft_("Backward", "Columnwise", &i__4, &ib, &a[i__ * a_dim1 + 1]
+		    , lda, &tau[i__], t, &c__65);
+	    if (left) {
+
+/*              H or H**T is applied to C(1:m-k+i+ib-1,1:n) */
+
+		mi = *m - *k + i__ + ib - 1;
+	    } else {
+
+/*              H or H**T is applied to C(1:m,1:n-k+i+ib-1) */
+
+		ni = *n - *k + i__ + ib - 1;
+	    }
+
+/*           Apply H or H**T */
+
+	    igraphdlarfb_(side, trans, "Backward", "Columnwise", &mi, &ni, &ib, &a[
+		    i__ * a_dim1 + 1], lda, t, &c__65, &c__[c_offset], ldc, &
+		    work[1], &ldwork);
+/* L10: */
+	}
+    }
+    work[1] = (doublereal) lwkopt;
+    return 0;
+
+/*     End of DORMQL */
+
+} /* igraphdormql_ */
+
diff --git a/igraph/src/dormqr.c b/igraph/src/dormqr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dormqr.c
@@ -0,0 +1,398 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__2 = 2;
+static integer c__65 = 65;
+
+/* > \brief \b DORMQR   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORMQR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dormqr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dormqr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dormqr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORMQR( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC,   
+                            WORK, LWORK, INFO )   
+
+         CHARACTER          SIDE, TRANS   
+         INTEGER            INFO, K, LDA, LDC, LWORK, M, N   
+         DOUBLE PRECISION   A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORMQR overwrites the general real M-by-N matrix C with   
+   >   
+   >                 SIDE = 'L'     SIDE = 'R'   
+   > TRANS = 'N':      Q * C          C * Q   
+   > TRANS = 'T':      Q**T * C       C * Q**T   
+   >   
+   > where Q is a real orthogonal matrix defined as the product of k   
+   > elementary reflectors   
+   >   
+   >       Q = H(1) H(2) . . . H(k)   
+   >   
+   > as returned by DGEQRF. Q is of order M if SIDE = 'L' and of order N   
+   > if SIDE = 'R'.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': apply Q or Q**T from the Left;   
+   >          = 'R': apply Q or Q**T from the Right.   
+   > \endverbatim   
+   >   
+   > \param[in] TRANS   
+   > \verbatim   
+   >          TRANS is CHARACTER*1   
+   >          = 'N':  No transpose, apply Q;   
+   >          = 'T':  Transpose, apply Q**T.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] K   
+   > \verbatim   
+   >          K is INTEGER   
+   >          The number of elementary reflectors whose product defines   
+   >          the matrix Q.   
+   >          If SIDE = 'L', M >= K >= 0;   
+   >          if SIDE = 'R', N >= K >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,K)   
+   >          The i-th column must contain the vector which defines the   
+   >          elementary reflector H(i), for i = 1,2,...,k, as returned by   
+   >          DGEQRF in the first k columns of its array argument A.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.   
+   >          If SIDE = 'L', LDA >= max(1,M);   
+   >          if SIDE = 'R', LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (K)   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DGEQRF.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the M-by-N matrix C.   
+   >          On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.   
+   >          If SIDE = 'L', LWORK >= max(1,N);   
+   >          if SIDE = 'R', LWORK >= max(1,M).   
+   >          For optimum performance LWORK >= N*NB if SIDE = 'L', and   
+   >          LWORK >= M*NB if SIDE = 'R', where NB is the optimal   
+   >          blocksize.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdormqr_(char *side, char *trans, integer *m, integer *n, 
+	integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal *
+	c__, integer *ldc, doublereal *work, integer *lwork, integer *info)
+{
+    /* System generated locals */
+    address a__1[2];
+    integer a_dim1, a_offset, c_dim1, c_offset, i__1, i__2, i__3[2], i__4, 
+	    i__5;
+    char ch__1[2];
+
+    /* Builtin functions   
+       Subroutine */ int s_cat(char *, char **, integer *, integer *, ftnlen);
+
+    /* Local variables */
+    integer i__;
+    doublereal t[4160]	/* was [65][64] */;
+    integer i1, i2, i3, ib, ic, jc, nb, mi, ni, nq, nw, iws;
+    logical left;
+    extern logical igraphlsame_(char *, char *);
+    integer nbmin, iinfo;
+    extern /* Subroutine */ int igraphdorm2r_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *), igraphdlarfb_(char 
+	    *, char *, char *, char *, integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *), igraphdlarft_(char *, char *, integer *, integer *, doublereal 
+	    *, integer *, doublereal *, doublereal *, integer *), igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    logical notran;
+    integer ldwork, lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    left = igraphlsame_(side, "L");
+    notran = igraphlsame_(trans, "N");
+    lquery = *lwork == -1;
+
+/*     NQ is the order of Q and NW is the minimum dimension of WORK */
+
+    if (left) {
+	nq = *m;
+	nw = *n;
+    } else {
+	nq = *n;
+	nw = *m;
+    }
+    if (! left && ! igraphlsame_(side, "R")) {
+	*info = -1;
+    } else if (! notran && ! igraphlsame_(trans, "T")) {
+	*info = -2;
+    } else if (*m < 0) {
+	*info = -3;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*k < 0 || *k > nq) {
+	*info = -5;
+    } else if (*lda < max(1,nq)) {
+	*info = -7;
+    } else if (*ldc < max(1,*m)) {
+	*info = -10;
+    } else if (*lwork < max(1,nw) && ! lquery) {
+	*info = -12;
+    }
+
+    if (*info == 0) {
+
+/*        Determine the block size.  NB may be at most NBMAX, where NBMAX   
+          is used to define the local array T.   
+
+   Computing MIN   
+   Writing concatenation */
+	i__3[0] = 1, a__1[0] = side;
+	i__3[1] = 1, a__1[1] = trans;
+	s_cat(ch__1, a__1, i__3, &c__2, (ftnlen)2);
+	i__1 = 64, i__2 = igraphilaenv_(&c__1, "DORMQR", ch__1, m, n, k, &c_n1, (
+		ftnlen)6, (ftnlen)2);
+	nb = min(i__1,i__2);
+	lwkopt = max(1,nw) * nb;
+	work[1] = (doublereal) lwkopt;
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DORMQR", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0 || *k == 0) {
+	work[1] = 1.;
+	return 0;
+    }
+
+    nbmin = 2;
+    ldwork = nw;
+    if (nb > 1 && nb < *k) {
+	iws = nw * nb;
+	if (*lwork < iws) {
+	    nb = *lwork / ldwork;
+/* Computing MAX   
+   Writing concatenation */
+	    i__3[0] = 1, a__1[0] = side;
+	    i__3[1] = 1, a__1[1] = trans;
+	    s_cat(ch__1, a__1, i__3, &c__2, (ftnlen)2);
+	    i__1 = 2, i__2 = igraphilaenv_(&c__2, "DORMQR", ch__1, m, n, k, &c_n1, (
+		    ftnlen)6, (ftnlen)2);
+	    nbmin = max(i__1,i__2);
+	}
+    } else {
+	iws = nw;
+    }
+
+    if (nb < nbmin || nb >= *k) {
+
+/*        Use unblocked code */
+
+	igraphdorm2r_(side, trans, m, n, k, &a[a_offset], lda, &tau[1], &c__[
+		c_offset], ldc, &work[1], &iinfo);
+    } else {
+
+/*        Use blocked code */
+
+	if (left && ! notran || ! left && notran) {
+	    i1 = 1;
+	    i2 = *k;
+	    i3 = nb;
+	} else {
+	    i1 = (*k - 1) / nb * nb + 1;
+	    i2 = 1;
+	    i3 = -nb;
+	}
+
+	if (left) {
+	    ni = *n;
+	    jc = 1;
+	} else {
+	    mi = *m;
+	    ic = 1;
+	}
+
+	i__1 = i2;
+	i__2 = i3;
+	for (i__ = i1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) {
+/* Computing MIN */
+	    i__4 = nb, i__5 = *k - i__ + 1;
+	    ib = min(i__4,i__5);
+
+/*           Form the triangular factor of the block reflector   
+             H = H(i) H(i+1) . . . H(i+ib-1) */
+
+	    i__4 = nq - i__ + 1;
+	    igraphdlarft_("Forward", "Columnwise", &i__4, &ib, &a[i__ + i__ * 
+		    a_dim1], lda, &tau[i__], t, &c__65)
+		    ;
+	    if (left) {
+
+/*              H or H**T is applied to C(i:m,1:n) */
+
+		mi = *m - i__ + 1;
+		ic = i__;
+	    } else {
+
+/*              H or H**T is applied to C(1:m,i:n) */
+
+		ni = *n - i__ + 1;
+		jc = i__;
+	    }
+
+/*           Apply H or H**T */
+
+	    igraphdlarfb_(side, trans, "Forward", "Columnwise", &mi, &ni, &ib, &a[
+		    i__ + i__ * a_dim1], lda, t, &c__65, &c__[ic + jc * 
+		    c_dim1], ldc, &work[1], &ldwork);
+/* L10: */
+	}
+    }
+    work[1] = (doublereal) lwkopt;
+    return 0;
+
+/*     End of DORMQR */
+
+} /* igraphdormqr_ */
+
diff --git a/igraph/src/dormtr.c b/igraph/src/dormtr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dormtr.c
@@ -0,0 +1,373 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__2 = 2;
+
+/* > \brief \b DORMTR   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DORMTR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dormtr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dormtr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dormtr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DORMTR( SIDE, UPLO, TRANS, M, N, A, LDA, TAU, C, LDC,   
+                            WORK, LWORK, INFO )   
+
+         CHARACTER          SIDE, TRANS, UPLO   
+         INTEGER            INFO, LDA, LDC, LWORK, M, N   
+         DOUBLE PRECISION   A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DORMTR overwrites the general real M-by-N matrix C with   
+   >   
+   >                 SIDE = 'L'     SIDE = 'R'   
+   > TRANS = 'N':      Q * C          C * Q   
+   > TRANS = 'T':      Q**T * C       C * Q**T   
+   >   
+   > where Q is a real orthogonal matrix of order nq, with nq = m if   
+   > SIDE = 'L' and nq = n if SIDE = 'R'. Q is defined as the product of   
+   > nq-1 elementary reflectors, as returned by DSYTRD:   
+   >   
+   > if UPLO = 'U', Q = H(nq-1) . . . H(2) H(1);   
+   >   
+   > if UPLO = 'L', Q = H(1) H(2) . . . H(nq-1).   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'L': apply Q or Q**T from the Left;   
+   >          = 'R': apply Q or Q**T from the Right.   
+   > \endverbatim   
+   >   
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          = 'U': Upper triangle of A contains elementary reflectors   
+   >                 from DSYTRD;   
+   >          = 'L': Lower triangle of A contains elementary reflectors   
+   >                 from DSYTRD.   
+   > \endverbatim   
+   >   
+   > \param[in] TRANS   
+   > \verbatim   
+   >          TRANS is CHARACTER*1   
+   >          = 'N':  No transpose, apply Q;   
+   >          = 'T':  Transpose, apply Q**T.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix C. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix C. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension   
+   >                               (LDA,M) if SIDE = 'L'   
+   >                               (LDA,N) if SIDE = 'R'   
+   >          The vectors which define the elementary reflectors, as   
+   >          returned by DSYTRD.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.   
+   >          LDA >= max(1,M) if SIDE = 'L'; LDA >= max(1,N) if SIDE = 'R'.   
+   > \endverbatim   
+   >   
+   > \param[in] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension   
+   >                               (M-1) if SIDE = 'L'   
+   >                               (N-1) if SIDE = 'R'   
+   >          TAU(i) must contain the scalar factor of the elementary   
+   >          reflector H(i), as returned by DSYTRD.   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the M-by-N matrix C.   
+   >          On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.   
+   >          If SIDE = 'L', LWORK >= max(1,N);   
+   >          if SIDE = 'R', LWORK >= max(1,M).   
+   >          For optimum performance LWORK >= N*NB if SIDE = 'L', and   
+   >          LWORK >= M*NB if SIDE = 'R', where NB is the optimal   
+   >          blocksize.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdormtr_(char *side, char *uplo, char *trans, integer *m, 
+	integer *n, doublereal *a, integer *lda, doublereal *tau, doublereal *
+	c__, integer *ldc, doublereal *work, integer *lwork, integer *info)
+{
+    /* System generated locals */
+    address a__1[2];
+    integer a_dim1, a_offset, c_dim1, c_offset, i__1[2], i__2, i__3;
+    char ch__1[2];
+
+    /* Builtin functions   
+       Subroutine */ int s_cat(char *, char **, integer *, integer *, ftnlen);
+
+    /* Local variables */
+    integer i1, i2, nb, mi, ni, nq, nw;
+    logical left;
+    extern logical igraphlsame_(char *, char *);
+    integer iinfo;
+    logical upper;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    extern /* Subroutine */ int igraphdormql_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *), 
+	    igraphdormqr_(char *, char *, integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *, integer *);
+    integer lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input arguments   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --tau;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    left = igraphlsame_(side, "L");
+    upper = igraphlsame_(uplo, "U");
+    lquery = *lwork == -1;
+
+/*     NQ is the order of Q and NW is the minimum dimension of WORK */
+
+    if (left) {
+	nq = *m;
+	nw = *n;
+    } else {
+	nq = *n;
+	nw = *m;
+    }
+    if (! left && ! igraphlsame_(side, "R")) {
+	*info = -1;
+    } else if (! upper && ! igraphlsame_(uplo, "L")) {
+	*info = -2;
+    } else if (! igraphlsame_(trans, "N") && ! igraphlsame_(trans, 
+	    "T")) {
+	*info = -3;
+    } else if (*m < 0) {
+	*info = -4;
+    } else if (*n < 0) {
+	*info = -5;
+    } else if (*lda < max(1,nq)) {
+	*info = -7;
+    } else if (*ldc < max(1,*m)) {
+	*info = -10;
+    } else if (*lwork < max(1,nw) && ! lquery) {
+	*info = -12;
+    }
+
+    if (*info == 0) {
+	if (upper) {
+	    if (left) {
+/* Writing concatenation */
+		i__1[0] = 1, a__1[0] = side;
+		i__1[1] = 1, a__1[1] = trans;
+		s_cat(ch__1, a__1, i__1, &c__2, (ftnlen)2);
+		i__2 = *m - 1;
+		i__3 = *m - 1;
+		nb = igraphilaenv_(&c__1, "DORMQL", ch__1, &i__2, n, &i__3, &c_n1, (
+			ftnlen)6, (ftnlen)2);
+	    } else {
+/* Writing concatenation */
+		i__1[0] = 1, a__1[0] = side;
+		i__1[1] = 1, a__1[1] = trans;
+		s_cat(ch__1, a__1, i__1, &c__2, (ftnlen)2);
+		i__2 = *n - 1;
+		i__3 = *n - 1;
+		nb = igraphilaenv_(&c__1, "DORMQL", ch__1, m, &i__2, &i__3, &c_n1, (
+			ftnlen)6, (ftnlen)2);
+	    }
+	} else {
+	    if (left) {
+/* Writing concatenation */
+		i__1[0] = 1, a__1[0] = side;
+		i__1[1] = 1, a__1[1] = trans;
+		s_cat(ch__1, a__1, i__1, &c__2, (ftnlen)2);
+		i__2 = *m - 1;
+		i__3 = *m - 1;
+		nb = igraphilaenv_(&c__1, "DORMQR", ch__1, &i__2, n, &i__3, &c_n1, (
+			ftnlen)6, (ftnlen)2);
+	    } else {
+/* Writing concatenation */
+		i__1[0] = 1, a__1[0] = side;
+		i__1[1] = 1, a__1[1] = trans;
+		s_cat(ch__1, a__1, i__1, &c__2, (ftnlen)2);
+		i__2 = *n - 1;
+		i__3 = *n - 1;
+		nb = igraphilaenv_(&c__1, "DORMQR", ch__1, m, &i__2, &i__3, &c_n1, (
+			ftnlen)6, (ftnlen)2);
+	    }
+	}
+	lwkopt = max(1,nw) * nb;
+	work[1] = (doublereal) lwkopt;
+    }
+
+    if (*info != 0) {
+	i__2 = -(*info);
+	igraphxerbla_("DORMTR", &i__2, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*m == 0 || *n == 0 || nq == 1) {
+	work[1] = 1.;
+	return 0;
+    }
+
+    if (left) {
+	mi = *m - 1;
+	ni = *n;
+    } else {
+	mi = *m;
+	ni = *n - 1;
+    }
+
+    if (upper) {
+
+/*        Q was determined by a call to DSYTRD with UPLO = 'U' */
+
+	i__2 = nq - 1;
+	igraphdormql_(side, trans, &mi, &ni, &i__2, &a[(a_dim1 << 1) + 1], lda, &
+		tau[1], &c__[c_offset], ldc, &work[1], lwork, &iinfo);
+    } else {
+
+/*        Q was determined by a call to DSYTRD with UPLO = 'L' */
+
+	if (left) {
+	    i1 = 2;
+	    i2 = 1;
+	} else {
+	    i1 = 1;
+	    i2 = 2;
+	}
+	i__2 = nq - 1;
+	igraphdormqr_(side, trans, &mi, &ni, &i__2, &a[a_dim1 + 2], lda, &tau[1], &
+		c__[i1 + i2 * c_dim1], ldc, &work[1], lwork, &iinfo);
+    }
+    work[1] = (doublereal) lwkopt;
+    return 0;
+
+/*     End of DORMTR */
+
+} /* igraphdormtr_ */
+
diff --git a/igraph/src/dotproduct.c b/igraph/src/dotproduct.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dotproduct.c
@@ -0,0 +1,280 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_games.h"
+#include "igraph_random.h"
+#include "igraph_constructors.h"
+#include "igraph_lapack.h"
+
+/**
+ * \function igraph_dot_product_game
+ * Generate a random dot product graph
+ *
+ * In this model, each vertex is represented by a latent
+ * position vector. Probability of an edge between two vertices are given
+ * by the dot product of their latent position vectors.
+ *
+ * </para><para>
+ * See also Christine Leigh Myers Nickel: Random dot product graphs, a
+ * model for social networks. Dissertation, Johns Hopkins University,
+ * Maryland, USA, 2006.
+ *
+ * \param graph The output graph is stored here.
+ * \param vecs A matrix in which each latent position vector is a
+ *    column. The dot product of the latent position vectors should be
+ *    in the [0,1] interval, otherwise a warning is given. For
+ *    negative dot products, no edges are added; dot products that are
+ *    larger than one always add an edge.
+ * \param directed Should the generated graph be directed?
+ * \return Error code.
+ *
+ * Time complexity: O(n*n*m), where n is the number of vertices,
+ * and m is the length of the latent vectors.
+ *
+ * \sa \ref igraph_sample_dirichlet(), \ref
+ * igraph_sample_sphere_volume(), \ref igraph_sample_sphere_surface()
+ * for functions to generate the latent vectors.
+ */
+
+int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs,
+                            igraph_bool_t directed) {
+
+    igraph_integer_t nrow = igraph_matrix_nrow(vecs);
+    igraph_integer_t ncol = igraph_matrix_ncol(vecs);
+    int i, j;
+    igraph_vector_t edges;
+    igraph_bool_t warned_neg = 0, warned_big = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < ncol; i++) {
+        int from = directed ? 0 : i + 1;
+        igraph_vector_t v1;
+        igraph_vector_view(&v1, &MATRIX(*vecs, 0, i), nrow);
+        for (j = from; j < ncol; j++) {
+            igraph_real_t prob;
+            igraph_vector_t v2;
+            if (i == j) {
+                continue;
+            }
+            igraph_vector_view(&v2, &MATRIX(*vecs, 0, j), nrow);
+            igraph_lapack_ddot(&v1, &v2, &prob);
+            if (prob < 0 && ! warned_neg) {
+                warned_neg = 1;
+                IGRAPH_WARNING("Negative connection probability in "
+                               "dot-product graph");
+            } else if (prob > 1 && ! warned_big) {
+                warned_big = 1;
+                IGRAPH_WARNING("Greater than 1 connection probability in "
+                               "dot-product graph");
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, j));
+            } else if (RNG_UNIF01() < prob) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, j));
+            }
+        }
+    }
+
+    RNG_END();
+
+    igraph_create(graph, &edges, ncol, directed);
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_sample_sphere_surface
+ * Sample points uniformly from the surface of a sphere
+ *
+ * The center of the sphere is at the origin.
+ *
+ * \param dim The dimension of the random vectors.
+ * \param n The number of vectors to sample.
+ * \param radius Radius of the sphere, it must be positive.
+ * \param positive Whether to restrict sampling to the positive
+ *    orthant.
+ * \param res Pointer to an initialized matrix, the result is
+ *    stored here, each column will be a sampled vector. The matrix is
+ *    resized, as needed.
+ * \return Error code.
+ *
+ * Time complexity: O(n*dim*g), where g is the time complexity of
+ * generating a standard normal random number.
+ *
+ * \sa \ref igraph_sample_sphere_volume(), \ref
+ * igraph_sample_dirichlet() for other similar samplers.
+ */
+
+int igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n,
+                                 igraph_real_t radius,
+                                 igraph_bool_t positive,
+                                 igraph_matrix_t *res) {
+    igraph_integer_t i, j;
+
+    if (dim < 2) {
+        IGRAPH_ERROR("Sphere must be at least two dimensional to sample from "
+                     "surface", IGRAPH_EINVAL);
+    }
+    if (n < 0) {
+        IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL);
+    }
+    if (radius <= 0) {
+        IGRAPH_ERROR("Sphere radius must be positive", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, dim, n));
+
+    RNG_BEGIN();
+
+    for (i = 0; i < n; i++) {
+        igraph_real_t *col = &MATRIX(*res, 0, i);
+        igraph_real_t sum = 0.0;
+        for (j = 0; j < dim; j++) {
+            col[j] = RNG_NORMAL(0, 1);
+            sum += col[j] * col[j];
+        }
+        sum = sqrt(sum);
+        for (j = 0; j < dim; j++) {
+            col[j] = radius * col[j] / sum;
+        }
+        if (positive) {
+            for (j = 0; j < dim; j++) {
+                col[j] = fabs(col[j]);
+            }
+        }
+    }
+
+    RNG_END();
+
+    return 0;
+}
+
+/**
+ * \function igraph_sample_sphere_volume
+ * Sample points uniformly from the volume of a sphere
+ *
+ * The center of the sphere is at the origin.
+ *
+ * \param dim The dimension of the random vectors.
+ * \param n The number of vectors to sample.
+ * \param radius Radius of the sphere, it must be positive.
+ * \param positive Whether to restrict sampling to the positive
+ *    orthant.
+ * \param res Pointer to an initialized matrix, the result is
+ *    stored here, each column will be a sampled vector. The matrix is
+ *    resized, as needed.
+ * \return Error code.
+ *
+ * Time complexity: O(n*dim*g), where g is the time complexity of
+ * generating a standard normal random number.
+ *
+ * \sa \ref igraph_sample_sphere_surface(), \ref
+ * igraph_sample_dirichlet() for other similar samplers.
+ */
+
+
+int igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n,
+                                igraph_real_t radius,
+                                igraph_bool_t positive,
+                                igraph_matrix_t *res) {
+
+    igraph_integer_t i, j;
+
+    /* Arguments are checked by the following call */
+
+    IGRAPH_CHECK(igraph_sample_sphere_surface(dim, n, radius, positive, res));
+
+    RNG_BEGIN();
+
+    for (i = 0; i < n; i++) {
+        igraph_real_t *col = &MATRIX(*res, 0, i);
+        igraph_real_t U = pow(RNG_UNIF01(), 1.0 / dim);
+        for (j = 0; j < dim; j++) {
+            col[j] *= U;
+        }
+    }
+
+    RNG_END();
+
+    return 0;
+}
+
+/**
+ * \function igraph_sample_dirichlet
+ * Sample points from a Dirichlet distribution
+ *
+ * \param n The number of vectors to sample.
+ * \param alpha The parameters of the Dirichlet distribution. They
+ *    must be positive. The length of this vector gives the dimension
+ *    of the generated samples.
+ * \param res Pointer to an initialized matrix, the result is stored
+ *    here, one sample in each column. It will be resized, as needed.
+ * \return Error code.
+ *
+ * Time complexity: O(n * dim * g), where dim is the dimension of the
+ * sample vectors, set by the length of alpha, and g is the time
+ * complexity of sampling from a Gamma distribution.
+ *
+ * \sa \ref igraph_sample_sphere_surface() and
+ * \ref igraph_sample_sphere_volume() for other methods to sample
+ * latent vectors.
+ */
+
+int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha,
+                            igraph_matrix_t *res) {
+
+    igraph_integer_t len = igraph_vector_size(alpha);
+    igraph_integer_t i;
+    igraph_vector_t vec;
+
+    if (n < 0) {
+        IGRAPH_ERROR("Number of samples should be non-negative",
+                     IGRAPH_EINVAL);
+    }
+    if (len < 2) {
+        IGRAPH_ERROR("Dirichlet parameter vector too short, must "
+                     "have at least two entries", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_min(alpha) <= 0) {
+        IGRAPH_ERROR("Dirichlet concentration parameters must be positive",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, len, n));
+
+    RNG_BEGIN();
+
+    for (i = 0; i < n; i++) {
+        igraph_vector_view(&vec, &MATRIX(*res, 0, i), len);
+        igraph_rng_get_dirichlet(igraph_rng_default(), alpha, &vec);
+    }
+
+    RNG_END();
+
+    return 0;
+}
diff --git a/igraph/src/dpotf2.c b/igraph/src/dpotf2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dpotf2.c
@@ -0,0 +1,272 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b10 = -1.;
+static doublereal c_b12 = 1.;
+
+/* > \brief \b DPOTF2 computes the Cholesky factorization of a symmetric/Hermitian positive definite matrix (u
+nblocked algorithm).   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DPOTF2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dpotf2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dpotf2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dpotf2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DPOTF2( UPLO, N, A, LDA, INFO )   
+
+         CHARACTER          UPLO   
+         INTEGER            INFO, LDA, N   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DPOTF2 computes the Cholesky factorization of a real symmetric   
+   > positive definite matrix A.   
+   >   
+   > The factorization has the form   
+   >    A = U**T * U ,  if UPLO = 'U', or   
+   >    A = L  * L**T,  if UPLO = 'L',   
+   > where U is an upper triangular matrix and L is lower triangular.   
+   >   
+   > This is the unblocked version of the algorithm, calling Level 2 BLAS.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          Specifies whether the upper or lower triangular part of the   
+   >          symmetric matrix A is stored.   
+   >          = 'U':  Upper triangular   
+   >          = 'L':  Lower triangular   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the symmetric matrix A.  If UPLO = 'U', the leading   
+   >          n by n upper triangular part of A contains the upper   
+   >          triangular part of the matrix A, and the strictly lower   
+   >          triangular part of A is not referenced.  If UPLO = 'L', the   
+   >          leading n by n lower triangular part of A contains the lower   
+   >          triangular part of the matrix A, and the strictly upper   
+   >          triangular part of A is not referenced.   
+   >   
+   >          On exit, if INFO = 0, the factor U or L from the Cholesky   
+   >          factorization A = U**T *U  or A = L*L**T.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -k, the k-th argument had an illegal value   
+   >          > 0: if INFO = k, the leading minor of order k is not   
+   >               positive definite, and the factorization could not be   
+   >               completed.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doublePOcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdpotf2_(char *uplo, integer *n, doublereal *a, integer *
+	lda, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+    doublereal d__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer j;
+    doublereal ajj;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *);
+    logical upper;
+    extern logical igraphdisnan_(doublereal *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    *info = 0;
+    upper = igraphlsame_(uplo, "U");
+    if (! upper && ! igraphlsame_(uplo, "L")) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*n)) {
+	*info = -4;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DPOTF2", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+    if (upper) {
+
+/*        Compute the Cholesky factorization A = U**T *U. */
+
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+
+/*           Compute U(J,J) and test for non-positive-definiteness. */
+
+	    i__2 = j - 1;
+	    ajj = a[j + j * a_dim1] - igraphddot_(&i__2, &a[j * a_dim1 + 1], &c__1, 
+		    &a[j * a_dim1 + 1], &c__1);
+	    if (ajj <= 0. || igraphdisnan_(&ajj)) {
+		a[j + j * a_dim1] = ajj;
+		goto L30;
+	    }
+	    ajj = sqrt(ajj);
+	    a[j + j * a_dim1] = ajj;
+
+/*           Compute elements J+1:N of row J. */
+
+	    if (j < *n) {
+		i__2 = j - 1;
+		i__3 = *n - j;
+		igraphdgemv_("Transpose", &i__2, &i__3, &c_b10, &a[(j + 1) * a_dim1 
+			+ 1], lda, &a[j * a_dim1 + 1], &c__1, &c_b12, &a[j + (
+			j + 1) * a_dim1], lda);
+		i__2 = *n - j;
+		d__1 = 1. / ajj;
+		igraphdscal_(&i__2, &d__1, &a[j + (j + 1) * a_dim1], lda);
+	    }
+/* L10: */
+	}
+    } else {
+
+/*        Compute the Cholesky factorization A = L*L**T. */
+
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+
+/*           Compute L(J,J) and test for non-positive-definiteness. */
+
+	    i__2 = j - 1;
+	    ajj = a[j + j * a_dim1] - igraphddot_(&i__2, &a[j + a_dim1], lda, &a[j 
+		    + a_dim1], lda);
+	    if (ajj <= 0. || igraphdisnan_(&ajj)) {
+		a[j + j * a_dim1] = ajj;
+		goto L30;
+	    }
+	    ajj = sqrt(ajj);
+	    a[j + j * a_dim1] = ajj;
+
+/*           Compute elements J+1:N of column J. */
+
+	    if (j < *n) {
+		i__2 = *n - j;
+		i__3 = j - 1;
+		igraphdgemv_("No transpose", &i__2, &i__3, &c_b10, &a[j + 1 + 
+			a_dim1], lda, &a[j + a_dim1], lda, &c_b12, &a[j + 1 + 
+			j * a_dim1], &c__1);
+		i__2 = *n - j;
+		d__1 = 1. / ajj;
+		igraphdscal_(&i__2, &d__1, &a[j + 1 + j * a_dim1], &c__1);
+	    }
+/* L20: */
+	}
+    }
+    goto L40;
+
+L30:
+    *info = j;
+
+L40:
+    return 0;
+
+/*     End of DPOTF2 */
+
+} /* igraphdpotf2_ */
+
diff --git a/igraph/src/dpotrf.c b/igraph/src/dpotrf.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dpotrf.c
@@ -0,0 +1,293 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static doublereal c_b13 = -1.;
+static doublereal c_b14 = 1.;
+
+/* > \brief \b DPOTRF   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DPOTRF + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dpotrf.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dpotrf.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dpotrf.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DPOTRF( UPLO, N, A, LDA, INFO )   
+
+         CHARACTER          UPLO   
+         INTEGER            INFO, LDA, N   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DPOTRF computes the Cholesky factorization of a real symmetric   
+   > positive definite matrix A.   
+   >   
+   > The factorization has the form   
+   >    A = U**T * U,  if UPLO = 'U', or   
+   >    A = L  * L**T,  if UPLO = 'L',   
+   > where U is an upper triangular matrix and L is lower triangular.   
+   >   
+   > This is the block version of the algorithm, calling Level 3 BLAS.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          = 'U':  Upper triangle of A is stored;   
+   >          = 'L':  Lower triangle of A is stored.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the symmetric matrix A.  If UPLO = 'U', the leading   
+   >          N-by-N upper triangular part of A contains the upper   
+   >          triangular part of the matrix A, and the strictly lower   
+   >          triangular part of A is not referenced.  If UPLO = 'L', the   
+   >          leading N-by-N lower triangular part of A contains the lower   
+   >          triangular part of the matrix A, and the strictly upper   
+   >          triangular part of A is not referenced.   
+   >   
+   >          On exit, if INFO = 0, the factor U or L from the Cholesky   
+   >          factorization A = U**T*U or A = L*L**T.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  if INFO = i, the leading minor of order i is not   
+   >                positive definite, and the factorization could not be   
+   >                completed.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doublePOcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdpotrf_(char *uplo, integer *n, doublereal *a, integer *
+	lda, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3, i__4;
+
+    /* Local variables */
+    integer j, jb, nb;
+    extern /* Subroutine */ int igraphdgemm_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdtrsm_(char *, char *, char *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    logical upper;
+    extern /* Subroutine */ int igraphdsyrk_(char *, char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, doublereal *,
+	     integer *), igraphdpotf2_(char *, integer *, 
+	    doublereal *, integer *, integer *), igraphxerbla_(char *, 
+	    integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    *info = 0;
+    upper = igraphlsame_(uplo, "U");
+    if (! upper && ! igraphlsame_(uplo, "L")) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*n)) {
+	*info = -4;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DPOTRF", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+/*     Determine the block size for this environment. */
+
+    nb = igraphilaenv_(&c__1, "DPOTRF", uplo, n, &c_n1, &c_n1, &c_n1, (ftnlen)6, (
+	    ftnlen)1);
+    if (nb <= 1 || nb >= *n) {
+
+/*        Use unblocked code. */
+
+	igraphdpotf2_(uplo, n, &a[a_offset], lda, info);
+    } else {
+
+/*        Use blocked code. */
+
+	if (upper) {
+
+/*           Compute the Cholesky factorization A = U**T*U. */
+
+	    i__1 = *n;
+	    i__2 = nb;
+	    for (j = 1; i__2 < 0 ? j >= i__1 : j <= i__1; j += i__2) {
+
+/*              Update and factorize the current diagonal block and test   
+                for non-positive-definiteness.   
+
+   Computing MIN */
+		i__3 = nb, i__4 = *n - j + 1;
+		jb = min(i__3,i__4);
+		i__3 = j - 1;
+		igraphdsyrk_("Upper", "Transpose", &jb, &i__3, &c_b13, &a[j * 
+			a_dim1 + 1], lda, &c_b14, &a[j + j * a_dim1], lda);
+		igraphdpotf2_("Upper", &jb, &a[j + j * a_dim1], lda, info);
+		if (*info != 0) {
+		    goto L30;
+		}
+		if (j + jb <= *n) {
+
+/*                 Compute the current block row. */
+
+		    i__3 = *n - j - jb + 1;
+		    i__4 = j - 1;
+		    igraphdgemm_("Transpose", "No transpose", &jb, &i__3, &i__4, &
+			    c_b13, &a[j * a_dim1 + 1], lda, &a[(j + jb) * 
+			    a_dim1 + 1], lda, &c_b14, &a[j + (j + jb) * 
+			    a_dim1], lda);
+		    i__3 = *n - j - jb + 1;
+		    igraphdtrsm_("Left", "Upper", "Transpose", "Non-unit", &jb, &
+			    i__3, &c_b14, &a[j + j * a_dim1], lda, &a[j + (j 
+			    + jb) * a_dim1], lda);
+		}
+/* L10: */
+	    }
+
+	} else {
+
+/*           Compute the Cholesky factorization A = L*L**T. */
+
+	    i__2 = *n;
+	    i__1 = nb;
+	    for (j = 1; i__1 < 0 ? j >= i__2 : j <= i__2; j += i__1) {
+
+/*              Update and factorize the current diagonal block and test   
+                for non-positive-definiteness.   
+
+   Computing MIN */
+		i__3 = nb, i__4 = *n - j + 1;
+		jb = min(i__3,i__4);
+		i__3 = j - 1;
+		igraphdsyrk_("Lower", "No transpose", &jb, &i__3, &c_b13, &a[j + 
+			a_dim1], lda, &c_b14, &a[j + j * a_dim1], lda);
+		igraphdpotf2_("Lower", &jb, &a[j + j * a_dim1], lda, info);
+		if (*info != 0) {
+		    goto L30;
+		}
+		if (j + jb <= *n) {
+
+/*                 Compute the current block column. */
+
+		    i__3 = *n - j - jb + 1;
+		    i__4 = j - 1;
+		    igraphdgemm_("No transpose", "Transpose", &i__3, &jb, &i__4, &
+			    c_b13, &a[j + jb + a_dim1], lda, &a[j + a_dim1], 
+			    lda, &c_b14, &a[j + jb + j * a_dim1], lda);
+		    i__3 = *n - j - jb + 1;
+		    igraphdtrsm_("Right", "Lower", "Transpose", "Non-unit", &i__3, &
+			    jb, &c_b14, &a[j + j * a_dim1], lda, &a[j + jb + 
+			    j * a_dim1], lda);
+		}
+/* L20: */
+	    }
+	}
+    }
+    goto L40;
+
+L30:
+    *info = *info + j - 1;
+
+L40:
+    return 0;
+
+/*     End of DPOTRF */
+
+} /* igraphdpotrf_ */
+
diff --git a/igraph/src/dqueue.c b/igraph/src/dqueue.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dqueue.c
@@ -0,0 +1,55 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_dqueue.h"
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "dqueue.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "dqueue.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "dqueue.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "dqueue.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "dqueue.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
diff --git a/igraph/src/drl_graph.cpp b/igraph/src/drl_graph.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/drl_graph.cpp
@@ -0,0 +1,1309 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// This file contains the member definitions of the master class
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <vector>
+#include <cstdlib>
+#include <cmath>
+#include <cstring>
+
+using namespace std;
+
+#include "drl_graph.h"
+#include "igraph_random.h"
+#include "igraph_interface.h"
+#include "igraph_progress.h"
+#include "igraph_interrupt_internal.h"
+#ifdef MUSE_MPI
+    #include <mpi.h>
+#endif
+
+namespace drl {
+
+// constructor -- initializes the schedule variables (as in
+// graph constructor)
+
+// graph::graph ( int proc_id, int tot_procs, char *int_file )
+// {
+
+//        // MPI parameters
+//        myid = proc_id;
+//        num_procs = tot_procs;
+
+//        // initial annealing parameters
+//        STAGE = 0;
+//        iterations = 0;
+//        temperature = 2000;
+//        attraction = 10;
+//        damping_mult = 1.0;
+//        min_edges = 20;
+//        first_add = fine_first_add = true;
+//        fineDensity = false;
+
+//        // Brian's original Vx schedule
+//        liquid.iterations = 200;
+//        liquid.temperature = 2000;
+//        liquid.attraction = 2;
+//        liquid.damping_mult = 1.0;
+//        liquid.time_elapsed = 0;
+
+//        expansion.iterations = 200;
+//        expansion.temperature = 2000;
+//        expansion.attraction = 10;
+//        expansion.damping_mult = 1.0;
+//        expansion.time_elapsed = 0;
+
+//        cooldown.iterations = 200;
+//        cooldown.temperature = 2000;
+//        cooldown.attraction = 1;
+//        cooldown.damping_mult = .1;
+//        cooldown.time_elapsed = 0;
+
+//        crunch.iterations = 50;
+//        crunch.temperature = 250;
+//        crunch.attraction = 1;
+//        crunch. damping_mult = .25;
+//        crunch.time_elapsed = 0;
+
+//        simmer.iterations = 100;
+//        simmer.temperature = 250;
+//        simmer.attraction = .5;
+//        simmer.damping_mult = 0.0;
+//        simmer.time_elapsed = 0;
+
+//        // scan .int file for node info
+//        scan_int ( int_file );
+
+//        // populate node positions and ids
+//        positions.reserve ( num_nodes );
+//        map < int, int >::iterator cat_iter;
+//        for ( cat_iter = id_catalog.begin();
+//              cat_iter != id_catalog.end();
+//              cat_iter++ )
+//          positions.push_back ( Node( cat_iter->first ) );
+
+//        /*
+//        // output positions .ids for debugging
+//        for ( int id = 0; id < num_nodes; id++ )
+//          cout << positions[id].id << endl;
+//        */
+
+//        // read .int file for graph info
+//        read_int ( int_file );
+
+//        // initialize density server
+//        density_server.Init();
+
+// }
+
+graph::graph(const igraph_t *igraph,
+             const igraph_layout_drl_options_t *options,
+             const igraph_vector_t *weights) {
+    myid = 0;
+    num_procs = 1;
+
+    STAGE = 0;
+    iterations = options->init_iterations;
+    temperature = options->init_temperature;
+    attraction = options->init_attraction;
+    damping_mult = options->init_damping_mult;
+    min_edges = 20;
+    first_add = fine_first_add = true;
+    fineDensity = false;
+
+    // Brian's original Vx schedule
+    liquid.iterations = options->liquid_iterations;
+    liquid.temperature = options->liquid_temperature;
+    liquid.attraction = options->liquid_attraction;
+    liquid.damping_mult = options->liquid_damping_mult;
+    liquid.time_elapsed = 0;
+
+    expansion.iterations = options->expansion_iterations;
+    expansion.temperature = options->expansion_temperature;
+    expansion.attraction = options->expansion_attraction;
+    expansion.damping_mult = options->expansion_damping_mult;
+    expansion.time_elapsed = 0;
+
+    cooldown.iterations = options->cooldown_iterations;
+    cooldown.temperature = options->cooldown_temperature;
+    cooldown.attraction = options->cooldown_attraction;
+    cooldown.damping_mult = options->cooldown_damping_mult;
+    cooldown.time_elapsed = 0;
+
+    crunch.iterations = options->crunch_iterations;
+    crunch.temperature = options->crunch_temperature;
+    crunch.attraction = options->crunch_attraction;
+    crunch.damping_mult = options->crunch_damping_mult;
+    crunch.time_elapsed = 0;
+
+    simmer.iterations = options->simmer_iterations;
+    simmer.temperature = options->simmer_temperature;
+    simmer.attraction = options->simmer_attraction;
+    simmer.damping_mult = options->simmer_damping_mult;
+    simmer.time_elapsed = 0;
+
+    // scan .int file for node info
+    highest_sim = 1.0;
+    num_nodes = igraph_vcount(igraph);
+    long int no_of_edges = igraph_ecount(igraph);
+    for (long int i = 0; i < num_nodes; i++) {
+        id_catalog[i] = 1;
+    }
+    map< int, int>::iterator cat_iter;
+    for ( cat_iter = id_catalog.begin();
+          cat_iter != id_catalog.end(); cat_iter++) {
+        cat_iter->second = cat_iter->first;
+    }
+
+    // populate node positions and ids
+    positions.reserve ( num_nodes );
+    for ( cat_iter = id_catalog.begin();
+          cat_iter != id_catalog.end();
+          cat_iter++ ) {
+        positions.push_back ( Node( cat_iter->first ) );
+    }
+
+    // read .int file for graph info
+    long int node_1, node_2;
+    double weight;
+    for (long int i = 0; i < no_of_edges; i++) {
+        node_1 = IGRAPH_FROM(igraph, i);
+        node_2 = IGRAPH_TO(igraph, i);
+        weight = weights ? VECTOR(*weights)[i] : 1.0 ;
+        (neighbors[id_catalog[node_1]])[id_catalog[node_2]] = weight;
+        (neighbors[id_catalog[node_2]])[id_catalog[node_1]] = weight;
+    }
+
+    // initialize density server
+    density_server.Init();
+
+}
+
+// The following subroutine scans the .int file for the following
+// information: number nodes, node ids, and highest similarity.  The
+// corresponding graph globals are populated: num_nodes, id_catalog,
+// and highest_sim.
+
+// void graph::scan_int ( char *filename )
+// {
+
+//   cout << "Proc. " << myid << " scanning .int file ..." << endl;
+
+//   // Open (sim) File
+//   ifstream fp ( filename );
+//   if ( !fp )
+//   {
+//  cout << "Error: could not open " << filename << ".  Program terminated." << endl;
+//  #ifdef MUSE_MPI
+//    MPI_Abort ( MPI_COMM_WORLD, 1 );
+//  #else
+//    exit (1);
+//     #endif
+//   }
+
+//   // Read file, parse, and add into data structure
+//   int id1, id2;
+//   float edge_weight;
+//   highest_sim = -1.0;
+//   while ( !fp.eof () )
+//  {
+//    fp >> id1 >> id2 >> edge_weight;
+
+//    // ignore negative weights!
+//    if ( edge_weight <= 0 )
+//    {
+//       cout << "Error: found negative edge weight in " << filename << ".  Program stopped." << endl;
+//       #ifdef MUSE_MPI
+//         MPI_Abort ( MPI_COMM_WORLD, 1 );
+//       #else
+//         exit (1);
+//          #endif
+//     }
+
+//     if ( highest_sim < edge_weight )
+//        highest_sim = edge_weight;
+
+//     id_catalog[id1] = 1;
+//     id_catalog[id2] = 1;
+//  }
+
+//   fp.close();
+
+//   if ( id_catalog.size() == 0 )
+//   {
+//     cout << "Error: Proc. " << myid << ": " << filename << " is empty.  Program terminated." << endl;
+//  #ifdef MUSE_MPI
+//    MPI_Abort ( MPI_COMM_WORLD, 1 );
+//  #else
+//    exit (1);
+//  #endif
+//   }
+
+//   // label nodes with sequential integers starting at 0
+//   map< int, int>::iterator cat_iter;
+//   int id_label;
+//   for ( cat_iter = id_catalog.begin(), id_label = 0;
+//      cat_iter != id_catalog.end(); cat_iter++, id_label++ )
+//     cat_iter->second = id_label;
+
+//   /*
+//   // output id_catalog for debugging:
+//   for ( cat_iter = id_catalog.begin();
+//      cat_iter != id_catalog.end();
+//      cat_iter++ )
+//  cout << cat_iter->first << "\t" << cat_iter->second << endl;
+//   */
+
+//   num_nodes = id_catalog.size();
+// }
+
+// read in .parms file, if present
+
+/*
+void graph::read_parms ( char *parms_file )
+{
+
+          // read from .parms file
+          ifstream parms_in ( parms_file );
+          if ( !parms_in )
+          {
+            cout << "Error: could not open .parms file!  Program stopped." << endl;
+            #ifdef MUSE_MPI
+              MPI_Abort ( MPI_COMM_WORLD, 1 );
+            #else
+              exit (1);
+            #endif
+          }
+
+          cout << "Processor " << myid << " reading .parms file." << endl;
+
+          // read in stage parameters
+          string parm_label;    // this is ignored in the .parms file
+
+          // initial parameters
+          parms_in >> parm_label >> iterations;
+          parms_in >> parm_label >> temperature;
+          parms_in >> parm_label >> attraction;
+          parms_in >> parm_label >> damping_mult;
+
+          // liquid stage
+          parms_in >> parm_label >> liquid.iterations;
+          parms_in >> parm_label >> liquid.temperature;
+          parms_in >> parm_label >> liquid.attraction;
+          parms_in >> parm_label >> liquid.damping_mult;
+
+          // expansion stage
+          parms_in >> parm_label >> expansion.iterations;
+          parms_in >> parm_label >> expansion.temperature;
+          parms_in >> parm_label >> expansion.attraction;
+          parms_in >> parm_label >> expansion.damping_mult;
+
+          // cooldown stage
+          parms_in >> parm_label >> cooldown.iterations;
+          parms_in >> parm_label >> cooldown.temperature;
+          parms_in >> parm_label >> cooldown.attraction;
+          parms_in >> parm_label >> cooldown.damping_mult;
+
+          // crunch stage
+          parms_in >> parm_label >> crunch.iterations;
+          parms_in >> parm_label >> crunch.temperature;
+          parms_in >> parm_label >> crunch.attraction;
+          parms_in >> parm_label >> crunch.damping_mult;
+
+          // simmer stage
+          parms_in >> parm_label >> simmer.iterations;
+          parms_in >> parm_label >> simmer.temperature;
+          parms_in >> parm_label >> simmer.attraction;
+          parms_in >> parm_label >> simmer.damping_mult;
+
+          parms_in.close();
+
+          // print out parameters for double checking
+          if ( myid == 0 )
+          {
+            cout << "Processor 0 reports the following inputs:" << endl;
+            cout << "inital.iterations = " << iterations << endl;
+            cout << "initial.temperature = " << temperature << endl;
+            cout << "initial.attraction = " << attraction << endl;
+            cout << "initial.damping_mult = " << damping_mult << endl;
+            cout << " ..." << endl;
+            cout << "liquid.iterations = " << liquid.iterations << endl;
+            cout << "liquid.temperature = " << liquid.temperature << endl;
+            cout << "liquid.attraction = " << liquid.attraction << endl;
+            cout << "liquid.damping_mult = " << liquid.damping_mult << endl;
+            cout << " ..." << endl;
+            cout << "simmer.iterations = " << simmer.iterations << endl;
+            cout << "simmer.temperature = " << simmer.temperature << endl;
+            cout << "simmer.attraction = " << simmer.attraction << endl;
+            cout << "simmer.damping_mult = " << simmer.damping_mult << endl;
+          }
+
+}
+*/
+
+// init_parms -- this subroutine initializes the edge_cut variables
+// used in the original VxOrd starting with the edge_cut parameter.
+// In our version, edge_cut = 0 means no cutting, 1 = maximum cut.
+// We also set the random seed here.
+
+void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) {
+    IGRAPH_UNUSED(rand_seed);
+
+    // first we translate edge_cut the former tcl sliding scale
+    //CUT_END = cut_length_end = 39000.0 * (1.0 - edge_cut) + 1000.0;
+    CUT_END = cut_length_end = 40000.0 * (1.0 - edge_cut);
+
+    // cut_length_end cannot actually be 0
+    if ( cut_length_end <= 1.0 ) {
+        cut_length_end = 1.0;
+    }
+
+    float cut_length_start = 4.0 * cut_length_end;
+
+    // now we set the parameters used by ReCompute
+    cut_off_length = cut_length_start;
+    cut_rate = ( cut_length_start - cut_length_end ) / 400.0;
+
+    // finally set the number of iterations to leave .real coords fixed
+    int full_comp_iters;
+    full_comp_iters = liquid.iterations + expansion.iterations +
+                      cooldown.iterations + crunch.iterations + 3;
+
+    // adjust real parm to iterations (do not enter simmer halfway)
+    if ( real_parm < 0 ) {
+        real_iterations = (int)real_parm;
+    } else if ( real_parm == 1) {
+        real_iterations = full_comp_iters + simmer.iterations + 100;
+    } else {
+        real_iterations = (int)(real_parm * full_comp_iters);
+    }
+
+    tot_iterations = 0;
+    if ( real_iterations > 0 ) {
+        real_fixed = true;
+    } else {
+        real_fixed = false;
+    }
+
+    // calculate total expected iterations (for progress bar display)
+    tot_expected_iterations = liquid.iterations +
+                              expansion.iterations + cooldown.iterations +
+                              crunch.iterations + simmer.iterations;
+
+    /*
+    // output edge_cutting parms (for debugging)
+    cout << "Processor " << myid << ": "
+         << "cut_length_end = CUT_END = " << cut_length_end
+         << ", cut_length_start = " << cut_length_start
+         << ", cut_rate = " << cut_rate << endl;
+    */
+
+    // set random seed
+    // srand ( rand_seed ); // Don't need this in igraph
+
+}
+
+void graph::init_parms(const igraph_layout_drl_options_t *options) {
+    double rand_seed = 0.0;
+    double real_in = -1.0;
+    init_parms(rand_seed, options->edge_cut, real_in);
+}
+
+// The following subroutine reads a .real file to obtain initial
+// coordinates.  If a node is missing coordinates the coordinates
+// are computed
+
+// void graph::read_real ( char *real_file )
+// {
+//   cout << "Processor " << myid << " reading .real file ..." << endl;
+
+//   // read in .real file and mark as fixed
+//   ifstream real_in ( real_file );
+//   if ( !real_in )
+//   {
+//     cout << "Error: proc. " << myid << " could not open .real file." << endl;
+//     #ifdef MUSE_MPI
+//    MPI_Abort ( MPI_COMM_WORLD, 1 );
+//  #else
+//    exit (1);
+//  #endif
+//   }
+
+//   int real_id;
+//   float real_x, real_y;
+//   while ( !real_in.eof () )
+//   {
+//     real_id = -1;
+//     real_in >> real_id >> real_x >> real_y;
+//  if ( real_id >= 0 )
+//  {
+//    positions[id_catalog[real_id]].x = real_x;
+//    positions[id_catalog[real_id]].y = real_y;
+//    positions[id_catalog[real_id]].fixed = true;
+
+//    /*
+//    // output positions read (for debugging)
+//       cout << id_catalog[real_id] << " (" << positions[id_catalog[real_id]].x
+//         << ", " << positions[id_catalog[real_id]].y << ") "
+//         << positions[id_catalog[real_id]].fixed << endl;
+//    */
+
+//    // add node to density grid
+//    if ( real_iterations > 0 )
+//      density_server.Add ( positions[id_catalog[real_id]], fineDensity );
+//  }
+
+//   }
+
+//   real_in.close();
+// }
+
+int graph::read_real ( const igraph_matrix_t *real_mat,
+                       const igraph_vector_bool_t *fixed) {
+    long int n = igraph_matrix_nrow(real_mat);
+    for (long int i = 0; i < n; i++) {
+        positions[id_catalog[i]].x = MATRIX(*real_mat, i, 0);
+        positions[id_catalog[i]].y = MATRIX(*real_mat, i, 1);
+        positions[id_catalog[i]].fixed = fixed ? VECTOR(*fixed)[i] : false;
+
+        if ( real_iterations > 0 ) {
+            density_server.Add ( positions[id_catalog[i]], fineDensity );
+        }
+    }
+
+    return 0;
+}
+
+// The read_part_int subroutine reads the .int
+// file produced by convert_sim and gathers the nodes and their
+// neighbors in the range start_ind to end_ind.
+
+// void graph::read_int ( char *file_name )
+// {
+
+//  ifstream int_file;
+
+//  int_file.open ( file_name );
+//  if ( !int_file )
+//  {
+//      cout << "Error (worker process " << myid << "): could not open .int file." << endl;
+//      #ifdef MUSE_MPI
+//        MPI_Abort ( MPI_COMM_WORLD, 1 );
+//      #else
+//        exit (1);
+//      #endif
+//  }
+
+//  cout << "Processor " << myid << " reading .int file ..." << endl;
+
+//  int node_1, node_2;
+//  float weight;
+
+//     while ( !int_file.eof() )
+//  {
+//      weight = 0;     // all weights should be >= 0
+//      int_file >> node_1 >> node_2 >> weight;
+//      if ( weight )       // otherwise we are at end of file
+//                              // or it is a self-connected node
+//      {
+//              // normalization from original vxord
+//              weight /= highest_sim;
+//              weight = weight*fabs(weight);
+
+//              // initialize graph
+//              if ( ( node_1 % num_procs ) == myid )
+//                  (neighbors[id_catalog[node_1]])[id_catalog[node_2]] = weight;
+//              if ( ( node_2 % num_procs ) == myid )
+//                  (neighbors[id_catalog[node_2]])[id_catalog[node_1]] = weight;
+//      }
+//  }
+//  int_file.close();
+
+//  /*
+//  // the following code outputs the contents of the neighbors structure
+//  // (to be used for debugging)
+
+//  map<int, map<int,float> >::iterator i;
+//  map<int,float>::iterator j;
+
+//  for ( i = neighbors.begin(); i != neighbors.end(); i++ ) {
+//    cout << myid << ": " << i->first << " ";
+//      for (j = (i->second).begin(); j != (i->second).end(); j++ )
+//          cout << j->first << " (" << j->second << ") ";
+//      cout << endl;
+//      }
+//  */
+
+// }
+
+/*********************************************
+ * Function: ReCompute                       *
+ * Description: Compute the graph locations  *
+ * Modified from original code by B. Wylie   *
+ ********************************************/
+
+int graph::ReCompute( ) {
+
+    // carryover from original VxOrd
+    int MIN = 1;
+
+    /*
+    // output parameters (for debugging)
+    cout << "ReCompute is using the following parameters: "<< endl;
+    cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature
+         << ", attract = " << attraction << ", damping_mult = " << damping_mult
+       << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length
+       << ", fineDensity = " << fineDensity << endl;
+    */
+
+    /* igraph progress report */
+    float progress = (tot_iterations * 100.0 / tot_expected_iterations);
+
+    switch (STAGE) {
+    case 0:
+        if (iterations == 0) {
+            IGRAPH_PROGRESS("DrL layout (initialization stage)", progress, 0);
+        } else {
+            IGRAPH_PROGRESS("DrL layout (liquid stage)", progress, 0);
+        }
+        break;
+    case 1:
+        IGRAPH_PROGRESS("DrL layout (expansion stage)", progress, 0); break;
+    case 2:
+        IGRAPH_PROGRESS("DrL layout (cooldown and cluster phase)", progress, 0); break;
+    case 3:
+        IGRAPH_PROGRESS("DrL layout (crunch phase)", progress, 0); break;
+    case 5:
+        IGRAPH_PROGRESS("DrL layout (simmer phase)", progress, 0); break;
+    case 6:
+        IGRAPH_PROGRESS("DrL layout (final phase)", 100.0, 0); break;
+    default:
+        IGRAPH_PROGRESS("DrL layout (unknown phase)", 0.0, 0); break;
+    }
+
+    /* Compute Energies for individual nodes */
+    update_nodes ();
+
+    // check to see if we need to free fixed nodes
+    tot_iterations++;
+    if ( tot_iterations >= real_iterations ) {
+        real_fixed = false;
+    }
+
+
+    // ****************************************
+    // AUTOMATIC CONTROL SECTION
+    // ****************************************
+
+    // STAGE 0: LIQUID
+    if (STAGE == 0) {
+
+        if ( iterations == 0 ) {
+            start_time = time( NULL );
+//          if ( myid == 0 )
+//              cout << "Entering liquid stage ...";
+        }
+
+        if (iterations < liquid.iterations) {
+            temperature = liquid.temperature;
+            attraction = liquid.attraction;
+            damping_mult = liquid.damping_mult;
+            iterations++;
+//          if ( myid == 0 )
+//              cout << "." << flush;
+
+        } else {
+
+            stop_time = time( NULL );
+            liquid.time_elapsed = liquid.time_elapsed + (stop_time - start_time);
+            temperature = expansion.temperature;
+            attraction = expansion.attraction;
+            damping_mult = expansion.damping_mult;
+            iterations = 0;
+
+            // go to next stage
+            STAGE = 1;
+            start_time = time( NULL );
+
+//          if ( myid == 0 )
+//              cout << "Entering expansion stage ...";
+        }
+    }
+
+    // STAGE 1: EXPANSION
+    if (STAGE == 1) {
+
+        if (iterations < expansion.iterations) {
+
+            // Play with vars
+            if (attraction > 1) {
+                attraction -= .05;
+            }
+            if (min_edges > 12) {
+                min_edges -= .05;
+            }
+            cut_off_length -= cut_rate;
+            if (damping_mult > .1) {
+                damping_mult -= .005;
+            }
+            iterations++;
+//          if ( myid == 0 ) cout << "." << flush;
+
+        } else {
+
+            stop_time = time( NULL );
+            expansion.time_elapsed = expansion.time_elapsed + (stop_time - start_time);
+            min_edges = 12;
+            damping_mult = cooldown.damping_mult;
+
+            STAGE = 2;
+            attraction = cooldown.attraction;
+            temperature = cooldown.temperature;
+            iterations = 0;
+            start_time = time( NULL );
+
+//          if ( myid == 0 )
+//              cout << "Entering cool-down stage ...";
+        }
+    }
+
+    // STAGE 2: Cool down and cluster
+    else if (STAGE == 2) {
+
+        if (iterations < cooldown.iterations) {
+
+            // Reduce temperature
+            if (temperature > 50) {
+                temperature -= 10;
+            }
+
+            // Reduce cut length
+            if (cut_off_length > cut_length_end) {
+                cut_off_length -= cut_rate * 2;
+            }
+            if (min_edges > MIN) {
+                min_edges -= .2;
+            }
+            //min_edges = 99;
+            iterations++;
+//          if ( myid == 0 )
+//              cout << "." << flush;
+
+        } else {
+
+            stop_time = time( NULL );
+            cooldown.time_elapsed = cooldown.time_elapsed + (stop_time - start_time);
+            cut_off_length = cut_length_end;
+            temperature = crunch.temperature;
+            damping_mult = crunch.damping_mult;
+            min_edges = MIN;
+            //min_edges = 99; // In other words: no more cutting
+
+            STAGE = 3;
+            iterations = 0;
+            attraction = crunch.attraction;
+            start_time = time( NULL );
+
+//          if ( myid == 0 )
+//              cout << "Entering crunch stage ...";
+        }
+    }
+
+    // STAGE 3: Crunch
+    else if (STAGE == 3) {
+
+        if (iterations < crunch.iterations) {
+            iterations++;
+//          if ( myid == 0 ) cout << "." << flush;
+        } else {
+
+            stop_time = time( NULL );
+            crunch.time_elapsed = crunch.time_elapsed + (stop_time - start_time);
+            iterations = 0;
+            temperature = simmer.temperature;
+            attraction = simmer.attraction;
+            damping_mult = simmer.damping_mult;
+            min_edges = 99;
+            fineDensity = true;
+
+            STAGE = 5;
+            start_time = time( NULL );
+
+//          if ( myid == 0 )
+//              cout << "Entering simmer stage ...";
+        }
+    }
+
+    // STAGE 5: Simmer
+    else if ( STAGE == 5 ) {
+
+        if (iterations < simmer.iterations) {
+            if (temperature > 50) {
+                temperature -= 2;
+            }
+            iterations++;
+//          if ( myid == 0 ) cout << "." << flush;
+        } else {
+            stop_time = time( NULL );
+            simmer.time_elapsed = simmer.time_elapsed + (stop_time - start_time);
+
+            STAGE = 6;
+
+//          if ( myid == 0 )
+//              cout << "Layout calculation completed in " <<
+//                ( liquid.time_elapsed + expansion.time_elapsed +
+//                  cooldown.time_elapsed + crunch.time_elapsed +
+//                  simmer.time_elapsed )
+//                   << " seconds (not including I/O)."
+//                   << endl;
+        }
+    }
+
+    // STAGE 6: All Done!
+    else if ( STAGE == 6) {
+
+        /*
+        // output parameters (for debugging)
+        cout << "ReCompute is using the following parameters: "<< endl;
+        cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature
+             << ", attract = " << attraction << ", damping_mult = " << damping_mult
+             << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length
+             << ", fineDensity = " << fineDensity << endl;
+        */
+
+        return 0;
+    }
+
+    // ****************************************
+    // END AUTOMATIC CONTROL SECTION
+    // ****************************************
+
+    // Still need more recomputation
+    return 1;
+
+}
+
+// update_nodes -- this function will complete the primary node update
+// loop in layout's recompute routine.  It follows exactly the same
+// sequence to ensure similarity of parallel layout to the standard layout
+
+void graph::update_nodes ( ) {
+
+    vector<int> node_indices;           // node list of nodes currently being updated
+    float old_positions[2 * MAX_PROCS]; // positions before update
+    float new_positions[2 * MAX_PROCS]; // positions after update
+
+    bool all_fixed;                     // check if all nodes are fixed
+
+    // initial node list consists of 0,1,...,num_procs
+    for ( int i = 0; i < num_procs; i++ ) {
+        node_indices.push_back( i );
+    }
+
+    // next we calculate the number of nodes there would be if the
+    // num_nodes by num_procs schedule grid were perfectly square
+    int square_num_nodes = (int)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs ));
+
+    for ( int i = myid; i < square_num_nodes; i += num_procs ) {
+
+        // get old positions
+        get_positions ( node_indices, old_positions );
+
+        // default new position is old position
+        get_positions ( node_indices, new_positions );
+
+        if ( i < num_nodes ) {
+
+            // advance random sequence according to myid
+            for ( int j = 0; j < 2 * myid; j++ ) {
+                RNG_UNIF01();
+            }
+            // rand();
+
+            // calculate node energy possibilities
+            if ( !(positions[i].fixed && real_fixed) ) {
+                update_node_pos ( i, old_positions, new_positions );
+            }
+
+            // advance random sequence for next iteration
+            for ( unsigned int j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) {
+                RNG_UNIF01();
+            }
+            // rand();
+
+        } else {
+            // advance random sequence according to use by
+            // the other processors
+            for ( unsigned int j = 0; j < 2 * (node_indices.size()); j++ ) {
+                RNG_UNIF01();
+            }
+            //rand();
+        }
+
+        // check if anything was actually updated (e.g. everything was fixed)
+        all_fixed = true;
+        for ( unsigned int j = 0; j < node_indices.size (); j++ )
+            if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) {
+                all_fixed = false;
+            }
+
+        // update positions across processors (if not all fixed)
+        if ( !all_fixed ) {
+#ifdef MUSE_MPI
+            MPI_Allgather ( &new_positions[2 * myid], 2, MPI_FLOAT,
+                            new_positions, 2, MPI_FLOAT, MPI_COMM_WORLD );
+#endif
+
+            // update positions (old to new)
+            update_density ( node_indices, old_positions, new_positions );
+        }
+
+        /*
+        if ( myid == 0 )
+          {
+            // output node list (for debugging)
+            for ( unsigned int j = 0; j < node_indices.size(); j++ )
+              cout << node_indices[j] << " ";
+            cout << endl;
+          }
+        */
+
+        // compute node list for next update
+        for ( unsigned int j = 0; j < node_indices.size(); j++ ) {
+            node_indices [j] += num_procs;
+        }
+
+        while ( !node_indices.empty() && node_indices.back() >= num_nodes ) {
+            node_indices.pop_back ( );
+        }
+
+    }
+
+    // update first_add and fine_first_add
+    first_add = false;
+    if ( fineDensity ) {
+        fine_first_add = false;
+    }
+
+}
+
+// The get_positions function takes the node_indices list
+// and returns the corresponding positions in an array.
+
+void graph::get_positions ( vector<int> &node_indices,
+                            float return_positions[2 * MAX_PROCS]  ) {
+
+    // fill positions
+    for (unsigned int i = 0; i < node_indices.size(); i++) {
+        return_positions[2 * i] = positions[ node_indices[i] ].x;
+        return_positions[2 * i + 1] = positions[ node_indices[i] ].y;
+    }
+
+}
+
+// update_node_pos -- this subroutine does the actual work of computing
+// the new position of a given node.  num_act_proc gives the number
+// of active processes at this level for use by the random number
+// generators.
+
+void graph::update_node_pos ( int node_ind,
+                              float old_positions[2 * MAX_PROCS],
+                              float new_positions[2 * MAX_PROCS] ) {
+
+    float energies[2];          // node energies for possible positions
+    float updated_pos[2][2];    // possible positions
+    float pos_x, pos_y;
+
+    // old VxOrd parameter
+    float jump_length = .010 * temperature;
+
+    // subtract old node
+    density_server.Subtract ( positions[node_ind], first_add, fine_first_add, fineDensity );
+
+    // compute node energy for old solution
+    energies[0] = Compute_Node_Energy ( node_ind );
+
+    // move node to centroid position
+    Solve_Analytic ( node_ind, pos_x, pos_y );
+    positions[node_ind].x = updated_pos[0][0] = pos_x;
+    positions[node_ind].y = updated_pos[0][1] = pos_y;
+
+    /*
+    // ouput random numbers (for debugging)
+    int rand_0, rand_1;
+    rand_0 = rand();
+    rand_1 = rand();
+    cout << myid << ": " << rand_0 << ", " << rand_1 << endl;
+    */
+
+    // Do random method (RAND_MAX is C++ maximum random number)
+    updated_pos[1][0] = updated_pos[0][0] + (.5 - RNG_UNIF01()) * jump_length;
+    updated_pos[1][1] = updated_pos[0][1] + (.5 - RNG_UNIF01()) * jump_length;
+
+    // compute node energy for random position
+    positions[node_ind].x = updated_pos[1][0];
+    positions[node_ind].y = updated_pos[1][1];
+    energies[1] = Compute_Node_Energy ( node_ind );
+
+    /*
+    // output update possiblities (debugging):
+    cout << node_ind << ": (" << updated_pos[0][0] << "," << updated_pos[0][1]
+         << "), " << energies[0] << "; (" << updated_pos[1][0] << ","
+         << updated_pos[1][1] << "), " << energies[1] << endl;
+    */
+
+    // add back old position
+    positions[node_ind].x = old_positions[2 * myid];
+    positions[node_ind].y = old_positions[2 * myid + 1];
+    if ( !fineDensity && !first_add ) {
+        density_server.Add ( positions[node_ind], fineDensity );
+    } else if ( !fine_first_add ) {
+        density_server.Add ( positions[node_ind], fineDensity );
+    }
+
+    // choose updated node position with lowest energy
+    if ( energies[0] < energies[1] ) {
+        new_positions[2 * myid] = updated_pos[0][0];
+        new_positions[2 * myid + 1] = updated_pos[0][1];
+        positions[node_ind].energy = energies[0];
+    } else {
+        new_positions[2 * myid] = updated_pos[1][0];
+        new_positions[2 * myid + 1] = updated_pos[1][1];
+        positions[node_ind].energy = energies[1];
+    }
+
+}
+
+// update_density takes a sequence of node_indices and their positions and
+// updates the positions by subtracting the old positions and adding the
+// new positions to the density grid.
+
+void graph::update_density ( vector<int> &node_indices,
+                             float old_positions[2 * MAX_PROCS],
+                             float new_positions[2 * MAX_PROCS] ) {
+
+    // go through each node and subtract old position from
+    // density grid before adding new position
+    for ( unsigned int i = 0; i < node_indices.size(); i++ ) {
+        positions[node_indices[i]].x = old_positions[2 * i];
+        positions[node_indices[i]].y = old_positions[2 * i + 1];
+        density_server.Subtract ( positions[node_indices[i]],
+                                  first_add, fine_first_add, fineDensity );
+
+        positions[node_indices[i]].x = new_positions[2 * i];
+        positions[node_indices[i]].y = new_positions[2 * i + 1];
+        density_server.Add ( positions[node_indices[i]], fineDensity );
+    }
+
+}
+
+/********************************************
+* Function: Compute_Node_Energy             *
+* Description: Compute the node energy      *
+* This code has been modified from the      *
+* original code by B. Wylie.                *
+*********************************************/
+
+float graph::Compute_Node_Energy( int node_ind ) {
+
+    /* Want to expand 4th power range of attraction */
+    float attraction_factor = attraction * attraction *
+                              attraction * attraction * 2e-2;
+
+    map <int, float>::iterator EI;
+    float x_dis, y_dis;
+    float energy_distance, weight;
+    float node_energy = 0;
+
+    // Add up all connection energies
+    for (EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) {
+
+        // Get edge weight
+        weight = EI->second;
+
+        // Compute x,y distance
+        x_dis = positions[ node_ind ].x - positions[ EI->first ].x;
+        y_dis = positions[ node_ind ].y - positions[ EI->first ].y;
+
+        // Energy Distance
+        energy_distance = x_dis * x_dis + y_dis * y_dis;
+        if (STAGE < 2) {
+            energy_distance *= energy_distance;
+        }
+
+        // In the liquid phase we want to discourage long link distances
+        if (STAGE == 0) {
+            energy_distance *= energy_distance;
+        }
+
+        node_energy += weight * attraction_factor * energy_distance;
+    }
+
+    // output effect of density (debugging)
+    //cout << "[before: " << node_energy;
+
+    // add density
+    node_energy += density_server.GetDensity ( positions[ node_ind ].x, positions[ node_ind ].y,
+                   fineDensity );
+
+    // after calling density server (debugging)
+    //cout << ", after: " << node_energy << "]" << endl;
+
+    // return computated energy
+    return node_energy;
+}
+
+
+/*********************************************
+* Function: Solve_Analytic                   *
+* Description: Compute the node position     *
+* This is a modified version of the function *
+* originally written by B. Wylie             *
+*********************************************/
+
+void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y ) {
+
+    map <int, float>::iterator EI;
+    float total_weight = 0;
+    float x_dis, y_dis, x_cen = 0, y_cen = 0;
+    float x = 0, y = 0, dis;
+    float damping, weight;
+
+    // Sum up all connections
+    for (EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) {
+        weight = EI->second;
+        total_weight += weight;
+        x +=  weight * positions[ EI->first ].x;
+        y +=  weight * positions[ EI->first ].y;
+    }
+
+    // Now set node position
+    if (total_weight > 0) {
+
+        // Compute centriod
+        x_cen = x / total_weight;
+        y_cen = y / total_weight;
+        damping = 1.0 - damping_mult;
+        pos_x = damping * positions[ node_ind ].x + (1.0 - damping) * x_cen;
+        pos_y = damping * positions[ node_ind ].y + (1.0 - damping) * y_cen;
+    } else {
+        pos_x = positions[ node_ind ].x;
+        pos_y = positions[ node_ind ].y;
+    }
+
+    // No cut edge flag (?)
+    if (min_edges == 99) {
+        return;
+    }
+
+    // Don't cut at end of scale
+    if ( CUT_END >= 39500 ) {
+        return;
+    }
+
+    float num_connections = sqrt((double)neighbors[node_ind].size());
+    float maxLength = 0;
+
+    map<int, float>::iterator maxIndex;
+
+    // Go through nodes edges... cutting if necessary
+    for (EI = maxIndex = neighbors[node_ind].begin();
+         EI != neighbors[node_ind].end(); ++EI) {
+
+        // Check for at least min edges
+        if (neighbors[node_ind].size() < min_edges) {
+            continue;
+        }
+
+        x_dis = x_cen - positions[ EI->first ].x;
+        y_dis = y_cen - positions[ EI->first ].y;
+        dis = x_dis * x_dis + y_dis * y_dis;
+        dis *= num_connections;
+
+        // Store maximum edge
+        if (dis > maxLength) {
+            maxLength = dis;
+            maxIndex = EI;
+        }
+    }
+
+    // If max length greater than cut_length then cut
+    if (maxLength > cut_off_length) {
+        neighbors[ node_ind ].erase( maxIndex );
+    }
+
+}
+
+
+// write_coord writes out the coordinate file of the final solutions
+
+// void graph::write_coord( const char *file_name )
+// {
+
+//   ofstream coordOUT( file_name );
+//   if ( !coordOUT )
+//   {
+//  cout << "Could not open " << file_name << ".  Program terminated." << endl;
+//  #ifdef MUSE_MPI
+//    MPI_Abort ( MPI_COMM_WORLD, 1 );
+//  #else
+//    exit (1);
+//  #endif
+//   }
+
+//   cout << "Writing out solution to " << file_name << " ..." << endl;
+
+//   for (unsigned int i = 0; i < positions.size(); i++) {
+//     coordOUT << positions[i].id << "\t" << positions[i].x << "\t" << positions[i].y <<endl;
+//   }
+//   coordOUT.close();
+
+// }
+
+// write_sim -- outputs .edges file, takes as input .coord filename,
+// with .coord extension
+
+/*
+void graph::write_sim ( const char *file_name )
+{
+
+  string prefix_name ( file_name, strlen(file_name)-7 );
+  prefix_name = prefix_name + ".iedges";
+
+  // first we overwrite, then we append
+  ofstream simOUT;
+  if ( myid == 0 )
+    simOUT.open ( prefix_name.c_str() );
+  else
+    simOUT.open ( prefix_name.c_str(), ios::app );
+
+  if ( !simOUT )
+    {
+      cout << "Could not open " << prefix_name << ". Program terminated." << endl;
+      #ifdef MUSE_MPI
+        MPI_Abort ( MPI_COMM_WORLD, 1 );
+      #else
+        exit (1);
+      #endif
+    }
+
+
+  cout << "Proc. " << myid << " writing to " << prefix_name << " ..." << endl;
+
+
+  // the following code outputs the contents of the neighbors structure
+
+  map<int, map<int,float> >::iterator i;
+  map<int,float>::iterator j;
+
+  for ( i = neighbors.begin(); i != neighbors.end(); i++ )
+    for (j = (i->second).begin(); j != (i->second).end(); j++ )
+    simOUT << positions[i->first].id << "\t"
+           << positions[j->first].id << "\t"
+           << j->second << endl;
+
+  simOUT.close();
+
+}
+*/
+
+// get_tot_energy adds up the energy for each node to give an estimate of the
+// quality of the minimization.
+
+float graph::get_tot_energy ( ) {
+
+    float my_tot_energy, tot_energy;
+    my_tot_energy = 0;
+    for ( int i = myid; i < num_nodes; i += num_procs ) {
+        my_tot_energy += positions[i].energy;
+    }
+
+    //vector<Node>::iterator i;
+    //for ( i = positions.begin(); i != positions.end(); i++ )
+    //  tot_energy += i->energy;
+
+#ifdef MUSE_MPI
+    MPI_Reduce ( &my_tot_energy, &tot_energy, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD );
+#else
+    tot_energy = my_tot_energy;
+#endif
+
+    return tot_energy;
+
+}
+
+
+// The following subroutine draws the graph with possible intermediate
+// output (int_out is set to 0 if not proc. 0).  int_out is the parameter
+// passed by the user, and coord_file is the .coord file.
+
+// void graph::draw_graph ( int int_out, char *coord_file )
+// {
+
+//  // layout graph (with possible intermediate output)
+//  int count_iter = 0, count_file = 1;
+//  char int_coord_file [MAX_FILE_NAME + MAX_INT_LENGTH];
+//  while ( ReCompute( ) )
+//      if ( (int_out > 0) && (count_iter == int_out) )
+//      {
+//          // output intermediate solution
+//          sprintf ( int_coord_file, "%s.%d", coord_file, count_file );
+//          write_coord ( int_coord_file );
+
+//          count_iter = 0;
+//          count_file++;
+//      }
+//      else
+//          count_iter++;
+
+// }
+
+int graph::draw_graph(igraph_matrix_t *res) {
+    int count_iter = 0;
+    while (ReCompute()) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        count_iter++;
+    }
+    long int n = positions.size();
+    IGRAPH_CHECK(igraph_matrix_resize(res, n, 2));
+    for (long int i = 0; i < n; i++) {
+        MATRIX(*res, i, 0) = positions[i].x;
+        MATRIX(*res, i, 1) = positions[i].y;
+    }
+    return 0;
+}
+
+} // namespace drl
diff --git a/igraph/src/drl_graph_3d.cpp b/igraph/src/drl_graph_3d.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/drl_graph_3d.cpp
@@ -0,0 +1,877 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// This file contains the member definitions of the master class
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <vector>
+#include <cstdlib>
+#include <cmath>
+#include <cstring>
+
+using namespace std;
+
+#include "drl_graph_3d.h"
+#include "igraph_random.h"
+#include "igraph_interface.h"
+#include "igraph_progress.h"
+#include "igraph_interrupt_internal.h"
+#ifdef MUSE_MPI
+    #include <mpi.h>
+#endif
+
+namespace drl3d {
+
+graph::graph(const igraph_t *igraph,
+             const igraph_layout_drl_options_t *options,
+             const igraph_vector_t *weights) {
+    myid = 0;
+    num_procs = 1;
+
+    STAGE = 0;
+    iterations = options->init_iterations;
+    temperature = options->init_temperature;
+    attraction = options->init_attraction;
+    damping_mult = options->init_damping_mult;
+    min_edges = 20;
+    first_add = fine_first_add = true;
+    fineDensity = false;
+
+    // Brian's original Vx schedule
+    liquid.iterations = options->liquid_iterations;
+    liquid.temperature = options->liquid_temperature;
+    liquid.attraction = options->liquid_attraction;
+    liquid.damping_mult = options->liquid_damping_mult;
+    liquid.time_elapsed = 0;
+
+    expansion.iterations = options->expansion_iterations;
+    expansion.temperature = options->expansion_temperature;
+    expansion.attraction = options->expansion_attraction;
+    expansion.damping_mult = options->expansion_damping_mult;
+    expansion.time_elapsed = 0;
+
+    cooldown.iterations = options->cooldown_iterations;
+    cooldown.temperature = options->cooldown_temperature;
+    cooldown.attraction = options->cooldown_attraction;
+    cooldown.damping_mult = options->cooldown_damping_mult;
+    cooldown.time_elapsed = 0;
+
+    crunch.iterations = options->crunch_iterations;
+    crunch.temperature = options->crunch_temperature;
+    crunch.attraction = options->crunch_attraction;
+    crunch.damping_mult = options->crunch_damping_mult;
+    crunch.time_elapsed = 0;
+
+    simmer.iterations = options->simmer_iterations;
+    simmer.temperature = options->simmer_temperature;
+    simmer.attraction = options->simmer_attraction;
+    simmer.damping_mult = options->simmer_damping_mult;
+    simmer.time_elapsed = 0;
+
+    // scan .int file for node info
+    highest_sim = 1.0;
+    num_nodes = igraph_vcount(igraph);
+    long int no_of_edges = igraph_ecount(igraph);
+    for (long int i = 0; i < num_nodes; i++) {
+        id_catalog[i] = 1;
+    }
+    map< int, int>::iterator cat_iter;
+    for ( cat_iter = id_catalog.begin();
+          cat_iter != id_catalog.end(); cat_iter++) {
+        cat_iter->second = cat_iter->first;
+    }
+
+    // populate node positions and ids
+    positions.reserve ( num_nodes );
+    for ( cat_iter = id_catalog.begin();
+          cat_iter != id_catalog.end();
+          cat_iter++ ) {
+        positions.push_back ( Node( cat_iter->first ) );
+    }
+
+    // read .int file for graph info
+    long int node_1, node_2;
+    double weight;
+    for (long int i = 0; i < no_of_edges; i++) {
+        node_1 = IGRAPH_FROM(igraph, i);
+        node_2 = IGRAPH_TO(igraph, i);
+        weight = weights ? VECTOR(*weights)[i] : 1.0 ;
+        (neighbors[id_catalog[node_1]])[id_catalog[node_2]] = weight;
+        (neighbors[id_catalog[node_2]])[id_catalog[node_1]] = weight;
+    }
+
+    // initialize density server
+    density_server.Init();
+
+}
+
+// init_parms -- this subroutine initializes the edge_cut variables
+// used in the original VxOrd starting with the edge_cut parameter.
+// In our version, edge_cut = 0 means no cutting, 1 = maximum cut.
+// We also set the random seed here.
+
+void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) {
+
+    IGRAPH_UNUSED(rand_seed);
+    // first we translate edge_cut the former tcl sliding scale
+    //CUT_END = cut_length_end = 39000.0 * (1.0 - edge_cut) + 1000.0;
+    CUT_END = cut_length_end = 40000.0 * (1.0 - edge_cut);
+
+    // cut_length_end cannot actually be 0
+    if ( cut_length_end <= 1.0 ) {
+        cut_length_end = 1.0;
+    }
+
+    float cut_length_start = 4.0 * cut_length_end;
+
+    // now we set the parameters used by ReCompute
+    cut_off_length = cut_length_start;
+    cut_rate = ( cut_length_start - cut_length_end ) / 400.0;
+
+    // finally set the number of iterations to leave .real coords fixed
+    int full_comp_iters;
+    full_comp_iters = liquid.iterations + expansion.iterations +
+                      cooldown.iterations + crunch.iterations + 3;
+
+    // adjust real parm to iterations (do not enter simmer halfway)
+    if ( real_parm < 0 ) {
+        real_iterations = (int)real_parm;
+    } else if ( real_parm == 1) {
+        real_iterations = full_comp_iters + simmer.iterations + 100;
+    } else {
+        real_iterations = (int)(real_parm * full_comp_iters);
+    }
+
+    tot_iterations = 0;
+    if ( real_iterations > 0 ) {
+        real_fixed = true;
+    } else {
+        real_fixed = false;
+    }
+
+    // calculate total expected iterations (for progress bar display)
+    tot_expected_iterations = liquid.iterations +
+                              expansion.iterations + cooldown.iterations +
+                              crunch.iterations + simmer.iterations;
+
+    /*
+    // output edge_cutting parms (for debugging)
+    cout << "Processor " << myid << ": "
+         << "cut_length_end = CUT_END = " << cut_length_end
+         << ", cut_length_start = " << cut_length_start
+         << ", cut_rate = " << cut_rate << endl;
+    */
+
+    // set random seed
+    // srand ( rand_seed ); // Don't need this in igraph
+
+}
+
+void graph::init_parms(const igraph_layout_drl_options_t *options) {
+    double rand_seed = 0.0;
+    double real_in = -1.0;
+    init_parms(rand_seed, options->edge_cut, real_in);
+}
+
+int graph::read_real ( const igraph_matrix_t *real_mat,
+                       const igraph_vector_bool_t *fixed) {
+    long int n = igraph_matrix_nrow(real_mat);
+    for (long int i = 0; i < n; i++) {
+        positions[id_catalog[i]].x = MATRIX(*real_mat, i, 0);
+        positions[id_catalog[i]].y = MATRIX(*real_mat, i, 1);
+        positions[id_catalog[i]].z = MATRIX(*real_mat, i, 2);
+        positions[id_catalog[i]].fixed = fixed ? VECTOR(*fixed)[i] : false;
+
+        if ( real_iterations > 0 ) {
+            density_server.Add ( positions[id_catalog[i]], fineDensity );
+        }
+    }
+
+    return 0;
+}
+
+/*********************************************
+ * Function: ReCompute                       *
+ * Description: Compute the graph locations  *
+ * Modified from original code by B. Wylie   *
+ ********************************************/
+
+int graph::ReCompute( ) {
+
+    // carryover from original VxOrd
+    int MIN = 1;
+
+    /*
+    // output parameters (for debugging)
+    cout << "ReCompute is using the following parameters: "<< endl;
+    cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature
+         << ", attract = " << attraction << ", damping_mult = " << damping_mult
+       << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length
+       << ", fineDensity = " << fineDensity << endl;
+    */
+
+    /* igraph progress report */
+    float progress = (tot_iterations * 100.0 / tot_expected_iterations);
+
+    switch (STAGE) {
+    case 0:
+        if (iterations == 0) {
+            IGRAPH_PROGRESS("DrL layout (initialization stage)", progress, 0);
+        } else {
+            IGRAPH_PROGRESS("DrL layout (liquid stage)", progress, 0);
+        }
+        break;
+    case 1:
+        IGRAPH_PROGRESS("DrL layout (expansion stage)", progress, 0); break;
+    case 2:
+        IGRAPH_PROGRESS("DrL layout (cooldown and cluster phase)", progress, 0); break;
+    case 3:
+        IGRAPH_PROGRESS("DrL layout (crunch phase)", progress, 0); break;
+    case 5:
+        IGRAPH_PROGRESS("DrL layout (simmer phase)", progress, 0); break;
+    case 6:
+        IGRAPH_PROGRESS("DrL layout (final phase)", 100.0, 0); break;
+    default:
+        IGRAPH_PROGRESS("DrL layout (unknown phase)", 0.0, 0); break;
+    }
+
+    /* Compute Energies for individual nodes */
+    update_nodes ();
+
+    // check to see if we need to free fixed nodes
+    tot_iterations++;
+    if ( tot_iterations >= real_iterations ) {
+        real_fixed = false;
+    }
+
+
+    // ****************************************
+    // AUTOMATIC CONTROL SECTION
+    // ****************************************
+
+    // STAGE 0: LIQUID
+    if (STAGE == 0) {
+
+        if ( iterations == 0 ) {
+            start_time = time( NULL );
+//          if ( myid == 0 )
+//              cout << "Entering liquid stage ...";
+        }
+
+        if (iterations < liquid.iterations) {
+            temperature = liquid.temperature;
+            attraction = liquid.attraction;
+            damping_mult = liquid.damping_mult;
+            iterations++;
+//          if ( myid == 0 )
+//              cout << "." << flush;
+
+        } else {
+
+            stop_time = time( NULL );
+            liquid.time_elapsed = liquid.time_elapsed + (stop_time - start_time);
+            temperature = expansion.temperature;
+            attraction = expansion.attraction;
+            damping_mult = expansion.damping_mult;
+            iterations = 0;
+
+            // go to next stage
+            STAGE = 1;
+            start_time = time( NULL );
+
+//          if ( myid == 0 )
+//              cout << "Entering expansion stage ...";
+        }
+    }
+
+    // STAGE 1: EXPANSION
+    if (STAGE == 1) {
+
+        if (iterations < expansion.iterations) {
+
+            // Play with vars
+            if (attraction > 1) {
+                attraction -= .05;
+            }
+            if (min_edges > 12) {
+                min_edges -= .05;
+            }
+            cut_off_length -= cut_rate;
+            if (damping_mult > .1) {
+                damping_mult -= .005;
+            }
+            iterations++;
+//          if ( myid == 0 ) cout << "." << flush;
+
+        } else {
+
+            stop_time = time( NULL );
+            expansion.time_elapsed = expansion.time_elapsed + (stop_time - start_time);
+            min_edges = 12;
+            damping_mult = cooldown.damping_mult;
+
+            STAGE = 2;
+            attraction = cooldown.attraction;
+            temperature = cooldown.temperature;
+            iterations = 0;
+            start_time = time( NULL );
+
+//          if ( myid == 0 )
+//              cout << "Entering cool-down stage ...";
+        }
+    }
+
+    // STAGE 2: Cool down and cluster
+    else if (STAGE == 2) {
+
+        if (iterations < cooldown.iterations) {
+
+            // Reduce temperature
+            if (temperature > 50) {
+                temperature -= 10;
+            }
+
+            // Reduce cut length
+            if (cut_off_length > cut_length_end) {
+                cut_off_length -= cut_rate * 2;
+            }
+            if (min_edges > MIN) {
+                min_edges -= .2;
+            }
+            //min_edges = 99;
+            iterations++;
+//          if ( myid == 0 )
+//              cout << "." << flush;
+
+        } else {
+
+            stop_time = time( NULL );
+            cooldown.time_elapsed = cooldown.time_elapsed + (stop_time - start_time);
+            cut_off_length = cut_length_end;
+            temperature = crunch.temperature;
+            damping_mult = crunch.damping_mult;
+            min_edges = MIN;
+            //min_edges = 99; // In other words: no more cutting
+
+            STAGE = 3;
+            iterations = 0;
+            attraction = crunch.attraction;
+            start_time = time( NULL );
+
+//          if ( myid == 0 )
+//              cout << "Entering crunch stage ...";
+        }
+    }
+
+    // STAGE 3: Crunch
+    else if (STAGE == 3) {
+
+        if (iterations < crunch.iterations) {
+            iterations++;
+//          if ( myid == 0 ) cout << "." << flush;
+        } else {
+
+            stop_time = time( NULL );
+            crunch.time_elapsed = crunch.time_elapsed + (stop_time - start_time);
+            iterations = 0;
+            temperature = simmer.temperature;
+            attraction = simmer.attraction;
+            damping_mult = simmer.damping_mult;
+            min_edges = 99;
+            fineDensity = true;
+
+            STAGE = 5;
+            start_time = time( NULL );
+
+//          if ( myid == 0 )
+//              cout << "Entering simmer stage ...";
+        }
+    }
+
+    // STAGE 5: Simmer
+    else if ( STAGE == 5 ) {
+
+        if (iterations < simmer.iterations) {
+            if (temperature > 50) {
+                temperature -= 2;
+            }
+            iterations++;
+//          if ( myid == 0 ) cout << "." << flush;
+        } else {
+            stop_time = time( NULL );
+            simmer.time_elapsed = simmer.time_elapsed + (stop_time - start_time);
+
+            STAGE = 6;
+
+//          if ( myid == 0 )
+//              cout << "Layout calculation completed in " <<
+//                ( liquid.time_elapsed + expansion.time_elapsed +
+//                  cooldown.time_elapsed + crunch.time_elapsed +
+//                  simmer.time_elapsed )
+//                   << " seconds (not including I/O)."
+//                   << endl;
+        }
+    }
+
+    // STAGE 6: All Done!
+    else if ( STAGE == 6) {
+
+        /*
+        // output parameters (for debugging)
+        cout << "ReCompute is using the following parameters: "<< endl;
+        cout << "STAGE: " << STAGE << ", iter: " << iterations << ", temp = " << temperature
+             << ", attract = " << attraction << ", damping_mult = " << damping_mult
+             << ", min_edges = " << min_edges << ", cut_off_length = " << cut_off_length
+             << ", fineDensity = " << fineDensity << endl;
+        */
+
+        return 0;
+    }
+
+    // ****************************************
+    // END AUTOMATIC CONTROL SECTION
+    // ****************************************
+
+    // Still need more recomputation
+    return 1;
+
+}
+
+// update_nodes -- this function will complete the primary node update
+// loop in layout's recompute routine.  It follows exactly the same
+// sequence to ensure similarity of parallel layout to the standard layout
+
+void graph::update_nodes ( ) {
+
+    vector<int> node_indices;           // node list of nodes currently being updated
+    float old_positions[2 * MAX_PROCS]; // positions before update
+    float new_positions[2 * MAX_PROCS]; // positions after update
+
+    bool all_fixed;                     // check if all nodes are fixed
+
+    // initial node list consists of 0,1,...,num_procs
+    for ( int i = 0; i < num_procs; i++ ) {
+        node_indices.push_back( i );
+    }
+
+    // next we calculate the number of nodes there would be if the
+    // num_nodes by num_procs schedule grid were perfectly square
+    int square_num_nodes = (int)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs ));
+
+    for ( int i = myid; i < square_num_nodes; i += num_procs ) {
+
+        // get old positions
+        get_positions ( node_indices, old_positions );
+
+        // default new position is old position
+        get_positions ( node_indices, new_positions );
+
+        if ( i < num_nodes ) {
+
+            // advance random sequence according to myid
+            for ( int j = 0; j < 2 * myid; j++ ) {
+                RNG_UNIF01();
+            }
+            // rand();
+
+            // calculate node energy possibilities
+            if ( !(positions[i].fixed && real_fixed) ) {
+                update_node_pos ( i, old_positions, new_positions );
+            }
+
+            // advance random sequence for next iteration
+            for ( unsigned int j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) {
+                RNG_UNIF01();
+            }
+            // rand();
+
+        } else {
+            // advance random sequence according to use by
+            // the other processors
+            for ( unsigned int j = 0; j < 2 * (node_indices.size()); j++ ) {
+                RNG_UNIF01();
+            }
+            //rand();
+        }
+
+        // check if anything was actually updated (e.g. everything was fixed)
+        all_fixed = true;
+        for ( unsigned int j = 0; j < node_indices.size (); j++ )
+            if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) {
+                all_fixed = false;
+            }
+
+        // update positions across processors (if not all fixed)
+        if ( !all_fixed ) {
+#ifdef MUSE_MPI
+            MPI_Allgather ( &new_positions[2 * myid], 2, MPI_FLOAT,
+                            new_positions, 2, MPI_FLOAT, MPI_COMM_WORLD );
+#endif
+
+            // update positions (old to new)
+            update_density ( node_indices, old_positions, new_positions );
+        }
+
+        /*
+        if ( myid == 0 )
+          {
+            // output node list (for debugging)
+            for ( unsigned int j = 0; j < node_indices.size(); j++ )
+              cout << node_indices[j] << " ";
+            cout << endl;
+          }
+        */
+
+        // compute node list for next update
+        for ( unsigned int j = 0; j < node_indices.size(); j++ ) {
+            node_indices [j] += num_procs;
+        }
+
+        while ( !node_indices.empty() && node_indices.back() >= num_nodes ) {
+            node_indices.pop_back ( );
+        }
+
+    }
+
+    // update first_add and fine_first_add
+    first_add = false;
+    if ( fineDensity ) {
+        fine_first_add = false;
+    }
+
+}
+
+// The get_positions function takes the node_indices list
+// and returns the corresponding positions in an array.
+
+void graph::get_positions ( vector<int> &node_indices,
+                            float return_positions[3 * MAX_PROCS]  ) {
+
+    // fill positions
+    for (unsigned int i = 0; i < node_indices.size(); i++) {
+        return_positions[3 * i] = positions[ node_indices[i] ].x;
+        return_positions[3 * i + 1] = positions[ node_indices[i] ].y;
+        return_positions[3 * i + 2] = positions[ node_indices[i] ].z;
+    }
+
+}
+
+// update_node_pos -- this subroutine does the actual work of computing
+// the new position of a given node.  num_act_proc gives the number
+// of active processes at this level for use by the random number
+// generators.
+
+void graph::update_node_pos ( int node_ind,
+                              float old_positions[3 * MAX_PROCS],
+                              float new_positions[3 * MAX_PROCS] ) {
+
+    float energies[2];          // node energies for possible positions
+    float updated_pos[2][3];    // possible positions
+    float pos_x, pos_y, pos_z;
+
+    // old VxOrd parameter
+    float jump_length = .010 * temperature;
+
+    // subtract old node
+    density_server.Subtract ( positions[node_ind], first_add, fine_first_add, fineDensity );
+
+    // compute node energy for old solution
+    energies[0] = Compute_Node_Energy ( node_ind );
+
+    // move node to centroid position
+    Solve_Analytic ( node_ind, pos_x, pos_y, pos_z );
+    positions[node_ind].x = updated_pos[0][0] = pos_x;
+    positions[node_ind].y = updated_pos[0][1] = pos_y;
+    positions[node_ind].z = updated_pos[0][2] = pos_z;
+
+    /*
+    // ouput random numbers (for debugging)
+    int rand_0, rand_1;
+    rand_0 = rand();
+    rand_1 = rand();
+    cout << myid << ": " << rand_0 << ", " << rand_1 << endl;
+    */
+
+    // Do random method (RAND_MAX is C++ maximum random number)
+    updated_pos[1][0] = updated_pos[0][0] + (.5 - RNG_UNIF01()) * jump_length;
+    updated_pos[1][1] = updated_pos[0][1] + (.5 - RNG_UNIF01()) * jump_length;
+    updated_pos[1][2] = updated_pos[0][2] + (.5 - RNG_UNIF01()) * jump_length;
+
+    // compute node energy for random position
+    positions[node_ind].x = updated_pos[1][0];
+    positions[node_ind].y = updated_pos[1][1];
+    positions[node_ind].z = updated_pos[1][2];
+    energies[1] = Compute_Node_Energy ( node_ind );
+
+    /*
+    // output update possiblities (debugging):
+    cout << node_ind << ": (" << updated_pos[0][0] << "," << updated_pos[0][1]
+         << "), " << energies[0] << "; (" << updated_pos[1][0] << ","
+         << updated_pos[1][1] << "), " << energies[1] << endl;
+    */
+
+    // add back old position
+    positions[node_ind].x = old_positions[3 * myid];
+    positions[node_ind].y = old_positions[3 * myid + 1];
+    positions[node_ind].z = old_positions[3 * myid + 2];
+    if ( !fineDensity && !first_add ) {
+        density_server.Add ( positions[node_ind], fineDensity );
+    } else if ( !fine_first_add ) {
+        density_server.Add ( positions[node_ind], fineDensity );
+    }
+
+    // choose updated node position with lowest energy
+    if ( energies[0] < energies[1] ) {
+        new_positions[3 * myid] = updated_pos[0][0];
+        new_positions[3 * myid + 1] = updated_pos[0][1];
+        new_positions[3 * myid + 2] = updated_pos[0][2];
+        positions[node_ind].energy = energies[0];
+    } else {
+        new_positions[3 * myid] = updated_pos[1][0];
+        new_positions[3 * myid + 1] = updated_pos[1][1];
+        new_positions[3 * myid + 2] = updated_pos[1][2];
+        positions[node_ind].energy = energies[1];
+    }
+
+}
+
+// update_density takes a sequence of node_indices and their positions and
+// updates the positions by subtracting the old positions and adding the
+// new positions to the density grid.
+
+void graph::update_density ( vector<int> &node_indices,
+                             float old_positions[3 * MAX_PROCS],
+                             float new_positions[3 * MAX_PROCS] ) {
+
+    // go through each node and subtract old position from
+    // density grid before adding new position
+    for ( unsigned int i = 0; i < node_indices.size(); i++ ) {
+        positions[node_indices[i]].x = old_positions[3 * i];
+        positions[node_indices[i]].y = old_positions[3 * i + 1];
+        positions[node_indices[i]].z = old_positions[3 * i + 2];
+        density_server.Subtract ( positions[node_indices[i]],
+                                  first_add, fine_first_add, fineDensity );
+
+        positions[node_indices[i]].x = new_positions[3 * i];
+        positions[node_indices[i]].y = new_positions[3 * i + 1];
+        positions[node_indices[i]].z = new_positions[3 * i + 2];
+        density_server.Add ( positions[node_indices[i]], fineDensity );
+    }
+
+}
+
+/********************************************
+* Function: Compute_Node_Energy             *
+* Description: Compute the node energy      *
+* This code has been modified from the      *
+* original code by B. Wylie.                *
+*********************************************/
+
+float graph::Compute_Node_Energy( int node_ind ) {
+
+    /* Want to expand 4th power range of attraction */
+    float attraction_factor = attraction * attraction *
+                              attraction * attraction * 2e-2;
+
+    map <int, float>::iterator EI;
+    float x_dis, y_dis, z_dis;
+    float energy_distance, weight;
+    float node_energy = 0;
+
+    // Add up all connection energies
+    for (EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) {
+
+        // Get edge weight
+        weight = EI->second;
+
+        // Compute x,y distance
+        x_dis = positions[ node_ind ].x - positions[ EI->first ].x;
+        y_dis = positions[ node_ind ].y - positions[ EI->first ].y;
+        z_dis = positions[ node_ind ].z - positions[ EI->first ].z;
+
+        // Energy Distance
+        energy_distance = x_dis * x_dis + y_dis * y_dis + z_dis * z_dis;
+        if (STAGE < 2) {
+            energy_distance *= energy_distance;
+        }
+
+        // In the liquid phase we want to discourage long link distances
+        if (STAGE == 0) {
+            energy_distance *= energy_distance;
+        }
+
+        node_energy += weight * attraction_factor * energy_distance;
+    }
+
+    // output effect of density (debugging)
+    //cout << "[before: " << node_energy;
+
+    // add density
+    node_energy += density_server.GetDensity ( positions[ node_ind ].x, positions[ node_ind ].y,
+                   positions[ node_ind ].z, fineDensity );
+
+    // after calling density server (debugging)
+    //cout << ", after: " << node_energy << "]" << endl;
+
+    // return computated energy
+    return node_energy;
+}
+
+
+/*********************************************
+* Function: Solve_Analytic                   *
+* Description: Compute the node position     *
+* This is a modified version of the function *
+* originally written by B. Wylie             *
+*********************************************/
+
+void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y,
+                            float &pos_z) {
+
+    map <int, float>::iterator EI;
+    float total_weight = 0;
+    float x_dis, y_dis, z_dis, x_cen = 0, y_cen = 0, z_cen = 0;
+    float x = 0, y = 0, z = 0, dis;
+    float damping, weight;
+
+    // Sum up all connections
+    for (EI = neighbors[node_ind].begin(); EI != neighbors[node_ind].end(); ++EI) {
+        weight = EI->second;
+        total_weight += weight;
+        x +=  weight * positions[ EI->first ].x;
+        y +=  weight * positions[ EI->first ].y;
+        z +=  weight * positions[ EI->first ].z;
+    }
+
+    // Now set node position
+    if (total_weight > 0) {
+
+        // Compute centriod
+        x_cen = x / total_weight;
+        y_cen = y / total_weight;
+        z_cen = z / total_weight;
+        damping = 1.0 - damping_mult;
+        pos_x = damping * positions[ node_ind ].x + (1.0 - damping) * x_cen;
+        pos_y = damping * positions[ node_ind ].y + (1.0 - damping) * y_cen;
+        pos_z = damping * positions[ node_ind ].z + (1.0 - damping) * z_cen;
+    }
+
+    // No cut edge flag (?)
+    if (min_edges == 99) {
+        return;
+    }
+
+    // Don't cut at end of scale
+    if ( CUT_END >= 39500 ) {
+        return;
+    }
+
+    float num_connections = (float)sqrt((float)neighbors[node_ind].size());
+    float maxLength = 0;
+
+    map<int, float>::iterator maxIndex;
+
+    // Go through nodes edges... cutting if necessary
+    for (EI = maxIndex = neighbors[node_ind].begin();
+         EI != neighbors[node_ind].end(); ++EI) {
+
+        // Check for at least min edges
+        if (neighbors[node_ind].size() < min_edges) {
+            continue;
+        }
+
+        x_dis = x_cen - positions[ EI->first ].x;
+        y_dis = y_cen - positions[ EI->first ].y;
+        z_dis = z_cen - positions[ EI->first ].z;
+        dis = x_dis * x_dis + y_dis * y_dis + z_dis * z_dis;
+        dis *= num_connections;
+
+        // Store maximum edge
+        if (dis > maxLength) {
+            maxLength = dis;
+            maxIndex = EI;
+        }
+    }
+
+    // If max length greater than cut_length then cut
+    if (maxLength > cut_off_length) {
+        neighbors[ node_ind ].erase( maxIndex );
+    }
+
+}
+
+
+// get_tot_energy adds up the energy for each node to give an estimate of the
+// quality of the minimization.
+
+float graph::get_tot_energy ( ) {
+
+    float my_tot_energy, tot_energy;
+    my_tot_energy = 0;
+    for ( int i = myid; i < num_nodes; i += num_procs ) {
+        my_tot_energy += positions[i].energy;
+    }
+
+    //vector<Node>::iterator i;
+    //for ( i = positions.begin(); i != positions.end(); i++ )
+    //  tot_energy += i->energy;
+
+#ifdef MUSE_MPI
+    MPI_Reduce ( &my_tot_energy, &tot_energy, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD );
+#else
+    tot_energy = my_tot_energy;
+#endif
+
+    return tot_energy;
+
+}
+
+
+int graph::draw_graph(igraph_matrix_t *res) {
+    int count_iter = 0;
+    while (ReCompute()) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        count_iter++;
+    }
+    long int n = positions.size();
+    IGRAPH_CHECK(igraph_matrix_resize(res, n, 3));
+    for (long int i = 0; i < n; i++) {
+        MATRIX(*res, i, 0) = positions[i].x;
+        MATRIX(*res, i, 1) = positions[i].y;
+        MATRIX(*res, i, 2) = positions[i].z;
+    }
+    return 0;
+}
+
+} // namespace drl3d
diff --git a/igraph/src/drl_layout.cpp b/igraph/src/drl_layout.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/drl_layout.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// Layout
+//
+// This program implements a parallel force directed graph drawing
+// algorithm.  The algorithm used is based upon a random decomposition
+// of the graph and simulated shared memory of node position and density.
+// In this version, the simulated shared memory is spread among all processors
+//
+// The structure of the inputs and outputs of this code will be displayed
+// if the program is called without parameters, or if an erroneous
+// parameter is passed to the program.
+//
+// S. Martin
+// 5/6/2005
+
+// C++ library routines
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <set>
+#include <string>
+#include <deque>
+#include <vector>
+
+using namespace std;
+
+// layout routines and constants
+#include "drl_layout.h"
+#include "drl_parse.h"
+#include "drl_graph.h"
+
+// MPI
+#ifdef MUSE_MPI
+    #include <mpi.h>
+#endif
+
+using namespace drl;
+#include "igraph_layout.h"
+#include "igraph_random.h"
+#include "igraph_interface.h"
+
+namespace drl {
+
+// int main(int argc, char **argv) {
+
+
+//   // initialize MPI
+//   int myid, num_procs;
+
+//   #ifdef MUSE_MPI
+//     MPI_Init ( &argc, &argv );
+//     MPI_Comm_size ( MPI_COMM_WORLD, &num_procs );
+//     MPI_Comm_rank ( MPI_COMM_WORLD, &myid );
+//   #else
+//     myid = 0;
+//  num_procs = 1;
+//   #endif
+
+//   // parameters that must be broadcast to all processors
+//   int rand_seed;
+//   float edge_cut;
+
+//   char int_file[MAX_FILE_NAME];
+//   char coord_file[MAX_FILE_NAME];
+//   char real_file[MAX_FILE_NAME];
+//   char parms_file[MAX_FILE_NAME];
+
+//   int int_out = 0;
+//   int edges_out = 0;
+//   int parms_in = 0;
+//   float real_in = -1.0;
+
+//   // user interaction is handled by processor 0
+//   if ( myid == 0 )
+//   {
+//     if ( num_procs > MAX_PROCS )
+//  {
+//      cout << "Error: Maximum number of processors is " << MAX_PROCS << "." << endl;
+//      cout << "Adjust compile time parameter." << endl;
+//      #ifdef MUSE_MPI
+//        MPI_Abort ( MPI_COMM_WORLD, 1 );
+//      #else
+//        exit (1);
+//      #endif
+//  }
+
+//  // get user input
+//     parse command_line ( argc, argv );
+//  rand_seed = command_line.rand_seed;
+//  edge_cut = command_line.edge_cut;
+//  int_out = command_line.int_out;
+//  edges_out = command_line.edges_out;
+//  parms_in = command_line.parms_in;
+//  real_in = command_line.real_in;
+//  strcpy ( coord_file, command_line.coord_file.c_str() );
+//  strcpy ( int_file, command_line.sim_file.c_str() );
+//  strcpy ( real_file, command_line.real_file.c_str() );
+//  strcpy ( parms_file, command_line.parms_file.c_str() );
+
+//   }
+
+//   // now we initialize all processors by reading .int file
+//   #ifdef MUSE_MPI
+//     MPI_Bcast ( &int_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD );
+//   #endif
+//   graph neighbors ( myid, num_procs, int_file );
+
+//   // check for user supplied parameters
+//   #ifdef MUSE_MPI
+//     MPI_Bcast ( &parms_in, 1, MPI_INT, 0, MPI_COMM_WORLD );
+//   #endif
+//   if ( parms_in )
+//   {
+//     #ifdef MUSE_MPI
+//    MPI_Bcast ( &parms_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD );
+//  #endif
+//  neighbors.read_parms ( parms_file );
+//   }
+
+//   // set random seed, edge cutting, and real iterations parameters
+//   #ifdef MUSE_MPI
+//     MPI_Bcast ( &rand_seed, 1, MPI_INT, 0, MPI_COMM_WORLD );
+//     MPI_Bcast ( &edge_cut, 1, MPI_FLOAT, 0, MPI_COMM_WORLD );
+//  MPI_Bcast ( &real_in, 1, MPI_INT, 0, MPI_COMM_WORLD );
+//   #endif
+//   neighbors.init_parms ( rand_seed, edge_cut, real_in );
+
+//   // check for .real file with existing coordinates
+//   if ( real_in >= 0 )
+//   {
+//     #ifdef MUSE_MPI
+//    MPI_Bcast ( &real_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD );
+//  #endif
+//  neighbors.read_real ( real_file );
+//   }
+
+//   neighbors.draw_graph ( int_out, coord_file );
+
+//   // do we have to write out the edges?
+//   #ifdef MUSE_MPI
+//     MPI_Bcast ( &edges_out, 1, MPI_INT, 0, MPI_COMM_WORLD );
+//   #endif
+//   if ( edges_out )
+//     {
+//    #ifdef MUSE_MPI
+//         MPI_Bcast ( &coord_file, MAX_FILE_NAME, MPI_CHAR, 0, MPI_COMM_WORLD );
+//    #endif
+//       for ( int i = 0; i < num_procs; i++ )
+//    {
+//      if ( myid == i )
+//        neighbors.write_sim ( coord_file );
+//      #ifdef MUSE_MPI
+//            MPI_Barrier ( MPI_COMM_WORLD );
+//      #endif
+//    }
+//     }
+
+//   // finally we output file and quit
+//   float tot_energy;
+//   tot_energy = neighbors.get_tot_energy ();
+//   if ( myid == 0 )
+//   {
+//  neighbors.write_coord ( coord_file );
+//  cout << "Total Energy: " << tot_energy << "." << endl
+//       << "Program terminated successfully." << endl;
+//   }
+
+//   // MPI finalize
+//   #ifdef MUSE_MPI
+//     MPI_Finalize ();
+//   #endif
+
+//   return 0;
+// }
+
+} // namespace drl
+
+/**
+ * \section about_drl
+ *
+ * <para>
+ * DrL is a sophisticated layout generator developed and implemented by
+ * Shawn Martin et al. As of October 2012 the original DrL homepage is
+ * unfortunately not available. You can read more about this algorithm
+ * in the following technical report: Martin, S., Brown, W.M.,
+ * Klavans, R., Boyack, K.W., DrL: Distributed Recursive (Graph)
+ * Layout. SAND Reports, 2008. 2936: p. 1-10.
+ * </para>
+ *
+ * <para>
+ * Only a subset of the complete DrL functionality is
+ * included in igraph, parallel runs and recursive, multi-level
+ * layouting is not supported.
+ * </para>
+ *
+ * <para>
+ * The parameters of the layout are stored in an \ref
+ * igraph_layout_drl_options_t structure, this can be initialized by
+ * calling the function \ref igraph_layout_drl_options_init().
+ * The fields of this structure can then be adjusted by hand if needed.
+ * The layout is calculated by an \ref igraph_layout_drl() call.
+ * </para>
+ */
+
+/**
+ * \function igraph_layout_drl_options_init
+ * Initialize parameters for the DrL layout generator
+ *
+ * This function can be used to initialize the struct holding the
+ * parameters for the DrL layout generator. There are a number of
+ * predefined templates available, it is a good idea to start from one
+ * of these by modifying some parameters.
+ * \param options The struct to initialize.
+ * \param templ The template to use. Currently the following templates
+ *     are supplied: \c IGRAPH_LAYOUT_DRL_DEFAULT, \c
+ *     IGRAPH_LAYOUT_DRL_COARSEN, \c IGRAPH_LAYOUT_DRL_COARSEST,
+ *     \c IGRAPH_LAYOUT_DRL_REFINE and \c IGRAPH_LAYOUT_DRL_FINAL.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options,
+                                   igraph_layout_drl_default_t templ) {
+
+    options->edge_cut = 32.0 / 40.0;
+
+    switch (templ) {
+    case IGRAPH_LAYOUT_DRL_DEFAULT:
+        options->init_iterations   = 0;
+        options->init_temperature  = 2000;
+        options->init_attraction   = 10;
+        options->init_damping_mult = 1.0;
+
+        options->liquid_iterations   = 200;
+        options->liquid_temperature  = 2000;
+        options->liquid_attraction   = 10;
+        options->liquid_damping_mult = 1.0;
+
+        options->expansion_iterations   = 200;
+        options->expansion_temperature  = 2000;
+        options->expansion_attraction   = 2;
+        options->expansion_damping_mult = 1.0;
+
+        options->cooldown_iterations   = 200;
+        options->cooldown_temperature  = 2000;
+        options->cooldown_attraction   = 1;
+        options->cooldown_damping_mult = .1;
+
+        options->crunch_iterations   = 50;
+        options->crunch_temperature  = 250;
+        options->crunch_attraction   = 1;
+        options->crunch_damping_mult = 0.25;
+
+        options->simmer_iterations   = 100;
+        options->simmer_temperature  = 250;
+        options->simmer_attraction   = .5;
+        options->simmer_damping_mult = 0;
+
+        break;
+    case IGRAPH_LAYOUT_DRL_COARSEN:
+        options->init_iterations   = 0;
+        options->init_temperature  = 2000;
+        options->init_attraction   = 10;
+        options->init_damping_mult = 1.0;
+
+        options->liquid_iterations   = 200;
+        options->liquid_temperature  = 2000;
+        options->liquid_attraction   = 2;
+        options->liquid_damping_mult = 1.0;
+
+        options->expansion_iterations   = 200;
+        options->expansion_temperature  = 2000;
+        options->expansion_attraction   = 10;
+        options->expansion_damping_mult = 1.0;
+
+        options->cooldown_iterations   = 200;
+        options->cooldown_temperature  = 2000;
+        options->cooldown_attraction   = 1;
+        options->cooldown_damping_mult = .1;
+
+        options->crunch_iterations   = 50;
+        options->crunch_temperature  = 250;
+        options->crunch_attraction   = 1;
+        options->crunch_damping_mult = 0.25;
+
+        options->simmer_iterations   = 100;
+        options->simmer_temperature  = 250;
+        options->simmer_attraction   = .5;
+        options->simmer_damping_mult = 0;
+
+        break;
+    case IGRAPH_LAYOUT_DRL_COARSEST:
+        options->init_iterations   = 0;
+        options->init_temperature  = 2000;
+        options->init_attraction   = 10;
+        options->init_damping_mult = 1.0;
+
+        options->liquid_iterations   = 200;
+        options->liquid_temperature  = 2000;
+        options->liquid_attraction   = 2;
+        options->liquid_damping_mult = 1.0;
+
+        options->expansion_iterations   = 200;
+        options->expansion_temperature  = 2000;
+        options->expansion_attraction   = 10;
+        options->expansion_damping_mult = 1.0;
+
+        options->cooldown_iterations   = 200;
+        options->cooldown_temperature  = 2000;
+        options->cooldown_attraction   = 1;
+        options->cooldown_damping_mult = .1;
+
+        options->crunch_iterations   = 200;
+        options->crunch_temperature  = 250;
+        options->crunch_attraction   = 1;
+        options->crunch_damping_mult = 0.25;
+
+        options->simmer_iterations   = 100;
+        options->simmer_temperature  = 250;
+        options->simmer_attraction   = .5;
+        options->simmer_damping_mult = 0;
+
+        break;
+    case IGRAPH_LAYOUT_DRL_REFINE:
+        options->init_iterations   = 0;
+        options->init_temperature  = 50;
+        options->init_attraction   = .5;
+        options->init_damping_mult = 0;
+
+        options->liquid_iterations   = 0;
+        options->liquid_temperature  = 2000;
+        options->liquid_attraction   = 2;
+        options->liquid_damping_mult = 1.0;
+
+        options->expansion_iterations   = 50;
+        options->expansion_temperature  = 500;
+        options->expansion_attraction   = .1;
+        options->expansion_damping_mult = .25;
+
+        options->cooldown_iterations   = 50;
+        options->cooldown_temperature  = 200;
+        options->cooldown_attraction   = 1;
+        options->cooldown_damping_mult = .1;
+
+        options->crunch_iterations   = 50;
+        options->crunch_temperature  = 250;
+        options->crunch_attraction   = 1;
+        options->crunch_damping_mult = 0.25;
+
+        options->simmer_iterations   = 0;
+        options->simmer_temperature  = 250;
+        options->simmer_attraction   = .5;
+        options->simmer_damping_mult = 0;
+
+        break;
+    case IGRAPH_LAYOUT_DRL_FINAL:
+        options->init_iterations   = 0;
+        options->init_temperature  = 50;
+        options->init_attraction   = .5;
+        options->init_damping_mult = 0;
+
+        options->liquid_iterations   = 0;
+        options->liquid_temperature  = 2000;
+        options->liquid_attraction   = 2;
+        options->liquid_damping_mult = 1.0;
+
+        options->expansion_iterations   = 50;
+        options->expansion_temperature  = 50;
+        options->expansion_attraction   = .1;
+        options->expansion_damping_mult = .25;
+
+        options->cooldown_iterations   = 50;
+        options->cooldown_temperature  = 200;
+        options->cooldown_attraction   = 1;
+        options->cooldown_damping_mult = .1;
+
+        options->crunch_iterations   = 50;
+        options->crunch_temperature  = 250;
+        options->crunch_attraction   = 1;
+        options->crunch_damping_mult = 0.25;
+
+        options->simmer_iterations   = 25;
+        options->simmer_temperature  = 250;
+        options->simmer_attraction   = .5;
+        options->simmer_damping_mult = 0;
+
+        break;
+    default:
+        IGRAPH_ERROR("Unknown DrL template", IGRAPH_EINVAL);
+        break;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_layout_drl
+ * The DrL layout generator
+ *
+ * This function implements the force-directed DrL layout generator.
+ * Please see more in the following technical report: Martin, S.,
+ * Brown, W.M., Klavans, R., Boyack, K.W., DrL: Distributed Recursive
+ * (Graph) Layout. SAND Reports, 2008. 2936: p. 1-10.
+ * \param graph The input graph.
+ * \param use_seed Logical scalar, if true, then the coordinates
+ *    supplied in the \p res argument are used as starting points.
+ * \param res Pointer to a matrix, the result layout is stored
+ *    here. It will be resized as needed.
+ * \param options The parameters to pass to the layout generator.
+ * \param weights Edge weights, pointer to a vector. If this is a null
+ *    pointer then every edge will have the same weight.
+ * \param fixed Pointer to a logical vector, or a null pointer. Originally,
+ *    this argument was used in the DrL algorithm to keep the nodes marked
+ *    with this argument as fixed; fixed nodes would then keep their
+ *    positions in the initial stages of the algorithm. However, due to how
+ *    the DrL code imported into igraph is organized, it seems that the
+ *    argument does not do anything and we are not sure whether this is a
+ *    bug or a feature in DrL. We are leaving the argument here in order not
+ *    to break the API, but note that at the present stage it has no effect.
+ * \return Error code.
+ *
+ * Time complexity: ???.
+ */
+
+int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res,
+                      igraph_bool_t use_seed,
+                      igraph_layout_drl_options_t *options,
+                      const igraph_vector_t *weights,
+                      const igraph_vector_bool_t *fixed) {
+
+    RNG_BEGIN();
+
+    drl::graph neighbors(graph, options, weights);
+    neighbors.init_parms(options);
+    if (use_seed) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 2));
+        neighbors.read_real(res, fixed);
+    }
+    neighbors.draw_graph(res);
+
+    RNG_END();
+
+    return 0;
+}
diff --git a/igraph/src/drl_layout_3d.cpp b/igraph/src/drl_layout_3d.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/drl_layout_3d.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// Layout
+//
+// This program implements a parallel force directed graph drawing
+// algorithm.  The algorithm used is based upon a random decomposition
+// of the graph and simulated shared memory of node position and density.
+// In this version, the simulated shared memory is spread among all processors
+//
+// The structure of the inputs and outputs of this code will be displayed
+// if the program is called without parameters, or if an erroneous
+// parameter is passed to the program.
+//
+// S. Martin
+// 5/6/2005
+
+// C++ library routines
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <set>
+#include <string>
+#include <deque>
+#include <vector>
+
+using namespace std;
+
+// layout routines and constants
+#include "drl_layout_3d.h"
+#include "drl_parse.h"
+#include "drl_graph_3d.h"
+
+// MPI
+#ifdef MUSE_MPI
+    #include <mpi.h>
+#endif
+
+using namespace drl3d;
+#include "igraph_layout.h"
+#include "igraph_random.h"
+#include "igraph_interface.h"
+
+/**
+ * \function igraph_layout_drl_3d
+ * The DrL layout generator, 3d version.
+ *
+ * This function implements the force-directed DrL layout generator.
+ * Please see more in the technical report: Martin, S., Brown, W.M.,
+ * Klavans, R., Boyack, K.W., DrL: Distributed Recursive (Graph)
+ * Layout. SAND Reports, 2008. 2936: p. 1-10.
+ *
+ * </para><para> This function uses a modified DrL generator that does
+ * the layout in three dimensions.
+ * \param graph The input graph.
+ * \param use_seed Logical scalar, if true, then the coordinates
+ *    supplied in the \p res argument are used as starting points.
+ * \param res Pointer to a matrix, the result layout is stored
+ *    here. It will be resized as needed.
+ * \param options The parameters to pass to the layout generator.
+ * \param weights Edge weights, pointer to a vector. If this is a null
+ *    pointer then every edge will have the same weight.
+ * \param fixed Pointer to a logical vector, or a null pointer. This
+ *    can be used to fix the position of some vertices. Vertices for
+ *    which it is true will not be moved, but stay at the coordinates
+ *    given in the \p res matrix. This argument is ignored if it is a
+ *    null pointer or if use_seed is false.
+ * \return Error code.
+ *
+ * Time complexity: ???.
+ *
+ * \sa \ref igraph_layout_drl() for the standard 2d version.
+ */
+
+int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res,
+                         igraph_bool_t use_seed,
+                         igraph_layout_drl_options_t *options,
+                         const igraph_vector_t *weights,
+                         const igraph_vector_bool_t *fixed) {
+
+    RNG_BEGIN();
+
+    drl3d::graph neighbors(graph, options, weights);
+    neighbors.init_parms(options);
+    if (use_seed) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 3));
+        neighbors.read_real(res, fixed);
+    }
+    neighbors.draw_graph(res);
+
+    RNG_END();
+
+    return 0;
+}
diff --git a/igraph/src/drl_parse.cpp b/igraph/src/drl_parse.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/drl_parse.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2007 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ * certain rights in this software.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Sandia National Laboratories nor the names of
+ * its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// This file contains the methods for the parse.h class
+
+#include <string>
+#include <iostream>
+#include <map>
+#include <cstdlib>
+#include <cstdio>
+
+using namespace std;
+
+#include "drl_layout.h"
+#include "drl_parse.h"
+
+namespace drl {
+
+// void parse::print_syntax( const char *error_string )
+// {
+//   cout << endl << "Error: " << error_string << endl;
+//   cout << endl << "Layout" << endl
+//     <<     "------" << endl
+//     << "S. Martin" << endl
+//     << "Version " << DRL_VERSION << endl << endl
+//     << "This program provides a parallel adaptation of a force directed" << endl
+//     << "graph layout algorithm for use with large datasets." << endl << endl
+//     << "Usage: layout [options] root_file" << endl << endl
+//     << "root_file -- the root name of the file being processed." << endl << endl
+//     << "INPUT" << endl
+//     << "-----" << endl
+//     << "root_file.int -- the input file containing the graph to draw using layout." << endl
+//     << "  The .int file must have the suffix \".int\" and each line of .int file" << endl
+//     << "  should have the form" << endl
+//     << "\tnode_id <tab> node_id <tab> weight" << endl
+//     << "  where node_id's are integers in sequence starting from 0, and" << endl
+//     << "  weight is a float > 0." << endl << endl
+//     << "OUTPUT" << endl
+//     << "------" << endl
+//     << "root_file.icoord -- the resulting output file, containing an ordination" << endl
+//     << "  of the graph.  The .icoord file will have the suffix \".icoord\" and" << endl
+//     << "  each line of the .icoord file will be of the form" << endl
+//     << "\tnode_id <tab> x-coord <tab> y-coord" << endl << endl
+//     << "Options:" << endl << endl
+//     << "\t-s {int>=0} random seed (default value is 0)" << endl
+//     << "\t-c {real[0,1]} edge cutting (default 32/40 = .8)" << endl
+//     << "\t   (old max was 39/40 = .975)" << endl
+//     << "\t-p input parameters from .parms file" << endl
+//     << "\t-r {real[0,1]} input coordinates from .real file" << endl
+//     << "\t   (hold fixed until fraction of optimization schedule reached)" << endl
+//     << "\t-i {int>=0} intermediate output interval (default 0: no output)" << endl
+//     << "\t-e output .iedges file (same prefix as .coord file)" << endl << endl;
+
+//   #ifdef MUSE_MPI
+//     MPI_Abort ( MPI_COMM_WORLD, 1 );
+//   #else
+//     exit (1);
+//   #endif
+// }
+
+// parse::parse ( int argc, char** argv)
+// {
+//   map<string,string> m;
+
+//   // make sure there is at least one argument
+//   if ( argc < 2)
+//  print_syntax ( "not enough arguments!" );
+
+//   // make sure coord_file ends in ".coord"
+//   parms_file = real_file = sim_file = coord_file = argv[argc-1];
+//   parms_file = parms_file + ".parms";
+//   real_file = real_file + ".real";
+//   sim_file = sim_file + ".int";
+//   coord_file = coord_file + ".icoord";
+
+//   char error_string[200];
+//   sprintf ( error_string, "%s %d %s", "root file name cannot be longer than", MAX_FILE_NAME-7,
+//                 "characters.");
+//   if ( coord_file.length() > MAX_FILE_NAME )
+//  print_syntax ( error_string );
+
+//   // echo sim_file and coord_file
+//   cout << "Using " << sim_file << " for .int file, and " << coord_file << " for .icoord file." << endl;
+
+//   // set defaults
+//   rand_seed = 0;
+//   //edge_cut = 32.0/39.0; // (old default)
+//   edge_cut = 32.0/40.0;
+//   int_out = 0;
+//   edges_out = 0;
+//   parms_in = 0;
+//   real_in = -1.0;
+
+//   // now check for optional arguments
+//   string arg;
+//   for( int i = 1; i<argc-1; i++ )
+//   {
+//  arg = argv[i];
+
+//  // check for random seed
+//     if ( arg == "-s" )
+//  {
+//      i++;
+//      if ( i >= (argc-1) )
+//          print_syntax ( "-s flag has no argument." );
+//      else
+//      {
+//          rand_seed = atoi ( argv[i] );
+//          if ( rand_seed < 0 )
+//              print_syntax ( "random seed must be >= 0." );
+//      }
+//  }
+//  // check for edge cutting
+//  else if ( arg == "-c" )
+//  {
+//      i++;
+//      if ( i >= (argc-1) )
+//          print_syntax ( "-c flag has no argument." );
+//      else
+//      {
+//          edge_cut = atof ( argv[i] );
+//          if ( (edge_cut < 0) || (edge_cut > 1) )
+//              print_syntax ( "edge cut must be between 0 and 1." );
+//      }
+//  }
+//  // check for intermediate output
+//  else if ( arg == "-i" )
+//  {
+//      i++;
+//      if ( i >= (argc-1) )
+//          print_syntax ( "-i flag has no argument." );
+//      else
+//      {
+//          int_out = atoi ( argv[i] );
+//          if ( int_out < 0 )
+//              print_syntax ( "intermediate output must be >= 0." );
+//      }
+//  }
+//  // check for .real input
+//  else if ( arg == "-r" )
+//  {
+//      i++;
+//      if ( i >= (argc-1) )
+//          print_syntax ( "-r flag has no argument." );
+//      else
+//      {
+//          real_in = atof ( argv[i] );
+//          if ( (real_in < 0) || (real_in > 1) )
+//              print_syntax ( "real iteration fraction must be from 0 to 1." );
+//      }
+//  }
+//  else if ( arg == "-e" )
+//      edges_out = 1;
+//  else if ( arg == "-p" )
+//      parms_in = 1;
+//  else
+//      print_syntax ( "unrecongized option!" );
+//   }
+
+//   if ( parms_in )
+//     cout << "Using " << parms_file << " for .parms file." << endl;
+
+//   if ( real_in >= 0 )
+//     cout << "Using " << real_file << " for .real file." << endl;
+
+//   // echo arguments input or default
+//   cout << "Using random seed = " << rand_seed << endl
+//        << "      edge_cutting = " << edge_cut << endl
+//        << "      intermediate output = " << int_out << endl
+//        << "      output .iedges file = " << edges_out << endl;
+//   if ( real_in >= 0 )
+//  cout << "      holding .real fixed until iterations = " << real_in << endl;
+
+// }
+
+} // namespace drl
diff --git a/igraph/src/drot.c b/igraph/src/drot.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/drot.c
@@ -0,0 +1,81 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdrot_(integer *n, doublereal *dx, integer *incx, 
+	doublereal *dy, integer *incy, doublereal *c__, doublereal *s)
+{
+    /* System generated locals */
+    integer i__1;
+
+    /* Local variables */
+    integer i__, ix, iy;
+    doublereal dtemp;
+
+
+/*  Purpose   
+    =======   
+
+       DROT applies a plane rotation.   
+
+    Further Details   
+    ===============   
+
+       jack dongarra, linpack, 3/11/78.   
+       modified 12/3/93, array(1) declarations changed to array(*)   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --dy;
+    --dx;
+
+    /* Function Body */
+    if (*n <= 0) {
+	return 0;
+    }
+    if (*incx == 1 && *incy == 1) {
+
+/*       code for both increments equal to 1 */
+
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    dtemp = *c__ * dx[i__] + *s * dy[i__];
+	    dy[i__] = *c__ * dy[i__] - *s * dx[i__];
+	    dx[i__] = dtemp;
+	}
+    } else {
+
+/*       code for unequal increments or equal increments not equal   
+           to 1 */
+
+	ix = 1;
+	iy = 1;
+	if (*incx < 0) {
+	    ix = (-(*n) + 1) * *incx + 1;
+	}
+	if (*incy < 0) {
+	    iy = (-(*n) + 1) * *incy + 1;
+	}
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    dtemp = *c__ * dx[ix] + *s * dy[iy];
+	    dy[iy] = *c__ * dy[iy] - *s * dx[ix];
+	    dx[ix] = dtemp;
+	    ix += *incx;
+	    iy += *incy;
+	}
+    }
+    return 0;
+} /* igraphdrot_ */
+
diff --git a/igraph/src/dsaitr.c b/igraph/src/dsaitr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsaitr.c
@@ -0,0 +1,950 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static logical c_false = FALSE_;
+static doublereal c_b24 = 1.;
+static doublereal c_b49 = 0.;
+static doublereal c_b57 = -1.;
+static integer c__2 = 2;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsaitr   
+
+   \Description:   
+    Reverse communication interface for applying NP additional steps to   
+    a K step symmetric Arnoldi factorization.   
+
+    Input:  OP*V_{k}  -  V_{k}*H = r_{k}*e_{k}^T   
+
+            with (V_{k}^T)*B*V_{k} = I, (V_{k}^T)*B*r_{k} = 0.   
+
+    Output: OP*V_{k+p}  -  V_{k+p}*H = r_{k+p}*e_{k+p}^T   
+
+            with (V_{k+p}^T)*B*V_{k+p} = I, (V_{k+p}^T)*B*r_{k+p} = 0.   
+
+    where OP and B are as in dsaupd.  The B-norm of r_{k+p} is also   
+    computed and returned.   
+
+   \Usage:   
+    call dsaitr   
+       ( IDO, BMAT, N, K, NP, MODE, RESID, RNORM, V, LDV, H, LDH,   
+         IPNTR, WORKD, INFO )   
+
+   \Arguments   
+    IDO     Integer.  (INPUT/OUTPUT)   
+            Reverse communication flag.   
+            -------------------------------------------------------------   
+            IDO =  0: first call to the reverse communication interface   
+            IDO = -1: compute  Y = OP * X  where   
+                      IPNTR(1) is the pointer into WORK for X,   
+                      IPNTR(2) is the pointer into WORK for Y.   
+                      This is for the restart phase to force the new   
+                      starting vector into the range of OP.   
+            IDO =  1: compute  Y = OP * X  where   
+                      IPNTR(1) is the pointer into WORK for X,   
+                      IPNTR(2) is the pointer into WORK for Y,   
+                      IPNTR(3) is the pointer into WORK for B * X.   
+            IDO =  2: compute  Y = B * X  where   
+                      IPNTR(1) is the pointer into WORK for X,   
+                      IPNTR(2) is the pointer into WORK for Y.   
+            IDO = 99: done   
+            -------------------------------------------------------------   
+            When the routine is used in the "shift-and-invert" mode, the   
+            vector B * Q is already available and does not need to be   
+            recomputed in forming OP * Q.   
+
+    BMAT    Character*1.  (INPUT)   
+            BMAT specifies the type of matrix B that defines the   
+            semi-inner product for the operator OP.  See dsaupd.   
+            B = 'I' -> standard eigenvalue problem A*x = lambda*x   
+            B = 'G' -> generalized eigenvalue problem A*x = lambda*M*x   
+
+    N       Integer.  (INPUT)   
+            Dimension of the eigenproblem.   
+
+    K       Integer.  (INPUT)   
+            Current order of H and the number of columns of V.   
+
+    NP      Integer.  (INPUT)   
+            Number of additional Arnoldi steps to take.   
+
+    MODE    Integer.  (INPUT)   
+            Signifies which form for "OP". If MODE=2 then   
+            a reduction in the number of B matrix vector multiplies   
+            is possible since the B-norm of OP*x is equivalent to   
+            the inv(B)-norm of A*x.   
+
+    RESID   Double precision array of length N.  (INPUT/OUTPUT)   
+            On INPUT:  RESID contains the residual vector r_{k}.   
+            On OUTPUT: RESID contains the residual vector r_{k+p}.   
+
+    RNORM   Double precision scalar.  (INPUT/OUTPUT)   
+            On INPUT the B-norm of r_{k}.   
+            On OUTPUT the B-norm of the updated residual r_{k+p}.   
+
+    V       Double precision N by K+NP array.  (INPUT/OUTPUT)   
+            On INPUT:  V contains the Arnoldi vectors in the first K   
+            columns.   
+            On OUTPUT: V contains the new NP Arnoldi vectors in the next   
+            NP columns.  The first K columns are unchanged.   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling   
+            program.   
+
+    H       Double precision (K+NP) by 2 array.  (INPUT/OUTPUT)   
+            H is used to store the generated symmetric tridiagonal matrix   
+            with the subdiagonal in the first column starting at H(2,1)   
+            and the main diagonal in the second column.   
+
+    LDH     Integer.  (INPUT)   
+            Leading dimension of H exactly as declared in the calling   
+            program.   
+
+    IPNTR   Integer array of length 3.  (OUTPUT)   
+            Pointer to mark the starting locations in the WORK for   
+            vectors used by the Arnoldi iteration.   
+            -------------------------------------------------------------   
+            IPNTR(1): pointer to the current operand vector X.   
+            IPNTR(2): pointer to the current result vector Y.   
+            IPNTR(3): pointer to the vector B * X when used in the   
+                      shift-and-invert mode.  X is the current operand.   
+            -------------------------------------------------------------   
+
+    WORKD   Double precision work array of length 3*N.  (REVERSE COMMUNICATION)   
+            Distributed array to be used in the basic Arnoldi iteration   
+            for reverse communication.  The calling program should not   
+            use WORKD as temporary workspace during the iteration !!!!!!   
+            On INPUT, WORKD(1:N) = B*RESID where RESID is associated   
+            with the K step Arnoldi factorization. Used to save some   
+            computation at the first step.   
+            On OUTPUT, WORKD(1:N) = B*RESID where RESID is associated   
+            with the K+NP step Arnoldi factorization.   
+
+    INFO    Integer.  (OUTPUT)   
+            = 0: Normal exit.   
+            > 0: Size of an invariant subspace of OP is found that is   
+                 less than K + NP.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \Routines called:   
+       dgetv0  ARPACK routine to generate the initial vector.   
+       ivout   ARPACK utility routine that prints integers.   
+       dmout   ARPACK utility routine that prints matrices.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlascl  LAPACK routine for careful scaling of a matrix.   
+       dgemv   Level 2 BLAS routine for matrix vector multiplication.   
+       daxpy   Level 1 BLAS that computes a vector triad.   
+       dscal   Level 1 BLAS that scales a vector.   
+       dcopy   Level 1 BLAS that copies one vector to another .   
+       ddot    Level 1 BLAS that computes the scalar product of two vectors.   
+       dnrm2   Level 1 BLAS that computes the norm of a vector.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/93: Version ' 2.4'   
+
+   \SCCS Information: @(#)   
+   FILE: saitr.F   SID: 2.6   DATE OF SID: 8/28/96   RELEASE: 2   
+
+   \Remarks   
+    The algorithm implemented is:   
+
+    restart = .false.   
+    Given V_{k} = [v_{1}, ..., v_{k}], r_{k};   
+    r_{k} contains the initial residual vector even for k = 0;   
+    Also assume that rnorm = || B*r_{k} || and B*r_{k} are already   
+    computed by the calling program.   
+
+    betaj = rnorm ; p_{k+1} = B*r_{k} ;   
+    For  j = k+1, ..., k+np  Do   
+       1) if ( betaj < tol ) stop or restart depending on j.   
+          if ( restart ) generate a new starting vector.   
+       2) v_{j} = r(j-1)/betaj;  V_{j} = [V_{j-1}, v_{j}];   
+          p_{j} = p_{j}/betaj   
+       3) r_{j} = OP*v_{j} where OP is defined as in dsaupd   
+          For shift-invert mode p_{j} = B*v_{j} is already available.   
+          wnorm = || OP*v_{j} ||   
+       4) Compute the j-th step residual vector.   
+          w_{j} =  V_{j}^T * B * OP * v_{j}   
+          r_{j} =  OP*v_{j} - V_{j} * w_{j}   
+          alphaj <- j-th component of w_{j}   
+          rnorm = || r_{j} ||   
+          betaj+1 = rnorm   
+          If (rnorm > 0.717*wnorm) accept step and go back to 1)   
+       5) Re-orthogonalization step:   
+          s = V_{j}'*B*r_{j}   
+          r_{j} = r_{j} - V_{j}*s;  rnorm1 = || r_{j} ||   
+          alphaj = alphaj + s_{j};   
+       6) Iterative refinement step:   
+          If (rnorm1 > 0.717*rnorm) then   
+             rnorm = rnorm1   
+             accept step and go back to 1)   
+          Else   
+             rnorm = rnorm1   
+             If this is the first time in step 6), go to 5)   
+             Else r_{j} lies in the span of V_{j} numerically.   
+                Set r_{j} = 0 and rnorm = 0; go to 1)   
+          EndIf   
+    End Do   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsaitr_(integer *ido, char *bmat, integer *n, integer *k,
+	 integer *np, integer *mode, doublereal *resid, doublereal *rnorm, 
+	doublereal *v, integer *ldv, doublereal *h__, integer *ldh, integer *
+	ipntr, doublereal *workd, integer *info)
+{
+    /* Initialized data */
+
+    IGRAPH_F77_SAVE logical first = TRUE_;
+
+    /* System generated locals */
+    integer h_dim1, h_offset, v_dim1, v_offset, i__1;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__;
+    IGRAPH_F77_SAVE integer j;
+    real t0, t1, t2 = 0.0, t3, t4, t5;
+    integer jj;
+    IGRAPH_F77_SAVE integer ipj, irj;
+    integer nbx = 0;
+    IGRAPH_F77_SAVE integer ivj;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    IGRAPH_F77_SAVE integer ierr, iter;
+    integer nopx = 0;
+    IGRAPH_F77_SAVE integer itry;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    doublereal temp1;
+    IGRAPH_F77_SAVE logical orth1, orth2, step3, step4;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *), igraphdgemv_(char *, integer *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *);
+    integer infol;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    doublereal xtemp[2];
+    real tmvbx = 0;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen);
+    IGRAPH_F77_SAVE doublereal wnorm;
+    extern /* Subroutine */ int igraphivout_(integer *, integer *, integer *, 
+	    integer *, char *, ftnlen), igraphdgetv0_(integer *, char *, integer *, 
+	    logical *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *);
+    IGRAPH_F77_SAVE doublereal rnorm1;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlascl_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *), igraphsecond_(real *);
+    integer logfil;
+    IGRAPH_F77_SAVE doublereal safmin;
+    integer ndigit = 0, nitref = 0;
+    real titref = 0;
+    integer msaitr = 0;
+    IGRAPH_F77_SAVE integer msglvl;
+    real tsaitr = 0;
+    integer nrorth = 0;
+    IGRAPH_F77_SAVE logical rstart;
+    integer nrstrt = 0;
+    real tmvopx = 0;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %-----------------------%   
+       | Local Array Arguments |   
+       %-----------------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %-----------------%   
+       | Data statements |   
+       %-----------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    --ipntr;
+
+    /* Function Body   
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------% */
+
+    if (first) {
+	first = FALSE_;
+
+/*        %--------------------------------%   
+          | safmin = safe minimum is such  |   
+          | that 1/sfmin does not overflow |   
+          %--------------------------------% */
+
+	safmin = igraphdlamch_("safmin");
+    }
+
+    if (*ido == 0) {
+
+/*        %-------------------------------%   
+          | Initialize timing statistics  |   
+          | & message level for debugging |   
+          %-------------------------------% */
+
+	igraphsecond_(&t0);
+	msglvl = msaitr;
+
+/*        %------------------------------%   
+          | Initial call to this routine |   
+          %------------------------------% */
+
+	*info = 0;
+	step3 = FALSE_;
+	step4 = FALSE_;
+	rstart = FALSE_;
+	orth1 = FALSE_;
+	orth2 = FALSE_;
+
+/*        %--------------------------------%   
+          | Pointer to the current step of |   
+          | the factorization to build     |   
+          %--------------------------------% */
+
+	j = *k + 1;
+
+/*        %------------------------------------------%   
+          | Pointers used for reverse communication  |   
+          | when using WORKD.                        |   
+          %------------------------------------------% */
+
+	ipj = 1;
+	irj = ipj + *n;
+	ivj = irj + *n;
+    }
+
+/*     %-------------------------------------------------%   
+       | When in reverse communication mode one of:      |   
+       | STEP3, STEP4, ORTH1, ORTH2, RSTART              |   
+       | will be .true.                                  |   
+       | STEP3: return from computing OP*v_{j}.          |   
+       | STEP4: return from computing B-norm of OP*v_{j} |   
+       | ORTH1: return from computing B-norm of r_{j+1}  |   
+       | ORTH2: return from computing B-norm of          |   
+       |        correction to the residual vector.       |   
+       | RSTART: return from OP computations needed by   |   
+       |         dgetv0.                                 |   
+       %-------------------------------------------------% */
+
+    if (step3) {
+	goto L50;
+    }
+    if (step4) {
+	goto L60;
+    }
+    if (orth1) {
+	goto L70;
+    }
+    if (orth2) {
+	goto L90;
+    }
+    if (rstart) {
+	goto L30;
+    }
+
+/*     %------------------------------%   
+       | Else this is the first step. |   
+       %------------------------------%   
+
+       %--------------------------------------------------------------%   
+       |                                                              |   
+       |        A R N O L D I     I T E R A T I O N     L O O P       |   
+       |                                                              |   
+       | Note:  B*r_{j-1} is already in WORKD(1:N)=WORKD(IPJ:IPJ+N-1) |   
+       %--------------------------------------------------------------% */
+
+L1000:
+
+    if (msglvl > 2) {
+	igraphivout_(&logfil, &c__1, &j, &ndigit, "_saitr: generating Arnoldi vect"
+		"or no.", (ftnlen)37);
+	igraphdvout_(&logfil, &c__1, rnorm, &ndigit, "_saitr: B-norm of the curren"
+		"t residual =", (ftnlen)40);
+    }
+
+/*        %---------------------------------------------------------%   
+          | Check for exact zero. Equivalent to determing whether a |   
+          | j-step Arnoldi factorization is present.                |   
+          %---------------------------------------------------------% */
+
+    if (*rnorm > 0.) {
+	goto L40;
+    }
+
+/*           %---------------------------------------------------%   
+             | Invariant subspace found, generate a new starting |   
+             | vector which is orthogonal to the current Arnoldi |   
+             | basis and continue the iteration.                 |   
+             %---------------------------------------------------% */
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, &j, &ndigit, "_saitr: ****** restart at step "
+		"******", (ftnlen)37);
+    }
+
+/*           %---------------------------------------------%   
+             | ITRY is the loop variable that controls the |   
+             | maximum amount of times that a restart is   |   
+             | attempted. NRSTRT is used by stat.h         |   
+             %---------------------------------------------% */
+
+    ++nrstrt;
+    itry = 1;
+L20:
+    rstart = TRUE_;
+    *ido = 0;
+L30:
+
+/*           %--------------------------------------%   
+             | If in reverse communication mode and |   
+             | RSTART = .true. flow returns here.   |   
+             %--------------------------------------% */
+
+    igraphdgetv0_(ido, bmat, &itry, &c_false, n, &j, &v[v_offset], ldv, &resid[1], 
+	    rnorm, &ipntr[1], &workd[1], &ierr);
+    if (*ido != 99) {
+	goto L9000;
+    }
+    if (ierr < 0) {
+	++itry;
+	if (itry <= 3) {
+	    goto L20;
+	}
+
+/*              %------------------------------------------------%   
+                | Give up after several restart attempts.        |   
+                | Set INFO to the size of the invariant subspace |   
+                | which spans OP and exit.                       |   
+                %------------------------------------------------% */
+
+	*info = j - 1;
+	igraphsecond_(&t1);
+	tsaitr += t1 - t0;
+	*ido = 99;
+	goto L9000;
+    }
+
+L40:
+
+/*        %---------------------------------------------------------%   
+          | STEP 2:  v_{j} = r_{j-1}/rnorm and p_{j} = p_{j}/rnorm  |   
+          | Note that p_{j} = B*r_{j-1}. In order to avoid overflow |   
+          | when reciprocating a small RNORM, test against lower    |   
+          | machine bound.                                          |   
+          %---------------------------------------------------------% */
+
+    igraphdcopy_(n, &resid[1], &c__1, &v[j * v_dim1 + 1], &c__1);
+    if (*rnorm >= safmin) {
+	temp1 = 1. / *rnorm;
+	igraphdscal_(n, &temp1, &v[j * v_dim1 + 1], &c__1);
+	igraphdscal_(n, &temp1, &workd[ipj], &c__1);
+    } else {
+
+/*            %-----------------------------------------%   
+              | To scale both v_{j} and p_{j} carefully |   
+              | use LAPACK routine SLASCL               |   
+              %-----------------------------------------% */
+
+	igraphdlascl_("General", &i__, &i__, rnorm, &c_b24, n, &c__1, &v[j * v_dim1 
+		+ 1], n, &infol);
+	igraphdlascl_("General", &i__, &i__, rnorm, &c_b24, n, &c__1, &workd[ipj], 
+		n, &infol);
+    }
+
+/*        %------------------------------------------------------%   
+          | STEP 3:  r_{j} = OP*v_{j}; Note that p_{j} = B*v_{j} |   
+          | Note that this is not quite yet r_{j}. See STEP 4    |   
+          %------------------------------------------------------% */
+
+    step3 = TRUE_;
+    ++nopx;
+    igraphsecond_(&t2);
+    igraphdcopy_(n, &v[j * v_dim1 + 1], &c__1, &workd[ivj], &c__1);
+    ipntr[1] = ivj;
+    ipntr[2] = irj;
+    ipntr[3] = ipj;
+    *ido = 1;
+
+/*        %-----------------------------------%   
+          | Exit in order to compute OP*v_{j} |   
+          %-----------------------------------% */
+
+    goto L9000;
+L50:
+
+/*        %-----------------------------------%   
+          | Back from reverse communication;  |   
+          | WORKD(IRJ:IRJ+N-1) := OP*v_{j}.   |   
+          %-----------------------------------% */
+
+    igraphsecond_(&t3);
+    tmvopx += t3 - t2;
+
+    step3 = FALSE_;
+
+/*        %------------------------------------------%   
+          | Put another copy of OP*v_{j} into RESID. |   
+          %------------------------------------------% */
+
+    igraphdcopy_(n, &workd[irj], &c__1, &resid[1], &c__1);
+
+/*        %-------------------------------------------%   
+          | STEP 4:  Finish extending the symmetric   |   
+          |          Arnoldi to length j. If MODE = 2 |   
+          |          then B*OP = B*inv(B)*A = A and   |   
+          |          we don't need to compute B*OP.   |   
+          | NOTE: If MODE = 2 WORKD(IVJ:IVJ+N-1) is   |   
+          | assumed to have A*v_{j}.                  |   
+          %-------------------------------------------% */
+
+    if (*mode == 2) {
+	goto L65;
+    }
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	step4 = TRUE_;
+	ipntr[1] = irj;
+	ipntr[2] = ipj;
+	*ido = 2;
+
+/*           %-------------------------------------%   
+             | Exit in order to compute B*OP*v_{j} |   
+             %-------------------------------------% */
+
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+    }
+L60:
+
+/*        %-----------------------------------%   
+          | Back from reverse communication;  |   
+          | WORKD(IPJ:IPJ+N-1) := B*OP*v_{j}. |   
+          %-----------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+    step4 = FALSE_;
+
+/*        %-------------------------------------%   
+          | The following is needed for STEP 5. |   
+          | Compute the B-norm of OP*v_{j}.     |   
+          %-------------------------------------% */
+
+L65:
+    if (*mode == 2) {
+
+/*           %----------------------------------%   
+             | Note that the B-norm of OP*v_{j} |   
+             | is the inv(B)-norm of A*v_{j}.   |   
+             %----------------------------------% */
+
+	wnorm = igraphddot_(n, &resid[1], &c__1, &workd[ivj], &c__1);
+	wnorm = sqrt((abs(wnorm)));
+    } else if (*(unsigned char *)bmat == 'G') {
+	wnorm = igraphddot_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+	wnorm = sqrt((abs(wnorm)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	wnorm = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+
+/*        %-----------------------------------------%   
+          | Compute the j-th residual corresponding |   
+          | to the j step factorization.            |   
+          | Use Classical Gram Schmidt and compute: |   
+          | w_{j} <-  V_{j}^T * B * OP * v_{j}      |   
+          | r_{j} <-  OP*v_{j} - V_{j} * w_{j}      |   
+          %-----------------------------------------%   
+
+
+          %------------------------------------------%   
+          | Compute the j Fourier coefficients w_{j} |   
+          | WORKD(IPJ:IPJ+N-1) contains B*OP*v_{j}.  |   
+          %------------------------------------------% */
+
+    if (*mode != 2) {
+	igraphdgemv_("T", n, &j, &c_b24, &v[v_offset], ldv, &workd[ipj], &c__1, &
+		c_b49, &workd[irj], &c__1);
+    } else if (*mode == 2) {
+	igraphdgemv_("T", n, &j, &c_b24, &v[v_offset], ldv, &workd[ivj], &c__1, &
+		c_b49, &workd[irj], &c__1);
+    }
+
+/*        %--------------------------------------%   
+          | Orthgonalize r_{j} against V_{j}.    |   
+          | RESID contains OP*v_{j}. See STEP 3. |   
+          %--------------------------------------% */
+
+    igraphdgemv_("N", n, &j, &c_b57, &v[v_offset], ldv, &workd[irj], &c__1, &c_b24, 
+	    &resid[1], &c__1);
+
+/*        %--------------------------------------%   
+          | Extend H to have j rows and columns. |   
+          %--------------------------------------% */
+
+    h__[j + (h_dim1 << 1)] = workd[irj + j - 1];
+    if (j == 1 || rstart) {
+	h__[j + h_dim1] = 0.;
+    } else {
+	h__[j + h_dim1] = *rnorm;
+    }
+    igraphsecond_(&t4);
+
+    orth1 = TRUE_;
+    iter = 0;
+
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	igraphdcopy_(n, &resid[1], &c__1, &workd[irj], &c__1);
+	ipntr[1] = irj;
+	ipntr[2] = ipj;
+	*ido = 2;
+
+/*           %----------------------------------%   
+             | Exit in order to compute B*r_{j} |   
+             %----------------------------------% */
+
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+    }
+L70:
+
+/*        %---------------------------------------------------%   
+          | Back from reverse communication if ORTH1 = .true. |   
+          | WORKD(IPJ:IPJ+N-1) := B*r_{j}.                    |   
+          %---------------------------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+    orth1 = FALSE_;
+
+/*        %------------------------------%   
+          | Compute the B-norm of r_{j}. |   
+          %------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	*rnorm = igraphddot_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+	*rnorm = sqrt((abs(*rnorm)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	*rnorm = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+
+/*        %-----------------------------------------------------------%   
+          | STEP 5: Re-orthogonalization / Iterative refinement phase |   
+          | Maximum NITER_ITREF tries.                                |   
+          |                                                           |   
+          |          s      = V_{j}^T * B * r_{j}                     |   
+          |          r_{j}  = r_{j} - V_{j}*s                         |   
+          |          alphaj = alphaj + s_{j}                          |   
+          |                                                           |   
+          | The stopping criteria used for iterative refinement is    |   
+          | discussed in Parlett's book SEP, page 107 and in Gragg &  |   
+          | Reichel ACM TOMS paper; Algorithm 686, Dec. 1990.         |   
+          | Determine if we need to correct the residual. The goal is |   
+          | to enforce ||v(:,1:j)^T * r_{j}|| .le. eps * || r_{j} ||  |   
+          %-----------------------------------------------------------% */
+
+    if (*rnorm > wnorm * .717f) {
+	goto L100;
+    }
+    ++nrorth;
+
+/*        %---------------------------------------------------%   
+          | Enter the Iterative refinement phase. If further  |   
+          | refinement is necessary, loop back here. The loop |   
+          | variable is ITER. Perform a step of Classical     |   
+          | Gram-Schmidt using all the Arnoldi vectors V_{j}  |   
+          %---------------------------------------------------% */
+
+L80:
+
+    if (msglvl > 2) {
+	xtemp[0] = wnorm;
+	xtemp[1] = *rnorm;
+	igraphdvout_(&logfil, &c__2, xtemp, &ndigit, "_saitr: re-orthonalization ;"
+		" wnorm and rnorm are", (ftnlen)48);
+    }
+
+/*        %----------------------------------------------------%   
+          | Compute V_{j}^T * B * r_{j}.                       |   
+          | WORKD(IRJ:IRJ+J-1) = v(:,1:J)'*WORKD(IPJ:IPJ+N-1). |   
+          %----------------------------------------------------% */
+
+    igraphdgemv_("T", n, &j, &c_b24, &v[v_offset], ldv, &workd[ipj], &c__1, &c_b49, 
+	    &workd[irj], &c__1);
+
+/*        %----------------------------------------------%   
+          | Compute the correction to the residual:      |   
+          | r_{j} = r_{j} - V_{j} * WORKD(IRJ:IRJ+J-1).  |   
+          | The correction to H is v(:,1:J)*H(1:J,1:J) + |   
+          | v(:,1:J)*WORKD(IRJ:IRJ+J-1)*e'_j, but only   |   
+          | H(j,j) is updated.                           |   
+          %----------------------------------------------% */
+
+    igraphdgemv_("N", n, &j, &c_b57, &v[v_offset], ldv, &workd[irj], &c__1, &c_b24, 
+	    &resid[1], &c__1);
+
+    if (j == 1 || rstart) {
+	h__[j + h_dim1] = 0.;
+    }
+    h__[j + (h_dim1 << 1)] += workd[irj + j - 1];
+
+    orth2 = TRUE_;
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	igraphdcopy_(n, &resid[1], &c__1, &workd[irj], &c__1);
+	ipntr[1] = irj;
+	ipntr[2] = ipj;
+	*ido = 2;
+
+/*           %-----------------------------------%   
+             | Exit in order to compute B*r_{j}. |   
+             | r_{j} is the corrected residual.  |   
+             %-----------------------------------% */
+
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+    }
+L90:
+
+/*        %---------------------------------------------------%   
+          | Back from reverse communication if ORTH2 = .true. |   
+          %---------------------------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+/*        %-----------------------------------------------------%   
+          | Compute the B-norm of the corrected residual r_{j}. |   
+          %-----------------------------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	rnorm1 = igraphddot_(n, &resid[1], &c__1, &workd[ipj], &c__1);
+	rnorm1 = sqrt((abs(rnorm1)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	rnorm1 = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+
+    if (msglvl > 0 && iter > 0) {
+	igraphivout_(&logfil, &c__1, &j, &ndigit, "_saitr: Iterative refinement fo"
+		"r Arnoldi residual", (ftnlen)49);
+	if (msglvl > 2) {
+	    xtemp[0] = *rnorm;
+	    xtemp[1] = rnorm1;
+	    igraphdvout_(&logfil, &c__2, xtemp, &ndigit, "_saitr: iterative refine"
+		    "ment ; rnorm and rnorm1 are", (ftnlen)51);
+	}
+    }
+
+/*        %-----------------------------------------%   
+          | Determine if we need to perform another |   
+          | step of re-orthogonalization.           |   
+          %-----------------------------------------% */
+
+    if (rnorm1 > *rnorm * .717f) {
+
+/*           %--------------------------------%   
+             | No need for further refinement |   
+             %--------------------------------% */
+
+	*rnorm = rnorm1;
+
+    } else {
+
+/*           %-------------------------------------------%   
+             | Another step of iterative refinement step |   
+             | is required. NITREF is used by stat.h     |   
+             %-------------------------------------------% */
+
+	++nitref;
+	*rnorm = rnorm1;
+	++iter;
+	if (iter <= 1) {
+	    goto L80;
+	}
+
+/*           %-------------------------------------------------%   
+             | Otherwise RESID is numerically in the span of V |   
+             %-------------------------------------------------% */
+
+	i__1 = *n;
+	for (jj = 1; jj <= i__1; ++jj) {
+	    resid[jj] = 0.;
+/* L95: */
+	}
+	*rnorm = 0.;
+    }
+
+/*        %----------------------------------------------%   
+          | Branch here directly if iterative refinement |   
+          | wasn't necessary or after at most NITER_REF  |   
+          | steps of iterative refinement.               |   
+          %----------------------------------------------% */
+
+L100:
+
+    rstart = FALSE_;
+    orth2 = FALSE_;
+
+    igraphsecond_(&t5);
+    titref += t5 - t4;
+
+/*        %----------------------------------------------------------%   
+          | Make sure the last off-diagonal element is non negative  |   
+          | If not perform a similarity transformation on H(1:j,1:j) |   
+          | and scale v(:,j) by -1.                                  |   
+          %----------------------------------------------------------% */
+
+    if (h__[j + h_dim1] < 0.) {
+	h__[j + h_dim1] = -h__[j + h_dim1];
+	if (j < *k + *np) {
+	    igraphdscal_(n, &c_b57, &v[(j + 1) * v_dim1 + 1], &c__1);
+	} else {
+	    igraphdscal_(n, &c_b57, &resid[1], &c__1);
+	}
+    }
+
+/*        %------------------------------------%   
+          | STEP 6: Update  j = j+1;  Continue |   
+          %------------------------------------% */
+
+    ++j;
+    if (j > *k + *np) {
+	igraphsecond_(&t1);
+	tsaitr += t1 - t0;
+	*ido = 99;
+
+	if (msglvl > 1) {
+	    i__1 = *k + *np;
+	    igraphdvout_(&logfil, &i__1, &h__[(h_dim1 << 1) + 1], &ndigit, "_saitr"
+		    ": main diagonal of matrix H of step K+NP.", (ftnlen)47);
+	    if (*k + *np > 1) {
+		i__1 = *k + *np - 1;
+		igraphdvout_(&logfil, &i__1, &h__[h_dim1 + 2], &ndigit, "_saitr: s"
+			"ub diagonal of matrix H of step K+NP.", (ftnlen)46);
+	    }
+	}
+
+	goto L9000;
+    }
+
+/*        %--------------------------------------------------------%   
+          | Loop back to extend the factorization by another step. |   
+          %--------------------------------------------------------% */
+
+    goto L1000;
+
+/*     %---------------------------------------------------------------%   
+       |                                                               |   
+       |  E N D     O F     M A I N     I T E R A T I O N     L O O P  |   
+       |                                                               |   
+       %---------------------------------------------------------------% */
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dsaitr |   
+       %---------------% */
+
+} /* igraphdsaitr_ */
+
diff --git a/igraph/src/dsapps.c b/igraph/src/dsapps.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsapps.c
@@ -0,0 +1,621 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b4 = 0.;
+static doublereal c_b5 = 1.;
+static integer c__1 = 1;
+static doublereal c_b20 = -1.;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsapps   
+
+   \Description:   
+    Given the Arnoldi factorization   
+
+       A*V_{k} - V_{k}*H_{k} = r_{k+p}*e_{k+p}^T,   
+
+    apply NP shifts implicitly resulting in   
+
+       A*(V_{k}*Q) - (V_{k}*Q)*(Q^T* H_{k}*Q) = r_{k+p}*e_{k+p}^T * Q   
+
+    where Q is an orthogonal matrix of order KEV+NP. Q is the product of   
+    rotations resulting from the NP bulge chasing sweeps.  The updated Arnoldi   
+    factorization becomes:   
+
+       A*VNEW_{k} - VNEW_{k}*HNEW_{k} = rnew_{k}*e_{k}^T.   
+
+   \Usage:   
+    call dsapps   
+       ( N, KEV, NP, SHIFT, V, LDV, H, LDH, RESID, Q, LDQ, WORKD )   
+
+   \Arguments   
+    N       Integer.  (INPUT)   
+            Problem size, i.e. dimension of matrix A.   
+
+    KEV     Integer.  (INPUT)   
+            INPUT: KEV+NP is the size of the input matrix H.   
+            OUTPUT: KEV is the size of the updated matrix HNEW.   
+
+    NP      Integer.  (INPUT)   
+            Number of implicit shifts to be applied.   
+
+    SHIFT   Double precision array of length NP.  (INPUT)   
+            The shifts to be applied.   
+
+    V       Double precision N by (KEV+NP) array.  (INPUT/OUTPUT)   
+            INPUT: V contains the current KEV+NP Arnoldi vectors.   
+            OUTPUT: VNEW = V(1:n,1:KEV); the updated Arnoldi vectors   
+            are in the first KEV columns of V.   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling   
+            program.   
+
+    H       Double precision (KEV+NP) by 2 array.  (INPUT/OUTPUT)   
+            INPUT: H contains the symmetric tridiagonal matrix of the   
+            Arnoldi factorization with the subdiagonal in the 1st column   
+            starting at H(2,1) and the main diagonal in the 2nd column.   
+            OUTPUT: H contains the updated tridiagonal matrix in the   
+            KEV leading submatrix.   
+
+    LDH     Integer.  (INPUT)   
+            Leading dimension of H exactly as declared in the calling   
+            program.   
+
+    RESID   Double precision array of length (N).  (INPUT/OUTPUT)   
+            INPUT: RESID contains the the residual vector r_{k+p}.   
+            OUTPUT: RESID is the updated residual vector rnew_{k}.   
+
+    Q       Double precision KEV+NP by KEV+NP work array.  (WORKSPACE)   
+            Work array used to accumulate the rotations during the bulge   
+            chase sweep.   
+
+    LDQ     Integer.  (INPUT)   
+            Leading dimension of Q exactly as declared in the calling   
+            program.   
+
+    WORKD   Double precision work array of length 2*N.  (WORKSPACE)   
+            Distributed array used in the application of the accumulated   
+            orthogonal matrix Q.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+
+   \Routines called:   
+       ivout   ARPACK utility routine that prints integers.   
+       second  ARPACK utility routine for timing.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlartg  LAPACK Givens rotation construction routine.   
+       dlacpy  LAPACK matrix copy routine.   
+       dlaset  LAPACK matrix initialization routine.   
+       dgemv   Level 2 BLAS routine for matrix vector multiplication.   
+       daxpy   Level 1 BLAS that computes a vector triad.   
+       dcopy   Level 1 BLAS that copies one vector to another.   
+       dscal   Level 1 BLAS that scales a vector.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       12/16/93: Version ' 2.1'   
+
+   \SCCS Information: @(#)   
+   FILE: sapps.F   SID: 2.5   DATE OF SID: 4/19/96   RELEASE: 2   
+
+   \Remarks   
+    1. In this version, each shift is applied to all the subblocks of   
+       the tridiagonal matrix H and not just to the submatrix that it   
+       comes from. This routine assumes that the subdiagonal elements   
+       of H that are stored in h(1:kev+np,1) are nonegative upon input   
+       and enforce this condition upon output. This version incorporates   
+       deflation. See code for documentation.   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsapps_(integer *n, integer *kev, integer *np, 
+	doublereal *shift, doublereal *v, integer *ldv, doublereal *h__, 
+	integer *ldh, doublereal *resid, doublereal *q, integer *ldq, 
+	doublereal *workd)
+{
+    /* Initialized data */
+
+    IGRAPH_F77_SAVE logical first = TRUE_;
+
+    /* System generated locals */
+    integer h_dim1, h_offset, q_dim1, q_offset, v_dim1, v_offset, i__1, i__2, 
+	    i__3, i__4;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    doublereal c__, f, g;
+    integer i__, j;
+    doublereal r__, s, a1, a2, a3, a4;
+    real t0, t1;
+    integer jj;
+    doublereal big;
+    integer iend, itop;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *), igraphdgemv_(char *, integer *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *), igraphdcopy_(integer *, doublereal *, 
+	    integer *, doublereal *, integer *), igraphdaxpy_(integer *, doublereal 
+	    *, doublereal *, integer *, doublereal *, integer *), igraphdvout_(
+	    integer *, integer *, doublereal *, integer *, char *, ftnlen), 
+	    igraphivout_(integer *, integer *, integer *, integer *, char *, ftnlen)
+	    ;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphsecond_(real *), igraphdlacpy_(char *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *), igraphdlartg_(doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *), igraphdlaset_(char *, integer *, integer *,
+	     doublereal *, doublereal *, doublereal *, integer *);
+    IGRAPH_F77_SAVE doublereal epsmch;
+    integer logfil, ndigit, msapps = 0, msglvl, istart;
+    real tsapps = 0;
+    integer kplusp;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %----------------------%   
+       | Intrinsics Functions |   
+       %----------------------%   
+
+
+       %----------------%   
+       | Data statments |   
+       %----------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    --shift;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    q_dim1 = *ldq;
+    q_offset = 1 + q_dim1;
+    q -= q_offset;
+
+    /* Function Body   
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------% */
+
+    if (first) {
+	epsmch = igraphdlamch_("Epsilon-Machine");
+	first = FALSE_;
+    }
+    itop = 1;
+
+/*     %-------------------------------%   
+       | Initialize timing statistics  |   
+       | & message level for debugging |   
+       %-------------------------------% */
+
+    igraphsecond_(&t0);
+    msglvl = msapps;
+
+    kplusp = *kev + *np;
+
+/*     %----------------------------------------------%   
+       | Initialize Q to the identity matrix of order |   
+       | kplusp used to accumulate the rotations.     |   
+       %----------------------------------------------% */
+
+    igraphdlaset_("All", &kplusp, &kplusp, &c_b4, &c_b5, &q[q_offset], ldq);
+
+/*     %----------------------------------------------%   
+       | Quick return if there are no shifts to apply |   
+       %----------------------------------------------% */
+
+    if (*np == 0) {
+	goto L9000;
+    }
+
+/*     %----------------------------------------------------------%   
+       | Apply the np shifts implicitly. Apply each shift to the  |   
+       | whole matrix and not just to the submatrix from which it |   
+       | comes.                                                   |   
+       %----------------------------------------------------------% */
+
+    i__1 = *np;
+    for (jj = 1; jj <= i__1; ++jj) {
+
+	istart = itop;
+
+/*        %----------------------------------------------------------%   
+          | Check for splitting and deflation. Currently we consider |   
+          | an off-diagonal element h(i+1,1) negligible if           |   
+          |         h(i+1,1) .le. epsmch*( |h(i,2)| + |h(i+1,2)| )   |   
+          | for i=1:KEV+NP-1.                                        |   
+          | If above condition tests true then we set h(i+1,1) = 0.  |   
+          | Note that h(1:KEV+NP,1) are assumed to be non negative.  |   
+          %----------------------------------------------------------% */
+
+L20:
+
+/*        %------------------------------------------------%   
+          | The following loop exits early if we encounter |   
+          | a negligible off diagonal element.             |   
+          %------------------------------------------------% */
+
+	i__2 = kplusp - 1;
+	for (i__ = istart; i__ <= i__2; ++i__) {
+	    big = (d__1 = h__[i__ + (h_dim1 << 1)], abs(d__1)) + (d__2 = h__[
+		    i__ + 1 + (h_dim1 << 1)], abs(d__2));
+	    if (h__[i__ + 1 + h_dim1] <= epsmch * big) {
+		if (msglvl > 0) {
+		    igraphivout_(&logfil, &c__1, &i__, &ndigit, "_sapps: deflation"
+			    " at row/column no.", (ftnlen)35);
+		    igraphivout_(&logfil, &c__1, &jj, &ndigit, "_sapps: occured be"
+			    "fore shift number.", (ftnlen)36);
+		    igraphdvout_(&logfil, &c__1, &h__[i__ + 1 + h_dim1], &ndigit, 
+			    "_sapps: the corresponding off diagonal element", 
+			    (ftnlen)46);
+		}
+		h__[i__ + 1 + h_dim1] = 0.;
+		iend = i__;
+		goto L40;
+	    }
+/* L30: */
+	}
+	iend = kplusp;
+L40:
+
+	if (istart < iend) {
+
+/*           %--------------------------------------------------------%   
+             | Construct the plane rotation G'(istart,istart+1,theta) |   
+             | that attempts to drive h(istart+1,1) to zero.          |   
+             %--------------------------------------------------------% */
+
+	    f = h__[istart + (h_dim1 << 1)] - shift[jj];
+	    g = h__[istart + 1 + h_dim1];
+	    igraphdlartg_(&f, &g, &c__, &s, &r__);
+
+/*            %-------------------------------------------------------%   
+              | Apply rotation to the left and right of H;            |   
+              | H <- G' * H * G,  where G = G(istart,istart+1,theta). |   
+              | This will create a "bulge".                           |   
+              %-------------------------------------------------------% */
+
+	    a1 = c__ * h__[istart + (h_dim1 << 1)] + s * h__[istart + 1 + 
+		    h_dim1];
+	    a2 = c__ * h__[istart + 1 + h_dim1] + s * h__[istart + 1 + (
+		    h_dim1 << 1)];
+	    a4 = c__ * h__[istart + 1 + (h_dim1 << 1)] - s * h__[istart + 1 + 
+		    h_dim1];
+	    a3 = c__ * h__[istart + 1 + h_dim1] - s * h__[istart + (h_dim1 << 
+		    1)];
+	    h__[istart + (h_dim1 << 1)] = c__ * a1 + s * a2;
+	    h__[istart + 1 + (h_dim1 << 1)] = c__ * a4 - s * a3;
+	    h__[istart + 1 + h_dim1] = c__ * a3 + s * a4;
+
+/*            %----------------------------------------------------%   
+              | Accumulate the rotation in the matrix Q;  Q <- Q*G |   
+              %----------------------------------------------------%   
+
+   Computing MIN */
+	    i__3 = istart + jj;
+	    i__2 = min(i__3,kplusp);
+	    for (j = 1; j <= i__2; ++j) {
+		a1 = c__ * q[j + istart * q_dim1] + s * q[j + (istart + 1) * 
+			q_dim1];
+		q[j + (istart + 1) * q_dim1] = -s * q[j + istart * q_dim1] + 
+			c__ * q[j + (istart + 1) * q_dim1];
+		q[j + istart * q_dim1] = a1;
+/* L60: */
+	    }
+
+
+/*            %----------------------------------------------%   
+              | The following loop chases the bulge created. |   
+              | Note that the previous rotation may also be  |   
+              | done within the following loop. But it is    |   
+              | kept separate to make the distinction among  |   
+              | the bulge chasing sweeps and the first plane |   
+              | rotation designed to drive h(istart+1,1) to  |   
+              | zero.                                        |   
+              %----------------------------------------------% */
+
+	    i__2 = iend - 1;
+	    for (i__ = istart + 1; i__ <= i__2; ++i__) {
+
+/*               %----------------------------------------------%   
+                 | Construct the plane rotation G'(i,i+1,theta) |   
+                 | that zeros the i-th bulge that was created   |   
+                 | by G(i-1,i,theta). g represents the bulge.   |   
+                 %----------------------------------------------% */
+
+		f = h__[i__ + h_dim1];
+		g = s * h__[i__ + 1 + h_dim1];
+
+/*               %----------------------------------%   
+                 | Final update with G(i-1,i,theta) |   
+                 %----------------------------------% */
+
+		h__[i__ + 1 + h_dim1] = c__ * h__[i__ + 1 + h_dim1];
+		igraphdlartg_(&f, &g, &c__, &s, &r__);
+
+/*               %-------------------------------------------%   
+                 | The following ensures that h(1:iend-1,1), |   
+                 | the first iend-2 off diagonal of elements |   
+                 | H, remain non negative.                   |   
+                 %-------------------------------------------% */
+
+		if (r__ < 0.) {
+		    r__ = -r__;
+		    c__ = -c__;
+		    s = -s;
+		}
+
+/*               %--------------------------------------------%   
+                 | Apply rotation to the left and right of H; |   
+                 | H <- G * H * G',  where G = G(i,i+1,theta) |   
+                 %--------------------------------------------% */
+
+		h__[i__ + h_dim1] = r__;
+
+		a1 = c__ * h__[i__ + (h_dim1 << 1)] + s * h__[i__ + 1 + 
+			h_dim1];
+		a2 = c__ * h__[i__ + 1 + h_dim1] + s * h__[i__ + 1 + (h_dim1 
+			<< 1)];
+		a3 = c__ * h__[i__ + 1 + h_dim1] - s * h__[i__ + (h_dim1 << 1)
+			];
+		a4 = c__ * h__[i__ + 1 + (h_dim1 << 1)] - s * h__[i__ + 1 + 
+			h_dim1];
+
+		h__[i__ + (h_dim1 << 1)] = c__ * a1 + s * a2;
+		h__[i__ + 1 + (h_dim1 << 1)] = c__ * a4 - s * a3;
+		h__[i__ + 1 + h_dim1] = c__ * a3 + s * a4;
+
+/*               %----------------------------------------------------%   
+                 | Accumulate the rotation in the matrix Q;  Q <- Q*G |   
+                 %----------------------------------------------------%   
+
+   Computing MIN */
+		i__4 = j + jj;
+		i__3 = min(i__4,kplusp);
+		for (j = 1; j <= i__3; ++j) {
+		    a1 = c__ * q[j + i__ * q_dim1] + s * q[j + (i__ + 1) * 
+			    q_dim1];
+		    q[j + (i__ + 1) * q_dim1] = -s * q[j + i__ * q_dim1] + 
+			    c__ * q[j + (i__ + 1) * q_dim1];
+		    q[j + i__ * q_dim1] = a1;
+/* L50: */
+		}
+
+/* L70: */
+	    }
+
+	}
+
+/*        %--------------------------%   
+          | Update the block pointer |   
+          %--------------------------% */
+
+	istart = iend + 1;
+
+/*        %------------------------------------------%   
+          | Make sure that h(iend,1) is non-negative |   
+          | If not then set h(iend,1) <-- -h(iend,1) |   
+          | and negate the last column of Q.         |   
+          | We have effectively carried out a        |   
+          | similarity on transformation H           |   
+          %------------------------------------------% */
+
+	if (h__[iend + h_dim1] < 0.) {
+	    h__[iend + h_dim1] = -h__[iend + h_dim1];
+	    igraphdscal_(&kplusp, &c_b20, &q[iend * q_dim1 + 1], &c__1);
+	}
+
+/*        %--------------------------------------------------------%   
+          | Apply the same shift to the next block if there is any |   
+          %--------------------------------------------------------% */
+
+	if (iend < kplusp) {
+	    goto L20;
+	}
+
+/*        %-----------------------------------------------------%   
+          | Check if we can increase the the start of the block |   
+          %-----------------------------------------------------% */
+
+	i__2 = kplusp - 1;
+	for (i__ = itop; i__ <= i__2; ++i__) {
+	    if (h__[i__ + 1 + h_dim1] > 0.) {
+		goto L90;
+	    }
+	    ++itop;
+/* L80: */
+	}
+
+/*        %-----------------------------------%   
+          | Finished applying the jj-th shift |   
+          %-----------------------------------% */
+
+L90:
+	;
+    }
+
+/*     %------------------------------------------%   
+       | All shifts have been applied. Check for  |   
+       | more possible deflation that might occur |   
+       | after the last shift is applied.         |   
+       %------------------------------------------% */
+
+    i__1 = kplusp - 1;
+    for (i__ = itop; i__ <= i__1; ++i__) {
+	big = (d__1 = h__[i__ + (h_dim1 << 1)], abs(d__1)) + (d__2 = h__[i__ 
+		+ 1 + (h_dim1 << 1)], abs(d__2));
+	if (h__[i__ + 1 + h_dim1] <= epsmch * big) {
+	    if (msglvl > 0) {
+		igraphivout_(&logfil, &c__1, &i__, &ndigit, "_sapps: deflation at "
+			"row/column no.", (ftnlen)35);
+		igraphdvout_(&logfil, &c__1, &h__[i__ + 1 + h_dim1], &ndigit, "_sa"
+			"pps: the corresponding off diagonal element", (ftnlen)
+			46);
+	    }
+	    h__[i__ + 1 + h_dim1] = 0.;
+	}
+/* L100: */
+    }
+
+/*     %-------------------------------------------------%   
+       | Compute the (kev+1)-st column of (V*Q) and      |   
+       | temporarily store the result in WORKD(N+1:2*N). |   
+       | This is not necessary if h(kev+1,1) = 0.         |   
+       %-------------------------------------------------% */
+
+    if (h__[*kev + 1 + h_dim1] > 0.) {
+	igraphdgemv_("N", n, &kplusp, &c_b5, &v[v_offset], ldv, &q[(*kev + 1) * 
+		q_dim1 + 1], &c__1, &c_b4, &workd[*n + 1], &c__1);
+    }
+
+/*     %-------------------------------------------------------%   
+       | Compute column 1 to kev of (V*Q) in backward order    |   
+       | taking advantage that Q is an upper triangular matrix |   
+       | with lower bandwidth np.                              |   
+       | Place results in v(:,kplusp-kev:kplusp) temporarily.  |   
+       %-------------------------------------------------------% */
+
+    i__1 = *kev;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	i__2 = kplusp - i__ + 1;
+	igraphdgemv_("N", n, &i__2, &c_b5, &v[v_offset], ldv, &q[(*kev - i__ + 1) * 
+		q_dim1 + 1], &c__1, &c_b4, &workd[1], &c__1);
+	igraphdcopy_(n, &workd[1], &c__1, &v[(kplusp - i__ + 1) * v_dim1 + 1], &
+		c__1);
+/* L130: */
+    }
+
+/*     %-------------------------------------------------%   
+       |  Move v(:,kplusp-kev+1:kplusp) into v(:,1:kev). |   
+       %-------------------------------------------------% */
+
+    igraphdlacpy_("All", n, kev, &v[(*np + 1) * v_dim1 + 1], ldv, &v[v_offset], ldv);
+
+/*     %--------------------------------------------%   
+       | Copy the (kev+1)-st column of (V*Q) in the |   
+       | appropriate place if h(kev+1,1) .ne. zero. |   
+       %--------------------------------------------% */
+
+    if (h__[*kev + 1 + h_dim1] > 0.) {
+	igraphdcopy_(n, &workd[*n + 1], &c__1, &v[(*kev + 1) * v_dim1 + 1], &c__1);
+    }
+
+/*     %-------------------------------------%   
+       | Update the residual vector:         |   
+       |    r <- sigmak*r + betak*v(:,kev+1) |   
+       | where                               |   
+       |    sigmak = (e_{kev+p}'*Q)*e_{kev}  |   
+       |    betak = e_{kev+1}'*H*e_{kev}     |   
+       %-------------------------------------% */
+
+    igraphdscal_(n, &q[kplusp + *kev * q_dim1], &resid[1], &c__1);
+    if (h__[*kev + 1 + h_dim1] > 0.) {
+	igraphdaxpy_(n, &h__[*kev + 1 + h_dim1], &v[(*kev + 1) * v_dim1 + 1], &c__1,
+		 &resid[1], &c__1);
+    }
+
+    if (msglvl > 1) {
+	igraphdvout_(&logfil, &c__1, &q[kplusp + *kev * q_dim1], &ndigit, "_sapps:"
+		" sigmak of the updated residual vector", (ftnlen)45);
+	igraphdvout_(&logfil, &c__1, &h__[*kev + 1 + h_dim1], &ndigit, "_sapps: be"
+		"tak of the updated residual vector", (ftnlen)44);
+	igraphdvout_(&logfil, kev, &h__[(h_dim1 << 1) + 1], &ndigit, "_sapps: upda"
+		"ted main diagonal of H for next iteration", (ftnlen)53);
+	if (*kev > 1) {
+	    i__1 = *kev - 1;
+	    igraphdvout_(&logfil, &i__1, &h__[h_dim1 + 2], &ndigit, "_sapps: updat"
+		    "ed sub diagonal of H for next iteration", (ftnlen)52);
+	}
+    }
+
+    igraphsecond_(&t1);
+    tsapps += t1 - t0;
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dsapps |   
+       %---------------% */
+
+} /* igraphdsapps_ */
+
diff --git a/igraph/src/dsaup2.c b/igraph/src/dsaup2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsaup2.c
@@ -0,0 +1,976 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b3 = .66666666666666663;
+static integer c__1 = 1;
+static integer c__0 = 0;
+static integer c__3 = 3;
+static logical c_true = TRUE_;
+static integer c__2 = 2;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsaup2   
+
+   \Description:   
+    Intermediate level interface called by dsaupd.   
+
+   \Usage:   
+    call dsaup2   
+       ( IDO, BMAT, N, WHICH, NEV, NP, TOL, RESID, MODE, IUPD,   
+         ISHIFT, MXITER, V, LDV, H, LDH, RITZ, BOUNDS, Q, LDQ, WORKL,   
+         IPNTR, WORKD, INFO )   
+
+   \Arguments   
+
+    IDO, BMAT, N, WHICH, NEV, TOL, RESID: same as defined in dsaupd.   
+    MODE, ISHIFT, MXITER: see the definition of IPARAM in dsaupd.   
+
+    NP      Integer.  (INPUT/OUTPUT)   
+            Contains the number of implicit shifts to apply during   
+            each Arnoldi/Lanczos iteration.   
+            If ISHIFT=1, NP is adjusted dynamically at each iteration   
+            to accelerate convergence and prevent stagnation.   
+            This is also roughly equal to the number of matrix-vector   
+            products (involving the operator OP) per Arnoldi iteration.   
+            The logic for adjusting is contained within the current   
+            subroutine.   
+            If ISHIFT=0, NP is the number of shifts the user needs   
+            to provide via reverse comunication. 0 < NP < NCV-NEV.   
+            NP may be less than NCV-NEV since a leading block of the current   
+            upper Tridiagonal matrix has split off and contains "unwanted"   
+            Ritz values.   
+            Upon termination of the IRA iteration, NP contains the number   
+            of "converged" wanted Ritz values.   
+
+    IUPD    Integer.  (INPUT)   
+            IUPD .EQ. 0: use explicit restart instead implicit update.   
+            IUPD .NE. 0: use implicit update.   
+
+    V       Double precision N by (NEV+NP) array.  (INPUT/OUTPUT)   
+            The Lanczos basis vectors.   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling   
+            program.   
+
+    H       Double precision (NEV+NP) by 2 array.  (OUTPUT)   
+            H is used to store the generated symmetric tridiagonal matrix   
+            The subdiagonal is stored in the first column of H starting   
+            at H(2,1).  The main diagonal is stored in the second column   
+            of H starting at H(1,2). If dsaup2 converges store the   
+            B-norm of the final residual vector in H(1,1).   
+
+    LDH     Integer.  (INPUT)   
+            Leading dimension of H exactly as declared in the calling   
+            program.   
+
+    RITZ    Double precision array of length NEV+NP.  (OUTPUT)   
+            RITZ(1:NEV) contains the computed Ritz values of OP.   
+
+    BOUNDS  Double precision array of length NEV+NP.  (OUTPUT)   
+            BOUNDS(1:NEV) contain the error bounds corresponding to RITZ.   
+
+    Q       Double precision (NEV+NP) by (NEV+NP) array.  (WORKSPACE)   
+            Private (replicated) work array used to accumulate the   
+            rotation in the shift application step.   
+
+    LDQ     Integer.  (INPUT)   
+            Leading dimension of Q exactly as declared in the calling   
+            program.   
+
+    WORKL   Double precision array of length at least 3*(NEV+NP).  (INPUT/WORKSPACE)   
+            Private (replicated) array on each PE or array allocated on   
+            the front end.  It is used in the computation of the   
+            tridiagonal eigenvalue problem, the calculation and   
+            application of the shifts and convergence checking.   
+            If ISHIFT .EQ. O and IDO .EQ. 3, the first NP locations   
+            of WORKL are used in reverse communication to hold the user   
+            supplied shifts.   
+
+    IPNTR   Integer array of length 3.  (OUTPUT)   
+            Pointer to mark the starting locations in the WORKD for   
+            vectors used by the Lanczos iteration.   
+            -------------------------------------------------------------   
+            IPNTR(1): pointer to the current operand vector X.   
+            IPNTR(2): pointer to the current result vector Y.   
+            IPNTR(3): pointer to the vector B * X when used in one of   
+                      the spectral transformation modes.  X is the current   
+                      operand.   
+            -------------------------------------------------------------   
+
+    WORKD   Double precision work array of length 3*N.  (REVERSE COMMUNICATION)   
+            Distributed array to be used in the basic Lanczos iteration   
+            for reverse communication.  The user should not use WORKD   
+            as temporary workspace during the iteration !!!!!!!!!!   
+            See Data Distribution Note in dsaupd.   
+
+    INFO    Integer.  (INPUT/OUTPUT)   
+            If INFO .EQ. 0, a randomly initial residual vector is used.   
+            If INFO .NE. 0, RESID contains the initial residual vector,   
+                            possibly from a previous run.   
+            Error flag on output.   
+            =     0: Normal return.   
+            =     1: All possible eigenvalues of OP has been found.   
+                     NP returns the size of the invariant subspace   
+                     spanning the operator OP.   
+            =     2: No shifts could be applied.   
+            =    -8: Error return from trid. eigenvalue calculation;   
+                     This should never happen.   
+            =    -9: Starting vector is zero.   
+            = -9999: Could not build an Lanczos factorization.   
+                     Size that was built in returned in NP.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+    3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall,   
+       1980.   
+    4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program",   
+       Computer Physics Communications, 53 (1989), pp 169-179.   
+    5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to   
+       Implement the Spectral Transformation", Math. Comp., 48 (1987),   
+       pp 663-673.   
+    6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos   
+       Algorithm for Solving Sparse Symmetric Generalized Eigenproblems",   
+       SIAM J. Matr. Anal. Apps.,  January (1993).   
+    7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines   
+       for Updating the QR decomposition", ACM TOMS, December 1990,   
+       Volume 16 Number 4, pp 369-377.   
+
+   \Routines called:   
+       dgetv0  ARPACK initial vector generation routine.   
+       dsaitr  ARPACK Lanczos factorization routine.   
+       dsapps  ARPACK application of implicit shifts routine.   
+       dsconv  ARPACK convergence of Ritz values routine.   
+       dseigt  ARPACK compute Ritz values and error bounds routine.   
+       dsgets  ARPACK reorder Ritz values and error bounds routine.   
+       dsortr  ARPACK sorting routine.   
+       ivout   ARPACK utility routine that prints integers.   
+       second  ARPACK utility routine for timing.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dcopy   Level 1 BLAS that copies one vector to another.   
+       ddot    Level 1 BLAS that computes the scalar product of two vectors.   
+       dnrm2   Level 1 BLAS that computes the norm of a vector.   
+       dscal   Level 1 BLAS that scales a vector.   
+       dswap   Level 1 BLAS that swaps two vectors.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       12/15/93: Version ' 2.4'   
+       xx/xx/95: Version ' 2.4'.  (R.B. Lehoucq)   
+
+   \SCCS Information: @(#)   
+   FILE: saup2.F   SID: 2.6   DATE OF SID: 8/16/96   RELEASE: 2   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsaup2_(integer *ido, char *bmat, integer *n, char *
+	which, integer *nev, integer *np, doublereal *tol, doublereal *resid, 
+	integer *mode, integer *iupd, integer *ishift, integer *mxiter, 
+	doublereal *v, integer *ldv, doublereal *h__, integer *ldh, 
+	doublereal *ritz, doublereal *bounds, doublereal *q, integer *ldq, 
+	doublereal *workl, integer *ipntr, doublereal *workd, integer *info)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, q_dim1, q_offset, v_dim1, v_offset, i__1, i__2, 
+	    i__3;
+    doublereal d__1, d__2, d__3;
+
+    /* Builtin functions */
+    double pow_dd(doublereal *, doublereal *);
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+    /* Subroutine */ int s_copy(char *, char *, ftnlen, ftnlen);
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer j;
+    real t0, t1, t2, t3;
+    integer kp[3];
+    IGRAPH_F77_SAVE integer np0;
+    integer nbx = 0;
+    IGRAPH_F77_SAVE integer nev0;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    IGRAPH_F77_SAVE doublereal eps23;
+    integer ierr;
+    IGRAPH_F77_SAVE integer iter;
+    doublereal temp;
+    integer nevd2;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    IGRAPH_F77_SAVE logical getv0;
+    integer nevm2;
+    IGRAPH_F77_SAVE logical cnorm;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdswap_(integer *, doublereal *, integer 
+	    *, doublereal *, integer *);
+    IGRAPH_F77_SAVE integer nconv;
+    IGRAPH_F77_SAVE logical initv;
+    IGRAPH_F77_SAVE doublereal rnorm;
+    real tmvbx = 0.0;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen), igraphivout_(integer *, integer *, integer *
+	    , integer *, char *, ftnlen), igraphdgetv0_(integer *, char *, integer *
+	    , logical *, integer *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *);
+    integer msaup2 = 0;
+    real tsaup2;
+    extern doublereal igraphdlamch_(char *);
+    integer nevbef;
+    extern /* Subroutine */ int igraphsecond_(real *);
+    integer logfil, ndigit;
+    extern /* Subroutine */ int igraphdseigt_(doublereal *, integer *, doublereal *,
+	     integer *, doublereal *, doublereal *, doublereal *, integer *);
+    IGRAPH_F77_SAVE logical update;
+    extern /* Subroutine */ int igraphdsaitr_(integer *, char *, integer *, integer 
+	    *, integer *, integer *, doublereal *, doublereal *, doublereal *,
+	     integer *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *), igraphdsgets_(integer *, char *, integer *, integer 
+	    *, doublereal *, doublereal *, doublereal *), igraphdsapps_(
+	    integer *, integer *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *), igraphdsconv_(integer *, doublereal *, 
+	    doublereal *, doublereal *, integer *);
+    IGRAPH_F77_SAVE logical ushift;
+    char wprime[2];
+    IGRAPH_F77_SAVE integer msglvl;
+    integer nptemp;
+    extern /* Subroutine */ int igraphdsortr_(char *, logical *, integer *, 
+	    doublereal *, doublereal *);
+    IGRAPH_F77_SAVE integer kplusp;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    --workl;
+    --bounds;
+    --ritz;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+    q_dim1 = *ldq;
+    q_offset = 1 + q_dim1;
+    q -= q_offset;
+    --ipntr;
+
+    /* Function Body */
+    if (*ido == 0) {
+
+/*        %-------------------------------%   
+          | Initialize timing statistics  |   
+          | & message level for debugging |   
+          %-------------------------------% */
+
+	igraphsecond_(&t0);
+	msglvl = msaup2;
+
+/*        %---------------------------------%   
+          | Set machine dependent constant. |   
+          %---------------------------------% */
+
+	eps23 = igraphdlamch_("Epsilon-Machine");
+	eps23 = pow_dd(&eps23, &c_b3);
+
+/*        %-------------------------------------%   
+          | nev0 and np0 are integer variables  |   
+          | hold the initial values of NEV & NP |   
+          %-------------------------------------% */
+
+	nev0 = *nev;
+	np0 = *np;
+
+/*        %-------------------------------------%   
+          | kplusp is the bound on the largest  |   
+          |        Lanczos factorization built. |   
+          | nconv is the current number of      |   
+          |        "converged" eigenvlues.      |   
+          | iter is the counter on the current  |   
+          |      iteration step.                |   
+          %-------------------------------------% */
+
+	kplusp = nev0 + np0;
+	nconv = 0;
+	iter = 0;
+
+/*        %--------------------------------------------%   
+          | Set flags for computing the first NEV steps |   
+          | of the Lanczos factorization.              |   
+          %--------------------------------------------% */
+
+	getv0 = TRUE_;
+	update = FALSE_;
+	ushift = FALSE_;
+	cnorm = FALSE_;
+
+	if (*info != 0) {
+
+/*        %--------------------------------------------%   
+          | User provides the initial residual vector. |   
+          %--------------------------------------------% */
+
+	    initv = TRUE_;
+	    *info = 0;
+	} else {
+	    initv = FALSE_;
+	}
+    }
+
+/*     %---------------------------------------------%   
+       | Get a possibly random starting vector and   |   
+       | force it into the range of the operator OP. |   
+       %---------------------------------------------%   
+
+   L10: */
+
+    if (getv0) {
+	igraphdgetv0_(ido, bmat, &c__1, &initv, n, &c__1, &v[v_offset], ldv, &resid[
+		1], &rnorm, &ipntr[1], &workd[1], info);
+
+	if (*ido != 99) {
+	    goto L9000;
+	}
+
+	if (rnorm == 0.) {
+
+/*           %-----------------------------------------%   
+             | The initial vector is zero. Error exit. |   
+             %-----------------------------------------% */
+
+	    *info = -9;
+	    goto L1200;
+	}
+	getv0 = FALSE_;
+	*ido = 0;
+    }
+
+/*     %------------------------------------------------------------%   
+       | Back from reverse communication: continue with update step |   
+       %------------------------------------------------------------% */
+
+    if (update) {
+	goto L20;
+    }
+
+/*     %-------------------------------------------%   
+       | Back from computing user specified shifts |   
+       %-------------------------------------------% */
+
+    if (ushift) {
+	goto L50;
+    }
+
+/*     %-------------------------------------%   
+       | Back from computing residual norm   |   
+       | at the end of the current iteration |   
+       %-------------------------------------% */
+
+    if (cnorm) {
+	goto L100;
+    }
+
+/*     %----------------------------------------------------------%   
+       | Compute the first NEV steps of the Lanczos factorization |   
+       %----------------------------------------------------------% */
+
+    igraphdsaitr_(ido, bmat, n, &c__0, &nev0, mode, &resid[1], &rnorm, &v[v_offset],
+	     ldv, &h__[h_offset], ldh, &ipntr[1], &workd[1], info);
+
+/*     %---------------------------------------------------%   
+       | ido .ne. 99 implies use of reverse communication  |   
+       | to compute operations involving OP and possibly B |   
+       %---------------------------------------------------% */
+
+    if (*ido != 99) {
+	goto L9000;
+    }
+
+    if (*info > 0) {
+
+/*        %-----------------------------------------------------%   
+          | dsaitr was unable to build an Lanczos factorization |   
+          | of length NEV0. INFO is returned with the size of   |   
+          | the factorization built. Exit main loop.            |   
+          %-----------------------------------------------------% */
+
+	*np = *info;
+	*mxiter = iter;
+	*info = -9999;
+	goto L1200;
+    }
+
+/*     %--------------------------------------------------------------%   
+       |                                                              |   
+       |           M A I N  LANCZOS  I T E R A T I O N  L O O P       |   
+       |           Each iteration implicitly restarts the Lanczos     |   
+       |           factorization in place.                            |   
+       |                                                              |   
+       %--------------------------------------------------------------% */
+
+L1000:
+
+    ++iter;
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, &iter, &ndigit, "_saup2: **** Start of major "
+		"iteration number ****", (ftnlen)49);
+    }
+    if (msglvl > 1) {
+	igraphivout_(&logfil, &c__1, nev, &ndigit, "_saup2: The length of the curr"
+		"ent Lanczos factorization", (ftnlen)55);
+	igraphivout_(&logfil, &c__1, np, &ndigit, "_saup2: Extend the Lanczos fact"
+		"orization by", (ftnlen)43);
+    }
+
+/*        %------------------------------------------------------------%   
+          | Compute NP additional steps of the Lanczos factorization. |   
+          %------------------------------------------------------------% */
+
+    *ido = 0;
+L20:
+    update = TRUE_;
+
+    igraphdsaitr_(ido, bmat, n, nev, np, mode, &resid[1], &rnorm, &v[v_offset], ldv,
+	     &h__[h_offset], ldh, &ipntr[1], &workd[1], info);
+
+/*        %---------------------------------------------------%   
+          | ido .ne. 99 implies use of reverse communication  |   
+          | to compute operations involving OP and possibly B |   
+          %---------------------------------------------------% */
+
+    if (*ido != 99) {
+	goto L9000;
+    }
+
+    if (*info > 0) {
+
+/*           %-----------------------------------------------------%   
+             | dsaitr was unable to build an Lanczos factorization |   
+             | of length NEV0+NP0. INFO is returned with the size  |   
+             | of the factorization built. Exit main loop.         |   
+             %-----------------------------------------------------% */
+
+	*np = *info;
+	*mxiter = iter;
+	*info = -9999;
+	goto L1200;
+    }
+    update = FALSE_;
+
+    if (msglvl > 1) {
+	igraphdvout_(&logfil, &c__1, &rnorm, &ndigit, "_saup2: Current B-norm of r"
+		"esidual for factorization", (ftnlen)52);
+    }
+
+/*        %--------------------------------------------------------%   
+          | Compute the eigenvalues and corresponding error bounds |   
+          | of the current symmetric tridiagonal matrix.           |   
+          %--------------------------------------------------------% */
+
+    igraphdseigt_(&rnorm, &kplusp, &h__[h_offset], ldh, &ritz[1], &bounds[1], &
+	    workl[1], &ierr);
+
+    if (ierr != 0) {
+	*info = -8;
+	goto L1200;
+    }
+
+/*        %----------------------------------------------------%   
+          | Make a copy of eigenvalues and corresponding error |   
+          | bounds obtained from _seigt.                       |   
+          %----------------------------------------------------% */
+
+    igraphdcopy_(&kplusp, &ritz[1], &c__1, &workl[kplusp + 1], &c__1);
+    igraphdcopy_(&kplusp, &bounds[1], &c__1, &workl[(kplusp << 1) + 1], &c__1);
+
+/*        %---------------------------------------------------%   
+          | Select the wanted Ritz values and their bounds    |   
+          | to be used in the convergence test.               |   
+          | The selection is based on the requested number of |   
+          | eigenvalues instead of the current NEV and NP to  |   
+          | prevent possible misconvergence.                  |   
+          | * Wanted Ritz values := RITZ(NP+1:NEV+NP)         |   
+          | * Shifts := RITZ(1:NP) := WORKL(1:NP)             |   
+          %---------------------------------------------------% */
+
+    *nev = nev0;
+    *np = np0;
+    igraphdsgets_(ishift, which, nev, np, &ritz[1], &bounds[1], &workl[1]);
+
+/*        %-------------------%   
+          | Convergence test. |   
+          %-------------------% */
+
+    igraphdcopy_(nev, &bounds[*np + 1], &c__1, &workl[*np + 1], &c__1);
+    igraphdsconv_(nev, &ritz[*np + 1], &workl[*np + 1], tol, &nconv);
+
+    if (msglvl > 2) {
+	kp[0] = *nev;
+	kp[1] = *np;
+	kp[2] = nconv;
+	igraphivout_(&logfil, &c__3, kp, &ndigit, "_saup2: NEV, NP, NCONV are", (
+		ftnlen)26);
+	igraphdvout_(&logfil, &kplusp, &ritz[1], &ndigit, "_saup2: The eigenvalues"
+		" of H", (ftnlen)28);
+	igraphdvout_(&logfil, &kplusp, &bounds[1], &ndigit, "_saup2: Ritz estimate"
+		"s of the current NCV Ritz values", (ftnlen)53);
+    }
+
+/*        %---------------------------------------------------------%   
+          | Count the number of unwanted Ritz values that have zero |   
+          | Ritz estimates. If any Ritz estimates are equal to zero |   
+          | then a leading block of H of order equal to at least    |   
+          | the number of Ritz values with zero Ritz estimates has  |   
+          | split off. None of these Ritz values may be removed by  |   
+          | shifting. Decrease NP the number of shifts to apply. If |   
+          | no shifts may be applied, then prepare to exit          |   
+          %---------------------------------------------------------% */
+
+    nptemp = *np;
+    i__1 = nptemp;
+    for (j = 1; j <= i__1; ++j) {
+	if (bounds[j] == 0.) {
+	    --(*np);
+	    ++(*nev);
+	}
+/* L30: */
+    }
+
+    if (nconv >= nev0 || iter > *mxiter || *np == 0) {
+
+/*           %------------------------------------------------%   
+             | Prepare to exit. Put the converged Ritz values |   
+             | and corresponding bounds in RITZ(1:NCONV) and  |   
+             | BOUNDS(1:NCONV) respectively. Then sort. Be    |   
+             | careful when NCONV > NP since we don't want to |   
+             | swap overlapping locations.                    |   
+             %------------------------------------------------% */
+
+	if (s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*              %-----------------------------------------------------%   
+                | Both ends of the spectrum are requested.            |   
+                | Sort the eigenvalues into algebraically decreasing  |   
+                | order first then swap low end of the spectrum next  |   
+                | to high end in appropriate locations.               |   
+                | NOTE: when np < floor(nev/2) be careful not to swap |   
+                | overlapping locations.                              |   
+                %-----------------------------------------------------% */
+
+	    s_copy(wprime, "SA", (ftnlen)2, (ftnlen)2);
+	    igraphdsortr_(wprime, &c_true, &kplusp, &ritz[1], &bounds[1])
+		    ;
+	    nevd2 = *nev / 2;
+	    nevm2 = *nev - nevd2;
+	    if (*nev > 1) {
+		i__1 = min(nevd2,*np);
+/* Computing MAX */
+		i__2 = kplusp - nevd2 + 1, i__3 = kplusp - *np + 1;
+		igraphdswap_(&i__1, &ritz[nevm2 + 1], &c__1, &ritz[max(i__2,i__3)], 
+			&c__1);
+		i__1 = min(nevd2,*np);
+/* Computing MAX */
+		i__2 = kplusp - nevd2 + 1, i__3 = kplusp - *np;
+		igraphdswap_(&i__1, &bounds[nevm2 + 1], &c__1, &bounds[max(i__2,
+			i__3) + 1], &c__1);
+	    }
+
+	} else {
+
+/*              %--------------------------------------------------%   
+                | LM, SM, LA, SA case.                             |   
+                | Sort the eigenvalues of H into the an order that |   
+                | is opposite to WHICH, and apply the resulting    |   
+                | order to BOUNDS.  The eigenvalues are sorted so  |   
+                | that the wanted part are always within the first |   
+                | NEV locations.                                   |   
+                %--------------------------------------------------% */
+
+	    if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+		s_copy(wprime, "SM", (ftnlen)2, (ftnlen)2);
+	    }
+	    if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+		s_copy(wprime, "LM", (ftnlen)2, (ftnlen)2);
+	    }
+	    if (s_cmp(which, "LA", (ftnlen)2, (ftnlen)2) == 0) {
+		s_copy(wprime, "SA", (ftnlen)2, (ftnlen)2);
+	    }
+	    if (s_cmp(which, "SA", (ftnlen)2, (ftnlen)2) == 0) {
+		s_copy(wprime, "LA", (ftnlen)2, (ftnlen)2);
+	    }
+
+	    igraphdsortr_(wprime, &c_true, &kplusp, &ritz[1], &bounds[1])
+		    ;
+
+	}
+
+/*           %--------------------------------------------------%   
+             | Scale the Ritz estimate of each Ritz value       |   
+             | by 1 / max(eps23,magnitude of the Ritz value).   |   
+             %--------------------------------------------------% */
+
+	i__1 = nev0;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MAX */
+	    d__2 = eps23, d__3 = (d__1 = ritz[j], abs(d__1));
+	    temp = max(d__2,d__3);
+	    bounds[j] /= temp;
+/* L35: */
+	}
+
+/*           %----------------------------------------------------%   
+             | Sort the Ritz values according to the scaled Ritz  |   
+             | esitmates.  This will push all the converged ones  |   
+             | towards the front of ritzr, ritzi, bounds          |   
+             | (in the case when NCONV < NEV.)                    |   
+             %----------------------------------------------------% */
+
+	s_copy(wprime, "LA", (ftnlen)2, (ftnlen)2);
+	igraphdsortr_(wprime, &c_true, &nev0, &bounds[1], &ritz[1]);
+
+/*           %----------------------------------------------%   
+             | Scale the Ritz estimate back to its original |   
+             | value.                                       |   
+             %----------------------------------------------% */
+
+	i__1 = nev0;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing MAX */
+	    d__2 = eps23, d__3 = (d__1 = ritz[j], abs(d__1));
+	    temp = max(d__2,d__3);
+	    bounds[j] *= temp;
+/* L40: */
+	}
+
+/*           %--------------------------------------------------%   
+             | Sort the "converged" Ritz values again so that   |   
+             | the "threshold" values and their associated Ritz |   
+             | estimates appear at the appropriate position in  |   
+             | ritz and bound.                                  |   
+             %--------------------------------------------------% */
+
+	if (s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*              %------------------------------------------------%   
+                | Sort the "converged" Ritz values in increasing |   
+                | order.  The "threshold" values are in the      |   
+                | middle.                                        |   
+                %------------------------------------------------% */
+
+	    s_copy(wprime, "LA", (ftnlen)2, (ftnlen)2);
+	    igraphdsortr_(wprime, &c_true, &nconv, &ritz[1], &bounds[1]);
+
+	} else {
+
+/*              %----------------------------------------------%   
+                | In LM, SM, LA, SA case, sort the "converged" |   
+                | Ritz values according to WHICH so that the   |   
+                | "threshold" value appears at the front of    |   
+                | ritz.                                        |   
+                %----------------------------------------------% */
+	    igraphdsortr_(which, &c_true, &nconv, &ritz[1], &bounds[1]);
+
+	}
+
+/*           %------------------------------------------%   
+             |  Use h( 1,1 ) as storage to communicate  |   
+             |  rnorm to _seupd if needed               |   
+             %------------------------------------------% */
+
+	h__[h_dim1 + 1] = rnorm;
+
+	if (msglvl > 1) {
+	    igraphdvout_(&logfil, &kplusp, &ritz[1], &ndigit, "_saup2: Sorted Ritz"
+		    " values.", (ftnlen)27);
+	    igraphdvout_(&logfil, &kplusp, &bounds[1], &ndigit, "_saup2: Sorted ri"
+		    "tz estimates.", (ftnlen)30);
+	}
+
+/*           %------------------------------------%   
+             | Max iterations have been exceeded. |   
+             %------------------------------------% */
+
+	if (iter > *mxiter && nconv < *nev) {
+	    *info = 1;
+	}
+
+/*           %---------------------%   
+             | No shifts to apply. |   
+             %---------------------% */
+
+	if (*np == 0 && nconv < nev0) {
+	    *info = 2;
+	}
+
+	*np = nconv;
+	goto L1100;
+
+    } else if (nconv < *nev && *ishift == 1) {
+
+/*           %---------------------------------------------------%   
+             | Do not have all the requested eigenvalues yet.    |   
+             | To prevent possible stagnation, adjust the number |   
+             | of Ritz values and the shifts.                    |   
+             %---------------------------------------------------% */
+
+	nevbef = *nev;
+/* Computing MIN */
+	i__1 = nconv, i__2 = *np / 2;
+	*nev += min(i__1,i__2);
+	if (*nev == 1 && kplusp >= 6) {
+	    *nev = kplusp / 2;
+	} else if (*nev == 1 && kplusp > 2) {
+	    *nev = 2;
+	}
+	*np = kplusp - *nev;
+
+/*           %---------------------------------------%   
+             | If the size of NEV was just increased |   
+             | resort the eigenvalues.               |   
+             %---------------------------------------% */
+
+	if (nevbef < *nev) {
+	    igraphdsgets_(ishift, which, nev, np, &ritz[1], &bounds[1], &workl[1]);
+	}
+
+    }
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, &nconv, &ndigit, "_saup2: no. of \"converge"
+		"d\" Ritz values at this iter.", (ftnlen)52);
+	if (msglvl > 1) {
+	    kp[0] = *nev;
+	    kp[1] = *np;
+	    igraphivout_(&logfil, &c__2, kp, &ndigit, "_saup2: NEV and NP are", (
+		    ftnlen)22);
+	    igraphdvout_(&logfil, nev, &ritz[*np + 1], &ndigit, "_saup2: \"wante"
+		    "d\" Ritz values.", (ftnlen)29);
+	    igraphdvout_(&logfil, nev, &bounds[*np + 1], &ndigit, "_saup2: Ritz es"
+		    "timates of the \"wanted\" values ", (ftnlen)46);
+	}
+    }
+
+    if (*ishift == 0) {
+
+/*           %-----------------------------------------------------%   
+             | User specified shifts: reverse communication to     |   
+             | compute the shifts. They are returned in the first  |   
+             | NP locations of WORKL.                              |   
+             %-----------------------------------------------------% */
+
+	ushift = TRUE_;
+	*ido = 3;
+	goto L9000;
+    }
+
+L50:
+
+/*        %------------------------------------%   
+          | Back from reverse communication;   |   
+          | User specified shifts are returned |   
+          | in WORKL(1:*NP)                   |   
+          %------------------------------------% */
+
+    ushift = FALSE_;
+
+
+/*        %---------------------------------------------------------%   
+          | Move the NP shifts to the first NP locations of RITZ to |   
+          | free up WORKL.  This is for the non-exact shift case;   |   
+          | in the exact shift case, dsgets already handles this.   |   
+          %---------------------------------------------------------% */
+
+    if (*ishift == 0) {
+	igraphdcopy_(np, &workl[1], &c__1, &ritz[1], &c__1);
+    }
+
+    if (msglvl > 2) {
+	igraphivout_(&logfil, &c__1, np, &ndigit, "_saup2: The number of shifts to"
+		" apply ", (ftnlen)38);
+	igraphdvout_(&logfil, np, &workl[1], &ndigit, "_saup2: shifts selected", (
+		ftnlen)23);
+	if (*ishift == 1) {
+	    igraphdvout_(&logfil, np, &bounds[1], &ndigit, "_saup2: corresponding "
+		    "Ritz estimates", (ftnlen)36);
+	}
+    }
+
+/*        %---------------------------------------------------------%   
+          | Apply the NP0 implicit shifts by QR bulge chasing.      |   
+          | Each shift is applied to the entire tridiagonal matrix. |   
+          | The first 2*N locations of WORKD are used as workspace. |   
+          | After dsapps is done, we have a Lanczos                 |   
+          | factorization of length NEV.                            |   
+          %---------------------------------------------------------% */
+
+    igraphdsapps_(n, nev, np, &ritz[1], &v[v_offset], ldv, &h__[h_offset], ldh, &
+	    resid[1], &q[q_offset], ldq, &workd[1]);
+
+/*        %---------------------------------------------%   
+          | Compute the B-norm of the updated residual. |   
+          | Keep B*RESID in WORKD(1:N) to be used in    |   
+          | the first step of the next call to dsaitr.  |   
+          %---------------------------------------------% */
+
+    cnorm = TRUE_;
+    igraphsecond_(&t2);
+    if (*(unsigned char *)bmat == 'G') {
+	++nbx;
+	igraphdcopy_(n, &resid[1], &c__1, &workd[*n + 1], &c__1);
+	ipntr[1] = *n + 1;
+	ipntr[2] = 1;
+	*ido = 2;
+
+/*           %----------------------------------%   
+             | Exit in order to compute B*RESID |   
+             %----------------------------------% */
+
+	goto L9000;
+    } else if (*(unsigned char *)bmat == 'I') {
+	igraphdcopy_(n, &resid[1], &c__1, &workd[1], &c__1);
+    }
+
+L100:
+
+/*        %----------------------------------%   
+          | Back from reverse communication; |   
+          | WORKD(1:N) := B*RESID            |   
+          %----------------------------------% */
+
+    if (*(unsigned char *)bmat == 'G') {
+	igraphsecond_(&t3);
+	tmvbx += t3 - t2;
+    }
+
+    if (*(unsigned char *)bmat == 'G') {
+	rnorm = igraphddot_(n, &resid[1], &c__1, &workd[1], &c__1);
+	rnorm = sqrt((abs(rnorm)));
+    } else if (*(unsigned char *)bmat == 'I') {
+	rnorm = igraphdnrm2_(n, &resid[1], &c__1);
+    }
+    cnorm = FALSE_;
+/* L130: */
+
+    if (msglvl > 2) {
+	igraphdvout_(&logfil, &c__1, &rnorm, &ndigit, "_saup2: B-norm of residual "
+		"for NEV factorization", (ftnlen)48);
+	igraphdvout_(&logfil, nev, &h__[(h_dim1 << 1) + 1], &ndigit, "_saup2: main"
+		" diagonal of compressed H matrix", (ftnlen)44);
+	i__1 = *nev - 1;
+	igraphdvout_(&logfil, &i__1, &h__[h_dim1 + 2], &ndigit, "_saup2: subdiagon"
+		"al of compressed H matrix", (ftnlen)42);
+    }
+
+    goto L1000;
+
+/*     %---------------------------------------------------------------%   
+       |                                                               |   
+       |  E N D     O F     M A I N     I T E R A T I O N     L O O P  |   
+       |                                                               |   
+       %---------------------------------------------------------------% */
+
+L1100:
+
+    *mxiter = iter;
+    *nev = nconv;
+
+L1200:
+    *ido = 99;
+
+/*     %------------%   
+       | Error exit |   
+       %------------% */
+
+    igraphsecond_(&t1);
+    tsaup2 = t1 - t0;
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dsaup2 |   
+       %---------------% */
+
+} /* igraphdsaup2_ */
+
diff --git a/igraph/src/dsaupd.c b/igraph/src/dsaupd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsaupd.c
@@ -0,0 +1,792 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsaupd   
+
+   \Description:   
+
+    Reverse communication interface for the Implicitly Restarted Arnoldi   
+    Iteration.  For symmetric problems this reduces to a variant of the Lanczos   
+    method.  This method has been designed to compute approximations to a   
+    few eigenpairs of a linear operator OP that is real and symmetric   
+    with respect to a real positive semi-definite symmetric matrix B,   
+    i.e.   
+
+         B*OP = (OP')*B.   
+
+    Another way to express this condition is   
+
+         < x,OPy > = < OPx,y >  where < z,w > = z'Bw  .   
+
+    In the standard eigenproblem B is the identity matrix.   
+    ( A' denotes transpose of A)   
+
+    The computed approximate eigenvalues are called Ritz values and   
+    the corresponding approximate eigenvectors are called Ritz vectors.   
+
+    dsaupd is usually called iteratively to solve one of the   
+    following problems:   
+
+    Mode 1:  A*x = lambda*x, A symmetric   
+             ===> OP = A  and  B = I.   
+
+    Mode 2:  A*x = lambda*M*x, A symmetric, M symmetric positive definite   
+             ===> OP = inv[M]*A  and  B = M.   
+             ===> (If M can be factored see remark 3 below)   
+
+    Mode 3:  K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite   
+             ===> OP = (inv[K - sigma*M])*M  and  B = M.   
+             ===> Shift-and-Invert mode   
+
+    Mode 4:  K*x = lambda*KG*x, K symmetric positive semi-definite,   
+             KG symmetric indefinite   
+             ===> OP = (inv[K - sigma*KG])*K  and  B = K.   
+             ===> Buckling mode   
+
+    Mode 5:  A*x = lambda*M*x, A symmetric, M symmetric positive semi-definite   
+             ===> OP = inv[A - sigma*M]*[A + sigma*M]  and  B = M.   
+             ===> Cayley transformed mode   
+
+    NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v   
+          should be accomplished either by a direct method   
+          using a sparse matrix factorization and solving   
+
+             [A - sigma*M]*w = v  or M*w = v,   
+
+          or through an iterative method for solving these   
+          systems.  If an iterative method is used, the   
+          convergence test must be more stringent than   
+          the accuracy requirements for the eigenvalue   
+          approximations.   
+
+   \Usage:   
+    call dsaupd   
+       ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM,   
+         IPNTR, WORKD, WORKL, LWORKL, INFO )   
+
+   \Arguments   
+    IDO     Integer.  (INPUT/OUTPUT)   
+            Reverse communication flag.  IDO must be zero on the first   
+            call to dsaupd.  IDO will be set internally to   
+            indicate the type of operation to be performed.  Control is   
+            then given back to the calling routine which has the   
+            responsibility to carry out the requested operation and call   
+            dsaupd with the result.  The operand is given in   
+            WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)).   
+            (If Mode = 2 see remark 5 below)   
+            -------------------------------------------------------------   
+            IDO =  0: first call to the reverse communication interface   
+            IDO = -1: compute  Y = OP * X  where   
+                      IPNTR(1) is the pointer into WORKD for X,   
+                      IPNTR(2) is the pointer into WORKD for Y.   
+                      This is for the initialization phase to force the   
+                      starting vector into the range of OP.   
+            IDO =  1: compute  Y = OP * X where   
+                      IPNTR(1) is the pointer into WORKD for X,   
+                      IPNTR(2) is the pointer into WORKD for Y.   
+                      In mode 3,4 and 5, the vector B * X is already   
+                      available in WORKD(ipntr(3)).  It does not   
+                      need to be recomputed in forming OP * X.   
+            IDO =  2: compute  Y = B * X  where   
+                      IPNTR(1) is the pointer into WORKD for X,   
+                      IPNTR(2) is the pointer into WORKD for Y.   
+            IDO =  3: compute the IPARAM(8) shifts where   
+                      IPNTR(11) is the pointer into WORKL for   
+                      placing the shifts. See remark 6 below.   
+            IDO = 99: done   
+            -------------------------------------------------------------   
+
+    BMAT    Character*1.  (INPUT)   
+            BMAT specifies the type of the matrix B that defines the   
+            semi-inner product for the operator OP.   
+            B = 'I' -> standard eigenvalue problem A*x = lambda*x   
+            B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x   
+
+    N       Integer.  (INPUT)   
+            Dimension of the eigenproblem.   
+
+    WHICH   Character*2.  (INPUT)   
+            Specify which of the Ritz values of OP to compute.   
+
+            'LA' - compute the NEV largest (algebraic) eigenvalues.   
+            'SA' - compute the NEV smallest (algebraic) eigenvalues.   
+            'LM' - compute the NEV largest (in magnitude) eigenvalues.   
+            'SM' - compute the NEV smallest (in magnitude) eigenvalues.   
+            'BE' - compute NEV eigenvalues, half from each end of the   
+                   spectrum.  When NEV is odd, compute one more from the   
+                   high end than from the low end.   
+             (see remark 1 below)   
+
+    NEV     Integer.  (INPUT)   
+            Number of eigenvalues of OP to be computed. 0 < NEV < N.   
+
+    TOL     Double precision scalar.  (INPUT)   
+            Stopping criterion: the relative accuracy of the Ritz value   
+            is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)).   
+            If TOL .LE. 0. is passed a default is set:   
+            DEFAULT = DLAMCH('EPS')  (machine precision as computed   
+                      by the LAPACK auxiliary subroutine DLAMCH).   
+
+    RESID   Double precision array of length N.  (INPUT/OUTPUT)   
+            On INPUT:   
+            If INFO .EQ. 0, a random initial residual vector is used.   
+            If INFO .NE. 0, RESID contains the initial residual vector,   
+                            possibly from a previous run.   
+            On OUTPUT:   
+            RESID contains the final residual vector.   
+
+    NCV     Integer.  (INPUT)   
+            Number of columns of the matrix V (less than or equal to N).   
+            This will indicate how many Lanczos vectors are generated   
+            at each iteration.  After the startup phase in which NEV   
+            Lanczos vectors are generated, the algorithm generates   
+            NCV-NEV Lanczos vectors at each subsequent update iteration.   
+            Most of the cost in generating each Lanczos vector is in the   
+            matrix-vector product OP*x. (See remark 4 below).   
+
+    V       Double precision N by NCV array.  (OUTPUT)   
+            The NCV columns of V contain the Lanczos basis vectors.   
+
+    LDV     Integer.  (INPUT)   
+            Leading dimension of V exactly as declared in the calling   
+            program.   
+
+    IPARAM  Integer array of length 11.  (INPUT/OUTPUT)   
+            IPARAM(1) = ISHIFT: method for selecting the implicit shifts.   
+            The shifts selected at each iteration are used to restart   
+            the Arnoldi iteration in an implicit fashion.   
+            -------------------------------------------------------------   
+            ISHIFT = 0: the shifts are provided by the user via   
+                        reverse communication.  The NCV eigenvalues of   
+                        the current tridiagonal matrix T are returned in   
+                        the part of WORKL array corresponding to RITZ.   
+                        See remark 6 below.   
+            ISHIFT = 1: exact shifts with respect to the reduced   
+                        tridiagonal matrix T.  This is equivalent to   
+                        restarting the iteration with a starting vector   
+                        that is a linear combination of Ritz vectors   
+                        associated with the "wanted" Ritz values.   
+            -------------------------------------------------------------   
+
+            IPARAM(2) = LEVEC   
+            No longer referenced. See remark 2 below.   
+
+            IPARAM(3) = MXITER   
+            On INPUT:  maximum number of Arnoldi update iterations allowed.   
+            On OUTPUT: actual number of Arnoldi update iterations taken.   
+
+            IPARAM(4) = NB: blocksize to be used in the recurrence.   
+            The code currently works only for NB = 1.   
+
+            IPARAM(5) = NCONV: number of "converged" Ritz values.   
+            This represents the number of Ritz values that satisfy   
+            the convergence criterion.   
+
+            IPARAM(6) = IUPD   
+            No longer referenced. Implicit restarting is ALWAYS used.   
+
+            IPARAM(7) = MODE   
+            On INPUT determines what type of eigenproblem is being solved.   
+            Must be 1,2,3,4,5; See under \Description of dsaupd for the   
+            five modes available.   
+
+            IPARAM(8) = NP   
+            When ido = 3 and the user provides shifts through reverse   
+            communication (IPARAM(1)=0), dsaupd returns NP, the number   
+            of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark   
+            6 below.   
+
+            IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO,   
+            OUTPUT: NUMOP  = total number of OP*x operations,   
+                    NUMOPB = total number of B*x operations if BMAT='G',   
+                    NUMREO = total number of steps of re-orthogonalization.   
+
+    IPNTR   Integer array of length 11.  (OUTPUT)   
+            Pointer to mark the starting locations in the WORKD and WORKL   
+            arrays for matrices/vectors used by the Lanczos iteration.   
+            -------------------------------------------------------------   
+            IPNTR(1): pointer to the current operand vector X in WORKD.   
+            IPNTR(2): pointer to the current result vector Y in WORKD.   
+            IPNTR(3): pointer to the vector B * X in WORKD when used in   
+                      the shift-and-invert mode.   
+            IPNTR(4): pointer to the next available location in WORKL   
+                      that is untouched by the program.   
+            IPNTR(5): pointer to the NCV by 2 tridiagonal matrix T in WORKL.   
+            IPNTR(6): pointer to the NCV RITZ values array in WORKL.   
+            IPNTR(7): pointer to the Ritz estimates in array WORKL associated   
+                      with the Ritz values located in RITZ in WORKL.   
+            IPNTR(11): pointer to the NP shifts in WORKL. See Remark 6 below.   
+
+            Note: IPNTR(8:10) is only referenced by dseupd. See Remark 2.   
+            IPNTR(8): pointer to the NCV RITZ values of the original system.   
+            IPNTR(9): pointer to the NCV corresponding error bounds.   
+            IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors   
+                       of the tridiagonal matrix T. Only referenced by   
+                       dseupd if RVEC = .TRUE. See Remarks.   
+            -------------------------------------------------------------   
+
+    WORKD   Double precision work array of length 3*N.  (REVERSE COMMUNICATION)   
+            Distributed array to be used in the basic Arnoldi iteration   
+            for reverse communication.  The user should not use WORKD   
+            as temporary workspace during the iteration. Upon termination   
+            WORKD(1:N) contains B*RESID(1:N). If the Ritz vectors are desired   
+            subroutine dseupd uses this output.   
+            See Data Distribution Note below.   
+
+    WORKL   Double precision work array of length LWORKL.  (OUTPUT/WORKSPACE)   
+            Private (replicated) array on each PE or array allocated on   
+            the front end.  See Data Distribution Note below.   
+
+    LWORKL  Integer.  (INPUT)   
+            LWORKL must be at least NCV**2 + 8*NCV .   
+
+    INFO    Integer.  (INPUT/OUTPUT)   
+            If INFO .EQ. 0, a randomly initial residual vector is used.   
+            If INFO .NE. 0, RESID contains the initial residual vector,   
+                            possibly from a previous run.   
+            Error flag on output.   
+            =  0: Normal exit.   
+            =  1: Maximum number of iterations taken.   
+                  All possible eigenvalues of OP has been found. IPARAM(5)   
+                  returns the number of wanted converged Ritz values.   
+            =  2: No longer an informational error. Deprecated starting   
+                  with release 2 of ARPACK.   
+            =  3: No shifts could be applied during a cycle of the   
+                  Implicitly restarted Arnoldi iteration. One possibility   
+                  is to increase the size of NCV relative to NEV.   
+                  See remark 4 below.   
+            = -1: N must be positive.   
+            = -2: NEV must be positive.   
+            = -3: NCV must be greater than NEV and less than or equal to N.   
+            = -4: The maximum number of Arnoldi update iterations allowed   
+                  must be greater than zero.   
+            = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.   
+            = -6: BMAT must be one of 'I' or 'G'.   
+            = -7: Length of private work array WORKL is not sufficient.   
+            = -8: Error return from trid. eigenvalue calculation;   
+                  Informatinal error from LAPACK routine dsteqr.   
+            = -9: Starting vector is zero.   
+            = -10: IPARAM(7) must be 1,2,3,4,5.   
+            = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable.   
+            = -12: IPARAM(1) must be equal to 0 or 1.   
+            = -13: NEV and WHICH = 'BE' are incompatable.   
+            = -9999: Could not build an Arnoldi factorization.   
+                     IPARAM(5) returns the size of the current Arnoldi   
+                     factorization. The user is advised to check that   
+                     enough workspace and array storage has been allocated.   
+
+
+   \Remarks   
+    1. The converged Ritz values are always returned in ascending   
+       algebraic order.  The computed Ritz values are approximate   
+       eigenvalues of OP.  The selection of WHICH should be made   
+       with this in mind when Mode = 3,4,5.  After convergence,   
+       approximate eigenvalues of the original problem may be obtained   
+       with the ARPACK subroutine dseupd.   
+
+    2. If the Ritz vectors corresponding to the converged Ritz values   
+       are needed, the user must call dseupd immediately following completion   
+       of dsaupd. This is new starting with version 2.1 of ARPACK.   
+
+    3. If M can be factored into a Cholesky factorization M = LL'   
+       then Mode = 2 should not be selected.  Instead one should use   
+       Mode = 1 with  OP = inv(L)*A*inv(L').  Appropriate triangular   
+       linear systems should be solved with L and L' rather   
+       than computing inverses.  After convergence, an approximate   
+       eigenvector z of the original problem is recovered by solving   
+       L'z = x  where x is a Ritz vector of OP.   
+
+    4. At present there is no a-priori analysis to guide the selection   
+       of NCV relative to NEV.  The only formal requrement is that NCV > NEV.   
+       However, it is recommended that NCV .ge. 2*NEV.  If many problems of   
+       the same type are to be solved, one should experiment with increasing   
+       NCV while keeping NEV fixed for a given test problem.  This will   
+       usually decrease the required number of OP*x operations but it   
+       also increases the work and storage required to maintain the orthogonal   
+       basis vectors.   The optimal "cross-over" with respect to CPU time   
+       is problem dependent and must be determined empirically.   
+
+    5. If IPARAM(7) = 2 then in the Reverse commuication interface the user   
+       must do the following. When IDO = 1, Y = OP * X is to be computed.   
+       When IPARAM(7) = 2 OP = inv(B)*A. After computing A*X the user   
+       must overwrite X with A*X. Y is then the solution to the linear set   
+       of equations B*Y = A*X.   
+
+    6. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the   
+       NP = IPARAM(8) shifts in locations:   
+       1   WORKL(IPNTR(11))   
+       2   WORKL(IPNTR(11)+1)   
+                          .   
+                          .   
+                          .   
+       NP  WORKL(IPNTR(11)+NP-1).   
+
+       The eigenvalues of the current tridiagonal matrix are located in   
+       WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1). They are in the   
+       order defined by WHICH. The associated Ritz estimates are located in   
+       WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1).   
+
+   -----------------------------------------------------------------------   
+
+   \Data Distribution Note:   
+
+    Fortran-D syntax:   
+    ================   
+    REAL       RESID(N), V(LDV,NCV), WORKD(3*N), WORKL(LWORKL)   
+    DECOMPOSE  D1(N), D2(N,NCV)   
+    ALIGN      RESID(I) with D1(I)   
+    ALIGN      V(I,J)   with D2(I,J)   
+    ALIGN      WORKD(I) with D1(I)     range (1:N)   
+    ALIGN      WORKD(I) with D1(I-N)   range (N+1:2*N)   
+    ALIGN      WORKD(I) with D1(I-2*N) range (2*N+1:3*N)   
+    DISTRIBUTE D1(BLOCK), D2(BLOCK,:)   
+    REPLICATED WORKL(LWORKL)   
+
+    Cray MPP syntax:   
+    ===============   
+    REAL       RESID(N), V(LDV,NCV), WORKD(N,3), WORKL(LWORKL)   
+    SHARED     RESID(BLOCK), V(BLOCK,:), WORKD(BLOCK,:)   
+    REPLICATED WORKL(LWORKL)   
+
+
+   \BeginLib   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+    3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall,   
+       1980.   
+    4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program",   
+       Computer Physics Communications, 53 (1989), pp 169-179.   
+    5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to   
+       Implement the Spectral Transformation", Math. Comp., 48 (1987),   
+       pp 663-673.   
+    6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos   
+       Algorithm for Solving Sparse Symmetric Generalized Eigenproblems",   
+       SIAM J. Matr. Anal. Apps.,  January (1993).   
+    7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines   
+       for Updating the QR decomposition", ACM TOMS, December 1990,   
+       Volume 16 Number 4, pp 369-377.   
+    8. R.B. Lehoucq, D.C. Sorensen, "Implementation of Some Spectral   
+       Transformations in a k-Step Arnoldi Method". In Preparation.   
+
+   \Routines called:   
+       dsaup2  ARPACK routine that implements the Implicitly Restarted   
+               Arnoldi Iteration.   
+       dstats  ARPACK routine that initialize timing and other statistics   
+               variables.   
+       ivout   ARPACK utility routine that prints integers.   
+       second  ARPACK utility routine for timing.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dlamch  LAPACK routine that determines machine constants.   
+
+   \Authors   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       12/15/93: Version ' 2.4'   
+
+   \SCCS Information: @(#)   
+   FILE: saupd.F   SID: 2.7   DATE OF SID: 8/27/96   RELEASE: 2   
+
+   \Remarks   
+       1. None   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsaupd_(integer *ido, char *bmat, integer *n, char *
+	which, integer *nev, doublereal *tol, doublereal *resid, integer *ncv,
+	 doublereal *v, integer *ldv, integer *iparam, integer *ipntr, 
+	doublereal *workd, doublereal *workl, integer *lworkl, integer *info)
+{
+    /* Format strings */
+    static char fmt_1000[] = "(//,5x,\002==================================="
+	    "=======\002,/5x,\002= Symmetric implicit Arnoldi update code "
+	    "=\002,/5x,\002= Version Number:\002,\002 2.4\002,19x,\002 =\002,"
+	    "/5x,\002= Version Date:  \002,\002 07/31/96\002,14x,\002 =\002,/"
+	    "5x,\002==========================================\002,/5x,\002= "
+	    "Summary of timing statistics           =\002,/5x,\002==========="
+	    "===============================\002,//)";
+    static char fmt_1100[] = "(5x,\002Total number update iterations        "
+	    "     = \002,i5,/5x,\002Total number of OP*x operations          "
+	    "  = \002,i5,/5x,\002Total number of B*x operations             = "
+	    "\002,i5,/5x,\002Total number of reorthogonalization steps  = "
+	    "\002,i5,/5x,\002Total number of iterative refinement steps = "
+	    "\002,i5,/5x,\002Total number of restart steps              = "
+	    "\002,i5,/5x,\002Total time in user OP*x operation          = "
+	    "\002,f12.6,/5x,\002Total time in user B*x operation           ="
+	    " \002,f12.6,/5x,\002Total time in Arnoldi update routine       = "
+	    "\002,f12.6,/5x,\002Total time in saup2 routine                ="
+	    " \002,f12.6,/5x,\002Total time in basic Arnoldi iteration loop = "
+	    "\002,f12.6,/5x,\002Total time in reorthogonalization phase    ="
+	    " \002,f12.6,/5x,\002Total time in (re)start vector generation  = "
+	    "\002,f12.6,/5x,\002Total time in trid eigenvalue subproblem   ="
+	    " \002,f12.6,/5x,\002Total time in getting the shifts           = "
+	    "\002,f12.6,/5x,\002Total time in applying the shifts          ="
+	    " \002,f12.6,/5x,\002Total time in convergence testing          = "
+	    "\002,f12.6)";
+
+    /* System generated locals */
+    integer v_dim1, v_offset, i__1, i__2;
+
+    /* Builtin functions */
+    integer s_cmp(char *, char *, ftnlen, ftnlen), s_wsfe(cilist *), e_wsfe(
+	    void), do_fio(integer *, char *, ftnlen);
+
+    /* Local variables */
+    integer j;
+    real t0, t1;
+    IGRAPH_F77_SAVE integer nb, ih, iq, np, iw, ldh, ldq;
+    integer nbx = 0;
+    IGRAPH_F77_SAVE integer nev0, mode, ierr, iupd, next;
+    integer nopx = 0;
+    IGRAPH_F77_SAVE integer ritz;
+    real tmvbx;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen), igraphivout_(integer *, integer *, integer *
+	    , integer *, char *, ftnlen), igraphdsaup2_(integer *, char *, integer *
+	    , char *, integer *, integer *, doublereal *, doublereal *, 
+	    integer *, integer *, integer *, integer *, doublereal *, integer 
+	    *, doublereal *, integer *, doublereal *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    real tgetv0, tsaup2;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphsecond_(real *);
+    integer logfil, ndigit;
+    IGRAPH_F77_SAVE integer ishift;
+    integer nitref, msaupd = 0;
+    IGRAPH_F77_SAVE integer bounds;
+    real titref, tseigt, tsaupd;
+    extern /* Subroutine */ int igraphdstats_(void);
+    IGRAPH_F77_SAVE integer msglvl;
+    real tsaitr = 0.0;
+    IGRAPH_F77_SAVE integer mxiter;
+    real tsgets, tsapps;
+    integer nrorth = 0;
+    real tsconv = 0.0;
+    integer nrstrt = 0;
+    real tmvopx = 0.0;
+
+    /* Fortran I/O blocks */
+    static cilist io___28 = { 0, 6, 0, fmt_1000, 0 };
+    static cilist io___29 = { 0, 6, 0, fmt_1100, 0 };
+
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    --iparam;
+    --ipntr;
+    --workl;
+
+    /* Function Body */
+    if (*ido == 0) {
+
+/*        %-------------------------------%   
+          | Initialize timing statistics  |   
+          | & message level for debugging |   
+          %-------------------------------% */
+
+	igraphdstats_();
+	igraphsecond_(&t0);
+	msglvl = msaupd;
+
+	ierr = 0;
+	ishift = iparam[1];
+	mxiter = iparam[3];
+	nb = iparam[4];
+
+/*        %--------------------------------------------%   
+          | Revision 2 performs only implicit restart. |   
+          %--------------------------------------------% */
+
+	iupd = 1;
+	mode = iparam[7];
+
+/*        %----------------%   
+          | Error checking |   
+          %----------------% */
+
+	if (*n <= 0) {
+	    ierr = -1;
+	} else if (*nev <= 0) {
+	    ierr = -2;
+	} else if (*ncv <= *nev || *ncv > *n) {
+	    ierr = -3;
+	}
+
+/*        %----------------------------------------------%   
+          | NP is the number of additional steps to      |   
+          | extend the length NEV Lanczos factorization. |   
+          %----------------------------------------------% */
+
+	np = *ncv - *nev;
+
+	if (mxiter <= 0) {
+	    ierr = -4;
+	}
+	if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, 
+		"SM", (ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, "LA", (
+		ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, "SA", (ftnlen)2, (
+		ftnlen)2) != 0 && s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) != 
+		0) {
+	    ierr = -5;
+	}
+	if (*(unsigned char *)bmat != 'I' && *(unsigned char *)bmat != 'G') {
+	    ierr = -6;
+	}
+
+/* Computing 2nd power */
+	i__1 = *ncv;
+	if (*lworkl < i__1 * i__1 + (*ncv << 3)) {
+	    ierr = -7;
+	}
+	if (mode < 1 || mode > 5) {
+	    ierr = -10;
+	} else if (mode == 1 && *(unsigned char *)bmat == 'G') {
+	    ierr = -11;
+	} else if (ishift < 0 || ishift > 1) {
+	    ierr = -12;
+	} else if (*nev == 1 && s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) == 0)
+		 {
+	    ierr = -13;
+	}
+
+/*        %------------%   
+          | Error Exit |   
+          %------------% */
+
+	if (ierr != 0) {
+	    *info = ierr;
+	    *ido = 99;
+	    goto L9000;
+	}
+
+/*        %------------------------%   
+          | Set default parameters |   
+          %------------------------% */
+
+	if (nb <= 0) {
+	    nb = 1;
+	}
+	if (*tol <= 0.) {
+	    *tol = igraphdlamch_("EpsMach");
+	}
+
+/*        %----------------------------------------------%   
+          | NP is the number of additional steps to      |   
+          | extend the length NEV Lanczos factorization. |   
+          | NEV0 is the local variable designating the   |   
+          | size of the invariant subspace desired.      |   
+          %----------------------------------------------% */
+
+	np = *ncv - *nev;
+	nev0 = *nev;
+
+/*        %-----------------------------%   
+          | Zero out internal workspace |   
+          %-----------------------------%   
+
+   Computing 2nd power */
+	i__2 = *ncv;
+	i__1 = i__2 * i__2 + (*ncv << 3);
+	for (j = 1; j <= i__1; ++j) {
+	    workl[j] = 0.;
+/* L10: */
+	}
+
+/*        %-------------------------------------------------------%   
+          | Pointer into WORKL for address of H, RITZ, BOUNDS, Q  |   
+          | etc... and the remaining workspace.                   |   
+          | Also update pointer to be used on output.             |   
+          | Memory is laid out as follows:                        |   
+          | workl(1:2*ncv) := generated tridiagonal matrix        |   
+          | workl(2*ncv+1:2*ncv+ncv) := ritz values               |   
+          | workl(3*ncv+1:3*ncv+ncv) := computed error bounds     |   
+          | workl(4*ncv+1:4*ncv+ncv*ncv) := rotation matrix Q     |   
+          | workl(4*ncv+ncv*ncv+1:7*ncv+ncv*ncv) := workspace     |   
+          %-------------------------------------------------------% */
+
+	ldh = *ncv;
+	ldq = *ncv;
+	ih = 1;
+	ritz = ih + (ldh << 1);
+	bounds = ritz + *ncv;
+	iq = bounds + *ncv;
+/* Computing 2nd power */
+	i__1 = *ncv;
+	iw = iq + i__1 * i__1;
+	next = iw + *ncv * 3;
+
+	ipntr[4] = next;
+	ipntr[5] = ih;
+	ipntr[6] = ritz;
+	ipntr[7] = bounds;
+	ipntr[11] = iw;
+    }
+
+/*     %-------------------------------------------------------%   
+       | Carry out the Implicitly restarted Lanczos Iteration. |   
+       %-------------------------------------------------------% */
+
+    igraphdsaup2_(ido, bmat, n, which, &nev0, &np, tol, &resid[1], &mode, &iupd, &
+	    ishift, &mxiter, &v[v_offset], ldv, &workl[ih], &ldh, &workl[ritz]
+	    , &workl[bounds], &workl[iq], &ldq, &workl[iw], &ipntr[1], &workd[
+	    1], info);
+
+/*     %--------------------------------------------------%   
+       | ido .ne. 99 implies use of reverse communication |   
+       | to compute operations involving OP or shifts.    |   
+       %--------------------------------------------------% */
+
+    if (*ido == 3) {
+	iparam[8] = np;
+    }
+    if (*ido != 99) {
+	goto L9000;
+    }
+
+    iparam[3] = mxiter;
+    iparam[5] = np;
+    iparam[9] = nopx;
+    iparam[10] = nbx;
+    iparam[11] = nrorth;
+
+/*     %------------------------------------%   
+       | Exit if there was an informational |   
+       | error within dsaup2.               |   
+       %------------------------------------% */
+
+    if (*info < 0) {
+	goto L9000;
+    }
+    if (*info == 2) {
+	*info = 3;
+    }
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, &mxiter, &ndigit, "_saupd: number of update i"
+		"terations taken", (ftnlen)41);
+	igraphivout_(&logfil, &c__1, &np, &ndigit, "_saupd: number of \"converge"
+		"d\" Ritz values", (ftnlen)41);
+	igraphdvout_(&logfil, &np, &workl[ritz], &ndigit, "_saupd: final Ritz valu"
+		"es", (ftnlen)25);
+	igraphdvout_(&logfil, &np, &workl[bounds], &ndigit, "_saupd: corresponding"
+		" error bounds", (ftnlen)34);
+    }
+
+    igraphsecond_(&t1);
+    tsaupd = t1 - t0;
+
+    if (msglvl > 0) {
+
+/*        %--------------------------------------------------------%   
+          | Version Number & Version Date are defined in version.h |   
+          %--------------------------------------------------------% */
+
+	s_wsfe(&io___28);
+	e_wsfe();
+	s_wsfe(&io___29);
+	do_fio(&c__1, (char *)&mxiter, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nopx, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nbx, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nrorth, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nitref, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&nrstrt, (ftnlen)sizeof(integer));
+	do_fio(&c__1, (char *)&tmvopx, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tmvbx, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tsaupd, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tsaup2, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tsaitr, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&titref, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tgetv0, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tseigt, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tsgets, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tsapps, (ftnlen)sizeof(real));
+	do_fio(&c__1, (char *)&tsconv, (ftnlen)sizeof(real));
+	e_wsfe();
+    }
+
+L9000:
+
+    return 0;
+
+/*     %---------------%   
+       | End of dsaupd |   
+       %---------------% */
+
+} /* igraphdsaupd_ */
+
diff --git a/igraph/src/dscal.c b/igraph/src/dscal.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dscal.c
@@ -0,0 +1,86 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdscal_(integer *n, doublereal *da, doublereal *dx, 
+	integer *incx)
+{
+    /* System generated locals */
+    integer i__1, i__2;
+
+    /* Local variables */
+    integer i__, m, mp1, nincx;
+
+
+/*  Purpose   
+    =======   
+
+       DSCAL scales a vector by a constant.   
+       uses unrolled loops for increment equal to one.   
+
+    Further Details   
+    ===============   
+
+       jack dongarra, linpack, 3/11/78.   
+       modified 3/93 to return if incx .le. 0.   
+       modified 12/3/93, array(1) declarations changed to array(*)   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --dx;
+
+    /* Function Body */
+    if (*n <= 0 || *incx <= 0) {
+	return 0;
+    }
+    if (*incx == 1) {
+
+/*        code for increment equal to 1   
+
+
+          clean-up loop */
+
+	m = *n % 5;
+	if (m != 0) {
+	    i__1 = m;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		dx[i__] = *da * dx[i__];
+	    }
+	    if (*n < 5) {
+		return 0;
+	    }
+	}
+	mp1 = m + 1;
+	i__1 = *n;
+	for (i__ = mp1; i__ <= i__1; i__ += 5) {
+	    dx[i__] = *da * dx[i__];
+	    dx[i__ + 1] = *da * dx[i__ + 1];
+	    dx[i__ + 2] = *da * dx[i__ + 2];
+	    dx[i__ + 3] = *da * dx[i__ + 3];
+	    dx[i__ + 4] = *da * dx[i__ + 4];
+	}
+    } else {
+
+/*        code for increment not equal to 1 */
+
+	nincx = *n * *incx;
+	i__1 = nincx;
+	i__2 = *incx;
+	for (i__ = 1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) {
+	    dx[i__] = *da * dx[i__];
+	}
+    }
+    return 0;
+} /* igraphdscal_ */
+
diff --git a/igraph/src/dsconv.c b/igraph/src/dsconv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsconv.c
@@ -0,0 +1,168 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b3 = .66666666666666663;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsconv   
+
+   \Description:   
+    Convergence testing for the symmetric Arnoldi eigenvalue routine.   
+
+   \Usage:   
+    call dsconv   
+       ( N, RITZ, BOUNDS, TOL, NCONV )   
+
+   \Arguments   
+    N       Integer.  (INPUT)   
+            Number of Ritz values to check for convergence.   
+
+    RITZ    Double precision array of length N.  (INPUT)   
+            The Ritz values to be checked for convergence.   
+
+    BOUNDS  Double precision array of length N.  (INPUT)   
+            Ritz estimates associated with the Ritz values in RITZ.   
+
+    TOL     Double precision scalar.  (INPUT)   
+            Desired relative accuracy for a Ritz value to be considered   
+            "converged".   
+
+    NCONV   Integer scalar.  (OUTPUT)   
+            Number of "converged" Ritz values.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Routines called:   
+       second  ARPACK utility routine for timing.   
+       dlamch  LAPACK routine that determines machine constants.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \SCCS Information: @(#)   
+   FILE: sconv.F   SID: 2.4   DATE OF SID: 4/19/96   RELEASE: 2   
+
+   \Remarks   
+       1. Starting with version 2.4, this routine no longer uses the   
+          Parlett strategy using the gap conditions.   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsconv_(integer *n, doublereal *ritz, doublereal *bounds,
+	 doublereal *tol, integer *nconv)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2, d__3;
+
+    /* Builtin functions */
+    double pow_dd(doublereal *, doublereal *);
+
+    /* Local variables */
+    integer i__;
+    real t0, t1;
+    doublereal eps23, temp;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphsecond_(real *);
+    real tsconv = 0;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %-------------------%   
+       | External routines |   
+       %-------------------%   
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       Parameter adjustments */
+    --bounds;
+    --ritz;
+
+    /* Function Body */
+    igraphsecond_(&t0);
+
+    eps23 = igraphdlamch_("Epsilon-Machine");
+    eps23 = pow_dd(&eps23, &c_b3);
+
+    *nconv = 0;
+    i__1 = *n;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+
+/*        %-----------------------------------------------------%   
+          | The i-th Ritz value is considered "converged"       |   
+          | when: bounds(i) .le. TOL*max(eps23, abs(ritz(i)))   |   
+          %-----------------------------------------------------%   
+
+   Computing MAX */
+	d__2 = eps23, d__3 = (d__1 = ritz[i__], abs(d__1));
+	temp = max(d__2,d__3);
+	if (bounds[i__] <= *tol * temp) {
+	    ++(*nconv);
+	}
+
+/* L10: */
+    }
+
+    igraphsecond_(&t1);
+    tsconv += t1 - t0;
+
+    return 0;
+
+/*     %---------------%   
+       | End of dsconv |   
+       %---------------% */
+
+} /* igraphdsconv_ */
+
diff --git a/igraph/src/dseigt.c b/igraph/src/dseigt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dseigt.c
@@ -0,0 +1,221 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dseigt   
+
+   \Description:   
+    Compute the eigenvalues of the current symmetric tridiagonal matrix   
+    and the corresponding error bounds given the current residual norm.   
+
+   \Usage:   
+    call dseigt   
+       ( RNORM, N, H, LDH, EIG, BOUNDS, WORKL, IERR )   
+
+   \Arguments   
+    RNORM   Double precision scalar.  (INPUT)   
+            RNORM contains the residual norm corresponding to the current   
+            symmetric tridiagonal matrix H.   
+
+    N       Integer.  (INPUT)   
+            Size of the symmetric tridiagonal matrix H.   
+
+    H       Double precision N by 2 array.  (INPUT)   
+            H contains the symmetric tridiagonal matrix with the   
+            subdiagonal in the first column starting at H(2,1) and the   
+            main diagonal in second column.   
+
+    LDH     Integer.  (INPUT)   
+            Leading dimension of H exactly as declared in the calling   
+            program.   
+
+    EIG     Double precision array of length N.  (OUTPUT)   
+            On output, EIG contains the N eigenvalues of H possibly   
+            unsorted.  The BOUNDS arrays are returned in the   
+            same sorted order as EIG.   
+
+    BOUNDS  Double precision array of length N.  (OUTPUT)   
+            On output, BOUNDS contains the error estimates corresponding   
+            to the eigenvalues EIG.  This is equal to RNORM times the   
+            last components of the eigenvectors corresponding to the   
+            eigenvalues in EIG.   
+
+    WORKL   Double precision work array of length 3*N.  (WORKSPACE)   
+            Private (replicated) array on each PE or array allocated on   
+            the front end.   
+
+    IERR    Integer.  (OUTPUT)   
+            Error exit flag from dstqrb.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \Routines called:   
+       dstqrb  ARPACK routine that computes the eigenvalues and the   
+               last components of the eigenvectors of a symmetric   
+               and tridiagonal matrix.   
+       second  ARPACK utility routine for timing.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dcopy   Level 1 BLAS that copies one vector to another.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/92: Version ' 2.4'   
+
+   \SCCS Information: @(#)   
+   FILE: seigt.F   SID: 2.4   DATE OF SID: 8/27/96   RELEASE: 2   
+
+   \Remarks   
+       None   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdseigt_(doublereal *rnorm, integer *n, doublereal *h__, 
+	integer *ldh, doublereal *eig, doublereal *bounds, doublereal *workl, 
+	integer *ierr)
+{
+    /* System generated locals */
+    integer h_dim1, h_offset, i__1;
+    doublereal d__1;
+
+    /* Local variables */
+    integer k;
+    real t0, t1;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdvout_(integer *, integer *, doublereal 
+	    *, integer *, char *, ftnlen), igraphsecond_(real *);
+    integer logfil, ndigit, mseigt = 0;
+    extern /* Subroutine */ int igraphdstqrb_(integer *, doublereal *, doublereal *,
+	     doublereal *, doublereal *, integer *);
+    real tseigt = 0.0;
+    integer msglvl;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       %-------------------------------%   
+       | Initialize timing statistics  |   
+       | & message level for debugging |   
+       %-------------------------------%   
+
+       Parameter adjustments */
+    --workl;
+    --bounds;
+    --eig;
+    h_dim1 = *ldh;
+    h_offset = 1 + h_dim1;
+    h__ -= h_offset;
+
+    /* Function Body */
+    igraphsecond_(&t0);
+    msglvl = mseigt;
+
+    if (msglvl > 0) {
+	igraphdvout_(&logfil, n, &h__[(h_dim1 << 1) + 1], &ndigit, "_seigt: main d"
+		"iagonal of matrix H", (ftnlen)33);
+	if (*n > 1) {
+	    i__1 = *n - 1;
+	    igraphdvout_(&logfil, &i__1, &h__[h_dim1 + 2], &ndigit, "_seigt: sub d"
+		    "iagonal of matrix H", (ftnlen)32);
+	}
+    }
+
+    igraphdcopy_(n, &h__[(h_dim1 << 1) + 1], &c__1, &eig[1], &c__1);
+    i__1 = *n - 1;
+    igraphdcopy_(&i__1, &h__[h_dim1 + 2], &c__1, &workl[1], &c__1);
+    igraphdstqrb_(n, &eig[1], &workl[1], &bounds[1], &workl[*n + 1], ierr);
+    if (*ierr != 0) {
+	goto L9000;
+    }
+    if (msglvl > 1) {
+	igraphdvout_(&logfil, n, &bounds[1], &ndigit, "_seigt: last row of the eig"
+		"envector matrix for H", (ftnlen)48);
+    }
+
+/*     %-----------------------------------------------%   
+       | Finally determine the error bounds associated |   
+       | with the n Ritz values of H.                  |   
+       %-----------------------------------------------% */
+
+    i__1 = *n;
+    for (k = 1; k <= i__1; ++k) {
+	bounds[k] = *rnorm * (d__1 = bounds[k], abs(d__1));
+/* L30: */
+    }
+
+    igraphsecond_(&t1);
+    tseigt += t1 - t0;
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dseigt |   
+       %---------------% */
+
+} /* igraphdseigt_ */
+
diff --git a/igraph/src/dsesrt.c b/igraph/src/dsesrt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsesrt.c
@@ -0,0 +1,288 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsesrt   
+
+   \Description:   
+    Sort the array X in the order specified by WHICH and optionally   
+    apply the permutation to the columns of the matrix A.   
+
+   \Usage:   
+    call dsesrt   
+       ( WHICH, APPLY, N, X, NA, A, LDA)   
+
+   \Arguments   
+    WHICH   Character*2.  (Input)   
+            'LM' -> X is sorted into increasing order of magnitude.   
+            'SM' -> X is sorted into decreasing order of magnitude.   
+            'LA' -> X is sorted into increasing order of algebraic.   
+            'SA' -> X is sorted into decreasing order of algebraic.   
+
+    APPLY   Logical.  (Input)   
+            APPLY = .TRUE.  -> apply the sorted order to A.   
+            APPLY = .FALSE. -> do not apply the sorted order to A.   
+
+    N       Integer.  (INPUT)   
+            Dimension of the array X.   
+
+    X      Double precision array of length N.  (INPUT/OUTPUT)   
+            The array to be sorted.   
+
+    NA      Integer.  (INPUT)   
+            Number of rows of the matrix A.   
+
+    A      Double precision array of length NA by N.  (INPUT/OUTPUT)   
+
+    LDA     Integer.  (INPUT)   
+            Leading dimension of A.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Routines   
+       dswap  Level 1 BLAS that swaps the contents of two vectors.   
+
+   \Authors   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       12/15/93: Version ' 2.1'.   
+                 Adapted from the sort routine in LANSO and   
+                 the ARPACK code dsortr   
+
+   \SCCS Information: @(#)   
+   FILE: sesrt.F   SID: 2.3   DATE OF SID: 4/19/96   RELEASE: 2   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsesrt_(char *which, logical *apply, integer *n, 
+	doublereal *x, integer *na, doublereal *a, integer *lda)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+
+    /* Local variables */
+    integer i__, j, igap;
+    doublereal temp;
+    extern /* Subroutine */ int igraphdswap_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+
+
+/*     %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1 * 0;
+    a -= a_offset;
+
+    /* Function Body */
+    igap = *n / 2;
+
+    if (s_cmp(which, "SA", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        X is sorted into decreasing order of algebraic. */
+
+L10:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L20:
+
+	    if (j < 0) {
+		goto L30;
+	    }
+
+	    if (x[j] < x[j + igap]) {
+		temp = x[j];
+		x[j] = x[j + igap];
+		x[j + igap] = temp;
+		if (*apply) {
+		    igraphdswap_(na, &a[j * a_dim1 + 1], &c__1, &a[(j + igap) * 
+			    a_dim1 + 1], &c__1);
+		}
+	    } else {
+		goto L30;
+	    }
+	    j -= igap;
+	    goto L20;
+L30:
+	    ;
+	}
+	igap /= 2;
+	goto L10;
+
+    } else if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        X is sorted into decreasing order of magnitude. */
+
+L40:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L50:
+
+	    if (j < 0) {
+		goto L60;
+	    }
+
+	    if ((d__1 = x[j], abs(d__1)) < (d__2 = x[j + igap], abs(d__2))) {
+		temp = x[j];
+		x[j] = x[j + igap];
+		x[j + igap] = temp;
+		if (*apply) {
+		    igraphdswap_(na, &a[j * a_dim1 + 1], &c__1, &a[(j + igap) * 
+			    a_dim1 + 1], &c__1);
+		}
+	    } else {
+		goto L60;
+	    }
+	    j -= igap;
+	    goto L50;
+L60:
+	    ;
+	}
+	igap /= 2;
+	goto L40;
+
+    } else if (s_cmp(which, "LA", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        X is sorted into increasing order of algebraic. */
+
+L70:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L80:
+
+	    if (j < 0) {
+		goto L90;
+	    }
+
+	    if (x[j] > x[j + igap]) {
+		temp = x[j];
+		x[j] = x[j + igap];
+		x[j + igap] = temp;
+		if (*apply) {
+		    igraphdswap_(na, &a[j * a_dim1 + 1], &c__1, &a[(j + igap) * 
+			    a_dim1 + 1], &c__1);
+		}
+	    } else {
+		goto L90;
+	    }
+	    j -= igap;
+	    goto L80;
+L90:
+	    ;
+	}
+	igap /= 2;
+	goto L70;
+
+    } else if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        X is sorted into increasing order of magnitude. */
+
+L100:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L110:
+
+	    if (j < 0) {
+		goto L120;
+	    }
+
+	    if ((d__1 = x[j], abs(d__1)) > (d__2 = x[j + igap], abs(d__2))) {
+		temp = x[j];
+		x[j] = x[j + igap];
+		x[j + igap] = temp;
+		if (*apply) {
+		    igraphdswap_(na, &a[j * a_dim1 + 1], &c__1, &a[(j + igap) * 
+			    a_dim1 + 1], &c__1);
+		}
+	    } else {
+		goto L120;
+	    }
+	    j -= igap;
+	    goto L110;
+L120:
+	    ;
+	}
+	igap /= 2;
+	goto L100;
+    }
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dsesrt |   
+       %---------------% */
+
+} /* igraphdsesrt_ */
+
diff --git a/igraph/src/dseupd.c b/igraph/src/dseupd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dseupd.c
@@ -0,0 +1,1046 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b21 = .66666666666666663;
+static integer c__1 = 1;
+static integer c__2 = 2;
+static logical c_true = TRUE_;
+static doublereal c_b119 = 1.;
+
+/* \BeginDoc   
+
+   \Name: dseupd   
+
+   \Description:   
+
+    This subroutine returns the converged approximations to eigenvalues   
+    of A*z = lambda*B*z and (optionally):   
+
+        (1) the corresponding approximate eigenvectors,   
+
+        (2) an orthonormal (Lanczos) basis for the associated approximate   
+            invariant subspace,   
+
+        (3) Both.   
+
+    There is negligible additional cost to obtain eigenvectors.  An orthonormal   
+    (Lanczos) basis is always computed.  There is an additional storage cost   
+    of n*nev if both are requested (in this case a separate array Z must be   
+    supplied).   
+
+    These quantities are obtained from the Lanczos factorization computed   
+    by DSAUPD for the linear operator OP prescribed by the MODE selection   
+    (see IPARAM(7) in DSAUPD documentation.)  DSAUPD must be called before   
+    this routine is called. These approximate eigenvalues and vectors are   
+    commonly called Ritz values and Ritz vectors respectively.  They are   
+    referred to as such in the comments that follow.   The computed orthonormal   
+    basis for the invariant subspace corresponding to these Ritz values is   
+    referred to as a Lanczos basis.   
+
+    See documentation in the header of the subroutine DSAUPD for a definition   
+    of OP as well as other terms and the relation of computed Ritz values   
+    and vectors of OP with respect to the given problem  A*z = lambda*B*z.   
+
+    The approximate eigenvalues of the original problem are returned in   
+    ascending algebraic order.  The user may elect to call this routine   
+    once for each desired Ritz vector and store it peripherally if desired.   
+    There is also the option of computing a selected set of these vectors   
+    with a single call.   
+
+   \Usage:   
+    call dseupd   
+       ( RVEC, HOWMNY, SELECT, D, Z, LDZ, SIGMA, BMAT, N, WHICH, NEV, TOL,   
+         RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, LWORKL, INFO )   
+
+    RVEC    LOGICAL  (INPUT)   
+            Specifies whether Ritz vectors corresponding to the Ritz value   
+            approximations to the eigenproblem A*z = lambda*B*z are computed.   
+
+               RVEC = .FALSE.     Compute Ritz values only.   
+
+               RVEC = .TRUE.      Compute Ritz vectors.   
+
+    HOWMNY  Character*1  (INPUT)   
+            Specifies how many Ritz vectors are wanted and the form of Z   
+            the matrix of Ritz vectors. See remark 1 below.   
+            = 'A': compute NEV Ritz vectors;   
+            = 'S': compute some of the Ritz vectors, specified   
+                   by the logical array SELECT.   
+
+    SELECT  Logical array of dimension NEV.  (INPUT)   
+            If HOWMNY = 'S', SELECT specifies the Ritz vectors to be   
+            computed. To select the Ritz vector corresponding to a   
+            Ritz value D(j), SELECT(j) must be set to .TRUE..   
+            If HOWMNY = 'A' , SELECT is not referenced.   
+
+    D       Double precision array of dimension NEV.  (OUTPUT)   
+            On exit, D contains the Ritz value approximations to the   
+            eigenvalues of A*z = lambda*B*z. The values are returned   
+            in ascending order. If IPARAM(7) = 3,4,5 then D represents   
+            the Ritz values of OP computed by dsaupd transformed to   
+            those of the original eigensystem A*z = lambda*B*z. If   
+            IPARAM(7) = 1,2 then the Ritz values of OP are the same   
+            as the those of A*z = lambda*B*z.   
+
+    Z       Double precision N by NEV array if HOWMNY = 'A'.  (OUTPUT)   
+            On exit, Z contains the B-orthonormal Ritz vectors of the   
+            eigensystem A*z = lambda*B*z corresponding to the Ritz   
+            value approximations.   
+            If  RVEC = .FALSE. then Z is not referenced.   
+            NOTE: The array Z may be set equal to first NEV columns of the   
+            Arnoldi/Lanczos basis array V computed by DSAUPD.   
+
+    LDZ     Integer.  (INPUT)   
+            The leading dimension of the array Z.  If Ritz vectors are   
+            desired, then  LDZ .ge.  max( 1, N ).  In any case,  LDZ .ge. 1.   
+
+    SIGMA   Double precision  (INPUT)   
+            If IPARAM(7) = 3,4,5 represents the shift. Not referenced if   
+            IPARAM(7) = 1 or 2.   
+
+
+    **** The remaining arguments MUST be the same as for the   ****   
+    **** call to DNAUPD that was just completed.               ****   
+
+    NOTE: The remaining arguments   
+
+             BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM, IPNTR,   
+             WORKD, WORKL, LWORKL, INFO   
+
+           must be passed directly to DSEUPD following the last call   
+           to DSAUPD.  These arguments MUST NOT BE MODIFIED between   
+           the the last call to DSAUPD and the call to DSEUPD.   
+
+    Two of these parameters (WORKL, INFO) are also output parameters:   
+
+    WORKL   Double precision work array of length LWORKL.  (OUTPUT/WORKSPACE)   
+            WORKL(1:4*ncv) contains information obtained in   
+            dsaupd.  They are not changed by dseupd.   
+            WORKL(4*ncv+1:ncv*ncv+8*ncv) holds the   
+            untransformed Ritz values, the computed error estimates,   
+            and the associated eigenvector matrix of H.   
+
+            Note: IPNTR(8:10) contains the pointer into WORKL for addresses   
+            of the above information computed by dseupd.   
+            -------------------------------------------------------------   
+            IPNTR(8): pointer to the NCV RITZ values of the original system.   
+            IPNTR(9): pointer to the NCV corresponding error bounds.   
+            IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors   
+                       of the tridiagonal matrix T. Only referenced by   
+                       dseupd if RVEC = .TRUE. See Remarks.   
+            -------------------------------------------------------------   
+
+    INFO    Integer.  (OUTPUT)   
+            Error flag on output.   
+            =  0: Normal exit.   
+            = -1: N must be positive.   
+            = -2: NEV must be positive.   
+            = -3: NCV must be greater than NEV and less than or equal to N.   
+            = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.   
+            = -6: BMAT must be one of 'I' or 'G'.   
+            = -7: Length of private work WORKL array is not sufficient.   
+            = -8: Error return from trid. eigenvalue calculation;   
+                  Information error from LAPACK routine dsteqr.   
+            = -9: Starting vector is zero.   
+            = -10: IPARAM(7) must be 1,2,3,4,5.   
+            = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatible.   
+            = -12: NEV and WHICH = 'BE' are incompatible.   
+            = -14: DSAUPD did not find any eigenvalues to sufficient   
+                   accuracy.   
+            = -15: HOWMNY must be one of 'A' or 'S' if RVEC = .true.   
+            = -16: HOWMNY = 'S' not yet implemented   
+
+   \BeginLib   
+
+   \References:   
+    1. D.C. Sorensen, "Implicit Application of Polynomial Filters in   
+       a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992),   
+       pp 357-385.   
+    2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly   
+       Restarted Arnoldi Iteration", Rice University Technical Report   
+       TR95-13, Department of Computational and Applied Mathematics.   
+    3. B.N. Parlett, "The Symmetric Eigenvalue Problem". Prentice-Hall,   
+       1980.   
+    4. B.N. Parlett, B. Nour-Omid, "Towards a Black Box Lanczos Program",   
+       Computer Physics Communications, 53 (1989), pp 169-179.   
+    5. B. Nour-Omid, B.N. Parlett, T. Ericson, P.S. Jensen, "How to   
+       Implement the Spectral Transformation", Math. Comp., 48 (1987),   
+       pp 663-673.   
+    6. R.G. Grimes, J.G. Lewis and H.D. Simon, "A Shifted Block Lanczos   
+       Algorithm for Solving Sparse Symmetric Generalized Eigenproblems",   
+       SIAM J. Matr. Anal. Apps.,  January (1993).   
+    7. L. Reichel, W.B. Gragg, "Algorithm 686: FORTRAN Subroutines   
+       for Updating the QR decomposition", ACM TOMS, December 1990,   
+       Volume 16 Number 4, pp 369-377.   
+
+   \Remarks   
+    1. The converged Ritz values are always returned in increasing   
+       (algebraic) order.   
+
+    2. Currently only HOWMNY = 'A' is implemented. It is included at this   
+       stage for the user who wants to incorporate it.   
+
+   \Routines called:   
+       dsesrt  ARPACK routine that sorts an array X, and applies the   
+               corresponding permutation to a matrix A.   
+       dsortr  dsortr  ARPACK sorting routine.   
+       ivout   ARPACK utility routine that prints integers.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dgeqr2  LAPACK routine that computes the QR factorization of   
+               a matrix.   
+       dlacpy  LAPACK matrix copy routine.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dorm2r  LAPACK routine that applies an orthogonal matrix in   
+               factored form.   
+       dsteqr  LAPACK routine that computes eigenvalues and eigenvectors   
+               of a tridiagonal matrix.   
+       dger    Level 2 BLAS rank one update to a matrix.   
+       dcopy   Level 1 BLAS that copies one vector to another .   
+       dnrm2   Level 1 BLAS that computes the norm of a vector.   
+       dscal   Level 1 BLAS that scales a vector.   
+       dswap   Level 1 BLAS that swaps the contents of two vectors.   
+   \Authors   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Chao Yang                    Houston, Texas   
+       Dept. of Computational &   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       12/15/93: Version ' 2.1'   
+
+   \SCCS Information: @(#)   
+   FILE: seupd.F   SID: 2.7   DATE OF SID: 8/27/96   RELEASE: 2   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+   Subroutine */ int igraphdseupd_(logical *rvec, char *howmny, logical *select, 
+	doublereal *d__, doublereal *z__, integer *ldz, doublereal *sigma, 
+	char *bmat, integer *n, char *which, integer *nev, doublereal *tol, 
+	doublereal *resid, integer *ncv, doublereal *v, integer *ldv, integer 
+	*iparam, integer *ipntr, doublereal *workd, doublereal *workl, 
+	integer *lworkl, integer *info)
+{
+    /* System generated locals */
+    integer v_dim1, v_offset, z_dim1, z_offset, i__1;
+    doublereal d__1, d__2, d__3;
+
+    /* Builtin functions */
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+    /* Subroutine */ int s_copy(char *, char *, ftnlen, ftnlen);
+    double pow_dd(doublereal *, doublereal *);
+
+    /* Local variables */
+    integer j, k, ih, iq, iw;
+    doublereal kv[2];
+    integer ibd, ihb, ihd, ldh, ilg, ldq, ism, irz;
+    extern /* Subroutine */ int igraphdger_(integer *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    integer mode;
+    doublereal eps23;
+    integer ierr;
+    doublereal temp;
+    integer next;
+    char type__[6];
+    integer ritz;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    logical reord;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    integer nconv;
+    doublereal rnorm;
+    extern /* Subroutine */ int igraphdvout_(integer *, integer *, doublereal *, 
+	    integer *, char *, ftnlen), igraphivout_(integer *, integer *, integer *
+	    , integer *, char *, ftnlen), igraphdgeqr2_(integer *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *);
+    doublereal bnorm2;
+    extern /* Subroutine */ int igraphdorm2r_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    doublereal thres1, thres2;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlacpy_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *);
+    integer logfil, ndigit, bounds, mseupd = 0;
+    extern /* Subroutine */ int igraphdsteqr_(char *, integer *, doublereal *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *);
+    integer msglvl, ktrord;
+    extern /* Subroutine */ int igraphdsesrt_(char *, logical *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), 
+	    igraphdsortr_(char *, logical *, integer *, doublereal *, doublereal *);
+    doublereal tempbnd;
+    integer leftptr, rghtptr;
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %--------------%   
+       | Local Arrays |   
+       %--------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       %------------------------%   
+       | Set default parameters |   
+       %------------------------%   
+
+       Parameter adjustments */
+    --workd;
+    --resid;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --d__;
+    --select;
+    v_dim1 = *ldv;
+    v_offset = 1 + v_dim1;
+    v -= v_offset;
+    --iparam;
+    --ipntr;
+    --workl;
+
+    /* Function Body */
+    msglvl = mseupd;
+    mode = iparam[7];
+    nconv = iparam[5];
+    *info = 0;
+
+/*     %--------------%   
+       | Quick return |   
+       %--------------% */
+
+    if (nconv == 0) {
+	goto L9000;
+    }
+    ierr = 0;
+
+    if (nconv <= 0) {
+	ierr = -14;
+    }
+    if (*n <= 0) {
+	ierr = -1;
+    }
+    if (*nev <= 0) {
+	ierr = -2;
+    }
+    if (*ncv <= *nev || *ncv > *n) {
+	ierr = -3;
+    }
+    if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, "SM", (
+	    ftnlen)2, (ftnlen)2) != 0 && s_cmp(which, "LA", (ftnlen)2, (
+	    ftnlen)2) != 0 && s_cmp(which, "SA", (ftnlen)2, (ftnlen)2) != 0 &&
+	     s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) != 0) {
+	ierr = -5;
+    }
+    if (*(unsigned char *)bmat != 'I' && *(unsigned char *)bmat != 'G') {
+	ierr = -6;
+    }
+    if (*(unsigned char *)howmny != 'A' && *(unsigned char *)howmny != 'P' && 
+	    *(unsigned char *)howmny != 'S' && *rvec) {
+	ierr = -15;
+    }
+    if (*rvec && *(unsigned char *)howmny == 'S') {
+	ierr = -16;
+    }
+
+/* Computing 2nd power */
+    i__1 = *ncv;
+    if (*rvec && *lworkl < i__1 * i__1 + (*ncv << 3)) {
+	ierr = -7;
+    }
+
+    if (mode == 1 || mode == 2) {
+	s_copy(type__, "REGULR", (ftnlen)6, (ftnlen)6);
+    } else if (mode == 3) {
+	s_copy(type__, "SHIFTI", (ftnlen)6, (ftnlen)6);
+    } else if (mode == 4) {
+	s_copy(type__, "BUCKLE", (ftnlen)6, (ftnlen)6);
+    } else if (mode == 5) {
+	s_copy(type__, "CAYLEY", (ftnlen)6, (ftnlen)6);
+    } else {
+	ierr = -10;
+    }
+    if (mode == 1 && *(unsigned char *)bmat == 'G') {
+	ierr = -11;
+    }
+    if (*nev == 1 && s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) == 0) {
+	ierr = -12;
+    }
+
+/*     %------------%   
+       | Error Exit |   
+       %------------% */
+
+    if (ierr != 0) {
+	*info = ierr;
+	goto L9000;
+    }
+
+/*     %-------------------------------------------------------%   
+       | Pointer into WORKL for address of H, RITZ, BOUNDS, Q  |   
+       | etc... and the remaining workspace.                   |   
+       | Also update pointer to be used on output.             |   
+       | Memory is laid out as follows:                        |   
+       | workl(1:2*ncv) := generated tridiagonal matrix H      |   
+       |       The subdiagonal is stored in workl(2:ncv).      |   
+       |       The dead spot is workl(1) but upon exiting      |   
+       |       dsaupd stores the B-norm of the last residual   |   
+       |       vector in workl(1). We use this !!!             |   
+       | workl(2*ncv+1:2*ncv+ncv) := ritz values               |   
+       |       The wanted values are in the first NCONV spots. |   
+       | workl(3*ncv+1:3*ncv+ncv) := computed Ritz estimates   |   
+       |       The wanted values are in the first NCONV spots. |   
+       | NOTE: workl(1:4*ncv) is set by dsaupd and is not      |   
+       |       modified by dseupd.                             |   
+       %-------------------------------------------------------%   
+
+       %-------------------------------------------------------%   
+       | The following is used and set by dseupd.              |   
+       | workl(4*ncv+1:4*ncv+ncv) := used as workspace during  |   
+       |       computation of the eigenvectors of H. Stores    |   
+       |       the diagonal of H. Upon EXIT contains the NCV   |   
+       |       Ritz values of the original system. The first   |   
+       |       NCONV spots have the wanted values. If MODE =   |   
+       |       1 or 2 then will equal workl(2*ncv+1:3*ncv).    |   
+       | workl(5*ncv+1:5*ncv+ncv) := used as workspace during  |   
+       |       computation of the eigenvectors of H. Stores    |   
+       |       the subdiagonal of H. Upon EXIT contains the    |   
+       |       NCV corresponding Ritz estimates of the         |   
+       |       original system. The first NCONV spots have the |   
+       |       wanted values. If MODE = 1,2 then will equal    |   
+       |       workl(3*ncv+1:4*ncv).                           |   
+       | workl(6*ncv+1:6*ncv+ncv*ncv) := orthogonal Q that is  |   
+       |       the eigenvector matrix for H as returned by     |   
+       |       dsteqr. Not referenced if RVEC = .False.        |   
+       |       Ordering follows that of workl(4*ncv+1:5*ncv)   |   
+       | workl(6*ncv+ncv*ncv+1:6*ncv+ncv*ncv+2*ncv) :=         |   
+       |       Workspace. Needed by dsteqr and by dseupd.      |   
+       | GRAND total of NCV*(NCV+8) locations.                 |   
+       %-------------------------------------------------------% */
+
+
+    ih = ipntr[5];
+    ritz = ipntr[6];
+    bounds = ipntr[7];
+    ldh = *ncv;
+    ldq = *ncv;
+    ihd = bounds + ldh;
+    ihb = ihd + ldh;
+    iq = ihb + ldh;
+    iw = iq + ldh * *ncv;
+    next = iw + (*ncv << 1);
+    ipntr[4] = next;
+    ipntr[8] = ihd;
+    ipntr[9] = ihb;
+    ipntr[10] = iq;
+
+/*     %----------------------------------------%   
+       | irz points to the Ritz values computed |   
+       |     by _seigt before exiting _saup2.   |   
+       | ibd points to the Ritz estimates       |   
+       |     computed by _seigt before exiting  |   
+       |     _saup2.                            |   
+       %----------------------------------------% */
+
+    irz = ipntr[11] + *ncv;
+    ibd = irz + *ncv;
+
+
+/*     %---------------------------------%   
+       | Set machine dependent constant. |   
+       %---------------------------------% */
+
+    eps23 = igraphdlamch_("Epsilon-Machine");
+    eps23 = pow_dd(&eps23, &c_b21);
+
+/*     %---------------------------------------%   
+       | RNORM is B-norm of the RESID(1:N).    |   
+       | BNORM2 is the 2 norm of B*RESID(1:N). |   
+       | Upon exit of dsaupd WORKD(1:N) has    |   
+       | B*RESID(1:N).                         |   
+       %---------------------------------------% */
+
+    rnorm = workl[ih];
+    if (*(unsigned char *)bmat == 'I') {
+	bnorm2 = rnorm;
+    } else if (*(unsigned char *)bmat == 'G') {
+	bnorm2 = igraphdnrm2_(n, &workd[1], &c__1);
+    }
+
+    if (*rvec) {
+
+/*        %------------------------------------------------%   
+          | Get the converged Ritz value on the boundary.  |   
+          | This value will be used to dermine whether we  |   
+          | need to reorder the eigenvalues and            |   
+          | eigenvectors comupted by _steqr, and is        |   
+          | referred to as the "threshold" value.          |   
+          |                                                |   
+          | A Ritz value gamma is said to be a wanted      |   
+          | one, if                                        |   
+          | abs(gamma) .ge. threshold, when WHICH = 'LM';  |   
+          | abs(gamma) .le. threshold, when WHICH = 'SM';  |   
+          | gamma      .ge. threshold, when WHICH = 'LA';  |   
+          | gamma      .le. threshold, when WHICH = 'SA';  |   
+          | gamma .le. thres1 .or. gamma .ge. thres2       |   
+          |                            when WHICH = 'BE';  |   
+          |                                                |   
+          | Note: converged Ritz values and associated     |   
+          | Ritz estimates have been placed in the first   |   
+          | NCONV locations in workl(ritz) and             |   
+          | workl(bounds) respectively. They have been     |   
+          | sorted (in _saup2) according to the WHICH      |   
+          | selection criterion. (Except in the case       |   
+          | WHICH = 'BE', they are sorted in an increasing |   
+          | order.)                                        |   
+          %------------------------------------------------% */
+
+	if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(which, 
+		"SM", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(which, "LA", (
+		ftnlen)2, (ftnlen)2) == 0 || s_cmp(which, "SA", (ftnlen)2, (
+		ftnlen)2) == 0) {
+
+	    thres1 = workl[ritz];
+
+	    if (msglvl > 2) {
+		igraphdvout_(&logfil, &c__1, &thres1, &ndigit, "_seupd: Threshold "
+			"eigenvalue used for re-ordering", (ftnlen)49);
+	    }
+
+	} else if (s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*            %------------------------------------------------%   
+              | Ritz values returned from _saup2 have been     |   
+              | sorted in increasing order.  Thus two          |   
+              | "threshold" values (one for the small end, one |   
+              | for the large end) are in the middle.          |   
+              %------------------------------------------------% */
+
+	    ism = max(*nev,nconv) / 2;
+	    ilg = ism + 1;
+	    thres1 = workl[ism];
+	    thres2 = workl[ilg];
+
+	    if (msglvl > 2) {
+		kv[0] = thres1;
+		kv[1] = thres2;
+		igraphdvout_(&logfil, &c__2, kv, &ndigit, "_seupd: Threshold eigen"
+			"values used for re-ordering", (ftnlen)50);
+	    }
+
+	}
+
+/*        %----------------------------------------------------------%   
+          | Check to see if all converged Ritz values appear within  |   
+          | the first NCONV diagonal elements returned from _seigt.  |   
+          | This is done in the following way:                       |   
+          |                                                          |   
+          | 1) For each Ritz value obtained from _seigt, compare it  |   
+          |    with the threshold Ritz value computed above to       |   
+          |    determine whether it is a wanted one.                 |   
+          |                                                          |   
+          | 2) If it is wanted, then check the corresponding Ritz    |   
+          |    estimate to see if it has converged.  If it has, set  |   
+          |    correponding entry in the logical array SELECT to     |   
+          |    .TRUE..                                               |   
+          |                                                          |   
+          | If SELECT(j) = .TRUE. and j > NCONV, then there is a     |   
+          | converged Ritz value that does not appear at the top of  |   
+          | the diagonal matrix computed by _seigt in _saup2.        |   
+          | Reordering is needed.                                    |   
+          %----------------------------------------------------------% */
+
+	reord = FALSE_;
+	ktrord = 0;
+	i__1 = *ncv - 1;
+	for (j = 0; j <= i__1; ++j) {
+	    select[j + 1] = FALSE_;
+	    if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+		if ((d__1 = workl[irz + j], abs(d__1)) >= abs(thres1)) {
+/* Computing MAX */
+		    d__2 = eps23, d__3 = (d__1 = workl[irz + j], abs(d__1));
+		    tempbnd = max(d__2,d__3);
+		    if (workl[ibd + j] <= *tol * tempbnd) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+		if ((d__1 = workl[irz + j], abs(d__1)) <= abs(thres1)) {
+/* Computing MAX */
+		    d__2 = eps23, d__3 = (d__1 = workl[irz + j], abs(d__1));
+		    tempbnd = max(d__2,d__3);
+		    if (workl[ibd + j] <= *tol * tempbnd) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "LA", (ftnlen)2, (ftnlen)2) == 0) {
+		if (workl[irz + j] >= thres1) {
+/* Computing MAX */
+		    d__2 = eps23, d__3 = (d__1 = workl[irz + j], abs(d__1));
+		    tempbnd = max(d__2,d__3);
+		    if (workl[ibd + j] <= *tol * tempbnd) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "SA", (ftnlen)2, (ftnlen)2) == 0) {
+		if (workl[irz + j] <= thres1) {
+/* Computing MAX */
+		    d__2 = eps23, d__3 = (d__1 = workl[irz + j], abs(d__1));
+		    tempbnd = max(d__2,d__3);
+		    if (workl[ibd + j] <= *tol * tempbnd) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    } else if (s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) == 0) {
+		if (workl[irz + j] <= thres1 || workl[irz + j] >= thres2) {
+/* Computing MAX */
+		    d__2 = eps23, d__3 = (d__1 = workl[irz + j], abs(d__1));
+		    tempbnd = max(d__2,d__3);
+		    if (workl[ibd + j] <= *tol * tempbnd) {
+			select[j + 1] = TRUE_;
+		    }
+		}
+	    }
+	    if (j + 1 > nconv) {
+		reord = select[j + 1] || reord;
+	    }
+	    if (select[j + 1]) {
+		++ktrord;
+	    }
+/* L10: */
+	}
+/*        %-------------------------------------------%   
+          | If KTRORD .ne. NCONV, something is wrong. |   
+          %-------------------------------------------% */
+
+	if (msglvl > 2) {
+	    igraphivout_(&logfil, &c__1, &ktrord, &ndigit, "_seupd: Number of spec"
+		    "ified eigenvalues", (ftnlen)39);
+	    igraphivout_(&logfil, &c__1, &nconv, &ndigit, "_seupd: Number of \"con"
+		    "verged\" eigenvalues", (ftnlen)41);
+	}
+
+/*        %-----------------------------------------------------------%   
+          | Call LAPACK routine _steqr to compute the eigenvalues and |   
+          | eigenvectors of the final symmetric tridiagonal matrix H. |   
+          | Initialize the eigenvector matrix Q to the identity.      |   
+          %-----------------------------------------------------------% */
+
+	i__1 = *ncv - 1;
+	igraphdcopy_(&i__1, &workl[ih + 1], &c__1, &workl[ihb], &c__1);
+	igraphdcopy_(ncv, &workl[ih + ldh], &c__1, &workl[ihd], &c__1);
+
+	igraphdsteqr_("Identity", ncv, &workl[ihd], &workl[ihb], &workl[iq], &ldq, &
+		workl[iw], &ierr);
+
+	if (ierr != 0) {
+	    *info = -8;
+	    goto L9000;
+	}
+
+	if (msglvl > 1) {
+	    igraphdcopy_(ncv, &workl[iq + *ncv - 1], &ldq, &workl[iw], &c__1);
+	    igraphdvout_(&logfil, ncv, &workl[ihd], &ndigit, "_seupd: NCV Ritz val"
+		    "ues of the final H matrix", (ftnlen)45);
+	    igraphdvout_(&logfil, ncv, &workl[iw], &ndigit, "_seupd: last row of t"
+		    "he eigenvector matrix for H", (ftnlen)48);
+	}
+
+	if (reord) {
+
+/*           %---------------------------------------------%   
+             | Reordered the eigenvalues and eigenvectors  |   
+             | computed by _steqr so that the "converged"  |   
+             | eigenvalues appear in the first NCONV       |   
+             | positions of workl(ihd), and the associated |   
+             | eigenvectors appear in the first NCONV      |   
+             | columns.                                    |   
+             %---------------------------------------------% */
+
+	    leftptr = 1;
+	    rghtptr = *ncv;
+
+	    if (*ncv == 1) {
+		goto L30;
+	    }
+
+L20:
+	    if (select[leftptr]) {
+
+/*              %-------------------------------------------%   
+                | Search, from the left, for the first Ritz |   
+                | value that has not converged.             |   
+                %-------------------------------------------% */
+
+		++leftptr;
+
+	    } else if (! select[rghtptr]) {
+
+/*              %----------------------------------------------%   
+                | Search, from the right, the first Ritz value |   
+                | that has converged.                          |   
+                %----------------------------------------------% */
+
+		--rghtptr;
+
+	    } else {
+
+/*              %----------------------------------------------%   
+                | Swap the Ritz value on the left that has not |   
+                | converged with the Ritz value on the right   |   
+                | that has converged.  Swap the associated     |   
+                | eigenvector of the tridiagonal matrix H as   |   
+                | well.                                        |   
+                %----------------------------------------------% */
+
+		temp = workl[ihd + leftptr - 1];
+		workl[ihd + leftptr - 1] = workl[ihd + rghtptr - 1];
+		workl[ihd + rghtptr - 1] = temp;
+		igraphdcopy_(ncv, &workl[iq + *ncv * (leftptr - 1)], &c__1, &workl[
+			iw], &c__1);
+		igraphdcopy_(ncv, &workl[iq + *ncv * (rghtptr - 1)], &c__1, &workl[
+			iq + *ncv * (leftptr - 1)], &c__1);
+		igraphdcopy_(ncv, &workl[iw], &c__1, &workl[iq + *ncv * (rghtptr - 
+			1)], &c__1);
+		++leftptr;
+		--rghtptr;
+
+	    }
+
+	    if (leftptr < rghtptr) {
+		goto L20;
+	    }
+
+L30:
+	    ;
+	}
+
+	if (msglvl > 2) {
+	    igraphdvout_(&logfil, ncv, &workl[ihd], &ndigit, "_seupd: The eigenval"
+		    "ues of H--reordered", (ftnlen)39);
+	}
+
+/*        %----------------------------------------%   
+          | Load the converged Ritz values into D. |   
+          %----------------------------------------% */
+
+	igraphdcopy_(&nconv, &workl[ihd], &c__1, &d__[1], &c__1);
+
+    } else {
+
+/*        %-----------------------------------------------------%   
+          | Ritz vectors not required. Load Ritz values into D. |   
+          %-----------------------------------------------------% */
+
+	igraphdcopy_(&nconv, &workl[ritz], &c__1, &d__[1], &c__1);
+	igraphdcopy_(ncv, &workl[ritz], &c__1, &workl[ihd], &c__1);
+
+    }
+
+/*     %------------------------------------------------------------------%   
+       | Transform the Ritz values and possibly vectors and corresponding |   
+       | Ritz estimates of OP to those of A*x=lambda*B*x. The Ritz values |   
+       | (and corresponding data) are returned in ascending order.        |   
+       %------------------------------------------------------------------% */
+
+    if (s_cmp(type__, "REGULR", (ftnlen)6, (ftnlen)6) == 0) {
+
+/*        %---------------------------------------------------------%   
+          | Ascending sort of wanted Ritz values, vectors and error |   
+          | bounds. Not necessary if only Ritz values are desired.  |   
+          %---------------------------------------------------------% */
+
+	if (*rvec) {
+	    igraphdsesrt_("LA", rvec, &nconv, &d__[1], ncv, &workl[iq], &ldq);
+	} else {
+	    igraphdcopy_(ncv, &workl[bounds], &c__1, &workl[ihb], &c__1);
+	}
+
+    } else {
+
+/*        %-------------------------------------------------------------%   
+          | *  Make a copy of all the Ritz values.                      |   
+          | *  Transform the Ritz values back to the original system.   |   
+          |    For TYPE = 'SHIFTI' the transformation is                |   
+          |             lambda = 1/theta + sigma                        |   
+          |    For TYPE = 'BUCKLE' the transformation is                |   
+          |             lambda = sigma * theta / ( theta - 1 )          |   
+          |    For TYPE = 'CAYLEY' the transformation is                |   
+          |             lambda = sigma * (theta + 1) / (theta - 1 )     |   
+          |    where the theta are the Ritz values returned by dsaupd.  |   
+          | NOTES:                                                      |   
+          | *The Ritz vectors are not affected by the transformation.   |   
+          |  They are only reordered.                                   |   
+          %-------------------------------------------------------------% */
+
+	igraphdcopy_(ncv, &workl[ihd], &c__1, &workl[iw], &c__1);
+	if (s_cmp(type__, "SHIFTI", (ftnlen)6, (ftnlen)6) == 0) {
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+		workl[ihd + k - 1] = 1. / workl[ihd + k - 1] + *sigma;
+/* L40: */
+	    }
+	} else if (s_cmp(type__, "BUCKLE", (ftnlen)6, (ftnlen)6) == 0) {
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+		workl[ihd + k - 1] = *sigma * workl[ihd + k - 1] / (workl[ihd 
+			+ k - 1] - 1.);
+/* L50: */
+	    }
+	} else if (s_cmp(type__, "CAYLEY", (ftnlen)6, (ftnlen)6) == 0) {
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+		workl[ihd + k - 1] = *sigma * (workl[ihd + k - 1] + 1.) / (
+			workl[ihd + k - 1] - 1.);
+/* L60: */
+	    }
+	}
+
+/*        %-------------------------------------------------------------%   
+          | *  Store the wanted NCONV lambda values into D.             |   
+          | *  Sort the NCONV wanted lambda in WORKL(IHD:IHD+NCONV-1)   |   
+          |    into ascending order and apply sort to the NCONV theta   |   
+          |    values in the transformed system. We'll need this to     |   
+          |    compute Ritz estimates in the original system.           |   
+          | *  Finally sort the lambda's into ascending order and apply |   
+          |    to Ritz vectors if wanted. Else just sort lambda's into  |   
+          |    ascending order.                                         |   
+          | NOTES:                                                      |   
+          | *workl(iw:iw+ncv-1) contain the theta ordered so that they  |   
+          |  match the ordering of the lambda. We'll use them again for |   
+          |  Ritz vector purification.                                  |   
+          %-------------------------------------------------------------% */
+
+	igraphdcopy_(&nconv, &workl[ihd], &c__1, &d__[1], &c__1);
+	igraphdsortr_("LA", &c_true, &nconv, &workl[ihd], &workl[iw]);
+	if (*rvec) {
+	    igraphdsesrt_("LA", rvec, &nconv, &d__[1], ncv, &workl[iq], &ldq);
+	} else {
+	    igraphdcopy_(ncv, &workl[bounds], &c__1, &workl[ihb], &c__1);
+	    d__1 = bnorm2 / rnorm;
+	    igraphdscal_(ncv, &d__1, &workl[ihb], &c__1);
+	    igraphdsortr_("LA", &c_true, &nconv, &d__[1], &workl[ihb]);
+	}
+
+    }
+
+/*     %------------------------------------------------%   
+       | Compute the Ritz vectors. Transform the wanted |   
+       | eigenvectors of the symmetric tridiagonal H by |   
+       | the Lanczos basis matrix V.                    |   
+       %------------------------------------------------% */
+
+    if (*rvec && *(unsigned char *)howmny == 'A') {
+
+/*        %----------------------------------------------------------%   
+          | Compute the QR factorization of the matrix representing  |   
+          | the wanted invariant subspace located in the first NCONV |   
+          | columns of workl(iq,ldq).                                |   
+          %----------------------------------------------------------% */
+
+	igraphdgeqr2_(ncv, &nconv, &workl[iq], &ldq, &workl[iw + *ncv], &workl[ihb],
+		 &ierr);
+
+
+/*        %--------------------------------------------------------%   
+          | * Postmultiply V by Q.                                 |   
+          | * Copy the first NCONV columns of VQ into Z.           |   
+          | The N by NCONV matrix Z is now a matrix representation |   
+          | of the approximate invariant subspace associated with  |   
+          | the Ritz values in workl(ihd).                         |   
+          %--------------------------------------------------------% */
+
+	igraphdorm2r_("Right", "Notranspose", n, ncv, &nconv, &workl[iq], &ldq, &
+		workl[iw + *ncv], &v[v_offset], ldv, &workd[*n + 1], &ierr);
+	igraphdlacpy_("All", n, &nconv, &v[v_offset], ldv, &z__[z_offset], ldz);
+
+/*        %-----------------------------------------------------%   
+          | In order to compute the Ritz estimates for the Ritz |   
+          | values in both systems, need the last row of the    |   
+          | eigenvector matrix. Remember, it's in factored form |   
+          %-----------------------------------------------------% */
+
+	i__1 = *ncv - 1;
+	for (j = 1; j <= i__1; ++j) {
+	    workl[ihb + j - 1] = 0.;
+/* L65: */
+	}
+	workl[ihb + *ncv - 1] = 1.;
+	igraphdorm2r_("Left", "Transpose", ncv, &c__1, &nconv, &workl[iq], &ldq, &
+		workl[iw + *ncv], &workl[ihb], ncv, &temp, &ierr);
+
+    } else if (*rvec && *(unsigned char *)howmny == 'S') {
+
+/*     Not yet implemented. See remark 2 above. */
+
+    }
+
+    if (s_cmp(type__, "REGULR", (ftnlen)6, (ftnlen)6) == 0 && *rvec) {
+
+	i__1 = *ncv;
+	for (j = 1; j <= i__1; ++j) {
+	    workl[ihb + j - 1] = rnorm * (d__1 = workl[ihb + j - 1], abs(d__1)
+		    );
+/* L70: */
+	}
+
+    } else if (s_cmp(type__, "REGULR", (ftnlen)6, (ftnlen)6) != 0 && *rvec) {
+
+/*        %-------------------------------------------------%   
+          | *  Determine Ritz estimates of the theta.       |   
+          |    If RVEC = .true. then compute Ritz estimates |   
+          |               of the theta.                     |   
+          |    If RVEC = .false. then copy Ritz estimates   |   
+          |              as computed by dsaupd.             |   
+          | *  Determine Ritz estimates of the lambda.      |   
+          %-------------------------------------------------% */
+
+	igraphdscal_(ncv, &bnorm2, &workl[ihb], &c__1);
+	if (s_cmp(type__, "SHIFTI", (ftnlen)6, (ftnlen)6) == 0) {
+
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+/* Computing 2nd power */
+		d__2 = workl[iw + k - 1];
+		workl[ihb + k - 1] = (d__1 = workl[ihb + k - 1], abs(d__1)) / 
+			(d__2 * d__2);
+/* L80: */
+	    }
+
+	} else if (s_cmp(type__, "BUCKLE", (ftnlen)6, (ftnlen)6) == 0) {
+
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+/* Computing 2nd power */
+		d__2 = workl[iw + k - 1] - 1.;
+		workl[ihb + k - 1] = *sigma * (d__1 = workl[ihb + k - 1], abs(
+			d__1)) / (d__2 * d__2);
+/* L90: */
+	    }
+
+	} else if (s_cmp(type__, "CAYLEY", (ftnlen)6, (ftnlen)6) == 0) {
+
+	    i__1 = *ncv;
+	    for (k = 1; k <= i__1; ++k) {
+		workl[ihb + k - 1] = (d__1 = workl[ihb + k - 1] / workl[iw + 
+			k - 1] * (workl[iw + k - 1] - 1.), abs(d__1));
+/* L100: */
+	    }
+
+	}
+
+    }
+
+    if (s_cmp(type__, "REGULR", (ftnlen)6, (ftnlen)6) != 0 && msglvl > 1) {
+	igraphdvout_(&logfil, &nconv, &d__[1], &ndigit, "_seupd: Untransformed con"
+		"verged Ritz values", (ftnlen)43);
+	igraphdvout_(&logfil, &nconv, &workl[ihb], &ndigit, "_seupd: Ritz estimate"
+		"s of the untransformed Ritz values", (ftnlen)55);
+    } else if (msglvl > 1) {
+	igraphdvout_(&logfil, &nconv, &d__[1], &ndigit, "_seupd: Converged Ritz va"
+		"lues", (ftnlen)29);
+	igraphdvout_(&logfil, &nconv, &workl[ihb], &ndigit, "_seupd: Associated Ri"
+		"tz estimates", (ftnlen)33);
+    }
+
+/*     %-------------------------------------------------%   
+       | Ritz vector purification step. Formally perform |   
+       | one of inverse subspace iteration. Only used    |   
+       | for MODE = 3,4,5. See reference 7               |   
+       %-------------------------------------------------% */
+
+    if (*rvec && (s_cmp(type__, "SHIFTI", (ftnlen)6, (ftnlen)6) == 0 || s_cmp(
+	    type__, "CAYLEY", (ftnlen)6, (ftnlen)6) == 0)) {
+
+	i__1 = nconv - 1;
+	for (k = 0; k <= i__1; ++k) {
+	    workl[iw + k] = workl[iq + k * ldq + *ncv - 1] / workl[iw + k];
+/* L110: */
+	}
+
+    } else if (*rvec && s_cmp(type__, "BUCKLE", (ftnlen)6, (ftnlen)6) == 0) {
+
+	i__1 = nconv - 1;
+	for (k = 0; k <= i__1; ++k) {
+	    workl[iw + k] = workl[iq + k * ldq + *ncv - 1] / (workl[iw + k] - 
+		    1.);
+/* L120: */
+	}
+
+    }
+
+    if (s_cmp(type__, "REGULR", (ftnlen)6, (ftnlen)6) != 0) {
+	igraphdger_(n, &nconv, &c_b119, &resid[1], &c__1, &workl[iw], &c__1, &z__[
+		z_offset], ldz);
+    }
+
+L9000:
+
+    return 0;
+
+/*     %---------------%   
+       | End of dseupd |   
+       %---------------% */
+
+} /* igraphdseupd_ */
+
diff --git a/igraph/src/dsgets.c b/igraph/src/dsgets.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsgets.c
@@ -0,0 +1,259 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static logical c_true = TRUE_;
+static integer c__1 = 1;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsgets   
+
+   \Description:   
+    Given the eigenvalues of the symmetric tridiagonal matrix H,   
+    computes the NP shifts AMU that are zeros of the polynomial of   
+    degree NP which filters out components of the unwanted eigenvectors   
+    corresponding to the AMU's based on some given criteria.   
+
+    NOTE: This is called even in the case of user specified shifts in   
+    order to sort the eigenvalues, and error bounds of H for later use.   
+
+   \Usage:   
+    call dsgets   
+       ( ISHIFT, WHICH, KEV, NP, RITZ, BOUNDS, SHIFTS )   
+
+   \Arguments   
+    ISHIFT  Integer.  (INPUT)   
+            Method for selecting the implicit shifts at each iteration.   
+            ISHIFT = 0: user specified shifts   
+            ISHIFT = 1: exact shift with respect to the matrix H.   
+
+    WHICH   Character*2.  (INPUT)   
+            Shift selection criteria.   
+            'LM' -> KEV eigenvalues of largest magnitude are retained.   
+            'SM' -> KEV eigenvalues of smallest magnitude are retained.   
+            'LA' -> KEV eigenvalues of largest value are retained.   
+            'SA' -> KEV eigenvalues of smallest value are retained.   
+            'BE' -> KEV eigenvalues, half from each end of the spectrum.   
+                    If KEV is odd, compute one more from the high end.   
+
+    KEV      Integer.  (INPUT)   
+            KEV+NP is the size of the matrix H.   
+
+    NP      Integer.  (INPUT)   
+            Number of implicit shifts to be computed.   
+
+    RITZ    Double precision array of length KEV+NP.  (INPUT/OUTPUT)   
+            On INPUT, RITZ contains the eigenvalues of H.   
+            On OUTPUT, RITZ are sorted so that the unwanted eigenvalues   
+            are in the first NP locations and the wanted part is in   
+            the last KEV locations.  When exact shifts are selected, the   
+            unwanted part corresponds to the shifts to be applied.   
+
+    BOUNDS  Double precision array of length KEV+NP.  (INPUT/OUTPUT)   
+            Error bounds corresponding to the ordering in RITZ.   
+
+    SHIFTS  Double precision array of length NP.  (INPUT/OUTPUT)   
+            On INPUT:  contains the user specified shifts if ISHIFT = 0.   
+            On OUTPUT: contains the shifts sorted into decreasing order   
+            of magnitude with respect to the Ritz estimates contained in   
+            BOUNDS. If ISHIFT = 0, SHIFTS is not modified on exit.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \Routines called:   
+       dsortr  ARPACK utility sorting routine.   
+       ivout   ARPACK utility routine that prints integers.   
+       second  ARPACK utility routine for timing.   
+       dvout   ARPACK utility routine that prints vectors.   
+       dcopy   Level 1 BLAS that copies one vector to another.   
+       dswap   Level 1 BLAS that swaps the contents of two vectors.   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/93: Version ' 2.1'   
+
+   \SCCS Information: @(#)   
+   FILE: sgets.F   SID: 2.4   DATE OF SID: 4/19/96   RELEASE: 2   
+
+   \Remarks   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsgets_(integer *ishift, char *which, integer *kev, 
+	integer *np, doublereal *ritz, doublereal *bounds, doublereal *shifts)
+{
+    /* System generated locals */
+    integer i__1;
+
+    /* Builtin functions */
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+
+    /* Local variables */
+    real t0, t1;
+    integer kevd2;
+    extern /* Subroutine */ int igraphdswap_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdcopy_(integer *, doublereal *, integer 
+	    *, doublereal *, integer *), igraphdvout_(integer *, integer *, 
+	    doublereal *, integer *, char *, ftnlen), igraphivout_(integer *, 
+	    integer *, integer *, integer *, char *, ftnlen), igraphsecond_(real *);
+    integer logfil, ndigit, msgets = 0, msglvl;
+    real tsgets = 0.0;
+    extern /* Subroutine */ int igraphdsortr_(char *, logical *, integer *, 
+	    doublereal *, doublereal *);
+
+
+/*     %----------------------------------------------------%   
+       | Include files for debugging and timing information |   
+       %----------------------------------------------------%   
+
+
+       %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %------------%   
+       | Parameters |   
+       %------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %----------------------%   
+       | External Subroutines |   
+       %----------------------%   
+
+
+       %---------------------%   
+       | Intrinsic Functions |   
+       %---------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------%   
+
+       %-------------------------------%   
+       | Initialize timing statistics  |   
+       | & message level for debugging |   
+       %-------------------------------%   
+
+       Parameter adjustments */
+    --shifts;
+    --bounds;
+    --ritz;
+
+    /* Function Body */
+    igraphsecond_(&t0);
+    msglvl = msgets;
+
+    if (s_cmp(which, "BE", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        %-----------------------------------------------------%   
+          | Both ends of the spectrum are requested.            |   
+          | Sort the eigenvalues into algebraically increasing  |   
+          | order first then swap high end of the spectrum next |   
+          | to low end in appropriate locations.                |   
+          | NOTE: when np < floor(kev/2) be careful not to swap |   
+          | overlapping locations.                              |   
+          %-----------------------------------------------------% */
+
+	i__1 = *kev + *np;
+	igraphdsortr_("LA", &c_true, &i__1, &ritz[1], &bounds[1]);
+	kevd2 = *kev / 2;
+	if (*kev > 1) {
+	    i__1 = min(kevd2,*np);
+	    igraphdswap_(&i__1, &ritz[1], &c__1, &ritz[max(kevd2,*np) + 1], &c__1);
+	    i__1 = min(kevd2,*np);
+	    igraphdswap_(&i__1, &bounds[1], &c__1, &bounds[max(kevd2,*np) + 1], &
+		    c__1);
+	}
+
+    } else {
+
+/*        %----------------------------------------------------%   
+          | LM, SM, LA, SA case.                               |   
+          | Sort the eigenvalues of H into the desired order   |   
+          | and apply the resulting order to BOUNDS.           |   
+          | The eigenvalues are sorted so that the wanted part |   
+          | are always in the last KEV locations.               |   
+          %----------------------------------------------------% */
+
+	i__1 = *kev + *np;
+	igraphdsortr_(which, &c_true, &i__1, &ritz[1], &bounds[1]);
+    }
+
+    if (*ishift == 1 && *np > 0) {
+
+/*        %-------------------------------------------------------%   
+          | Sort the unwanted Ritz values used as shifts so that  |   
+          | the ones with largest Ritz estimates are first.       |   
+          | This will tend to minimize the effects of the         |   
+          | forward instability of the iteration when the shifts  |   
+          | are applied in subroutine dsapps.                     |   
+          %-------------------------------------------------------% */
+
+	igraphdsortr_("SM", &c_true, np, &bounds[1], &ritz[1]);
+	igraphdcopy_(np, &ritz[1], &c__1, &shifts[1], &c__1);
+    }
+
+    igraphsecond_(&t1);
+    tsgets += t1 - t0;
+
+    if (msglvl > 0) {
+	igraphivout_(&logfil, &c__1, kev, &ndigit, "_sgets: KEV is", (ftnlen)14);
+	igraphivout_(&logfil, &c__1, np, &ndigit, "_sgets: NP is", (ftnlen)13);
+	i__1 = *kev + *np;
+	igraphdvout_(&logfil, &i__1, &ritz[1], &ndigit, "_sgets: Eigenvalues of cu"
+		"rrent H matrix", (ftnlen)39);
+	i__1 = *kev + *np;
+	igraphdvout_(&logfil, &i__1, &bounds[1], &ndigit, "_sgets: Associated Ritz"
+		" estimates", (ftnlen)33);
+    }
+
+    return 0;
+
+/*     %---------------%   
+       | End of dsgets |   
+       %---------------% */
+
+} /* igraphdsgets_ */
+
diff --git a/igraph/src/dsortc.c b/igraph/src/dsortc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsortc.c
@@ -0,0 +1,406 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsortc   
+
+   \Description:   
+    Sorts the complex array in XREAL and XIMAG into the order   
+    specified by WHICH and optionally applies the permutation to the   
+    real array Y. It is assumed that if an element of XIMAG is   
+    nonzero, then its negative is also an element. In other words,   
+    both members of a complex conjugate pair are to be sorted and the   
+    pairs are kept adjacent to each other.   
+
+   \Usage:   
+    call dsortc   
+       ( WHICH, APPLY, N, XREAL, XIMAG, Y )   
+
+   \Arguments   
+    WHICH   Character*2.  (Input)   
+            'LM' -> sort XREAL,XIMAG into increasing order of magnitude.   
+            'SM' -> sort XREAL,XIMAG into decreasing order of magnitude.   
+            'LR' -> sort XREAL into increasing order of algebraic.   
+            'SR' -> sort XREAL into decreasing order of algebraic.   
+            'LI' -> sort XIMAG into increasing order of magnitude.   
+            'SI' -> sort XIMAG into decreasing order of magnitude.   
+            NOTE: If an element of XIMAG is non-zero, then its negative   
+                  is also an element.   
+
+    APPLY   Logical.  (Input)   
+            APPLY = .TRUE.  -> apply the sorted order to array Y.   
+            APPLY = .FALSE. -> do not apply the sorted order to array Y.   
+
+    N       Integer.  (INPUT)   
+            Size of the arrays.   
+
+    XREAL,  Double precision array of length N.  (INPUT/OUTPUT)   
+    XIMAG   Real and imaginary part of the array to be sorted.   
+
+    Y       Double precision array of length N.  (INPUT/OUTPUT)   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       xx/xx/92: Version ' 2.1'   
+                 Adapted from the sort routine in LANSO.   
+
+   \SCCS Information: @(#)   
+   FILE: sortc.F   SID: 2.3   DATE OF SID: 4/20/96   RELEASE: 2   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsortc_(char *which, logical *apply, integer *n, 
+	doublereal *xreal, doublereal *ximag, doublereal *y)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+
+    /* Local variables */
+    integer i__, j, igap;
+    doublereal temp, temp1, temp2;
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *);
+
+
+/*     %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %--------------------%   
+       | External Functions |   
+       %--------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------% */
+
+    igap = *n / 2;
+
+    if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        %------------------------------------------------------%   
+          | Sort XREAL,XIMAG into increasing order of magnitude. |   
+          %------------------------------------------------------% */
+
+L10:
+	if (igap == 0) {
+	    goto L9000;
+	}
+
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L20:
+
+	    if (j < 0) {
+		goto L30;
+	    }
+
+	    temp1 = igraphdlapy2_(&xreal[j], &ximag[j]);
+	    temp2 = igraphdlapy2_(&xreal[j + igap], &ximag[j + igap]);
+
+	    if (temp1 > temp2) {
+		temp = xreal[j];
+		xreal[j] = xreal[j + igap];
+		xreal[j + igap] = temp;
+
+		temp = ximag[j];
+		ximag[j] = ximag[j + igap];
+		ximag[j + igap] = temp;
+
+		if (*apply) {
+		    temp = y[j];
+		    y[j] = y[j + igap];
+		    y[j + igap] = temp;
+		}
+	    } else {
+		goto L30;
+	    }
+	    j -= igap;
+	    goto L20;
+L30:
+	    ;
+	}
+	igap /= 2;
+	goto L10;
+
+    } else if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        %------------------------------------------------------%   
+          | Sort XREAL,XIMAG into decreasing order of magnitude. |   
+          %------------------------------------------------------% */
+
+L40:
+	if (igap == 0) {
+	    goto L9000;
+	}
+
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L50:
+
+	    if (j < 0) {
+		goto L60;
+	    }
+
+	    temp1 = igraphdlapy2_(&xreal[j], &ximag[j]);
+	    temp2 = igraphdlapy2_(&xreal[j + igap], &ximag[j + igap]);
+
+	    if (temp1 < temp2) {
+		temp = xreal[j];
+		xreal[j] = xreal[j + igap];
+		xreal[j + igap] = temp;
+
+		temp = ximag[j];
+		ximag[j] = ximag[j + igap];
+		ximag[j + igap] = temp;
+
+		if (*apply) {
+		    temp = y[j];
+		    y[j] = y[j + igap];
+		    y[j + igap] = temp;
+		}
+	    } else {
+		goto L60;
+	    }
+	    j -= igap;
+	    goto L50;
+L60:
+	    ;
+	}
+	igap /= 2;
+	goto L40;
+
+    } else if (s_cmp(which, "LR", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        %------------------------------------------------%   
+          | Sort XREAL into increasing order of algebraic. |   
+          %------------------------------------------------% */
+
+L70:
+	if (igap == 0) {
+	    goto L9000;
+	}
+
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L80:
+
+	    if (j < 0) {
+		goto L90;
+	    }
+
+	    if (xreal[j] > xreal[j + igap]) {
+		temp = xreal[j];
+		xreal[j] = xreal[j + igap];
+		xreal[j + igap] = temp;
+
+		temp = ximag[j];
+		ximag[j] = ximag[j + igap];
+		ximag[j + igap] = temp;
+
+		if (*apply) {
+		    temp = y[j];
+		    y[j] = y[j + igap];
+		    y[j + igap] = temp;
+		}
+	    } else {
+		goto L90;
+	    }
+	    j -= igap;
+	    goto L80;
+L90:
+	    ;
+	}
+	igap /= 2;
+	goto L70;
+
+    } else if (s_cmp(which, "SR", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        %------------------------------------------------%   
+          | Sort XREAL into decreasing order of algebraic. |   
+          %------------------------------------------------% */
+
+L100:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L110:
+
+	    if (j < 0) {
+		goto L120;
+	    }
+
+	    if (xreal[j] < xreal[j + igap]) {
+		temp = xreal[j];
+		xreal[j] = xreal[j + igap];
+		xreal[j + igap] = temp;
+
+		temp = ximag[j];
+		ximag[j] = ximag[j + igap];
+		ximag[j + igap] = temp;
+
+		if (*apply) {
+		    temp = y[j];
+		    y[j] = y[j + igap];
+		    y[j + igap] = temp;
+		}
+	    } else {
+		goto L120;
+	    }
+	    j -= igap;
+	    goto L110;
+L120:
+	    ;
+	}
+	igap /= 2;
+	goto L100;
+
+    } else if (s_cmp(which, "LI", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        %------------------------------------------------%   
+          | Sort XIMAG into increasing order of magnitude. |   
+          %------------------------------------------------% */
+
+L130:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L140:
+
+	    if (j < 0) {
+		goto L150;
+	    }
+
+	    if ((d__1 = ximag[j], abs(d__1)) > (d__2 = ximag[j + igap], abs(
+		    d__2))) {
+		temp = xreal[j];
+		xreal[j] = xreal[j + igap];
+		xreal[j + igap] = temp;
+
+		temp = ximag[j];
+		ximag[j] = ximag[j + igap];
+		ximag[j + igap] = temp;
+
+		if (*apply) {
+		    temp = y[j];
+		    y[j] = y[j + igap];
+		    y[j + igap] = temp;
+		}
+	    } else {
+		goto L150;
+	    }
+	    j -= igap;
+	    goto L140;
+L150:
+	    ;
+	}
+	igap /= 2;
+	goto L130;
+
+    } else if (s_cmp(which, "SI", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        %------------------------------------------------%   
+          | Sort XIMAG into decreasing order of magnitude. |   
+          %------------------------------------------------% */
+
+L160:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L170:
+
+	    if (j < 0) {
+		goto L180;
+	    }
+
+	    if ((d__1 = ximag[j], abs(d__1)) < (d__2 = ximag[j + igap], abs(
+		    d__2))) {
+		temp = xreal[j];
+		xreal[j] = xreal[j + igap];
+		xreal[j + igap] = temp;
+
+		temp = ximag[j];
+		ximag[j] = ximag[j + igap];
+		ximag[j + igap] = temp;
+
+		if (*apply) {
+		    temp = y[j];
+		    y[j] = y[j + igap];
+		    y[j + igap] = temp;
+		}
+	    } else {
+		goto L180;
+	    }
+	    j -= igap;
+	    goto L170;
+L180:
+	    ;
+	}
+	igap /= 2;
+	goto L160;
+    }
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dsortc |   
+       %---------------% */
+
+} /* igraphdsortc_ */
+
diff --git a/igraph/src/dsortr.c b/igraph/src/dsortr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsortr.c
@@ -0,0 +1,268 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dsortr   
+
+   \Description:   
+    Sort the array X1 in the order specified by WHICH and optionally   
+    applies the permutation to the array X2.   
+
+   \Usage:   
+    call dsortr   
+       ( WHICH, APPLY, N, X1, X2 )   
+
+   \Arguments   
+    WHICH   Character*2.  (Input)   
+            'LM' -> X1 is sorted into increasing order of magnitude.   
+            'SM' -> X1 is sorted into decreasing order of magnitude.   
+            'LA' -> X1 is sorted into increasing order of algebraic.   
+            'SA' -> X1 is sorted into decreasing order of algebraic.   
+
+    APPLY   Logical.  (Input)   
+            APPLY = .TRUE.  -> apply the sorted order to X2.   
+            APPLY = .FALSE. -> do not apply the sorted order to X2.   
+
+    N       Integer.  (INPUT)   
+            Size of the arrays.   
+
+    X1      Double precision array of length N.  (INPUT/OUTPUT)   
+            The array to be sorted.   
+
+    X2      Double precision array of length N.  (INPUT/OUTPUT)   
+            Only referenced if APPLY = .TRUE.   
+
+   \EndDoc   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \Revision history:   
+       12/16/93: Version ' 2.1'.   
+                 Adapted from the sort routine in LANSO.   
+
+   \SCCS Information: @(#)   
+   FILE: sortr.F   SID: 2.3   DATE OF SID: 4/19/96   RELEASE: 2   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdsortr_(char *which, logical *apply, integer *n, 
+	doublereal *x1, doublereal *x2)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+
+    /* Local variables */
+    integer i__, j, igap;
+    doublereal temp;
+
+
+/*     %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+       %---------------%   
+       | Local Scalars |   
+       %---------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------% */
+
+    igap = *n / 2;
+
+    if (s_cmp(which, "SA", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        X1 is sorted into decreasing order of algebraic. */
+
+L10:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L20:
+
+	    if (j < 0) {
+		goto L30;
+	    }
+
+	    if (x1[j] < x1[j + igap]) {
+		temp = x1[j];
+		x1[j] = x1[j + igap];
+		x1[j + igap] = temp;
+		if (*apply) {
+		    temp = x2[j];
+		    x2[j] = x2[j + igap];
+		    x2[j + igap] = temp;
+		}
+	    } else {
+		goto L30;
+	    }
+	    j -= igap;
+	    goto L20;
+L30:
+	    ;
+	}
+	igap /= 2;
+	goto L10;
+
+    } else if (s_cmp(which, "SM", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        X1 is sorted into decreasing order of magnitude. */
+
+L40:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L50:
+
+	    if (j < 0) {
+		goto L60;
+	    }
+
+	    if ((d__1 = x1[j], abs(d__1)) < (d__2 = x1[j + igap], abs(d__2))) 
+		    {
+		temp = x1[j];
+		x1[j] = x1[j + igap];
+		x1[j + igap] = temp;
+		if (*apply) {
+		    temp = x2[j];
+		    x2[j] = x2[j + igap];
+		    x2[j + igap] = temp;
+		}
+	    } else {
+		goto L60;
+	    }
+	    j -= igap;
+	    goto L50;
+L60:
+	    ;
+	}
+	igap /= 2;
+	goto L40;
+
+    } else if (s_cmp(which, "LA", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        X1 is sorted into increasing order of algebraic. */
+
+L70:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L80:
+
+	    if (j < 0) {
+		goto L90;
+	    }
+
+	    if (x1[j] > x1[j + igap]) {
+		temp = x1[j];
+		x1[j] = x1[j + igap];
+		x1[j + igap] = temp;
+		if (*apply) {
+		    temp = x2[j];
+		    x2[j] = x2[j + igap];
+		    x2[j + igap] = temp;
+		}
+	    } else {
+		goto L90;
+	    }
+	    j -= igap;
+	    goto L80;
+L90:
+	    ;
+	}
+	igap /= 2;
+	goto L70;
+
+    } else if (s_cmp(which, "LM", (ftnlen)2, (ftnlen)2) == 0) {
+
+/*        X1 is sorted into increasing order of magnitude. */
+
+L100:
+	if (igap == 0) {
+	    goto L9000;
+	}
+	i__1 = *n - 1;
+	for (i__ = igap; i__ <= i__1; ++i__) {
+	    j = i__ - igap;
+L110:
+
+	    if (j < 0) {
+		goto L120;
+	    }
+
+	    if ((d__1 = x1[j], abs(d__1)) > (d__2 = x1[j + igap], abs(d__2))) 
+		    {
+		temp = x1[j];
+		x1[j] = x1[j + igap];
+		x1[j + igap] = temp;
+		if (*apply) {
+		    temp = x2[j];
+		    x2[j] = x2[j + igap];
+		    x2[j + igap] = temp;
+		}
+	    } else {
+		goto L120;
+	    }
+	    j -= igap;
+	    goto L110;
+L120:
+	    ;
+	}
+	igap /= 2;
+	goto L100;
+    }
+
+L9000:
+    return 0;
+
+/*     %---------------%   
+       | End of dsortr |   
+       %---------------% */
+
+} /* igraphdsortr_ */
+
diff --git a/igraph/src/dstatn.c b/igraph/src/dstatn.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dstatn.c
@@ -0,0 +1,83 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+
+/*     %---------------------------------------------%   
+       | Initialize statistic and timing information |   
+       | for nonsymmetric Arnoldi code.              |   
+       %---------------------------------------------%   
+
+   \Author   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \SCCS Information: @(#)   
+   FILE: statn.F   SID: 2.4   DATE OF SID: 4/20/96   RELEASE: 2   
+
+   Subroutine */ int igraphdstatn_(void)
+{
+    integer nbx, nopx;
+    real trvec, tmvbx, tnaup2, tgetv0, tneigh;
+    integer nitref;
+    real tnaupd, titref, tnaitr, tngets, tnapps, tnconv;
+    integer nrorth, nrstrt;
+    real tmvopx;
+
+
+/*     %--------------------------------%   
+       | See stat.doc for documentation |   
+       %--------------------------------%   
+
+
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------% */
+
+    nopx = 0;
+    nbx = 0;
+    nrorth = 0;
+    nitref = 0;
+    nrstrt = 0;
+
+    tnaupd = 0.f;
+    tnaup2 = 0.f;
+    tnaitr = 0.f;
+    tneigh = 0.f;
+    tngets = 0.f;
+    tnapps = 0.f;
+    tnconv = 0.f;
+    titref = 0.f;
+    tgetv0 = 0.f;
+    trvec = 0.f;
+
+/*     %----------------------------------------------------%   
+       | User time including reverse communication overhead |   
+       %----------------------------------------------------% */
+
+    tmvopx = 0.f;
+    tmvbx = 0.f;
+
+    return 0;
+
+
+/*     %---------------%   
+       | End of dstatn |   
+       %---------------% */
+
+} /* igraphdstatn_ */
+
diff --git a/igraph/src/dstats.c b/igraph/src/dstats.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dstats.c
@@ -0,0 +1,64 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+
+/* \SCCS Information: @(#)   
+   FILE: stats.F   SID: 2.1   DATE OF SID: 4/19/96   RELEASE: 2   
+       %---------------------------------------------%   
+       | Initialize statistic and timing information |   
+       | for symmetric Arnoldi code.                 |   
+       %---------------------------------------------%   
+   Subroutine */ int igraphdstats_(void)
+{
+    integer nbx, nopx;
+    real trvec, tmvbx, tgetv0, tsaup2;
+    integer nitref;
+    real titref, tseigt, tsaupd, tsaitr, tsgets, tsapps;
+    integer nrorth;
+    real tsconv;
+    integer nrstrt;
+    real tmvopx;
+
+/*     %--------------------------------%   
+       | See stat.doc for documentation |   
+       %--------------------------------%   
+       %-----------------------%   
+       | Executable Statements |   
+       %-----------------------% */
+    nopx = 0;
+    nbx = 0;
+    nrorth = 0;
+    nitref = 0;
+    nrstrt = 0;
+    tsaupd = 0.f;
+    tsaup2 = 0.f;
+    tsaitr = 0.f;
+    tseigt = 0.f;
+    tsgets = 0.f;
+    tsapps = 0.f;
+    tsconv = 0.f;
+    titref = 0.f;
+    tgetv0 = 0.f;
+    trvec = 0.f;
+/*     %----------------------------------------------------%   
+       | User time including reverse communication overhead |   
+       %----------------------------------------------------% */
+    tmvopx = 0.f;
+    tmvbx = 0.f;
+    return 0;
+
+/*     End of dstats */
+
+} /* igraphdstats_ */
+
diff --git a/igraph/src/dstebz.c b/igraph/src/dstebz.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dstebz.c
@@ -0,0 +1,867 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__3 = 3;
+static integer c__2 = 2;
+static integer c__0 = 0;
+
+/* > \brief \b DSTEBZ   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DSTEBZ + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dstebz.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dstebz.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dstebz.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DSTEBZ( RANGE, ORDER, N, VL, VU, IL, IU, ABSTOL, D, E,   
+                            M, NSPLIT, W, IBLOCK, ISPLIT, WORK, IWORK,   
+                            INFO )   
+
+         CHARACTER          ORDER, RANGE   
+         INTEGER            IL, INFO, IU, M, N, NSPLIT   
+         DOUBLE PRECISION   ABSTOL, VL, VU   
+         INTEGER            IBLOCK( * ), ISPLIT( * ), IWORK( * )   
+         DOUBLE PRECISION   D( * ), E( * ), W( * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DSTEBZ computes the eigenvalues of a symmetric tridiagonal   
+   > matrix T.  The user may ask for all eigenvalues, all eigenvalues   
+   > in the half-open interval (VL, VU], or the IL-th through IU-th   
+   > eigenvalues.   
+   >   
+   > To avoid overflow, the matrix must be scaled so that its   
+   > largest element is no greater than overflow**(1/2) * underflow**(1/4) in absolute value, and for greatest
+   
+   > accuracy, it should not be much smaller than that.   
+   >   
+   > See W. Kahan "Accurate Eigenvalues of a Symmetric Tridiagonal   
+   > Matrix", Report CS41, Computer Science Dept., Stanford   
+   > University, July 21, 1966.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] RANGE   
+   > \verbatim   
+   >          RANGE is CHARACTER*1   
+   >          = 'A': ("All")   all eigenvalues will be found.   
+   >          = 'V': ("Value") all eigenvalues in the half-open interval   
+   >                           (VL, VU] will be found.   
+   >          = 'I': ("Index") the IL-th through IU-th eigenvalues (of the   
+   >                           entire matrix) will be found.   
+   > \endverbatim   
+   >   
+   > \param[in] ORDER   
+   > \verbatim   
+   >          ORDER is CHARACTER*1   
+   >          = 'B': ("By Block") the eigenvalues will be grouped by   
+   >                              split-off block (see IBLOCK, ISPLIT) and   
+   >                              ordered from smallest to largest within   
+   >                              the block.   
+   >          = 'E': ("Entire matrix")   
+   >                              the eigenvalues for the entire matrix   
+   >                              will be ordered from smallest to   
+   >                              largest.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the tridiagonal matrix T.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] VU   
+   > \verbatim   
+   >          VU is DOUBLE PRECISION   
+   >   
+   >          If RANGE='V', the lower and upper bounds of the interval to   
+   >          be searched for eigenvalues.  Eigenvalues less than or equal   
+   >          to VL, or greater than VU, will not be returned.  VL < VU.   
+   >          Not referenced if RANGE = 'A' or 'I'.   
+   > \endverbatim   
+   >   
+   > \param[in] IL   
+   > \verbatim   
+   >          IL is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IU   
+   > \verbatim   
+   >          IU is INTEGER   
+   >   
+   >          If RANGE='I', the indices (in ascending order) of the   
+   >          smallest and largest eigenvalues to be returned.   
+   >          1 <= IL <= IU <= N, if N > 0; IL = 1 and IU = 0 if N = 0.   
+   >          Not referenced if RANGE = 'A' or 'V'.   
+   > \endverbatim   
+   >   
+   > \param[in] ABSTOL   
+   > \verbatim   
+   >          ABSTOL is DOUBLE PRECISION   
+   >          The absolute tolerance for the eigenvalues.  An eigenvalue   
+   >          (or cluster) is considered to be located if it has been   
+   >          determined to lie in an interval whose width is ABSTOL or   
+   >          less.  If ABSTOL is less than or equal to zero, then ULP*|T|   
+   >          will be used, where |T| means the 1-norm of T.   
+   >   
+   >          Eigenvalues will be computed most accurately when ABSTOL is   
+   >          set to twice the underflow threshold 2*DLAMCH('S'), not zero.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The n diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (n-1) off-diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[out] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The actual number of eigenvalues found. 0 <= M <= N.   
+   >          (See also the description of INFO=2,3.)   
+   > \endverbatim   
+   >   
+   > \param[out] NSPLIT   
+   > \verbatim   
+   >          NSPLIT is INTEGER   
+   >          The number of diagonal blocks in the matrix T.   
+   >          1 <= NSPLIT <= N.   
+   > \endverbatim   
+   >   
+   > \param[out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          On exit, the first M elements of W will contain the   
+   >          eigenvalues.  (DSTEBZ may use the remaining N-M elements as   
+   >          workspace.)   
+   > \endverbatim   
+   >   
+   > \param[out] IBLOCK   
+   > \verbatim   
+   >          IBLOCK is INTEGER array, dimension (N)   
+   >          At each row/column j where E(j) is zero or small, the   
+   >          matrix T is considered to split into a block diagonal   
+   >          matrix.  On exit, if INFO = 0, IBLOCK(i) specifies to which   
+   >          block (from 1 to the number of blocks) the eigenvalue W(i)   
+   >          belongs.  (DSTEBZ may use the remaining N-M elements as   
+   >          workspace.)   
+   > \endverbatim   
+   >   
+   > \param[out] ISPLIT   
+   > \verbatim   
+   >          ISPLIT is INTEGER array, dimension (N)   
+   >          The splitting points, at which T breaks up into submatrices.   
+   >          The first submatrix consists of rows/columns 1 to ISPLIT(1),   
+   >          the second of rows/columns ISPLIT(1)+1 through ISPLIT(2),   
+   >          etc., and the NSPLIT-th consists of rows/columns   
+   >          ISPLIT(NSPLIT-1)+1 through ISPLIT(NSPLIT)=N.   
+   >          (Only the first NSPLIT elements will actually be used, but   
+   >          since the user cannot know a priori what value NSPLIT will   
+   >          have, N words must be reserved for ISPLIT.)   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (4*N)   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (3*N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  some or all of the eigenvalues failed to converge or   
+   >                were not computed:   
+   >                =1 or 3: Bisection failed to converge for some   
+   >                        eigenvalues; these eigenvalues are flagged by a   
+   >                        negative block number.  The effect is that the   
+   >                        eigenvalues may not be as accurate as the   
+   >                        absolute and relative tolerances.  This is   
+   >                        generally caused by unexpectedly inaccurate   
+   >                        arithmetic.   
+   >                =2 or 3: RANGE='I' only: Not all of the eigenvalues   
+   >                        IL:IU were found.   
+   >                        Effect: M < IU+1-IL   
+   >                        Cause:  non-monotonic arithmetic, causing the   
+   >                                Sturm sequence to be non-monotonic.   
+   >                        Cure:   recalculate, using RANGE='A', and pick   
+   >                                out eigenvalues IL:IU.  In some cases,   
+   >                                increasing the PARAMETER "FUDGE" may   
+   >                                make things work.   
+   >                = 4:    RANGE='I', and the Gershgorin interval   
+   >                        initially used was too small.  No eigenvalues   
+   >                        were computed.   
+   >                        Probable cause: your machine has sloppy   
+   >                                        floating-point arithmetic.   
+   >                        Cure: Increase the PARAMETER "FUDGE",   
+   >                              recompile, and try again.   
+   > \endverbatim   
+
+   > \par Internal Parameters:   
+    =========================   
+   >   
+   > \verbatim   
+   >  RELFAC  DOUBLE PRECISION, default = 2.0e0   
+   >          The relative tolerance.  An interval (a,b] lies within   
+   >          "relative tolerance" if  b-a < RELFAC*ulp*max(|a|,|b|),   
+   >          where "ulp" is the machine precision (distance from 1 to   
+   >          the next larger floating point number.)   
+   >   
+   >  FUDGE   DOUBLE PRECISION, default = 2   
+   >          A "fudge factor" to widen the Gershgorin intervals.  Ideally,   
+   >          a value of 1 should work, but on machines with sloppy   
+   >          arithmetic, this needs to be larger.  The default for   
+   >          publicly released versions should be large enough to handle   
+   >          the worst machine around.  Note that this has no effect   
+   >          on accuracy of the solution.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdstebz_(char *range, char *order, integer *n, doublereal 
+	*vl, doublereal *vu, integer *il, integer *iu, doublereal *abstol, 
+	doublereal *d__, doublereal *e, integer *m, integer *nsplit, 
+	doublereal *w, integer *iblock, integer *isplit, doublereal *work, 
+	integer *iwork, integer *info)
+{
+    /* System generated locals */
+    integer i__1, i__2, i__3;
+    doublereal d__1, d__2, d__3, d__4, d__5;
+
+    /* Builtin functions */
+    double sqrt(doublereal), log(doublereal);
+
+    /* Local variables */
+    integer j, ib, jb, ie, je, nb;
+    doublereal gl;
+    integer im, in;
+    doublereal gu;
+    integer iw;
+    doublereal wl, wu;
+    integer nwl;
+    doublereal ulp, wlu, wul;
+    integer nwu;
+    doublereal tmp1, tmp2;
+    integer iend, ioff, iout, itmp1, jdisc;
+    extern logical igraphlsame_(char *, char *);
+    integer iinfo;
+    doublereal atoli;
+    integer iwoff;
+    doublereal bnorm;
+    integer itmax;
+    doublereal wkill, rtoli, tnorm;
+    extern doublereal igraphdlamch_(char *);
+    integer ibegin;
+    extern /* Subroutine */ int igraphdlaebz_(integer *, integer *, integer *, 
+	    integer *, integer *, integer *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, integer *,
+	     doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *);
+    integer irange, idiscl;
+    doublereal safemn;
+    integer idumma[1];
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    integer idiscu, iorder;
+    logical ncnvrg;
+    doublereal pivmin;
+    logical toofew;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Parameter adjustments */
+    --iwork;
+    --work;
+    --isplit;
+    --iblock;
+    --w;
+    --e;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+
+/*     Decode RANGE */
+
+    if (igraphlsame_(range, "A")) {
+	irange = 1;
+    } else if (igraphlsame_(range, "V")) {
+	irange = 2;
+    } else if (igraphlsame_(range, "I")) {
+	irange = 3;
+    } else {
+	irange = 0;
+    }
+
+/*     Decode ORDER */
+
+    if (igraphlsame_(order, "B")) {
+	iorder = 2;
+    } else if (igraphlsame_(order, "E")) {
+	iorder = 1;
+    } else {
+	iorder = 0;
+    }
+
+/*     Check for Errors */
+
+    if (irange <= 0) {
+	*info = -1;
+    } else if (iorder <= 0) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -3;
+    } else if (irange == 2) {
+	if (*vl >= *vu) {
+	    *info = -5;
+	}
+    } else if (irange == 3 && (*il < 1 || *il > max(1,*n))) {
+	*info = -6;
+    } else if (irange == 3 && (*iu < min(*n,*il) || *iu > *n)) {
+	*info = -7;
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DSTEBZ", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Initialize error flags */
+
+    *info = 0;
+    ncnvrg = FALSE_;
+    toofew = FALSE_;
+
+/*     Quick return if possible */
+
+    *m = 0;
+    if (*n == 0) {
+	return 0;
+    }
+
+/*     Simplifications: */
+
+    if (irange == 3 && *il == 1 && *iu == *n) {
+	irange = 1;
+    }
+
+/*     Get machine constants   
+       NB is the minimum vector length for vector bisection, or 0   
+       if only scalar is to be done. */
+
+    safemn = igraphdlamch_("S");
+    ulp = igraphdlamch_("P");
+    rtoli = ulp * 2.;
+    nb = igraphilaenv_(&c__1, "DSTEBZ", " ", n, &c_n1, &c_n1, &c_n1, (ftnlen)6, (
+	    ftnlen)1);
+    if (nb <= 1) {
+	nb = 0;
+    }
+
+/*     Special Case when N=1 */
+
+    if (*n == 1) {
+	*nsplit = 1;
+	isplit[1] = 1;
+	if (irange == 2 && (*vl >= d__[1] || *vu < d__[1])) {
+	    *m = 0;
+	} else {
+	    w[1] = d__[1];
+	    iblock[1] = 1;
+	    *m = 1;
+	}
+	return 0;
+    }
+
+/*     Compute Splitting Points */
+
+    *nsplit = 1;
+    work[*n] = 0.;
+    pivmin = 1.;
+
+    i__1 = *n;
+    for (j = 2; j <= i__1; ++j) {
+/* Computing 2nd power */
+	d__1 = e[j - 1];
+	tmp1 = d__1 * d__1;
+/* Computing 2nd power */
+	d__2 = ulp;
+	if ((d__1 = d__[j] * d__[j - 1], abs(d__1)) * (d__2 * d__2) + safemn 
+		> tmp1) {
+	    isplit[*nsplit] = j - 1;
+	    ++(*nsplit);
+	    work[j - 1] = 0.;
+	} else {
+	    work[j - 1] = tmp1;
+	    pivmin = max(pivmin,tmp1);
+	}
+/* L10: */
+    }
+    isplit[*nsplit] = *n;
+    pivmin *= safemn;
+
+/*     Compute Interval and ATOLI */
+
+    if (irange == 3) {
+
+/*        RANGE='I': Compute the interval containing eigenvalues   
+                     IL through IU.   
+
+          Compute Gershgorin interval for entire (split) matrix   
+          and use it as the initial interval */
+
+	gu = d__[1];
+	gl = d__[1];
+	tmp1 = 0.;
+
+	i__1 = *n - 1;
+	for (j = 1; j <= i__1; ++j) {
+	    tmp2 = sqrt(work[j]);
+/* Computing MAX */
+	    d__1 = gu, d__2 = d__[j] + tmp1 + tmp2;
+	    gu = max(d__1,d__2);
+/* Computing MIN */
+	    d__1 = gl, d__2 = d__[j] - tmp1 - tmp2;
+	    gl = min(d__1,d__2);
+	    tmp1 = tmp2;
+/* L20: */
+	}
+
+/* Computing MAX */
+	d__1 = gu, d__2 = d__[*n] + tmp1;
+	gu = max(d__1,d__2);
+/* Computing MIN */
+	d__1 = gl, d__2 = d__[*n] - tmp1;
+	gl = min(d__1,d__2);
+/* Computing MAX */
+	d__1 = abs(gl), d__2 = abs(gu);
+	tnorm = max(d__1,d__2);
+	gl = gl - tnorm * 2.1 * ulp * *n - pivmin * 4.2000000000000002;
+	gu = gu + tnorm * 2.1 * ulp * *n + pivmin * 2.1;
+
+/*        Compute Iteration parameters */
+
+	itmax = (integer) ((log(tnorm + pivmin) - log(pivmin)) / log(2.)) + 2;
+	if (*abstol <= 0.) {
+	    atoli = ulp * tnorm;
+	} else {
+	    atoli = *abstol;
+	}
+
+	work[*n + 1] = gl;
+	work[*n + 2] = gl;
+	work[*n + 3] = gu;
+	work[*n + 4] = gu;
+	work[*n + 5] = gl;
+	work[*n + 6] = gu;
+	iwork[1] = -1;
+	iwork[2] = -1;
+	iwork[3] = *n + 1;
+	iwork[4] = *n + 1;
+	iwork[5] = *il - 1;
+	iwork[6] = *iu;
+
+	igraphdlaebz_(&c__3, &itmax, n, &c__2, &c__2, &nb, &atoli, &rtoli, &pivmin, 
+		&d__[1], &e[1], &work[1], &iwork[5], &work[*n + 1], &work[*n 
+		+ 5], &iout, &iwork[1], &w[1], &iblock[1], &iinfo);
+
+	if (iwork[6] == *iu) {
+	    wl = work[*n + 1];
+	    wlu = work[*n + 3];
+	    nwl = iwork[1];
+	    wu = work[*n + 4];
+	    wul = work[*n + 2];
+	    nwu = iwork[4];
+	} else {
+	    wl = work[*n + 2];
+	    wlu = work[*n + 4];
+	    nwl = iwork[2];
+	    wu = work[*n + 3];
+	    wul = work[*n + 1];
+	    nwu = iwork[3];
+	}
+
+	if (nwl < 0 || nwl >= *n || nwu < 1 || nwu > *n) {
+	    *info = 4;
+	    return 0;
+	}
+    } else {
+
+/*        RANGE='A' or 'V' -- Set ATOLI   
+
+   Computing MAX */
+	d__3 = abs(d__[1]) + abs(e[1]), d__4 = (d__1 = d__[*n], abs(d__1)) + (
+		d__2 = e[*n - 1], abs(d__2));
+	tnorm = max(d__3,d__4);
+
+	i__1 = *n - 1;
+	for (j = 2; j <= i__1; ++j) {
+/* Computing MAX */
+	    d__4 = tnorm, d__5 = (d__1 = d__[j], abs(d__1)) + (d__2 = e[j - 1]
+		    , abs(d__2)) + (d__3 = e[j], abs(d__3));
+	    tnorm = max(d__4,d__5);
+/* L30: */
+	}
+
+	if (*abstol <= 0.) {
+	    atoli = ulp * tnorm;
+	} else {
+	    atoli = *abstol;
+	}
+
+	if (irange == 2) {
+	    wl = *vl;
+	    wu = *vu;
+	} else {
+	    wl = 0.;
+	    wu = 0.;
+	}
+    }
+
+/*     Find Eigenvalues -- Loop Over Blocks and recompute NWL and NWU.   
+       NWL accumulates the number of eigenvalues .le. WL,   
+       NWU accumulates the number of eigenvalues .le. WU */
+
+    *m = 0;
+    iend = 0;
+    *info = 0;
+    nwl = 0;
+    nwu = 0;
+
+    i__1 = *nsplit;
+    for (jb = 1; jb <= i__1; ++jb) {
+	ioff = iend;
+	ibegin = ioff + 1;
+	iend = isplit[jb];
+	in = iend - ioff;
+
+	if (in == 1) {
+
+/*           Special Case -- IN=1 */
+
+	    if (irange == 1 || wl >= d__[ibegin] - pivmin) {
+		++nwl;
+	    }
+	    if (irange == 1 || wu >= d__[ibegin] - pivmin) {
+		++nwu;
+	    }
+	    if (irange == 1 || wl < d__[ibegin] - pivmin && wu >= d__[ibegin] 
+		    - pivmin) {
+		++(*m);
+		w[*m] = d__[ibegin];
+		iblock[*m] = jb;
+	    }
+	} else {
+
+/*           General Case -- IN > 1   
+
+             Compute Gershgorin Interval   
+             and use it as the initial interval */
+
+	    gu = d__[ibegin];
+	    gl = d__[ibegin];
+	    tmp1 = 0.;
+
+	    i__2 = iend - 1;
+	    for (j = ibegin; j <= i__2; ++j) {
+		tmp2 = (d__1 = e[j], abs(d__1));
+/* Computing MAX */
+		d__1 = gu, d__2 = d__[j] + tmp1 + tmp2;
+		gu = max(d__1,d__2);
+/* Computing MIN */
+		d__1 = gl, d__2 = d__[j] - tmp1 - tmp2;
+		gl = min(d__1,d__2);
+		tmp1 = tmp2;
+/* L40: */
+	    }
+
+/* Computing MAX */
+	    d__1 = gu, d__2 = d__[iend] + tmp1;
+	    gu = max(d__1,d__2);
+/* Computing MIN */
+	    d__1 = gl, d__2 = d__[iend] - tmp1;
+	    gl = min(d__1,d__2);
+/* Computing MAX */
+	    d__1 = abs(gl), d__2 = abs(gu);
+	    bnorm = max(d__1,d__2);
+	    gl = gl - bnorm * 2.1 * ulp * in - pivmin * 2.1;
+	    gu = gu + bnorm * 2.1 * ulp * in + pivmin * 2.1;
+
+/*           Compute ATOLI for the current submatrix */
+
+	    if (*abstol <= 0.) {
+/* Computing MAX */
+		d__1 = abs(gl), d__2 = abs(gu);
+		atoli = ulp * max(d__1,d__2);
+	    } else {
+		atoli = *abstol;
+	    }
+
+	    if (irange > 1) {
+		if (gu < wl) {
+		    nwl += in;
+		    nwu += in;
+		    goto L70;
+		}
+		gl = max(gl,wl);
+		gu = min(gu,wu);
+		if (gl >= gu) {
+		    goto L70;
+		}
+	    }
+
+/*           Set Up Initial Interval */
+
+	    work[*n + 1] = gl;
+	    work[*n + in + 1] = gu;
+	    igraphdlaebz_(&c__1, &c__0, &in, &in, &c__1, &nb, &atoli, &rtoli, &
+		    pivmin, &d__[ibegin], &e[ibegin], &work[ibegin], idumma, &
+		    work[*n + 1], &work[*n + (in << 1) + 1], &im, &iwork[1], &
+		    w[*m + 1], &iblock[*m + 1], &iinfo);
+
+	    nwl += iwork[1];
+	    nwu += iwork[in + 1];
+	    iwoff = *m - iwork[1];
+
+/*           Compute Eigenvalues */
+
+	    itmax = (integer) ((log(gu - gl + pivmin) - log(pivmin)) / log(2.)
+		    ) + 2;
+	    igraphdlaebz_(&c__2, &itmax, &in, &in, &c__1, &nb, &atoli, &rtoli, &
+		    pivmin, &d__[ibegin], &e[ibegin], &work[ibegin], idumma, &
+		    work[*n + 1], &work[*n + (in << 1) + 1], &iout, &iwork[1],
+		     &w[*m + 1], &iblock[*m + 1], &iinfo);
+
+/*           Copy Eigenvalues Into W and IBLOCK   
+             Use -JB for block number for unconverged eigenvalues. */
+
+	    i__2 = iout;
+	    for (j = 1; j <= i__2; ++j) {
+		tmp1 = (work[j + *n] + work[j + in + *n]) * .5;
+
+/*              Flag non-convergence. */
+
+		if (j > iout - iinfo) {
+		    ncnvrg = TRUE_;
+		    ib = -jb;
+		} else {
+		    ib = jb;
+		}
+		i__3 = iwork[j + in] + iwoff;
+		for (je = iwork[j] + 1 + iwoff; je <= i__3; ++je) {
+		    w[je] = tmp1;
+		    iblock[je] = ib;
+/* L50: */
+		}
+/* L60: */
+	    }
+
+	    *m += im;
+	}
+L70:
+	;
+    }
+
+/*     If RANGE='I', then (WL,WU) contains eigenvalues NWL+1,...,NWU   
+       If NWL+1 < IL or NWU > IU, discard extra eigenvalues. */
+
+    if (irange == 3) {
+	im = 0;
+	idiscl = *il - 1 - nwl;
+	idiscu = nwu - *iu;
+
+	if (idiscl > 0 || idiscu > 0) {
+	    i__1 = *m;
+	    for (je = 1; je <= i__1; ++je) {
+		if (w[je] <= wlu && idiscl > 0) {
+		    --idiscl;
+		} else if (w[je] >= wul && idiscu > 0) {
+		    --idiscu;
+		} else {
+		    ++im;
+		    w[im] = w[je];
+		    iblock[im] = iblock[je];
+		}
+/* L80: */
+	    }
+	    *m = im;
+	}
+	if (idiscl > 0 || idiscu > 0) {
+
+/*           Code to deal with effects of bad arithmetic:   
+             Some low eigenvalues to be discarded are not in (WL,WLU],   
+             or high eigenvalues to be discarded are not in (WUL,WU]   
+             so just kill off the smallest IDISCL/largest IDISCU   
+             eigenvalues, by simply finding the smallest/largest   
+             eigenvalue(s).   
+
+             (If N(w) is monotone non-decreasing, this should never   
+                 happen.) */
+
+	    if (idiscl > 0) {
+		wkill = wu;
+		i__1 = idiscl;
+		for (jdisc = 1; jdisc <= i__1; ++jdisc) {
+		    iw = 0;
+		    i__2 = *m;
+		    for (je = 1; je <= i__2; ++je) {
+			if (iblock[je] != 0 && (w[je] < wkill || iw == 0)) {
+			    iw = je;
+			    wkill = w[je];
+			}
+/* L90: */
+		    }
+		    iblock[iw] = 0;
+/* L100: */
+		}
+	    }
+	    if (idiscu > 0) {
+
+		wkill = wl;
+		i__1 = idiscu;
+		for (jdisc = 1; jdisc <= i__1; ++jdisc) {
+		    iw = 0;
+		    i__2 = *m;
+		    for (je = 1; je <= i__2; ++je) {
+			if (iblock[je] != 0 && (w[je] > wkill || iw == 0)) {
+			    iw = je;
+			    wkill = w[je];
+			}
+/* L110: */
+		    }
+		    iblock[iw] = 0;
+/* L120: */
+		}
+	    }
+	    im = 0;
+	    i__1 = *m;
+	    for (je = 1; je <= i__1; ++je) {
+		if (iblock[je] != 0) {
+		    ++im;
+		    w[im] = w[je];
+		    iblock[im] = iblock[je];
+		}
+/* L130: */
+	    }
+	    *m = im;
+	}
+	if (idiscl < 0 || idiscu < 0) {
+	    toofew = TRUE_;
+	}
+    }
+
+/*     If ORDER='B', do nothing -- the eigenvalues are already sorted   
+          by block.   
+       If ORDER='E', sort the eigenvalues from smallest to largest */
+
+    if (iorder == 1 && *nsplit > 1) {
+	i__1 = *m - 1;
+	for (je = 1; je <= i__1; ++je) {
+	    ie = 0;
+	    tmp1 = w[je];
+	    i__2 = *m;
+	    for (j = je + 1; j <= i__2; ++j) {
+		if (w[j] < tmp1) {
+		    ie = j;
+		    tmp1 = w[j];
+		}
+/* L140: */
+	    }
+
+	    if (ie != 0) {
+		itmp1 = iblock[ie];
+		w[ie] = w[je];
+		iblock[ie] = iblock[je];
+		w[je] = tmp1;
+		iblock[je] = itmp1;
+	    }
+/* L150: */
+	}
+    }
+
+    *info = 0;
+    if (ncnvrg) {
+	++(*info);
+    }
+    if (toofew) {
+	*info += 2;
+    }
+    return 0;
+
+/*     End of DSTEBZ */
+
+} /* igraphdstebz_ */
+
diff --git a/igraph/src/dstein.c b/igraph/src/dstein.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dstein.c
@@ -0,0 +1,525 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__2 = 2;
+static integer c__1 = 1;
+static integer c_n1 = -1;
+
+/* > \brief \b DSTEIN   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DSTEIN + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dstein.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dstein.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dstein.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DSTEIN( N, D, E, M, W, IBLOCK, ISPLIT, Z, LDZ, WORK,   
+                            IWORK, IFAIL, INFO )   
+
+         INTEGER            INFO, LDZ, M, N   
+         INTEGER            IBLOCK( * ), IFAIL( * ), ISPLIT( * ),   
+        $                   IWORK( * )   
+         DOUBLE PRECISION   D( * ), E( * ), W( * ), WORK( * ), Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DSTEIN computes the eigenvectors of a real symmetric tridiagonal   
+   > matrix T corresponding to specified eigenvalues, using inverse   
+   > iteration.   
+   >   
+   > The maximum number of iterations allowed for each eigenvector is   
+   > specified by an internal parameter MAXITS (currently set to 5).   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The n diagonal elements of the tridiagonal matrix T.   
+   > \endverbatim   
+   >   
+   > \param[in] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          The (n-1) subdiagonal elements of the tridiagonal matrix   
+   >          T, in elements 1 to N-1.   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of eigenvectors to be found.  0 <= M <= N.   
+   > \endverbatim   
+   >   
+   > \param[in] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          The first M elements of W contain the eigenvalues for   
+   >          which eigenvectors are to be computed.  The eigenvalues   
+   >          should be grouped by split-off block and ordered from   
+   >          smallest to largest within the block.  ( The output array   
+   >          W from DSTEBZ with ORDER = 'B' is expected here. )   
+   > \endverbatim   
+   >   
+   > \param[in] IBLOCK   
+   > \verbatim   
+   >          IBLOCK is INTEGER array, dimension (N)   
+   >          The submatrix indices associated with the corresponding   
+   >          eigenvalues in W; IBLOCK(i)=1 if eigenvalue W(i) belongs to   
+   >          the first submatrix from the top, =2 if W(i) belongs to   
+   >          the second submatrix, etc.  ( The output array IBLOCK   
+   >          from DSTEBZ is expected here. )   
+   > \endverbatim   
+   >   
+   > \param[in] ISPLIT   
+   > \verbatim   
+   >          ISPLIT is INTEGER array, dimension (N)   
+   >          The splitting points, at which T breaks up into submatrices.   
+   >          The first submatrix consists of rows/columns 1 to   
+   >          ISPLIT( 1 ), the second of rows/columns ISPLIT( 1 )+1   
+   >          through ISPLIT( 2 ), etc.   
+   >          ( The output array ISPLIT from DSTEBZ is expected here. )   
+   > \endverbatim   
+   >   
+   > \param[out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ, M)   
+   >          The computed eigenvectors.  The eigenvector associated   
+   >          with the eigenvalue W(i) is stored in the i-th column of   
+   >          Z.  Any vector which fails to converge is set to its current   
+   >          iterate after MAXITS iterations.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >          The leading dimension of the array Z.  LDZ >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (5*N)   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] IFAIL   
+   > \verbatim   
+   >          IFAIL is INTEGER array, dimension (M)   
+   >          On normal exit, all elements of IFAIL are zero.   
+   >          If one or more eigenvectors fail to converge after   
+   >          MAXITS iterations, then their indices are stored in   
+   >          array IFAIL.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit.   
+   >          < 0: if INFO = -i, the i-th argument had an illegal value   
+   >          > 0: if INFO = i, then i eigenvectors failed to converge   
+   >               in MAXITS iterations.  Their indices are stored in   
+   >               array IFAIL.   
+   > \endverbatim   
+
+   > \par Internal Parameters:   
+    =========================   
+   >   
+   > \verbatim   
+   >  MAXITS  INTEGER, default = 5   
+   >          The maximum number of iterations performed.   
+   >   
+   >  EXTRA   INTEGER, default = 2   
+   >          The number of iterations performed after norm growth   
+   >          criterion is satisfied, should be at least 1.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdstein_(integer *n, doublereal *d__, doublereal *e, 
+	integer *m, doublereal *w, integer *iblock, integer *isplit, 
+	doublereal *z__, integer *ldz, doublereal *work, integer *iwork, 
+	integer *ifail, integer *info)
+{
+    /* System generated locals */
+    integer z_dim1, z_offset, i__1, i__2, i__3;
+    doublereal d__1, d__2, d__3, d__4, d__5;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j, b1, j1, bn;
+    doublereal xj, scl, eps, sep, nrm, tol;
+    integer its;
+    doublereal xjm, ztr, eps1;
+    integer jblk, nblk;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    integer jmax;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    integer iseed[4], gpind, iinfo;
+    extern doublereal igraphdasum_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdaxpy_(integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *);
+    doublereal ortol;
+    integer indrv1, indrv2, indrv3, indrv4, indrv5;
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlagtf_(integer *, doublereal *, doublereal *,
+	     doublereal *, doublereal *, doublereal *, doublereal *, integer *
+	    , integer *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen), igraphdlagts_(
+	    integer *, integer *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, integer *, doublereal *, doublereal *, integer *);
+    integer nrmchk;
+    extern /* Subroutine */ int igraphdlarnv_(integer *, integer *, integer *, 
+	    doublereal *);
+    integer blksiz;
+    doublereal onenrm, dtpcrt, pertol;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    --d__;
+    --e;
+    --w;
+    --iblock;
+    --isplit;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --work;
+    --iwork;
+    --ifail;
+
+    /* Function Body */
+    *info = 0;
+    i__1 = *m;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	ifail[i__] = 0;
+/* L10: */
+    }
+
+    if (*n < 0) {
+	*info = -1;
+    } else if (*m < 0 || *m > *n) {
+	*info = -4;
+    } else if (*ldz < max(1,*n)) {
+	*info = -9;
+    } else {
+	i__1 = *m;
+	for (j = 2; j <= i__1; ++j) {
+	    if (iblock[j] < iblock[j - 1]) {
+		*info = -6;
+		goto L30;
+	    }
+	    if (iblock[j] == iblock[j - 1] && w[j] < w[j - 1]) {
+		*info = -5;
+		goto L30;
+	    }
+/* L20: */
+	}
+L30:
+	;
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DSTEIN", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0 || *m == 0) {
+	return 0;
+    } else if (*n == 1) {
+	z__[z_dim1 + 1] = 1.;
+	return 0;
+    }
+
+/*     Get machine constants. */
+
+    eps = igraphdlamch_("Precision");
+
+/*     Initialize seed for random number generator DLARNV. */
+
+    for (i__ = 1; i__ <= 4; ++i__) {
+	iseed[i__ - 1] = 1;
+/* L40: */
+    }
+
+/*     Initialize pointers. */
+
+    indrv1 = 0;
+    indrv2 = indrv1 + *n;
+    indrv3 = indrv2 + *n;
+    indrv4 = indrv3 + *n;
+    indrv5 = indrv4 + *n;
+
+/*     Compute eigenvectors of matrix blocks. */
+
+    j1 = 1;
+    i__1 = iblock[*m];
+    for (nblk = 1; nblk <= i__1; ++nblk) {
+
+/*        Find starting and ending indices of block nblk. */
+
+	if (nblk == 1) {
+	    b1 = 1;
+	} else {
+	    b1 = isplit[nblk - 1] + 1;
+	}
+	bn = isplit[nblk];
+	blksiz = bn - b1 + 1;
+	if (blksiz == 1) {
+	    goto L60;
+	}
+	gpind = b1;
+
+/*        Compute reorthogonalization criterion and stopping criterion. */
+
+	onenrm = (d__1 = d__[b1], abs(d__1)) + (d__2 = e[b1], abs(d__2));
+/* Computing MAX */
+	d__3 = onenrm, d__4 = (d__1 = d__[bn], abs(d__1)) + (d__2 = e[bn - 1],
+		 abs(d__2));
+	onenrm = max(d__3,d__4);
+	i__2 = bn - 1;
+	for (i__ = b1 + 1; i__ <= i__2; ++i__) {
+/* Computing MAX */
+	    d__4 = onenrm, d__5 = (d__1 = d__[i__], abs(d__1)) + (d__2 = e[
+		    i__ - 1], abs(d__2)) + (d__3 = e[i__], abs(d__3));
+	    onenrm = max(d__4,d__5);
+/* L50: */
+	}
+	ortol = onenrm * .001;
+
+	dtpcrt = sqrt(.1 / blksiz);
+
+/*        Loop through eigenvalues of block nblk. */
+
+L60:
+	jblk = 0;
+	i__2 = *m;
+	for (j = j1; j <= i__2; ++j) {
+	    if (iblock[j] != nblk) {
+		j1 = j;
+		goto L160;
+	    }
+	    ++jblk;
+	    xj = w[j];
+
+/*           Skip all the work if the block size is one. */
+
+	    if (blksiz == 1) {
+		work[indrv1 + 1] = 1.;
+		goto L120;
+	    }
+
+/*           If eigenvalues j and j-1 are too close, add a relatively   
+             small perturbation. */
+
+	    if (jblk > 1) {
+		eps1 = (d__1 = eps * xj, abs(d__1));
+		pertol = eps1 * 10.;
+		sep = xj - xjm;
+		if (sep < pertol) {
+		    xj = xjm + pertol;
+		}
+	    }
+
+	    its = 0;
+	    nrmchk = 0;
+
+/*           Get random starting vector. */
+
+	    igraphdlarnv_(&c__2, iseed, &blksiz, &work[indrv1 + 1]);
+
+/*           Copy the matrix T so it won't be destroyed in factorization. */
+
+	    igraphdcopy_(&blksiz, &d__[b1], &c__1, &work[indrv4 + 1], &c__1);
+	    i__3 = blksiz - 1;
+	    igraphdcopy_(&i__3, &e[b1], &c__1, &work[indrv2 + 2], &c__1);
+	    i__3 = blksiz - 1;
+	    igraphdcopy_(&i__3, &e[b1], &c__1, &work[indrv3 + 1], &c__1);
+
+/*           Compute LU factors with partial pivoting  ( PT = LU ) */
+
+	    tol = 0.;
+	    igraphdlagtf_(&blksiz, &work[indrv4 + 1], &xj, &work[indrv2 + 2], &work[
+		    indrv3 + 1], &tol, &work[indrv5 + 1], &iwork[1], &iinfo);
+
+/*           Update iteration count. */
+
+L70:
+	    ++its;
+	    if (its > 5) {
+		goto L100;
+	    }
+
+/*           Normalize and scale the righthand side vector Pb.   
+
+   Computing MAX */
+	    d__2 = eps, d__3 = (d__1 = work[indrv4 + blksiz], abs(d__1));
+	    scl = blksiz * onenrm * max(d__2,d__3) / igraphdasum_(&blksiz, &work[
+		    indrv1 + 1], &c__1);
+	    igraphdscal_(&blksiz, &scl, &work[indrv1 + 1], &c__1);
+
+/*           Solve the system LU = Pb. */
+
+	    igraphdlagts_(&c_n1, &blksiz, &work[indrv4 + 1], &work[indrv2 + 2], &
+		    work[indrv3 + 1], &work[indrv5 + 1], &iwork[1], &work[
+		    indrv1 + 1], &tol, &iinfo);
+
+/*           Reorthogonalize by modified Gram-Schmidt if eigenvalues are   
+             close enough. */
+
+	    if (jblk == 1) {
+		goto L90;
+	    }
+	    if ((d__1 = xj - xjm, abs(d__1)) > ortol) {
+		gpind = j;
+	    }
+	    if (gpind != j) {
+		i__3 = j - 1;
+		for (i__ = gpind; i__ <= i__3; ++i__) {
+		    ztr = -igraphddot_(&blksiz, &work[indrv1 + 1], &c__1, &z__[b1 + 
+			    i__ * z_dim1], &c__1);
+		    igraphdaxpy_(&blksiz, &ztr, &z__[b1 + i__ * z_dim1], &c__1, &
+			    work[indrv1 + 1], &c__1);
+/* L80: */
+		}
+	    }
+
+/*           Check the infinity norm of the iterate. */
+
+L90:
+	    jmax = igraphidamax_(&blksiz, &work[indrv1 + 1], &c__1);
+	    nrm = (d__1 = work[indrv1 + jmax], abs(d__1));
+
+/*           Continue for additional iterations after norm reaches   
+             stopping criterion. */
+
+	    if (nrm < dtpcrt) {
+		goto L70;
+	    }
+	    ++nrmchk;
+	    if (nrmchk < 3) {
+		goto L70;
+	    }
+
+	    goto L110;
+
+/*           If stopping criterion was not satisfied, update info and   
+             store eigenvector number in array ifail. */
+
+L100:
+	    ++(*info);
+	    ifail[*info] = j;
+
+/*           Accept iterate as jth eigenvector. */
+
+L110:
+	    scl = 1. / igraphdnrm2_(&blksiz, &work[indrv1 + 1], &c__1);
+	    jmax = igraphidamax_(&blksiz, &work[indrv1 + 1], &c__1);
+	    if (work[indrv1 + jmax] < 0.) {
+		scl = -scl;
+	    }
+	    igraphdscal_(&blksiz, &scl, &work[indrv1 + 1], &c__1);
+L120:
+	    i__3 = *n;
+	    for (i__ = 1; i__ <= i__3; ++i__) {
+		z__[i__ + j * z_dim1] = 0.;
+/* L130: */
+	    }
+	    i__3 = blksiz;
+	    for (i__ = 1; i__ <= i__3; ++i__) {
+		z__[b1 + i__ - 1 + j * z_dim1] = work[indrv1 + i__];
+/* L140: */
+	    }
+
+/*           Save the shift to check eigenvalue spacing at next   
+             iteration. */
+
+	    xjm = xj;
+
+/* L150: */
+	}
+L160:
+	;
+    }
+
+    return 0;
+
+/*     End of DSTEIN */
+
+} /* igraphdstein_ */
+
diff --git a/igraph/src/dstemr.c b/igraph/src/dstemr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dstemr.c
@@ -0,0 +1,831 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b18 = .001;
+
+/* > \brief \b DSTEMR   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DSTEMR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dstemr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dstemr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dstemr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DSTEMR( JOBZ, RANGE, N, D, E, VL, VU, IL, IU,   
+                            M, W, Z, LDZ, NZC, ISUPPZ, TRYRAC, WORK, LWORK,   
+                            IWORK, LIWORK, INFO )   
+
+         CHARACTER          JOBZ, RANGE   
+         LOGICAL            TRYRAC   
+         INTEGER            IL, INFO, IU, LDZ, NZC, LIWORK, LWORK, M, N   
+         DOUBLE PRECISION VL, VU   
+         INTEGER            ISUPPZ( * ), IWORK( * )   
+         DOUBLE PRECISION   D( * ), E( * ), W( * ), WORK( * )   
+         DOUBLE PRECISION   Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DSTEMR computes selected eigenvalues and, optionally, eigenvectors   
+   > of a real symmetric tridiagonal matrix T. Any such unreduced matrix has   
+   > a well defined set of pairwise different real eigenvalues, the corresponding   
+   > real eigenvectors are pairwise orthogonal.   
+   >   
+   > The spectrum may be computed either completely or partially by specifying   
+   > either an interval (VL,VU] or a range of indices IL:IU for the desired   
+   > eigenvalues.   
+   >   
+   > Depending on the number of desired eigenvalues, these are computed either   
+   > by bisection or the dqds algorithm. Numerically orthogonal eigenvectors are   
+   > computed by the use of various suitable L D L^T factorizations near clusters   
+   > of close eigenvalues (referred to as RRRs, Relatively Robust   
+   > Representations). An informal sketch of the algorithm follows.   
+   >   
+   > For each unreduced block (submatrix) of T,   
+   >    (a) Compute T - sigma I  = L D L^T, so that L and D   
+   >        define all the wanted eigenvalues to high relative accuracy.   
+   >        This means that small relative changes in the entries of D and L   
+   >        cause only small relative changes in the eigenvalues and   
+   >        eigenvectors. The standard (unfactored) representation of the   
+   >        tridiagonal matrix T does not have this property in general.   
+   >    (b) Compute the eigenvalues to suitable accuracy.   
+   >        If the eigenvectors are desired, the algorithm attains full   
+   >        accuracy of the computed eigenvalues only right before   
+   >        the corresponding vectors have to be computed, see steps c) and d).   
+   >    (c) For each cluster of close eigenvalues, select a new   
+   >        shift close to the cluster, find a new factorization, and refine   
+   >        the shifted eigenvalues to suitable accuracy.   
+   >    (d) For each eigenvalue with a large enough relative separation compute   
+   >        the corresponding eigenvector by forming a rank revealing twisted   
+   >        factorization. Go back to (c) for any clusters that remain.   
+   >   
+   > For more details, see:   
+   > - Inderjit S. Dhillon and Beresford N. Parlett: "Multiple representations   
+   >   to compute orthogonal eigenvectors of symmetric tridiagonal matrices,"   
+   >   Linear Algebra and its Applications, 387(1), pp. 1-28, August 2004.   
+   > - Inderjit Dhillon and Beresford Parlett: "Orthogonal Eigenvectors and   
+   >   Relative Gaps," SIAM Journal on Matrix Analysis and Applications, Vol. 25,   
+   >   2004.  Also LAPACK Working Note 154.   
+   > - Inderjit Dhillon: "A new O(n^2) algorithm for the symmetric   
+   >   tridiagonal eigenvalue/eigenvector problem",   
+   >   Computer Science Division Technical Report No. UCB/CSD-97-971,   
+   >   UC Berkeley, May 1997.   
+   >   
+   > Further Details   
+   > 1.DSTEMR works only on machines which follow IEEE-754   
+   > floating-point standard in their handling of infinities and NaNs.   
+   > This permits the use of efficient inner loops avoiding a check for   
+   > zero divisors.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOBZ   
+   > \verbatim   
+   >          JOBZ is CHARACTER*1   
+   >          = 'N':  Compute eigenvalues only;   
+   >          = 'V':  Compute eigenvalues and eigenvectors.   
+   > \endverbatim   
+   >   
+   > \param[in] RANGE   
+   > \verbatim   
+   >          RANGE is CHARACTER*1   
+   >          = 'A': all eigenvalues will be found.   
+   >          = 'V': all eigenvalues in the half-open interval (VL,VU]   
+   >                 will be found.   
+   >          = 'I': the IL-th through IU-th eigenvalues will be found.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the N diagonal elements of the tridiagonal matrix   
+   >          T. On exit, D is overwritten.   
+   > \endverbatim   
+   >   
+   > \param[in,out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the (N-1) subdiagonal elements of the tridiagonal   
+   >          matrix T in elements 1 to N-1 of E. E(N) need not be set on   
+   >          input, but is used internally as workspace.   
+   >          On exit, E is overwritten.   
+   > \endverbatim   
+   >   
+   > \param[in] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] VU   
+   > \verbatim   
+   >          VU is DOUBLE PRECISION   
+   >   
+   >          If RANGE='V', the lower and upper bounds of the interval to   
+   >          be searched for eigenvalues. VL < VU.   
+   >          Not referenced if RANGE = 'A' or 'I'.   
+   > \endverbatim   
+   >   
+   > \param[in] IL   
+   > \verbatim   
+   >          IL is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IU   
+   > \verbatim   
+   >          IU is INTEGER   
+   >   
+   >          If RANGE='I', the indices (in ascending order) of the   
+   >          smallest and largest eigenvalues to be returned.   
+   >          1 <= IL <= IU <= N, if N > 0.   
+   >          Not referenced if RANGE = 'A' or 'V'.   
+   > \endverbatim   
+   >   
+   > \param[out] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The total number of eigenvalues found.  0 <= M <= N.   
+   >          If RANGE = 'A', M = N, and if RANGE = 'I', M = IU-IL+1.   
+   > \endverbatim   
+   >   
+   > \param[out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          The first M elements contain the selected eigenvalues in   
+   >          ascending order.   
+   > \endverbatim   
+   >   
+   > \param[out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ, max(1,M) )   
+   >          If JOBZ = 'V', and if INFO = 0, then the first M columns of Z   
+   >          contain the orthonormal eigenvectors of the matrix T   
+   >          corresponding to the selected eigenvalues, with the i-th   
+   >          column of Z holding the eigenvector associated with W(i).   
+   >          If JOBZ = 'N', then Z is not referenced.   
+   >          Note: the user must ensure that at least max(1,M) columns are   
+   >          supplied in the array Z; if RANGE = 'V', the exact value of M   
+   >          is not known in advance and can be computed with a workspace   
+   >          query by setting NZC = -1, see below.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >          The leading dimension of the array Z.  LDZ >= 1, and if   
+   >          JOBZ = 'V', then LDZ >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] NZC   
+   > \verbatim   
+   >          NZC is INTEGER   
+   >          The number of eigenvectors to be held in the array Z.   
+   >          If RANGE = 'A', then NZC >= max(1,N).   
+   >          If RANGE = 'V', then NZC >= the number of eigenvalues in (VL,VU].   
+   >          If RANGE = 'I', then NZC >= IU-IL+1.   
+   >          If NZC = -1, then a workspace query is assumed; the   
+   >          routine calculates the number of columns of the array Z that   
+   >          are needed to hold the eigenvectors.   
+   >          This value is returned as the first entry of the Z array, and   
+   >          no error message related to NZC is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] ISUPPZ   
+   > \verbatim   
+   >          ISUPPZ is INTEGER ARRAY, dimension ( 2*max(1,M) )   
+   >          The support of the eigenvectors in Z, i.e., the indices   
+   >          indicating the nonzero elements in Z. The i-th computed eigenvector   
+   >          is nonzero only in elements ISUPPZ( 2*i-1 ) through   
+   >          ISUPPZ( 2*i ). This is relevant in the case when the matrix   
+   >          is split. ISUPPZ is only accessed when JOBZ is 'V' and N > 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] TRYRAC   
+   > \verbatim   
+   >          TRYRAC is LOGICAL   
+   >          If TRYRAC.EQ..TRUE., indicates that the code should check whether   
+   >          the tridiagonal matrix defines its eigenvalues to high relative   
+   >          accuracy.  If so, the code uses relative-accuracy preserving   
+   >          algorithms that might be (a bit) slower depending on the matrix.   
+   >          If the matrix does not define its eigenvalues to high relative   
+   >          accuracy, the code can uses possibly faster algorithms.   
+   >          If TRYRAC.EQ..FALSE., the code is not required to guarantee   
+   >          relatively accurate eigenvalues and can use the fastest possible   
+   >          techniques.   
+   >          On exit, a .TRUE. TRYRAC will be set to .FALSE. if the matrix   
+   >          does not define its eigenvalues to high relative accuracy.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (LWORK)   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal   
+   >          (and minimal) LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK. LWORK >= max(1,18*N)   
+   >          if JOBZ = 'V', and LWORK >= max(1,12*N) if JOBZ = 'N'.   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (LIWORK)   
+   >          On exit, if INFO = 0, IWORK(1) returns the optimal LIWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LIWORK   
+   > \verbatim   
+   >          LIWORK is INTEGER   
+   >          The dimension of the array IWORK.  LIWORK >= max(1,10*N)   
+   >          if the eigenvectors are desired, and LIWORK >= max(1,8*N)   
+   >          if only the eigenvalues are to be computed.   
+   >          If LIWORK = -1, then a workspace query is assumed; the   
+   >          routine only calculates the optimal size of the IWORK array,   
+   >          returns this value as the first entry of the IWORK array, and   
+   >          no error message related to LIWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          On exit, INFO   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  if INFO = 1X, internal error in DLARRE,   
+   >                if INFO = 2X, internal error in DLARRV.   
+   >                Here, the digit X = ABS( IINFO ) < 10, where IINFO is   
+   >                the nonzero error code returned by DLARRE or   
+   >                DLARRV, respectively.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2013   
+
+   > \ingroup doubleOTHERcomputational   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   > Beresford Parlett, University of California, Berkeley, USA \n   
+   > Jim Demmel, University of California, Berkeley, USA \n   
+   > Inderjit Dhillon, University of Texas, Austin, USA \n   
+   > Osni Marques, LBNL/NERSC, USA \n   
+   > Christof Voemel, University of California, Berkeley, USA   
+
+    =====================================================================   
+   Subroutine */ int igraphdstemr_(char *jobz, char *range, integer *n, doublereal *
+	d__, doublereal *e, doublereal *vl, doublereal *vu, integer *il, 
+	integer *iu, integer *m, doublereal *w, doublereal *z__, integer *ldz,
+	 integer *nzc, integer *isuppz, logical *tryrac, doublereal *work, 
+	integer *lwork, integer *iwork, integer *liwork, integer *info)
+{
+    /* System generated locals */
+    integer z_dim1, z_offset, i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j;
+    doublereal r1, r2;
+    integer jj;
+    doublereal cs;
+    integer in;
+    doublereal sn, wl, wu;
+    integer iil, iiu;
+    doublereal eps, tmp;
+    integer indd, iend, jblk, wend;
+    doublereal rmin, rmax;
+    integer itmp;
+    doublereal tnrm;
+    extern /* Subroutine */ int igraphdlae2_(doublereal *, doublereal *, doublereal 
+	    *, doublereal *, doublereal *);
+    integer inde2, itmp2;
+    doublereal rtol1, rtol2;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    doublereal scale;
+    integer indgp;
+    extern logical igraphlsame_(char *, char *);
+    integer iinfo, iindw, ilast;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdswap_(integer *, doublereal *, integer 
+	    *, doublereal *, integer *);
+    integer lwmin;
+    logical wantz;
+    extern /* Subroutine */ int igraphdlaev2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    logical alleig;
+    integer ibegin;
+    logical indeig;
+    integer iindbl;
+    logical valeig;
+    extern /* Subroutine */ int igraphdlarrc_(char *, integer *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, integer *,
+	     integer *, integer *, integer *), igraphdlarre_(char *, 
+	    integer *, doublereal *, doublereal *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, integer *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *, integer *);
+    integer wbegin;
+    doublereal safmin;
+    extern /* Subroutine */ int igraphdlarrj_(integer *, doublereal *, doublereal *,
+	     integer *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, doublereal *, integer *, doublereal *, doublereal *,
+	     integer *), igraphxerbla_(char *, integer *, ftnlen);
+    doublereal bignum;
+    integer inderr, iindwk, indgrs, offset;
+    extern doublereal igraphdlanst_(char *, integer *, doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlarrr_(integer *, doublereal *, doublereal *,
+	     integer *), igraphdlarrv_(integer *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, integer *, integer *, 
+	    integer *, integer *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *), igraphdlasrt_(char *, integer *, doublereal *, 
+	    integer *);
+    doublereal thresh;
+    integer iinspl, ifirst, indwrk, liwmin, nzcmin;
+    doublereal pivmin;
+    integer nsplit;
+    doublereal smlnum;
+    logical lquery, zquery;
+
+
+/*  -- LAPACK computational routine (version 3.5.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2013   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    --d__;
+    --e;
+    --w;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --isuppz;
+    --work;
+    --iwork;
+
+    /* Function Body */
+    wantz = igraphlsame_(jobz, "V");
+    alleig = igraphlsame_(range, "A");
+    valeig = igraphlsame_(range, "V");
+    indeig = igraphlsame_(range, "I");
+
+    lquery = *lwork == -1 || *liwork == -1;
+    zquery = *nzc == -1;
+/*     DSTEMR needs WORK of size 6*N, IWORK of size 3*N.   
+       In addition, DLARRE needs WORK of size 6*N, IWORK of size 5*N.   
+       Furthermore, DLARRV needs WORK of size 12*N, IWORK of size 7*N. */
+    if (wantz) {
+	lwmin = *n * 18;
+	liwmin = *n * 10;
+    } else {
+/*        need less workspace if only the eigenvalues are wanted */
+	lwmin = *n * 12;
+	liwmin = *n << 3;
+    }
+    wl = 0.;
+    wu = 0.;
+    iil = 0;
+    iiu = 0;
+    nsplit = 0;
+    if (valeig) {
+/*        We do not reference VL, VU in the cases RANGE = 'I','A'   
+          The interval (WL, WU] contains all the wanted eigenvalues.   
+          It is either given by the user or computed in DLARRE. */
+	wl = *vl;
+	wu = *vu;
+    } else if (indeig) {
+/*        We do not reference IL, IU in the cases RANGE = 'V','A' */
+	iil = *il;
+	iiu = *iu;
+    }
+
+    *info = 0;
+    if (! (wantz || igraphlsame_(jobz, "N"))) {
+	*info = -1;
+    } else if (! (alleig || valeig || indeig)) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -3;
+    } else if (valeig && *n > 0 && wu <= wl) {
+	*info = -7;
+    } else if (indeig && (iil < 1 || iil > *n)) {
+	*info = -8;
+    } else if (indeig && (iiu < iil || iiu > *n)) {
+	*info = -9;
+    } else if (*ldz < 1 || wantz && *ldz < *n) {
+	*info = -13;
+    } else if (*lwork < lwmin && ! lquery) {
+	*info = -17;
+    } else if (*liwork < liwmin && ! lquery) {
+	*info = -19;
+    }
+
+/*     Get machine constants. */
+
+    safmin = igraphdlamch_("Safe minimum");
+    eps = igraphdlamch_("Precision");
+    smlnum = safmin / eps;
+    bignum = 1. / smlnum;
+    rmin = sqrt(smlnum);
+/* Computing MIN */
+    d__1 = sqrt(bignum), d__2 = 1. / sqrt(sqrt(safmin));
+    rmax = min(d__1,d__2);
+
+    if (*info == 0) {
+	work[1] = (doublereal) lwmin;
+	iwork[1] = liwmin;
+
+	if (wantz && alleig) {
+	    nzcmin = *n;
+	} else if (wantz && valeig) {
+	    igraphdlarrc_("T", n, vl, vu, &d__[1], &e[1], &safmin, &nzcmin, &itmp, &
+		    itmp2, info);
+	} else if (wantz && indeig) {
+	    nzcmin = iiu - iil + 1;
+	} else {
+/*           WANTZ .EQ. FALSE. */
+	    nzcmin = 0;
+	}
+	if (zquery && *info == 0) {
+	    z__[z_dim1 + 1] = (doublereal) nzcmin;
+	} else if (*nzc < nzcmin && ! zquery) {
+	    *info = -14;
+	}
+    }
+    if (*info != 0) {
+
+	i__1 = -(*info);
+	igraphxerbla_("DSTEMR", &i__1, (ftnlen)6);
+
+	return 0;
+    } else if (lquery || zquery) {
+	return 0;
+    }
+
+/*     Handle N = 0, 1, and 2 cases immediately */
+
+    *m = 0;
+    if (*n == 0) {
+	return 0;
+    }
+
+    if (*n == 1) {
+	if (alleig || indeig) {
+	    *m = 1;
+	    w[1] = d__[1];
+	} else {
+	    if (wl < d__[1] && wu >= d__[1]) {
+		*m = 1;
+		w[1] = d__[1];
+	    }
+	}
+	if (wantz && ! zquery) {
+	    z__[z_dim1 + 1] = 1.;
+	    isuppz[1] = 1;
+	    isuppz[2] = 1;
+	}
+	return 0;
+    }
+
+    if (*n == 2) {
+	if (! wantz) {
+	    igraphdlae2_(&d__[1], &e[1], &d__[2], &r1, &r2);
+	} else if (wantz && ! zquery) {
+	    igraphdlaev2_(&d__[1], &e[1], &d__[2], &r1, &r2, &cs, &sn);
+	}
+	if (alleig || valeig && r2 > wl && r2 <= wu || indeig && iil == 1) {
+	    ++(*m);
+	    w[*m] = r2;
+	    if (wantz && ! zquery) {
+		z__[*m * z_dim1 + 1] = -sn;
+		z__[*m * z_dim1 + 2] = cs;
+/*              Note: At most one of SN and CS can be zero. */
+		if (sn != 0.) {
+		    if (cs != 0.) {
+			isuppz[(*m << 1) - 1] = 1;
+			isuppz[*m * 2] = 2;
+		    } else {
+			isuppz[(*m << 1) - 1] = 1;
+			isuppz[*m * 2] = 1;
+		    }
+		} else {
+		    isuppz[(*m << 1) - 1] = 2;
+		    isuppz[*m * 2] = 2;
+		}
+	    }
+	}
+	if (alleig || valeig && r1 > wl && r1 <= wu || indeig && iiu == 2) {
+	    ++(*m);
+	    w[*m] = r1;
+	    if (wantz && ! zquery) {
+		z__[*m * z_dim1 + 1] = cs;
+		z__[*m * z_dim1 + 2] = sn;
+/*              Note: At most one of SN and CS can be zero. */
+		if (sn != 0.) {
+		    if (cs != 0.) {
+			isuppz[(*m << 1) - 1] = 1;
+			isuppz[*m * 2] = 2;
+		    } else {
+			isuppz[(*m << 1) - 1] = 1;
+			isuppz[*m * 2] = 1;
+		    }
+		} else {
+		    isuppz[(*m << 1) - 1] = 2;
+		    isuppz[*m * 2] = 2;
+		}
+	    }
+	}
+    } else {
+/*     Continue with general N */
+	indgrs = 1;
+	inderr = (*n << 1) + 1;
+	indgp = *n * 3 + 1;
+	indd = (*n << 2) + 1;
+	inde2 = *n * 5 + 1;
+	indwrk = *n * 6 + 1;
+
+	iinspl = 1;
+	iindbl = *n + 1;
+	iindw = (*n << 1) + 1;
+	iindwk = *n * 3 + 1;
+
+/*        Scale matrix to allowable range, if necessary.   
+          The allowable range is related to the PIVMIN parameter; see the   
+          comments in DLARRD.  The preference for scaling small values   
+          up is heuristic; we expect users' matrices not to be close to the   
+          RMAX threshold. */
+
+	scale = 1.;
+	tnrm = igraphdlanst_("M", n, &d__[1], &e[1]);
+	if (tnrm > 0. && tnrm < rmin) {
+	    scale = rmin / tnrm;
+	} else if (tnrm > rmax) {
+	    scale = rmax / tnrm;
+	}
+	if (scale != 1.) {
+	    igraphdscal_(n, &scale, &d__[1], &c__1);
+	    i__1 = *n - 1;
+	    igraphdscal_(&i__1, &scale, &e[1], &c__1);
+	    tnrm *= scale;
+	    if (valeig) {
+/*              If eigenvalues in interval have to be found,   
+                scale (WL, WU] accordingly */
+		wl *= scale;
+		wu *= scale;
+	    }
+	}
+
+/*        Compute the desired eigenvalues of the tridiagonal after splitting   
+          into smaller subblocks if the corresponding off-diagonal elements   
+          are small   
+          THRESH is the splitting parameter for DLARRE   
+          A negative THRESH forces the old splitting criterion based on the   
+          size of the off-diagonal. A positive THRESH switches to splitting   
+          which preserves relative accuracy. */
+
+	if (*tryrac) {
+/*           Test whether the matrix warrants the more expensive relative approach. */
+	    igraphdlarrr_(n, &d__[1], &e[1], &iinfo);
+	} else {
+/*           The user does not care about relative accurately eigenvalues */
+	    iinfo = -1;
+	}
+/*        Set the splitting criterion */
+	if (iinfo == 0) {
+	    thresh = eps;
+	} else {
+	    thresh = -eps;
+/*           relative accuracy is desired but T does not guarantee it */
+	    *tryrac = FALSE_;
+	}
+
+	if (*tryrac) {
+/*           Copy original diagonal, needed to guarantee relative accuracy */
+	    igraphdcopy_(n, &d__[1], &c__1, &work[indd], &c__1);
+	}
+/*        Store the squares of the offdiagonal values of T */
+	i__1 = *n - 1;
+	for (j = 1; j <= i__1; ++j) {
+/* Computing 2nd power */
+	    d__1 = e[j];
+	    work[inde2 + j - 1] = d__1 * d__1;
+/* L5: */
+	}
+/*        Set the tolerance parameters for bisection */
+	if (! wantz) {
+/*           DLARRE computes the eigenvalues to full precision. */
+	    rtol1 = eps * 4.;
+	    rtol2 = eps * 4.;
+	} else {
+/*           DLARRE computes the eigenvalues to less than full precision.   
+             DLARRV will refine the eigenvalue approximations, and we can   
+             need less accurate initial bisection in DLARRE.   
+             Note: these settings do only affect the subset case and DLARRE */
+	    rtol1 = sqrt(eps);
+/* Computing MAX */
+	    d__1 = sqrt(eps) * .005, d__2 = eps * 4.;
+	    rtol2 = max(d__1,d__2);
+	}
+	igraphdlarre_(range, n, &wl, &wu, &iil, &iiu, &d__[1], &e[1], &work[inde2], 
+		&rtol1, &rtol2, &thresh, &nsplit, &iwork[iinspl], m, &w[1], &
+		work[inderr], &work[indgp], &iwork[iindbl], &iwork[iindw], &
+		work[indgrs], &pivmin, &work[indwrk], &iwork[iindwk], &iinfo);
+	if (iinfo != 0) {
+	    *info = abs(iinfo) + 10;
+	    return 0;
+	}
+/*        Note that if RANGE .NE. 'V', DLARRE computes bounds on the desired   
+          part of the spectrum. All desired eigenvalues are contained in   
+          (WL,WU] */
+	if (wantz) {
+
+/*           Compute the desired eigenvectors corresponding to the computed   
+             eigenvalues */
+
+	    igraphdlarrv_(n, &wl, &wu, &d__[1], &e[1], &pivmin, &iwork[iinspl], m, &
+		    c__1, m, &c_b18, &rtol1, &rtol2, &w[1], &work[inderr], &
+		    work[indgp], &iwork[iindbl], &iwork[iindw], &work[indgrs],
+		     &z__[z_offset], ldz, &isuppz[1], &work[indwrk], &iwork[
+		    iindwk], &iinfo);
+	    if (iinfo != 0) {
+		*info = abs(iinfo) + 20;
+		return 0;
+	    }
+	} else {
+/*           DLARRE computes eigenvalues of the (shifted) root representation   
+             DLARRV returns the eigenvalues of the unshifted matrix.   
+             However, if the eigenvectors are not desired by the user, we need   
+             to apply the corresponding shifts from DLARRE to obtain the   
+             eigenvalues of the original matrix. */
+	    i__1 = *m;
+	    for (j = 1; j <= i__1; ++j) {
+		itmp = iwork[iindbl + j - 1];
+		w[j] += e[iwork[iinspl + itmp - 1]];
+/* L20: */
+	    }
+	}
+
+	if (*tryrac) {
+/*           Refine computed eigenvalues so that they are relatively accurate   
+             with respect to the original matrix T. */
+	    ibegin = 1;
+	    wbegin = 1;
+	    i__1 = iwork[iindbl + *m - 1];
+	    for (jblk = 1; jblk <= i__1; ++jblk) {
+		iend = iwork[iinspl + jblk - 1];
+		in = iend - ibegin + 1;
+		wend = wbegin - 1;
+/*              check if any eigenvalues have to be refined in this block */
+L36:
+		if (wend < *m) {
+		    if (iwork[iindbl + wend] == jblk) {
+			++wend;
+			goto L36;
+		    }
+		}
+		if (wend < wbegin) {
+		    ibegin = iend + 1;
+		    goto L39;
+		}
+		offset = iwork[iindw + wbegin - 1] - 1;
+		ifirst = iwork[iindw + wbegin - 1];
+		ilast = iwork[iindw + wend - 1];
+		rtol2 = eps * 4.;
+		igraphdlarrj_(&in, &work[indd + ibegin - 1], &work[inde2 + ibegin - 
+			1], &ifirst, &ilast, &rtol2, &offset, &w[wbegin], &
+			work[inderr + wbegin - 1], &work[indwrk], &iwork[
+			iindwk], &pivmin, &tnrm, &iinfo);
+		ibegin = iend + 1;
+		wbegin = wend + 1;
+L39:
+		;
+	    }
+	}
+
+/*        If matrix was scaled, then rescale eigenvalues appropriately. */
+
+	if (scale != 1.) {
+	    d__1 = 1. / scale;
+	    igraphdscal_(m, &d__1, &w[1], &c__1);
+	}
+    }
+
+/*     If eigenvalues are not in increasing order, then sort them,   
+       possibly along with eigenvectors. */
+
+    if (nsplit > 1 || *n == 2) {
+	if (! wantz) {
+	    igraphdlasrt_("I", m, &w[1], &iinfo);
+	    if (iinfo != 0) {
+		*info = 3;
+		return 0;
+	    }
+	} else {
+	    i__1 = *m - 1;
+	    for (j = 1; j <= i__1; ++j) {
+		i__ = 0;
+		tmp = w[j];
+		i__2 = *m;
+		for (jj = j + 1; jj <= i__2; ++jj) {
+		    if (w[jj] < tmp) {
+			i__ = jj;
+			tmp = w[jj];
+		    }
+/* L50: */
+		}
+		if (i__ != 0) {
+		    w[i__] = w[j];
+		    w[j] = tmp;
+		    if (wantz) {
+			igraphdswap_(n, &z__[i__ * z_dim1 + 1], &c__1, &z__[j * 
+				z_dim1 + 1], &c__1);
+			itmp = isuppz[(i__ << 1) - 1];
+			isuppz[(i__ << 1) - 1] = isuppz[(j << 1) - 1];
+			isuppz[(j << 1) - 1] = itmp;
+			itmp = isuppz[i__ * 2];
+			isuppz[i__ * 2] = isuppz[j * 2];
+			isuppz[j * 2] = itmp;
+		    }
+		}
+/* L60: */
+	    }
+	}
+    }
+
+
+    work[1] = (doublereal) lwmin;
+    iwork[1] = liwmin;
+    return 0;
+
+/*     End of DSTEMR */
+
+} /* igraphdstemr_ */
+
diff --git a/igraph/src/dsteqr.c b/igraph/src/dsteqr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsteqr.c
@@ -0,0 +1,677 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static doublereal c_b9 = 0.;
+static doublereal c_b10 = 1.;
+static integer c__0 = 0;
+static integer c__1 = 1;
+static integer c__2 = 2;
+
+/* > \brief \b DSTEQR   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DSTEQR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dsteqr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dsteqr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dsteqr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DSTEQR( COMPZ, N, D, E, Z, LDZ, WORK, INFO )   
+
+         CHARACTER          COMPZ   
+         INTEGER            INFO, LDZ, N   
+         DOUBLE PRECISION   D( * ), E( * ), WORK( * ), Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DSTEQR computes all eigenvalues and, optionally, eigenvectors of a   
+   > symmetric tridiagonal matrix using the implicit QL or QR method.   
+   > The eigenvectors of a full or band symmetric matrix can also be found   
+   > if DSYTRD or DSPTRD or DSBTRD has been used to reduce this matrix to   
+   > tridiagonal form.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] COMPZ   
+   > \verbatim   
+   >          COMPZ is CHARACTER*1   
+   >          = 'N':  Compute eigenvalues only.   
+   >          = 'V':  Compute eigenvalues and eigenvectors of the original   
+   >                  symmetric matrix.  On entry, Z must contain the   
+   >                  orthogonal matrix used to reduce the original matrix   
+   >                  to tridiagonal form.   
+   >          = 'I':  Compute eigenvalues and eigenvectors of the   
+   >                  tridiagonal matrix.  Z is initialized to the identity   
+   >                  matrix.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the diagonal elements of the tridiagonal matrix.   
+   >          On exit, if INFO = 0, the eigenvalues in ascending order.   
+   > \endverbatim   
+   >   
+   > \param[in,out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          On entry, the (n-1) subdiagonal elements of the tridiagonal   
+   >          matrix.   
+   >          On exit, E has been destroyed.   
+   > \endverbatim   
+   >   
+   > \param[in,out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ, N)   
+   >          On entry, if  COMPZ = 'V', then Z contains the orthogonal   
+   >          matrix used in the reduction to tridiagonal form.   
+   >          On exit, if INFO = 0, then if  COMPZ = 'V', Z contains the   
+   >          orthonormal eigenvectors of the original symmetric matrix,   
+   >          and if COMPZ = 'I', Z contains the orthonormal eigenvectors   
+   >          of the symmetric tridiagonal matrix.   
+   >          If COMPZ = 'N', then Z is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >          The leading dimension of the array Z.  LDZ >= 1, and if   
+   >          eigenvectors are desired, then  LDZ >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (max(1,2*N-2))   
+   >          If COMPZ = 'N', then WORK is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  the algorithm has failed to find all the eigenvalues in   
+   >                a total of 30*N iterations; if INFO = i, then i   
+   >                elements of E have not converged to zero; on exit, D   
+   >                and E contain the elements of a symmetric tridiagonal   
+   >                matrix which is orthogonally similar to the original   
+   >                matrix.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdsteqr_(char *compz, integer *n, doublereal *d__, 
+	doublereal *e, doublereal *z__, integer *ldz, doublereal *work, 
+	integer *info)
+{
+    /* System generated locals */
+    integer z_dim1, z_offset, i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal), d_sign(doublereal *, doublereal *);
+
+    /* Local variables */
+    doublereal b, c__, f, g;
+    integer i__, j, k, l, m;
+    doublereal p, r__, s;
+    integer l1, ii, mm, lm1, mm1, nm1;
+    doublereal rt1, rt2, eps;
+    integer lsv;
+    doublereal tst, eps2;
+    integer lend, jtot;
+    extern /* Subroutine */ int igraphdlae2_(doublereal *, doublereal *, doublereal 
+	    *, doublereal *, doublereal *);
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdlasr_(char *, char *, char *, integer *, 
+	    integer *, doublereal *, doublereal *, doublereal *, integer *);
+    doublereal anorm;
+    extern /* Subroutine */ int igraphdswap_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdlaev2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *);
+    integer lendm1, lendp1;
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *), igraphdlamch_(char *);
+    integer iscale;
+    extern /* Subroutine */ int igraphdlascl_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *), igraphdlaset_(char *, integer *, integer 
+	    *, doublereal *, doublereal *, doublereal *, integer *);
+    doublereal safmin;
+    extern /* Subroutine */ int igraphdlartg_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *);
+    doublereal safmax;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    extern doublereal igraphdlanst_(char *, integer *, doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlasrt_(char *, integer *, doublereal *, 
+	    integer *);
+    integer lendsv;
+    doublereal ssfmin;
+    integer nmaxit, icompz;
+    doublereal ssfmax;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    --d__;
+    --e;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+
+    if (igraphlsame_(compz, "N")) {
+	icompz = 0;
+    } else if (igraphlsame_(compz, "V")) {
+	icompz = 1;
+    } else if (igraphlsame_(compz, "I")) {
+	icompz = 2;
+    } else {
+	icompz = -1;
+    }
+    if (icompz < 0) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*ldz < 1 || icompz > 0 && *ldz < max(1,*n)) {
+	*info = -6;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DSTEQR", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+    if (*n == 1) {
+	if (icompz == 2) {
+	    z__[z_dim1 + 1] = 1.;
+	}
+	return 0;
+    }
+
+/*     Determine the unit roundoff and over/underflow thresholds. */
+
+    eps = igraphdlamch_("E");
+/* Computing 2nd power */
+    d__1 = eps;
+    eps2 = d__1 * d__1;
+    safmin = igraphdlamch_("S");
+    safmax = 1. / safmin;
+    ssfmax = sqrt(safmax) / 3.;
+    ssfmin = sqrt(safmin) / eps2;
+
+/*     Compute the eigenvalues and eigenvectors of the tridiagonal   
+       matrix. */
+
+    if (icompz == 2) {
+	igraphdlaset_("Full", n, n, &c_b9, &c_b10, &z__[z_offset], ldz);
+    }
+
+    nmaxit = *n * 30;
+    jtot = 0;
+
+/*     Determine where the matrix splits and choose QL or QR iteration   
+       for each block, according to whether top or bottom diagonal   
+       element is smaller. */
+
+    l1 = 1;
+    nm1 = *n - 1;
+
+L10:
+    if (l1 > *n) {
+	goto L160;
+    }
+    if (l1 > 1) {
+	e[l1 - 1] = 0.;
+    }
+    if (l1 <= nm1) {
+	i__1 = nm1;
+	for (m = l1; m <= i__1; ++m) {
+	    tst = (d__1 = e[m], abs(d__1));
+	    if (tst == 0.) {
+		goto L30;
+	    }
+	    if (tst <= sqrt((d__1 = d__[m], abs(d__1))) * sqrt((d__2 = d__[m 
+		    + 1], abs(d__2))) * eps) {
+		e[m] = 0.;
+		goto L30;
+	    }
+/* L20: */
+	}
+    }
+    m = *n;
+
+L30:
+    l = l1;
+    lsv = l;
+    lend = m;
+    lendsv = lend;
+    l1 = m + 1;
+    if (lend == l) {
+	goto L10;
+    }
+
+/*     Scale submatrix in rows and columns L to LEND */
+
+    i__1 = lend - l + 1;
+    anorm = igraphdlanst_("M", &i__1, &d__[l], &e[l]);
+    iscale = 0;
+    if (anorm == 0.) {
+	goto L10;
+    }
+    if (anorm > ssfmax) {
+	iscale = 1;
+	i__1 = lend - l + 1;
+	igraphdlascl_("G", &c__0, &c__0, &anorm, &ssfmax, &i__1, &c__1, &d__[l], n, 
+		info);
+	i__1 = lend - l;
+	igraphdlascl_("G", &c__0, &c__0, &anorm, &ssfmax, &i__1, &c__1, &e[l], n, 
+		info);
+    } else if (anorm < ssfmin) {
+	iscale = 2;
+	i__1 = lend - l + 1;
+	igraphdlascl_("G", &c__0, &c__0, &anorm, &ssfmin, &i__1, &c__1, &d__[l], n, 
+		info);
+	i__1 = lend - l;
+	igraphdlascl_("G", &c__0, &c__0, &anorm, &ssfmin, &i__1, &c__1, &e[l], n, 
+		info);
+    }
+
+/*     Choose between QL and QR iteration */
+
+    if ((d__1 = d__[lend], abs(d__1)) < (d__2 = d__[l], abs(d__2))) {
+	lend = lsv;
+	l = lendsv;
+    }
+
+    if (lend > l) {
+
+/*        QL Iteration   
+
+          Look for small subdiagonal element. */
+
+L40:
+	if (l != lend) {
+	    lendm1 = lend - 1;
+	    i__1 = lendm1;
+	    for (m = l; m <= i__1; ++m) {
+/* Computing 2nd power */
+		d__2 = (d__1 = e[m], abs(d__1));
+		tst = d__2 * d__2;
+		if (tst <= eps2 * (d__1 = d__[m], abs(d__1)) * (d__2 = d__[m 
+			+ 1], abs(d__2)) + safmin) {
+		    goto L60;
+		}
+/* L50: */
+	    }
+	}
+
+	m = lend;
+
+L60:
+	if (m < lend) {
+	    e[m] = 0.;
+	}
+	p = d__[l];
+	if (m == l) {
+	    goto L80;
+	}
+
+/*        If remaining matrix is 2-by-2, use DLAE2 or SLAEV2   
+          to compute its eigensystem. */
+
+	if (m == l + 1) {
+	    if (icompz > 0) {
+		igraphdlaev2_(&d__[l], &e[l], &d__[l + 1], &rt1, &rt2, &c__, &s);
+		work[l] = c__;
+		work[*n - 1 + l] = s;
+		igraphdlasr_("R", "V", "B", n, &c__2, &work[l], &work[*n - 1 + l], &
+			z__[l * z_dim1 + 1], ldz);
+	    } else {
+		igraphdlae2_(&d__[l], &e[l], &d__[l + 1], &rt1, &rt2);
+	    }
+	    d__[l] = rt1;
+	    d__[l + 1] = rt2;
+	    e[l] = 0.;
+	    l += 2;
+	    if (l <= lend) {
+		goto L40;
+	    }
+	    goto L140;
+	}
+
+	if (jtot == nmaxit) {
+	    goto L140;
+	}
+	++jtot;
+
+/*        Form shift. */
+
+	g = (d__[l + 1] - p) / (e[l] * 2.);
+	r__ = igraphdlapy2_(&g, &c_b10);
+	g = d__[m] - p + e[l] / (g + d_sign(&r__, &g));
+
+	s = 1.;
+	c__ = 1.;
+	p = 0.;
+
+/*        Inner loop */
+
+	mm1 = m - 1;
+	i__1 = l;
+	for (i__ = mm1; i__ >= i__1; --i__) {
+	    f = s * e[i__];
+	    b = c__ * e[i__];
+	    igraphdlartg_(&g, &f, &c__, &s, &r__);
+	    if (i__ != m - 1) {
+		e[i__ + 1] = r__;
+	    }
+	    g = d__[i__ + 1] - p;
+	    r__ = (d__[i__] - g) * s + c__ * 2. * b;
+	    p = s * r__;
+	    d__[i__ + 1] = g + p;
+	    g = c__ * r__ - b;
+
+/*           If eigenvectors are desired, then save rotations. */
+
+	    if (icompz > 0) {
+		work[i__] = c__;
+		work[*n - 1 + i__] = -s;
+	    }
+
+/* L70: */
+	}
+
+/*        If eigenvectors are desired, then apply saved rotations. */
+
+	if (icompz > 0) {
+	    mm = m - l + 1;
+	    igraphdlasr_("R", "V", "B", n, &mm, &work[l], &work[*n - 1 + l], &z__[l 
+		    * z_dim1 + 1], ldz);
+	}
+
+	d__[l] -= p;
+	e[l] = g;
+	goto L40;
+
+/*        Eigenvalue found. */
+
+L80:
+	d__[l] = p;
+
+	++l;
+	if (l <= lend) {
+	    goto L40;
+	}
+	goto L140;
+
+    } else {
+
+/*        QR Iteration   
+
+          Look for small superdiagonal element. */
+
+L90:
+	if (l != lend) {
+	    lendp1 = lend + 1;
+	    i__1 = lendp1;
+	    for (m = l; m >= i__1; --m) {
+/* Computing 2nd power */
+		d__2 = (d__1 = e[m - 1], abs(d__1));
+		tst = d__2 * d__2;
+		if (tst <= eps2 * (d__1 = d__[m], abs(d__1)) * (d__2 = d__[m 
+			- 1], abs(d__2)) + safmin) {
+		    goto L110;
+		}
+/* L100: */
+	    }
+	}
+
+	m = lend;
+
+L110:
+	if (m > lend) {
+	    e[m - 1] = 0.;
+	}
+	p = d__[l];
+	if (m == l) {
+	    goto L130;
+	}
+
+/*        If remaining matrix is 2-by-2, use DLAE2 or SLAEV2   
+          to compute its eigensystem. */
+
+	if (m == l - 1) {
+	    if (icompz > 0) {
+		igraphdlaev2_(&d__[l - 1], &e[l - 1], &d__[l], &rt1, &rt2, &c__, &s)
+			;
+		work[m] = c__;
+		work[*n - 1 + m] = s;
+		igraphdlasr_("R", "V", "F", n, &c__2, &work[m], &work[*n - 1 + m], &
+			z__[(l - 1) * z_dim1 + 1], ldz);
+	    } else {
+		igraphdlae2_(&d__[l - 1], &e[l - 1], &d__[l], &rt1, &rt2);
+	    }
+	    d__[l - 1] = rt1;
+	    d__[l] = rt2;
+	    e[l - 1] = 0.;
+	    l += -2;
+	    if (l >= lend) {
+		goto L90;
+	    }
+	    goto L140;
+	}
+
+	if (jtot == nmaxit) {
+	    goto L140;
+	}
+	++jtot;
+
+/*        Form shift. */
+
+	g = (d__[l - 1] - p) / (e[l - 1] * 2.);
+	r__ = igraphdlapy2_(&g, &c_b10);
+	g = d__[m] - p + e[l - 1] / (g + d_sign(&r__, &g));
+
+	s = 1.;
+	c__ = 1.;
+	p = 0.;
+
+/*        Inner loop */
+
+	lm1 = l - 1;
+	i__1 = lm1;
+	for (i__ = m; i__ <= i__1; ++i__) {
+	    f = s * e[i__];
+	    b = c__ * e[i__];
+	    igraphdlartg_(&g, &f, &c__, &s, &r__);
+	    if (i__ != m) {
+		e[i__ - 1] = r__;
+	    }
+	    g = d__[i__] - p;
+	    r__ = (d__[i__ + 1] - g) * s + c__ * 2. * b;
+	    p = s * r__;
+	    d__[i__] = g + p;
+	    g = c__ * r__ - b;
+
+/*           If eigenvectors are desired, then save rotations. */
+
+	    if (icompz > 0) {
+		work[i__] = c__;
+		work[*n - 1 + i__] = s;
+	    }
+
+/* L120: */
+	}
+
+/*        If eigenvectors are desired, then apply saved rotations. */
+
+	if (icompz > 0) {
+	    mm = l - m + 1;
+	    igraphdlasr_("R", "V", "F", n, &mm, &work[m], &work[*n - 1 + m], &z__[m 
+		    * z_dim1 + 1], ldz);
+	}
+
+	d__[l] -= p;
+	e[lm1] = g;
+	goto L90;
+
+/*        Eigenvalue found. */
+
+L130:
+	d__[l] = p;
+
+	--l;
+	if (l >= lend) {
+	    goto L90;
+	}
+	goto L140;
+
+    }
+
+/*     Undo scaling if necessary */
+
+L140:
+    if (iscale == 1) {
+	i__1 = lendsv - lsv + 1;
+	igraphdlascl_("G", &c__0, &c__0, &ssfmax, &anorm, &i__1, &c__1, &d__[lsv], 
+		n, info);
+	i__1 = lendsv - lsv;
+	igraphdlascl_("G", &c__0, &c__0, &ssfmax, &anorm, &i__1, &c__1, &e[lsv], n, 
+		info);
+    } else if (iscale == 2) {
+	i__1 = lendsv - lsv + 1;
+	igraphdlascl_("G", &c__0, &c__0, &ssfmin, &anorm, &i__1, &c__1, &d__[lsv], 
+		n, info);
+	i__1 = lendsv - lsv;
+	igraphdlascl_("G", &c__0, &c__0, &ssfmin, &anorm, &i__1, &c__1, &e[lsv], n, 
+		info);
+    }
+
+/*     Check for no convergence to an eigenvalue after a total   
+       of N*MAXIT iterations. */
+
+    if (jtot < nmaxit) {
+	goto L10;
+    }
+    i__1 = *n - 1;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	if (e[i__] != 0.) {
+	    ++(*info);
+	}
+/* L150: */
+    }
+    goto L190;
+
+/*     Order eigenvalues and eigenvectors. */
+
+L160:
+    if (icompz == 0) {
+
+/*        Use Quick Sort */
+
+	igraphdlasrt_("I", n, &d__[1], info);
+
+    } else {
+
+/*        Use Selection Sort to minimize swaps of eigenvectors */
+
+	i__1 = *n;
+	for (ii = 2; ii <= i__1; ++ii) {
+	    i__ = ii - 1;
+	    k = i__;
+	    p = d__[i__];
+	    i__2 = *n;
+	    for (j = ii; j <= i__2; ++j) {
+		if (d__[j] < p) {
+		    k = j;
+		    p = d__[j];
+		}
+/* L170: */
+	    }
+	    if (k != i__) {
+		d__[k] = d__[i__];
+		d__[i__] = p;
+		igraphdswap_(n, &z__[i__ * z_dim1 + 1], &c__1, &z__[k * z_dim1 + 1],
+			 &c__1);
+	    }
+/* L180: */
+	}
+    }
+
+L190:
+    return 0;
+
+/*     End of DSTEQR */
+
+} /* igraphdsteqr_ */
+
diff --git a/igraph/src/dsterf.c b/igraph/src/dsterf.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsterf.c
@@ -0,0 +1,510 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__0 = 0;
+static integer c__1 = 1;
+static doublereal c_b33 = 1.;
+
+/* > \brief \b DSTERF   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DSTERF + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dsterf.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dsterf.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dsterf.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DSTERF( N, D, E, INFO )   
+
+         INTEGER            INFO, N   
+         DOUBLE PRECISION   D( * ), E( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DSTERF computes all eigenvalues of a symmetric tridiagonal matrix   
+   > using the Pal-Walker-Kahan variant of the QL or QR algorithm.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          On entry, the n diagonal elements of the tridiagonal matrix.   
+   >          On exit, if INFO = 0, the eigenvalues in ascending order.   
+   > \endverbatim   
+   >   
+   > \param[in,out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          On entry, the (n-1) subdiagonal elements of the tridiagonal   
+   >          matrix.   
+   >          On exit, E has been destroyed.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  the algorithm failed to find all of the eigenvalues in   
+   >                a total of 30*N iterations; if INFO = i, then i   
+   >                elements of E have not converged to zero.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdsterf_(integer *n, doublereal *d__, doublereal *e, 
+	integer *info)
+{
+    /* System generated locals */
+    integer i__1;
+    doublereal d__1, d__2, d__3;
+
+    /* Builtin functions */
+    double sqrt(doublereal), d_sign(doublereal *, doublereal *);
+
+    /* Local variables */
+    doublereal c__;
+    integer i__, l, m;
+    doublereal p, r__, s;
+    integer l1;
+    doublereal bb, rt1, rt2, eps, rte;
+    integer lsv;
+    doublereal eps2, oldc;
+    integer lend;
+    doublereal rmax;
+    integer jtot;
+    extern /* Subroutine */ int igraphdlae2_(doublereal *, doublereal *, doublereal 
+	    *, doublereal *, doublereal *);
+    doublereal gamma, alpha, sigma, anorm;
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *), igraphdlamch_(char *);
+    integer iscale;
+    extern /* Subroutine */ int igraphdlascl_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *);
+    doublereal oldgam, safmin;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    doublereal safmax;
+    extern doublereal igraphdlanst_(char *, integer *, doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlasrt_(char *, integer *, doublereal *, 
+	    integer *);
+    integer lendsv;
+    doublereal ssfmin;
+    integer nmaxit;
+    doublereal ssfmax;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    --e;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+
+/*     Quick return if possible */
+
+    if (*n < 0) {
+	*info = -1;
+	i__1 = -(*info);
+	igraphxerbla_("DSTERF", &i__1, (ftnlen)6);
+	return 0;
+    }
+    if (*n <= 1) {
+	return 0;
+    }
+
+/*     Determine the unit roundoff for this environment. */
+
+    eps = igraphdlamch_("E");
+/* Computing 2nd power */
+    d__1 = eps;
+    eps2 = d__1 * d__1;
+    safmin = igraphdlamch_("S");
+    safmax = 1. / safmin;
+    ssfmax = sqrt(safmax) / 3.;
+    ssfmin = sqrt(safmin) / eps2;
+    rmax = igraphdlamch_("O");
+
+/*     Compute the eigenvalues of the tridiagonal matrix. */
+
+    nmaxit = *n * 30;
+    sigma = 0.;
+    jtot = 0;
+
+/*     Determine where the matrix splits and choose QL or QR iteration   
+       for each block, according to whether top or bottom diagonal   
+       element is smaller. */
+
+    l1 = 1;
+
+L10:
+    if (l1 > *n) {
+	goto L170;
+    }
+    if (l1 > 1) {
+	e[l1 - 1] = 0.;
+    }
+    i__1 = *n - 1;
+    for (m = l1; m <= i__1; ++m) {
+	if ((d__3 = e[m], abs(d__3)) <= sqrt((d__1 = d__[m], abs(d__1))) * 
+		sqrt((d__2 = d__[m + 1], abs(d__2))) * eps) {
+	    e[m] = 0.;
+	    goto L30;
+	}
+/* L20: */
+    }
+    m = *n;
+
+L30:
+    l = l1;
+    lsv = l;
+    lend = m;
+    lendsv = lend;
+    l1 = m + 1;
+    if (lend == l) {
+	goto L10;
+    }
+
+/*     Scale submatrix in rows and columns L to LEND */
+
+    i__1 = lend - l + 1;
+    anorm = igraphdlanst_("M", &i__1, &d__[l], &e[l]);
+    iscale = 0;
+    if (anorm == 0.) {
+	goto L10;
+    }
+    if (anorm > ssfmax) {
+	iscale = 1;
+	i__1 = lend - l + 1;
+	igraphdlascl_("G", &c__0, &c__0, &anorm, &ssfmax, &i__1, &c__1, &d__[l], n, 
+		info);
+	i__1 = lend - l;
+	igraphdlascl_("G", &c__0, &c__0, &anorm, &ssfmax, &i__1, &c__1, &e[l], n, 
+		info);
+    } else if (anorm < ssfmin) {
+	iscale = 2;
+	i__1 = lend - l + 1;
+	igraphdlascl_("G", &c__0, &c__0, &anorm, &ssfmin, &i__1, &c__1, &d__[l], n, 
+		info);
+	i__1 = lend - l;
+	igraphdlascl_("G", &c__0, &c__0, &anorm, &ssfmin, &i__1, &c__1, &e[l], n, 
+		info);
+    }
+
+    i__1 = lend - 1;
+    for (i__ = l; i__ <= i__1; ++i__) {
+/* Computing 2nd power */
+	d__1 = e[i__];
+	e[i__] = d__1 * d__1;
+/* L40: */
+    }
+
+/*     Choose between QL and QR iteration */
+
+    if ((d__1 = d__[lend], abs(d__1)) < (d__2 = d__[l], abs(d__2))) {
+	lend = lsv;
+	l = lendsv;
+    }
+
+    if (lend >= l) {
+
+/*        QL Iteration   
+
+          Look for small subdiagonal element. */
+
+L50:
+	if (l != lend) {
+	    i__1 = lend - 1;
+	    for (m = l; m <= i__1; ++m) {
+		if ((d__2 = e[m], abs(d__2)) <= eps2 * (d__1 = d__[m] * d__[m 
+			+ 1], abs(d__1))) {
+		    goto L70;
+		}
+/* L60: */
+	    }
+	}
+	m = lend;
+
+L70:
+	if (m < lend) {
+	    e[m] = 0.;
+	}
+	p = d__[l];
+	if (m == l) {
+	    goto L90;
+	}
+
+/*        If remaining matrix is 2 by 2, use DLAE2 to compute its   
+          eigenvalues. */
+
+	if (m == l + 1) {
+	    rte = sqrt(e[l]);
+	    igraphdlae2_(&d__[l], &rte, &d__[l + 1], &rt1, &rt2);
+	    d__[l] = rt1;
+	    d__[l + 1] = rt2;
+	    e[l] = 0.;
+	    l += 2;
+	    if (l <= lend) {
+		goto L50;
+	    }
+	    goto L150;
+	}
+
+	if (jtot == nmaxit) {
+	    goto L150;
+	}
+	++jtot;
+
+/*        Form shift. */
+
+	rte = sqrt(e[l]);
+	sigma = (d__[l + 1] - p) / (rte * 2.);
+	r__ = igraphdlapy2_(&sigma, &c_b33);
+	sigma = p - rte / (sigma + d_sign(&r__, &sigma));
+
+	c__ = 1.;
+	s = 0.;
+	gamma = d__[m] - sigma;
+	p = gamma * gamma;
+
+/*        Inner loop */
+
+	i__1 = l;
+	for (i__ = m - 1; i__ >= i__1; --i__) {
+	    bb = e[i__];
+	    r__ = p + bb;
+	    if (i__ != m - 1) {
+		e[i__ + 1] = s * r__;
+	    }
+	    oldc = c__;
+	    c__ = p / r__;
+	    s = bb / r__;
+	    oldgam = gamma;
+	    alpha = d__[i__];
+	    gamma = c__ * (alpha - sigma) - s * oldgam;
+	    d__[i__ + 1] = oldgam + (alpha - gamma);
+	    if (c__ != 0.) {
+		p = gamma * gamma / c__;
+	    } else {
+		p = oldc * bb;
+	    }
+/* L80: */
+	}
+
+	e[l] = s * p;
+	d__[l] = sigma + gamma;
+	goto L50;
+
+/*        Eigenvalue found. */
+
+L90:
+	d__[l] = p;
+
+	++l;
+	if (l <= lend) {
+	    goto L50;
+	}
+	goto L150;
+
+    } else {
+
+/*        QR Iteration   
+
+          Look for small superdiagonal element. */
+
+L100:
+	i__1 = lend + 1;
+	for (m = l; m >= i__1; --m) {
+	    if ((d__2 = e[m - 1], abs(d__2)) <= eps2 * (d__1 = d__[m] * d__[m 
+		    - 1], abs(d__1))) {
+		goto L120;
+	    }
+/* L110: */
+	}
+	m = lend;
+
+L120:
+	if (m > lend) {
+	    e[m - 1] = 0.;
+	}
+	p = d__[l];
+	if (m == l) {
+	    goto L140;
+	}
+
+/*        If remaining matrix is 2 by 2, use DLAE2 to compute its   
+          eigenvalues. */
+
+	if (m == l - 1) {
+	    rte = sqrt(e[l - 1]);
+	    igraphdlae2_(&d__[l], &rte, &d__[l - 1], &rt1, &rt2);
+	    d__[l] = rt1;
+	    d__[l - 1] = rt2;
+	    e[l - 1] = 0.;
+	    l += -2;
+	    if (l >= lend) {
+		goto L100;
+	    }
+	    goto L150;
+	}
+
+	if (jtot == nmaxit) {
+	    goto L150;
+	}
+	++jtot;
+
+/*        Form shift. */
+
+	rte = sqrt(e[l - 1]);
+	sigma = (d__[l - 1] - p) / (rte * 2.);
+	r__ = igraphdlapy2_(&sigma, &c_b33);
+	sigma = p - rte / (sigma + d_sign(&r__, &sigma));
+
+	c__ = 1.;
+	s = 0.;
+	gamma = d__[m] - sigma;
+	p = gamma * gamma;
+
+/*        Inner loop */
+
+	i__1 = l - 1;
+	for (i__ = m; i__ <= i__1; ++i__) {
+	    bb = e[i__];
+	    r__ = p + bb;
+	    if (i__ != m) {
+		e[i__ - 1] = s * r__;
+	    }
+	    oldc = c__;
+	    c__ = p / r__;
+	    s = bb / r__;
+	    oldgam = gamma;
+	    alpha = d__[i__ + 1];
+	    gamma = c__ * (alpha - sigma) - s * oldgam;
+	    d__[i__] = oldgam + (alpha - gamma);
+	    if (c__ != 0.) {
+		p = gamma * gamma / c__;
+	    } else {
+		p = oldc * bb;
+	    }
+/* L130: */
+	}
+
+	e[l - 1] = s * p;
+	d__[l] = sigma + gamma;
+	goto L100;
+
+/*        Eigenvalue found. */
+
+L140:
+	d__[l] = p;
+
+	--l;
+	if (l >= lend) {
+	    goto L100;
+	}
+	goto L150;
+
+    }
+
+/*     Undo scaling if necessary */
+
+L150:
+    if (iscale == 1) {
+	i__1 = lendsv - lsv + 1;
+	igraphdlascl_("G", &c__0, &c__0, &ssfmax, &anorm, &i__1, &c__1, &d__[lsv], 
+		n, info);
+    }
+    if (iscale == 2) {
+	i__1 = lendsv - lsv + 1;
+	igraphdlascl_("G", &c__0, &c__0, &ssfmin, &anorm, &i__1, &c__1, &d__[lsv], 
+		n, info);
+    }
+
+/*     Check for no convergence to an eigenvalue after a total   
+       of N*MAXIT iterations. */
+
+    if (jtot < nmaxit) {
+	goto L10;
+    }
+    i__1 = *n - 1;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	if (e[i__] != 0.) {
+	    ++(*info);
+	}
+/* L160: */
+    }
+    goto L180;
+
+/*     Sort eigenvalues in increasing order. */
+
+L170:
+    igraphdlasrt_("I", n, &d__[1], info);
+
+L180:
+    return 0;
+
+/*     End of DSTERF */
+
+} /* igraphdsterf_ */
+
diff --git a/igraph/src/dstqrb.c b/igraph/src/dstqrb.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dstqrb.c
@@ -0,0 +1,691 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__0 = 0;
+static integer c__1 = 1;
+static doublereal c_b31 = 1.;
+
+/* -----------------------------------------------------------------------   
+   \BeginDoc   
+
+   \Name: dstqrb   
+
+   \Description:   
+    Computes all eigenvalues and the last component of the eigenvectors   
+    of a symmetric tridiagonal matrix using the implicit QL or QR method.   
+
+    This is mostly a modification of the LAPACK routine dsteqr.   
+    See Remarks.   
+
+   \Usage:   
+    call dstqrb   
+       ( N, D, E, Z, WORK, INFO )   
+
+   \Arguments   
+    N       Integer.  (INPUT)   
+            The number of rows and columns in the matrix.  N >= 0.   
+
+    D       Double precision array, dimension (N).  (INPUT/OUTPUT)   
+            On entry, D contains the diagonal elements of the   
+            tridiagonal matrix.   
+            On exit, D contains the eigenvalues, in ascending order.   
+            If an error exit is made, the eigenvalues are correct   
+            for indices 1,2,...,INFO-1, but they are unordered and   
+            may not be the smallest eigenvalues of the matrix.   
+
+    E       Double precision array, dimension (N-1).  (INPUT/OUTPUT)   
+            On entry, E contains the subdiagonal elements of the   
+            tridiagonal matrix in positions 1 through N-1.   
+            On exit, E has been destroyed.   
+
+    Z       Double precision array, dimension (N).  (OUTPUT)   
+            On exit, Z contains the last row of the orthonormal   
+            eigenvector matrix of the symmetric tridiagonal matrix.   
+            If an error exit is made, Z contains the last row of the   
+            eigenvector matrix associated with the stored eigenvalues.   
+
+    WORK    Double precision array, dimension (max(1,2*N-2)).  (WORKSPACE)   
+            Workspace used in accumulating the transformation for   
+            computing the last components of the eigenvectors.   
+
+    INFO    Integer.  (OUTPUT)   
+            = 0:  normal return.   
+            < 0:  if INFO = -i, the i-th argument had an illegal value.   
+            > 0:  if INFO = +i, the i-th eigenvalue has not converged   
+                                after a total of  30*N  iterations.   
+
+   \Remarks   
+    1. None.   
+
+   -----------------------------------------------------------------------   
+
+   \BeginLib   
+
+   \Local variables:   
+       xxxxxx  real   
+
+   \Routines called:   
+       daxpy   Level 1 BLAS that computes a vector triad.   
+       dcopy   Level 1 BLAS that copies one vector to another.   
+       dswap   Level 1 BLAS that swaps the contents of two vectors.   
+       lsame   LAPACK character comparison routine.   
+       dlae2   LAPACK routine that computes the eigenvalues of a 2-by-2   
+               symmetric matrix.   
+       dlaev2  LAPACK routine that eigendecomposition of a 2-by-2 symmetric   
+               matrix.   
+       dlamch  LAPACK routine that determines machine constants.   
+       dlanst  LAPACK routine that computes the norm of a matrix.   
+       dlapy2  LAPACK routine to compute sqrt(x**2+y**2) carefully.   
+       dlartg  LAPACK Givens rotation construction routine.   
+       dlascl  LAPACK routine for careful scaling of a matrix.   
+       dlaset  LAPACK matrix initialization routine.   
+       dlasr   LAPACK routine that applies an orthogonal transformation to   
+               a matrix.   
+       dlasrt  LAPACK sorting routine.   
+       dsteqr  LAPACK routine that computes eigenvalues and eigenvectors   
+               of a symmetric tridiagonal matrix.   
+       xerbla  LAPACK error handler routine.   
+
+   \Authors   
+       Danny Sorensen               Phuong Vu   
+       Richard Lehoucq              CRPC / Rice University   
+       Dept. of Computational &     Houston, Texas   
+       Applied Mathematics   
+       Rice University   
+       Houston, Texas   
+
+   \SCCS Information: @(#)   
+   FILE: stqrb.F   SID: 2.5   DATE OF SID: 8/27/96   RELEASE: 2   
+
+   \Remarks   
+       1. Starting with version 2.5, this routine is a modified version   
+          of LAPACK version 2.0 subroutine SSTEQR. No lines are deleted,   
+          only commeted out and new lines inserted.   
+          All lines commented out have "c$$$" at the beginning.   
+          Note that the LAPACK version 1.0 subroutine SSTEQR contained   
+          bugs.   
+
+   \EndLib   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdstqrb_(integer *n, doublereal *d__, doublereal *e, 
+	doublereal *z__, doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal), d_sign(doublereal *, doublereal *);
+
+    /* Local variables */
+    doublereal b, c__, f, g;
+    integer i__, j, k, l, m;
+    doublereal p, r__, s;
+    integer l1, ii, mm, lm1, mm1, nm1;
+    doublereal rt1, rt2, eps;
+    integer lsv;
+    doublereal tst, eps2;
+    integer lend, jtot;
+    extern /* Subroutine */ int igraphdlae2_(doublereal *, doublereal *, doublereal 
+	    *, doublereal *, doublereal *), igraphdlasr_(char *, char *, char *, 
+	    integer *, integer *, doublereal *, doublereal *, doublereal *, 
+	    integer *);
+    doublereal anorm;
+    extern /* Subroutine */ int igraphdlaev2_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *, doublereal *, 
+	    doublereal *);
+    integer lendm1, lendp1;
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *), igraphdlamch_(char *);
+    integer iscale;
+    extern /* Subroutine */ int igraphdlascl_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, integer *);
+    doublereal safmin;
+    extern /* Subroutine */ int igraphdlartg_(doublereal *, doublereal *, 
+	    doublereal *, doublereal *, doublereal *);
+    doublereal safmax;
+    extern doublereal igraphdlanst_(char *, integer *, doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlasrt_(char *, integer *, doublereal *, 
+	    integer *);
+    integer lendsv, nmaxit, icompz;
+    doublereal ssfmax, ssfmin;
+
+
+/*     %------------------%   
+       | Scalar Arguments |   
+       %------------------%   
+
+
+       %-----------------%   
+       | Array Arguments |   
+       %-----------------%   
+
+
+
+       test the input parameters.   
+
+       Parameter adjustments */
+    --work;
+    --z__;
+    --e;
+    --d__;
+
+    /* Function Body */
+    *info = 0;
+
+/* $$$      IF( LSAME( COMPZ, 'N' ) ) THEN   
+   $$$         ICOMPZ = 0   
+   $$$      ELSE IF( LSAME( COMPZ, 'V' ) ) THEN   
+   $$$         ICOMPZ = 1   
+   $$$      ELSE IF( LSAME( COMPZ, 'I' ) ) THEN   
+   $$$         ICOMPZ = 2   
+   $$$      ELSE   
+   $$$         ICOMPZ = -1   
+   $$$      END IF   
+   $$$      IF( ICOMPZ.LT.0 ) THEN   
+   $$$         INFO = -1   
+   $$$      ELSE IF( N.LT.0 ) THEN   
+   $$$         INFO = -2   
+   $$$      ELSE IF( ( LDZ.LT.1 ) .OR. ( ICOMPZ.GT.0 .AND. LDZ.LT.MAX( 1,   
+   $$$     $         N ) ) ) THEN   
+   $$$         INFO = -6   
+   $$$      END IF   
+   $$$      IF( INFO.NE.0 ) THEN   
+   $$$         CALL XERBLA( 'SSTEQR', -INFO )   
+   $$$         RETURN   
+   $$$      END IF   
+
+      *** New starting with version 2.5 *** */
+
+    icompz = 2;
+/*    *************************************   
+
+       quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+    if (*n == 1) {
+	if (icompz == 2) {
+	    z__[1] = 1.;
+	}
+	return 0;
+    }
+
+/*     determine the unit roundoff and over/underflow thresholds. */
+
+    eps = igraphdlamch_("e");
+/* Computing 2nd power */
+    d__1 = eps;
+    eps2 = d__1 * d__1;
+    safmin = igraphdlamch_("s");
+    safmax = 1. / safmin;
+    ssfmax = sqrt(safmax) / 3.;
+    ssfmin = sqrt(safmin) / eps2;
+
+/*     compute the eigenvalues and eigenvectors of the tridiagonal   
+       matrix.   
+
+   $$      if( icompz.eq.2 )   
+   $$$     $   call dlaset( 'full', n, n, zero, one, z, ldz )   
+
+       *** New starting with version 2.5 *** */
+
+    if (icompz == 2) {
+	i__1 = *n - 1;
+	for (j = 1; j <= i__1; ++j) {
+	    z__[j] = 0.;
+/* L5: */
+	}
+	z__[*n] = 1.;
+    }
+/*     ************************************* */
+
+    nmaxit = *n * 30;
+    jtot = 0;
+
+/*     determine where the matrix splits and choose ql or qr iteration   
+       for each block, according to whether top or bottom diagonal   
+       element is smaller. */
+
+    l1 = 1;
+    nm1 = *n - 1;
+
+L10:
+    if (l1 > *n) {
+	goto L160;
+    }
+    if (l1 > 1) {
+	e[l1 - 1] = 0.;
+    }
+    if (l1 <= nm1) {
+	i__1 = nm1;
+	for (m = l1; m <= i__1; ++m) {
+	    tst = (d__1 = e[m], abs(d__1));
+	    if (tst == 0.) {
+		goto L30;
+	    }
+	    if (tst <= sqrt((d__1 = d__[m], abs(d__1))) * sqrt((d__2 = d__[m 
+		    + 1], abs(d__2))) * eps) {
+		e[m] = 0.;
+		goto L30;
+	    }
+/* L20: */
+	}
+    }
+    m = *n;
+
+L30:
+    l = l1;
+    lsv = l;
+    lend = m;
+    lendsv = lend;
+    l1 = m + 1;
+    if (lend == l) {
+	goto L10;
+    }
+
+/*     scale submatrix in rows and columns l to lend */
+
+    i__1 = lend - l + 1;
+    anorm = igraphdlanst_("i", &i__1, &d__[l], &e[l]);
+    iscale = 0;
+    if (anorm == 0.) {
+	goto L10;
+    }
+    if (anorm > ssfmax) {
+	iscale = 1;
+	i__1 = lend - l + 1;
+	igraphdlascl_("g", &c__0, &c__0, &anorm, &ssfmax, &i__1, &c__1, &d__[l], n, 
+		info);
+	i__1 = lend - l;
+	igraphdlascl_("g", &c__0, &c__0, &anorm, &ssfmax, &i__1, &c__1, &e[l], n, 
+		info);
+    } else if (anorm < ssfmin) {
+	iscale = 2;
+	i__1 = lend - l + 1;
+	igraphdlascl_("g", &c__0, &c__0, &anorm, &ssfmin, &i__1, &c__1, &d__[l], n, 
+		info);
+	i__1 = lend - l;
+	igraphdlascl_("g", &c__0, &c__0, &anorm, &ssfmin, &i__1, &c__1, &e[l], n, 
+		info);
+    }
+
+/*     choose between ql and qr iteration */
+
+    if ((d__1 = d__[lend], abs(d__1)) < (d__2 = d__[l], abs(d__2))) {
+	lend = lsv;
+	l = lendsv;
+    }
+
+    if (lend > l) {
+
+/*        ql iteration   
+
+          look for small subdiagonal element. */
+
+L40:
+	if (l != lend) {
+	    lendm1 = lend - 1;
+	    i__1 = lendm1;
+	    for (m = l; m <= i__1; ++m) {
+/* Computing 2nd power */
+		d__2 = (d__1 = e[m], abs(d__1));
+		tst = d__2 * d__2;
+		if (tst <= eps2 * (d__1 = d__[m], abs(d__1)) * (d__2 = d__[m 
+			+ 1], abs(d__2)) + safmin) {
+		    goto L60;
+		}
+/* L50: */
+	    }
+	}
+
+	m = lend;
+
+L60:
+	if (m < lend) {
+	    e[m] = 0.;
+	}
+	p = d__[l];
+	if (m == l) {
+	    goto L80;
+	}
+
+/*        if remaining matrix is 2-by-2, use dlae2 or dlaev2   
+          to compute its eigensystem. */
+
+	if (m == l + 1) {
+	    if (icompz > 0) {
+		igraphdlaev2_(&d__[l], &e[l], &d__[l + 1], &rt1, &rt2, &c__, &s);
+		work[l] = c__;
+		work[*n - 1 + l] = s;
+/* $$$               call dlasr( 'r', 'v', 'b', n, 2, work( l ),   
+   $$$     $                     work( n-1+l ), z( 1, l ), ldz )   
+
+                *** New starting with version 2.5 *** */
+
+		tst = z__[l + 1];
+		z__[l + 1] = c__ * tst - s * z__[l];
+		z__[l] = s * tst + c__ * z__[l];
+/*              ************************************* */
+	    } else {
+		igraphdlae2_(&d__[l], &e[l], &d__[l + 1], &rt1, &rt2);
+	    }
+	    d__[l] = rt1;
+	    d__[l + 1] = rt2;
+	    e[l] = 0.;
+	    l += 2;
+	    if (l <= lend) {
+		goto L40;
+	    }
+	    goto L140;
+	}
+
+	if (jtot == nmaxit) {
+	    goto L140;
+	}
+	++jtot;
+
+/*        form shift. */
+
+	g = (d__[l + 1] - p) / (e[l] * 2.);
+	r__ = igraphdlapy2_(&g, &c_b31);
+	g = d__[m] - p + e[l] / (g + d_sign(&r__, &g));
+
+	s = 1.;
+	c__ = 1.;
+	p = 0.;
+
+/*        inner loop */
+
+	mm1 = m - 1;
+	i__1 = l;
+	for (i__ = mm1; i__ >= i__1; --i__) {
+	    f = s * e[i__];
+	    b = c__ * e[i__];
+	    igraphdlartg_(&g, &f, &c__, &s, &r__);
+	    if (i__ != m - 1) {
+		e[i__ + 1] = r__;
+	    }
+	    g = d__[i__ + 1] - p;
+	    r__ = (d__[i__] - g) * s + c__ * 2. * b;
+	    p = s * r__;
+	    d__[i__ + 1] = g + p;
+	    g = c__ * r__ - b;
+
+/*           if eigenvectors are desired, then save rotations. */
+
+	    if (icompz > 0) {
+		work[i__] = c__;
+		work[*n - 1 + i__] = -s;
+	    }
+
+/* L70: */
+	}
+
+/*        if eigenvectors are desired, then apply saved rotations. */
+
+	if (icompz > 0) {
+	    mm = m - l + 1;
+/* $$$            call dlasr( 'r', 'v', 'b', n, mm, work( l ), work( n-1+l ),   
+   $$$     $                  z( 1, l ), ldz )   
+
+               *** New starting with version 2.5 *** */
+
+	    igraphdlasr_("r", "v", "b", &c__1, &mm, &work[l], &work[*n - 1 + l], &
+		    z__[l], &c__1);
+/*             ************************************* */
+	}
+
+	d__[l] -= p;
+	e[l] = g;
+	goto L40;
+
+/*        eigenvalue found. */
+
+L80:
+	d__[l] = p;
+
+	++l;
+	if (l <= lend) {
+	    goto L40;
+	}
+	goto L140;
+
+    } else {
+
+/*        qr iteration   
+
+          look for small superdiagonal element. */
+
+L90:
+	if (l != lend) {
+	    lendp1 = lend + 1;
+	    i__1 = lendp1;
+	    for (m = l; m >= i__1; --m) {
+/* Computing 2nd power */
+		d__2 = (d__1 = e[m - 1], abs(d__1));
+		tst = d__2 * d__2;
+		if (tst <= eps2 * (d__1 = d__[m], abs(d__1)) * (d__2 = d__[m 
+			- 1], abs(d__2)) + safmin) {
+		    goto L110;
+		}
+/* L100: */
+	    }
+	}
+
+	m = lend;
+
+L110:
+	if (m > lend) {
+	    e[m - 1] = 0.;
+	}
+	p = d__[l];
+	if (m == l) {
+	    goto L130;
+	}
+
+/*        if remaining matrix is 2-by-2, use dlae2 or dlaev2   
+          to compute its eigensystem. */
+
+	if (m == l - 1) {
+	    if (icompz > 0) {
+		igraphdlaev2_(&d__[l - 1], &e[l - 1], &d__[l], &rt1, &rt2, &c__, &s)
+			;
+/* $$$               work( m ) = c   
+   $$$               work( n-1+m ) = s   
+   $$$               call dlasr( 'r', 'v', 'f', n, 2, work( m ),   
+   $$$     $                     work( n-1+m ), z( 1, l-1 ), ldz )   
+
+                 *** New starting with version 2.5 *** */
+
+		tst = z__[l];
+		z__[l] = c__ * tst - s * z__[l - 1];
+		z__[l - 1] = s * tst + c__ * z__[l - 1];
+/*               ************************************* */
+	    } else {
+		igraphdlae2_(&d__[l - 1], &e[l - 1], &d__[l], &rt1, &rt2);
+	    }
+	    d__[l - 1] = rt1;
+	    d__[l] = rt2;
+	    e[l - 1] = 0.;
+	    l += -2;
+	    if (l >= lend) {
+		goto L90;
+	    }
+	    goto L140;
+	}
+
+	if (jtot == nmaxit) {
+	    goto L140;
+	}
+	++jtot;
+
+/*        form shift. */
+
+	g = (d__[l - 1] - p) / (e[l - 1] * 2.);
+	r__ = igraphdlapy2_(&g, &c_b31);
+	g = d__[m] - p + e[l - 1] / (g + d_sign(&r__, &g));
+
+	s = 1.;
+	c__ = 1.;
+	p = 0.;
+
+/*        inner loop */
+
+	lm1 = l - 1;
+	i__1 = lm1;
+	for (i__ = m; i__ <= i__1; ++i__) {
+	    f = s * e[i__];
+	    b = c__ * e[i__];
+	    igraphdlartg_(&g, &f, &c__, &s, &r__);
+	    if (i__ != m) {
+		e[i__ - 1] = r__;
+	    }
+	    g = d__[i__] - p;
+	    r__ = (d__[i__ + 1] - g) * s + c__ * 2. * b;
+	    p = s * r__;
+	    d__[i__] = g + p;
+	    g = c__ * r__ - b;
+
+/*           if eigenvectors are desired, then save rotations. */
+
+	    if (icompz > 0) {
+		work[i__] = c__;
+		work[*n - 1 + i__] = s;
+	    }
+
+/* L120: */
+	}
+
+/*        if eigenvectors are desired, then apply saved rotations. */
+
+	if (icompz > 0) {
+	    mm = l - m + 1;
+/* $$$            call dlasr( 'r', 'v', 'f', n, mm, work( m ), work( n-1+m ),   
+   $$$     $                  z( 1, m ), ldz )   
+
+             *** New starting with version 2.5 *** */
+
+	    igraphdlasr_("r", "v", "f", &c__1, &mm, &work[m], &work[*n - 1 + m], &
+		    z__[m], &c__1);
+/*           ************************************* */
+	}
+
+	d__[l] -= p;
+	e[lm1] = g;
+	goto L90;
+
+/*        eigenvalue found. */
+
+L130:
+	d__[l] = p;
+
+	--l;
+	if (l >= lend) {
+	    goto L90;
+	}
+	goto L140;
+
+    }
+
+/*     undo scaling if necessary */
+
+L140:
+    if (iscale == 1) {
+	i__1 = lendsv - lsv + 1;
+	igraphdlascl_("g", &c__0, &c__0, &ssfmax, &anorm, &i__1, &c__1, &d__[lsv], 
+		n, info);
+	i__1 = lendsv - lsv;
+	igraphdlascl_("g", &c__0, &c__0, &ssfmax, &anorm, &i__1, &c__1, &e[lsv], n, 
+		info);
+    } else if (iscale == 2) {
+	i__1 = lendsv - lsv + 1;
+	igraphdlascl_("g", &c__0, &c__0, &ssfmin, &anorm, &i__1, &c__1, &d__[lsv], 
+		n, info);
+	i__1 = lendsv - lsv;
+	igraphdlascl_("g", &c__0, &c__0, &ssfmin, &anorm, &i__1, &c__1, &e[lsv], n, 
+		info);
+    }
+
+/*     check for no convergence to an eigenvalue after a total   
+       of n*maxit iterations. */
+
+    if (jtot < nmaxit) {
+	goto L10;
+    }
+    i__1 = *n - 1;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	if (e[i__] != 0.) {
+	    ++(*info);
+	}
+/* L150: */
+    }
+    goto L190;
+
+/*     order eigenvalues and eigenvectors. */
+
+L160:
+    if (icompz == 0) {
+
+/*        use quick sort */
+
+	igraphdlasrt_("i", n, &d__[1], info);
+
+    } else {
+
+/*        use selection sort to minimize swaps of eigenvectors */
+
+	i__1 = *n;
+	for (ii = 2; ii <= i__1; ++ii) {
+	    i__ = ii - 1;
+	    k = i__;
+	    p = d__[i__];
+	    i__2 = *n;
+	    for (j = ii; j <= i__2; ++j) {
+		if (d__[j] < p) {
+		    k = j;
+		    p = d__[j];
+		}
+/* L170: */
+	    }
+	    if (k != i__) {
+		d__[k] = d__[i__];
+		d__[i__] = p;
+/* $$$               call dswap( n, z( 1, i ), 1, z( 1, k ), 1 )   
+             *** New starting with version 2.5 *** */
+
+		p = z__[k];
+		z__[k] = z__[i__];
+		z__[i__] = p;
+/*           ************************************* */
+	    }
+/* L180: */
+	}
+    }
+
+L190:
+    return 0;
+
+/*     %---------------%   
+       | End of dstqrb |   
+       %---------------% */
+
+} /* igraphdstqrb_ */
+
diff --git a/igraph/src/dswap.c b/igraph/src/dswap.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dswap.c
@@ -0,0 +1,104 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdswap_(integer *n, doublereal *dx, integer *incx, 
+	doublereal *dy, integer *incy)
+{
+    /* System generated locals */
+    integer i__1;
+
+    /* Local variables */
+    integer i__, m, ix, iy, mp1;
+    doublereal dtemp;
+
+
+/*  Purpose   
+    =======   
+
+       interchanges two vectors.   
+       uses unrolled loops for increments equal one.   
+
+    Further Details   
+    ===============   
+
+       jack dongarra, linpack, 3/11/78.   
+       modified 12/3/93, array(1) declarations changed to array(*)   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --dy;
+    --dx;
+
+    /* Function Body */
+    if (*n <= 0) {
+	return 0;
+    }
+    if (*incx == 1 && *incy == 1) {
+
+/*       code for both increments equal to 1   
+
+
+         clean-up loop */
+
+	m = *n % 3;
+	if (m != 0) {
+	    i__1 = m;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		dtemp = dx[i__];
+		dx[i__] = dy[i__];
+		dy[i__] = dtemp;
+	    }
+	    if (*n < 3) {
+		return 0;
+	    }
+	}
+	mp1 = m + 1;
+	i__1 = *n;
+	for (i__ = mp1; i__ <= i__1; i__ += 3) {
+	    dtemp = dx[i__];
+	    dx[i__] = dy[i__];
+	    dy[i__] = dtemp;
+	    dtemp = dx[i__ + 1];
+	    dx[i__ + 1] = dy[i__ + 1];
+	    dy[i__ + 1] = dtemp;
+	    dtemp = dx[i__ + 2];
+	    dx[i__ + 2] = dy[i__ + 2];
+	    dy[i__ + 2] = dtemp;
+	}
+    } else {
+
+/*       code for unequal increments or equal increments not equal   
+           to 1 */
+
+	ix = 1;
+	iy = 1;
+	if (*incx < 0) {
+	    ix = (-(*n) + 1) * *incx + 1;
+	}
+	if (*incy < 0) {
+	    iy = (-(*n) + 1) * *incy + 1;
+	}
+	i__1 = *n;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+	    dtemp = dx[ix];
+	    dx[ix] = dy[iy];
+	    dy[iy] = dtemp;
+	    ix += *incx;
+	    iy += *incy;
+	}
+    }
+    return 0;
+} /* igraphdswap_ */
+
diff --git a/igraph/src/dsyevr.c b/igraph/src/dsyevr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsyevr.c
@@ -0,0 +1,759 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__10 = 10;
+static integer c__1 = 1;
+static integer c__2 = 2;
+static integer c__3 = 3;
+static integer c__4 = 4;
+static integer c_n1 = -1;
+
+/* > \brief <b> DSYEVR computes the eigenvalues and, optionally, the left and/or right eigenvectors for SY mat
+rices</b>   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DSYEVR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dsyevr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dsyevr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dsyevr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DSYEVR( JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU,   
+                            ABSTOL, M, W, Z, LDZ, ISUPPZ, WORK, LWORK,   
+                            IWORK, LIWORK, INFO )   
+
+         CHARACTER          JOBZ, RANGE, UPLO   
+         INTEGER            IL, INFO, IU, LDA, LDZ, LIWORK, LWORK, M, N   
+         DOUBLE PRECISION   ABSTOL, VL, VU   
+         INTEGER            ISUPPZ( * ), IWORK( * )   
+         DOUBLE PRECISION   A( LDA, * ), W( * ), WORK( * ), Z( LDZ, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DSYEVR computes selected eigenvalues and, optionally, eigenvectors   
+   > of a real symmetric matrix A.  Eigenvalues and eigenvectors can be   
+   > selected by specifying either a range of values or a range of   
+   > indices for the desired eigenvalues.   
+   >   
+   > DSYEVR first reduces the matrix A to tridiagonal form T with a call   
+   > to DSYTRD.  Then, whenever possible, DSYEVR calls DSTEMR to compute   
+   > the eigenspectrum using Relatively Robust Representations.  DSTEMR   
+   > computes eigenvalues by the dqds algorithm, while orthogonal   
+   > eigenvectors are computed from various "good" L D L^T representations   
+   > (also known as Relatively Robust Representations). Gram-Schmidt   
+   > orthogonalization is avoided as far as possible. More specifically,   
+   > the various steps of the algorithm are as follows.   
+   >   
+   > For each unreduced block (submatrix) of T,   
+   >    (a) Compute T - sigma I  = L D L^T, so that L and D   
+   >        define all the wanted eigenvalues to high relative accuracy.   
+   >        This means that small relative changes in the entries of D and L   
+   >        cause only small relative changes in the eigenvalues and   
+   >        eigenvectors. The standard (unfactored) representation of the   
+   >        tridiagonal matrix T does not have this property in general.   
+   >    (b) Compute the eigenvalues to suitable accuracy.   
+   >        If the eigenvectors are desired, the algorithm attains full   
+   >        accuracy of the computed eigenvalues only right before   
+   >        the corresponding vectors have to be computed, see steps c) and d).   
+   >    (c) For each cluster of close eigenvalues, select a new   
+   >        shift close to the cluster, find a new factorization, and refine   
+   >        the shifted eigenvalues to suitable accuracy.   
+   >    (d) For each eigenvalue with a large enough relative separation compute   
+   >        the corresponding eigenvector by forming a rank revealing twisted   
+   >        factorization. Go back to (c) for any clusters that remain.   
+   >   
+   > The desired accuracy of the output can be specified by the input   
+   > parameter ABSTOL.   
+   >   
+   > For more details, see DSTEMR's documentation and:   
+   > - Inderjit S. Dhillon and Beresford N. Parlett: "Multiple representations   
+   >   to compute orthogonal eigenvectors of symmetric tridiagonal matrices,"   
+   >   Linear Algebra and its Applications, 387(1), pp. 1-28, August 2004.   
+   > - Inderjit Dhillon and Beresford Parlett: "Orthogonal Eigenvectors and   
+   >   Relative Gaps," SIAM Journal on Matrix Analysis and Applications, Vol. 25,   
+   >   2004.  Also LAPACK Working Note 154.   
+   > - Inderjit Dhillon: "A new O(n^2) algorithm for the symmetric   
+   >   tridiagonal eigenvalue/eigenvector problem",   
+   >   Computer Science Division Technical Report No. UCB/CSD-97-971,   
+   >   UC Berkeley, May 1997.   
+   >   
+   >   
+   > Note 1 : DSYEVR calls DSTEMR when the full spectrum is requested   
+   > on machines which conform to the ieee-754 floating point standard.   
+   > DSYEVR calls DSTEBZ and SSTEIN on non-ieee machines and   
+   > when partial spectrum requests are made.   
+   >   
+   > Normal execution of DSTEMR may create NaNs and infinities and   
+   > hence may abort due to a floating point exception in environments   
+   > which do not handle NaNs and infinities in the ieee standard default   
+   > manner.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOBZ   
+   > \verbatim   
+   >          JOBZ is CHARACTER*1   
+   >          = 'N':  Compute eigenvalues only;   
+   >          = 'V':  Compute eigenvalues and eigenvectors.   
+   > \endverbatim   
+   >   
+   > \param[in] RANGE   
+   > \verbatim   
+   >          RANGE is CHARACTER*1   
+   >          = 'A': all eigenvalues will be found.   
+   >          = 'V': all eigenvalues in the half-open interval (VL,VU]   
+   >                 will be found.   
+   >          = 'I': the IL-th through IU-th eigenvalues will be found.   
+   >          For RANGE = 'V' or 'I' and IU - IL < N - 1, DSTEBZ and   
+   >          DSTEIN are called   
+   > \endverbatim   
+   >   
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          = 'U':  Upper triangle of A is stored;   
+   >          = 'L':  Lower triangle of A is stored.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA, N)   
+   >          On entry, the symmetric matrix A.  If UPLO = 'U', the   
+   >          leading N-by-N upper triangular part of A contains the   
+   >          upper triangular part of the matrix A.  If UPLO = 'L',   
+   >          the leading N-by-N lower triangular part of A contains   
+   >          the lower triangular part of the matrix A.   
+   >          On exit, the lower triangle (if UPLO='L') or the upper   
+   >          triangle (if UPLO='U') of A, including the diagonal, is   
+   >          destroyed.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION   
+   > \endverbatim   
+   >   
+   > \param[in] VU   
+   > \verbatim   
+   >          VU is DOUBLE PRECISION   
+   >          If RANGE='V', the lower and upper bounds of the interval to   
+   >          be searched for eigenvalues. VL < VU.   
+   >          Not referenced if RANGE = 'A' or 'I'.   
+   > \endverbatim   
+   >   
+   > \param[in] IL   
+   > \verbatim   
+   >          IL is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IU   
+   > \verbatim   
+   >          IU is INTEGER   
+   >          If RANGE='I', the indices (in ascending order) of the   
+   >          smallest and largest eigenvalues to be returned.   
+   >          1 <= IL <= IU <= N, if N > 0; IL = 1 and IU = 0 if N = 0.   
+   >          Not referenced if RANGE = 'A' or 'V'.   
+   > \endverbatim   
+   >   
+   > \param[in] ABSTOL   
+   > \verbatim   
+   >          ABSTOL is DOUBLE PRECISION   
+   >          The absolute error tolerance for the eigenvalues.   
+   >          An approximate eigenvalue is accepted as converged   
+   >          when it is determined to lie in an interval [a,b]   
+   >          of width less than or equal to   
+   >   
+   >                  ABSTOL + EPS *   max( |a|,|b| ) ,   
+   >   
+   >          where EPS is the machine precision.  If ABSTOL is less than   
+   >          or equal to zero, then  EPS*|T|  will be used in its place,   
+   >          where |T| is the 1-norm of the tridiagonal matrix obtained   
+   >          by reducing A to tridiagonal form.   
+   >   
+   >          See "Computing Small Singular Values of Bidiagonal Matrices   
+   >          with Guaranteed High Relative Accuracy," by Demmel and   
+   >          Kahan, LAPACK Working Note #3.   
+   >   
+   >          If high relative accuracy is important, set ABSTOL to   
+   >          DLAMCH( 'Safe minimum' ).  Doing so will guarantee that   
+   >          eigenvalues are computed to high relative accuracy when   
+   >          possible in future releases.  The current code does not   
+   >          make any guarantees about high relative accuracy, but   
+   >          future releases will. See J. Barlow and J. Demmel,   
+   >          "Computing Accurate Eigensystems of Scaled Diagonally   
+   >          Dominant Matrices", LAPACK Working Note #7, for a discussion   
+   >          of which matrices define their eigenvalues to high relative   
+   >          accuracy.   
+   > \endverbatim   
+   >   
+   > \param[out] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The total number of eigenvalues found.  0 <= M <= N.   
+   >          If RANGE = 'A', M = N, and if RANGE = 'I', M = IU-IL+1.   
+   > \endverbatim   
+   >   
+   > \param[out] W   
+   > \verbatim   
+   >          W is DOUBLE PRECISION array, dimension (N)   
+   >          The first M elements contain the selected eigenvalues in   
+   >          ascending order.   
+   > \endverbatim   
+   >   
+   > \param[out] Z   
+   > \verbatim   
+   >          Z is DOUBLE PRECISION array, dimension (LDZ, max(1,M))   
+   >          If JOBZ = 'V', then if INFO = 0, the first M columns of Z   
+   >          contain the orthonormal eigenvectors of the matrix A   
+   >          corresponding to the selected eigenvalues, with the i-th   
+   >          column of Z holding the eigenvector associated with W(i).   
+   >          If JOBZ = 'N', then Z is not referenced.   
+   >          Note: the user must ensure that at least max(1,M) columns are   
+   >          supplied in the array Z; if RANGE = 'V', the exact value of M   
+   >          is not known in advance and an upper bound must be used.   
+   >          Supplying N columns is always safe.   
+   > \endverbatim   
+   >   
+   > \param[in] LDZ   
+   > \verbatim   
+   >          LDZ is INTEGER   
+   >          The leading dimension of the array Z.  LDZ >= 1, and if   
+   >          JOBZ = 'V', LDZ >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] ISUPPZ   
+   > \verbatim   
+   >          ISUPPZ is INTEGER array, dimension ( 2*max(1,M) )   
+   >          The support of the eigenvectors in Z, i.e., the indices   
+   >          indicating the nonzero elements in Z. The i-th eigenvector   
+   >          is nonzero only in elements ISUPPZ( 2*i-1 ) through   
+   >          ISUPPZ( 2*i ).   
+   >          Implemented only for RANGE = 'A' or 'I' and IU - IL = N - 1   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.  LWORK >= max(1,26*N).   
+   >          For optimal efficiency, LWORK >= (NB+6)*N,   
+   >          where NB is the max of the blocksize for DSYTRD and DORMTR   
+   >          returned by ILAENV.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (MAX(1,LIWORK))   
+   >          On exit, if INFO = 0, IWORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LIWORK   
+   > \verbatim   
+   >          LIWORK is INTEGER   
+   >          The dimension of the array IWORK.  LIWORK >= max(1,10*N).   
+   >   
+   >          If LIWORK = -1, then a workspace query is assumed; the   
+   >          routine only calculates the optimal size of the IWORK array,   
+   >          returns this value as the first entry of the IWORK array, and   
+   >          no error message related to LIWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          > 0:  Internal error   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleSYeigen   
+
+   > \par Contributors:   
+    ==================   
+   >   
+   >     Inderjit Dhillon, IBM Almaden, USA \n   
+   >     Osni Marques, LBNL/NERSC, USA \n   
+   >     Ken Stanley, Computer Science Division, University of   
+   >       California at Berkeley, USA \n   
+   >     Jason Riedy, Computer Science Division, University of   
+   >       California at Berkeley, USA \n   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdsyevr_(char *jobz, char *range, char *uplo, integer *n, 
+	doublereal *a, integer *lda, doublereal *vl, doublereal *vu, integer *
+	il, integer *iu, doublereal *abstol, integer *m, doublereal *w, 
+	doublereal *z__, integer *ldz, integer *isuppz, doublereal *work, 
+	integer *lwork, integer *iwork, integer *liwork, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, z_dim1, z_offset, i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j, nb, jj;
+    doublereal eps, vll, vuu, tmp1;
+    integer indd, inde;
+    doublereal anrm;
+    integer imax;
+    doublereal rmin, rmax;
+    integer inddd, indee;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    doublereal sigma;
+    extern logical igraphlsame_(char *, char *);
+    integer iinfo;
+    char order[1];
+    integer indwk;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *), igraphdswap_(integer *, doublereal *, integer 
+	    *, doublereal *, integer *);
+    integer lwmin;
+    logical lower, wantz;
+    extern doublereal igraphdlamch_(char *);
+    logical alleig, indeig;
+    integer iscale, ieeeok, indibl, indifl;
+    logical valeig;
+    doublereal safmin;
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    doublereal abstll, bignum;
+    integer indtau, indisp;
+    extern /* Subroutine */ int igraphdstein_(integer *, doublereal *, doublereal *,
+	     integer *, doublereal *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *, integer *), 
+	    igraphdsterf_(integer *, doublereal *, doublereal *, integer *);
+    integer indiwo, indwkn;
+    extern doublereal igraphdlansy_(char *, char *, integer *, doublereal *, 
+	    integer *, doublereal *);
+    extern /* Subroutine */ int igraphdstebz_(char *, char *, integer *, doublereal 
+	    *, doublereal *, integer *, integer *, doublereal *, doublereal *,
+	     doublereal *, integer *, integer *, doublereal *, integer *, 
+	    integer *, doublereal *, integer *, integer *), 
+	    igraphdstemr_(char *, char *, integer *, doublereal *, doublereal *, 
+	    doublereal *, doublereal *, integer *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, integer *, integer *, 
+	    logical *, doublereal *, integer *, integer *, integer *, integer 
+	    *);
+    integer liwmin;
+    logical tryrac;
+    extern /* Subroutine */ int igraphdormtr_(char *, char *, char *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *);
+    integer llwrkn, llwork, nsplit;
+    doublereal smlnum;
+    extern /* Subroutine */ int igraphdsytrd_(char *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, doublereal *, doublereal *,
+	     integer *, integer *);
+    integer lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK driver routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+   =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --w;
+    z_dim1 = *ldz;
+    z_offset = 1 + z_dim1;
+    z__ -= z_offset;
+    --isuppz;
+    --work;
+    --iwork;
+
+    /* Function Body */
+    ieeeok = igraphilaenv_(&c__10, "DSYEVR", "N", &c__1, &c__2, &c__3, &c__4, (
+	    ftnlen)6, (ftnlen)1);
+
+    lower = igraphlsame_(uplo, "L");
+    wantz = igraphlsame_(jobz, "V");
+    alleig = igraphlsame_(range, "A");
+    valeig = igraphlsame_(range, "V");
+    indeig = igraphlsame_(range, "I");
+
+    lquery = *lwork == -1 || *liwork == -1;
+
+/* Computing MAX */
+    i__1 = 1, i__2 = *n * 26;
+    lwmin = max(i__1,i__2);
+/* Computing MAX */
+    i__1 = 1, i__2 = *n * 10;
+    liwmin = max(i__1,i__2);
+
+    *info = 0;
+    if (! (wantz || igraphlsame_(jobz, "N"))) {
+	*info = -1;
+    } else if (! (alleig || valeig || indeig)) {
+	*info = -2;
+    } else if (! (lower || igraphlsame_(uplo, "U"))) {
+	*info = -3;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*lda < max(1,*n)) {
+	*info = -6;
+    } else {
+	if (valeig) {
+	    if (*n > 0 && *vu <= *vl) {
+		*info = -8;
+	    }
+	} else if (indeig) {
+	    if (*il < 1 || *il > max(1,*n)) {
+		*info = -9;
+	    } else if (*iu < min(*n,*il) || *iu > *n) {
+		*info = -10;
+	    }
+	}
+    }
+    if (*info == 0) {
+	if (*ldz < 1 || wantz && *ldz < *n) {
+	    *info = -15;
+	} else if (*lwork < lwmin && ! lquery) {
+	    *info = -18;
+	} else if (*liwork < liwmin && ! lquery) {
+	    *info = -20;
+	}
+    }
+
+    if (*info == 0) {
+	nb = igraphilaenv_(&c__1, "DSYTRD", uplo, n, &c_n1, &c_n1, &c_n1, (ftnlen)6,
+		 (ftnlen)1);
+/* Computing MAX */
+	i__1 = nb, i__2 = igraphilaenv_(&c__1, "DORMTR", uplo, n, &c_n1, &c_n1, &
+		c_n1, (ftnlen)6, (ftnlen)1);
+	nb = max(i__1,i__2);
+/* Computing MAX */
+	i__1 = (nb + 1) * *n;
+	lwkopt = max(i__1,lwmin);
+	work[1] = (doublereal) lwkopt;
+	iwork[1] = liwmin;
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DSYEVR", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    *m = 0;
+    if (*n == 0) {
+	work[1] = 1.;
+	return 0;
+    }
+
+    if (*n == 1) {
+	work[1] = 7.;
+	if (alleig || indeig) {
+	    *m = 1;
+	    w[1] = a[a_dim1 + 1];
+	} else {
+	    if (*vl < a[a_dim1 + 1] && *vu >= a[a_dim1 + 1]) {
+		*m = 1;
+		w[1] = a[a_dim1 + 1];
+	    }
+	}
+	if (wantz) {
+	    z__[z_dim1 + 1] = 1.;
+	    isuppz[1] = 1;
+	    isuppz[2] = 1;
+	}
+	return 0;
+    }
+
+/*     Get machine constants. */
+
+    safmin = igraphdlamch_("Safe minimum");
+    eps = igraphdlamch_("Precision");
+    smlnum = safmin / eps;
+    bignum = 1. / smlnum;
+    rmin = sqrt(smlnum);
+/* Computing MIN */
+    d__1 = sqrt(bignum), d__2 = 1. / sqrt(sqrt(safmin));
+    rmax = min(d__1,d__2);
+
+/*     Scale matrix to allowable range, if necessary. */
+
+    iscale = 0;
+    abstll = *abstol;
+    if (valeig) {
+	vll = *vl;
+	vuu = *vu;
+    }
+    anrm = igraphdlansy_("M", uplo, n, &a[a_offset], lda, &work[1]);
+    if (anrm > 0. && anrm < rmin) {
+	iscale = 1;
+	sigma = rmin / anrm;
+    } else if (anrm > rmax) {
+	iscale = 1;
+	sigma = rmax / anrm;
+    }
+    if (iscale == 1) {
+	if (lower) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *n - j + 1;
+		igraphdscal_(&i__2, &sigma, &a[j + j * a_dim1], &c__1);
+/* L10: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		igraphdscal_(&j, &sigma, &a[j * a_dim1 + 1], &c__1);
+/* L20: */
+	    }
+	}
+	if (*abstol > 0.) {
+	    abstll = *abstol * sigma;
+	}
+	if (valeig) {
+	    vll = *vl * sigma;
+	    vuu = *vu * sigma;
+	}
+    }
+/*     Initialize indices into workspaces.  Note: The IWORK indices are   
+       used only if DSTERF or DSTEMR fail.   
+       WORK(INDTAU:INDTAU+N-1) stores the scalar factors of the   
+       elementary reflectors used in DSYTRD. */
+    indtau = 1;
+/*     WORK(INDD:INDD+N-1) stores the tridiagonal's diagonal entries. */
+    indd = indtau + *n;
+/*     WORK(INDE:INDE+N-1) stores the off-diagonal entries of the   
+       tridiagonal matrix from DSYTRD. */
+    inde = indd + *n;
+/*     WORK(INDDD:INDDD+N-1) is a copy of the diagonal entries over   
+       -written by DSTEMR (the DSTERF path copies the diagonal to W). */
+    inddd = inde + *n;
+/*     WORK(INDEE:INDEE+N-1) is a copy of the off-diagonal entries over   
+       -written while computing the eigenvalues in DSTERF and DSTEMR. */
+    indee = inddd + *n;
+/*     INDWK is the starting offset of the left-over workspace, and   
+       LLWORK is the remaining workspace size. */
+    indwk = indee + *n;
+    llwork = *lwork - indwk + 1;
+/*     IWORK(INDIBL:INDIBL+M-1) corresponds to IBLOCK in DSTEBZ and   
+       stores the block indices of each of the M<=N eigenvalues. */
+    indibl = 1;
+/*     IWORK(INDISP:INDISP+NSPLIT-1) corresponds to ISPLIT in DSTEBZ and   
+       stores the starting and finishing indices of each block. */
+    indisp = indibl + *n;
+/*     IWORK(INDIFL:INDIFL+N-1) stores the indices of eigenvectors   
+       that corresponding to eigenvectors that fail to converge in   
+       DSTEIN.  This information is discarded; if any fail, the driver   
+       returns INFO > 0. */
+    indifl = indisp + *n;
+/*     INDIWO is the offset of the remaining integer workspace. */
+    indiwo = indifl + *n;
+
+/*     Call DSYTRD to reduce symmetric matrix to tridiagonal form. */
+
+    igraphdsytrd_(uplo, n, &a[a_offset], lda, &work[indd], &work[inde], &work[
+	    indtau], &work[indwk], &llwork, &iinfo);
+
+/*     If all eigenvalues are desired   
+       then call DSTERF or DSTEMR and DORMTR. */
+
+    if ((alleig || indeig && *il == 1 && *iu == *n) && ieeeok == 1) {
+	if (! wantz) {
+	    igraphdcopy_(n, &work[indd], &c__1, &w[1], &c__1);
+	    i__1 = *n - 1;
+	    igraphdcopy_(&i__1, &work[inde], &c__1, &work[indee], &c__1);
+	    igraphdsterf_(n, &w[1], &work[indee], info);
+	} else {
+	    i__1 = *n - 1;
+	    igraphdcopy_(&i__1, &work[inde], &c__1, &work[indee], &c__1);
+	    igraphdcopy_(n, &work[indd], &c__1, &work[inddd], &c__1);
+
+	    if (*abstol <= *n * 2. * eps) {
+		tryrac = TRUE_;
+	    } else {
+		tryrac = FALSE_;
+	    }
+	    igraphdstemr_(jobz, "A", n, &work[inddd], &work[indee], vl, vu, il, iu, 
+		    m, &w[1], &z__[z_offset], ldz, n, &isuppz[1], &tryrac, &
+		    work[indwk], lwork, &iwork[1], liwork, info);
+
+
+
+/*        Apply orthogonal matrix used in reduction to tridiagonal   
+          form to eigenvectors returned by DSTEIN. */
+
+	    if (wantz && *info == 0) {
+		indwkn = inde;
+		llwrkn = *lwork - indwkn + 1;
+		igraphdormtr_("L", uplo, "N", n, m, &a[a_offset], lda, &work[indtau]
+			, &z__[z_offset], ldz, &work[indwkn], &llwrkn, &iinfo);
+	    }
+	}
+
+
+	if (*info == 0) {
+/*           Everything worked.  Skip DSTEBZ/DSTEIN.  IWORK(:) are   
+             undefined. */
+	    *m = *n;
+	    goto L30;
+	}
+	*info = 0;
+    }
+
+/*     Otherwise, call DSTEBZ and, if eigenvectors are desired, DSTEIN.   
+       Also call DSTEBZ and DSTEIN if DSTEMR fails. */
+
+    if (wantz) {
+	*(unsigned char *)order = 'B';
+    } else {
+	*(unsigned char *)order = 'E';
+    }
+    igraphdstebz_(range, order, n, &vll, &vuu, il, iu, &abstll, &work[indd], &work[
+	    inde], m, &nsplit, &w[1], &iwork[indibl], &iwork[indisp], &work[
+	    indwk], &iwork[indiwo], info);
+
+    if (wantz) {
+	igraphdstein_(n, &work[indd], &work[inde], m, &w[1], &iwork[indibl], &iwork[
+		indisp], &z__[z_offset], ldz, &work[indwk], &iwork[indiwo], &
+		iwork[indifl], info);
+
+/*        Apply orthogonal matrix used in reduction to tridiagonal   
+          form to eigenvectors returned by DSTEIN. */
+
+	indwkn = inde;
+	llwrkn = *lwork - indwkn + 1;
+	igraphdormtr_("L", uplo, "N", n, m, &a[a_offset], lda, &work[indtau], &z__[
+		z_offset], ldz, &work[indwkn], &llwrkn, &iinfo);
+    }
+
+/*     If matrix was scaled, then rescale eigenvalues appropriately.   
+
+    Jump here if DSTEMR/DSTEIN succeeded. */
+L30:
+    if (iscale == 1) {
+	if (*info == 0) {
+	    imax = *m;
+	} else {
+	    imax = *info - 1;
+	}
+	d__1 = 1. / sigma;
+	igraphdscal_(&imax, &d__1, &w[1], &c__1);
+    }
+
+/*     If eigenvalues are not in order, then sort them, along with   
+       eigenvectors.  Note: We do not sort the IFAIL portion of IWORK.   
+       It may not be initialized (if DSTEMR/DSTEIN succeeded), and we do   
+       not return this detailed information to the user. */
+
+    if (wantz) {
+	i__1 = *m - 1;
+	for (j = 1; j <= i__1; ++j) {
+	    i__ = 0;
+	    tmp1 = w[j];
+	    i__2 = *m;
+	    for (jj = j + 1; jj <= i__2; ++jj) {
+		if (w[jj] < tmp1) {
+		    i__ = jj;
+		    tmp1 = w[jj];
+		}
+/* L40: */
+	    }
+
+	    if (i__ != 0) {
+		w[i__] = w[j];
+		w[j] = tmp1;
+		igraphdswap_(n, &z__[i__ * z_dim1 + 1], &c__1, &z__[j * z_dim1 + 1],
+			 &c__1);
+	    }
+/* L50: */
+	}
+    }
+
+/*     Set WORK(1) to optimal workspace size. */
+
+    work[1] = (doublereal) lwkopt;
+    iwork[1] = liwmin;
+
+    return 0;
+
+/*     End of DSYEVR */
+
+} /* igraphdsyevr_ */
+
diff --git a/igraph/src/dsymv.c b/igraph/src/dsymv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsymv.c
@@ -0,0 +1,303 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdsymv_(char *uplo, integer *n, doublereal *alpha, 
+	doublereal *a, integer *lda, doublereal *x, integer *incx, doublereal 
+	*beta, doublereal *y, integer *incy)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j, ix, iy, jx, jy, kx, ky, info;
+    doublereal temp1, temp2;
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  Purpose   
+    =======   
+
+    DSYMV  performs the matrix-vector  operation   
+
+       y := alpha*A*x + beta*y,   
+
+    where alpha and beta are scalars, x and y are n element vectors and   
+    A is an n by n symmetric matrix.   
+
+    Arguments   
+    ==========   
+
+    UPLO   - CHARACTER*1.   
+             On entry, UPLO specifies whether the upper or lower   
+             triangular part of the array A is to be referenced as   
+             follows:   
+
+                UPLO = 'U' or 'u'   Only the upper triangular part of A   
+                                    is to be referenced.   
+
+                UPLO = 'L' or 'l'   Only the lower triangular part of A   
+                                    is to be referenced.   
+
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry, N specifies the order of the matrix A.   
+             N must be at least zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry, ALPHA specifies the scalar alpha.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, n ).   
+             Before entry with  UPLO = 'U' or 'u', the leading n by n   
+             upper triangular part of the array A must contain the upper   
+             triangular part of the symmetric matrix and the strictly   
+             lower triangular part of A is not referenced.   
+             Before entry with UPLO = 'L' or 'l', the leading n by n   
+             lower triangular part of the array A must contain the lower   
+             triangular part of the symmetric matrix and the strictly   
+             upper triangular part of A is not referenced.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program. LDA must be at least   
+             max( 1, n ).   
+             Unchanged on exit.   
+
+    X      - DOUBLE PRECISION array of dimension at least   
+             ( 1 + ( n - 1 )*abs( INCX ) ).   
+             Before entry, the incremented array X must contain the n   
+             element vector x.   
+             Unchanged on exit.   
+
+    INCX   - INTEGER.   
+             On entry, INCX specifies the increment for the elements of   
+             X. INCX must not be zero.   
+             Unchanged on exit.   
+
+    BETA   - DOUBLE PRECISION.   
+             On entry, BETA specifies the scalar beta. When BETA is   
+             supplied as zero then Y need not be set on input.   
+             Unchanged on exit.   
+
+    Y      - DOUBLE PRECISION array of dimension at least   
+             ( 1 + ( n - 1 )*abs( INCY ) ).   
+             Before entry, the incremented array Y must contain the n   
+             element vector y. On exit, Y is overwritten by the updated   
+             vector y.   
+
+    INCY   - INTEGER.   
+             On entry, INCY specifies the increment for the elements of   
+             Y. INCY must not be zero.   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 2 Blas routine.   
+    The vector and matrix arguments are not referenced when N = 0, or M = 0   
+
+    -- Written on 22-October-1986.   
+       Jack Dongarra, Argonne National Lab.   
+       Jeremy Du Croz, Nag Central Office.   
+       Sven Hammarling, Nag Central Office.   
+       Richard Hanson, Sandia National Labs.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --x;
+    --y;
+
+    /* Function Body */
+    info = 0;
+    if (! igraphlsame_(uplo, "U") && ! igraphlsame_(uplo, "L")) {
+	info = 1;
+    } else if (*n < 0) {
+	info = 2;
+    } else if (*lda < max(1,*n)) {
+	info = 5;
+    } else if (*incx == 0) {
+	info = 7;
+    } else if (*incy == 0) {
+	info = 10;
+    }
+    if (info != 0) {
+	igraphxerbla_("DSYMV ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*n == 0 || *alpha == 0. && *beta == 1.) {
+	return 0;
+    }
+
+/*     Set up the start points in  X  and  Y. */
+
+    if (*incx > 0) {
+	kx = 1;
+    } else {
+	kx = 1 - (*n - 1) * *incx;
+    }
+    if (*incy > 0) {
+	ky = 1;
+    } else {
+	ky = 1 - (*n - 1) * *incy;
+    }
+
+/*     Start the operations. In this version the elements of A are   
+       accessed sequentially with one pass through the triangular part   
+       of A.   
+
+       First form  y := beta*y. */
+
+    if (*beta != 1.) {
+	if (*incy == 1) {
+	    if (*beta == 0.) {
+		i__1 = *n;
+		for (i__ = 1; i__ <= i__1; ++i__) {
+		    y[i__] = 0.;
+/* L10: */
+		}
+	    } else {
+		i__1 = *n;
+		for (i__ = 1; i__ <= i__1; ++i__) {
+		    y[i__] = *beta * y[i__];
+/* L20: */
+		}
+	    }
+	} else {
+	    iy = ky;
+	    if (*beta == 0.) {
+		i__1 = *n;
+		for (i__ = 1; i__ <= i__1; ++i__) {
+		    y[iy] = 0.;
+		    iy += *incy;
+/* L30: */
+		}
+	    } else {
+		i__1 = *n;
+		for (i__ = 1; i__ <= i__1; ++i__) {
+		    y[iy] = *beta * y[iy];
+		    iy += *incy;
+/* L40: */
+		}
+	    }
+	}
+    }
+    if (*alpha == 0.) {
+	return 0;
+    }
+    if (igraphlsame_(uplo, "U")) {
+
+/*        Form  y  when A is stored in upper triangle. */
+
+	if (*incx == 1 && *incy == 1) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		temp1 = *alpha * x[j];
+		temp2 = 0.;
+		i__2 = j - 1;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    y[i__] += temp1 * a[i__ + j * a_dim1];
+		    temp2 += a[i__ + j * a_dim1] * x[i__];
+/* L50: */
+		}
+		y[j] = y[j] + temp1 * a[j + j * a_dim1] + *alpha * temp2;
+/* L60: */
+	    }
+	} else {
+	    jx = kx;
+	    jy = ky;
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		temp1 = *alpha * x[jx];
+		temp2 = 0.;
+		ix = kx;
+		iy = ky;
+		i__2 = j - 1;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    y[iy] += temp1 * a[i__ + j * a_dim1];
+		    temp2 += a[i__ + j * a_dim1] * x[ix];
+		    ix += *incx;
+		    iy += *incy;
+/* L70: */
+		}
+		y[jy] = y[jy] + temp1 * a[j + j * a_dim1] + *alpha * temp2;
+		jx += *incx;
+		jy += *incy;
+/* L80: */
+	    }
+	}
+    } else {
+
+/*        Form  y  when A is stored in lower triangle. */
+
+	if (*incx == 1 && *incy == 1) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		temp1 = *alpha * x[j];
+		temp2 = 0.;
+		y[j] += temp1 * a[j + j * a_dim1];
+		i__2 = *n;
+		for (i__ = j + 1; i__ <= i__2; ++i__) {
+		    y[i__] += temp1 * a[i__ + j * a_dim1];
+		    temp2 += a[i__ + j * a_dim1] * x[i__];
+/* L90: */
+		}
+		y[j] += *alpha * temp2;
+/* L100: */
+	    }
+	} else {
+	    jx = kx;
+	    jy = ky;
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		temp1 = *alpha * x[jx];
+		temp2 = 0.;
+		y[jy] += temp1 * a[j + j * a_dim1];
+		ix = jx;
+		iy = jy;
+		i__2 = *n;
+		for (i__ = j + 1; i__ <= i__2; ++i__) {
+		    ix += *incx;
+		    iy += *incy;
+		    y[iy] += temp1 * a[i__ + j * a_dim1];
+		    temp2 += a[i__ + j * a_dim1] * x[ix];
+/* L110: */
+		}
+		y[jy] += *alpha * temp2;
+		jx += *incx;
+		jy += *incy;
+/* L120: */
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DSYMV . */
+
+} /* igraphdsymv_ */
+
diff --git a/igraph/src/dsyr2.c b/igraph/src/dsyr2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsyr2.c
@@ -0,0 +1,264 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdsyr2_(char *uplo, integer *n, doublereal *alpha, 
+	doublereal *x, integer *incx, doublereal *y, integer *incy, 
+	doublereal *a, integer *lda)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j, ix, iy, jx, jy, kx, ky, info;
+    doublereal temp1, temp2;
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  Purpose   
+    =======   
+
+    DSYR2  performs the symmetric rank 2 operation   
+
+       A := alpha*x*y**T + alpha*y*x**T + A,   
+
+    where alpha is a scalar, x and y are n element vectors and A is an n   
+    by n symmetric matrix.   
+
+    Arguments   
+    ==========   
+
+    UPLO   - CHARACTER*1.   
+             On entry, UPLO specifies whether the upper or lower   
+             triangular part of the array A is to be referenced as   
+             follows:   
+
+                UPLO = 'U' or 'u'   Only the upper triangular part of A   
+                                    is to be referenced.   
+
+                UPLO = 'L' or 'l'   Only the lower triangular part of A   
+                                    is to be referenced.   
+
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry, N specifies the order of the matrix A.   
+             N must be at least zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry, ALPHA specifies the scalar alpha.   
+             Unchanged on exit.   
+
+    X      - DOUBLE PRECISION array of dimension at least   
+             ( 1 + ( n - 1 )*abs( INCX ) ).   
+             Before entry, the incremented array X must contain the n   
+             element vector x.   
+             Unchanged on exit.   
+
+    INCX   - INTEGER.   
+             On entry, INCX specifies the increment for the elements of   
+             X. INCX must not be zero.   
+             Unchanged on exit.   
+
+    Y      - DOUBLE PRECISION array of dimension at least   
+             ( 1 + ( n - 1 )*abs( INCY ) ).   
+             Before entry, the incremented array Y must contain the n   
+             element vector y.   
+             Unchanged on exit.   
+
+    INCY   - INTEGER.   
+             On entry, INCY specifies the increment for the elements of   
+             Y. INCY must not be zero.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, n ).   
+             Before entry with  UPLO = 'U' or 'u', the leading n by n   
+             upper triangular part of the array A must contain the upper   
+             triangular part of the symmetric matrix and the strictly   
+             lower triangular part of A is not referenced. On exit, the   
+             upper triangular part of the array A is overwritten by the   
+             upper triangular part of the updated matrix.   
+             Before entry with UPLO = 'L' or 'l', the leading n by n   
+             lower triangular part of the array A must contain the lower   
+             triangular part of the symmetric matrix and the strictly   
+             upper triangular part of A is not referenced. On exit, the   
+             lower triangular part of the array A is overwritten by the   
+             lower triangular part of the updated matrix.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program. LDA must be at least   
+             max( 1, n ).   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 2 Blas routine.   
+
+    -- Written on 22-October-1986.   
+       Jack Dongarra, Argonne National Lab.   
+       Jeremy Du Croz, Nag Central Office.   
+       Sven Hammarling, Nag Central Office.   
+       Richard Hanson, Sandia National Labs.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    --x;
+    --y;
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    info = 0;
+    if (! igraphlsame_(uplo, "U") && ! igraphlsame_(uplo, "L")) {
+	info = 1;
+    } else if (*n < 0) {
+	info = 2;
+    } else if (*incx == 0) {
+	info = 5;
+    } else if (*incy == 0) {
+	info = 7;
+    } else if (*lda < max(1,*n)) {
+	info = 9;
+    }
+    if (info != 0) {
+	igraphxerbla_("DSYR2 ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*n == 0 || *alpha == 0.) {
+	return 0;
+    }
+
+/*     Set up the start points in X and Y if the increments are not both   
+       unity. */
+
+    if (*incx != 1 || *incy != 1) {
+	if (*incx > 0) {
+	    kx = 1;
+	} else {
+	    kx = 1 - (*n - 1) * *incx;
+	}
+	if (*incy > 0) {
+	    ky = 1;
+	} else {
+	    ky = 1 - (*n - 1) * *incy;
+	}
+	jx = kx;
+	jy = ky;
+    }
+
+/*     Start the operations. In this version the elements of A are   
+       accessed sequentially with one pass through the triangular part   
+       of A. */
+
+    if (igraphlsame_(uplo, "U")) {
+
+/*        Form  A  when A is stored in the upper triangle. */
+
+	if (*incx == 1 && *incy == 1) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (x[j] != 0. || y[j] != 0.) {
+		    temp1 = *alpha * y[j];
+		    temp2 = *alpha * x[j];
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			a[i__ + j * a_dim1] = a[i__ + j * a_dim1] + x[i__] * 
+				temp1 + y[i__] * temp2;
+/* L10: */
+		    }
+		}
+/* L20: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (x[jx] != 0. || y[jy] != 0.) {
+		    temp1 = *alpha * y[jy];
+		    temp2 = *alpha * x[jx];
+		    ix = kx;
+		    iy = ky;
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			a[i__ + j * a_dim1] = a[i__ + j * a_dim1] + x[ix] * 
+				temp1 + y[iy] * temp2;
+			ix += *incx;
+			iy += *incy;
+/* L30: */
+		    }
+		}
+		jx += *incx;
+		jy += *incy;
+/* L40: */
+	    }
+	}
+    } else {
+
+/*        Form  A  when A is stored in the lower triangle. */
+
+	if (*incx == 1 && *incy == 1) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (x[j] != 0. || y[j] != 0.) {
+		    temp1 = *alpha * y[j];
+		    temp2 = *alpha * x[j];
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			a[i__ + j * a_dim1] = a[i__ + j * a_dim1] + x[i__] * 
+				temp1 + y[i__] * temp2;
+/* L50: */
+		    }
+		}
+/* L60: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (x[jx] != 0. || y[jy] != 0.) {
+		    temp1 = *alpha * y[jy];
+		    temp2 = *alpha * x[jx];
+		    ix = jx;
+		    iy = jy;
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			a[i__ + j * a_dim1] = a[i__ + j * a_dim1] + x[ix] * 
+				temp1 + y[iy] * temp2;
+			ix += *incx;
+			iy += *incy;
+/* L70: */
+		    }
+		}
+		jx += *incx;
+		jy += *incy;
+/* L80: */
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DSYR2 . */
+
+} /* igraphdsyr2_ */
+
diff --git a/igraph/src/dsyr2k.c b/igraph/src/dsyr2k.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsyr2k.c
@@ -0,0 +1,396 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdsyr2k_(char *uplo, char *trans, integer *n, integer *k, 
+	doublereal *alpha, doublereal *a, integer *lda, doublereal *b, 
+	integer *ldb, doublereal *beta, doublereal *c__, integer *ldc)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, c_dim1, c_offset, i__1, i__2, 
+	    i__3;
+
+    /* Local variables */
+    integer i__, j, l, info;
+    doublereal temp1, temp2;
+    extern logical igraphlsame_(char *, char *);
+    integer nrowa;
+    logical upper;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  Purpose   
+    =======   
+
+    DSYR2K  performs one of the symmetric rank 2k operations   
+
+       C := alpha*A*B**T + alpha*B*A**T + beta*C,   
+
+    or   
+
+       C := alpha*A**T*B + alpha*B**T*A + beta*C,   
+
+    where  alpha and beta  are scalars, C is an  n by n  symmetric matrix   
+    and  A and B  are  n by k  matrices  in the  first  case  and  k by n   
+    matrices in the second case.   
+
+    Arguments   
+    ==========   
+
+    UPLO   - CHARACTER*1.   
+             On  entry,   UPLO  specifies  whether  the  upper  or  lower   
+             triangular  part  of the  array  C  is to be  referenced  as   
+             follows:   
+
+                UPLO = 'U' or 'u'   Only the  upper triangular part of  C   
+                                    is to be referenced.   
+
+                UPLO = 'L' or 'l'   Only the  lower triangular part of  C   
+                                    is to be referenced.   
+
+             Unchanged on exit.   
+
+    TRANS  - CHARACTER*1.   
+             On entry,  TRANS  specifies the operation to be performed as   
+             follows:   
+
+                TRANS = 'N' or 'n'   C := alpha*A*B**T + alpha*B*A**T +   
+                                          beta*C.   
+
+                TRANS = 'T' or 't'   C := alpha*A**T*B + alpha*B**T*A +   
+                                          beta*C.   
+
+                TRANS = 'C' or 'c'   C := alpha*A**T*B + alpha*B**T*A +   
+                                          beta*C.   
+
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry,  N specifies the order of the matrix C.  N must be   
+             at least zero.   
+             Unchanged on exit.   
+
+    K      - INTEGER.   
+             On entry with  TRANS = 'N' or 'n',  K  specifies  the number   
+             of  columns  of the  matrices  A and B,  and on  entry  with   
+             TRANS = 'T' or 't' or 'C' or 'c',  K  specifies  the  number   
+             of rows of the matrices  A and B.  K must be at least  zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry, ALPHA specifies the scalar alpha.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is   
+             k  when  TRANS = 'N' or 'n',  and is  n  otherwise.   
+             Before entry with  TRANS = 'N' or 'n',  the  leading  n by k   
+             part of the array  A  must contain the matrix  A,  otherwise   
+             the leading  k by n  part of the array  A  must contain  the   
+             matrix A.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in  the  calling  (sub)  program.   When  TRANS = 'N' or 'n'   
+             then  LDA must be at least  max( 1, n ), otherwise  LDA must   
+             be at least  max( 1, k ).   
+             Unchanged on exit.   
+
+    B      - DOUBLE PRECISION array of DIMENSION ( LDB, kb ), where kb is   
+             k  when  TRANS = 'N' or 'n',  and is  n  otherwise.   
+             Before entry with  TRANS = 'N' or 'n',  the  leading  n by k   
+             part of the array  B  must contain the matrix  B,  otherwise   
+             the leading  k by n  part of the array  B  must contain  the   
+             matrix B.   
+             Unchanged on exit.   
+
+    LDB    - INTEGER.   
+             On entry, LDB specifies the first dimension of B as declared   
+             in  the  calling  (sub)  program.   When  TRANS = 'N' or 'n'   
+             then  LDB must be at least  max( 1, n ), otherwise  LDB must   
+             be at least  max( 1, k ).   
+             Unchanged on exit.   
+
+    BETA   - DOUBLE PRECISION.   
+             On entry, BETA specifies the scalar beta.   
+             Unchanged on exit.   
+
+    C      - DOUBLE PRECISION array of DIMENSION ( LDC, n ).   
+             Before entry  with  UPLO = 'U' or 'u',  the leading  n by n   
+             upper triangular part of the array C must contain the upper   
+             triangular part  of the  symmetric matrix  and the strictly   
+             lower triangular part of C is not referenced.  On exit, the   
+             upper triangular part of the array  C is overwritten by the   
+             upper triangular part of the updated matrix.   
+             Before entry  with  UPLO = 'L' or 'l',  the leading  n by n   
+             lower triangular part of the array C must contain the lower   
+             triangular part  of the  symmetric matrix  and the strictly   
+             upper triangular part of C is not referenced.  On exit, the   
+             lower triangular part of the array  C is overwritten by the   
+             lower triangular part of the updated matrix.   
+
+    LDC    - INTEGER.   
+             On entry, LDC specifies the first dimension of C as declared   
+             in  the  calling  (sub)  program.   LDC  must  be  at  least   
+             max( 1, n ).   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 3 Blas routine.   
+
+
+    -- Written on 8-February-1989.   
+       Jack Dongarra, Argonne National Laboratory.   
+       Iain Duff, AERE Harwell.   
+       Jeremy Du Croz, Numerical Algorithms Group Ltd.   
+       Sven Hammarling, Numerical Algorithms Group Ltd.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+
+    /* Function Body */
+    if (igraphlsame_(trans, "N")) {
+	nrowa = *n;
+    } else {
+	nrowa = *k;
+    }
+    upper = igraphlsame_(uplo, "U");
+
+    info = 0;
+    if (! upper && ! igraphlsame_(uplo, "L")) {
+	info = 1;
+    } else if (! igraphlsame_(trans, "N") && ! igraphlsame_(trans, 
+	    "T") && ! igraphlsame_(trans, "C")) {
+	info = 2;
+    } else if (*n < 0) {
+	info = 3;
+    } else if (*k < 0) {
+	info = 4;
+    } else if (*lda < max(1,nrowa)) {
+	info = 7;
+    } else if (*ldb < max(1,nrowa)) {
+	info = 9;
+    } else if (*ldc < max(1,*n)) {
+	info = 12;
+    }
+    if (info != 0) {
+	igraphxerbla_("DSYR2K", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*n == 0 || (*alpha == 0. || *k == 0) && *beta == 1.) {
+	return 0;
+    }
+
+/*     And when  alpha.eq.zero. */
+
+    if (*alpha == 0.) {
+	if (upper) {
+	    if (*beta == 0.) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L10: */
+		    }
+/* L20: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L30: */
+		    }
+/* L40: */
+		}
+	    }
+	} else {
+	    if (*beta == 0.) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L50: */
+		    }
+/* L60: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L70: */
+		    }
+/* L80: */
+		}
+	    }
+	}
+	return 0;
+    }
+
+/*     Start the operations. */
+
+    if (igraphlsame_(trans, "N")) {
+
+/*        Form  C := alpha*A*B**T + alpha*B*A**T + C. */
+
+	if (upper) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (*beta == 0.) {
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L90: */
+		    }
+		} else if (*beta != 1.) {
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L100: */
+		    }
+		}
+		i__2 = *k;
+		for (l = 1; l <= i__2; ++l) {
+		    if (a[j + l * a_dim1] != 0. || b[j + l * b_dim1] != 0.) {
+			temp1 = *alpha * b[j + l * b_dim1];
+			temp2 = *alpha * a[j + l * a_dim1];
+			i__3 = j;
+			for (i__ = 1; i__ <= i__3; ++i__) {
+			    c__[i__ + j * c_dim1] = c__[i__ + j * c_dim1] + a[
+				    i__ + l * a_dim1] * temp1 + b[i__ + l * 
+				    b_dim1] * temp2;
+/* L110: */
+			}
+		    }
+/* L120: */
+		}
+/* L130: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (*beta == 0.) {
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L140: */
+		    }
+		} else if (*beta != 1.) {
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L150: */
+		    }
+		}
+		i__2 = *k;
+		for (l = 1; l <= i__2; ++l) {
+		    if (a[j + l * a_dim1] != 0. || b[j + l * b_dim1] != 0.) {
+			temp1 = *alpha * b[j + l * b_dim1];
+			temp2 = *alpha * a[j + l * a_dim1];
+			i__3 = *n;
+			for (i__ = j; i__ <= i__3; ++i__) {
+			    c__[i__ + j * c_dim1] = c__[i__ + j * c_dim1] + a[
+				    i__ + l * a_dim1] * temp1 + b[i__ + l * 
+				    b_dim1] * temp2;
+/* L160: */
+			}
+		    }
+/* L170: */
+		}
+/* L180: */
+	    }
+	}
+    } else {
+
+/*        Form  C := alpha*A**T*B + alpha*B**T*A + C. */
+
+	if (upper) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = j;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    temp1 = 0.;
+		    temp2 = 0.;
+		    i__3 = *k;
+		    for (l = 1; l <= i__3; ++l) {
+			temp1 += a[l + i__ * a_dim1] * b[l + j * b_dim1];
+			temp2 += b[l + i__ * b_dim1] * a[l + j * a_dim1];
+/* L190: */
+		    }
+		    if (*beta == 0.) {
+			c__[i__ + j * c_dim1] = *alpha * temp1 + *alpha * 
+				temp2;
+		    } else {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1] 
+				+ *alpha * temp1 + *alpha * temp2;
+		    }
+/* L200: */
+		}
+/* L210: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *n;
+		for (i__ = j; i__ <= i__2; ++i__) {
+		    temp1 = 0.;
+		    temp2 = 0.;
+		    i__3 = *k;
+		    for (l = 1; l <= i__3; ++l) {
+			temp1 += a[l + i__ * a_dim1] * b[l + j * b_dim1];
+			temp2 += b[l + i__ * b_dim1] * a[l + j * a_dim1];
+/* L220: */
+		    }
+		    if (*beta == 0.) {
+			c__[i__ + j * c_dim1] = *alpha * temp1 + *alpha * 
+				temp2;
+		    } else {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1] 
+				+ *alpha * temp1 + *alpha * temp2;
+		    }
+/* L230: */
+		}
+/* L240: */
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DSYR2K. */
+
+} /* igraphdsyr2k_ */
+
diff --git a/igraph/src/dsyrk.c b/igraph/src/dsyrk.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsyrk.c
@@ -0,0 +1,361 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdsyrk_(char *uplo, char *trans, integer *n, integer *k, 
+	doublereal *alpha, doublereal *a, integer *lda, doublereal *beta, 
+	doublereal *c__, integer *ldc)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, c_dim1, c_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__, j, l, info;
+    doublereal temp;
+    extern logical igraphlsame_(char *, char *);
+    integer nrowa;
+    logical upper;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+
+
+/*  Purpose   
+    =======   
+
+    DSYRK  performs one of the symmetric rank k operations   
+
+       C := alpha*A*A**T + beta*C,   
+
+    or   
+
+       C := alpha*A**T*A + beta*C,   
+
+    where  alpha and beta  are scalars, C is an  n by n  symmetric matrix   
+    and  A  is an  n by k  matrix in the first case and a  k by n  matrix   
+    in the second case.   
+
+    Arguments   
+    ==========   
+
+    UPLO   - CHARACTER*1.   
+             On  entry,   UPLO  specifies  whether  the  upper  or  lower   
+             triangular  part  of the  array  C  is to be  referenced  as   
+             follows:   
+
+                UPLO = 'U' or 'u'   Only the  upper triangular part of  C   
+                                    is to be referenced.   
+
+                UPLO = 'L' or 'l'   Only the  lower triangular part of  C   
+                                    is to be referenced.   
+
+             Unchanged on exit.   
+
+    TRANS  - CHARACTER*1.   
+             On entry,  TRANS  specifies the operation to be performed as   
+             follows:   
+
+                TRANS = 'N' or 'n'   C := alpha*A*A**T + beta*C.   
+
+                TRANS = 'T' or 't'   C := alpha*A**T*A + beta*C.   
+
+                TRANS = 'C' or 'c'   C := alpha*A**T*A + beta*C.   
+
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry,  N specifies the order of the matrix C.  N must be   
+             at least zero.   
+             Unchanged on exit.   
+
+    K      - INTEGER.   
+             On entry with  TRANS = 'N' or 'n',  K  specifies  the number   
+             of  columns   of  the   matrix   A,   and  on   entry   with   
+             TRANS = 'T' or 't' or 'C' or 'c',  K  specifies  the  number   
+             of rows of the matrix  A.  K must be at least zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry, ALPHA specifies the scalar alpha.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is   
+             k  when  TRANS = 'N' or 'n',  and is  n  otherwise.   
+             Before entry with  TRANS = 'N' or 'n',  the  leading  n by k   
+             part of the array  A  must contain the matrix  A,  otherwise   
+             the leading  k by n  part of the array  A  must contain  the   
+             matrix A.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in  the  calling  (sub)  program.   When  TRANS = 'N' or 'n'   
+             then  LDA must be at least  max( 1, n ), otherwise  LDA must   
+             be at least  max( 1, k ).   
+             Unchanged on exit.   
+
+    BETA   - DOUBLE PRECISION.   
+             On entry, BETA specifies the scalar beta.   
+             Unchanged on exit.   
+
+    C      - DOUBLE PRECISION array of DIMENSION ( LDC, n ).   
+             Before entry  with  UPLO = 'U' or 'u',  the leading  n by n   
+             upper triangular part of the array C must contain the upper   
+             triangular part  of the  symmetric matrix  and the strictly   
+             lower triangular part of C is not referenced.  On exit, the   
+             upper triangular part of the array  C is overwritten by the   
+             upper triangular part of the updated matrix.   
+             Before entry  with  UPLO = 'L' or 'l',  the leading  n by n   
+             lower triangular part of the array C must contain the lower   
+             triangular part  of the  symmetric matrix  and the strictly   
+             upper triangular part of C is not referenced.  On exit, the   
+             lower triangular part of the array  C is overwritten by the   
+             lower triangular part of the updated matrix.   
+
+    LDC    - INTEGER.   
+             On entry, LDC specifies the first dimension of C as declared   
+             in  the  calling  (sub)  program.   LDC  must  be  at  least   
+             max( 1, n ).   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 3 Blas routine.   
+
+    -- Written on 8-February-1989.   
+       Jack Dongarra, Argonne National Laboratory.   
+       Iain Duff, AERE Harwell.   
+       Jeremy Du Croz, Numerical Algorithms Group Ltd.   
+       Sven Hammarling, Numerical Algorithms Group Ltd.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+
+    /* Function Body */
+    if (igraphlsame_(trans, "N")) {
+	nrowa = *n;
+    } else {
+	nrowa = *k;
+    }
+    upper = igraphlsame_(uplo, "U");
+
+    info = 0;
+    if (! upper && ! igraphlsame_(uplo, "L")) {
+	info = 1;
+    } else if (! igraphlsame_(trans, "N") && ! igraphlsame_(trans, 
+	    "T") && ! igraphlsame_(trans, "C")) {
+	info = 2;
+    } else if (*n < 0) {
+	info = 3;
+    } else if (*k < 0) {
+	info = 4;
+    } else if (*lda < max(1,nrowa)) {
+	info = 7;
+    } else if (*ldc < max(1,*n)) {
+	info = 10;
+    }
+    if (info != 0) {
+	igraphxerbla_("DSYRK ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*n == 0 || (*alpha == 0. || *k == 0) && *beta == 1.) {
+	return 0;
+    }
+
+/*     And when  alpha.eq.zero. */
+
+    if (*alpha == 0.) {
+	if (upper) {
+	    if (*beta == 0.) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L10: */
+		    }
+/* L20: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L30: */
+		    }
+/* L40: */
+		}
+	    }
+	} else {
+	    if (*beta == 0.) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L50: */
+		    }
+/* L60: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L70: */
+		    }
+/* L80: */
+		}
+	    }
+	}
+	return 0;
+    }
+
+/*     Start the operations. */
+
+    if (igraphlsame_(trans, "N")) {
+
+/*        Form  C := alpha*A*A**T + beta*C. */
+
+	if (upper) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (*beta == 0.) {
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L90: */
+		    }
+		} else if (*beta != 1.) {
+		    i__2 = j;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L100: */
+		    }
+		}
+		i__2 = *k;
+		for (l = 1; l <= i__2; ++l) {
+		    if (a[j + l * a_dim1] != 0.) {
+			temp = *alpha * a[j + l * a_dim1];
+			i__3 = j;
+			for (i__ = 1; i__ <= i__3; ++i__) {
+			    c__[i__ + j * c_dim1] += temp * a[i__ + l * 
+				    a_dim1];
+/* L110: */
+			}
+		    }
+/* L120: */
+		}
+/* L130: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (*beta == 0.) {
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = 0.;
+/* L140: */
+		    }
+		} else if (*beta != 1.) {
+		    i__2 = *n;
+		    for (i__ = j; i__ <= i__2; ++i__) {
+			c__[i__ + j * c_dim1] = *beta * c__[i__ + j * c_dim1];
+/* L150: */
+		    }
+		}
+		i__2 = *k;
+		for (l = 1; l <= i__2; ++l) {
+		    if (a[j + l * a_dim1] != 0.) {
+			temp = *alpha * a[j + l * a_dim1];
+			i__3 = *n;
+			for (i__ = j; i__ <= i__3; ++i__) {
+			    c__[i__ + j * c_dim1] += temp * a[i__ + l * 
+				    a_dim1];
+/* L160: */
+			}
+		    }
+/* L170: */
+		}
+/* L180: */
+	    }
+	}
+    } else {
+
+/*        Form  C := alpha*A**T*A + beta*C. */
+
+	if (upper) {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = j;
+		for (i__ = 1; i__ <= i__2; ++i__) {
+		    temp = 0.;
+		    i__3 = *k;
+		    for (l = 1; l <= i__3; ++l) {
+			temp += a[l + i__ * a_dim1] * a[l + j * a_dim1];
+/* L190: */
+		    }
+		    if (*beta == 0.) {
+			c__[i__ + j * c_dim1] = *alpha * temp;
+		    } else {
+			c__[i__ + j * c_dim1] = *alpha * temp + *beta * c__[
+				i__ + j * c_dim1];
+		    }
+/* L200: */
+		}
+/* L210: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		i__2 = *n;
+		for (i__ = j; i__ <= i__2; ++i__) {
+		    temp = 0.;
+		    i__3 = *k;
+		    for (l = 1; l <= i__3; ++l) {
+			temp += a[l + i__ * a_dim1] * a[l + j * a_dim1];
+/* L220: */
+		    }
+		    if (*beta == 0.) {
+			c__[i__ + j * c_dim1] = *alpha * temp;
+		    } else {
+			c__[i__ + j * c_dim1] = *alpha * temp + *beta * c__[
+				i__ + j * c_dim1];
+		    }
+/* L230: */
+		}
+/* L240: */
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DSYRK . */
+
+} /* igraphdsyrk_ */
+
diff --git a/igraph/src/dsytd2.c b/igraph/src/dsytd2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsytd2.c
@@ -0,0 +1,366 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static doublereal c_b8 = 0.;
+static doublereal c_b14 = -1.;
+
+/* > \brief \b DSYTD2 reduces a symmetric matrix to real symmetric tridiagonal form by an orthogonal similarit
+y transformation (unblocked algorithm).   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DSYTD2 + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dsytd2.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dsytd2.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dsytd2.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DSYTD2( UPLO, N, A, LDA, D, E, TAU, INFO )   
+
+         CHARACTER          UPLO   
+         INTEGER            INFO, LDA, N   
+         DOUBLE PRECISION   A( LDA, * ), D( * ), E( * ), TAU( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DSYTD2 reduces a real symmetric matrix A to symmetric tridiagonal   
+   > form T by an orthogonal similarity transformation: Q**T * A * Q = T.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          Specifies whether the upper or lower triangular part of the   
+   >          symmetric matrix A is stored:   
+   >          = 'U':  Upper triangular   
+   >          = 'L':  Lower triangular   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the symmetric matrix A.  If UPLO = 'U', the leading   
+   >          n-by-n upper triangular part of A contains the upper   
+   >          triangular part of the matrix A, and the strictly lower   
+   >          triangular part of A is not referenced.  If UPLO = 'L', the   
+   >          leading n-by-n lower triangular part of A contains the lower   
+   >          triangular part of the matrix A, and the strictly upper   
+   >          triangular part of A is not referenced.   
+   >          On exit, if UPLO = 'U', the diagonal and first superdiagonal   
+   >          of A are overwritten by the corresponding elements of the   
+   >          tridiagonal matrix T, and the elements above the first   
+   >          superdiagonal, with the array TAU, represent the orthogonal   
+   >          matrix Q as a product of elementary reflectors; if UPLO   
+   >          = 'L', the diagonal and first subdiagonal of A are over-   
+   >          written by the corresponding elements of the tridiagonal   
+   >          matrix T, and the elements below the first subdiagonal, with   
+   >          the array TAU, represent the orthogonal matrix Q as a product   
+   >          of elementary reflectors. See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The diagonal elements of the tridiagonal matrix T:   
+   >          D(i) = A(i,i).   
+   > \endverbatim   
+   >   
+   > \param[out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          The off-diagonal elements of the tridiagonal matrix T:   
+   >          E(i) = A(i,i+1) if UPLO = 'U', E(i) = A(i+1,i) if UPLO = 'L'.   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (N-1)   
+   >          The scalar factors of the elementary reflectors (see Further   
+   >          Details).   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup doubleSYcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  If UPLO = 'U', the matrix Q is represented as a product of elementary   
+   >  reflectors   
+   >   
+   >     Q = H(n-1) . . . H(2) H(1).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(i+1:n) = 0 and v(i) = 1; v(1:i-1) is stored on exit in   
+   >  A(1:i-1,i+1), and tau in TAU(i).   
+   >   
+   >  If UPLO = 'L', the matrix Q is represented as a product of elementary   
+   >  reflectors   
+   >   
+   >     Q = H(1) H(2) . . . H(n-1).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(1:i) = 0 and v(i+1) = 1; v(i+2:n) is stored on exit in A(i+2:n,i),   
+   >  and tau in TAU(i).   
+   >   
+   >  The contents of A on exit are illustrated by the following examples   
+   >  with n = 5:   
+   >   
+   >  if UPLO = 'U':                       if UPLO = 'L':   
+   >   
+   >    (  d   e   v2  v3  v4 )              (  d                  )   
+   >    (      d   e   v3  v4 )              (  e   d              )   
+   >    (          d   e   v4 )              (  v1  e   d          )   
+   >    (              d   e  )              (  v1  v2  e   d      )   
+   >    (                  d  )              (  v1  v2  v3  e   d  )   
+   >   
+   >  where d and e denote diagonal and off-diagonal elements of T, and vi   
+   >  denotes an element of the vector defining H(i).   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdsytd2_(char *uplo, integer *n, doublereal *a, integer *
+	lda, doublereal *d__, doublereal *e, doublereal *tau, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    doublereal taui;
+    extern /* Subroutine */ int igraphdsyr2_(char *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    doublereal alpha;
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdaxpy_(integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    logical upper;
+    extern /* Subroutine */ int igraphdsymv_(char *, integer *, doublereal *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, integer *), igraphdlarfg_(integer *, doublereal *,
+	     doublereal *, integer *, doublereal *), igraphxerbla_(char *, integer *
+	    , ftnlen);
+
+
+/*  -- LAPACK computational routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Test the input parameters   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --d__;
+    --e;
+    --tau;
+
+    /* Function Body */
+    *info = 0;
+    upper = igraphlsame_(uplo, "U");
+    if (! upper && ! igraphlsame_(uplo, "L")) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*n)) {
+	*info = -4;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DSYTD2", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n <= 0) {
+	return 0;
+    }
+
+    if (upper) {
+
+/*        Reduce the upper triangle of A */
+
+	for (i__ = *n - 1; i__ >= 1; --i__) {
+
+/*           Generate elementary reflector H(i) = I - tau * v * v**T   
+             to annihilate A(1:i-1,i+1) */
+
+	    igraphdlarfg_(&i__, &a[i__ + (i__ + 1) * a_dim1], &a[(i__ + 1) * a_dim1 
+		    + 1], &c__1, &taui);
+	    e[i__] = a[i__ + (i__ + 1) * a_dim1];
+
+	    if (taui != 0.) {
+
+/*              Apply H(i) from both sides to A(1:i,1:i) */
+
+		a[i__ + (i__ + 1) * a_dim1] = 1.;
+
+/*              Compute  x := tau * A * v  storing x in TAU(1:i) */
+
+		igraphdsymv_(uplo, &i__, &taui, &a[a_offset], lda, &a[(i__ + 1) * 
+			a_dim1 + 1], &c__1, &c_b8, &tau[1], &c__1);
+
+/*              Compute  w := x - 1/2 * tau * (x**T * v) * v */
+
+		alpha = taui * -.5 * igraphddot_(&i__, &tau[1], &c__1, &a[(i__ + 1) 
+			* a_dim1 + 1], &c__1);
+		igraphdaxpy_(&i__, &alpha, &a[(i__ + 1) * a_dim1 + 1], &c__1, &tau[
+			1], &c__1);
+
+/*              Apply the transformation as a rank-2 update:   
+                   A := A - v * w**T - w * v**T */
+
+		igraphdsyr2_(uplo, &i__, &c_b14, &a[(i__ + 1) * a_dim1 + 1], &c__1, 
+			&tau[1], &c__1, &a[a_offset], lda);
+
+		a[i__ + (i__ + 1) * a_dim1] = e[i__];
+	    }
+	    d__[i__ + 1] = a[i__ + 1 + (i__ + 1) * a_dim1];
+	    tau[i__] = taui;
+/* L10: */
+	}
+	d__[1] = a[a_dim1 + 1];
+    } else {
+
+/*        Reduce the lower triangle of A */
+
+	i__1 = *n - 1;
+	for (i__ = 1; i__ <= i__1; ++i__) {
+
+/*           Generate elementary reflector H(i) = I - tau * v * v**T   
+             to annihilate A(i+2:n,i) */
+
+	    i__2 = *n - i__;
+/* Computing MIN */
+	    i__3 = i__ + 2;
+	    igraphdlarfg_(&i__2, &a[i__ + 1 + i__ * a_dim1], &a[min(i__3,*n) + i__ *
+		     a_dim1], &c__1, &taui);
+	    e[i__] = a[i__ + 1 + i__ * a_dim1];
+
+	    if (taui != 0.) {
+
+/*              Apply H(i) from both sides to A(i+1:n,i+1:n) */
+
+		a[i__ + 1 + i__ * a_dim1] = 1.;
+
+/*              Compute  x := tau * A * v  storing y in TAU(i:n-1) */
+
+		i__2 = *n - i__;
+		igraphdsymv_(uplo, &i__2, &taui, &a[i__ + 1 + (i__ + 1) * a_dim1], 
+			lda, &a[i__ + 1 + i__ * a_dim1], &c__1, &c_b8, &tau[
+			i__], &c__1);
+
+/*              Compute  w := x - 1/2 * tau * (x**T * v) * v */
+
+		i__2 = *n - i__;
+		alpha = taui * -.5 * igraphddot_(&i__2, &tau[i__], &c__1, &a[i__ + 
+			1 + i__ * a_dim1], &c__1);
+		i__2 = *n - i__;
+		igraphdaxpy_(&i__2, &alpha, &a[i__ + 1 + i__ * a_dim1], &c__1, &tau[
+			i__], &c__1);
+
+/*              Apply the transformation as a rank-2 update:   
+                   A := A - v * w**T - w * v**T */
+
+		i__2 = *n - i__;
+		igraphdsyr2_(uplo, &i__2, &c_b14, &a[i__ + 1 + i__ * a_dim1], &c__1,
+			 &tau[i__], &c__1, &a[i__ + 1 + (i__ + 1) * a_dim1], 
+			lda);
+
+		a[i__ + 1 + i__ * a_dim1] = e[i__];
+	    }
+	    d__[i__] = a[i__ + i__ * a_dim1];
+	    tau[i__] = taui;
+/* L20: */
+	}
+	d__[*n] = a[*n + *n * a_dim1];
+    }
+
+    return 0;
+
+/*     End of DSYTD2 */
+
+} /* igraphdsytd2_ */
+
diff --git a/igraph/src/dsytrd.c b/igraph/src/dsytrd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dsytrd.c
@@ -0,0 +1,428 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c_n1 = -1;
+static integer c__3 = 3;
+static integer c__2 = 2;
+static doublereal c_b22 = -1.;
+static doublereal c_b23 = 1.;
+
+/* > \brief \b DSYTRD   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DSYTRD + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dsytrd.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dsytrd.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dsytrd.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DSYTRD( UPLO, N, A, LDA, D, E, TAU, WORK, LWORK, INFO )   
+
+         CHARACTER          UPLO   
+         INTEGER            INFO, LDA, LWORK, N   
+         DOUBLE PRECISION   A( LDA, * ), D( * ), E( * ), TAU( * ),   
+        $                   WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DSYTRD reduces a real symmetric matrix A to real symmetric   
+   > tridiagonal form T by an orthogonal similarity transformation:   
+   > Q**T * A * Q = T.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] UPLO   
+   > \verbatim   
+   >          UPLO is CHARACTER*1   
+   >          = 'U':  Upper triangle of A is stored;   
+   >          = 'L':  Lower triangle of A is stored.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix A.  N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          On entry, the symmetric matrix A.  If UPLO = 'U', the leading   
+   >          N-by-N upper triangular part of A contains the upper   
+   >          triangular part of the matrix A, and the strictly lower   
+   >          triangular part of A is not referenced.  If UPLO = 'L', the   
+   >          leading N-by-N lower triangular part of A contains the lower   
+   >          triangular part of the matrix A, and the strictly upper   
+   >          triangular part of A is not referenced.   
+   >          On exit, if UPLO = 'U', the diagonal and first superdiagonal   
+   >          of A are overwritten by the corresponding elements of the   
+   >          tridiagonal matrix T, and the elements above the first   
+   >          superdiagonal, with the array TAU, represent the orthogonal   
+   >          matrix Q as a product of elementary reflectors; if UPLO   
+   >          = 'L', the diagonal and first subdiagonal of A are over-   
+   >          written by the corresponding elements of the tridiagonal   
+   >          matrix T, and the elements below the first subdiagonal, with   
+   >          the array TAU, represent the orthogonal matrix Q as a product   
+   >          of elementary reflectors. See Further Details.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A.  LDA >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[out] D   
+   > \verbatim   
+   >          D is DOUBLE PRECISION array, dimension (N)   
+   >          The diagonal elements of the tridiagonal matrix T:   
+   >          D(i) = A(i,i).   
+   > \endverbatim   
+   >   
+   > \param[out] E   
+   > \verbatim   
+   >          E is DOUBLE PRECISION array, dimension (N-1)   
+   >          The off-diagonal elements of the tridiagonal matrix T:   
+   >          E(i) = A(i,i+1) if UPLO = 'U', E(i) = A(i+1,i) if UPLO = 'L'.   
+   > \endverbatim   
+   >   
+   > \param[out] TAU   
+   > \verbatim   
+   >          TAU is DOUBLE PRECISION array, dimension (N-1)   
+   >          The scalar factors of the elementary reflectors (see Further   
+   >          Details).   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.  LWORK >= 1.   
+   >          For optimum performance LWORK >= N*NB, where NB is the   
+   >          optimal blocksize.   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleSYcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  If UPLO = 'U', the matrix Q is represented as a product of elementary   
+   >  reflectors   
+   >   
+   >     Q = H(n-1) . . . H(2) H(1).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(i+1:n) = 0 and v(i) = 1; v(1:i-1) is stored on exit in   
+   >  A(1:i-1,i+1), and tau in TAU(i).   
+   >   
+   >  If UPLO = 'L', the matrix Q is represented as a product of elementary   
+   >  reflectors   
+   >   
+   >     Q = H(1) H(2) . . . H(n-1).   
+   >   
+   >  Each H(i) has the form   
+   >   
+   >     H(i) = I - tau * v * v**T   
+   >   
+   >  where tau is a real scalar, and v is a real vector with   
+   >  v(1:i) = 0 and v(i+1) = 1; v(i+2:n) is stored on exit in A(i+2:n,i),   
+   >  and tau in TAU(i).   
+   >   
+   >  The contents of A on exit are illustrated by the following examples   
+   >  with n = 5:   
+   >   
+   >  if UPLO = 'U':                       if UPLO = 'L':   
+   >   
+   >    (  d   e   v2  v3  v4 )              (  d                  )   
+   >    (      d   e   v3  v4 )              (  e   d              )   
+   >    (          d   e   v4 )              (  v1  e   d          )   
+   >    (              d   e  )              (  v1  v2  e   d      )   
+   >    (                  d  )              (  v1  v2  v3  e   d  )   
+   >   
+   >  where d and e denote diagonal and off-diagonal elements of T, and vi   
+   >  denotes an element of the vector defining H(i).   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdsytrd_(char *uplo, integer *n, doublereal *a, integer *
+	lda, doublereal *d__, doublereal *e, doublereal *tau, doublereal *
+	work, integer *lwork, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__, j, nb, kk, nx, iws;
+    extern logical igraphlsame_(char *, char *);
+    integer nbmin, iinfo;
+    logical upper;
+    extern /* Subroutine */ int igraphdsytd2_(char *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, doublereal *, integer *), igraphdsyr2k_(char *, char *, integer *, integer *, doublereal 
+	    *, doublereal *, integer *, doublereal *, integer *, doublereal *,
+	     doublereal *, integer *), igraphdlatrd_(char *, 
+	    integer *, integer *, doublereal *, integer *, doublereal *, 
+	    doublereal *, doublereal *, integer *), igraphxerbla_(char *, 
+	    integer *, ftnlen);
+    extern integer igraphilaenv_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *, ftnlen, ftnlen);
+    integer ldwork, lwkopt;
+    logical lquery;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Test the input parameters   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --d__;
+    --e;
+    --tau;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    upper = igraphlsame_(uplo, "U");
+    lquery = *lwork == -1;
+    if (! upper && ! igraphlsame_(uplo, "L")) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*lda < max(1,*n)) {
+	*info = -4;
+    } else if (*lwork < 1 && ! lquery) {
+	*info = -9;
+    }
+
+    if (*info == 0) {
+
+/*        Determine the block size. */
+
+	nb = igraphilaenv_(&c__1, "DSYTRD", uplo, n, &c_n1, &c_n1, &c_n1, (ftnlen)6,
+		 (ftnlen)1);
+	lwkopt = *n * nb;
+	work[1] = (doublereal) lwkopt;
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DSYTRD", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	work[1] = 1.;
+	return 0;
+    }
+
+    nx = *n;
+    iws = 1;
+    if (nb > 1 && nb < *n) {
+
+/*        Determine when to cross over from blocked to unblocked code   
+          (last block is always handled by unblocked code).   
+
+   Computing MAX */
+	i__1 = nb, i__2 = igraphilaenv_(&c__3, "DSYTRD", uplo, n, &c_n1, &c_n1, &
+		c_n1, (ftnlen)6, (ftnlen)1);
+	nx = max(i__1,i__2);
+	if (nx < *n) {
+
+/*           Determine if workspace is large enough for blocked code. */
+
+	    ldwork = *n;
+	    iws = ldwork * nb;
+	    if (*lwork < iws) {
+
+/*              Not enough workspace to use optimal NB:  determine the   
+                minimum value of NB, and reduce NB or force use of   
+                unblocked code by setting NX = N.   
+
+   Computing MAX */
+		i__1 = *lwork / ldwork;
+		nb = max(i__1,1);
+		nbmin = igraphilaenv_(&c__2, "DSYTRD", uplo, n, &c_n1, &c_n1, &c_n1,
+			 (ftnlen)6, (ftnlen)1);
+		if (nb < nbmin) {
+		    nx = *n;
+		}
+	    }
+	} else {
+	    nx = *n;
+	}
+    } else {
+	nb = 1;
+    }
+
+    if (upper) {
+
+/*        Reduce the upper triangle of A.   
+          Columns 1:kk are handled by the unblocked method. */
+
+	kk = *n - (*n - nx + nb - 1) / nb * nb;
+	i__1 = kk + 1;
+	i__2 = -nb;
+	for (i__ = *n - nb + 1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += 
+		i__2) {
+
+/*           Reduce columns i:i+nb-1 to tridiagonal form and form the   
+             matrix W which is needed to update the unreduced part of   
+             the matrix */
+
+	    i__3 = i__ + nb - 1;
+	    igraphdlatrd_(uplo, &i__3, &nb, &a[a_offset], lda, &e[1], &tau[1], &
+		    work[1], &ldwork);
+
+/*           Update the unreduced submatrix A(1:i-1,1:i-1), using an   
+             update of the form:  A := A - V*W**T - W*V**T */
+
+	    i__3 = i__ - 1;
+	    igraphdsyr2k_(uplo, "No transpose", &i__3, &nb, &c_b22, &a[i__ * a_dim1 
+		    + 1], lda, &work[1], &ldwork, &c_b23, &a[a_offset], lda);
+
+/*           Copy superdiagonal elements back into A, and diagonal   
+             elements into D */
+
+	    i__3 = i__ + nb - 1;
+	    for (j = i__; j <= i__3; ++j) {
+		a[j - 1 + j * a_dim1] = e[j - 1];
+		d__[j] = a[j + j * a_dim1];
+/* L10: */
+	    }
+/* L20: */
+	}
+
+/*        Use unblocked code to reduce the last or only block */
+
+	igraphdsytd2_(uplo, &kk, &a[a_offset], lda, &d__[1], &e[1], &tau[1], &iinfo);
+    } else {
+
+/*        Reduce the lower triangle of A */
+
+	i__2 = *n - nx;
+	i__1 = nb;
+	for (i__ = 1; i__1 < 0 ? i__ >= i__2 : i__ <= i__2; i__ += i__1) {
+
+/*           Reduce columns i:i+nb-1 to tridiagonal form and form the   
+             matrix W which is needed to update the unreduced part of   
+             the matrix */
+
+	    i__3 = *n - i__ + 1;
+	    igraphdlatrd_(uplo, &i__3, &nb, &a[i__ + i__ * a_dim1], lda, &e[i__], &
+		    tau[i__], &work[1], &ldwork);
+
+/*           Update the unreduced submatrix A(i+ib:n,i+ib:n), using   
+             an update of the form:  A := A - V*W**T - W*V**T */
+
+	    i__3 = *n - i__ - nb + 1;
+	    igraphdsyr2k_(uplo, "No transpose", &i__3, &nb, &c_b22, &a[i__ + nb + 
+		    i__ * a_dim1], lda, &work[nb + 1], &ldwork, &c_b23, &a[
+		    i__ + nb + (i__ + nb) * a_dim1], lda);
+
+/*           Copy subdiagonal elements back into A, and diagonal   
+             elements into D */
+
+	    i__3 = i__ + nb - 1;
+	    for (j = i__; j <= i__3; ++j) {
+		a[j + 1 + j * a_dim1] = e[j];
+		d__[j] = a[j + j * a_dim1];
+/* L30: */
+	    }
+/* L40: */
+	}
+
+/*        Use unblocked code to reduce the last or only block */
+
+	i__1 = *n - i__ + 1;
+	igraphdsytd2_(uplo, &i__1, &a[i__ + i__ * a_dim1], lda, &d__[i__], &e[i__], 
+		&tau[i__], &iinfo);
+    }
+
+    work[1] = (doublereal) lwkopt;
+    return 0;
+
+/*     End of DSYTRD */
+
+} /* igraphdsytrd_ */
+
diff --git a/igraph/src/dtime_.c b/igraph/src/dtime_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtime_.c
@@ -0,0 +1,63 @@
+#include "time.h"
+
+#ifdef MSDOS
+#undef USE_CLOCK
+#define USE_CLOCK
+#endif
+
+#ifndef REAL
+#define REAL double
+#endif
+
+#ifndef USE_CLOCK
+#define _INCLUDE_POSIX_SOURCE	/* for HP-UX */
+#define _INCLUDE_XOPEN_SOURCE	/* for HP-UX */
+#include "sys/types.h"
+#include "sys/times.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#endif
+
+#undef Hz
+#ifdef CLK_TCK
+#define Hz CLK_TCK
+#else
+#ifdef HZ
+#define Hz HZ
+#else
+#define Hz 60
+#endif
+#endif
+
+ REAL
+#ifdef KR_headers
+dtime_(tarray) float *tarray;
+#else
+dtime_(float *tarray)
+#endif
+{
+#ifdef USE_CLOCK
+#ifndef CLOCKS_PER_SECOND
+#define CLOCKS_PER_SECOND Hz
+#endif
+	static double t0;
+	double t = clock();
+	tarray[1] = 0;
+	tarray[0] = (t - t0) / CLOCKS_PER_SECOND;
+	t0 = t;
+	return tarray[0];
+#else
+	struct tms t;
+	static struct tms t0;
+
+	times(&t);
+	tarray[0] = (double)(t.tms_utime - t0.tms_utime) / Hz;
+	tarray[1] = (double)(t.tms_stime - t0.tms_stime) / Hz;
+	t0 = t;
+	return tarray[0] + tarray[1];
+#endif
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/dtrevc.c b/igraph/src/dtrevc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrevc.c
@@ -0,0 +1,1306 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static logical c_false = FALSE_;
+static integer c__1 = 1;
+static doublereal c_b22 = 1.;
+static doublereal c_b25 = 0.;
+static integer c__2 = 2;
+static logical c_true = TRUE_;
+
+/* > \brief \b DTREVC   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DTREVC + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dtrevc.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dtrevc.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dtrevc.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR,   
+                            LDVR, MM, M, WORK, INFO )   
+
+         CHARACTER          HOWMNY, SIDE   
+         INTEGER            INFO, LDT, LDVL, LDVR, M, MM, N   
+         LOGICAL            SELECT( * )   
+         DOUBLE PRECISION   T( LDT, * ), VL( LDVL, * ), VR( LDVR, * ),   
+        $                   WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DTREVC computes some or all of the right and/or left eigenvectors of   
+   > a real upper quasi-triangular matrix T.   
+   > Matrices of this type are produced by the Schur factorization of   
+   > a real general matrix:  A = Q*T*Q**T, as computed by DHSEQR.   
+   >   
+   > The right eigenvector x and the left eigenvector y of T corresponding   
+   > to an eigenvalue w are defined by:   
+   >   
+   >    T*x = w*x,     (y**T)*T = w*(y**T)   
+   >   
+   > where y**T denotes the transpose of y.   
+   > The eigenvalues are not input to this routine, but are read directly   
+   > from the diagonal blocks of T.   
+   >   
+   > This routine returns the matrices X and/or Y of right and left   
+   > eigenvectors of T, or the products Q*X and/or Q*Y, where Q is an   
+   > input matrix.  If Q is the orthogonal factor that reduces a matrix   
+   > A to Schur form T, then Q*X and Q*Y are the matrices of right and   
+   > left eigenvectors of A.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SIDE   
+   > \verbatim   
+   >          SIDE is CHARACTER*1   
+   >          = 'R':  compute right eigenvectors only;   
+   >          = 'L':  compute left eigenvectors only;   
+   >          = 'B':  compute both right and left eigenvectors.   
+   > \endverbatim   
+   >   
+   > \param[in] HOWMNY   
+   > \verbatim   
+   >          HOWMNY is CHARACTER*1   
+   >          = 'A':  compute all right and/or left eigenvectors;   
+   >          = 'B':  compute all right and/or left eigenvectors,   
+   >                  backtransformed by the matrices in VR and/or VL;   
+   >          = 'S':  compute selected right and/or left eigenvectors,   
+   >                  as indicated by the logical array SELECT.   
+   > \endverbatim   
+   >   
+   > \param[in,out] SELECT   
+   > \verbatim   
+   >          SELECT is LOGICAL array, dimension (N)   
+   >          If HOWMNY = 'S', SELECT specifies the eigenvectors to be   
+   >          computed.   
+   >          If w(j) is a real eigenvalue, the corresponding real   
+   >          eigenvector is computed if SELECT(j) is .TRUE..   
+   >          If w(j) and w(j+1) are the real and imaginary parts of a   
+   >          complex eigenvalue, the corresponding complex eigenvector is   
+   >          computed if either SELECT(j) or SELECT(j+1) is .TRUE., and   
+   >          on exit SELECT(j) is set to .TRUE. and SELECT(j+1) is set to   
+   >          .FALSE..   
+   >          Not referenced if HOWMNY = 'A' or 'B'.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix T. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,N)   
+   >          The upper quasi-triangular matrix T in Schur canonical form.   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is INTEGER   
+   >          The leading dimension of the array T. LDT >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in,out] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION array, dimension (LDVL,MM)   
+   >          On entry, if SIDE = 'L' or 'B' and HOWMNY = 'B', VL must   
+   >          contain an N-by-N matrix Q (usually the orthogonal matrix Q   
+   >          of Schur vectors returned by DHSEQR).   
+   >          On exit, if SIDE = 'L' or 'B', VL contains:   
+   >          if HOWMNY = 'A', the matrix Y of left eigenvectors of T;   
+   >          if HOWMNY = 'B', the matrix Q*Y;   
+   >          if HOWMNY = 'S', the left eigenvectors of T specified by   
+   >                           SELECT, stored consecutively in the columns   
+   >                           of VL, in the same order as their   
+   >                           eigenvalues.   
+   >          A complex eigenvector corresponding to a complex eigenvalue   
+   >          is stored in two consecutive columns, the first holding the   
+   >          real part, and the second the imaginary part.   
+   >          Not referenced if SIDE = 'R'.   
+   > \endverbatim   
+   >   
+   > \param[in] LDVL   
+   > \verbatim   
+   >          LDVL is INTEGER   
+   >          The leading dimension of the array VL.  LDVL >= 1, and if   
+   >          SIDE = 'L' or 'B', LDVL >= N.   
+   > \endverbatim   
+   >   
+   > \param[in,out] VR   
+   > \verbatim   
+   >          VR is DOUBLE PRECISION array, dimension (LDVR,MM)   
+   >          On entry, if SIDE = 'R' or 'B' and HOWMNY = 'B', VR must   
+   >          contain an N-by-N matrix Q (usually the orthogonal matrix Q   
+   >          of Schur vectors returned by DHSEQR).   
+   >          On exit, if SIDE = 'R' or 'B', VR contains:   
+   >          if HOWMNY = 'A', the matrix X of right eigenvectors of T;   
+   >          if HOWMNY = 'B', the matrix Q*X;   
+   >          if HOWMNY = 'S', the right eigenvectors of T specified by   
+   >                           SELECT, stored consecutively in the columns   
+   >                           of VR, in the same order as their   
+   >                           eigenvalues.   
+   >          A complex eigenvector corresponding to a complex eigenvalue   
+   >          is stored in two consecutive columns, the first holding the   
+   >          real part and the second the imaginary part.   
+   >          Not referenced if SIDE = 'L'.   
+   > \endverbatim   
+   >   
+   > \param[in] LDVR   
+   > \verbatim   
+   >          LDVR is INTEGER   
+   >          The leading dimension of the array VR.  LDVR >= 1, and if   
+   >          SIDE = 'R' or 'B', LDVR >= N.   
+   > \endverbatim   
+   >   
+   > \param[in] MM   
+   > \verbatim   
+   >          MM is INTEGER   
+   >          The number of columns in the arrays VL and/or VR. MM >= M.   
+   > \endverbatim   
+   >   
+   > \param[out] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of columns in the arrays VL and/or VR actually   
+   >          used to store the eigenvectors.   
+   >          If HOWMNY = 'A' or 'B', M is set to N.   
+   >          Each selected real eigenvector occupies one column and each   
+   >          selected complex eigenvector occupies two columns.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (3*N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The algorithm used in this program is basically backward (forward)   
+   >  substitution, with scaling to make the the code robust against   
+   >  possible overflow.   
+   >   
+   >  Each eigenvector is normalized so that the element of largest   
+   >  magnitude has magnitude 1; here the magnitude of a complex number   
+   >  (x,y) is taken to be |x| + |y|.   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdtrevc_(char *side, char *howmny, logical *select, 
+	integer *n, doublereal *t, integer *ldt, doublereal *vl, integer *
+	ldvl, doublereal *vr, integer *ldvr, integer *mm, integer *m, 
+	doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer t_dim1, t_offset, vl_dim1, vl_offset, vr_dim1, vr_offset, i__1, 
+	    i__2, i__3;
+    doublereal d__1, d__2, d__3, d__4;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j, k;
+    doublereal x[4]	/* was [2][2] */;
+    integer j1, j2, n2, ii, ki, ip, is;
+    doublereal wi, wr, rec, ulp, beta, emax;
+    logical pair;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    logical allv;
+    integer ierr;
+    doublereal unfl, ovfl, smin;
+    logical over;
+    doublereal vmax;
+    integer jnxt;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    doublereal scale;
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphdgemv_(char *, integer *, integer *, 
+	    doublereal *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, doublereal *, integer *);
+    doublereal remax;
+    extern /* Subroutine */ int igraphdcopy_(integer *, doublereal *, integer *, 
+	    doublereal *, integer *);
+    logical leftv, bothv;
+    extern /* Subroutine */ int igraphdaxpy_(integer *, doublereal *, doublereal *, 
+	    integer *, doublereal *, integer *);
+    doublereal vcrit;
+    logical somev;
+    doublereal xnorm;
+    extern /* Subroutine */ int igraphdlaln2_(logical *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *, doublereal *,
+	     doublereal *, doublereal *, integer *, doublereal *, doublereal *
+	    , doublereal *, integer *, doublereal *, doublereal *, integer *),
+	     igraphdlabad_(doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern integer igraphidamax_(integer *, doublereal *, integer *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    doublereal bignum;
+    logical rightv;
+    doublereal smlnum;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Decode and test the input parameters   
+
+       Parameter adjustments */
+    --select;
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    vl_dim1 = *ldvl;
+    vl_offset = 1 + vl_dim1;
+    vl -= vl_offset;
+    vr_dim1 = *ldvr;
+    vr_offset = 1 + vr_dim1;
+    vr -= vr_offset;
+    --work;
+
+    /* Function Body */
+    bothv = igraphlsame_(side, "B");
+    rightv = igraphlsame_(side, "R") || bothv;
+    leftv = igraphlsame_(side, "L") || bothv;
+
+    allv = igraphlsame_(howmny, "A");
+    over = igraphlsame_(howmny, "B");
+    somev = igraphlsame_(howmny, "S");
+
+    *info = 0;
+    if (! rightv && ! leftv) {
+	*info = -1;
+    } else if (! allv && ! over && ! somev) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*ldt < max(1,*n)) {
+	*info = -6;
+    } else if (*ldvl < 1 || leftv && *ldvl < *n) {
+	*info = -8;
+    } else if (*ldvr < 1 || rightv && *ldvr < *n) {
+	*info = -10;
+    } else {
+
+/*        Set M to the number of columns required to store the selected   
+          eigenvectors, standardize the array SELECT if necessary, and   
+          test MM. */
+
+	if (somev) {
+	    *m = 0;
+	    pair = FALSE_;
+	    i__1 = *n;
+	    for (j = 1; j <= i__1; ++j) {
+		if (pair) {
+		    pair = FALSE_;
+		    select[j] = FALSE_;
+		} else {
+		    if (j < *n) {
+			if (t[j + 1 + j * t_dim1] == 0.) {
+			    if (select[j]) {
+				++(*m);
+			    }
+			} else {
+			    pair = TRUE_;
+			    if (select[j] || select[j + 1]) {
+				select[j] = TRUE_;
+				*m += 2;
+			    }
+			}
+		    } else {
+			if (select[*n]) {
+			    ++(*m);
+			}
+		    }
+		}
+/* L10: */
+	    }
+	} else {
+	    *m = *n;
+	}
+
+	if (*mm < *m) {
+	    *info = -11;
+	}
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DTREVC", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+/*     Set the constants to control overflow. */
+
+    unfl = igraphdlamch_("Safe minimum");
+    ovfl = 1. / unfl;
+    igraphdlabad_(&unfl, &ovfl);
+    ulp = igraphdlamch_("Precision");
+    smlnum = unfl * (*n / ulp);
+    bignum = (1. - ulp) / smlnum;
+
+/*     Compute 1-norm of each column of strictly upper triangular   
+       part of T to control overflow in triangular solver. */
+
+    work[1] = 0.;
+    i__1 = *n;
+    for (j = 2; j <= i__1; ++j) {
+	work[j] = 0.;
+	i__2 = j - 1;
+	for (i__ = 1; i__ <= i__2; ++i__) {
+	    work[j] += (d__1 = t[i__ + j * t_dim1], abs(d__1));
+/* L20: */
+	}
+/* L30: */
+    }
+
+/*     Index IP is used to specify the real or complex eigenvalue:   
+         IP = 0, real eigenvalue,   
+              1, first of conjugate complex pair: (wr,wi)   
+             -1, second of conjugate complex pair: (wr,wi) */
+
+    n2 = *n << 1;
+
+    if (rightv) {
+
+/*        Compute right eigenvectors. */
+
+	ip = 0;
+	is = *m;
+	for (ki = *n; ki >= 1; --ki) {
+
+	    if (ip == 1) {
+		goto L130;
+	    }
+	    if (ki == 1) {
+		goto L40;
+	    }
+	    if (t[ki + (ki - 1) * t_dim1] == 0.) {
+		goto L40;
+	    }
+	    ip = -1;
+
+L40:
+	    if (somev) {
+		if (ip == 0) {
+		    if (! select[ki]) {
+			goto L130;
+		    }
+		} else {
+		    if (! select[ki - 1]) {
+			goto L130;
+		    }
+		}
+	    }
+
+/*           Compute the KI-th eigenvalue (WR,WI). */
+
+	    wr = t[ki + ki * t_dim1];
+	    wi = 0.;
+	    if (ip != 0) {
+		wi = sqrt((d__1 = t[ki + (ki - 1) * t_dim1], abs(d__1))) * 
+			sqrt((d__2 = t[ki - 1 + ki * t_dim1], abs(d__2)));
+	    }
+/* Computing MAX */
+	    d__1 = ulp * (abs(wr) + abs(wi));
+	    smin = max(d__1,smlnum);
+
+	    if (ip == 0) {
+
+/*              Real right eigenvector */
+
+		work[ki + *n] = 1.;
+
+/*              Form right-hand side */
+
+		i__1 = ki - 1;
+		for (k = 1; k <= i__1; ++k) {
+		    work[k + *n] = -t[k + ki * t_dim1];
+/* L50: */
+		}
+
+/*              Solve the upper quasi-triangular system:   
+                   (T(1:KI-1,1:KI-1) - WR)*X = SCALE*WORK. */
+
+		jnxt = ki - 1;
+		for (j = ki - 1; j >= 1; --j) {
+		    if (j > jnxt) {
+			goto L60;
+		    }
+		    j1 = j;
+		    j2 = j;
+		    jnxt = j - 1;
+		    if (j > 1) {
+			if (t[j + (j - 1) * t_dim1] != 0.) {
+			    j1 = j - 1;
+			    jnxt = j - 2;
+			}
+		    }
+
+		    if (j1 == j2) {
+
+/*                    1-by-1 diagonal block */
+
+			igraphdlaln2_(&c_false, &c__1, &c__1, &smin, &c_b22, &t[j + 
+				j * t_dim1], ldt, &c_b22, &c_b22, &work[j + *
+				n], n, &wr, &c_b25, x, &c__2, &scale, &xnorm, 
+				&ierr);
+
+/*                    Scale X(1,1) to avoid overflow when updating   
+                      the right-hand side. */
+
+			if (xnorm > 1.) {
+			    if (work[j] > bignum / xnorm) {
+				x[0] /= xnorm;
+				scale /= xnorm;
+			    }
+			}
+
+/*                    Scale if necessary */
+
+			if (scale != 1.) {
+			    igraphdscal_(&ki, &scale, &work[*n + 1], &c__1);
+			}
+			work[j + *n] = x[0];
+
+/*                    Update right-hand side */
+
+			i__1 = j - 1;
+			d__1 = -x[0];
+			igraphdaxpy_(&i__1, &d__1, &t[j * t_dim1 + 1], &c__1, &work[
+				*n + 1], &c__1);
+
+		    } else {
+
+/*                    2-by-2 diagonal block */
+
+			igraphdlaln2_(&c_false, &c__2, &c__1, &smin, &c_b22, &t[j - 
+				1 + (j - 1) * t_dim1], ldt, &c_b22, &c_b22, &
+				work[j - 1 + *n], n, &wr, &c_b25, x, &c__2, &
+				scale, &xnorm, &ierr);
+
+/*                    Scale X(1,1) and X(2,1) to avoid overflow when   
+                      updating the right-hand side. */
+
+			if (xnorm > 1.) {
+/* Computing MAX */
+			    d__1 = work[j - 1], d__2 = work[j];
+			    beta = max(d__1,d__2);
+			    if (beta > bignum / xnorm) {
+				x[0] /= xnorm;
+				x[1] /= xnorm;
+				scale /= xnorm;
+			    }
+			}
+
+/*                    Scale if necessary */
+
+			if (scale != 1.) {
+			    igraphdscal_(&ki, &scale, &work[*n + 1], &c__1);
+			}
+			work[j - 1 + *n] = x[0];
+			work[j + *n] = x[1];
+
+/*                    Update right-hand side */
+
+			i__1 = j - 2;
+			d__1 = -x[0];
+			igraphdaxpy_(&i__1, &d__1, &t[(j - 1) * t_dim1 + 1], &c__1, 
+				&work[*n + 1], &c__1);
+			i__1 = j - 2;
+			d__1 = -x[1];
+			igraphdaxpy_(&i__1, &d__1, &t[j * t_dim1 + 1], &c__1, &work[
+				*n + 1], &c__1);
+		    }
+L60:
+		    ;
+		}
+
+/*              Copy the vector x or Q*x to VR and normalize. */
+
+		if (! over) {
+		    igraphdcopy_(&ki, &work[*n + 1], &c__1, &vr[is * vr_dim1 + 1], &
+			    c__1);
+
+		    ii = igraphidamax_(&ki, &vr[is * vr_dim1 + 1], &c__1);
+		    remax = 1. / (d__1 = vr[ii + is * vr_dim1], abs(d__1));
+		    igraphdscal_(&ki, &remax, &vr[is * vr_dim1 + 1], &c__1);
+
+		    i__1 = *n;
+		    for (k = ki + 1; k <= i__1; ++k) {
+			vr[k + is * vr_dim1] = 0.;
+/* L70: */
+		    }
+		} else {
+		    if (ki > 1) {
+			i__1 = ki - 1;
+			igraphdgemv_("N", n, &i__1, &c_b22, &vr[vr_offset], ldvr, &
+				work[*n + 1], &c__1, &work[ki + *n], &vr[ki * 
+				vr_dim1 + 1], &c__1);
+		    }
+
+		    ii = igraphidamax_(n, &vr[ki * vr_dim1 + 1], &c__1);
+		    remax = 1. / (d__1 = vr[ii + ki * vr_dim1], abs(d__1));
+		    igraphdscal_(n, &remax, &vr[ki * vr_dim1 + 1], &c__1);
+		}
+
+	    } else {
+
+/*              Complex right eigenvector.   
+
+                Initial solve   
+                  [ (T(KI-1,KI-1) T(KI-1,KI) ) - (WR + I* WI)]*X = 0.   
+                  [ (T(KI,KI-1)   T(KI,KI)   )               ] */
+
+		if ((d__1 = t[ki - 1 + ki * t_dim1], abs(d__1)) >= (d__2 = t[
+			ki + (ki - 1) * t_dim1], abs(d__2))) {
+		    work[ki - 1 + *n] = 1.;
+		    work[ki + n2] = wi / t[ki - 1 + ki * t_dim1];
+		} else {
+		    work[ki - 1 + *n] = -wi / t[ki + (ki - 1) * t_dim1];
+		    work[ki + n2] = 1.;
+		}
+		work[ki + *n] = 0.;
+		work[ki - 1 + n2] = 0.;
+
+/*              Form right-hand side */
+
+		i__1 = ki - 2;
+		for (k = 1; k <= i__1; ++k) {
+		    work[k + *n] = -work[ki - 1 + *n] * t[k + (ki - 1) * 
+			    t_dim1];
+		    work[k + n2] = -work[ki + n2] * t[k + ki * t_dim1];
+/* L80: */
+		}
+
+/*              Solve upper quasi-triangular system:   
+                (T(1:KI-2,1:KI-2) - (WR+i*WI))*X = SCALE*(WORK+i*WORK2) */
+
+		jnxt = ki - 2;
+		for (j = ki - 2; j >= 1; --j) {
+		    if (j > jnxt) {
+			goto L90;
+		    }
+		    j1 = j;
+		    j2 = j;
+		    jnxt = j - 1;
+		    if (j > 1) {
+			if (t[j + (j - 1) * t_dim1] != 0.) {
+			    j1 = j - 1;
+			    jnxt = j - 2;
+			}
+		    }
+
+		    if (j1 == j2) {
+
+/*                    1-by-1 diagonal block */
+
+			igraphdlaln2_(&c_false, &c__1, &c__2, &smin, &c_b22, &t[j + 
+				j * t_dim1], ldt, &c_b22, &c_b22, &work[j + *
+				n], n, &wr, &wi, x, &c__2, &scale, &xnorm, &
+				ierr);
+
+/*                    Scale X(1,1) and X(1,2) to avoid overflow when   
+                      updating the right-hand side. */
+
+			if (xnorm > 1.) {
+			    if (work[j] > bignum / xnorm) {
+				x[0] /= xnorm;
+				x[2] /= xnorm;
+				scale /= xnorm;
+			    }
+			}
+
+/*                    Scale if necessary */
+
+			if (scale != 1.) {
+			    igraphdscal_(&ki, &scale, &work[*n + 1], &c__1);
+			    igraphdscal_(&ki, &scale, &work[n2 + 1], &c__1);
+			}
+			work[j + *n] = x[0];
+			work[j + n2] = x[2];
+
+/*                    Update the right-hand side */
+
+			i__1 = j - 1;
+			d__1 = -x[0];
+			igraphdaxpy_(&i__1, &d__1, &t[j * t_dim1 + 1], &c__1, &work[
+				*n + 1], &c__1);
+			i__1 = j - 1;
+			d__1 = -x[2];
+			igraphdaxpy_(&i__1, &d__1, &t[j * t_dim1 + 1], &c__1, &work[
+				n2 + 1], &c__1);
+
+		    } else {
+
+/*                    2-by-2 diagonal block */
+
+			igraphdlaln2_(&c_false, &c__2, &c__2, &smin, &c_b22, &t[j - 
+				1 + (j - 1) * t_dim1], ldt, &c_b22, &c_b22, &
+				work[j - 1 + *n], n, &wr, &wi, x, &c__2, &
+				scale, &xnorm, &ierr);
+
+/*                    Scale X to avoid overflow when updating   
+                      the right-hand side. */
+
+			if (xnorm > 1.) {
+/* Computing MAX */
+			    d__1 = work[j - 1], d__2 = work[j];
+			    beta = max(d__1,d__2);
+			    if (beta > bignum / xnorm) {
+				rec = 1. / xnorm;
+				x[0] *= rec;
+				x[2] *= rec;
+				x[1] *= rec;
+				x[3] *= rec;
+				scale *= rec;
+			    }
+			}
+
+/*                    Scale if necessary */
+
+			if (scale != 1.) {
+			    igraphdscal_(&ki, &scale, &work[*n + 1], &c__1);
+			    igraphdscal_(&ki, &scale, &work[n2 + 1], &c__1);
+			}
+			work[j - 1 + *n] = x[0];
+			work[j + *n] = x[1];
+			work[j - 1 + n2] = x[2];
+			work[j + n2] = x[3];
+
+/*                    Update the right-hand side */
+
+			i__1 = j - 2;
+			d__1 = -x[0];
+			igraphdaxpy_(&i__1, &d__1, &t[(j - 1) * t_dim1 + 1], &c__1, 
+				&work[*n + 1], &c__1);
+			i__1 = j - 2;
+			d__1 = -x[1];
+			igraphdaxpy_(&i__1, &d__1, &t[j * t_dim1 + 1], &c__1, &work[
+				*n + 1], &c__1);
+			i__1 = j - 2;
+			d__1 = -x[2];
+			igraphdaxpy_(&i__1, &d__1, &t[(j - 1) * t_dim1 + 1], &c__1, 
+				&work[n2 + 1], &c__1);
+			i__1 = j - 2;
+			d__1 = -x[3];
+			igraphdaxpy_(&i__1, &d__1, &t[j * t_dim1 + 1], &c__1, &work[
+				n2 + 1], &c__1);
+		    }
+L90:
+		    ;
+		}
+
+/*              Copy the vector x or Q*x to VR and normalize. */
+
+		if (! over) {
+		    igraphdcopy_(&ki, &work[*n + 1], &c__1, &vr[(is - 1) * vr_dim1 
+			    + 1], &c__1);
+		    igraphdcopy_(&ki, &work[n2 + 1], &c__1, &vr[is * vr_dim1 + 1], &
+			    c__1);
+
+		    emax = 0.;
+		    i__1 = ki;
+		    for (k = 1; k <= i__1; ++k) {
+/* Computing MAX */
+			d__3 = emax, d__4 = (d__1 = vr[k + (is - 1) * vr_dim1]
+				, abs(d__1)) + (d__2 = vr[k + is * vr_dim1], 
+				abs(d__2));
+			emax = max(d__3,d__4);
+/* L100: */
+		    }
+
+		    remax = 1. / emax;
+		    igraphdscal_(&ki, &remax, &vr[(is - 1) * vr_dim1 + 1], &c__1);
+		    igraphdscal_(&ki, &remax, &vr[is * vr_dim1 + 1], &c__1);
+
+		    i__1 = *n;
+		    for (k = ki + 1; k <= i__1; ++k) {
+			vr[k + (is - 1) * vr_dim1] = 0.;
+			vr[k + is * vr_dim1] = 0.;
+/* L110: */
+		    }
+
+		} else {
+
+		    if (ki > 2) {
+			i__1 = ki - 2;
+			igraphdgemv_("N", n, &i__1, &c_b22, &vr[vr_offset], ldvr, &
+				work[*n + 1], &c__1, &work[ki - 1 + *n], &vr[(
+				ki - 1) * vr_dim1 + 1], &c__1);
+			i__1 = ki - 2;
+			igraphdgemv_("N", n, &i__1, &c_b22, &vr[vr_offset], ldvr, &
+				work[n2 + 1], &c__1, &work[ki + n2], &vr[ki * 
+				vr_dim1 + 1], &c__1);
+		    } else {
+			igraphdscal_(n, &work[ki - 1 + *n], &vr[(ki - 1) * vr_dim1 
+				+ 1], &c__1);
+			igraphdscal_(n, &work[ki + n2], &vr[ki * vr_dim1 + 1], &
+				c__1);
+		    }
+
+		    emax = 0.;
+		    i__1 = *n;
+		    for (k = 1; k <= i__1; ++k) {
+/* Computing MAX */
+			d__3 = emax, d__4 = (d__1 = vr[k + (ki - 1) * vr_dim1]
+				, abs(d__1)) + (d__2 = vr[k + ki * vr_dim1], 
+				abs(d__2));
+			emax = max(d__3,d__4);
+/* L120: */
+		    }
+		    remax = 1. / emax;
+		    igraphdscal_(n, &remax, &vr[(ki - 1) * vr_dim1 + 1], &c__1);
+		    igraphdscal_(n, &remax, &vr[ki * vr_dim1 + 1], &c__1);
+		}
+	    }
+
+	    --is;
+	    if (ip != 0) {
+		--is;
+	    }
+L130:
+	    if (ip == 1) {
+		ip = 0;
+	    }
+	    if (ip == -1) {
+		ip = 1;
+	    }
+/* L140: */
+	}
+    }
+
+    if (leftv) {
+
+/*        Compute left eigenvectors. */
+
+	ip = 0;
+	is = 1;
+	i__1 = *n;
+	for (ki = 1; ki <= i__1; ++ki) {
+
+	    if (ip == -1) {
+		goto L250;
+	    }
+	    if (ki == *n) {
+		goto L150;
+	    }
+	    if (t[ki + 1 + ki * t_dim1] == 0.) {
+		goto L150;
+	    }
+	    ip = 1;
+
+L150:
+	    if (somev) {
+		if (! select[ki]) {
+		    goto L250;
+		}
+	    }
+
+/*           Compute the KI-th eigenvalue (WR,WI). */
+
+	    wr = t[ki + ki * t_dim1];
+	    wi = 0.;
+	    if (ip != 0) {
+		wi = sqrt((d__1 = t[ki + (ki + 1) * t_dim1], abs(d__1))) * 
+			sqrt((d__2 = t[ki + 1 + ki * t_dim1], abs(d__2)));
+	    }
+/* Computing MAX */
+	    d__1 = ulp * (abs(wr) + abs(wi));
+	    smin = max(d__1,smlnum);
+
+	    if (ip == 0) {
+
+/*              Real left eigenvector. */
+
+		work[ki + *n] = 1.;
+
+/*              Form right-hand side */
+
+		i__2 = *n;
+		for (k = ki + 1; k <= i__2; ++k) {
+		    work[k + *n] = -t[ki + k * t_dim1];
+/* L160: */
+		}
+
+/*              Solve the quasi-triangular system:   
+                   (T(KI+1:N,KI+1:N) - WR)**T*X = SCALE*WORK */
+
+		vmax = 1.;
+		vcrit = bignum;
+
+		jnxt = ki + 1;
+		i__2 = *n;
+		for (j = ki + 1; j <= i__2; ++j) {
+		    if (j < jnxt) {
+			goto L170;
+		    }
+		    j1 = j;
+		    j2 = j;
+		    jnxt = j + 1;
+		    if (j < *n) {
+			if (t[j + 1 + j * t_dim1] != 0.) {
+			    j2 = j + 1;
+			    jnxt = j + 2;
+			}
+		    }
+
+		    if (j1 == j2) {
+
+/*                    1-by-1 diagonal block   
+
+                      Scale if necessary to avoid overflow when forming   
+                      the right-hand side. */
+
+			if (work[j] > vcrit) {
+			    rec = 1. / vmax;
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &rec, &work[ki + *n], &c__1);
+			    vmax = 1.;
+			    vcrit = bignum;
+			}
+
+			i__3 = j - ki - 1;
+			work[j + *n] -= igraphddot_(&i__3, &t[ki + 1 + j * t_dim1], 
+				&c__1, &work[ki + 1 + *n], &c__1);
+
+/*                    Solve (T(J,J)-WR)**T*X = WORK */
+
+			igraphdlaln2_(&c_false, &c__1, &c__1, &smin, &c_b22, &t[j + 
+				j * t_dim1], ldt, &c_b22, &c_b22, &work[j + *
+				n], n, &wr, &c_b25, x, &c__2, &scale, &xnorm, 
+				&ierr);
+
+/*                    Scale if necessary */
+
+			if (scale != 1.) {
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &scale, &work[ki + *n], &c__1);
+			}
+			work[j + *n] = x[0];
+/* Computing MAX */
+			d__2 = (d__1 = work[j + *n], abs(d__1));
+			vmax = max(d__2,vmax);
+			vcrit = bignum / vmax;
+
+		    } else {
+
+/*                    2-by-2 diagonal block   
+
+                      Scale if necessary to avoid overflow when forming   
+                      the right-hand side.   
+
+   Computing MAX */
+			d__1 = work[j], d__2 = work[j + 1];
+			beta = max(d__1,d__2);
+			if (beta > vcrit) {
+			    rec = 1. / vmax;
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &rec, &work[ki + *n], &c__1);
+			    vmax = 1.;
+			    vcrit = bignum;
+			}
+
+			i__3 = j - ki - 1;
+			work[j + *n] -= igraphddot_(&i__3, &t[ki + 1 + j * t_dim1], 
+				&c__1, &work[ki + 1 + *n], &c__1);
+
+			i__3 = j - ki - 1;
+			work[j + 1 + *n] -= igraphddot_(&i__3, &t[ki + 1 + (j + 1) *
+				 t_dim1], &c__1, &work[ki + 1 + *n], &c__1);
+
+/*                    Solve   
+                        [T(J,J)-WR   T(J,J+1)     ]**T * X = SCALE*( WORK1 )   
+                        [T(J+1,J)    T(J+1,J+1)-WR]                ( WORK2 ) */
+
+			igraphdlaln2_(&c_true, &c__2, &c__1, &smin, &c_b22, &t[j + 
+				j * t_dim1], ldt, &c_b22, &c_b22, &work[j + *
+				n], n, &wr, &c_b25, x, &c__2, &scale, &xnorm, 
+				&ierr);
+
+/*                    Scale if necessary */
+
+			if (scale != 1.) {
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &scale, &work[ki + *n], &c__1);
+			}
+			work[j + *n] = x[0];
+			work[j + 1 + *n] = x[1];
+
+/* Computing MAX */
+			d__3 = (d__1 = work[j + *n], abs(d__1)), d__4 = (d__2 
+				= work[j + 1 + *n], abs(d__2)), d__3 = max(
+				d__3,d__4);
+			vmax = max(d__3,vmax);
+			vcrit = bignum / vmax;
+
+		    }
+L170:
+		    ;
+		}
+
+/*              Copy the vector x or Q*x to VL and normalize. */
+
+		if (! over) {
+		    i__2 = *n - ki + 1;
+		    igraphdcopy_(&i__2, &work[ki + *n], &c__1, &vl[ki + is * 
+			    vl_dim1], &c__1);
+
+		    i__2 = *n - ki + 1;
+		    ii = igraphidamax_(&i__2, &vl[ki + is * vl_dim1], &c__1) + ki - 
+			    1;
+		    remax = 1. / (d__1 = vl[ii + is * vl_dim1], abs(d__1));
+		    i__2 = *n - ki + 1;
+		    igraphdscal_(&i__2, &remax, &vl[ki + is * vl_dim1], &c__1);
+
+		    i__2 = ki - 1;
+		    for (k = 1; k <= i__2; ++k) {
+			vl[k + is * vl_dim1] = 0.;
+/* L180: */
+		    }
+
+		} else {
+
+		    if (ki < *n) {
+			i__2 = *n - ki;
+			igraphdgemv_("N", n, &i__2, &c_b22, &vl[(ki + 1) * vl_dim1 
+				+ 1], ldvl, &work[ki + 1 + *n], &c__1, &work[
+				ki + *n], &vl[ki * vl_dim1 + 1], &c__1);
+		    }
+
+		    ii = igraphidamax_(n, &vl[ki * vl_dim1 + 1], &c__1);
+		    remax = 1. / (d__1 = vl[ii + ki * vl_dim1], abs(d__1));
+		    igraphdscal_(n, &remax, &vl[ki * vl_dim1 + 1], &c__1);
+
+		}
+
+	    } else {
+
+/*              Complex left eigenvector.   
+
+                 Initial solve:   
+                   ((T(KI,KI)    T(KI,KI+1) )**T - (WR - I* WI))*X = 0.   
+                   ((T(KI+1,KI) T(KI+1,KI+1))                ) */
+
+		if ((d__1 = t[ki + (ki + 1) * t_dim1], abs(d__1)) >= (d__2 = 
+			t[ki + 1 + ki * t_dim1], abs(d__2))) {
+		    work[ki + *n] = wi / t[ki + (ki + 1) * t_dim1];
+		    work[ki + 1 + n2] = 1.;
+		} else {
+		    work[ki + *n] = 1.;
+		    work[ki + 1 + n2] = -wi / t[ki + 1 + ki * t_dim1];
+		}
+		work[ki + 1 + *n] = 0.;
+		work[ki + n2] = 0.;
+
+/*              Form right-hand side */
+
+		i__2 = *n;
+		for (k = ki + 2; k <= i__2; ++k) {
+		    work[k + *n] = -work[ki + *n] * t[ki + k * t_dim1];
+		    work[k + n2] = -work[ki + 1 + n2] * t[ki + 1 + k * t_dim1]
+			    ;
+/* L190: */
+		}
+
+/*              Solve complex quasi-triangular system:   
+                ( T(KI+2,N:KI+2,N) - (WR-i*WI) )*X = WORK1+i*WORK2 */
+
+		vmax = 1.;
+		vcrit = bignum;
+
+		jnxt = ki + 2;
+		i__2 = *n;
+		for (j = ki + 2; j <= i__2; ++j) {
+		    if (j < jnxt) {
+			goto L200;
+		    }
+		    j1 = j;
+		    j2 = j;
+		    jnxt = j + 1;
+		    if (j < *n) {
+			if (t[j + 1 + j * t_dim1] != 0.) {
+			    j2 = j + 1;
+			    jnxt = j + 2;
+			}
+		    }
+
+		    if (j1 == j2) {
+
+/*                    1-by-1 diagonal block   
+
+                      Scale if necessary to avoid overflow when   
+                      forming the right-hand side elements. */
+
+			if (work[j] > vcrit) {
+			    rec = 1. / vmax;
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &rec, &work[ki + *n], &c__1);
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &rec, &work[ki + n2], &c__1);
+			    vmax = 1.;
+			    vcrit = bignum;
+			}
+
+			i__3 = j - ki - 2;
+			work[j + *n] -= igraphddot_(&i__3, &t[ki + 2 + j * t_dim1], 
+				&c__1, &work[ki + 2 + *n], &c__1);
+			i__3 = j - ki - 2;
+			work[j + n2] -= igraphddot_(&i__3, &t[ki + 2 + j * t_dim1], 
+				&c__1, &work[ki + 2 + n2], &c__1);
+
+/*                    Solve (T(J,J)-(WR-i*WI))*(X11+i*X12)= WK+I*WK2 */
+
+			d__1 = -wi;
+			igraphdlaln2_(&c_false, &c__1, &c__2, &smin, &c_b22, &t[j + 
+				j * t_dim1], ldt, &c_b22, &c_b22, &work[j + *
+				n], n, &wr, &d__1, x, &c__2, &scale, &xnorm, &
+				ierr);
+
+/*                    Scale if necessary */
+
+			if (scale != 1.) {
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &scale, &work[ki + *n], &c__1);
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &scale, &work[ki + n2], &c__1);
+			}
+			work[j + *n] = x[0];
+			work[j + n2] = x[2];
+/* Computing MAX */
+			d__3 = (d__1 = work[j + *n], abs(d__1)), d__4 = (d__2 
+				= work[j + n2], abs(d__2)), d__3 = max(d__3,
+				d__4);
+			vmax = max(d__3,vmax);
+			vcrit = bignum / vmax;
+
+		    } else {
+
+/*                    2-by-2 diagonal block   
+
+                      Scale if necessary to avoid overflow when forming   
+                      the right-hand side elements.   
+
+   Computing MAX */
+			d__1 = work[j], d__2 = work[j + 1];
+			beta = max(d__1,d__2);
+			if (beta > vcrit) {
+			    rec = 1. / vmax;
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &rec, &work[ki + *n], &c__1);
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &rec, &work[ki + n2], &c__1);
+			    vmax = 1.;
+			    vcrit = bignum;
+			}
+
+			i__3 = j - ki - 2;
+			work[j + *n] -= igraphddot_(&i__3, &t[ki + 2 + j * t_dim1], 
+				&c__1, &work[ki + 2 + *n], &c__1);
+
+			i__3 = j - ki - 2;
+			work[j + n2] -= igraphddot_(&i__3, &t[ki + 2 + j * t_dim1], 
+				&c__1, &work[ki + 2 + n2], &c__1);
+
+			i__3 = j - ki - 2;
+			work[j + 1 + *n] -= igraphddot_(&i__3, &t[ki + 2 + (j + 1) *
+				 t_dim1], &c__1, &work[ki + 2 + *n], &c__1);
+
+			i__3 = j - ki - 2;
+			work[j + 1 + n2] -= igraphddot_(&i__3, &t[ki + 2 + (j + 1) *
+				 t_dim1], &c__1, &work[ki + 2 + n2], &c__1);
+
+/*                    Solve 2-by-2 complex linear equation   
+                        ([T(j,j)   T(j,j+1)  ]**T-(wr-i*wi)*I)*X = SCALE*B   
+                        ([T(j+1,j) T(j+1,j+1)]               ) */
+
+			d__1 = -wi;
+			igraphdlaln2_(&c_true, &c__2, &c__2, &smin, &c_b22, &t[j + 
+				j * t_dim1], ldt, &c_b22, &c_b22, &work[j + *
+				n], n, &wr, &d__1, x, &c__2, &scale, &xnorm, &
+				ierr);
+
+/*                    Scale if necessary */
+
+			if (scale != 1.) {
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &scale, &work[ki + *n], &c__1);
+			    i__3 = *n - ki + 1;
+			    igraphdscal_(&i__3, &scale, &work[ki + n2], &c__1);
+			}
+			work[j + *n] = x[0];
+			work[j + n2] = x[2];
+			work[j + 1 + *n] = x[1];
+			work[j + 1 + n2] = x[3];
+/* Computing MAX */
+			d__1 = abs(x[0]), d__2 = abs(x[2]), d__1 = max(d__1,
+				d__2), d__2 = abs(x[1]), d__1 = max(d__1,d__2)
+				, d__2 = abs(x[3]), d__1 = max(d__1,d__2);
+			vmax = max(d__1,vmax);
+			vcrit = bignum / vmax;
+
+		    }
+L200:
+		    ;
+		}
+
+/*              Copy the vector x or Q*x to VL and normalize. */
+
+		if (! over) {
+		    i__2 = *n - ki + 1;
+		    igraphdcopy_(&i__2, &work[ki + *n], &c__1, &vl[ki + is * 
+			    vl_dim1], &c__1);
+		    i__2 = *n - ki + 1;
+		    igraphdcopy_(&i__2, &work[ki + n2], &c__1, &vl[ki + (is + 1) * 
+			    vl_dim1], &c__1);
+
+		    emax = 0.;
+		    i__2 = *n;
+		    for (k = ki; k <= i__2; ++k) {
+/* Computing MAX */
+			d__3 = emax, d__4 = (d__1 = vl[k + is * vl_dim1], abs(
+				d__1)) + (d__2 = vl[k + (is + 1) * vl_dim1], 
+				abs(d__2));
+			emax = max(d__3,d__4);
+/* L220: */
+		    }
+		    remax = 1. / emax;
+		    i__2 = *n - ki + 1;
+		    igraphdscal_(&i__2, &remax, &vl[ki + is * vl_dim1], &c__1);
+		    i__2 = *n - ki + 1;
+		    igraphdscal_(&i__2, &remax, &vl[ki + (is + 1) * vl_dim1], &c__1)
+			    ;
+
+		    i__2 = ki - 1;
+		    for (k = 1; k <= i__2; ++k) {
+			vl[k + is * vl_dim1] = 0.;
+			vl[k + (is + 1) * vl_dim1] = 0.;
+/* L230: */
+		    }
+		} else {
+		    if (ki < *n - 1) {
+			i__2 = *n - ki - 1;
+			igraphdgemv_("N", n, &i__2, &c_b22, &vl[(ki + 2) * vl_dim1 
+				+ 1], ldvl, &work[ki + 2 + *n], &c__1, &work[
+				ki + *n], &vl[ki * vl_dim1 + 1], &c__1);
+			i__2 = *n - ki - 1;
+			igraphdgemv_("N", n, &i__2, &c_b22, &vl[(ki + 2) * vl_dim1 
+				+ 1], ldvl, &work[ki + 2 + n2], &c__1, &work[
+				ki + 1 + n2], &vl[(ki + 1) * vl_dim1 + 1], &
+				c__1);
+		    } else {
+			igraphdscal_(n, &work[ki + *n], &vl[ki * vl_dim1 + 1], &
+				c__1);
+			igraphdscal_(n, &work[ki + 1 + n2], &vl[(ki + 1) * vl_dim1 
+				+ 1], &c__1);
+		    }
+
+		    emax = 0.;
+		    i__2 = *n;
+		    for (k = 1; k <= i__2; ++k) {
+/* Computing MAX */
+			d__3 = emax, d__4 = (d__1 = vl[k + ki * vl_dim1], abs(
+				d__1)) + (d__2 = vl[k + (ki + 1) * vl_dim1], 
+				abs(d__2));
+			emax = max(d__3,d__4);
+/* L240: */
+		    }
+		    remax = 1. / emax;
+		    igraphdscal_(n, &remax, &vl[ki * vl_dim1 + 1], &c__1);
+		    igraphdscal_(n, &remax, &vl[(ki + 1) * vl_dim1 + 1], &c__1);
+
+		}
+
+	    }
+
+	    ++is;
+	    if (ip != 0) {
+		++is;
+	    }
+L250:
+	    if (ip == -1) {
+		ip = 0;
+	    }
+	    if (ip == 1) {
+		ip = -1;
+	    }
+
+/* L260: */
+	}
+
+    }
+
+    return 0;
+
+/*     End of DTREVC */
+
+} /* igraphdtrevc_ */
+
diff --git a/igraph/src/dtrexc.c b/igraph/src/dtrexc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrexc.c
@@ -0,0 +1,468 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static integer c__2 = 2;
+
+/* > \brief \b DTREXC   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DTREXC + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dtrexc.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dtrexc.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dtrexc.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DTREXC( COMPQ, N, T, LDT, Q, LDQ, IFST, ILST, WORK,   
+                            INFO )   
+
+         CHARACTER          COMPQ   
+         INTEGER            IFST, ILST, INFO, LDQ, LDT, N   
+         DOUBLE PRECISION   Q( LDQ, * ), T( LDT, * ), WORK( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DTREXC reorders the real Schur factorization of a real matrix   
+   > A = Q*T*Q**T, so that the diagonal block of T with row index IFST is   
+   > moved to row ILST.   
+   >   
+   > The real Schur form T is reordered by an orthogonal similarity   
+   > transformation Z**T*T*Z, and optionally the matrix Q of Schur vectors   
+   > is updated by postmultiplying it with Z.   
+   >   
+   > T must be in Schur canonical form (as returned by DHSEQR), that is,   
+   > block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; each   
+   > 2-by-2 diagonal block has its diagonal elements equal and its   
+   > off-diagonal elements of opposite sign.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] COMPQ   
+   > \verbatim   
+   >          COMPQ is CHARACTER*1   
+   >          = 'V':  update the matrix Q of Schur vectors;   
+   >          = 'N':  do not update Q.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix T. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,N)   
+   >          On entry, the upper quasi-triangular matrix T, in Schur   
+   >          Schur canonical form.   
+   >          On exit, the reordered upper quasi-triangular matrix, again   
+   >          in Schur canonical form.   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is INTEGER   
+   >          The leading dimension of the array T. LDT >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in,out] Q   
+   > \verbatim   
+   >          Q is DOUBLE PRECISION array, dimension (LDQ,N)   
+   >          On entry, if COMPQ = 'V', the matrix Q of Schur vectors.   
+   >          On exit, if COMPQ = 'V', Q has been postmultiplied by the   
+   >          orthogonal transformation matrix Z which reorders T.   
+   >          If COMPQ = 'N', Q is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDQ   
+   > \verbatim   
+   >          LDQ is INTEGER   
+   >          The leading dimension of the array Q.  LDQ >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in,out] IFST   
+   > \verbatim   
+   >          IFST is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in,out] ILST   
+   > \verbatim   
+   >          ILST is INTEGER   
+   >   
+   >          Specify the reordering of the diagonal blocks of T.   
+   >          The block with row index IFST is moved to row ILST, by a   
+   >          sequence of transpositions between adjacent blocks.   
+   >          On exit, if IFST pointed on entry to the second row of a   
+   >          2-by-2 block, it is changed to point to the first row; ILST   
+   >          always points to the first row of the block in its final   
+   >          position (which may differ from its input value by +1 or -1).   
+   >          1 <= IFST <= N; 1 <= ILST <= N.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0:  successful exit   
+   >          < 0:  if INFO = -i, the i-th argument had an illegal value   
+   >          = 1:  two adjacent blocks were too close to swap (the problem   
+   >                is very ill-conditioned); T may have been partially   
+   >                reordered, and ILST points to the first row of the   
+   >                current position of the block being moved.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdtrexc_(char *compq, integer *n, doublereal *t, integer *
+	ldt, doublereal *q, integer *ldq, integer *ifst, integer *ilst, 
+	doublereal *work, integer *info)
+{
+    /* System generated locals */
+    integer q_dim1, q_offset, t_dim1, t_offset, i__1;
+
+    /* Local variables */
+    integer nbf, nbl, here;
+    extern logical igraphlsame_(char *, char *);
+    logical wantq;
+    extern /* Subroutine */ int igraphdlaexc_(logical *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *, integer *, integer 
+	    *, doublereal *, integer *), igraphxerbla_(char *, integer *, ftnlen);
+    integer nbnext;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Decode and test the input arguments.   
+
+       Parameter adjustments */
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    q_dim1 = *ldq;
+    q_offset = 1 + q_dim1;
+    q -= q_offset;
+    --work;
+
+    /* Function Body */
+    *info = 0;
+    wantq = igraphlsame_(compq, "V");
+    if (! wantq && ! igraphlsame_(compq, "N")) {
+	*info = -1;
+    } else if (*n < 0) {
+	*info = -2;
+    } else if (*ldt < max(1,*n)) {
+	*info = -4;
+    } else if (*ldq < 1 || wantq && *ldq < max(1,*n)) {
+	*info = -6;
+    } else if (*ifst < 1 || *ifst > *n) {
+	*info = -7;
+    } else if (*ilst < 1 || *ilst > *n) {
+	*info = -8;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DTREXC", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n <= 1) {
+	return 0;
+    }
+
+/*     Determine the first row of specified block   
+       and find out it is 1 by 1 or 2 by 2. */
+
+    if (*ifst > 1) {
+	if (t[*ifst + (*ifst - 1) * t_dim1] != 0.) {
+	    --(*ifst);
+	}
+    }
+    nbf = 1;
+    if (*ifst < *n) {
+	if (t[*ifst + 1 + *ifst * t_dim1] != 0.) {
+	    nbf = 2;
+	}
+    }
+
+/*     Determine the first row of the final block   
+       and find out it is 1 by 1 or 2 by 2. */
+
+    if (*ilst > 1) {
+	if (t[*ilst + (*ilst - 1) * t_dim1] != 0.) {
+	    --(*ilst);
+	}
+    }
+    nbl = 1;
+    if (*ilst < *n) {
+	if (t[*ilst + 1 + *ilst * t_dim1] != 0.) {
+	    nbl = 2;
+	}
+    }
+
+    if (*ifst == *ilst) {
+	return 0;
+    }
+
+    if (*ifst < *ilst) {
+
+/*        Update ILST */
+
+	if (nbf == 2 && nbl == 1) {
+	    --(*ilst);
+	}
+	if (nbf == 1 && nbl == 2) {
+	    ++(*ilst);
+	}
+
+	here = *ifst;
+
+L10:
+
+/*        Swap block with next one below */
+
+	if (nbf == 1 || nbf == 2) {
+
+/*           Current block either 1 by 1 or 2 by 2 */
+
+	    nbnext = 1;
+	    if (here + nbf + 1 <= *n) {
+		if (t[here + nbf + 1 + (here + nbf) * t_dim1] != 0.) {
+		    nbnext = 2;
+		}
+	    }
+	    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &here, &
+		    nbf, &nbnext, &work[1], info);
+	    if (*info != 0) {
+		*ilst = here;
+		return 0;
+	    }
+	    here += nbnext;
+
+/*           Test if 2 by 2 block breaks into two 1 by 1 blocks */
+
+	    if (nbf == 2) {
+		if (t[here + 1 + here * t_dim1] == 0.) {
+		    nbf = 3;
+		}
+	    }
+
+	} else {
+
+/*           Current block consists of two 1 by 1 blocks each of which   
+             must be swapped individually */
+
+	    nbnext = 1;
+	    if (here + 3 <= *n) {
+		if (t[here + 3 + (here + 2) * t_dim1] != 0.) {
+		    nbnext = 2;
+		}
+	    }
+	    i__1 = here + 1;
+	    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &i__1, &
+		    c__1, &nbnext, &work[1], info);
+	    if (*info != 0) {
+		*ilst = here;
+		return 0;
+	    }
+	    if (nbnext == 1) {
+
+/*              Swap two 1 by 1 blocks, no problems possible */
+
+		igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			here, &c__1, &nbnext, &work[1], info);
+		++here;
+	    } else {
+
+/*              Recompute NBNEXT in case 2 by 2 split */
+
+		if (t[here + 2 + (here + 1) * t_dim1] == 0.) {
+		    nbnext = 1;
+		}
+		if (nbnext == 2) {
+
+/*                 2 by 2 Block did not split */
+
+		    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			    here, &c__1, &nbnext, &work[1], info);
+		    if (*info != 0) {
+			*ilst = here;
+			return 0;
+		    }
+		    here += 2;
+		} else {
+
+/*                 2 by 2 Block did split */
+
+		    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			    here, &c__1, &c__1, &work[1], info);
+		    i__1 = here + 1;
+		    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			    i__1, &c__1, &c__1, &work[1], info);
+		    here += 2;
+		}
+	    }
+	}
+	if (here < *ilst) {
+	    goto L10;
+	}
+
+    } else {
+
+	here = *ifst;
+L20:
+
+/*        Swap block with next one above */
+
+	if (nbf == 1 || nbf == 2) {
+
+/*           Current block either 1 by 1 or 2 by 2 */
+
+	    nbnext = 1;
+	    if (here >= 3) {
+		if (t[here - 1 + (here - 2) * t_dim1] != 0.) {
+		    nbnext = 2;
+		}
+	    }
+	    i__1 = here - nbnext;
+	    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &i__1, &
+		    nbnext, &nbf, &work[1], info);
+	    if (*info != 0) {
+		*ilst = here;
+		return 0;
+	    }
+	    here -= nbnext;
+
+/*           Test if 2 by 2 block breaks into two 1 by 1 blocks */
+
+	    if (nbf == 2) {
+		if (t[here + 1 + here * t_dim1] == 0.) {
+		    nbf = 3;
+		}
+	    }
+
+	} else {
+
+/*           Current block consists of two 1 by 1 blocks each of which   
+             must be swapped individually */
+
+	    nbnext = 1;
+	    if (here >= 3) {
+		if (t[here - 1 + (here - 2) * t_dim1] != 0.) {
+		    nbnext = 2;
+		}
+	    }
+	    i__1 = here - nbnext;
+	    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &i__1, &
+		    nbnext, &c__1, &work[1], info);
+	    if (*info != 0) {
+		*ilst = here;
+		return 0;
+	    }
+	    if (nbnext == 1) {
+
+/*              Swap two 1 by 1 blocks, no problems possible */
+
+		igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			here, &nbnext, &c__1, &work[1], info);
+		--here;
+	    } else {
+
+/*              Recompute NBNEXT in case 2 by 2 split */
+
+		if (t[here + (here - 1) * t_dim1] == 0.) {
+		    nbnext = 1;
+		}
+		if (nbnext == 2) {
+
+/*                 2 by 2 Block did not split */
+
+		    i__1 = here - 1;
+		    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			    i__1, &c__2, &c__1, &work[1], info);
+		    if (*info != 0) {
+			*ilst = here;
+			return 0;
+		    }
+		    here += -2;
+		} else {
+
+/*                 2 by 2 Block did split */
+
+		    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			    here, &c__1, &c__1, &work[1], info);
+		    i__1 = here - 1;
+		    igraphdlaexc_(&wantq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			    i__1, &c__1, &c__1, &work[1], info);
+		    here += -2;
+		}
+	    }
+	}
+	if (here > *ilst) {
+	    goto L20;
+	}
+    }
+    *ilst = here;
+
+    return 0;
+
+/*     End of DTREXC */
+
+} /* igraphdtrexc_ */
+
diff --git a/igraph/src/dtrmm.c b/igraph/src/dtrmm.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrmm.c
@@ -0,0 +1,442 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdtrmm_(char *side, char *uplo, char *transa, char *diag, 
+	integer *m, integer *n, doublereal *alpha, doublereal *a, integer *
+	lda, doublereal *b, integer *ldb)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__, j, k, info;
+    doublereal temp;
+    logical lside;
+    extern logical igraphlsame_(char *, char *);
+    integer nrowa;
+    logical upper;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical nounit;
+
+
+/*  Purpose   
+    =======   
+
+    DTRMM  performs one of the matrix-matrix operations   
+
+       B := alpha*op( A )*B,   or   B := alpha*B*op( A ),   
+
+    where  alpha  is a scalar,  B  is an m by n matrix,  A  is a unit, or   
+    non-unit,  upper or lower triangular matrix  and  op( A )  is one  of   
+
+       op( A ) = A   or   op( A ) = A**T.   
+
+    Arguments   
+    ==========   
+
+    SIDE   - CHARACTER*1.   
+             On entry,  SIDE specifies whether  op( A ) multiplies B from   
+             the left or right as follows:   
+
+                SIDE = 'L' or 'l'   B := alpha*op( A )*B.   
+
+                SIDE = 'R' or 'r'   B := alpha*B*op( A ).   
+
+             Unchanged on exit.   
+
+    UPLO   - CHARACTER*1.   
+             On entry, UPLO specifies whether the matrix A is an upper or   
+             lower triangular matrix as follows:   
+
+                UPLO = 'U' or 'u'   A is an upper triangular matrix.   
+
+                UPLO = 'L' or 'l'   A is a lower triangular matrix.   
+
+             Unchanged on exit.   
+
+    TRANSA - CHARACTER*1.   
+             On entry, TRANSA specifies the form of op( A ) to be used in   
+             the matrix multiplication as follows:   
+
+                TRANSA = 'N' or 'n'   op( A ) = A.   
+
+                TRANSA = 'T' or 't'   op( A ) = A**T.   
+
+                TRANSA = 'C' or 'c'   op( A ) = A**T.   
+
+             Unchanged on exit.   
+
+    DIAG   - CHARACTER*1.   
+             On entry, DIAG specifies whether or not A is unit triangular   
+             as follows:   
+
+                DIAG = 'U' or 'u'   A is assumed to be unit triangular.   
+
+                DIAG = 'N' or 'n'   A is not assumed to be unit   
+                                    triangular.   
+
+             Unchanged on exit.   
+
+    M      - INTEGER.   
+             On entry, M specifies the number of rows of B. M must be at   
+             least zero.   
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry, N specifies the number of columns of B.  N must be   
+             at least zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry,  ALPHA specifies the scalar  alpha. When  alpha is   
+             zero then  A is not referenced and  B need not be set before   
+             entry.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, k ), where k is m   
+             when  SIDE = 'L' or 'l'  and is  n  when  SIDE = 'R' or 'r'.   
+             Before entry  with  UPLO = 'U' or 'u',  the  leading  k by k   
+             upper triangular part of the array  A must contain the upper   
+             triangular matrix  and the strictly lower triangular part of   
+             A is not referenced.   
+             Before entry  with  UPLO = 'L' or 'l',  the  leading  k by k   
+             lower triangular part of the array  A must contain the lower   
+             triangular matrix  and the strictly upper triangular part of   
+             A is not referenced.   
+             Note that when  DIAG = 'U' or 'u',  the diagonal elements of   
+             A  are not referenced either,  but are assumed to be  unity.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program.  When  SIDE = 'L' or 'l'  then   
+             LDA  must be at least  max( 1, m ),  when  SIDE = 'R' or 'r'   
+             then LDA must be at least max( 1, n ).   
+             Unchanged on exit.   
+
+    B      - DOUBLE PRECISION array of DIMENSION ( LDB, n ).   
+             Before entry,  the leading  m by n part of the array  B must   
+             contain the matrix  B,  and  on exit  is overwritten  by the   
+             transformed matrix.   
+
+    LDB    - INTEGER.   
+             On entry, LDB specifies the first dimension of B as declared   
+             in  the  calling  (sub)  program.   LDB  must  be  at  least   
+             max( 1, m ).   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 3 Blas routine.   
+
+    -- Written on 8-February-1989.   
+       Jack Dongarra, Argonne National Laboratory.   
+       Iain Duff, AERE Harwell.   
+       Jeremy Du Croz, Numerical Algorithms Group Ltd.   
+       Sven Hammarling, Numerical Algorithms Group Ltd.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+
+    /* Function Body */
+    lside = igraphlsame_(side, "L");
+    if (lside) {
+	nrowa = *m;
+    } else {
+	nrowa = *n;
+    }
+    nounit = igraphlsame_(diag, "N");
+    upper = igraphlsame_(uplo, "U");
+
+    info = 0;
+    if (! lside && ! igraphlsame_(side, "R")) {
+	info = 1;
+    } else if (! upper && ! igraphlsame_(uplo, "L")) {
+	info = 2;
+    } else if (! igraphlsame_(transa, "N") && ! igraphlsame_(transa,
+	     "T") && ! igraphlsame_(transa, "C")) {
+	info = 3;
+    } else if (! igraphlsame_(diag, "U") && ! igraphlsame_(diag, 
+	    "N")) {
+	info = 4;
+    } else if (*m < 0) {
+	info = 5;
+    } else if (*n < 0) {
+	info = 6;
+    } else if (*lda < max(1,nrowa)) {
+	info = 9;
+    } else if (*ldb < max(1,*m)) {
+	info = 11;
+    }
+    if (info != 0) {
+	igraphxerbla_("DTRMM ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*m == 0 || *n == 0) {
+	return 0;
+    }
+
+/*     And when  alpha.eq.zero. */
+
+    if (*alpha == 0.) {
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		b[i__ + j * b_dim1] = 0.;
+/* L10: */
+	    }
+/* L20: */
+	}
+	return 0;
+    }
+
+/*     Start the operations. */
+
+    if (lside) {
+	if (igraphlsame_(transa, "N")) {
+
+/*           Form  B := alpha*A*B. */
+
+	    if (upper) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *m;
+		    for (k = 1; k <= i__2; ++k) {
+			if (b[k + j * b_dim1] != 0.) {
+			    temp = *alpha * b[k + j * b_dim1];
+			    i__3 = k - 1;
+			    for (i__ = 1; i__ <= i__3; ++i__) {
+				b[i__ + j * b_dim1] += temp * a[i__ + k * 
+					a_dim1];
+/* L30: */
+			    }
+			    if (nounit) {
+				temp *= a[k + k * a_dim1];
+			    }
+			    b[k + j * b_dim1] = temp;
+			}
+/* L40: */
+		    }
+/* L50: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    for (k = *m; k >= 1; --k) {
+			if (b[k + j * b_dim1] != 0.) {
+			    temp = *alpha * b[k + j * b_dim1];
+			    b[k + j * b_dim1] = temp;
+			    if (nounit) {
+				b[k + j * b_dim1] *= a[k + k * a_dim1];
+			    }
+			    i__2 = *m;
+			    for (i__ = k + 1; i__ <= i__2; ++i__) {
+				b[i__ + j * b_dim1] += temp * a[i__ + k * 
+					a_dim1];
+/* L60: */
+			    }
+			}
+/* L70: */
+		    }
+/* L80: */
+		}
+	    }
+	} else {
+
+/*           Form  B := alpha*A**T*B. */
+
+	    if (upper) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    for (i__ = *m; i__ >= 1; --i__) {
+			temp = b[i__ + j * b_dim1];
+			if (nounit) {
+			    temp *= a[i__ + i__ * a_dim1];
+			}
+			i__2 = i__ - 1;
+			for (k = 1; k <= i__2; ++k) {
+			    temp += a[k + i__ * a_dim1] * b[k + j * b_dim1];
+/* L90: */
+			}
+			b[i__ + j * b_dim1] = *alpha * temp;
+/* L100: */
+		    }
+/* L110: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			temp = b[i__ + j * b_dim1];
+			if (nounit) {
+			    temp *= a[i__ + i__ * a_dim1];
+			}
+			i__3 = *m;
+			for (k = i__ + 1; k <= i__3; ++k) {
+			    temp += a[k + i__ * a_dim1] * b[k + j * b_dim1];
+/* L120: */
+			}
+			b[i__ + j * b_dim1] = *alpha * temp;
+/* L130: */
+		    }
+/* L140: */
+		}
+	    }
+	}
+    } else {
+	if (igraphlsame_(transa, "N")) {
+
+/*           Form  B := alpha*B*A. */
+
+	    if (upper) {
+		for (j = *n; j >= 1; --j) {
+		    temp = *alpha;
+		    if (nounit) {
+			temp *= a[j + j * a_dim1];
+		    }
+		    i__1 = *m;
+		    for (i__ = 1; i__ <= i__1; ++i__) {
+			b[i__ + j * b_dim1] = temp * b[i__ + j * b_dim1];
+/* L150: */
+		    }
+		    i__1 = j - 1;
+		    for (k = 1; k <= i__1; ++k) {
+			if (a[k + j * a_dim1] != 0.) {
+			    temp = *alpha * a[k + j * a_dim1];
+			    i__2 = *m;
+			    for (i__ = 1; i__ <= i__2; ++i__) {
+				b[i__ + j * b_dim1] += temp * b[i__ + k * 
+					b_dim1];
+/* L160: */
+			    }
+			}
+/* L170: */
+		    }
+/* L180: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    temp = *alpha;
+		    if (nounit) {
+			temp *= a[j + j * a_dim1];
+		    }
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			b[i__ + j * b_dim1] = temp * b[i__ + j * b_dim1];
+/* L190: */
+		    }
+		    i__2 = *n;
+		    for (k = j + 1; k <= i__2; ++k) {
+			if (a[k + j * a_dim1] != 0.) {
+			    temp = *alpha * a[k + j * a_dim1];
+			    i__3 = *m;
+			    for (i__ = 1; i__ <= i__3; ++i__) {
+				b[i__ + j * b_dim1] += temp * b[i__ + k * 
+					b_dim1];
+/* L200: */
+			    }
+			}
+/* L210: */
+		    }
+/* L220: */
+		}
+	    }
+	} else {
+
+/*           Form  B := alpha*B*A**T. */
+
+	    if (upper) {
+		i__1 = *n;
+		for (k = 1; k <= i__1; ++k) {
+		    i__2 = k - 1;
+		    for (j = 1; j <= i__2; ++j) {
+			if (a[j + k * a_dim1] != 0.) {
+			    temp = *alpha * a[j + k * a_dim1];
+			    i__3 = *m;
+			    for (i__ = 1; i__ <= i__3; ++i__) {
+				b[i__ + j * b_dim1] += temp * b[i__ + k * 
+					b_dim1];
+/* L230: */
+			    }
+			}
+/* L240: */
+		    }
+		    temp = *alpha;
+		    if (nounit) {
+			temp *= a[k + k * a_dim1];
+		    }
+		    if (temp != 1.) {
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    b[i__ + k * b_dim1] = temp * b[i__ + k * b_dim1];
+/* L250: */
+			}
+		    }
+/* L260: */
+		}
+	    } else {
+		for (k = *n; k >= 1; --k) {
+		    i__1 = *n;
+		    for (j = k + 1; j <= i__1; ++j) {
+			if (a[j + k * a_dim1] != 0.) {
+			    temp = *alpha * a[j + k * a_dim1];
+			    i__2 = *m;
+			    for (i__ = 1; i__ <= i__2; ++i__) {
+				b[i__ + j * b_dim1] += temp * b[i__ + k * 
+					b_dim1];
+/* L270: */
+			    }
+			}
+/* L280: */
+		    }
+		    temp = *alpha;
+		    if (nounit) {
+			temp *= a[k + k * a_dim1];
+		    }
+		    if (temp != 1.) {
+			i__1 = *m;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    b[i__ + k * b_dim1] = temp * b[i__ + k * b_dim1];
+/* L290: */
+			}
+		    }
+/* L300: */
+		}
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DTRMM . */
+
+} /* igraphdtrmm_ */
+
diff --git a/igraph/src/dtrmv.c b/igraph/src/dtrmv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrmv.c
@@ -0,0 +1,335 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdtrmv_(char *uplo, char *trans, char *diag, integer *n, 
+	doublereal *a, integer *lda, doublereal *x, integer *incx)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j, ix, jx, kx, info;
+    doublereal temp;
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical nounit;
+
+
+/*  Purpose   
+    =======   
+
+    DTRMV  performs one of the matrix-vector operations   
+
+       x := A*x,   or   x := A**T*x,   
+
+    where x is an n element vector and  A is an n by n unit, or non-unit,   
+    upper or lower triangular matrix.   
+
+    Arguments   
+    ==========   
+
+    UPLO   - CHARACTER*1.   
+             On entry, UPLO specifies whether the matrix is an upper or   
+             lower triangular matrix as follows:   
+
+                UPLO = 'U' or 'u'   A is an upper triangular matrix.   
+
+                UPLO = 'L' or 'l'   A is a lower triangular matrix.   
+
+             Unchanged on exit.   
+
+    TRANS  - CHARACTER*1.   
+             On entry, TRANS specifies the operation to be performed as   
+             follows:   
+
+                TRANS = 'N' or 'n'   x := A*x.   
+
+                TRANS = 'T' or 't'   x := A**T*x.   
+
+                TRANS = 'C' or 'c'   x := A**T*x.   
+
+             Unchanged on exit.   
+
+    DIAG   - CHARACTER*1.   
+             On entry, DIAG specifies whether or not A is unit   
+             triangular as follows:   
+
+                DIAG = 'U' or 'u'   A is assumed to be unit triangular.   
+
+                DIAG = 'N' or 'n'   A is not assumed to be unit   
+                                    triangular.   
+
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry, N specifies the order of the matrix A.   
+             N must be at least zero.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, n ).   
+             Before entry with  UPLO = 'U' or 'u', the leading n by n   
+             upper triangular part of the array A must contain the upper   
+             triangular matrix and the strictly lower triangular part of   
+             A is not referenced.   
+             Before entry with UPLO = 'L' or 'l', the leading n by n   
+             lower triangular part of the array A must contain the lower   
+             triangular matrix and the strictly upper triangular part of   
+             A is not referenced.   
+             Note that when  DIAG = 'U' or 'u', the diagonal elements of   
+             A are not referenced either, but are assumed to be unity.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program. LDA must be at least   
+             max( 1, n ).   
+             Unchanged on exit.   
+
+    X      - DOUBLE PRECISION array of dimension at least   
+             ( 1 + ( n - 1 )*abs( INCX ) ).   
+             Before entry, the incremented array X must contain the n   
+             element vector x. On exit, X is overwritten with the   
+             tranformed vector x.   
+
+    INCX   - INTEGER.   
+             On entry, INCX specifies the increment for the elements of   
+             X. INCX must not be zero.   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 2 Blas routine.   
+    The vector and matrix arguments are not referenced when N = 0, or M = 0   
+
+    -- Written on 22-October-1986.   
+       Jack Dongarra, Argonne National Lab.   
+       Jeremy Du Croz, Nag Central Office.   
+       Sven Hammarling, Nag Central Office.   
+       Richard Hanson, Sandia National Labs.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --x;
+
+    /* Function Body */
+    info = 0;
+    if (! igraphlsame_(uplo, "U") && ! igraphlsame_(uplo, "L")) {
+	info = 1;
+    } else if (! igraphlsame_(trans, "N") && ! igraphlsame_(trans, 
+	    "T") && ! igraphlsame_(trans, "C")) {
+	info = 2;
+    } else if (! igraphlsame_(diag, "U") && ! igraphlsame_(diag, 
+	    "N")) {
+	info = 3;
+    } else if (*n < 0) {
+	info = 4;
+    } else if (*lda < max(1,*n)) {
+	info = 6;
+    } else if (*incx == 0) {
+	info = 8;
+    }
+    if (info != 0) {
+	igraphxerbla_("DTRMV ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+    nounit = igraphlsame_(diag, "N");
+
+/*     Set up the start point in X if the increment is not unity. This   
+       will be  ( N - 1 )*INCX  too small for descending loops. */
+
+    if (*incx <= 0) {
+	kx = 1 - (*n - 1) * *incx;
+    } else if (*incx != 1) {
+	kx = 1;
+    }
+
+/*     Start the operations. In this version the elements of A are   
+       accessed sequentially with one pass through A. */
+
+    if (igraphlsame_(trans, "N")) {
+
+/*        Form  x := A*x. */
+
+	if (igraphlsame_(uplo, "U")) {
+	    if (*incx == 1) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    if (x[j] != 0.) {
+			temp = x[j];
+			i__2 = j - 1;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    x[i__] += temp * a[i__ + j * a_dim1];
+/* L10: */
+			}
+			if (nounit) {
+			    x[j] *= a[j + j * a_dim1];
+			}
+		    }
+/* L20: */
+		}
+	    } else {
+		jx = kx;
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    if (x[jx] != 0.) {
+			temp = x[jx];
+			ix = kx;
+			i__2 = j - 1;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    x[ix] += temp * a[i__ + j * a_dim1];
+			    ix += *incx;
+/* L30: */
+			}
+			if (nounit) {
+			    x[jx] *= a[j + j * a_dim1];
+			}
+		    }
+		    jx += *incx;
+/* L40: */
+		}
+	    }
+	} else {
+	    if (*incx == 1) {
+		for (j = *n; j >= 1; --j) {
+		    if (x[j] != 0.) {
+			temp = x[j];
+			i__1 = j + 1;
+			for (i__ = *n; i__ >= i__1; --i__) {
+			    x[i__] += temp * a[i__ + j * a_dim1];
+/* L50: */
+			}
+			if (nounit) {
+			    x[j] *= a[j + j * a_dim1];
+			}
+		    }
+/* L60: */
+		}
+	    } else {
+		kx += (*n - 1) * *incx;
+		jx = kx;
+		for (j = *n; j >= 1; --j) {
+		    if (x[jx] != 0.) {
+			temp = x[jx];
+			ix = kx;
+			i__1 = j + 1;
+			for (i__ = *n; i__ >= i__1; --i__) {
+			    x[ix] += temp * a[i__ + j * a_dim1];
+			    ix -= *incx;
+/* L70: */
+			}
+			if (nounit) {
+			    x[jx] *= a[j + j * a_dim1];
+			}
+		    }
+		    jx -= *incx;
+/* L80: */
+		}
+	    }
+	}
+    } else {
+
+/*        Form  x := A**T*x. */
+
+	if (igraphlsame_(uplo, "U")) {
+	    if (*incx == 1) {
+		for (j = *n; j >= 1; --j) {
+		    temp = x[j];
+		    if (nounit) {
+			temp *= a[j + j * a_dim1];
+		    }
+		    for (i__ = j - 1; i__ >= 1; --i__) {
+			temp += a[i__ + j * a_dim1] * x[i__];
+/* L90: */
+		    }
+		    x[j] = temp;
+/* L100: */
+		}
+	    } else {
+		jx = kx + (*n - 1) * *incx;
+		for (j = *n; j >= 1; --j) {
+		    temp = x[jx];
+		    ix = jx;
+		    if (nounit) {
+			temp *= a[j + j * a_dim1];
+		    }
+		    for (i__ = j - 1; i__ >= 1; --i__) {
+			ix -= *incx;
+			temp += a[i__ + j * a_dim1] * x[ix];
+/* L110: */
+		    }
+		    x[jx] = temp;
+		    jx -= *incx;
+/* L120: */
+		}
+	    }
+	} else {
+	    if (*incx == 1) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    temp = x[j];
+		    if (nounit) {
+			temp *= a[j + j * a_dim1];
+		    }
+		    i__2 = *n;
+		    for (i__ = j + 1; i__ <= i__2; ++i__) {
+			temp += a[i__ + j * a_dim1] * x[i__];
+/* L130: */
+		    }
+		    x[j] = temp;
+/* L140: */
+		}
+	    } else {
+		jx = kx;
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    temp = x[jx];
+		    ix = jx;
+		    if (nounit) {
+			temp *= a[j + j * a_dim1];
+		    }
+		    i__2 = *n;
+		    for (i__ = j + 1; i__ <= i__2; ++i__) {
+			ix += *incx;
+			temp += a[i__ + j * a_dim1] * x[ix];
+/* L150: */
+		    }
+		    x[jx] = temp;
+		    jx += *incx;
+/* L160: */
+		}
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DTRMV . */
+
+} /* igraphdtrmv_ */
+
diff --git a/igraph/src/dtrsen.c b/igraph/src/dtrsen.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrsen.c
@@ -0,0 +1,623 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c_n1 = -1;
+
+/* > \brief \b DTRSEN   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DTRSEN + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dtrsen.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dtrsen.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dtrsen.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DTRSEN( JOB, COMPQ, SELECT, N, T, LDT, Q, LDQ, WR, WI,   
+                            M, S, SEP, WORK, LWORK, IWORK, LIWORK, INFO )   
+
+         CHARACTER          COMPQ, JOB   
+         INTEGER            INFO, LDQ, LDT, LIWORK, LWORK, M, N   
+         DOUBLE PRECISION   S, SEP   
+         LOGICAL            SELECT( * )   
+         INTEGER            IWORK( * )   
+         DOUBLE PRECISION   Q( LDQ, * ), T( LDT, * ), WI( * ), WORK( * ),   
+        $                   WR( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DTRSEN reorders the real Schur factorization of a real matrix   
+   > A = Q*T*Q**T, so that a selected cluster of eigenvalues appears in   
+   > the leading diagonal blocks of the upper quasi-triangular matrix T,   
+   > and the leading columns of Q form an orthonormal basis of the   
+   > corresponding right invariant subspace.   
+   >   
+   > Optionally the routine computes the reciprocal condition numbers of   
+   > the cluster of eigenvalues and/or the invariant subspace.   
+   >   
+   > T must be in Schur canonical form (as returned by DHSEQR), that is,   
+   > block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; each   
+   > 2-by-2 diagonal block has its diagonal elements equal and its   
+   > off-diagonal elements of opposite sign.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOB   
+   > \verbatim   
+   >          JOB is CHARACTER*1   
+   >          Specifies whether condition numbers are required for the   
+   >          cluster of eigenvalues (S) or the invariant subspace (SEP):   
+   >          = 'N': none;   
+   >          = 'E': for eigenvalues only (S);   
+   >          = 'V': for invariant subspace only (SEP);   
+   >          = 'B': for both eigenvalues and invariant subspace (S and   
+   >                 SEP).   
+   > \endverbatim   
+   >   
+   > \param[in] COMPQ   
+   > \verbatim   
+   >          COMPQ is CHARACTER*1   
+   >          = 'V': update the matrix Q of Schur vectors;   
+   >          = 'N': do not update Q.   
+   > \endverbatim   
+   >   
+   > \param[in] SELECT   
+   > \verbatim   
+   >          SELECT is LOGICAL array, dimension (N)   
+   >          SELECT specifies the eigenvalues in the selected cluster. To   
+   >          select a real eigenvalue w(j), SELECT(j) must be set to   
+   >          .TRUE.. To select a complex conjugate pair of eigenvalues   
+   >          w(j) and w(j+1), corresponding to a 2-by-2 diagonal block,   
+   >          either SELECT(j) or SELECT(j+1) or both must be set to   
+   >          .TRUE.; a complex conjugate pair of eigenvalues must be   
+   >          either both included in the cluster or both excluded.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix T. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in,out] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,N)   
+   >          On entry, the upper quasi-triangular matrix T, in Schur   
+   >          canonical form.   
+   >          On exit, T is overwritten by the reordered matrix T, again in   
+   >          Schur canonical form, with the selected eigenvalues in the   
+   >          leading diagonal blocks.   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is INTEGER   
+   >          The leading dimension of the array T. LDT >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in,out] Q   
+   > \verbatim   
+   >          Q is DOUBLE PRECISION array, dimension (LDQ,N)   
+   >          On entry, if COMPQ = 'V', the matrix Q of Schur vectors.   
+   >          On exit, if COMPQ = 'V', Q has been postmultiplied by the   
+   >          orthogonal transformation matrix which reorders T; the   
+   >          leading M columns of Q form an orthonormal basis for the   
+   >          specified invariant subspace.   
+   >          If COMPQ = 'N', Q is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDQ   
+   > \verbatim   
+   >          LDQ is INTEGER   
+   >          The leading dimension of the array Q.   
+   >          LDQ >= 1; and if COMPQ = 'V', LDQ >= N.   
+   > \endverbatim   
+   >   
+   > \param[out] WR   
+   > \verbatim   
+   >          WR is DOUBLE PRECISION array, dimension (N)   
+   > \endverbatim   
+   > \param[out] WI   
+   > \verbatim   
+   >          WI is DOUBLE PRECISION array, dimension (N)   
+   >   
+   >          The real and imaginary parts, respectively, of the reordered   
+   >          eigenvalues of T. The eigenvalues are stored in the same   
+   >          order as on the diagonal of T, with WR(i) = T(i,i) and, if   
+   >          T(i:i+1,i:i+1) is a 2-by-2 diagonal block, WI(i) > 0 and   
+   >          WI(i+1) = -WI(i). Note that if a complex eigenvalue is   
+   >          sufficiently ill-conditioned, then its value may differ   
+   >          significantly from its value before reordering.   
+   > \endverbatim   
+   >   
+   > \param[out] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The dimension of the specified invariant subspace.   
+   >          0 < = M <= N.   
+   > \endverbatim   
+   >   
+   > \param[out] S   
+   > \verbatim   
+   >          S is DOUBLE PRECISION   
+   >          If JOB = 'E' or 'B', S is a lower bound on the reciprocal   
+   >          condition number for the selected cluster of eigenvalues.   
+   >          S cannot underestimate the true reciprocal condition number   
+   >          by more than a factor of sqrt(N). If M = 0 or N, S = 1.   
+   >          If JOB = 'N' or 'V', S is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[out] SEP   
+   > \verbatim   
+   >          SEP is DOUBLE PRECISION   
+   >          If JOB = 'V' or 'B', SEP is the estimated reciprocal   
+   >          condition number of the specified invariant subspace. If   
+   >          M = 0 or N, SEP = norm(T).   
+   >          If JOB = 'N' or 'E', SEP is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))   
+   >          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is INTEGER   
+   >          The dimension of the array WORK.   
+   >          If JOB = 'N', LWORK >= max(1,N);   
+   >          if JOB = 'E', LWORK >= max(1,M*(N-M));   
+   >          if JOB = 'V' or 'B', LWORK >= max(1,2*M*(N-M)).   
+   >   
+   >          If LWORK = -1, then a workspace query is assumed; the routine   
+   >          only calculates the optimal size of the WORK array, returns   
+   >          this value as the first entry of the WORK array, and no error   
+   >          message related to LWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (MAX(1,LIWORK))   
+   >          On exit, if INFO = 0, IWORK(1) returns the optimal LIWORK.   
+   > \endverbatim   
+   >   
+   > \param[in] LIWORK   
+   > \verbatim   
+   >          LIWORK is INTEGER   
+   >          The dimension of the array IWORK.   
+   >          If JOB = 'N' or 'E', LIWORK >= 1;   
+   >          if JOB = 'V' or 'B', LIWORK >= max(1,M*(N-M)).   
+   >   
+   >          If LIWORK = -1, then a workspace query is assumed; the   
+   >          routine only calculates the optimal size of the IWORK array,   
+   >          returns this value as the first entry of the IWORK array, and   
+   >          no error message related to LIWORK is issued by XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -i, the i-th argument had an illegal value   
+   >          = 1: reordering of T failed because some eigenvalues are too   
+   >               close to separate (the problem is very ill-conditioned);   
+   >               T may have been partially reordered, and WR and WI   
+   >               contain the eigenvalues in the same order as in T; S and   
+   >               SEP (if requested) are set to zero.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date April 2012   
+
+   > \ingroup doubleOTHERcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  DTRSEN first collects the selected eigenvalues by computing an   
+   >  orthogonal transformation Z to move them to the top left corner of T.   
+   >  In other words, the selected eigenvalues are the eigenvalues of T11   
+   >  in:   
+   >   
+   >          Z**T * T * Z = ( T11 T12 ) n1   
+   >                         (  0  T22 ) n2   
+   >                            n1  n2   
+   >   
+   >  where N = n1+n2 and Z**T means the transpose of Z. The first n1 columns   
+   >  of Z span the specified invariant subspace of T.   
+   >   
+   >  If T has been obtained from the real Schur factorization of a matrix   
+   >  A = Q*T*Q**T, then the reordered real Schur factorization of A is given   
+   >  by A = (Q*Z)*(Z**T*T*Z)*(Q*Z)**T, and the first n1 columns of Q*Z span   
+   >  the corresponding invariant subspace of A.   
+   >   
+   >  The reciprocal condition number of the average of the eigenvalues of   
+   >  T11 may be returned in S. S lies between 0 (very badly conditioned)   
+   >  and 1 (very well conditioned). It is computed as follows. First we   
+   >  compute R so that   
+   >   
+   >                         P = ( I  R ) n1   
+   >                             ( 0  0 ) n2   
+   >                               n1 n2   
+   >   
+   >  is the projector on the invariant subspace associated with T11.   
+   >  R is the solution of the Sylvester equation:   
+   >   
+   >                        T11*R - R*T22 = T12.   
+   >   
+   >  Let F-norm(M) denote the Frobenius-norm of M and 2-norm(M) denote   
+   >  the two-norm of M. Then S is computed as the lower bound   
+   >   
+   >                      (1 + F-norm(R)**2)**(-1/2)   
+   >   
+   >  on the reciprocal of 2-norm(P), the true reciprocal condition number.   
+   >  S cannot underestimate 1 / 2-norm(P) by more than a factor of   
+   >  sqrt(N).   
+   >   
+   >  An approximate error bound for the computed average of the   
+   >  eigenvalues of T11 is   
+   >   
+   >                         EPS * norm(T) / S   
+   >   
+   >  where EPS is the machine precision.   
+   >   
+   >  The reciprocal condition number of the right invariant subspace   
+   >  spanned by the first n1 columns of Z (or of Q*Z) is returned in SEP.   
+   >  SEP is defined as the separation of T11 and T22:   
+   >   
+   >                     sep( T11, T22 ) = sigma-min( C )   
+   >   
+   >  where sigma-min(C) is the smallest singular value of the   
+   >  n1*n2-by-n1*n2 matrix   
+   >   
+   >     C  = kprod( I(n2), T11 ) - kprod( transpose(T22), I(n1) )   
+   >   
+   >  I(m) is an m by m identity matrix, and kprod denotes the Kronecker   
+   >  product. We estimate sigma-min(C) by the reciprocal of an estimate of   
+   >  the 1-norm of inverse(C). The true reciprocal 1-norm of inverse(C)   
+   >  cannot differ from sigma-min(C) by more than a factor of sqrt(n1*n2).   
+   >   
+   >  When SEP is small, small changes in T can cause large changes in   
+   >  the invariant subspace. An approximate bound on the maximum angular   
+   >  error in the computed right invariant subspace is   
+   >   
+   >                      EPS * norm(T) / SEP   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdtrsen_(char *job, char *compq, logical *select, integer 
+	*n, doublereal *t, integer *ldt, doublereal *q, integer *ldq, 
+	doublereal *wr, doublereal *wi, integer *m, doublereal *s, doublereal 
+	*sep, doublereal *work, integer *lwork, integer *iwork, integer *
+	liwork, integer *info)
+{
+    /* System generated locals */
+    integer q_dim1, q_offset, t_dim1, t_offset, i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer k, n1, n2, kk, nn, ks;
+    doublereal est;
+    integer kase;
+    logical pair;
+    integer ierr;
+    logical swap;
+    doublereal scale;
+    extern logical igraphlsame_(char *, char *);
+    integer isave[3], lwmin = 0;
+    logical wantq, wants;
+    doublereal rnorm;
+    extern /* Subroutine */ int igraphdlacn2_(integer *, doublereal *, doublereal *,
+	     integer *, doublereal *, integer *, integer *);
+    extern doublereal igraphdlange_(char *, integer *, integer *, doublereal *, 
+	    integer *, doublereal *);
+    extern /* Subroutine */ int igraphdlacpy_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), 
+	    igraphxerbla_(char *, integer *, ftnlen);
+    logical wantbh;
+    extern /* Subroutine */ int igraphdtrexc_(char *, integer *, doublereal *, 
+	    integer *, doublereal *, integer *, integer *, integer *, 
+	    doublereal *, integer *);
+    integer liwmin;
+    logical wantsp, lquery;
+    extern /* Subroutine */ int igraphdtrsyl_(char *, char *, integer *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *);
+
+
+/*  -- LAPACK computational routine (version 3.4.1) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       April 2012   
+
+
+    =====================================================================   
+
+
+       Decode and test the input parameters   
+
+       Parameter adjustments */
+    --select;
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    q_dim1 = *ldq;
+    q_offset = 1 + q_dim1;
+    q -= q_offset;
+    --wr;
+    --wi;
+    --work;
+    --iwork;
+
+    /* Function Body */
+    wantbh = igraphlsame_(job, "B");
+    wants = igraphlsame_(job, "E") || wantbh;
+    wantsp = igraphlsame_(job, "V") || wantbh;
+    wantq = igraphlsame_(compq, "V");
+
+    *info = 0;
+    lquery = *lwork == -1;
+    if (! igraphlsame_(job, "N") && ! wants && ! wantsp) {
+	*info = -1;
+    } else if (! igraphlsame_(compq, "N") && ! wantq) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*ldt < max(1,*n)) {
+	*info = -6;
+    } else if (*ldq < 1 || wantq && *ldq < *n) {
+	*info = -8;
+    } else {
+
+/*        Set M to the dimension of the specified invariant subspace,   
+          and test LWORK and LIWORK. */
+
+	*m = 0;
+	pair = FALSE_;
+	i__1 = *n;
+	for (k = 1; k <= i__1; ++k) {
+	    if (pair) {
+		pair = FALSE_;
+	    } else {
+		if (k < *n) {
+		    if (t[k + 1 + k * t_dim1] == 0.) {
+			if (select[k]) {
+			    ++(*m);
+			}
+		    } else {
+			pair = TRUE_;
+			if (select[k] || select[k + 1]) {
+			    *m += 2;
+			}
+		    }
+		} else {
+		    if (select[*n]) {
+			++(*m);
+		    }
+		}
+	    }
+/* L10: */
+	}
+
+	n1 = *m;
+	n2 = *n - *m;
+	nn = n1 * n2;
+
+	if (wantsp) {
+/* Computing MAX */
+	    i__1 = 1, i__2 = nn << 1;
+	    lwmin = max(i__1,i__2);
+	    liwmin = max(1,nn);
+	} else if (igraphlsame_(job, "N")) {
+	    lwmin = max(1,*n);
+	    liwmin = 1;
+	} else if (igraphlsame_(job, "E")) {
+	    lwmin = max(1,nn);
+	    liwmin = 1;
+	}
+
+	if (*lwork < lwmin && ! lquery) {
+	    *info = -15;
+	} else if (*liwork < liwmin && ! lquery) {
+	    *info = -17;
+	}
+    }
+
+    if (*info == 0) {
+	work[1] = (doublereal) lwmin;
+	iwork[1] = liwmin;
+    }
+
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DTRSEN", &i__1, (ftnlen)6);
+	return 0;
+    } else if (lquery) {
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*m == *n || *m == 0) {
+	if (wants) {
+	    *s = 1.;
+	}
+	if (wantsp) {
+	    *sep = igraphdlange_("1", n, n, &t[t_offset], ldt, &work[1]);
+	}
+	goto L40;
+    }
+
+/*     Collect the selected blocks at the top-left corner of T. */
+
+    ks = 0;
+    pair = FALSE_;
+    i__1 = *n;
+    for (k = 1; k <= i__1; ++k) {
+	if (pair) {
+	    pair = FALSE_;
+	} else {
+	    swap = select[k];
+	    if (k < *n) {
+		if (t[k + 1 + k * t_dim1] != 0.) {
+		    pair = TRUE_;
+		    swap = swap || select[k + 1];
+		}
+	    }
+	    if (swap) {
+		++ks;
+
+/*              Swap the K-th block to position KS. */
+
+		ierr = 0;
+		kk = k;
+		if (k != ks) {
+		    igraphdtrexc_(compq, n, &t[t_offset], ldt, &q[q_offset], ldq, &
+			    kk, &ks, &work[1], &ierr);
+		}
+		if (ierr == 1 || ierr == 2) {
+
+/*                 Blocks too close to swap: exit. */
+
+		    *info = 1;
+		    if (wants) {
+			*s = 0.;
+		    }
+		    if (wantsp) {
+			*sep = 0.;
+		    }
+		    goto L40;
+		}
+		if (pair) {
+		    ++ks;
+		}
+	    }
+	}
+/* L20: */
+    }
+
+    if (wants) {
+
+/*        Solve Sylvester equation for R:   
+
+             T11*R - R*T22 = scale*T12 */
+
+	igraphdlacpy_("F", &n1, &n2, &t[(n1 + 1) * t_dim1 + 1], ldt, &work[1], &n1);
+	igraphdtrsyl_("N", "N", &c_n1, &n1, &n2, &t[t_offset], ldt, &t[n1 + 1 + (n1 
+		+ 1) * t_dim1], ldt, &work[1], &n1, &scale, &ierr);
+
+/*        Estimate the reciprocal of the condition number of the cluster   
+          of eigenvalues. */
+
+	rnorm = igraphdlange_("F", &n1, &n2, &work[1], &n1, &work[1]);
+	if (rnorm == 0.) {
+	    *s = 1.;
+	} else {
+	    *s = scale / (sqrt(scale * scale / rnorm + rnorm) * sqrt(rnorm));
+	}
+    }
+
+    if (wantsp) {
+
+/*        Estimate sep(T11,T22). */
+
+	est = 0.;
+	kase = 0;
+L30:
+	igraphdlacn2_(&nn, &work[nn + 1], &work[1], &iwork[1], &est, &kase, isave);
+	if (kase != 0) {
+	    if (kase == 1) {
+
+/*              Solve  T11*R - R*T22 = scale*X. */
+
+		igraphdtrsyl_("N", "N", &c_n1, &n1, &n2, &t[t_offset], ldt, &t[n1 + 
+			1 + (n1 + 1) * t_dim1], ldt, &work[1], &n1, &scale, &
+			ierr);
+	    } else {
+
+/*              Solve T11**T*R - R*T22**T = scale*X. */
+
+		igraphdtrsyl_("T", "T", &c_n1, &n1, &n2, &t[t_offset], ldt, &t[n1 + 
+			1 + (n1 + 1) * t_dim1], ldt, &work[1], &n1, &scale, &
+			ierr);
+	    }
+	    goto L30;
+	}
+
+	*sep = scale / est;
+    }
+
+L40:
+
+/*     Store the output eigenvalues in WR and WI. */
+
+    i__1 = *n;
+    for (k = 1; k <= i__1; ++k) {
+	wr[k] = t[k + k * t_dim1];
+	wi[k] = 0.;
+/* L50: */
+    }
+    i__1 = *n - 1;
+    for (k = 1; k <= i__1; ++k) {
+	if (t[k + 1 + k * t_dim1] != 0.) {
+	    wi[k] = sqrt((d__1 = t[k + (k + 1) * t_dim1], abs(d__1))) * sqrt((
+		    d__2 = t[k + 1 + k * t_dim1], abs(d__2)));
+	    wi[k + 1] = -wi[k];
+	}
+/* L60: */
+    }
+
+    work[1] = (doublereal) lwmin;
+    iwork[1] = liwmin;
+
+    return 0;
+
+/*     End of DTRSEN */
+
+} /* igraphdtrsen_ */
+
diff --git a/igraph/src/dtrsm.c b/igraph/src/dtrsm.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrsm.c
@@ -0,0 +1,479 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdtrsm_(char *side, char *uplo, char *transa, char *diag, 
+	integer *m, integer *n, doublereal *alpha, doublereal *a, integer *
+	lda, doublereal *b, integer *ldb)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, i__1, i__2, i__3;
+
+    /* Local variables */
+    integer i__, j, k, info;
+    doublereal temp;
+    logical lside;
+    extern logical igraphlsame_(char *, char *);
+    integer nrowa;
+    logical upper;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical nounit;
+
+
+/*  Purpose   
+    =======   
+
+    DTRSM  solves one of the matrix equations   
+
+       op( A )*X = alpha*B,   or   X*op( A ) = alpha*B,   
+
+    where alpha is a scalar, X and B are m by n matrices, A is a unit, or   
+    non-unit,  upper or lower triangular matrix  and  op( A )  is one  of   
+
+       op( A ) = A   or   op( A ) = A**T.   
+
+    The matrix X is overwritten on B.   
+
+    Arguments   
+    ==========   
+
+    SIDE   - CHARACTER*1.   
+             On entry, SIDE specifies whether op( A ) appears on the left   
+             or right of X as follows:   
+
+                SIDE = 'L' or 'l'   op( A )*X = alpha*B.   
+
+                SIDE = 'R' or 'r'   X*op( A ) = alpha*B.   
+
+             Unchanged on exit.   
+
+    UPLO   - CHARACTER*1.   
+             On entry, UPLO specifies whether the matrix A is an upper or   
+             lower triangular matrix as follows:   
+
+                UPLO = 'U' or 'u'   A is an upper triangular matrix.   
+
+                UPLO = 'L' or 'l'   A is a lower triangular matrix.   
+
+             Unchanged on exit.   
+
+    TRANSA - CHARACTER*1.   
+             On entry, TRANSA specifies the form of op( A ) to be used in   
+             the matrix multiplication as follows:   
+
+                TRANSA = 'N' or 'n'   op( A ) = A.   
+
+                TRANSA = 'T' or 't'   op( A ) = A**T.   
+
+                TRANSA = 'C' or 'c'   op( A ) = A**T.   
+
+             Unchanged on exit.   
+
+    DIAG   - CHARACTER*1.   
+             On entry, DIAG specifies whether or not A is unit triangular   
+             as follows:   
+
+                DIAG = 'U' or 'u'   A is assumed to be unit triangular.   
+
+                DIAG = 'N' or 'n'   A is not assumed to be unit   
+                                    triangular.   
+
+             Unchanged on exit.   
+
+    M      - INTEGER.   
+             On entry, M specifies the number of rows of B. M must be at   
+             least zero.   
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry, N specifies the number of columns of B.  N must be   
+             at least zero.   
+             Unchanged on exit.   
+
+    ALPHA  - DOUBLE PRECISION.   
+             On entry,  ALPHA specifies the scalar  alpha. When  alpha is   
+             zero then  A is not referenced and  B need not be set before   
+             entry.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, k ), where k is m   
+             when  SIDE = 'L' or 'l'  and is  n  when  SIDE = 'R' or 'r'.   
+             Before entry  with  UPLO = 'U' or 'u',  the  leading  k by k   
+             upper triangular part of the array  A must contain the upper   
+             triangular matrix  and the strictly lower triangular part of   
+             A is not referenced.   
+             Before entry  with  UPLO = 'L' or 'l',  the  leading  k by k   
+             lower triangular part of the array  A must contain the lower   
+             triangular matrix  and the strictly upper triangular part of   
+             A is not referenced.   
+             Note that when  DIAG = 'U' or 'u',  the diagonal elements of   
+             A  are not referenced either,  but are assumed to be  unity.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program.  When  SIDE = 'L' or 'l'  then   
+             LDA  must be at least  max( 1, m ),  when  SIDE = 'R' or 'r'   
+             then LDA must be at least max( 1, n ).   
+             Unchanged on exit.   
+
+    B      - DOUBLE PRECISION array of DIMENSION ( LDB, n ).   
+             Before entry,  the leading  m by n part of the array  B must   
+             contain  the  right-hand  side  matrix  B,  and  on exit  is   
+             overwritten by the solution matrix  X.   
+
+    LDB    - INTEGER.   
+             On entry, LDB specifies the first dimension of B as declared   
+             in  the  calling  (sub)  program.   LDB  must  be  at  least   
+             max( 1, m ).   
+             Unchanged on exit.   
+
+    Further Details   
+    ===============   
+
+    Level 3 Blas routine.   
+
+
+    -- Written on 8-February-1989.   
+       Jack Dongarra, Argonne National Laboratory.   
+       Iain Duff, AERE Harwell.   
+       Jeremy Du Croz, Numerical Algorithms Group Ltd.   
+       Sven Hammarling, Numerical Algorithms Group Ltd.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+
+    /* Function Body */
+    lside = igraphlsame_(side, "L");
+    if (lside) {
+	nrowa = *m;
+    } else {
+	nrowa = *n;
+    }
+    nounit = igraphlsame_(diag, "N");
+    upper = igraphlsame_(uplo, "U");
+
+    info = 0;
+    if (! lside && ! igraphlsame_(side, "R")) {
+	info = 1;
+    } else if (! upper && ! igraphlsame_(uplo, "L")) {
+	info = 2;
+    } else if (! igraphlsame_(transa, "N") && ! igraphlsame_(transa,
+	     "T") && ! igraphlsame_(transa, "C")) {
+	info = 3;
+    } else if (! igraphlsame_(diag, "U") && ! igraphlsame_(diag, 
+	    "N")) {
+	info = 4;
+    } else if (*m < 0) {
+	info = 5;
+    } else if (*n < 0) {
+	info = 6;
+    } else if (*lda < max(1,nrowa)) {
+	info = 9;
+    } else if (*ldb < max(1,*m)) {
+	info = 11;
+    }
+    if (info != 0) {
+	igraphxerbla_("DTRSM ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*m == 0 || *n == 0) {
+	return 0;
+    }
+
+/*     And when  alpha.eq.zero. */
+
+    if (*alpha == 0.) {
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__2 = *m;
+	    for (i__ = 1; i__ <= i__2; ++i__) {
+		b[i__ + j * b_dim1] = 0.;
+/* L10: */
+	    }
+/* L20: */
+	}
+	return 0;
+    }
+
+/*     Start the operations. */
+
+    if (lside) {
+	if (igraphlsame_(transa, "N")) {
+
+/*           Form  B := alpha*inv( A )*B. */
+
+	    if (upper) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    if (*alpha != 1.) {
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    b[i__ + j * b_dim1] = *alpha * b[i__ + j * b_dim1]
+				    ;
+/* L30: */
+			}
+		    }
+		    for (k = *m; k >= 1; --k) {
+			if (b[k + j * b_dim1] != 0.) {
+			    if (nounit) {
+				b[k + j * b_dim1] /= a[k + k * a_dim1];
+			    }
+			    i__2 = k - 1;
+			    for (i__ = 1; i__ <= i__2; ++i__) {
+				b[i__ + j * b_dim1] -= b[k + j * b_dim1] * a[
+					i__ + k * a_dim1];
+/* L40: */
+			    }
+			}
+/* L50: */
+		    }
+/* L60: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    if (*alpha != 1.) {
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    b[i__ + j * b_dim1] = *alpha * b[i__ + j * b_dim1]
+				    ;
+/* L70: */
+			}
+		    }
+		    i__2 = *m;
+		    for (k = 1; k <= i__2; ++k) {
+			if (b[k + j * b_dim1] != 0.) {
+			    if (nounit) {
+				b[k + j * b_dim1] /= a[k + k * a_dim1];
+			    }
+			    i__3 = *m;
+			    for (i__ = k + 1; i__ <= i__3; ++i__) {
+				b[i__ + j * b_dim1] -= b[k + j * b_dim1] * a[
+					i__ + k * a_dim1];
+/* L80: */
+			    }
+			}
+/* L90: */
+		    }
+/* L100: */
+		}
+	    }
+	} else {
+
+/*           Form  B := alpha*inv( A**T )*B. */
+
+	    if (upper) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    i__2 = *m;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			temp = *alpha * b[i__ + j * b_dim1];
+			i__3 = i__ - 1;
+			for (k = 1; k <= i__3; ++k) {
+			    temp -= a[k + i__ * a_dim1] * b[k + j * b_dim1];
+/* L110: */
+			}
+			if (nounit) {
+			    temp /= a[i__ + i__ * a_dim1];
+			}
+			b[i__ + j * b_dim1] = temp;
+/* L120: */
+		    }
+/* L130: */
+		}
+	    } else {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    for (i__ = *m; i__ >= 1; --i__) {
+			temp = *alpha * b[i__ + j * b_dim1];
+			i__2 = *m;
+			for (k = i__ + 1; k <= i__2; ++k) {
+			    temp -= a[k + i__ * a_dim1] * b[k + j * b_dim1];
+/* L140: */
+			}
+			if (nounit) {
+			    temp /= a[i__ + i__ * a_dim1];
+			}
+			b[i__ + j * b_dim1] = temp;
+/* L150: */
+		    }
+/* L160: */
+		}
+	    }
+	}
+    } else {
+	if (igraphlsame_(transa, "N")) {
+
+/*           Form  B := alpha*B*inv( A ). */
+
+	    if (upper) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    if (*alpha != 1.) {
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    b[i__ + j * b_dim1] = *alpha * b[i__ + j * b_dim1]
+				    ;
+/* L170: */
+			}
+		    }
+		    i__2 = j - 1;
+		    for (k = 1; k <= i__2; ++k) {
+			if (a[k + j * a_dim1] != 0.) {
+			    i__3 = *m;
+			    for (i__ = 1; i__ <= i__3; ++i__) {
+				b[i__ + j * b_dim1] -= a[k + j * a_dim1] * b[
+					i__ + k * b_dim1];
+/* L180: */
+			    }
+			}
+/* L190: */
+		    }
+		    if (nounit) {
+			temp = 1. / a[j + j * a_dim1];
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    b[i__ + j * b_dim1] = temp * b[i__ + j * b_dim1];
+/* L200: */
+			}
+		    }
+/* L210: */
+		}
+	    } else {
+		for (j = *n; j >= 1; --j) {
+		    if (*alpha != 1.) {
+			i__1 = *m;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    b[i__ + j * b_dim1] = *alpha * b[i__ + j * b_dim1]
+				    ;
+/* L220: */
+			}
+		    }
+		    i__1 = *n;
+		    for (k = j + 1; k <= i__1; ++k) {
+			if (a[k + j * a_dim1] != 0.) {
+			    i__2 = *m;
+			    for (i__ = 1; i__ <= i__2; ++i__) {
+				b[i__ + j * b_dim1] -= a[k + j * a_dim1] * b[
+					i__ + k * b_dim1];
+/* L230: */
+			    }
+			}
+/* L240: */
+		    }
+		    if (nounit) {
+			temp = 1. / a[j + j * a_dim1];
+			i__1 = *m;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    b[i__ + j * b_dim1] = temp * b[i__ + j * b_dim1];
+/* L250: */
+			}
+		    }
+/* L260: */
+		}
+	    }
+	} else {
+
+/*           Form  B := alpha*B*inv( A**T ). */
+
+	    if (upper) {
+		for (k = *n; k >= 1; --k) {
+		    if (nounit) {
+			temp = 1. / a[k + k * a_dim1];
+			i__1 = *m;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    b[i__ + k * b_dim1] = temp * b[i__ + k * b_dim1];
+/* L270: */
+			}
+		    }
+		    i__1 = k - 1;
+		    for (j = 1; j <= i__1; ++j) {
+			if (a[j + k * a_dim1] != 0.) {
+			    temp = a[j + k * a_dim1];
+			    i__2 = *m;
+			    for (i__ = 1; i__ <= i__2; ++i__) {
+				b[i__ + j * b_dim1] -= temp * b[i__ + k * 
+					b_dim1];
+/* L280: */
+			    }
+			}
+/* L290: */
+		    }
+		    if (*alpha != 1.) {
+			i__1 = *m;
+			for (i__ = 1; i__ <= i__1; ++i__) {
+			    b[i__ + k * b_dim1] = *alpha * b[i__ + k * b_dim1]
+				    ;
+/* L300: */
+			}
+		    }
+/* L310: */
+		}
+	    } else {
+		i__1 = *n;
+		for (k = 1; k <= i__1; ++k) {
+		    if (nounit) {
+			temp = 1. / a[k + k * a_dim1];
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    b[i__ + k * b_dim1] = temp * b[i__ + k * b_dim1];
+/* L320: */
+			}
+		    }
+		    i__2 = *n;
+		    for (j = k + 1; j <= i__2; ++j) {
+			if (a[j + k * a_dim1] != 0.) {
+			    temp = a[j + k * a_dim1];
+			    i__3 = *m;
+			    for (i__ = 1; i__ <= i__3; ++i__) {
+				b[i__ + j * b_dim1] -= temp * b[i__ + k * 
+					b_dim1];
+/* L330: */
+			    }
+			}
+/* L340: */
+		    }
+		    if (*alpha != 1.) {
+			i__2 = *m;
+			for (i__ = 1; i__ <= i__2; ++i__) {
+			    b[i__ + k * b_dim1] = *alpha * b[i__ + k * b_dim1]
+				    ;
+/* L350: */
+			}
+		    }
+/* L360: */
+		}
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DTRSM . */
+
+} /* igraphdtrsm_ */
+
diff --git a/igraph/src/dtrsna.c b/igraph/src/dtrsna.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrsna.c
@@ -0,0 +1,696 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static logical c_true = TRUE_;
+static logical c_false = FALSE_;
+
+/* > \brief \b DTRSNA   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DTRSNA + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dtrsna.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dtrsna.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dtrsna.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DTRSNA( JOB, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR,   
+                            LDVR, S, SEP, MM, M, WORK, LDWORK, IWORK,   
+                            INFO )   
+
+         CHARACTER          HOWMNY, JOB   
+         INTEGER            INFO, LDT, LDVL, LDVR, LDWORK, M, MM, N   
+         LOGICAL            SELECT( * )   
+         INTEGER            IWORK( * )   
+         DOUBLE PRECISION   S( * ), SEP( * ), T( LDT, * ), VL( LDVL, * ),   
+        $                   VR( LDVR, * ), WORK( LDWORK, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DTRSNA estimates reciprocal condition numbers for specified   
+   > eigenvalues and/or right eigenvectors of a real upper   
+   > quasi-triangular matrix T (or of any matrix Q*T*Q**T with Q   
+   > orthogonal).   
+   >   
+   > T must be in Schur canonical form (as returned by DHSEQR), that is,   
+   > block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; each   
+   > 2-by-2 diagonal block has its diagonal elements equal and its   
+   > off-diagonal elements of opposite sign.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] JOB   
+   > \verbatim   
+   >          JOB is CHARACTER*1   
+   >          Specifies whether condition numbers are required for   
+   >          eigenvalues (S) or eigenvectors (SEP):   
+   >          = 'E': for eigenvalues only (S);   
+   >          = 'V': for eigenvectors only (SEP);   
+   >          = 'B': for both eigenvalues and eigenvectors (S and SEP).   
+   > \endverbatim   
+   >   
+   > \param[in] HOWMNY   
+   > \verbatim   
+   >          HOWMNY is CHARACTER*1   
+   >          = 'A': compute condition numbers for all eigenpairs;   
+   >          = 'S': compute condition numbers for selected eigenpairs   
+   >                 specified by the array SELECT.   
+   > \endverbatim   
+   >   
+   > \param[in] SELECT   
+   > \verbatim   
+   >          SELECT is LOGICAL array, dimension (N)   
+   >          If HOWMNY = 'S', SELECT specifies the eigenpairs for which   
+   >          condition numbers are required. To select condition numbers   
+   >          for the eigenpair corresponding to a real eigenvalue w(j),   
+   >          SELECT(j) must be set to .TRUE.. To select condition numbers   
+   >          corresponding to a complex conjugate pair of eigenvalues w(j)   
+   >          and w(j+1), either SELECT(j) or SELECT(j+1) or both, must be   
+   >          set to .TRUE..   
+   >          If HOWMNY = 'A', SELECT is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix T. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] T   
+   > \verbatim   
+   >          T is DOUBLE PRECISION array, dimension (LDT,N)   
+   >          The upper quasi-triangular matrix T, in Schur canonical form.   
+   > \endverbatim   
+   >   
+   > \param[in] LDT   
+   > \verbatim   
+   >          LDT is INTEGER   
+   >          The leading dimension of the array T. LDT >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in] VL   
+   > \verbatim   
+   >          VL is DOUBLE PRECISION array, dimension (LDVL,M)   
+   >          If JOB = 'E' or 'B', VL must contain left eigenvectors of T   
+   >          (or of any Q*T*Q**T with Q orthogonal), corresponding to the   
+   >          eigenpairs specified by HOWMNY and SELECT. The eigenvectors   
+   >          must be stored in consecutive columns of VL, as returned by   
+   >          DHSEIN or DTREVC.   
+   >          If JOB = 'V', VL is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDVL   
+   > \verbatim   
+   >          LDVL is INTEGER   
+   >          The leading dimension of the array VL.   
+   >          LDVL >= 1; and if JOB = 'E' or 'B', LDVL >= N.   
+   > \endverbatim   
+   >   
+   > \param[in] VR   
+   > \verbatim   
+   >          VR is DOUBLE PRECISION array, dimension (LDVR,M)   
+   >          If JOB = 'E' or 'B', VR must contain right eigenvectors of T   
+   >          (or of any Q*T*Q**T with Q orthogonal), corresponding to the   
+   >          eigenpairs specified by HOWMNY and SELECT. The eigenvectors   
+   >          must be stored in consecutive columns of VR, as returned by   
+   >          DHSEIN or DTREVC.   
+   >          If JOB = 'V', VR is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDVR   
+   > \verbatim   
+   >          LDVR is INTEGER   
+   >          The leading dimension of the array VR.   
+   >          LDVR >= 1; and if JOB = 'E' or 'B', LDVR >= N.   
+   > \endverbatim   
+   >   
+   > \param[out] S   
+   > \verbatim   
+   >          S is DOUBLE PRECISION array, dimension (MM)   
+   >          If JOB = 'E' or 'B', the reciprocal condition numbers of the   
+   >          selected eigenvalues, stored in consecutive elements of the   
+   >          array. For a complex conjugate pair of eigenvalues two   
+   >          consecutive elements of S are set to the same value. Thus   
+   >          S(j), SEP(j), and the j-th columns of VL and VR all   
+   >          correspond to the same eigenpair (but not in general the   
+   >          j-th eigenpair, unless all eigenpairs are selected).   
+   >          If JOB = 'V', S is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[out] SEP   
+   > \verbatim   
+   >          SEP is DOUBLE PRECISION array, dimension (MM)   
+   >          If JOB = 'V' or 'B', the estimated reciprocal condition   
+   >          numbers of the selected eigenvectors, stored in consecutive   
+   >          elements of the array. For a complex eigenvector two   
+   >          consecutive elements of SEP are set to the same value. If   
+   >          the eigenvalues cannot be reordered to compute SEP(j), SEP(j)   
+   >          is set to 0; this can only occur when the true value would be   
+   >          very small anyway.   
+   >          If JOB = 'E', SEP is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] MM   
+   > \verbatim   
+   >          MM is INTEGER   
+   >          The number of elements in the arrays S (if JOB = 'E' or 'B')   
+   >           and/or SEP (if JOB = 'V' or 'B'). MM >= M.   
+   > \endverbatim   
+   >   
+   > \param[out] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of elements of the arrays S and/or SEP actually   
+   >          used to store the estimated condition numbers.   
+   >          If HOWMNY = 'A', M is set to N.   
+   > \endverbatim   
+   >   
+   > \param[out] WORK   
+   > \verbatim   
+   >          WORK is DOUBLE PRECISION array, dimension (LDWORK,N+6)   
+   >          If JOB = 'E', WORK is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[in] LDWORK   
+   > \verbatim   
+   >          LDWORK is INTEGER   
+   >          The leading dimension of the array WORK.   
+   >          LDWORK >= 1; and if JOB = 'V' or 'B', LDWORK >= N.   
+   > \endverbatim   
+   >   
+   > \param[out] IWORK   
+   > \verbatim   
+   >          IWORK is INTEGER array, dimension (2*(N-1))   
+   >          If JOB = 'E', IWORK is not referenced.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -i, the i-th argument had an illegal value   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleOTHERcomputational   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The reciprocal of the condition number of an eigenvalue lambda is   
+   >  defined as   
+   >   
+   >          S(lambda) = |v**T*u| / (norm(u)*norm(v))   
+   >   
+   >  where u and v are the right and left eigenvectors of T corresponding   
+   >  to lambda; v**T denotes the transpose of v, and norm(u)   
+   >  denotes the Euclidean norm. These reciprocal condition numbers always   
+   >  lie between zero (very badly conditioned) and one (very well   
+   >  conditioned). If n = 1, S(lambda) is defined to be 1.   
+   >   
+   >  An approximate error bound for a computed eigenvalue W(i) is given by   
+   >   
+   >                      EPS * norm(T) / S(i)   
+   >   
+   >  where EPS is the machine precision.   
+   >   
+   >  The reciprocal of the condition number of the right eigenvector u   
+   >  corresponding to lambda is defined as follows. Suppose   
+   >   
+   >              T = ( lambda  c  )   
+   >                  (   0    T22 )   
+   >   
+   >  Then the reciprocal condition number is   
+   >   
+   >          SEP( lambda, T22 ) = sigma-min( T22 - lambda*I )   
+   >   
+   >  where sigma-min denotes the smallest singular value. We approximate   
+   >  the smallest singular value by the reciprocal of an estimate of the   
+   >  one-norm of the inverse of T22 - lambda*I. If n = 1, SEP(1) is   
+   >  defined to be abs(T(1,1)).   
+   >   
+   >  An approximate error bound for a computed right eigenvector VR(i)   
+   >  is given by   
+   >   
+   >                      EPS * norm(T) / SEP(i)   
+   > \endverbatim   
+   >   
+    =====================================================================   
+   Subroutine */ int igraphdtrsna_(char *job, char *howmny, logical *select, 
+	integer *n, doublereal *t, integer *ldt, doublereal *vl, integer *
+	ldvl, doublereal *vr, integer *ldvr, doublereal *s, doublereal *sep, 
+	integer *mm, integer *m, doublereal *work, integer *ldwork, integer *
+	iwork, integer *info)
+{
+    /* System generated locals */
+    integer t_dim1, t_offset, vl_dim1, vl_offset, vr_dim1, vr_offset, 
+	    work_dim1, work_offset, i__1, i__2;
+    doublereal d__1, d__2;
+
+    /* Builtin functions */
+    double sqrt(doublereal);
+
+    /* Local variables */
+    integer i__, j, k, n2;
+    doublereal cs;
+    integer nn, ks;
+    doublereal sn, mu, eps, est;
+    integer kase;
+    doublereal cond;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    logical pair;
+    integer ierr;
+    doublereal dumm, prod;
+    integer ifst;
+    doublereal lnrm;
+    integer ilst;
+    doublereal rnrm;
+    extern doublereal igraphdnrm2_(integer *, doublereal *, integer *);
+    doublereal prod1, prod2, scale, delta;
+    extern logical igraphlsame_(char *, char *);
+    integer isave[3];
+    logical wants;
+    doublereal dummy[1];
+    extern /* Subroutine */ int igraphdlacn2_(integer *, doublereal *, doublereal *,
+	     integer *, doublereal *, integer *, integer *);
+    extern doublereal igraphdlapy2_(doublereal *, doublereal *);
+    extern /* Subroutine */ int igraphdlabad_(doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *);
+    extern /* Subroutine */ int igraphdlacpy_(char *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *), 
+	    igraphxerbla_(char *, integer *, ftnlen);
+    doublereal bignum;
+    logical wantbh;
+    extern /* Subroutine */ int igraphdlaqtr_(logical *, logical *, integer *, 
+	    doublereal *, integer *, doublereal *, doublereal *, doublereal *,
+	     doublereal *, doublereal *, integer *), igraphdtrexc_(char *, integer *
+	    , doublereal *, integer *, doublereal *, integer *, integer *, 
+	    integer *, doublereal *, integer *);
+    logical somcon;
+    doublereal smlnum;
+    logical wantsp;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Decode and test the input parameters   
+
+       Parameter adjustments */
+    --select;
+    t_dim1 = *ldt;
+    t_offset = 1 + t_dim1;
+    t -= t_offset;
+    vl_dim1 = *ldvl;
+    vl_offset = 1 + vl_dim1;
+    vl -= vl_offset;
+    vr_dim1 = *ldvr;
+    vr_offset = 1 + vr_dim1;
+    vr -= vr_offset;
+    --s;
+    --sep;
+    work_dim1 = *ldwork;
+    work_offset = 1 + work_dim1;
+    work -= work_offset;
+    --iwork;
+
+    /* Function Body */
+    wantbh = igraphlsame_(job, "B");
+    wants = igraphlsame_(job, "E") || wantbh;
+    wantsp = igraphlsame_(job, "V") || wantbh;
+
+    somcon = igraphlsame_(howmny, "S");
+
+    *info = 0;
+    if (! wants && ! wantsp) {
+	*info = -1;
+    } else if (! igraphlsame_(howmny, "A") && ! somcon) {
+	*info = -2;
+    } else if (*n < 0) {
+	*info = -4;
+    } else if (*ldt < max(1,*n)) {
+	*info = -6;
+    } else if (*ldvl < 1 || wants && *ldvl < *n) {
+	*info = -8;
+    } else if (*ldvr < 1 || wants && *ldvr < *n) {
+	*info = -10;
+    } else {
+
+/*        Set M to the number of eigenpairs for which condition numbers   
+          are required, and test MM. */
+
+	if (somcon) {
+	    *m = 0;
+	    pair = FALSE_;
+	    i__1 = *n;
+	    for (k = 1; k <= i__1; ++k) {
+		if (pair) {
+		    pair = FALSE_;
+		} else {
+		    if (k < *n) {
+			if (t[k + 1 + k * t_dim1] == 0.) {
+			    if (select[k]) {
+				++(*m);
+			    }
+			} else {
+			    pair = TRUE_;
+			    if (select[k] || select[k + 1]) {
+				*m += 2;
+			    }
+			}
+		    } else {
+			if (select[*n]) {
+			    ++(*m);
+			}
+		    }
+		}
+/* L10: */
+	    }
+	} else {
+	    *m = *n;
+	}
+
+	if (*mm < *m) {
+	    *info = -13;
+	} else if (*ldwork < 1 || wantsp && *ldwork < *n) {
+	    *info = -16;
+	}
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DTRSNA", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+    if (*n == 1) {
+	if (somcon) {
+	    if (! select[1]) {
+		return 0;
+	    }
+	}
+	if (wants) {
+	    s[1] = 1.;
+	}
+	if (wantsp) {
+	    sep[1] = (d__1 = t[t_dim1 + 1], abs(d__1));
+	}
+	return 0;
+    }
+
+/*     Get machine constants */
+
+    eps = igraphdlamch_("P");
+    smlnum = igraphdlamch_("S") / eps;
+    bignum = 1. / smlnum;
+    igraphdlabad_(&smlnum, &bignum);
+
+    ks = 0;
+    pair = FALSE_;
+    i__1 = *n;
+    for (k = 1; k <= i__1; ++k) {
+
+/*        Determine whether T(k,k) begins a 1-by-1 or 2-by-2 block. */
+
+	if (pair) {
+	    pair = FALSE_;
+	    goto L60;
+	} else {
+	    if (k < *n) {
+		pair = t[k + 1 + k * t_dim1] != 0.;
+	    }
+	}
+
+/*        Determine whether condition numbers are required for the k-th   
+          eigenpair. */
+
+	if (somcon) {
+	    if (pair) {
+		if (! select[k] && ! select[k + 1]) {
+		    goto L60;
+		}
+	    } else {
+		if (! select[k]) {
+		    goto L60;
+		}
+	    }
+	}
+
+	++ks;
+
+	if (wants) {
+
+/*           Compute the reciprocal condition number of the k-th   
+             eigenvalue. */
+
+	    if (! pair) {
+
+/*              Real eigenvalue. */
+
+		prod = igraphddot_(n, &vr[ks * vr_dim1 + 1], &c__1, &vl[ks * 
+			vl_dim1 + 1], &c__1);
+		rnrm = igraphdnrm2_(n, &vr[ks * vr_dim1 + 1], &c__1);
+		lnrm = igraphdnrm2_(n, &vl[ks * vl_dim1 + 1], &c__1);
+		s[ks] = abs(prod) / (rnrm * lnrm);
+	    } else {
+
+/*              Complex eigenvalue. */
+
+		prod1 = igraphddot_(n, &vr[ks * vr_dim1 + 1], &c__1, &vl[ks * 
+			vl_dim1 + 1], &c__1);
+		prod1 += igraphddot_(n, &vr[(ks + 1) * vr_dim1 + 1], &c__1, &vl[(ks 
+			+ 1) * vl_dim1 + 1], &c__1);
+		prod2 = igraphddot_(n, &vl[ks * vl_dim1 + 1], &c__1, &vr[(ks + 1) * 
+			vr_dim1 + 1], &c__1);
+		prod2 -= igraphddot_(n, &vl[(ks + 1) * vl_dim1 + 1], &c__1, &vr[ks *
+			 vr_dim1 + 1], &c__1);
+		d__1 = igraphdnrm2_(n, &vr[ks * vr_dim1 + 1], &c__1);
+		d__2 = igraphdnrm2_(n, &vr[(ks + 1) * vr_dim1 + 1], &c__1);
+		rnrm = igraphdlapy2_(&d__1, &d__2);
+		d__1 = igraphdnrm2_(n, &vl[ks * vl_dim1 + 1], &c__1);
+		d__2 = igraphdnrm2_(n, &vl[(ks + 1) * vl_dim1 + 1], &c__1);
+		lnrm = igraphdlapy2_(&d__1, &d__2);
+		cond = igraphdlapy2_(&prod1, &prod2) / (rnrm * lnrm);
+		s[ks] = cond;
+		s[ks + 1] = cond;
+	    }
+	}
+
+	if (wantsp) {
+
+/*           Estimate the reciprocal condition number of the k-th   
+             eigenvector.   
+
+             Copy the matrix T to the array WORK and swap the diagonal   
+             block beginning at T(k,k) to the (1,1) position. */
+
+	    igraphdlacpy_("Full", n, n, &t[t_offset], ldt, &work[work_offset], 
+		    ldwork);
+	    ifst = k;
+	    ilst = 1;
+	    igraphdtrexc_("No Q", n, &work[work_offset], ldwork, dummy, &c__1, &
+		    ifst, &ilst, &work[(*n + 1) * work_dim1 + 1], &ierr);
+
+	    if (ierr == 1 || ierr == 2) {
+
+/*              Could not swap because blocks not well separated */
+
+		scale = 1.;
+		est = bignum;
+	    } else {
+
+/*              Reordering successful */
+
+		if (work[work_dim1 + 2] == 0.) {
+
+/*                 Form C = T22 - lambda*I in WORK(2:N,2:N). */
+
+		    i__2 = *n;
+		    for (i__ = 2; i__ <= i__2; ++i__) {
+			work[i__ + i__ * work_dim1] -= work[work_dim1 + 1];
+/* L20: */
+		    }
+		    n2 = 1;
+		    nn = *n - 1;
+		} else {
+
+/*                 Triangularize the 2 by 2 block by unitary   
+                   transformation U = [  cs   i*ss ]   
+                                      [ i*ss   cs  ].   
+                   such that the (1,1) position of WORK is complex   
+                   eigenvalue lambda with positive imaginary part. (2,2)   
+                   position of WORK is the complex eigenvalue lambda   
+                   with negative imaginary  part. */
+
+		    mu = sqrt((d__1 = work[(work_dim1 << 1) + 1], abs(d__1))) 
+			    * sqrt((d__2 = work[work_dim1 + 2], abs(d__2)));
+		    delta = igraphdlapy2_(&mu, &work[work_dim1 + 2]);
+		    cs = mu / delta;
+		    sn = -work[work_dim1 + 2] / delta;
+
+/*                 Form   
+
+                   C**T = WORK(2:N,2:N) + i*[rwork(1) ..... rwork(n-1) ]   
+                                            [   mu                     ]   
+                                            [         ..               ]   
+                                            [             ..           ]   
+                                            [                  mu      ]   
+                   where C**T is transpose of matrix C,   
+                   and RWORK is stored starting in the N+1-st column of   
+                   WORK. */
+
+		    i__2 = *n;
+		    for (j = 3; j <= i__2; ++j) {
+			work[j * work_dim1 + 2] = cs * work[j * work_dim1 + 2]
+				;
+			work[j + j * work_dim1] -= work[work_dim1 + 1];
+/* L30: */
+		    }
+		    work[(work_dim1 << 1) + 2] = 0.;
+
+		    work[(*n + 1) * work_dim1 + 1] = mu * 2.;
+		    i__2 = *n - 1;
+		    for (i__ = 2; i__ <= i__2; ++i__) {
+			work[i__ + (*n + 1) * work_dim1] = sn * work[(i__ + 1)
+				 * work_dim1 + 1];
+/* L40: */
+		    }
+		    n2 = 2;
+		    nn = *n - 1 << 1;
+		}
+
+/*              Estimate norm(inv(C**T)) */
+
+		est = 0.;
+		kase = 0;
+L50:
+		igraphdlacn2_(&nn, &work[(*n + 2) * work_dim1 + 1], &work[(*n + 4) *
+			 work_dim1 + 1], &iwork[1], &est, &kase, isave);
+		if (kase != 0) {
+		    if (kase == 1) {
+			if (n2 == 1) {
+
+/*                       Real eigenvalue: solve C**T*x = scale*c. */
+
+			    i__2 = *n - 1;
+			    igraphdlaqtr_(&c_true, &c_true, &i__2, &work[(work_dim1 
+				    << 1) + 2], ldwork, dummy, &dumm, &scale, 
+				    &work[(*n + 4) * work_dim1 + 1], &work[(*
+				    n + 6) * work_dim1 + 1], &ierr);
+			} else {
+
+/*                       Complex eigenvalue: solve   
+                         C**T*(p+iq) = scale*(c+id) in real arithmetic. */
+
+			    i__2 = *n - 1;
+			    igraphdlaqtr_(&c_true, &c_false, &i__2, &work[(
+				    work_dim1 << 1) + 2], ldwork, &work[(*n + 
+				    1) * work_dim1 + 1], &mu, &scale, &work[(*
+				    n + 4) * work_dim1 + 1], &work[(*n + 6) * 
+				    work_dim1 + 1], &ierr);
+			}
+		    } else {
+			if (n2 == 1) {
+
+/*                       Real eigenvalue: solve C*x = scale*c. */
+
+			    i__2 = *n - 1;
+			    igraphdlaqtr_(&c_false, &c_true, &i__2, &work[(
+				    work_dim1 << 1) + 2], ldwork, dummy, &
+				    dumm, &scale, &work[(*n + 4) * work_dim1 
+				    + 1], &work[(*n + 6) * work_dim1 + 1], &
+				    ierr);
+			} else {
+
+/*                       Complex eigenvalue: solve   
+                         C*(p+iq) = scale*(c+id) in real arithmetic. */
+
+			    i__2 = *n - 1;
+			    igraphdlaqtr_(&c_false, &c_false, &i__2, &work[(
+				    work_dim1 << 1) + 2], ldwork, &work[(*n + 
+				    1) * work_dim1 + 1], &mu, &scale, &work[(*
+				    n + 4) * work_dim1 + 1], &work[(*n + 6) * 
+				    work_dim1 + 1], &ierr);
+
+			}
+		    }
+
+		    goto L50;
+		}
+	    }
+
+	    sep[ks] = scale / max(est,smlnum);
+	    if (pair) {
+		sep[ks + 1] = sep[ks];
+	    }
+	}
+
+	if (pair) {
+	    ++ks;
+	}
+
+L60:
+	;
+    }
+    return 0;
+
+/*     End of DTRSNA */
+
+} /* igraphdtrsna_ */
+
diff --git a/igraph/src/dtrsv.c b/igraph/src/dtrsv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrsv.c
@@ -0,0 +1,335 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphdtrsv_(char *uplo, char *trans, char *diag, integer *n, 
+	doublereal *a, integer *lda, doublereal *x, integer *incx)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, i__1, i__2;
+
+    /* Local variables */
+    integer i__, j, ix, jx, kx, info;
+    doublereal temp;
+    extern logical igraphlsame_(char *, char *);
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    logical nounit;
+
+
+/*  Purpose   
+    =======   
+
+    DTRSV  solves one of the systems of equations   
+
+       A*x = b,   or   A**T*x = b,   
+
+    where b and x are n element vectors and A is an n by n unit, or   
+    non-unit, upper or lower triangular matrix.   
+
+    No test for singularity or near-singularity is included in this   
+    routine. Such tests must be performed before calling this routine.   
+
+    Arguments   
+    ==========   
+
+    UPLO   - CHARACTER*1.   
+             On entry, UPLO specifies whether the matrix is an upper or   
+             lower triangular matrix as follows:   
+
+                UPLO = 'U' or 'u'   A is an upper triangular matrix.   
+
+                UPLO = 'L' or 'l'   A is a lower triangular matrix.   
+
+             Unchanged on exit.   
+
+    TRANS  - CHARACTER*1.   
+             On entry, TRANS specifies the equations to be solved as   
+             follows:   
+
+                TRANS = 'N' or 'n'   A*x = b.   
+
+                TRANS = 'T' or 't'   A**T*x = b.   
+
+                TRANS = 'C' or 'c'   A**T*x = b.   
+
+             Unchanged on exit.   
+
+    DIAG   - CHARACTER*1.   
+             On entry, DIAG specifies whether or not A is unit   
+             triangular as follows:   
+
+                DIAG = 'U' or 'u'   A is assumed to be unit triangular.   
+
+                DIAG = 'N' or 'n'   A is not assumed to be unit   
+                                    triangular.   
+
+             Unchanged on exit.   
+
+    N      - INTEGER.   
+             On entry, N specifies the order of the matrix A.   
+             N must be at least zero.   
+             Unchanged on exit.   
+
+    A      - DOUBLE PRECISION array of DIMENSION ( LDA, n ).   
+             Before entry with  UPLO = 'U' or 'u', the leading n by n   
+             upper triangular part of the array A must contain the upper   
+             triangular matrix and the strictly lower triangular part of   
+             A is not referenced.   
+             Before entry with UPLO = 'L' or 'l', the leading n by n   
+             lower triangular part of the array A must contain the lower   
+             triangular matrix and the strictly upper triangular part of   
+             A is not referenced.   
+             Note that when  DIAG = 'U' or 'u', the diagonal elements of   
+             A are not referenced either, but are assumed to be unity.   
+             Unchanged on exit.   
+
+    LDA    - INTEGER.   
+             On entry, LDA specifies the first dimension of A as declared   
+             in the calling (sub) program. LDA must be at least   
+             max( 1, n ).   
+             Unchanged on exit.   
+
+    X      - DOUBLE PRECISION array of dimension at least   
+             ( 1 + ( n - 1 )*abs( INCX ) ).   
+             Before entry, the incremented array X must contain the n   
+             element right-hand side vector b. On exit, X is overwritten   
+             with the solution vector x.   
+
+    INCX   - INTEGER.   
+             On entry, INCX specifies the increment for the elements of   
+             X. INCX must not be zero.   
+             Unchanged on exit.   
+
+
+    Level 2 Blas routine.   
+
+    -- Written on 22-October-1986.   
+       Jack Dongarra, Argonne National Lab.   
+       Jeremy Du Croz, Nag Central Office.   
+       Sven Hammarling, Nag Central Office.   
+       Richard Hanson, Sandia National Labs.   
+
+    =====================================================================   
+
+
+       Test the input parameters.   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    --x;
+
+    /* Function Body */
+    info = 0;
+    if (! igraphlsame_(uplo, "U") && ! igraphlsame_(uplo, "L")) {
+	info = 1;
+    } else if (! igraphlsame_(trans, "N") && ! igraphlsame_(trans, 
+	    "T") && ! igraphlsame_(trans, "C")) {
+	info = 2;
+    } else if (! igraphlsame_(diag, "U") && ! igraphlsame_(diag, 
+	    "N")) {
+	info = 3;
+    } else if (*n < 0) {
+	info = 4;
+    } else if (*lda < max(1,*n)) {
+	info = 6;
+    } else if (*incx == 0) {
+	info = 8;
+    }
+    if (info != 0) {
+	igraphxerbla_("DTRSV ", &info, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible. */
+
+    if (*n == 0) {
+	return 0;
+    }
+
+    nounit = igraphlsame_(diag, "N");
+
+/*     Set up the start point in X if the increment is not unity. This   
+       will be  ( N - 1 )*INCX  too small for descending loops. */
+
+    if (*incx <= 0) {
+	kx = 1 - (*n - 1) * *incx;
+    } else if (*incx != 1) {
+	kx = 1;
+    }
+
+/*     Start the operations. In this version the elements of A are   
+       accessed sequentially with one pass through A. */
+
+    if (igraphlsame_(trans, "N")) {
+
+/*        Form  x := inv( A )*x. */
+
+	if (igraphlsame_(uplo, "U")) {
+	    if (*incx == 1) {
+		for (j = *n; j >= 1; --j) {
+		    if (x[j] != 0.) {
+			if (nounit) {
+			    x[j] /= a[j + j * a_dim1];
+			}
+			temp = x[j];
+			for (i__ = j - 1; i__ >= 1; --i__) {
+			    x[i__] -= temp * a[i__ + j * a_dim1];
+/* L10: */
+			}
+		    }
+/* L20: */
+		}
+	    } else {
+		jx = kx + (*n - 1) * *incx;
+		for (j = *n; j >= 1; --j) {
+		    if (x[jx] != 0.) {
+			if (nounit) {
+			    x[jx] /= a[j + j * a_dim1];
+			}
+			temp = x[jx];
+			ix = jx;
+			for (i__ = j - 1; i__ >= 1; --i__) {
+			    ix -= *incx;
+			    x[ix] -= temp * a[i__ + j * a_dim1];
+/* L30: */
+			}
+		    }
+		    jx -= *incx;
+/* L40: */
+		}
+	    }
+	} else {
+	    if (*incx == 1) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    if (x[j] != 0.) {
+			if (nounit) {
+			    x[j] /= a[j + j * a_dim1];
+			}
+			temp = x[j];
+			i__2 = *n;
+			for (i__ = j + 1; i__ <= i__2; ++i__) {
+			    x[i__] -= temp * a[i__ + j * a_dim1];
+/* L50: */
+			}
+		    }
+/* L60: */
+		}
+	    } else {
+		jx = kx;
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    if (x[jx] != 0.) {
+			if (nounit) {
+			    x[jx] /= a[j + j * a_dim1];
+			}
+			temp = x[jx];
+			ix = jx;
+			i__2 = *n;
+			for (i__ = j + 1; i__ <= i__2; ++i__) {
+			    ix += *incx;
+			    x[ix] -= temp * a[i__ + j * a_dim1];
+/* L70: */
+			}
+		    }
+		    jx += *incx;
+/* L80: */
+		}
+	    }
+	}
+    } else {
+
+/*        Form  x := inv( A**T )*x. */
+
+	if (igraphlsame_(uplo, "U")) {
+	    if (*incx == 1) {
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    temp = x[j];
+		    i__2 = j - 1;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			temp -= a[i__ + j * a_dim1] * x[i__];
+/* L90: */
+		    }
+		    if (nounit) {
+			temp /= a[j + j * a_dim1];
+		    }
+		    x[j] = temp;
+/* L100: */
+		}
+	    } else {
+		jx = kx;
+		i__1 = *n;
+		for (j = 1; j <= i__1; ++j) {
+		    temp = x[jx];
+		    ix = kx;
+		    i__2 = j - 1;
+		    for (i__ = 1; i__ <= i__2; ++i__) {
+			temp -= a[i__ + j * a_dim1] * x[ix];
+			ix += *incx;
+/* L110: */
+		    }
+		    if (nounit) {
+			temp /= a[j + j * a_dim1];
+		    }
+		    x[jx] = temp;
+		    jx += *incx;
+/* L120: */
+		}
+	    }
+	} else {
+	    if (*incx == 1) {
+		for (j = *n; j >= 1; --j) {
+		    temp = x[j];
+		    i__1 = j + 1;
+		    for (i__ = *n; i__ >= i__1; --i__) {
+			temp -= a[i__ + j * a_dim1] * x[i__];
+/* L130: */
+		    }
+		    if (nounit) {
+			temp /= a[j + j * a_dim1];
+		    }
+		    x[j] = temp;
+/* L140: */
+		}
+	    } else {
+		kx += (*n - 1) * *incx;
+		jx = kx;
+		for (j = *n; j >= 1; --j) {
+		    temp = x[jx];
+		    ix = kx;
+		    i__1 = j + 1;
+		    for (i__ = *n; i__ >= i__1; --i__) {
+			temp -= a[i__ + j * a_dim1] * x[ix];
+			ix -= *incx;
+/* L150: */
+		    }
+		    if (nounit) {
+			temp /= a[j + j * a_dim1];
+		    }
+		    x[jx] = temp;
+		    jx -= *incx;
+/* L160: */
+		}
+	    }
+	}
+    }
+
+    return 0;
+
+/*     End of DTRSV . */
+
+} /* igraphdtrsv_ */
+
diff --git a/igraph/src/dtrsyl.c b/igraph/src/dtrsyl.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dtrsyl.c
@@ -0,0 +1,1389 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static logical c_false = FALSE_;
+static integer c__2 = 2;
+static doublereal c_b26 = 1.;
+static doublereal c_b30 = 0.;
+static logical c_true = TRUE_;
+
+/* > \brief \b DTRSYL   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download DTRSYL + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dtrsyl.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dtrsyl.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dtrsyl.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE DTRSYL( TRANA, TRANB, ISGN, M, N, A, LDA, B, LDB, C,   
+                            LDC, SCALE, INFO )   
+
+         CHARACTER          TRANA, TRANB   
+         INTEGER            INFO, ISGN, LDA, LDB, LDC, M, N   
+         DOUBLE PRECISION   SCALE   
+         DOUBLE PRECISION   A( LDA, * ), B( LDB, * ), C( LDC, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > DTRSYL solves the real Sylvester matrix equation:   
+   >   
+   >    op(A)*X + X*op(B) = scale*C or   
+   >    op(A)*X - X*op(B) = scale*C,   
+   >   
+   > where op(A) = A or A**T, and  A and B are both upper quasi-   
+   > triangular. A is M-by-M and B is N-by-N; the right hand side C and   
+   > the solution X are M-by-N; and scale is an output scale factor, set   
+   > <= 1 to avoid overflow in X.   
+   >   
+   > A and B must be in Schur canonical form (as returned by DHSEQR), that   
+   > is, block upper triangular with 1-by-1 and 2-by-2 diagonal blocks;   
+   > each 2-by-2 diagonal block has its diagonal elements equal and its   
+   > off-diagonal elements of opposite sign.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] TRANA   
+   > \verbatim   
+   >          TRANA is CHARACTER*1   
+   >          Specifies the option op(A):   
+   >          = 'N': op(A) = A    (No transpose)   
+   >          = 'T': op(A) = A**T (Transpose)   
+   >          = 'C': op(A) = A**H (Conjugate transpose = Transpose)   
+   > \endverbatim   
+   >   
+   > \param[in] TRANB   
+   > \verbatim   
+   >          TRANB is CHARACTER*1   
+   >          Specifies the option op(B):   
+   >          = 'N': op(B) = B    (No transpose)   
+   >          = 'T': op(B) = B**T (Transpose)   
+   >          = 'C': op(B) = B**H (Conjugate transpose = Transpose)   
+   > \endverbatim   
+   >   
+   > \param[in] ISGN   
+   > \verbatim   
+   >          ISGN is INTEGER   
+   >          Specifies the sign in the equation:   
+   >          = +1: solve op(A)*X + X*op(B) = scale*C   
+   >          = -1: solve op(A)*X - X*op(B) = scale*C   
+   > \endverbatim   
+   >   
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The order of the matrix A, and the number of rows in the   
+   >          matrices X and C. M >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The order of the matrix B, and the number of columns in the   
+   >          matrices X and C. N >= 0.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,M)   
+   >          The upper quasi-triangular matrix A, in Schur canonical form.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A. LDA >= max(1,M).   
+   > \endverbatim   
+   >   
+   > \param[in] B   
+   > \verbatim   
+   >          B is DOUBLE PRECISION array, dimension (LDB,N)   
+   >          The upper quasi-triangular matrix B, in Schur canonical form.   
+   > \endverbatim   
+   >   
+   > \param[in] LDB   
+   > \verbatim   
+   >          LDB is INTEGER   
+   >          The leading dimension of the array B. LDB >= max(1,N).   
+   > \endverbatim   
+   >   
+   > \param[in,out] C   
+   > \verbatim   
+   >          C is DOUBLE PRECISION array, dimension (LDC,N)   
+   >          On entry, the M-by-N right hand side matrix C.   
+   >          On exit, C is overwritten by the solution matrix X.   
+   > \endverbatim   
+   >   
+   > \param[in] LDC   
+   > \verbatim   
+   >          LDC is INTEGER   
+   >          The leading dimension of the array C. LDC >= max(1,M)   
+   > \endverbatim   
+   >   
+   > \param[out] SCALE   
+   > \verbatim   
+   >          SCALE is DOUBLE PRECISION   
+   >          The scale factor, scale, set <= 1 to avoid overflow in X.   
+   > \endverbatim   
+   >   
+   > \param[out] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          = 0: successful exit   
+   >          < 0: if INFO = -i, the i-th argument had an illegal value   
+   >          = 1: A and B have common or very close eigenvalues; perturbed   
+   >               values were used to solve the equation (but the matrices   
+   >               A and B are unchanged).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup doubleSYcomputational   
+
+    =====================================================================   
+   Subroutine */ int igraphdtrsyl_(char *trana, char *tranb, integer *isgn, integer 
+	*m, integer *n, doublereal *a, integer *lda, doublereal *b, integer *
+	ldb, doublereal *c__, integer *ldc, doublereal *scale, integer *info)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, b_dim1, b_offset, c_dim1, c_offset, i__1, i__2, 
+	    i__3, i__4;
+    doublereal d__1, d__2;
+
+    /* Local variables */
+    integer j, k, l;
+    doublereal x[4]	/* was [2][2] */;
+    integer k1, k2, l1, l2;
+    doublereal a11, db, da11, vec[4]	/* was [2][2] */, dum[1], eps, sgn;
+    extern doublereal igraphddot_(integer *, doublereal *, integer *, doublereal *, 
+	    integer *);
+    integer ierr;
+    doublereal smin, suml, sumr;
+    extern /* Subroutine */ int igraphdscal_(integer *, doublereal *, doublereal *, 
+	    integer *);
+    extern logical igraphlsame_(char *, char *);
+    integer knext, lnext;
+    doublereal xnorm;
+    extern /* Subroutine */ int igraphdlaln2_(logical *, integer *, integer *, 
+	    doublereal *, doublereal *, doublereal *, integer *, doublereal *,
+	     doublereal *, doublereal *, integer *, doublereal *, doublereal *
+	    , doublereal *, integer *, doublereal *, doublereal *, integer *),
+	     igraphdlasy2_(logical *, logical *, integer *, integer *, integer *, 
+	    doublereal *, integer *, doublereal *, integer *, doublereal *, 
+	    integer *, doublereal *, doublereal *, integer *, doublereal *, 
+	    integer *), igraphdlabad_(doublereal *, doublereal *);
+    extern doublereal igraphdlamch_(char *), igraphdlange_(char *, integer *, 
+	    integer *, doublereal *, integer *, doublereal *);
+    doublereal scaloc;
+    extern /* Subroutine */ int igraphxerbla_(char *, integer *, ftnlen);
+    doublereal bignum;
+    logical notrna, notrnb;
+    doublereal smlnum;
+
+
+/*  -- LAPACK computational routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    =====================================================================   
+
+
+       Decode and Test input parameters   
+
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+    b_dim1 = *ldb;
+    b_offset = 1 + b_dim1;
+    b -= b_offset;
+    c_dim1 = *ldc;
+    c_offset = 1 + c_dim1;
+    c__ -= c_offset;
+
+    /* Function Body */
+    notrna = igraphlsame_(trana, "N");
+    notrnb = igraphlsame_(tranb, "N");
+
+    *info = 0;
+    if (! notrna && ! igraphlsame_(trana, "T") && ! igraphlsame_(
+	    trana, "C")) {
+	*info = -1;
+    } else if (! notrnb && ! igraphlsame_(tranb, "T") && ! 
+	    igraphlsame_(tranb, "C")) {
+	*info = -2;
+    } else if (*isgn != 1 && *isgn != -1) {
+	*info = -3;
+    } else if (*m < 0) {
+	*info = -4;
+    } else if (*n < 0) {
+	*info = -5;
+    } else if (*lda < max(1,*m)) {
+	*info = -7;
+    } else if (*ldb < max(1,*n)) {
+	*info = -9;
+    } else if (*ldc < max(1,*m)) {
+	*info = -11;
+    }
+    if (*info != 0) {
+	i__1 = -(*info);
+	igraphxerbla_("DTRSYL", &i__1, (ftnlen)6);
+	return 0;
+    }
+
+/*     Quick return if possible */
+
+    *scale = 1.;
+    if (*m == 0 || *n == 0) {
+	return 0;
+    }
+
+/*     Set constants to control overflow */
+
+    eps = igraphdlamch_("P");
+    smlnum = igraphdlamch_("S");
+    bignum = 1. / smlnum;
+    igraphdlabad_(&smlnum, &bignum);
+    smlnum = smlnum * (doublereal) (*m * *n) / eps;
+    bignum = 1. / smlnum;
+
+/* Computing MAX */
+    d__1 = smlnum, d__2 = eps * igraphdlange_("M", m, m, &a[a_offset], lda, dum), d__1 = max(d__1,d__2), d__2 = eps * igraphdlange_("M", n, n, 
+	    &b[b_offset], ldb, dum);
+    smin = max(d__1,d__2);
+
+    sgn = (doublereal) (*isgn);
+
+    if (notrna && notrnb) {
+
+/*        Solve    A*X + ISGN*X*B = scale*C.   
+
+          The (K,L)th block of X is determined starting from   
+          bottom-left corner column by column by   
+
+           A(K,K)*X(K,L) + ISGN*X(K,L)*B(L,L) = C(K,L) - R(K,L)   
+
+          Where   
+                    M                         L-1   
+          R(K,L) = SUM [A(K,I)*X(I,L)] + ISGN*SUM [X(K,J)*B(J,L)].   
+                  I=K+1                       J=1   
+
+          Start column loop (index = L)   
+          L1 (L2) : column index of the first (first) row of X(K,L). */
+
+	lnext = 1;
+	i__1 = *n;
+	for (l = 1; l <= i__1; ++l) {
+	    if (l < lnext) {
+		goto L60;
+	    }
+	    if (l == *n) {
+		l1 = l;
+		l2 = l;
+	    } else {
+		if (b[l + 1 + l * b_dim1] != 0.) {
+		    l1 = l;
+		    l2 = l + 1;
+		    lnext = l + 2;
+		} else {
+		    l1 = l;
+		    l2 = l;
+		    lnext = l + 1;
+		}
+	    }
+
+/*           Start row loop (index = K)   
+             K1 (K2): row index of the first (last) row of X(K,L). */
+
+	    knext = *m;
+	    for (k = *m; k >= 1; --k) {
+		if (k > knext) {
+		    goto L50;
+		}
+		if (k == 1) {
+		    k1 = k;
+		    k2 = k;
+		} else {
+		    if (a[k + (k - 1) * a_dim1] != 0.) {
+			k1 = k - 1;
+			k2 = k;
+			knext = k - 2;
+		    } else {
+			k1 = k;
+			k2 = k;
+			knext = k - 1;
+		    }
+		}
+
+		if (l1 == l2 && k1 == k2) {
+		    i__2 = *m - k1;
+/* Computing MIN */
+		    i__3 = k1 + 1;
+/* Computing MIN */
+		    i__4 = k1 + 1;
+		    suml = igraphddot_(&i__2, &a[k1 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l1 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+		    scaloc = 1.;
+
+		    a11 = a[k1 + k1 * a_dim1] + sgn * b[l1 + l1 * b_dim1];
+		    da11 = abs(a11);
+		    if (da11 <= smin) {
+			a11 = smin;
+			da11 = smin;
+			*info = 1;
+		    }
+		    db = abs(vec[0]);
+		    if (da11 < 1. && db > 1.) {
+			if (db > bignum * da11) {
+			    scaloc = 1. / db;
+			}
+		    }
+		    x[0] = vec[0] * scaloc / a11;
+
+		    if (scaloc != 1.) {
+			i__2 = *n;
+			for (j = 1; j <= i__2; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L10: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+
+		} else if (l1 == l2 && k1 != k2) {
+
+		    i__2 = *m - k2;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+/* Computing MIN */
+		    i__4 = k2 + 1;
+		    suml = igraphddot_(&i__2, &a[k1 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l1 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__2 = *m - k2;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+/* Computing MIN */
+		    i__4 = k2 + 1;
+		    suml = igraphddot_(&i__2, &a[k2 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l1 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k2 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[1] = c__[k2 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    d__1 = -sgn * b[l1 + l1 * b_dim1];
+		    igraphdlaln2_(&c_false, &c__2, &c__1, &smin, &c_b26, &a[k1 + k1 
+			    * a_dim1], lda, &c_b26, &c_b26, vec, &c__2, &d__1,
+			     &c_b30, x, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__2 = *n;
+			for (j = 1; j <= i__2; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L20: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k2 + l1 * c_dim1] = x[1];
+
+		} else if (l1 != l2 && k1 == k2) {
+
+		    i__2 = *m - k1;
+/* Computing MIN */
+		    i__3 = k1 + 1;
+/* Computing MIN */
+		    i__4 = k1 + 1;
+		    suml = igraphddot_(&i__2, &a[k1 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l1 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[0] = sgn * (c__[k1 + l1 * c_dim1] - (suml + sgn * 
+			    sumr));
+
+		    i__2 = *m - k1;
+/* Computing MIN */
+		    i__3 = k1 + 1;
+/* Computing MIN */
+		    i__4 = k1 + 1;
+		    suml = igraphddot_(&i__2, &a[k1 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l2 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + c_dim1], ldc, &b[l2 * 
+			    b_dim1 + 1], &c__1);
+		    vec[1] = sgn * (c__[k1 + l2 * c_dim1] - (suml + sgn * 
+			    sumr));
+
+		    d__1 = -sgn * a[k1 + k1 * a_dim1];
+		    igraphdlaln2_(&c_true, &c__2, &c__1, &smin, &c_b26, &b[l1 + l1 *
+			     b_dim1], ldb, &c_b26, &c_b26, vec, &c__2, &d__1, 
+			    &c_b30, x, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__2 = *n;
+			for (j = 1; j <= i__2; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L30: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k1 + l2 * c_dim1] = x[1];
+
+		} else if (l1 != l2 && k1 != k2) {
+
+		    i__2 = *m - k2;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+/* Computing MIN */
+		    i__4 = k2 + 1;
+		    suml = igraphddot_(&i__2, &a[k1 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l1 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__2 = *m - k2;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+/* Computing MIN */
+		    i__4 = k2 + 1;
+		    suml = igraphddot_(&i__2, &a[k1 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l2 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + c_dim1], ldc, &b[l2 * 
+			    b_dim1 + 1], &c__1);
+		    vec[2] = c__[k1 + l2 * c_dim1] - (suml + sgn * sumr);
+
+		    i__2 = *m - k2;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+/* Computing MIN */
+		    i__4 = k2 + 1;
+		    suml = igraphddot_(&i__2, &a[k2 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l1 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k2 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[1] = c__[k2 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__2 = *m - k2;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+/* Computing MIN */
+		    i__4 = k2 + 1;
+		    suml = igraphddot_(&i__2, &a[k2 + min(i__3,*m) * a_dim1], lda, &
+			    c__[min(i__4,*m) + l2 * c_dim1], &c__1);
+		    i__2 = l1 - 1;
+		    sumr = igraphddot_(&i__2, &c__[k2 + c_dim1], ldc, &b[l2 * 
+			    b_dim1 + 1], &c__1);
+		    vec[3] = c__[k2 + l2 * c_dim1] - (suml + sgn * sumr);
+
+		    igraphdlasy2_(&c_false, &c_false, isgn, &c__2, &c__2, &a[k1 + 
+			    k1 * a_dim1], lda, &b[l1 + l1 * b_dim1], ldb, vec,
+			     &c__2, &scaloc, x, &c__2, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__2 = *n;
+			for (j = 1; j <= i__2; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L40: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k1 + l2 * c_dim1] = x[2];
+		    c__[k2 + l1 * c_dim1] = x[1];
+		    c__[k2 + l2 * c_dim1] = x[3];
+		}
+
+L50:
+		;
+	    }
+
+L60:
+	    ;
+	}
+
+    } else if (! notrna && notrnb) {
+
+/*        Solve    A**T *X + ISGN*X*B = scale*C.   
+
+          The (K,L)th block of X is determined starting from   
+          upper-left corner column by column by   
+
+            A(K,K)**T*X(K,L) + ISGN*X(K,L)*B(L,L) = C(K,L) - R(K,L)   
+
+          Where   
+                     K-1        T                    L-1   
+            R(K,L) = SUM [A(I,K)**T*X(I,L)] +ISGN*SUM [X(K,J)*B(J,L)]   
+                     I=1                          J=1   
+
+          Start column loop (index = L)   
+          L1 (L2): column index of the first (last) row of X(K,L) */
+
+	lnext = 1;
+	i__1 = *n;
+	for (l = 1; l <= i__1; ++l) {
+	    if (l < lnext) {
+		goto L120;
+	    }
+	    if (l == *n) {
+		l1 = l;
+		l2 = l;
+	    } else {
+		if (b[l + 1 + l * b_dim1] != 0.) {
+		    l1 = l;
+		    l2 = l + 1;
+		    lnext = l + 2;
+		} else {
+		    l1 = l;
+		    l2 = l;
+		    lnext = l + 1;
+		}
+	    }
+
+/*           Start row loop (index = K)   
+             K1 (K2): row index of the first (last) row of X(K,L) */
+
+	    knext = 1;
+	    i__2 = *m;
+	    for (k = 1; k <= i__2; ++k) {
+		if (k < knext) {
+		    goto L110;
+		}
+		if (k == *m) {
+		    k1 = k;
+		    k2 = k;
+		} else {
+		    if (a[k + 1 + k * a_dim1] != 0.) {
+			k1 = k;
+			k2 = k + 1;
+			knext = k + 2;
+		    } else {
+			k1 = k;
+			k2 = k;
+			knext = k + 1;
+		    }
+		}
+
+		if (l1 == l2 && k1 == k2) {
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k1 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k1 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+		    scaloc = 1.;
+
+		    a11 = a[k1 + k1 * a_dim1] + sgn * b[l1 + l1 * b_dim1];
+		    da11 = abs(a11);
+		    if (da11 <= smin) {
+			a11 = smin;
+			da11 = smin;
+			*info = 1;
+		    }
+		    db = abs(vec[0]);
+		    if (da11 < 1. && db > 1.) {
+			if (db > bignum * da11) {
+			    scaloc = 1. / db;
+			}
+		    }
+		    x[0] = vec[0] * scaloc / a11;
+
+		    if (scaloc != 1.) {
+			i__3 = *n;
+			for (j = 1; j <= i__3; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L70: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+
+		} else if (l1 == l2 && k1 != k2) {
+
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k1 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k1 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k2 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k2 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[1] = c__[k2 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    d__1 = -sgn * b[l1 + l1 * b_dim1];
+		    igraphdlaln2_(&c_true, &c__2, &c__1, &smin, &c_b26, &a[k1 + k1 *
+			     a_dim1], lda, &c_b26, &c_b26, vec, &c__2, &d__1, 
+			    &c_b30, x, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__3 = *n;
+			for (j = 1; j <= i__3; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L80: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k2 + l1 * c_dim1] = x[1];
+
+		} else if (l1 != l2 && k1 == k2) {
+
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k1 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k1 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[0] = sgn * (c__[k1 + l1 * c_dim1] - (suml + sgn * 
+			    sumr));
+
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k1 * a_dim1 + 1], &c__1, &c__[l2 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k1 + c_dim1], ldc, &b[l2 * 
+			    b_dim1 + 1], &c__1);
+		    vec[1] = sgn * (c__[k1 + l2 * c_dim1] - (suml + sgn * 
+			    sumr));
+
+		    d__1 = -sgn * a[k1 + k1 * a_dim1];
+		    igraphdlaln2_(&c_true, &c__2, &c__1, &smin, &c_b26, &b[l1 + l1 *
+			     b_dim1], ldb, &c_b26, &c_b26, vec, &c__2, &d__1, 
+			    &c_b30, x, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__3 = *n;
+			for (j = 1; j <= i__3; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L90: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k1 + l2 * c_dim1] = x[1];
+
+		} else if (l1 != l2 && k1 != k2) {
+
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k1 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k1 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k1 * a_dim1 + 1], &c__1, &c__[l2 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k1 + c_dim1], ldc, &b[l2 * 
+			    b_dim1 + 1], &c__1);
+		    vec[2] = c__[k1 + l2 * c_dim1] - (suml + sgn * sumr);
+
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k2 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k2 + c_dim1], ldc, &b[l1 * 
+			    b_dim1 + 1], &c__1);
+		    vec[1] = c__[k2 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__3 = k1 - 1;
+		    suml = igraphddot_(&i__3, &a[k2 * a_dim1 + 1], &c__1, &c__[l2 * 
+			    c_dim1 + 1], &c__1);
+		    i__3 = l1 - 1;
+		    sumr = igraphddot_(&i__3, &c__[k2 + c_dim1], ldc, &b[l2 * 
+			    b_dim1 + 1], &c__1);
+		    vec[3] = c__[k2 + l2 * c_dim1] - (suml + sgn * sumr);
+
+		    igraphdlasy2_(&c_true, &c_false, isgn, &c__2, &c__2, &a[k1 + k1 
+			    * a_dim1], lda, &b[l1 + l1 * b_dim1], ldb, vec, &
+			    c__2, &scaloc, x, &c__2, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__3 = *n;
+			for (j = 1; j <= i__3; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L100: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k1 + l2 * c_dim1] = x[2];
+		    c__[k2 + l1 * c_dim1] = x[1];
+		    c__[k2 + l2 * c_dim1] = x[3];
+		}
+
+L110:
+		;
+	    }
+L120:
+	    ;
+	}
+
+    } else if (! notrna && ! notrnb) {
+
+/*        Solve    A**T*X + ISGN*X*B**T = scale*C.   
+
+          The (K,L)th block of X is determined starting from   
+          top-right corner column by column by   
+
+             A(K,K)**T*X(K,L) + ISGN*X(K,L)*B(L,L)**T = C(K,L) - R(K,L)   
+
+          Where   
+                       K-1                            N   
+              R(K,L) = SUM [A(I,K)**T*X(I,L)] + ISGN*SUM [X(K,J)*B(L,J)**T].   
+                       I=1                          J=L+1   
+
+          Start column loop (index = L)   
+          L1 (L2): column index of the first (last) row of X(K,L) */
+
+	lnext = *n;
+	for (l = *n; l >= 1; --l) {
+	    if (l > lnext) {
+		goto L180;
+	    }
+	    if (l == 1) {
+		l1 = l;
+		l2 = l;
+	    } else {
+		if (b[l + (l - 1) * b_dim1] != 0.) {
+		    l1 = l - 1;
+		    l2 = l;
+		    lnext = l - 2;
+		} else {
+		    l1 = l;
+		    l2 = l;
+		    lnext = l - 1;
+		}
+	    }
+
+/*           Start row loop (index = K)   
+             K1 (K2): row index of the first (last) row of X(K,L) */
+
+	    knext = 1;
+	    i__1 = *m;
+	    for (k = 1; k <= i__1; ++k) {
+		if (k < knext) {
+		    goto L170;
+		}
+		if (k == *m) {
+		    k1 = k;
+		    k2 = k;
+		} else {
+		    if (a[k + 1 + k * a_dim1] != 0.) {
+			k1 = k;
+			k2 = k + 1;
+			knext = k + 2;
+		    } else {
+			k1 = k;
+			k2 = k;
+			knext = k + 1;
+		    }
+		}
+
+		if (l1 == l2 && k1 == k2) {
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k1 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l1;
+/* Computing MIN */
+		    i__3 = l1 + 1;
+/* Computing MIN */
+		    i__4 = l1 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__4,*n) * b_dim1], ldb);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+		    scaloc = 1.;
+
+		    a11 = a[k1 + k1 * a_dim1] + sgn * b[l1 + l1 * b_dim1];
+		    da11 = abs(a11);
+		    if (da11 <= smin) {
+			a11 = smin;
+			da11 = smin;
+			*info = 1;
+		    }
+		    db = abs(vec[0]);
+		    if (da11 < 1. && db > 1.) {
+			if (db > bignum * da11) {
+			    scaloc = 1. / db;
+			}
+		    }
+		    x[0] = vec[0] * scaloc / a11;
+
+		    if (scaloc != 1.) {
+			i__2 = *n;
+			for (j = 1; j <= i__2; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L130: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+
+		} else if (l1 == l2 && k1 != k2) {
+
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k1 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l2;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+/* Computing MIN */
+		    i__4 = l2 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__4,*n) * b_dim1], ldb);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k2 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l2;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+/* Computing MIN */
+		    i__4 = l2 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k2 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__4,*n) * b_dim1], ldb);
+		    vec[1] = c__[k2 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    d__1 = -sgn * b[l1 + l1 * b_dim1];
+		    igraphdlaln2_(&c_true, &c__2, &c__1, &smin, &c_b26, &a[k1 + k1 *
+			     a_dim1], lda, &c_b26, &c_b26, vec, &c__2, &d__1, 
+			    &c_b30, x, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__2 = *n;
+			for (j = 1; j <= i__2; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L140: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k2 + l1 * c_dim1] = x[1];
+
+		} else if (l1 != l2 && k1 == k2) {
+
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k1 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l2;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+/* Computing MIN */
+		    i__4 = l2 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__4,*n) * b_dim1], ldb);
+		    vec[0] = sgn * (c__[k1 + l1 * c_dim1] - (suml + sgn * 
+			    sumr));
+
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k1 * a_dim1 + 1], &c__1, &c__[l2 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l2;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+/* Computing MIN */
+		    i__4 = l2 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l2 + min(i__4,*n) * b_dim1], ldb);
+		    vec[1] = sgn * (c__[k1 + l2 * c_dim1] - (suml + sgn * 
+			    sumr));
+
+		    d__1 = -sgn * a[k1 + k1 * a_dim1];
+		    igraphdlaln2_(&c_false, &c__2, &c__1, &smin, &c_b26, &b[l1 + l1 
+			    * b_dim1], ldb, &c_b26, &c_b26, vec, &c__2, &d__1,
+			     &c_b30, x, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__2 = *n;
+			for (j = 1; j <= i__2; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L150: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k1 + l2 * c_dim1] = x[1];
+
+		} else if (l1 != l2 && k1 != k2) {
+
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k1 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l2;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+/* Computing MIN */
+		    i__4 = l2 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__4,*n) * b_dim1], ldb);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k1 * a_dim1 + 1], &c__1, &c__[l2 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l2;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+/* Computing MIN */
+		    i__4 = l2 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k1 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l2 + min(i__4,*n) * b_dim1], ldb);
+		    vec[2] = c__[k1 + l2 * c_dim1] - (suml + sgn * sumr);
+
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k2 * a_dim1 + 1], &c__1, &c__[l1 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l2;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+/* Computing MIN */
+		    i__4 = l2 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k2 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__4,*n) * b_dim1], ldb);
+		    vec[1] = c__[k2 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__2 = k1 - 1;
+		    suml = igraphddot_(&i__2, &a[k2 * a_dim1 + 1], &c__1, &c__[l2 * 
+			    c_dim1 + 1], &c__1);
+		    i__2 = *n - l2;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+/* Computing MIN */
+		    i__4 = l2 + 1;
+		    sumr = igraphddot_(&i__2, &c__[k2 + min(i__3,*n) * c_dim1], ldc,
+			     &b[l2 + min(i__4,*n) * b_dim1], ldb);
+		    vec[3] = c__[k2 + l2 * c_dim1] - (suml + sgn * sumr);
+
+		    igraphdlasy2_(&c_true, &c_true, isgn, &c__2, &c__2, &a[k1 + k1 *
+			     a_dim1], lda, &b[l1 + l1 * b_dim1], ldb, vec, &
+			    c__2, &scaloc, x, &c__2, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__2 = *n;
+			for (j = 1; j <= i__2; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L160: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k1 + l2 * c_dim1] = x[2];
+		    c__[k2 + l1 * c_dim1] = x[1];
+		    c__[k2 + l2 * c_dim1] = x[3];
+		}
+
+L170:
+		;
+	    }
+L180:
+	    ;
+	}
+
+    } else if (notrna && ! notrnb) {
+
+/*        Solve    A*X + ISGN*X*B**T = scale*C.   
+
+          The (K,L)th block of X is determined starting from   
+          bottom-right corner column by column by   
+
+              A(K,K)*X(K,L) + ISGN*X(K,L)*B(L,L)**T = C(K,L) - R(K,L)   
+
+          Where   
+                        M                          N   
+              R(K,L) = SUM [A(K,I)*X(I,L)] + ISGN*SUM [X(K,J)*B(L,J)**T].   
+                      I=K+1                      J=L+1   
+
+          Start column loop (index = L)   
+          L1 (L2): column index of the first (last) row of X(K,L) */
+
+	lnext = *n;
+	for (l = *n; l >= 1; --l) {
+	    if (l > lnext) {
+		goto L240;
+	    }
+	    if (l == 1) {
+		l1 = l;
+		l2 = l;
+	    } else {
+		if (b[l + (l - 1) * b_dim1] != 0.) {
+		    l1 = l - 1;
+		    l2 = l;
+		    lnext = l - 2;
+		} else {
+		    l1 = l;
+		    l2 = l;
+		    lnext = l - 1;
+		}
+	    }
+
+/*           Start row loop (index = K)   
+             K1 (K2): row index of the first (last) row of X(K,L) */
+
+	    knext = *m;
+	    for (k = *m; k >= 1; --k) {
+		if (k > knext) {
+		    goto L230;
+		}
+		if (k == 1) {
+		    k1 = k;
+		    k2 = k;
+		} else {
+		    if (a[k + (k - 1) * a_dim1] != 0.) {
+			k1 = k - 1;
+			k2 = k;
+			knext = k - 2;
+		    } else {
+			k1 = k;
+			k2 = k;
+			knext = k - 1;
+		    }
+		}
+
+		if (l1 == l2 && k1 == k2) {
+		    i__1 = *m - k1;
+/* Computing MIN */
+		    i__2 = k1 + 1;
+/* Computing MIN */
+		    i__3 = k1 + 1;
+		    suml = igraphddot_(&i__1, &a[k1 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l1 * c_dim1], &c__1);
+		    i__1 = *n - l1;
+/* Computing MIN */
+		    i__2 = l1 + 1;
+/* Computing MIN */
+		    i__3 = l1 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k1 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__3,*n) * b_dim1], ldb);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+		    scaloc = 1.;
+
+		    a11 = a[k1 + k1 * a_dim1] + sgn * b[l1 + l1 * b_dim1];
+		    da11 = abs(a11);
+		    if (da11 <= smin) {
+			a11 = smin;
+			da11 = smin;
+			*info = 1;
+		    }
+		    db = abs(vec[0]);
+		    if (da11 < 1. && db > 1.) {
+			if (db > bignum * da11) {
+			    scaloc = 1. / db;
+			}
+		    }
+		    x[0] = vec[0] * scaloc / a11;
+
+		    if (scaloc != 1.) {
+			i__1 = *n;
+			for (j = 1; j <= i__1; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L190: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+
+		} else if (l1 == l2 && k1 != k2) {
+
+		    i__1 = *m - k2;
+/* Computing MIN */
+		    i__2 = k2 + 1;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+		    suml = igraphddot_(&i__1, &a[k1 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l1 * c_dim1], &c__1);
+		    i__1 = *n - l2;
+/* Computing MIN */
+		    i__2 = l2 + 1;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k1 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__3,*n) * b_dim1], ldb);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__1 = *m - k2;
+/* Computing MIN */
+		    i__2 = k2 + 1;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+		    suml = igraphddot_(&i__1, &a[k2 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l1 * c_dim1], &c__1);
+		    i__1 = *n - l2;
+/* Computing MIN */
+		    i__2 = l2 + 1;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k2 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__3,*n) * b_dim1], ldb);
+		    vec[1] = c__[k2 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    d__1 = -sgn * b[l1 + l1 * b_dim1];
+		    igraphdlaln2_(&c_false, &c__2, &c__1, &smin, &c_b26, &a[k1 + k1 
+			    * a_dim1], lda, &c_b26, &c_b26, vec, &c__2, &d__1,
+			     &c_b30, x, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__1 = *n;
+			for (j = 1; j <= i__1; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L200: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k2 + l1 * c_dim1] = x[1];
+
+		} else if (l1 != l2 && k1 == k2) {
+
+		    i__1 = *m - k1;
+/* Computing MIN */
+		    i__2 = k1 + 1;
+/* Computing MIN */
+		    i__3 = k1 + 1;
+		    suml = igraphddot_(&i__1, &a[k1 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l1 * c_dim1], &c__1);
+		    i__1 = *n - l2;
+/* Computing MIN */
+		    i__2 = l2 + 1;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k1 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__3,*n) * b_dim1], ldb);
+		    vec[0] = sgn * (c__[k1 + l1 * c_dim1] - (suml + sgn * 
+			    sumr));
+
+		    i__1 = *m - k1;
+/* Computing MIN */
+		    i__2 = k1 + 1;
+/* Computing MIN */
+		    i__3 = k1 + 1;
+		    suml = igraphddot_(&i__1, &a[k1 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l2 * c_dim1], &c__1);
+		    i__1 = *n - l2;
+/* Computing MIN */
+		    i__2 = l2 + 1;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k1 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l2 + min(i__3,*n) * b_dim1], ldb);
+		    vec[1] = sgn * (c__[k1 + l2 * c_dim1] - (suml + sgn * 
+			    sumr));
+
+		    d__1 = -sgn * a[k1 + k1 * a_dim1];
+		    igraphdlaln2_(&c_false, &c__2, &c__1, &smin, &c_b26, &b[l1 + l1 
+			    * b_dim1], ldb, &c_b26, &c_b26, vec, &c__2, &d__1,
+			     &c_b30, x, &c__2, &scaloc, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__1 = *n;
+			for (j = 1; j <= i__1; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L210: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k1 + l2 * c_dim1] = x[1];
+
+		} else if (l1 != l2 && k1 != k2) {
+
+		    i__1 = *m - k2;
+/* Computing MIN */
+		    i__2 = k2 + 1;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+		    suml = igraphddot_(&i__1, &a[k1 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l1 * c_dim1], &c__1);
+		    i__1 = *n - l2;
+/* Computing MIN */
+		    i__2 = l2 + 1;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k1 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__3,*n) * b_dim1], ldb);
+		    vec[0] = c__[k1 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__1 = *m - k2;
+/* Computing MIN */
+		    i__2 = k2 + 1;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+		    suml = igraphddot_(&i__1, &a[k1 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l2 * c_dim1], &c__1);
+		    i__1 = *n - l2;
+/* Computing MIN */
+		    i__2 = l2 + 1;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k1 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l2 + min(i__3,*n) * b_dim1], ldb);
+		    vec[2] = c__[k1 + l2 * c_dim1] - (suml + sgn * sumr);
+
+		    i__1 = *m - k2;
+/* Computing MIN */
+		    i__2 = k2 + 1;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+		    suml = igraphddot_(&i__1, &a[k2 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l1 * c_dim1], &c__1);
+		    i__1 = *n - l2;
+/* Computing MIN */
+		    i__2 = l2 + 1;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k2 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l1 + min(i__3,*n) * b_dim1], ldb);
+		    vec[1] = c__[k2 + l1 * c_dim1] - (suml + sgn * sumr);
+
+		    i__1 = *m - k2;
+/* Computing MIN */
+		    i__2 = k2 + 1;
+/* Computing MIN */
+		    i__3 = k2 + 1;
+		    suml = igraphddot_(&i__1, &a[k2 + min(i__2,*m) * a_dim1], lda, &
+			    c__[min(i__3,*m) + l2 * c_dim1], &c__1);
+		    i__1 = *n - l2;
+/* Computing MIN */
+		    i__2 = l2 + 1;
+/* Computing MIN */
+		    i__3 = l2 + 1;
+		    sumr = igraphddot_(&i__1, &c__[k2 + min(i__2,*n) * c_dim1], ldc,
+			     &b[l2 + min(i__3,*n) * b_dim1], ldb);
+		    vec[3] = c__[k2 + l2 * c_dim1] - (suml + sgn * sumr);
+
+		    igraphdlasy2_(&c_false, &c_true, isgn, &c__2, &c__2, &a[k1 + k1 
+			    * a_dim1], lda, &b[l1 + l1 * b_dim1], ldb, vec, &
+			    c__2, &scaloc, x, &c__2, &xnorm, &ierr);
+		    if (ierr != 0) {
+			*info = 1;
+		    }
+
+		    if (scaloc != 1.) {
+			i__1 = *n;
+			for (j = 1; j <= i__1; ++j) {
+			    igraphdscal_(m, &scaloc, &c__[j * c_dim1 + 1], &c__1);
+/* L220: */
+			}
+			*scale *= scaloc;
+		    }
+		    c__[k1 + l1 * c_dim1] = x[0];
+		    c__[k1 + l2 * c_dim1] = x[2];
+		    c__[k2 + l1 * c_dim1] = x[1];
+		    c__[k2 + l2 * c_dim1] = x[3];
+		}
+
+L230:
+		;
+	    }
+L240:
+	    ;
+	}
+
+    }
+
+    return 0;
+
+/*     End of DTRSYL */
+
+} /* igraphdtrsyl_ */
+
diff --git a/igraph/src/due.c b/igraph/src/due.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/due.c
@@ -0,0 +1,77 @@
+#include "f2c.h"
+#include "fio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ int
+#ifdef KR_headers
+c_due(a) cilist *a;
+#else
+c_due(cilist *a)
+#endif
+{
+	if(!f__init) f_init();
+	f__sequential=f__formatted=f__recpos=0;
+	f__external=1;
+	f__curunit = &f__units[a->ciunit];
+	if(a->ciunit>=MXUNIT || a->ciunit<0)
+		err(a->cierr,101,"startio");
+	f__elist=a;
+	if(f__curunit->ufd==NULL && fk_open(DIR,UNF,a->ciunit) ) err(a->cierr,104,"due");
+	f__cf=f__curunit->ufd;
+	if(f__curunit->ufmt) err(a->cierr,102,"cdue")
+	if(!f__curunit->useek) err(a->cierr,104,"cdue")
+	if(f__curunit->ufd==NULL) err(a->cierr,114,"cdue")
+	if(a->cirec <= 0)
+		err(a->cierr,130,"due")
+	FSEEK(f__cf,(OFF_T)(a->cirec-1)*f__curunit->url,SEEK_SET);
+	f__curunit->uend = 0;
+	return(0);
+}
+#ifdef KR_headers
+integer s_rdue(a) cilist *a;
+#else
+integer s_rdue(cilist *a)
+#endif
+{
+	int n;
+	f__reading=1;
+	if(n=c_due(a)) return(n);
+	if(f__curunit->uwrt && f__nowreading(f__curunit))
+		err(a->cierr,errno,"read start");
+	return(0);
+}
+#ifdef KR_headers
+integer s_wdue(a) cilist *a;
+#else
+integer s_wdue(cilist *a)
+#endif
+{
+	int n;
+	f__reading=0;
+	if(n=c_due(a)) return(n);
+	if(f__curunit->uwrt != 1 && f__nowwriting(f__curunit))
+		err(a->cierr,errno,"write start");
+	return(0);
+}
+integer e_rdue(Void)
+{
+	if(f__curunit->url==1 || f__recpos==f__curunit->url)
+		return(0);
+	FSEEK(f__cf,(OFF_T)(f__curunit->url-f__recpos),SEEK_CUR);
+	if(FTELL(f__cf)%f__curunit->url)
+		err(f__elist->cierr,200,"syserr");
+	return(0);
+}
+integer e_wdue(Void)
+{
+#ifdef ALWAYS_FLUSH
+	if (fflush(f__cf))
+		err(f__elist->cierr,errno,"write end");
+#endif
+	return(e_rdue());
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/dummy.c b/igraph/src/dummy.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dummy.c
@@ -0,0 +1,2 @@
+
+int MAIN__(void) { return 0; }
diff --git a/igraph/src/dvout.c b/igraph/src/dvout.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/dvout.c
@@ -0,0 +1,276 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* -----------------------------------------------------------------------   
+    Routine:    DVOUT   
+
+    Purpose:    Real vector output routine.   
+
+    Usage:      CALL DVOUT (LOUT, N, SX, IDIGIT, IFMT)   
+
+    Arguments   
+       N      - Length of array SX.  (Input)   
+       SX     - Real array to be printed.  (Input)   
+       IFMT   - Format to be used in printing array SX.  (Input)   
+       IDIGIT - Print up to IABS(IDIGIT) decimal digits per number.  (In)   
+                If IDIGIT .LT. 0, printing is done with 72 columns.   
+                If IDIGIT .GT. 0, printing is done with 132 columns.   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphdvout_(integer *lout, integer *n, doublereal *sx, 
+	integer *idigit, char *ifmt, ftnlen ifmt_len)
+{
+    /* Format strings */
+    static char fmt_9999[] = "(/1x,a,/1x,a)";
+    static char fmt_9998[] = "(1x,i4,\002 - \002,i4,\002:\002,1p,10d12.3)";
+    static char fmt_9997[] = "(1x,i4,\002 - \002,i4,\002:\002,1x,1p,8d14.5)";
+    static char fmt_9996[] = "(1x,i4,\002 - \002,i4,\002:\002,1x,1p,6d18.9)";
+    static char fmt_9995[] = "(1x,i4,\002 - \002,i4,\002:\002,1x,1p,5d24.13)";
+    static char fmt_9994[] = "(1x,\002 \002)";
+
+    /* System generated locals */
+    integer i__1, i__2, i__3;
+
+    /* Builtin functions */
+    integer i_len(char *, ftnlen), s_wsfe(cilist *), do_fio(integer *, char *,
+	     ftnlen), e_wsfe(void);
+
+    /* Local variables */
+    integer i__, k1, k2, lll;
+    char line[80];
+    integer ndigit;
+
+    /* Fortran I/O blocks */
+    static cilist io___4 = { 0, 0, 0, fmt_9999, 0 };
+    static cilist io___8 = { 0, 0, 0, fmt_9998, 0 };
+    static cilist io___9 = { 0, 0, 0, fmt_9997, 0 };
+    static cilist io___10 = { 0, 0, 0, fmt_9996, 0 };
+    static cilist io___11 = { 0, 0, 0, fmt_9995, 0 };
+    static cilist io___12 = { 0, 0, 0, fmt_9998, 0 };
+    static cilist io___13 = { 0, 0, 0, fmt_9997, 0 };
+    static cilist io___14 = { 0, 0, 0, fmt_9996, 0 };
+    static cilist io___15 = { 0, 0, 0, fmt_9995, 0 };
+    static cilist io___16 = { 0, 0, 0, fmt_9994, 0 };
+
+
+/*     ...   
+       ... SPECIFICATIONS FOR ARGUMENTS   
+       ...   
+       ... SPECIFICATIONS FOR LOCAL VARIABLES   
+       ...   
+       ... FIRST EXECUTABLE STATEMENT   
+
+
+       Parameter adjustments */
+    --sx;
+
+    /* Function Body   
+   Computing MIN */
+    i__1 = i_len(ifmt, ifmt_len);
+    lll = min(i__1,80);
+    i__1 = lll;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	*(unsigned char *)&line[i__ - 1] = '-';
+/* L10: */
+    }
+
+    for (i__ = lll + 1; i__ <= 80; ++i__) {
+	*(unsigned char *)&line[i__ - 1] = ' ';
+/* L20: */
+    }
+
+    io___4.ciunit = *lout;
+    s_wsfe(&io___4);
+    do_fio(&c__1, ifmt, ifmt_len);
+    do_fio(&c__1, line, lll);
+    e_wsfe();
+
+    if (*n <= 0) {
+	return 0;
+    }
+    ndigit = *idigit;
+    if (*idigit == 0) {
+	ndigit = 4;
+    }
+
+/* =======================================================================   
+               CODE FOR OUTPUT USING 72 COLUMNS FORMAT   
+   ======================================================================= */
+
+    if (*idigit < 0) {
+	ndigit = -(*idigit);
+	if (ndigit <= 4) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 5) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 4;
+		k2 = min(i__2,i__3);
+		io___8.ciunit = *lout;
+		s_wsfe(&io___8);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&sx[i__], (ftnlen)sizeof(doublereal)
+			    );
+		}
+		e_wsfe();
+/* L30: */
+	    }
+	} else if (ndigit <= 6) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 4) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 3;
+		k2 = min(i__2,i__3);
+		io___9.ciunit = *lout;
+		s_wsfe(&io___9);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&sx[i__], (ftnlen)sizeof(doublereal)
+			    );
+		}
+		e_wsfe();
+/* L40: */
+	    }
+	} else if (ndigit <= 10) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 3) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 2;
+		k2 = min(i__2,i__3);
+		io___10.ciunit = *lout;
+		s_wsfe(&io___10);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&sx[i__], (ftnlen)sizeof(doublereal)
+			    );
+		}
+		e_wsfe();
+/* L50: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 2) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 1;
+		k2 = min(i__2,i__3);
+		io___11.ciunit = *lout;
+		s_wsfe(&io___11);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&sx[i__], (ftnlen)sizeof(doublereal)
+			    );
+		}
+		e_wsfe();
+/* L60: */
+	    }
+	}
+
+/* =======================================================================   
+               CODE FOR OUTPUT USING 132 COLUMNS FORMAT   
+   ======================================================================= */
+
+    } else {
+	if (ndigit <= 4) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 10) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 9;
+		k2 = min(i__2,i__3);
+		io___12.ciunit = *lout;
+		s_wsfe(&io___12);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&sx[i__], (ftnlen)sizeof(doublereal)
+			    );
+		}
+		e_wsfe();
+/* L70: */
+	    }
+	} else if (ndigit <= 6) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 8) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 7;
+		k2 = min(i__2,i__3);
+		io___13.ciunit = *lout;
+		s_wsfe(&io___13);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&sx[i__], (ftnlen)sizeof(doublereal)
+			    );
+		}
+		e_wsfe();
+/* L80: */
+	    }
+	} else if (ndigit <= 10) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 6) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 5;
+		k2 = min(i__2,i__3);
+		io___14.ciunit = *lout;
+		s_wsfe(&io___14);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&sx[i__], (ftnlen)sizeof(doublereal)
+			    );
+		}
+		e_wsfe();
+/* L90: */
+	    }
+	} else {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 5) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 4;
+		k2 = min(i__2,i__3);
+		io___15.ciunit = *lout;
+		s_wsfe(&io___15);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&sx[i__], (ftnlen)sizeof(doublereal)
+			    );
+		}
+		e_wsfe();
+/* L100: */
+	    }
+	}
+    }
+    io___16.ciunit = *lout;
+    s_wsfe(&io___16);
+    e_wsfe();
+    return 0;
+} /* igraphdvout_ */
+
diff --git a/igraph/src/ef1asc_.c b/igraph/src/ef1asc_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ef1asc_.c
@@ -0,0 +1,25 @@
+/* EFL support routine to copy string b to string a */
+
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define M	( (long) (sizeof(long) - 1) )
+#define EVEN(x)	( ( (x)+ M) & (~M) )
+
+#ifdef KR_headers
+extern VOID s_copy();
+ef1asc_(a, la, b, lb) ftnint *a, *b; ftnlen *la, *lb;
+#else
+extern void s_copy(char*,char*,ftnlen,ftnlen);
+int ef1asc_(ftnint *a, ftnlen *la, ftnint *b, ftnlen *lb)
+#endif
+{
+s_copy( (char *)a, (char *)b, EVEN(*la), *lb );
+return 0;	/* ignored return value */
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/ef1cmc_.c b/igraph/src/ef1cmc_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ef1cmc_.c
@@ -0,0 +1,20 @@
+/* EFL support routine to compare two character strings */
+
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+integer ef1cmc_(a, la, b, lb) ftnint *a, *b; ftnlen *la, *lb;
+#else
+extern integer s_cmp(char*,char*,ftnlen,ftnlen);
+integer ef1cmc_(ftnint *a, ftnlen *la, ftnint *b, ftnlen *lb)
+#endif
+{
+return( s_cmp( (char *)a, (char *)b, *la, *lb) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/eigen.c b/igraph/src/eigen.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/eigen.c
@@ -0,0 +1,1520 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_eigen.h"
+#include "igraph_qsort.h"
+#include "igraph_blas.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+int igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t *fun,
+                                    int n, void *extra,
+                                    igraph_matrix_t *res) {
+
+    int i;
+    igraph_vector_t v;
+
+    IGRAPH_CHECK(igraph_matrix_init(res, n, n));
+    IGRAPH_FINALLY(igraph_matrix_destroy, res);
+    IGRAPH_VECTOR_INIT_FINALLY(&v, n);
+    VECTOR(v)[0] = 1;
+    IGRAPH_CHECK(fun(/*to=*/ &MATRIX(*res, 0, 0), /*from=*/ VECTOR(v), n,
+                             extra));
+    for (i = 1; i < n; i++) {
+        VECTOR(v)[i - 1] = 0;
+        VECTOR(v)[i  ] = 1;
+        IGRAPH_CHECK(fun(/*to=*/ &MATRIX(*res, 0, i), /*from=*/ VECTOR(v), n,
+                                 extra));
+    }
+    igraph_vector_destroy(&v);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack_lm(const igraph_matrix_t *A,
+        const igraph_eigen_which_t *which,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    igraph_matrix_t vec1, vec2;
+    igraph_vector_t val1, val2;
+    int n = (int) igraph_matrix_nrow(A);
+    int p1 = 0, p2 = which->howmany - 1, pr = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&val1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&val2, 0);
+
+    if (vectors) {
+        IGRAPH_CHECK(igraph_matrix_init(&vec1, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &vec1);
+        IGRAPH_CHECK(igraph_matrix_init(&vec2, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &vec1);
+    }
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT,
+                                      /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ 1, /*iu=*/ which->howmany,
+                                      /*abstol=*/ 1e-14, &val1,
+                                      vectors ? &vec1 : 0,
+                                      /*support=*/ 0));
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT,
+                                      /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ n - which->howmany + 1, /*iu=*/ n,
+                                      /*abstol=*/ 1e-14, &val2,
+                                      vectors ? &vec2 : 0,
+                                      /*support=*/ 0));
+
+    if (values) {
+        IGRAPH_CHECK(igraph_vector_resize(values, which->howmany));
+    }
+    if (vectors) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany));
+    }
+
+    while (pr < which->howmany) {
+        if (p2 < 0 || fabs(VECTOR(val1)[p1]) > fabs(VECTOR(val2)[p2])) {
+            if (values) {
+                VECTOR(*values)[pr] = VECTOR(val1)[p1];
+            }
+            if (vectors) {
+                memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec1, 0, p1),
+                       sizeof(igraph_real_t) * (size_t) n);
+            }
+            p1++;
+            pr++;
+        } else {
+            if (values) {
+                VECTOR(*values)[pr] = VECTOR(val2)[p2];
+            }
+            if (vectors) {
+                memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec2, 0, p2),
+                       sizeof(igraph_real_t) * (size_t) n);
+            }
+            p2--;
+            pr++;
+        }
+    }
+
+
+    if (vectors) {
+        igraph_matrix_destroy(&vec2);
+        igraph_matrix_destroy(&vec1);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+    igraph_vector_destroy(&val2);
+    igraph_vector_destroy(&val1);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack_sm(const igraph_matrix_t *A,
+        const igraph_eigen_which_t *which,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    igraph_vector_t val;
+    igraph_matrix_t vec;
+    int i, w = 0, n = (int) igraph_matrix_nrow(A);
+    igraph_real_t small;
+    int p1, p2, pr = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&val, 0);
+
+    if (vectors) {
+        IGRAPH_MATRIX_INIT_FINALLY(&vec, 0, 0);
+    }
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_ALL, /*vl=*/ 0,
+                                      /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ 0, /*iu=*/ 0,
+                                      /*abstol=*/ 1e-14, &val,
+                                      vectors ? &vec : 0,
+                                      /*support=*/ 0));
+
+    /* Look for smallest value */
+    small = fabs(VECTOR(val)[0]);
+    for (i = 1; i < n; i++) {
+        igraph_real_t v = fabs(VECTOR(val)[i]);
+        if (v < small) {
+            small = v;
+            w = i;
+        }
+    }
+    p1 = w - 1; p2 = w;
+
+    if (values) {
+        IGRAPH_CHECK(igraph_vector_resize(values, which->howmany));
+    }
+    if (vectors) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany));
+    }
+
+    while (pr < which->howmany) {
+        if (p2 == n - 1 || fabs(VECTOR(val)[p1]) < fabs(VECTOR(val)[p2])) {
+            if (values) {
+                VECTOR(*values)[pr] = VECTOR(val)[p1];
+            }
+            if (vectors) {
+                memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec, 0, p1),
+                       sizeof(igraph_real_t) * (size_t) n);
+            }
+            p1--;
+            pr++;
+        } else {
+            if (values) {
+                VECTOR(*values)[pr] = VECTOR(val)[p2];
+            }
+            if (vectors) {
+                memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec, 0, p2),
+                       sizeof(igraph_real_t) * (size_t) n);
+            }
+            p2++;
+            pr++;
+        }
+    }
+
+    if (vectors) {
+        igraph_matrix_destroy(&vec);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    igraph_vector_destroy(&val);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack_la(const igraph_matrix_t *A,
+        const igraph_eigen_which_t *which,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    /* TODO: ordering? */
+
+    int n = (int) igraph_matrix_nrow(A);
+    int il = n - which->howmany + 1;
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT,
+                                      /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ il, /*iu=*/ n,
+                                      /*abstol=*/ 1e-14, values, vectors,
+                                      /*support=*/ 0));
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack_sa(const igraph_matrix_t *A,
+        const igraph_eigen_which_t *which,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    /* TODO: ordering? */
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT,
+                                      /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ 1, /*iu=*/ which->howmany,
+                                      /*abstol=*/ 1e-14, values, vectors,
+                                      /*support=*/ 0));
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_matrix_t *A,
+        const igraph_eigen_which_t *which,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    /* TODO: ordering? */
+
+    igraph_matrix_t vec1, vec2;
+    igraph_vector_t val1, val2;
+    int n = (int) igraph_matrix_nrow(A);
+    int p1 = 0, p2 = which->howmany / 2, pr = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&val1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&val2, 0);
+
+    if (vectors) {
+        IGRAPH_CHECK(igraph_matrix_init(&vec1, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &vec1);
+        IGRAPH_CHECK(igraph_matrix_init(&vec2, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &vec1);
+    }
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT,
+                                      /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ 1, /*iu=*/ (which->howmany) / 2,
+                                      /*abstol=*/ 1e-14, &val1,
+                                      vectors ? &vec1 : 0,
+                                      /*support=*/ 0));
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT,
+                                      /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ n - (which->howmany) / 2, /*iu=*/ n,
+                                      /*abstol=*/ 1e-14, &val2,
+                                      vectors ? &vec2 : 0,
+                                      /*support=*/ 0));
+
+    if (values) {
+        IGRAPH_CHECK(igraph_vector_resize(values, which->howmany));
+    }
+    if (vectors) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, n, which->howmany));
+    }
+
+    while (pr < which->howmany) {
+        if (pr % 2) {
+            if (values) {
+                VECTOR(*values)[pr] = VECTOR(val1)[p1];
+            }
+            if (vectors) {
+                memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec1, 0, p1),
+                       sizeof(igraph_real_t) * (size_t) n);
+            }
+            p1++;
+            pr++;
+        } else {
+            if (values) {
+                VECTOR(*values)[pr] = VECTOR(val2)[p2];
+            }
+            if (vectors) {
+                memcpy(&MATRIX(*vectors, 0, pr), &MATRIX(vec2, 0, p2),
+                       sizeof(igraph_real_t) * (size_t) n);
+            }
+            p2--;
+            pr++;
+        }
+    }
+
+    if (vectors) {
+        igraph_matrix_destroy(&vec2);
+        igraph_matrix_destroy(&vec1);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+    igraph_vector_destroy(&val2);
+    igraph_vector_destroy(&val1);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_matrix_t *A,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_ALL, /*vl=*/ 0,
+                                      /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ 0, /*iu=*/ 0,
+                                      /*abstol=*/ 1e-14, values, vectors,
+                                      /*support=*/ 0));
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack_iv(const igraph_matrix_t *A,
+        const igraph_eigen_which_t *which,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_INTERVAL,
+                                      /*vl=*/ which->vl, /*vu=*/ which->vu,
+                                      /*vestimate=*/ which->vestimate,
+                                      /*il=*/ 0, /*iu=*/ 0,
+                                      /*abstol=*/ 1e-14, values, vectors,
+                                      /*support=*/ 0));
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack_sel(const igraph_matrix_t *A,
+        const igraph_eigen_which_t *which,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    IGRAPH_CHECK(igraph_lapack_dsyevr(A, IGRAPH_LAPACK_DSYEV_SELECT,
+                                      /*vl=*/ 0, /*vu=*/ 0, /*vestimate=*/ 0,
+                                      /*il=*/ which->il, /*iu=*/ which->iu,
+                                      /*abstol=*/ 1e-14, values, vectors,
+                                      /*support=*/ 0));
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix_t *A,
+        const igraph_sparsemat_t *sA,
+        igraph_arpack_function_t *fun,
+        int n, void *extra,
+        const igraph_eigen_which_t *which,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    const igraph_matrix_t *myA = A;
+    igraph_matrix_t mA;
+
+    /* First we need to create a dense square matrix */
+
+    if (A) {
+        n = (int) igraph_matrix_nrow(A);
+    } else if (sA) {
+        n = (int) igraph_sparsemat_nrow(sA);
+        IGRAPH_CHECK(igraph_matrix_init(&mA, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &mA);
+        IGRAPH_CHECK(igraph_sparsemat_as_matrix(&mA, sA));
+        myA = &mA;
+    } else if (fun) {
+        IGRAPH_CHECK(igraph_i_eigen_arpackfun_to_mat(fun, n, extra, &mA));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &mA);
+        myA = &mA;
+    }
+
+    switch (which->pos) {
+    case IGRAPH_EIGEN_LM:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_lm(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_SM:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sm(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_LA:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_la(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_SA:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sa(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_BE:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_be(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_ALL:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_all(myA,
+                     values,
+                     vectors));
+        break;
+    case IGRAPH_EIGEN_INTERVAL:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_iv(myA, which,
+                     values,
+                     vectors));
+        break;
+    case IGRAPH_EIGEN_SELECT:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack_sel(myA, which,
+                     values,
+                     vectors));
+        break;
+    default:
+        /* This cannot happen */
+        break;
+    }
+
+    if (!A) {
+        igraph_matrix_destroy(&mA);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+typedef struct igraph_i_eigen_matrix_sym_arpack_data_t {
+    const igraph_matrix_t *A;
+    const igraph_sparsemat_t *sA;
+} igraph_i_eigen_matrix_sym_arpack_data_t;
+
+int igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to,
+                                        const igraph_real_t *from,
+                                        int n, void *extra) {
+
+    igraph_i_eigen_matrix_sym_arpack_data_t *data =
+        (igraph_i_eigen_matrix_sym_arpack_data_t *) extra;
+
+    if (data->A) {
+        igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0,
+                                               data->A, from, /*beta=*/ 0.0, to);
+    } else { /* data->sA */
+        igraph_vector_t vto, vfrom;
+        igraph_vector_view(&vto, to, n);
+        igraph_vector_view(&vfrom, to, n);
+        igraph_vector_null(&vto);
+        igraph_sparsemat_gaxpy(data->sA, &vfrom, &vto);
+    }
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_arpack_be(const igraph_matrix_t *A,
+        const igraph_sparsemat_t *sA,
+        igraph_arpack_function_t *fun,
+        int n, void *extra,
+        const igraph_eigen_which_t *which,
+        igraph_arpack_options_t *options,
+        igraph_arpack_storage_t *storage,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    igraph_vector_t tmpvalues, tmpvalues2;
+    igraph_matrix_t tmpvectors, tmpvectors2;
+    igraph_i_eigen_matrix_sym_arpack_data_t myextra = { A, sA };
+    int low = (int) floor(which->howmany / 2.0), high = (int) ceil(which->howmany / 2.0);
+    int l1, l2, w;
+
+    if (low + high >= n) {
+        IGRAPH_ERROR("Requested too many eigenvalues/vectors", IGRAPH_EINVAL);
+    }
+
+    if (!fun) {
+        fun = igraph_i_eigen_matrix_sym_arpack_cb;
+        extra = (void*) &myextra;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tmpvalues, high);
+    IGRAPH_MATRIX_INIT_FINALLY(&tmpvectors, n, high);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmpvalues2, low);
+    IGRAPH_MATRIX_INIT_FINALLY(&tmpvectors2, n, low);
+
+    options->n = n;
+    options->nev = high;
+    options->ncv = 2 * options->nev < n ? 2 * options->nev : n;
+    options->which[0] = 'L'; options->which[1] = 'A';
+
+    IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage,
+                                       &tmpvalues, &tmpvectors));
+
+    options->nev = low;
+    options->ncv = 2 * options->nev < n ? 2 * options->nev : n;
+    options->which[0] = 'S'; options->which[1] = 'A';
+
+    IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage,
+                                       &tmpvalues2, &tmpvectors2));
+
+    IGRAPH_CHECK(igraph_vector_resize(values, low + high));
+    IGRAPH_CHECK(igraph_matrix_resize(vectors, n, low + high));
+
+    l1 = 0; l2 = 0; w = 0;
+    while (w < which->howmany) {
+        VECTOR(*values)[w] = VECTOR(tmpvalues)[l1];
+        memcpy(&MATRIX(*vectors, 0, w), &MATRIX(tmpvectors, 0, l1),
+               (size_t) n * sizeof(igraph_real_t));
+        w++; l1++;
+        if (w < which->howmany) {
+            VECTOR(*values)[w] = VECTOR(tmpvalues2)[l2];
+            memcpy(&MATRIX(*vectors, 0, w), &MATRIX(tmpvectors2, 0, l2),
+                   (size_t) n * sizeof(igraph_real_t));
+            w++; l2++;
+        }
+    }
+
+    igraph_matrix_destroy(&tmpvectors2);
+    igraph_vector_destroy(&tmpvalues2);
+    igraph_matrix_destroy(&tmpvectors);
+    igraph_vector_destroy(&tmpvalues);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_symmetric_arpack(const igraph_matrix_t *A,
+        const igraph_sparsemat_t *sA,
+        igraph_arpack_function_t *fun,
+        int n, void *extra,
+        const igraph_eigen_which_t *which,
+        igraph_arpack_options_t *options,
+        igraph_arpack_storage_t *storage,
+        igraph_vector_t *values,
+        igraph_matrix_t *vectors) {
+
+    /* For ARPACK we need a matrix multiplication operation.
+       This can be done in any format, so everything is fine,
+       we don't have to convert. */
+
+    igraph_i_eigen_matrix_sym_arpack_data_t myextra = { A, sA };
+
+    if (!options) {
+        IGRAPH_ERROR("`options' must be given for ARPACK algorithm",
+                     IGRAPH_EINVAL);
+    }
+
+    if (which->pos == IGRAPH_EIGEN_BE) {
+        return igraph_i_eigen_matrix_symmetric_arpack_be(A, sA, fun, n, extra,
+                which, options, storage,
+                values, vectors);
+    } else {
+
+        switch (which->pos) {
+        case IGRAPH_EIGEN_LM:
+            options->which[0] = 'L'; options->which[1] = 'M';
+            options->nev = which->howmany;
+            break;
+        case IGRAPH_EIGEN_SM:
+            options->which[0] = 'S'; options->which[1] = 'M';
+            options->nev = which->howmany;
+            break;
+        case IGRAPH_EIGEN_LA:
+            options->which[0] = 'L'; options->which[1] = 'A';
+            options->nev = which->howmany;
+            break;
+        case IGRAPH_EIGEN_SA:
+            options->which[0] = 'S'; options->which[1] = 'A';
+            options->nev = which->howmany;
+            break;
+        case IGRAPH_EIGEN_ALL:
+            options->which[0] = 'L'; options->which[1] = 'M';
+            options->nev = n;
+            break;
+        case IGRAPH_EIGEN_INTERVAL:
+            IGRAPH_ERROR("Interval of eigenvectors with ARPACK",
+                         IGRAPH_UNIMPLEMENTED);
+            /* TODO */
+            break;
+        case IGRAPH_EIGEN_SELECT:
+            IGRAPH_ERROR("Selected eigenvalues with ARPACK",
+                         IGRAPH_UNIMPLEMENTED);
+            /* TODO */
+            break;
+        default:
+            /* This cannot happen */
+            break;
+        }
+
+        options->n = n;
+        options->ncv = 2 * options->nev < n ? 2 * options->nev : n;
+
+        if (!fun) {
+            fun = igraph_i_eigen_matrix_sym_arpack_cb;
+            extra = (void*) &myextra;
+        }
+
+        IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage,
+                                           values, vectors));
+        return 0;
+    }
+}
+
+/* Get the eigenvalues and the eigenvectors from the compressed
+   form. Order them according to the ordering criteria.
+   Comparison functions for the reordering first */
+
+typedef int (*igraph_i_eigen_matrix_lapack_cmp_t)(void*, const void*,
+        const void *);
+
+typedef struct igraph_i_eml_cmp_t {
+    const igraph_vector_t *mag, *real, *imag;
+} igraph_i_eml_cmp_t;
+
+/* TODO: these should be defined in some header */
+
+#define EPS        (DBL_EPSILON*100)
+#define LESS(a,b)  ((a) < (b)-EPS)
+#define MORE(a,b)  ((a) > (b)+EPS)
+#define ZERO(a)    ((a) > -EPS && (a) < EPS)
+#define NONZERO(a) ((a) < -EPS || (a) > EPS)
+
+/* Largest magnitude. Ordering is according to
+   1 Larger magnitude
+   2 Real eigenvalues before complex ones
+   3 Larger real part
+   4 Larger imaginary part */
+
+int igraph_i_eigen_matrix_lapack_cmp_lm(void *extra, const void *a,
+                                        const void *b) {
+    igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra;
+    int *aa = (int*) a, *bb = (int*) b;
+    igraph_real_t a_m = VECTOR(*myextra->mag)[*aa];
+    igraph_real_t b_m = VECTOR(*myextra->mag)[*bb];
+
+    if (LESS(a_m, b_m)) {
+        return 1;
+    } else if (MORE(a_m, b_m)) {
+        return -1;
+    } else {
+        igraph_real_t a_r = VECTOR(*myextra->real)[*aa];
+        igraph_real_t a_i = VECTOR(*myextra->imag)[*aa];
+        igraph_real_t b_r = VECTOR(*myextra->real)[*bb];
+        igraph_real_t b_i = VECTOR(*myextra->imag)[*bb];
+        if (ZERO(a_i)    && NONZERO(b_i))  {
+            return -1;
+        }
+        if (NONZERO(a_i) && ZERO(b_i))     {
+            return  1;
+        }
+        if (MORE(a_r, b_r)) {
+            return -1;
+        }
+        if (LESS(a_r, b_r)) {
+            return  1;
+        }
+        if (MORE(a_i, b_i)) {
+            return -1;
+        }
+        if (LESS(a_i, b_i)) {
+            return  1;
+        }
+    }
+    return 0;
+}
+
+/* Smallest marginude. Ordering is according to
+   1 Magnitude (smaller first)
+   2 Complex eigenvalues before real ones
+   3 Smaller real part
+   4 Smaller imaginary part
+   This ensures that lm has exactly the opposite order to sm */
+
+int igraph_i_eigen_matrix_lapack_cmp_sm(void *extra, const void *a,
+                                        const void *b) {
+    igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra;
+    int *aa = (int*) a, *bb = (int*) b;
+    igraph_real_t a_m = VECTOR(*myextra->mag)[*aa];
+    igraph_real_t b_m = VECTOR(*myextra->mag)[*bb];
+
+    if (MORE(a_m, b_m)) {
+        return 1;
+    } else if (LESS(a_m, b_m)) {
+        return -1;
+    } else {
+        igraph_real_t a_r = VECTOR(*myextra->real)[*aa];
+        igraph_real_t a_i = VECTOR(*myextra->imag)[*aa];
+        igraph_real_t b_r = VECTOR(*myextra->real)[*bb];
+        igraph_real_t b_i = VECTOR(*myextra->imag)[*bb];
+        if (NONZERO(a_i) && ZERO(b_i))    {
+            return -1;
+        }
+        if (ZERO(a_i)    && NONZERO(b_i)) {
+            return  1;
+        }
+        if (LESS(a_r, b_r)) {
+            return -1;
+        }
+        if (MORE(a_r, b_r)) {
+            return  1;
+        }
+        if (LESS(a_i, b_i)) {
+            return -1;
+        }
+        if (MORE(a_i, b_i)) {
+            return  1;
+        }
+    }
+    return 0;
+}
+
+/* Largest real part. Ordering is according to
+   1 Larger real part
+   2 Real eigenvalues come before complex ones
+   3 Larger complex part */
+
+int igraph_i_eigen_matrix_lapack_cmp_lr(void *extra, const void *a,
+                                        const void *b) {
+
+    igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra;
+    int *aa = (int*) a, *bb = (int*) b;
+    igraph_real_t a_r = VECTOR(*myextra->real)[*aa];
+    igraph_real_t b_r = VECTOR(*myextra->real)[*bb];
+
+    if (MORE(a_r, b_r)) {
+        return -1;
+    } else if (LESS(a_r, b_r)) {
+        return 1;
+    } else {
+        igraph_real_t a_i = VECTOR(*myextra->imag)[*aa];
+        igraph_real_t b_i = VECTOR(*myextra->imag)[*bb];
+        if (ZERO(a_i) && NONZERO(b_i)) {
+            return -1;
+        }
+        if (NONZERO(a_i) && ZERO(b_i)) {
+            return  1;
+        }
+        if (MORE(a_i, b_i)) {
+            return -1;
+        }
+        if (LESS(a_i, b_i)) {
+            return  1;
+        }
+    }
+
+    return 0;
+}
+
+/* Largest real part. Ordering is according to
+   1 Smaller real part
+   2 Complex eigenvalues come before real ones
+   3 Smaller complex part
+   This is opposite to LR
+*/
+
+int igraph_i_eigen_matrix_lapack_cmp_sr(void *extra, const void *a,
+                                        const void *b) {
+
+    igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra;
+    int *aa = (int*) a, *bb = (int*) b;
+    igraph_real_t a_r = VECTOR(*myextra->real)[*aa];
+    igraph_real_t b_r = VECTOR(*myextra->real)[*bb];
+
+    if (LESS(a_r, b_r)) {
+        return -1;
+    } else if (MORE(a_r, b_r)) {
+        return 1;
+    } else {
+        igraph_real_t a_i = VECTOR(*myextra->imag)[*aa];
+        igraph_real_t b_i = VECTOR(*myextra->imag)[*bb];
+        if (NONZERO(a_i) && ZERO(b_i)) {
+            return -1;
+        }
+        if (ZERO(a_i) && NONZERO(b_i)) {
+            return  1;
+        }
+        if (LESS(a_i, b_i)) {
+            return -1;
+        }
+        if (MORE(a_i, b_i)) {
+            return  1;
+        }
+    }
+
+    return 0;
+}
+
+/* Order:
+   1 Larger imaginary part
+   2 Real eigenvalues before complex ones
+   3 Larger real part */
+
+int igraph_i_eigen_matrix_lapack_cmp_li(void *extra, const void *a,
+                                        const void *b) {
+
+    igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra;
+    int *aa = (int*) a, *bb = (int*) b;
+    igraph_real_t a_i = VECTOR(*myextra->imag)[*aa];
+    igraph_real_t b_i = VECTOR(*myextra->imag)[*bb];
+
+    if (MORE(a_i, b_i)) {
+        return -1;
+    } else if (LESS(a_i, b_i)) {
+        return 1;
+    } else {
+        igraph_real_t a_r = VECTOR(*myextra->real)[*aa];
+        igraph_real_t b_r = VECTOR(*myextra->real)[*bb];
+        if (ZERO(a_i) && NONZERO(b_i)) {
+            return -1;
+        }
+        if (NONZERO(a_i) && ZERO(b_i)) {
+            return  1;
+        }
+        if (MORE(a_r, b_r)) {
+            return -1;
+        }
+        if (LESS(a_r, b_r)) {
+            return  1;
+        }
+    }
+
+    return 0;
+}
+
+/* Order:
+   1 Smaller imaginary part
+   2 Complex eigenvalues before real ones
+   3 Smaller real part
+   Order is opposite to LI */
+
+int igraph_i_eigen_matrix_lapack_cmp_si(void *extra, const void *a,
+                                        const void *b) {
+
+    igraph_i_eml_cmp_t *myextra = (igraph_i_eml_cmp_t *) extra;
+    int *aa = (int*) a, *bb = (int*) b;
+    igraph_real_t a_i = VECTOR(*myextra->imag)[*aa];
+    igraph_real_t b_i = VECTOR(*myextra->imag)[*bb];
+
+    if (LESS(a_i, b_i)) {
+        return -1;
+    } else if (MORE(a_i, b_i)) {
+        return 1;
+    } else {
+        igraph_real_t a_r = VECTOR(*myextra->real)[*aa];
+        igraph_real_t b_r = VECTOR(*myextra->real)[*bb];
+        if (NONZERO(a_i) && ZERO(b_i)) {
+            return -1;
+        }
+        if (ZERO(a_i) && NONZERO(b_i)) {
+            return  1;
+        }
+        if (LESS(a_r, b_r)) {
+            return -1;
+        }
+        if (MORE(a_r, b_r)) {
+            return  1;
+        }
+    }
+
+    return 0;
+}
+
+#undef EPS
+#undef LESS
+#undef MORE
+#undef ZERO
+#undef NONZERO
+
+#define INITMAG()                           \
+    do {                                  \
+        int i;                              \
+        IGRAPH_VECTOR_INIT_FINALLY(&mag, nev);              \
+        hasmag=1;                               \
+        for (i=0; i<nev; i++) {                     \
+            VECTOR(mag)[i] = VECTOR(*real)[i] * VECTOR(*real)[i] +        \
+                             VECTOR(*imag)[i] * VECTOR(*imag)[i];                \
+        }                                   \
+    } while (0)
+
+int igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real,
+        const igraph_vector_t *imag,
+        const igraph_matrix_t *compressed,
+        const igraph_eigen_which_t *which,
+        igraph_vector_complex_t *values,
+        igraph_matrix_complex_t *vectors) {
+    igraph_vector_int_t idx;
+    igraph_vector_t mag;
+    igraph_bool_t hasmag = 0;
+    int nev = (int) igraph_vector_size(real);
+    int howmany = 0, start = 0;
+    int i;
+    igraph_i_eigen_matrix_lapack_cmp_t cmpfunc = 0;
+    igraph_i_eml_cmp_t vextra = { &mag, real, imag };
+    void *extra = &vextra;
+
+    IGRAPH_CHECK(igraph_vector_int_init(&idx, nev));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &idx);
+
+    switch (which->pos) {
+    case IGRAPH_EIGEN_LM:
+        INITMAG();
+        cmpfunc = igraph_i_eigen_matrix_lapack_cmp_lm;
+        howmany = which->howmany;
+        break;
+    case IGRAPH_EIGEN_ALL:
+        INITMAG();
+        cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm;
+        howmany = nev;
+        break;
+    case IGRAPH_EIGEN_SM:
+        INITMAG();
+        cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm;
+        howmany = which->howmany;
+        break;
+    case IGRAPH_EIGEN_LR:
+        cmpfunc = igraph_i_eigen_matrix_lapack_cmp_lr;
+        howmany = which->howmany;
+        break;
+    case IGRAPH_EIGEN_SR:
+        cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sr;
+        howmany = which->howmany;
+        break;
+    case IGRAPH_EIGEN_SELECT:
+        INITMAG();
+        cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm;
+        start = which->il - 1;
+        howmany = which->iu - which->il + 1;
+        break;
+    case IGRAPH_EIGEN_LI:
+        cmpfunc = igraph_i_eigen_matrix_lapack_cmp_li;
+        howmany = which->howmany;
+        break;
+    case IGRAPH_EIGEN_SI:
+        cmpfunc = igraph_i_eigen_matrix_lapack_cmp_si;
+        howmany = which->howmany;
+        break;
+    case IGRAPH_EIGEN_INTERVAL:
+    case IGRAPH_EIGEN_BE:
+    default:
+        IGRAPH_ERROR("Unimplemented eigenvalue ordering", IGRAPH_UNIMPLEMENTED);
+        break;
+    }
+
+    for (i = 0; i < nev; i++) {
+        VECTOR(idx)[i] = i;
+    }
+
+    igraph_qsort_r(VECTOR(idx), (size_t) nev, sizeof(VECTOR(idx)[0]), extra,
+                   cmpfunc);
+
+    if (hasmag) {
+        igraph_vector_destroy(&mag);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (values) {
+        IGRAPH_CHECK(igraph_vector_complex_resize(values, howmany));
+        for (i = 0; i < howmany; i++) {
+            int x = VECTOR(idx)[start + i];
+            VECTOR(*values)[i] = igraph_complex(VECTOR(*real)[x],
+                                                VECTOR(*imag)[x]);
+        }
+    }
+
+    if (vectors) {
+        int n = (int) igraph_matrix_nrow(compressed);
+        IGRAPH_CHECK(igraph_matrix_complex_resize(vectors, n, howmany));
+        for (i = 0; i < howmany; i++) {
+            int j, x = VECTOR(idx)[start + i];
+            if (VECTOR(*imag)[x] == 0) {
+                /* real eigenvalue */
+                for (j = 0; j < n; j++) {
+                    MATRIX(*vectors, j, i) = igraph_complex(MATRIX(*compressed, j, x),
+                                                            0.0);
+                }
+            } else {
+                /* complex eigenvalue */
+                int neg = 1, co = 0;
+                if (VECTOR(*imag)[x] < 0) {
+                    neg = -1;
+                    co = 1;
+                }
+                for (j = 0; j < n; j++) {
+                    MATRIX(*vectors, j, i) =
+                        igraph_complex(MATRIX(*compressed, j, x - co),
+                                       neg * MATRIX(*compressed, j, x + 1 - co));
+                }
+            }
+        }
+    }
+
+    igraph_vector_int_destroy(&idx);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_eigen_matrix_lapack_common(const igraph_matrix_t *A,
+                                        const igraph_eigen_which_t *which,
+                                        igraph_vector_complex_t *values,
+                                        igraph_matrix_complex_t *vectors) {
+
+    igraph_vector_t valuesreal, valuesimag;
+    igraph_matrix_t vectorsright, *myvectors = vectors ? &vectorsright : 0;
+    int n = (int) igraph_matrix_nrow(A);
+    int info = 1;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&valuesreal, n);
+    IGRAPH_VECTOR_INIT_FINALLY(&valuesimag, n);
+    if (vectors) {
+        IGRAPH_MATRIX_INIT_FINALLY(&vectorsright, n, n);
+    }
+    IGRAPH_CHECK(igraph_lapack_dgeev(A, &valuesreal, &valuesimag,
+                                     /*vectorsleft=*/ 0, myvectors, &info));
+
+    IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_reorder(&valuesreal,
+                 &valuesimag,
+                 myvectors, which, values,
+                 vectors));
+
+    if (vectors) {
+        igraph_matrix_destroy(&vectorsright);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&valuesimag);
+    igraph_vector_destroy(&valuesreal);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+
+}
+
+int igraph_i_eigen_matrix_lapack_lm(const igraph_matrix_t *A,
+                                    const igraph_eigen_which_t *which,
+                                    igraph_vector_complex_t *values,
+                                    igraph_matrix_complex_t *vectors) {
+    return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors);
+}
+
+int igraph_i_eigen_matrix_lapack_sm(const igraph_matrix_t *A,
+                                    const igraph_eigen_which_t *which,
+                                    igraph_vector_complex_t *values,
+                                    igraph_matrix_complex_t *vectors) {
+    return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors);
+}
+
+int igraph_i_eigen_matrix_lapack_lr(const igraph_matrix_t *A,
+                                    const igraph_eigen_which_t *which,
+                                    igraph_vector_complex_t *values,
+                                    igraph_matrix_complex_t *vectors) {
+    return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors);
+}
+
+
+int igraph_i_eigen_matrix_lapack_sr(const igraph_matrix_t *A,
+                                    const igraph_eigen_which_t *which,
+                                    igraph_vector_complex_t *values,
+                                    igraph_matrix_complex_t *vectors) {
+    return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors);
+}
+
+int igraph_i_eigen_matrix_lapack_li(const igraph_matrix_t *A,
+                                    const igraph_eigen_which_t *which,
+                                    igraph_vector_complex_t *values,
+                                    igraph_matrix_complex_t *vectors) {
+    return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors);
+}
+
+int igraph_i_eigen_matrix_lapack_si(const igraph_matrix_t *A,
+                                    const igraph_eigen_which_t *which,
+                                    igraph_vector_complex_t *values,
+                                    igraph_matrix_complex_t *vectors) {
+    return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors);
+}
+
+int igraph_i_eigen_matrix_lapack_select(const igraph_matrix_t *A,
+                                        const igraph_eigen_which_t *which,
+                                        igraph_vector_complex_t *values,
+                                        igraph_matrix_complex_t *vectors) {
+    return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors);
+}
+
+int igraph_i_eigen_matrix_lapack_all(const igraph_matrix_t *A,
+                                     const igraph_eigen_which_t *which,
+                                     igraph_vector_complex_t *values,
+                                     igraph_matrix_complex_t *vectors) {
+    return igraph_i_eigen_matrix_lapack_common(A, which, values, vectors);
+}
+
+int igraph_i_eigen_matrix_lapack(const igraph_matrix_t *A,
+                                 const igraph_sparsemat_t *sA,
+                                 igraph_arpack_function_t *fun,
+                                 int n, void *extra,
+                                 const igraph_eigen_which_t *which,
+                                 igraph_vector_complex_t *values,
+                                 igraph_matrix_complex_t *vectors) {
+
+    const igraph_matrix_t *myA = A;
+    igraph_matrix_t mA;
+
+    /* We need to create a dense square matrix first */
+
+    if (A) {
+        n = (int) igraph_matrix_nrow(A);
+    } else if (sA) {
+        n = (int) igraph_sparsemat_nrow(sA);
+        IGRAPH_CHECK(igraph_matrix_init(&mA, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &mA);
+        IGRAPH_CHECK(igraph_sparsemat_as_matrix(&mA, sA));
+        myA = &mA;
+    } else if (fun) {
+        IGRAPH_CHECK(igraph_i_eigen_arpackfun_to_mat(fun, n, extra, &mA));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &mA);
+    }
+
+    switch (which->pos) {
+    case IGRAPH_EIGEN_LM:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_lm(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_SM:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_sm(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_LR:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_lr(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_SR:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_sr(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_LI:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_li(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_SI:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_si(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_SELECT:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_select(myA, which,
+                     values, vectors));
+        break;
+    case IGRAPH_EIGEN_ALL:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_all(myA, which,
+                     values,
+                     vectors));
+        break;
+    default:
+        /* This cannot happen */
+        break;
+    }
+
+    if (!A) {
+        igraph_matrix_destroy(&mA);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+int igraph_i_eigen_checks(const igraph_matrix_t *A,
+                          const igraph_sparsemat_t *sA,
+                          igraph_arpack_function_t *fun, int n) {
+
+    if ( (A ? 1 : 0) + (sA ? 1 : 0) + (fun ? 1 : 0) != 1) {
+        IGRAPH_ERROR("Exactly one of 'A', 'sA' and 'fun' must be given",
+                     IGRAPH_EINVAL);
+    }
+
+    if (A) {
+        if (n != igraph_matrix_ncol(A) || n != igraph_matrix_nrow(A)) {
+            IGRAPH_ERROR("Invalid matrix", IGRAPH_NONSQUARE);
+        }
+    } else if (sA) {
+        if (n != igraph_sparsemat_ncol(sA) || n != igraph_sparsemat_nrow(sA)) {
+            IGRAPH_ERROR("Invalid matrix", IGRAPH_NONSQUARE);
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_eigen_matrix_symmetric
+ *
+ * \example examples/simple/igraph_eigen_matrix_symmetric.c
+ */
+
+int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A,
+                                  const igraph_sparsemat_t *sA,
+                                  igraph_arpack_function_t *fun, int n,
+                                  void *extra,
+                                  igraph_eigen_algorithm_t algorithm,
+                                  const igraph_eigen_which_t *which,
+                                  igraph_arpack_options_t *options,
+                                  igraph_arpack_storage_t *storage,
+                                  igraph_vector_t *values,
+                                  igraph_matrix_t *vectors) {
+
+    IGRAPH_CHECK(igraph_i_eigen_checks(A, sA, fun, n));
+
+    if (which->pos != IGRAPH_EIGEN_LM &&
+        which->pos != IGRAPH_EIGEN_SM &&
+        which->pos != IGRAPH_EIGEN_LA &&
+        which->pos != IGRAPH_EIGEN_SA &&
+        which->pos != IGRAPH_EIGEN_BE &&
+        which->pos != IGRAPH_EIGEN_ALL &&
+        which->pos != IGRAPH_EIGEN_INTERVAL &&
+        which->pos != IGRAPH_EIGEN_SELECT) {
+        IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL);
+    }
+
+    switch (algorithm) {
+    case IGRAPH_EIGEN_AUTO:
+        if (which->howmany == n || n < 100) {
+            IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n,
+                         extra, which,
+                         values, vectors));
+        } else {
+            IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n,
+                         extra, which,
+                         options, storage,
+                         values, vectors));
+        }
+        break;
+    case IGRAPH_EIGEN_LAPACK:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n, extra,
+                     which, values,
+                     vectors));
+        break;
+    case IGRAPH_EIGEN_ARPACK:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, extra,
+                     which, options,
+                     storage,
+                     values, vectors));
+        break;
+    default:
+        IGRAPH_ERROR("Unknown 'algorithm'", IGRAPH_EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_eigen_matrix
+ *
+ */
+
+int igraph_eigen_matrix(const igraph_matrix_t *A,
+                        const igraph_sparsemat_t *sA,
+                        igraph_arpack_function_t *fun, int n,
+                        void *extra,
+                        igraph_eigen_algorithm_t algorithm,
+                        const igraph_eigen_which_t *which,
+                        igraph_arpack_options_t *options,
+                        igraph_arpack_storage_t *storage,
+                        igraph_vector_complex_t *values,
+                        igraph_matrix_complex_t *vectors) {
+
+    IGRAPH_CHECK(igraph_i_eigen_checks(A, sA, fun, n));
+
+    if (which->pos != IGRAPH_EIGEN_LM &&
+        which->pos != IGRAPH_EIGEN_SM &&
+        which->pos != IGRAPH_EIGEN_LR &&
+        which->pos != IGRAPH_EIGEN_SR &&
+        which->pos != IGRAPH_EIGEN_LI &&
+        which->pos != IGRAPH_EIGEN_SI &&
+        which->pos != IGRAPH_EIGEN_SELECT &&
+        which->pos != IGRAPH_EIGEN_ALL) {
+        IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL);
+    }
+
+    switch (algorithm) {
+    case IGRAPH_EIGEN_AUTO:
+        IGRAPH_ERROR("'AUTO' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_LAPACK:
+        IGRAPH_CHECK(igraph_i_eigen_matrix_lapack(A, sA, fun, n, extra, which,
+                     values, vectors));
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_ARPACK:
+        IGRAPH_ERROR("'ARPACK' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_COMP_AUTO:
+        IGRAPH_ERROR("'COMP_AUTO' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_COMP_LAPACK:
+        IGRAPH_ERROR("'COMP_LAPACK' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_COMP_ARPACK:
+        IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    default:
+        IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL);
+    }
+
+    return 0;
+}
+
+int igraph_i_eigen_adjacency_arpack_sym_cb(igraph_real_t *to,
+        const igraph_real_t *from,
+        int n, void *extra) {
+    igraph_adjlist_t *adjlist = (igraph_adjlist_t *) extra;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(adjlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            int nei = VECTOR(*neis)[j];
+            to[i] += from[nei];
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_eigen_adjacency_arpack(const igraph_t *graph,
+                                    const igraph_eigen_which_t *which,
+                                    igraph_arpack_options_t *options,
+                                    igraph_arpack_storage_t* storage,
+                                    igraph_vector_t *values,
+                                    igraph_matrix_t *vectors,
+                                    igraph_vector_complex_t *cmplxvalues,
+                                    igraph_matrix_complex_t *cmplxvectors) {
+
+    igraph_adjlist_t adjlist;
+    void *extra = (void*) &adjlist;
+    int n = igraph_vcount(graph);
+
+    if (!options) {
+        IGRAPH_ERROR("`options' must be given for ARPACK algorithm",
+                     IGRAPH_EINVAL);
+    }
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("ARPACK adjacency eigensolver not implemented for "
+                     "directed graphs", IGRAPH_UNIMPLEMENTED);
+    }
+    if (which->pos == IGRAPH_EIGEN_INTERVAL) {
+        IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement "
+                     "`INTERNAL' eigenvalues", IGRAPH_UNIMPLEMENTED);
+    }
+    if (which->pos == IGRAPH_EIGEN_SELECT) {
+        IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement "
+                     "`SELECT' eigenvalues", IGRAPH_UNIMPLEMENTED);
+    }
+    if (which->pos == IGRAPH_EIGEN_ALL) {
+        IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement "
+                     "`ALL' eigenvalues", IGRAPH_UNIMPLEMENTED);
+    }
+
+    switch (which->pos) {
+    case IGRAPH_EIGEN_LM:
+        options->which[0] = 'L'; options->which[1] = 'M';
+        options->nev = which->howmany;
+        break;
+    case IGRAPH_EIGEN_SM:
+        options->which[0] = 'S'; options->which[1] = 'M';
+        options->nev = which->howmany;
+        break;
+    case IGRAPH_EIGEN_LA:
+        options->which[0] = 'L'; options->which[1] = 'A';
+        options->nev = which->howmany;
+        break;
+    case IGRAPH_EIGEN_SA:
+        options->which[0] = 'S'; options->which[1] = 'A';
+        options->nev = which->howmany;
+        break;
+    case IGRAPH_EIGEN_ALL:
+        options->which[0] = 'L'; options->which[1] = 'M';
+        options->nev = n;
+        break;
+    case IGRAPH_EIGEN_BE:
+        IGRAPH_ERROR("Eigenvectors from both ends with ARPACK",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_INTERVAL:
+        IGRAPH_ERROR("Interval of eigenvectors with ARPACK",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_SELECT:
+        IGRAPH_ERROR("Selected eigenvalues with ARPACK",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    default:
+        /* This cannot happen */
+        break;
+    }
+
+    options->n = n;
+    options->ncv = 2 * options->nev < n ? 2 * options->nev : n;
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigen_adjacency_arpack_sym_cb,
+                                       extra, options, storage, values, vectors));
+
+    igraph_adjlist_destroy(&adjlist);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_eigen_adjacency
+ *
+ */
+
+int igraph_eigen_adjacency(const igraph_t *graph,
+                           igraph_eigen_algorithm_t algorithm,
+                           const igraph_eigen_which_t *which,
+                           igraph_arpack_options_t *options,
+                           igraph_arpack_storage_t *storage,
+                           igraph_vector_t *values,
+                           igraph_matrix_t *vectors,
+                           igraph_vector_complex_t *cmplxvalues,
+                           igraph_matrix_complex_t *cmplxvectors) {
+
+    if (which->pos != IGRAPH_EIGEN_LM &&
+        which->pos != IGRAPH_EIGEN_SM &&
+        which->pos != IGRAPH_EIGEN_LA &&
+        which->pos != IGRAPH_EIGEN_SA &&
+        which->pos != IGRAPH_EIGEN_BE &&
+        which->pos != IGRAPH_EIGEN_SELECT &&
+        which->pos != IGRAPH_EIGEN_INTERVAL &&
+        which->pos != IGRAPH_EIGEN_ALL) {
+        IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL);
+    }
+
+    switch (algorithm) {
+    case IGRAPH_EIGEN_AUTO:
+        IGRAPH_ERROR("'AUTO' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_LAPACK:
+        IGRAPH_ERROR("'LAPACK' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_ARPACK:
+        IGRAPH_CHECK(igraph_i_eigen_adjacency_arpack(graph, which, options,
+                     storage, values, vectors,
+                     cmplxvalues,
+                     cmplxvectors));
+        break;
+    case IGRAPH_EIGEN_COMP_AUTO:
+        IGRAPH_ERROR("'COMP_AUTO' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_COMP_LAPACK:
+        IGRAPH_ERROR("'COMP_LAPACK' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    case IGRAPH_EIGEN_COMP_ARPACK:
+        IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet",
+                     IGRAPH_UNIMPLEMENTED);
+        /* TODO */
+        break;
+    default:
+        IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL);
+    }
+
+
+    return 0;
+}
+
+/**
+ * \function igraph_eigen_laplacian
+ *
+ */
+
+int igraph_eigen_laplacian(const igraph_t *graph,
+                           igraph_eigen_algorithm_t algorithm,
+                           const igraph_eigen_which_t *which,
+                           igraph_arpack_options_t *options,
+                           igraph_arpack_storage_t *storage,
+                           igraph_vector_t *values,
+                           igraph_matrix_t *vectors,
+                           igraph_vector_complex_t *cmplxvalues,
+                           igraph_matrix_complex_t *cmplxvectors) {
+
+    IGRAPH_ERROR("'igraph_eigen_laplacian'", IGRAPH_UNIMPLEMENTED);
+    /* TODO */
+    return 0;
+}
diff --git a/igraph/src/embedding.c b/igraph/src/embedding.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/embedding.c
@@ -0,0 +1,1170 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_embedding.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_random.h"
+#include "igraph_centrality.h"
+#include "igraph_blas.h"
+
+typedef struct {
+    const igraph_t *graph;
+    const igraph_vector_t *cvec;
+    const igraph_vector_t *cvec2;
+    igraph_adjlist_t *outlist, *inlist;
+    igraph_inclist_t *eoutlist, *einlist;
+    igraph_vector_t *tmp;
+    const igraph_vector_t *weights;
+} igraph_i_asembedding_data_t;
+
+/* Adjacency matrix, unweighted, undirected.
+   Eigendecomposition is used */
+int igraph_i_asembeddingu(igraph_real_t *to, const igraph_real_t *from,
+                          int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_adjlist_t *outlist = data->outlist;
+    const igraph_vector_t *cvec = data->cvec;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* to = (A+cD) from */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(outlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            to[i] += from[nei];
+        }
+        to[i] += VECTOR(*cvec)[i] * from[i];
+    }
+
+    return 0;
+}
+
+/* Adjacency matrix, weighted, undirected.
+   Eigendecomposition is used. */
+int igraph_i_asembeddinguw(igraph_real_t *to, const igraph_real_t *from,
+                           int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_inclist_t *outlist = data->eoutlist;
+    const igraph_vector_t *cvec = data->cvec;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_int_t *incs;
+    int i, j, nlen;
+
+    /* to = (A+cD) from */
+    for (i = 0; i < n; i++) {
+        incs = igraph_inclist_get(outlist, i);
+        nlen = igraph_vector_int_size(incs);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = VECTOR(*incs)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            to[i] += w * from[nei];
+        }
+        to[i] += VECTOR(*cvec)[i] * from[i];
+    }
+
+    return 0;
+}
+
+/* Adjacency matrix, unweighted, directed. SVD. */
+int igraph_i_asembedding(igraph_real_t *to, const igraph_real_t *from,
+                         int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_adjlist_t *outlist = data->outlist;
+    igraph_adjlist_t *inlist = data->inlist;
+    const igraph_vector_t *cvec = data->cvec;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* tmp = (A+cD)' from */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(inlist, i);
+        nlen = igraph_vector_int_size(neis);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            VECTOR(*tmp)[i] += from[nei];
+        }
+        VECTOR(*tmp)[i] += VECTOR(*cvec)[i] * from[i];
+    }
+
+    /* to = (A+cD) tmp */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(outlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            to[i] += VECTOR(*tmp)[nei];
+        }
+        to[i] += VECTOR(*cvec)[i] * VECTOR(*tmp)[i];
+    }
+
+    return 0;
+}
+
+/* Adjacency matrix, unweighted, directed. SVD, right eigenvectors */
+int igraph_i_asembedding_right(igraph_real_t *to, const igraph_real_t *from,
+                               int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_adjlist_t *inlist = data->inlist;
+    const igraph_vector_t *cvec = data->cvec;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* to = (A+cD)' from */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(inlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            to[i] += from[nei];
+        }
+        to[i] += VECTOR(*cvec)[i] * from[i];
+    }
+
+    return 0;
+}
+
+/* Adjacency matrix, weighted, directed. SVD. */
+int igraph_i_asembeddingw(igraph_real_t *to, const igraph_real_t *from,
+                          int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_inclist_t *outlist = data->eoutlist;
+    igraph_inclist_t *inlist = data->einlist;
+    const igraph_vector_t *cvec = data->cvec;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *incs;
+    int i, j, nlen;
+
+    /* tmp = (A+cD)' from */
+    for (i = 0; i < n; i++) {
+        incs = igraph_inclist_get(inlist, i);
+        nlen = igraph_vector_int_size(incs);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = VECTOR(*incs)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            VECTOR(*tmp)[i] += w * from[nei];
+        }
+        VECTOR(*tmp)[i] += VECTOR(*cvec)[i] * from[i];
+    }
+
+    /* to = (A+cD) tmp */
+    for (i = 0; i < n; i++) {
+        incs = igraph_inclist_get(outlist, i);
+        nlen = igraph_vector_int_size(incs);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = VECTOR(*incs)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            to[i] += w * VECTOR(*tmp)[nei];
+        }
+        to[i] += VECTOR(*cvec)[i] * VECTOR(*tmp)[i];
+    }
+
+    return 0;
+}
+
+/* Adjacency matrix, weighted, directed. SVD, right eigenvectors. */
+int igraph_i_asembeddingw_right(igraph_real_t *to, const igraph_real_t *from,
+                                int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_inclist_t *inlist = data->einlist;
+    const igraph_vector_t *cvec = data->cvec;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_int_t *incs;
+    int i, j, nlen;
+
+    /* to = (A+cD)' from */
+    for (i = 0; i < n; i++) {
+        incs = igraph_inclist_get(inlist, i);
+        nlen = igraph_vector_int_size(incs);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = VECTOR(*incs)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            to[i] += w * from[nei];
+        }
+        to[i] += VECTOR(*cvec)[i] * from[i];
+    }
+
+    return 0;
+}
+
+/* Laplacian D-A, unweighted, undirected. Eigendecomposition. */
+int igraph_i_lsembedding_da(igraph_real_t *to, const igraph_real_t *from,
+                            int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_adjlist_t *outlist = data->outlist;
+    const igraph_vector_t *cvec = data->cvec;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* to = (D-A) from */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(outlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            to[i] -= from[nei];
+        }
+        to[i] += VECTOR(*cvec)[i] * from[i];
+    }
+
+    return 0;
+}
+
+/* Laplacian D-A, weighted, undirected. Eigendecomposition. */
+int igraph_i_lsembedding_daw(igraph_real_t *to, const igraph_real_t *from,
+                             int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_inclist_t *outlist = data->eoutlist;
+    const igraph_vector_t *cvec = data->cvec;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_int_t *incs;
+    int i, j, nlen;
+
+    /* to = (D-A) from */
+    for (i = 0; i < n; i++) {
+        incs = igraph_inclist_get(outlist, i);
+        nlen = igraph_vector_int_size(incs);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = VECTOR(*incs)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            to[i] -= w * from[nei];
+        }
+        to[i] += VECTOR(*cvec)[i] * from[i];
+    }
+
+    return 0;
+}
+
+/* Laplacian DAD, unweighted, undirected. Eigendecomposition. */
+int igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_real_t *from,
+                             int n, void *extra) {
+
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_adjlist_t *outlist = data->outlist;
+    const igraph_vector_t *cvec = data->cvec;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* to = D^1/2 from */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*cvec)[i] * from[i];
+    }
+
+    /* tmp = A to */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(outlist, i);
+        nlen = igraph_vector_int_size(neis);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            VECTOR(*tmp)[i] += to[nei];
+        }
+    }
+
+    /* to = D tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*cvec)[i] * VECTOR(*tmp)[i];
+    }
+
+    return 0;
+}
+
+int igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *from,
+                              int n, void *extra) {
+
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_inclist_t *outlist = data->eoutlist;
+    const igraph_vector_t *cvec = data->cvec;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *incs;
+    int i, j, nlen;
+
+    /* to = D^-1/2 from */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*cvec)[i] * from[i];
+    }
+
+    /* tmp = A' to */
+    for (i = 0; i < n; i++) {
+        incs = igraph_inclist_get(outlist, i);
+        nlen = igraph_vector_int_size(incs);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = VECTOR(*incs)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            VECTOR(*tmp)[i] += w * to[nei];
+        }
+    }
+
+    /* to = D tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*cvec)[i] * VECTOR(*cvec)[i] * VECTOR(*tmp)[i];
+    }
+
+    /* tmp = A to */
+    for (i = 0; i < n; i++) {
+        incs = igraph_inclist_get(outlist, i);
+        nlen = igraph_vector_int_size(incs);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            long int edge = VECTOR(*incs)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            VECTOR(*tmp)[i] += w * to[nei];
+        }
+    }
+
+    /* to = D^-1/2 tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*cvec)[i] * VECTOR(*tmp)[i];
+    }
+
+    return 0;
+}
+
+/* Laplacian I-DAD, unweighted, undirected. Eigendecomposition. */
+int igraph_i_lsembedding_idad(igraph_real_t *to, const igraph_real_t *from,
+                              int n, void *extra) {
+
+    int i;
+
+    igraph_i_lsembedding_dad(to, from, n, extra);
+    for (i = 0; i < n; i++) {
+        to[i] = from[i] - to[i];
+    }
+
+    return 0;
+}
+
+int igraph_i_lsembedding_idadw(igraph_real_t *to, const igraph_real_t *from,
+                               int n, void *extra) {
+    int i;
+
+    igraph_i_lsembedding_dadw(to, from, n, extra);
+    for (i = 0; i < n; i++) {
+        to[i] = from[i] - to[i];
+    }
+
+    return 0;
+}
+
+/* Laplacian OAP, unweighted, directed. SVD. */
+int igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *from,
+                              int n, void *extra) {
+
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_adjlist_t *outlist = data->outlist;
+    igraph_adjlist_t *inlist = data->inlist;
+    const igraph_vector_t *deg_in = data->cvec;
+    const igraph_vector_t *deg_out = data->cvec2;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* tmp = O' from */
+    for (i = 0; i < n; i++) {
+        VECTOR(*tmp)[i] = VECTOR(*deg_out)[i] * from[i];
+    }
+
+    /* to = A' tmp */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(inlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            int nei = VECTOR(*neis)[j];
+            to[i] += VECTOR(*tmp)[nei];
+        }
+    }
+
+    /* tmp = P' to */
+    for (i = 0; i < n; i++) {
+        VECTOR(*tmp)[i] = VECTOR(*deg_in)[i] * to[i];
+    }
+
+    /* to = P tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i];
+    }
+
+    /* tmp = A to */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(outlist, i);
+        nlen = igraph_vector_int_size(neis);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            int nei = VECTOR(*neis)[j];
+            VECTOR(*tmp)[i] += to[nei];
+        }
+    }
+
+    /* to = O tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*deg_out)[i] * VECTOR(*tmp)[i];
+    }
+
+    return 0;
+}
+
+/* Laplacian OAP, unweighted, directed. SVD, right eigenvectors. */
+int igraph_i_lseembedding_oap_right(igraph_real_t *to,
+                                    const igraph_real_t *from,
+                                    int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_adjlist_t *inlist = data->inlist;
+    const igraph_vector_t *deg_in = data->cvec;
+    const igraph_vector_t *deg_out = data->cvec2;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* to = O' from */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*deg_out)[i] * from[i];
+    }
+
+    /* tmp = A' to */
+    for (i = 0; i < n; i++) {
+        neis = igraph_adjlist_get(inlist, i);
+        nlen = igraph_vector_int_size(neis);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            int nei = VECTOR(*neis)[j];
+            VECTOR(*tmp)[i] += to[nei];
+        }
+    }
+
+    /* to = P' tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i];
+    }
+
+    return 0;
+}
+
+/* Laplacian OAP, weighted, directed. SVD. */
+int igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *from,
+                               int n, void *extra) {
+
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_inclist_t *outlist = data->eoutlist;
+    igraph_inclist_t *inlist = data->einlist;
+    const igraph_vector_t *deg_in = data->cvec;
+    const igraph_vector_t *deg_out = data->cvec2;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* tmp = O' from */
+    for (i = 0; i < n; i++) {
+        VECTOR(*tmp)[i] = VECTOR(*deg_out)[i] * from[i];
+    }
+
+    /* to = A' tmp */
+    for (i = 0; i < n; i++) {
+        neis = igraph_inclist_get(inlist, i);
+        nlen = igraph_vector_int_size(neis);
+        to[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            int edge = VECTOR(*neis)[j];
+            int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            to[i] += w * VECTOR(*tmp)[nei];
+        }
+    }
+
+    /* tmp = P' to */
+    for (i = 0; i < n; i++) {
+        VECTOR(*tmp)[i] = VECTOR(*deg_in)[i] * to[i];
+    }
+
+    /* to = P tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i];
+    }
+
+    /* tmp = A to */
+    for (i = 0; i < n; i++) {
+        neis = igraph_inclist_get(outlist, i);
+        nlen = igraph_vector_int_size(neis);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            int edge = VECTOR(*neis)[j];
+            int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            VECTOR(*tmp)[i] += w * to[nei];
+        }
+    }
+
+    /* to = O tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*deg_out)[i] * VECTOR(*tmp)[i];
+    }
+
+    return 0;
+}
+
+/* Laplacian OAP, weighted, directed. SVD, right eigenvectors. */
+int igraph_i_lseembedding_oapw_right(igraph_real_t *to,
+                                     const igraph_real_t *from,
+                                     int n, void *extra) {
+    igraph_i_asembedding_data_t *data = extra;
+    igraph_inclist_t *inlist = data->einlist;
+    const igraph_vector_t *deg_in = data->cvec;
+    const igraph_vector_t *deg_out = data->cvec2;
+    const igraph_vector_t *weights = data->weights;
+    const igraph_t *graph = data->graph;
+    igraph_vector_t *tmp = data->tmp;
+    igraph_vector_int_t *neis;
+    int i, j, nlen;
+
+    /* to = O' from */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*deg_out)[i] * from[i];
+    }
+
+    /* tmp = A' to */
+    for (i = 0; i < n; i++) {
+        neis = igraph_inclist_get(inlist, i);
+        nlen = igraph_vector_int_size(neis);
+        VECTOR(*tmp)[i] = 0.0;
+        for (j = 0; j < nlen; j++) {
+            int edge = VECTOR(*neis)[j];
+            int nei = IGRAPH_OTHER(graph, edge, i);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            VECTOR(*tmp)[i] += w * to[nei];
+        }
+    }
+
+    /* to = P' tmp */
+    for (i = 0; i < n; i++) {
+        to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i];
+    }
+
+    return 0;
+}
+
+int igraph_i_spectral_embedding(const igraph_t *graph,
+                                igraph_integer_t no,
+                                const igraph_vector_t *weights,
+                                igraph_eigen_which_position_t which,
+                                igraph_bool_t scaled,
+                                igraph_matrix_t *X,
+                                igraph_matrix_t *Y,
+                                igraph_vector_t *D,
+                                const igraph_vector_t *cvec,
+                                const igraph_vector_t *cvec2,
+                                igraph_arpack_options_t *options,
+                                igraph_arpack_function_t *callback,
+                                igraph_arpack_function_t *callback_right,
+                                igraph_bool_t symmetric,
+                                igraph_bool_t eigen,
+                                igraph_bool_t zapsmall) {
+
+    igraph_integer_t vc = igraph_vcount(graph);
+    igraph_vector_t tmp;
+    igraph_adjlist_t outlist, inlist;
+    igraph_inclist_t eoutlist, einlist;
+    int i, j, cveclen = igraph_vector_size(cvec);
+    igraph_i_asembedding_data_t data = { graph, cvec, cvec2, &outlist, &inlist,
+                                         &eoutlist, &einlist, &tmp, weights
+                                       };
+    igraph_vector_t tmpD;
+
+    if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    if (which != IGRAPH_EIGEN_LM &&
+        which != IGRAPH_EIGEN_LA &&
+        which != IGRAPH_EIGEN_SA) {
+        IGRAPH_ERROR("Invalid eigenvalue chosen, must be one of "
+                     "`largest magnitude', `largest algebraic' or "
+                     "`smallest algebraic'", IGRAPH_EINVAL);
+    }
+
+    if (no > vc) {
+        IGRAPH_ERROR("Too many singular values requested", IGRAPH_EINVAL);
+    }
+    if (no <= 0) {
+        IGRAPH_ERROR("No singular values requested", IGRAPH_EINVAL);
+    }
+
+    if (cveclen != 1 && cveclen != vc) {
+        IGRAPH_ERROR("Augmentation vector size is invalid, it should be "
+                     "the number of vertices or scalar", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(X, vc, no));
+    if (Y) {
+        IGRAPH_CHECK(igraph_matrix_resize(Y, vc, no));
+    }
+
+    /* empty graph */
+    if (igraph_ecount(graph) == 0) {
+        igraph_matrix_null(X);
+        if (Y) {
+            igraph_matrix_null(Y);
+        }
+        return 0;
+    }
+
+    igraph_vector_init(&tmp, vc);
+    IGRAPH_FINALLY(igraph_vector_destroy, &tmp);
+    if (!weights) {
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &outlist, IGRAPH_OUT));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &outlist);
+        if (!symmetric) {
+            IGRAPH_CHECK(igraph_adjlist_init(graph, &inlist, IGRAPH_IN));
+            IGRAPH_FINALLY(igraph_adjlist_destroy, &inlist);
+        }
+    } else {
+        IGRAPH_CHECK(igraph_inclist_init(graph, &eoutlist, IGRAPH_OUT));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &eoutlist);
+        if (!symmetric) {
+            IGRAPH_CHECK(igraph_inclist_init(graph, &einlist, IGRAPH_IN));
+            IGRAPH_FINALLY(igraph_inclist_destroy, &einlist);
+        }
+    }
+    IGRAPH_VECTOR_INIT_FINALLY(&tmpD, no);
+
+    options->n = vc;
+    options->start = 0;   /* random start vector */
+    options->nev = no;
+    switch (which) {
+    case IGRAPH_EIGEN_LM:
+        options->which[0] = 'L'; options->which[1] = 'M';
+        break;
+    case IGRAPH_EIGEN_LA:
+        options->which[0] = 'L'; options->which[1] = 'A';
+        break;
+    case IGRAPH_EIGEN_SA:
+        options->which[0] = 'S'; options->which[1] = 'A';
+        break;
+    default:
+        break;
+    }
+    options->ncv = no + 3;
+    if (options->ncv > vc) {
+        options->ncv = vc;
+    }
+
+    IGRAPH_CHECK(igraph_arpack_rssolve(callback, &data, options, 0, &tmpD, X));
+
+    if (!symmetric) {
+        /* calculate left eigenvalues */
+        IGRAPH_CHECK(igraph_matrix_resize(Y, vc, no));
+        for (i = 0; i < no; i++) {
+            igraph_real_t norm;
+            igraph_vector_t v;
+            callback_right(&MATRIX(*Y, 0, i), &MATRIX(*X, 0, i), vc, &data);
+            igraph_vector_view(&v, &MATRIX(*Y, 0, i), vc);
+            norm = 1.0 / igraph_blas_dnrm2(&v);
+            igraph_vector_scale(&v, norm);
+        }
+    } else if (Y) {
+        IGRAPH_CHECK(igraph_matrix_update(Y, X));
+    }
+
+    if (zapsmall) {
+        igraph_vector_zapsmall(&tmpD, 0);
+        igraph_matrix_zapsmall(X, 0);
+        if (Y) {
+            igraph_matrix_zapsmall(Y, 0);
+        }
+    }
+
+    if (D) {
+        igraph_vector_update(D, &tmpD);
+        if (!eigen) {
+            for (i = 0; i < no; i++) {
+                VECTOR(*D)[i] = sqrt(VECTOR(*D)[i]);
+            }
+        }
+    }
+
+    if (scaled) {
+        if (eigen) {
+            /* eigenvalues were calculated */
+            for (i = 0; i < no; i++) {
+                VECTOR(tmpD)[i] = sqrt(fabs(VECTOR(tmpD)[i]));
+            }
+        } else {
+            /* singular values were calculated */
+            for (i = 0; i < no; i++) {
+                VECTOR(tmpD)[i] = sqrt(sqrt(VECTOR(tmpD)[i]));
+            }
+        }
+
+        for (j = 0; j < vc; j++) {
+            for (i = 0; i < no; i++) {
+                MATRIX(*X, j, i) *= VECTOR(tmpD)[i];
+            }
+        }
+
+        if (Y) {
+            for (j = 0; j < vc; j++) {
+                for (i = 0; i < no; i++) {
+                    MATRIX(*Y, j, i) *= VECTOR(tmpD)[i];
+                }
+            }
+        }
+    }
+
+    igraph_vector_destroy(&tmpD);
+    if (!weights) {
+        if (!symmetric) {
+            igraph_adjlist_destroy(&inlist);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        igraph_adjlist_destroy(&outlist);
+    } else {
+        if (!symmetric) {
+            igraph_inclist_destroy(&einlist);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        igraph_inclist_destroy(&eoutlist);
+    }
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_adjacency_spectral_embedding
+ * Adjacency spectral embedding
+ *
+ * Spectral decomposition of the adjacency matrices of graphs.
+ * This function computes a \code{no}-dimensional Euclidean
+ * representation of the graph based on its adjacency
+ * matrix, A. This representation is computed via the singular value
+ * decomposition of the adjacency matrix, A=UDV^T. In the case,
+ * where the graph is a random dot product graph generated using latent
+ * position vectors in R^no for each vertex, the embedding will
+ * provide an estimate of these latent vectors.
+ *
+ * </para><para>
+ * For undirected graphs the latent positions are calculated as
+ * X=U^no D^(1/2) where U^no equals to the first no columns of U, and
+ * D^(1/2) is a diagonal matrix containing the square root of the selected
+ * singular values on the diagonal.
+ *
+ * </para><para>
+ * For directed graphs the embedding is defined as the pair
+ * X=U^no D^(1/2), Y=V^no D^(1/2). (For undirected graphs U=V,
+ * so it is enough to keep one of them.)
+ *
+ * \param graph The input graph, can be directed or undirected.
+ * \param no An integer scalar. This value is the embedding dimension of
+ *        the spectral embedding. Should be smaller than the number of
+ *        vertices. The largest no-dimensional non-zero
+ *        singular values are used for the spectral embedding.
+ * \param weights Optional edge weights. Supply a null pointer for
+ *        unweighted graphs.
+ * \param which Which eigenvalues (or singular values, for directed
+ *        graphs) to use, possible values:
+ *        \clist
+ *          \cli IGRAPH_EIGEN_LM
+ *          the ones with the largest magnitude
+ *          \cli IGRAPH_EIGEN_LA
+ *          the (algebraic) largest ones
+ *          \cli IGRAPH_EIGEN_SA
+ *          the (algebraic) smallest ones.
+ *        \endclist
+ *        For directed graphs, <code>IGRAPH_EIGEN_LM</code> and
+ *        <code>IGRAPH_EIGEN_LA</code> are the same because singular
+ *        values are used for the ordering instead of eigenvalues.
+ * \param scaled Whether to return X and Y (if scaled is non-zero), or
+ *        U and V.
+ * \param X Initialized matrix, the estimated latent positions are
+ *        stored here.
+ * \param Y Initialized matrix or a null pointer. If not a null
+ *        pointer, then the second half of the latent positions are
+ *        stored here. (For undirected graphs, this always equals X.)
+ * \param D Initialized vector or a null pointer. If not a null
+ *        pointer, then the eigenvalues (for undirected graphs) or the
+ *        singular values (for directed graphs) are stored here.
+ * \param cvec A numeric vector, its length is the number vertices in the
+ *        graph. This vector is added to the diagonal of the adjacency
+ *        matrix, before performing the SVD.
+ * \param options Options to ARPACK. See \ref igraph_arpack_options_t
+ *        for details. Note that the function overwrites the
+ *        <code>n</code> (number of vertices), <code>nev</code> and
+ *        <code>which</code> parameters and it always starts the
+ *        calculation from a random start vector.
+ * \return Error code.
+ *
+ */
+
+int igraph_adjacency_spectral_embedding(const igraph_t *graph,
+                                        igraph_integer_t no,
+                                        const igraph_vector_t *weights,
+                                        igraph_eigen_which_position_t which,
+                                        igraph_bool_t scaled,
+                                        igraph_matrix_t *X,
+                                        igraph_matrix_t *Y,
+                                        igraph_vector_t *D,
+                                        const igraph_vector_t *cvec,
+                                        igraph_arpack_options_t *options) {
+
+    igraph_arpack_function_t *callback, *callback_right;
+    igraph_bool_t directed = igraph_is_directed(graph);
+
+    if (directed) {
+        callback = weights ? igraph_i_asembeddingw : igraph_i_asembedding;
+        callback_right = (weights ? igraph_i_asembeddingw_right :
+                          igraph_i_asembedding_right);
+    } else {
+        callback = weights ? igraph_i_asembeddinguw : igraph_i_asembeddingu;
+        callback_right = 0;
+    }
+
+    return igraph_i_spectral_embedding(graph, no, weights, which, scaled,
+                                       X, Y, D, cvec, /* deg2=*/ 0,
+                                       options, callback, callback_right,
+                                       /*symmetric=*/ !directed,
+                                       /*eigen=*/ !directed, /*zapsmall=*/ 1);
+}
+
+int igraph_i_lse_und(const igraph_t *graph,
+                     igraph_integer_t no,
+                     const igraph_vector_t *weights,
+                     igraph_eigen_which_position_t which,
+                     igraph_neimode_t degmode,
+                     igraph_laplacian_spectral_embedding_type_t type,
+                     igraph_bool_t scaled,
+                     igraph_matrix_t *X,
+                     igraph_matrix_t *Y,
+                     igraph_vector_t *D,
+                     igraph_arpack_options_t *options) {
+
+    igraph_arpack_function_t *callback;
+    igraph_vector_t deg;
+
+    switch (type) {
+    case IGRAPH_EMBEDDING_D_A:
+        callback = weights ? igraph_i_lsembedding_daw : igraph_i_lsembedding_da;
+        break;
+    case IGRAPH_EMBEDDING_DAD:
+        callback = weights ? igraph_i_lsembedding_dadw : igraph_i_lsembedding_dad;
+        break;
+    case IGRAPH_EMBEDDING_I_DAD:
+        callback = weights ? igraph_i_lsembedding_idadw : igraph_i_lsembedding_idad;
+        break;
+    default:
+        IGRAPH_ERROR("Invalid Laplacian spectral embedding type",
+                     IGRAPH_EINVAL);
+        break;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&deg, 0);
+    igraph_strength(graph, &deg, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 1,
+                    weights);
+
+    switch (type) {
+    case IGRAPH_EMBEDDING_D_A:
+        break;
+    case IGRAPH_EMBEDDING_DAD:
+    case IGRAPH_EMBEDDING_I_DAD: {
+        int i, n = igraph_vector_size(&deg);
+        for (i = 0; i < n; i++) {
+            VECTOR(deg)[i] = 1.0 / sqrt(VECTOR(deg)[i]);
+        }
+    }
+    break;
+    default:
+        break;
+    }
+
+    IGRAPH_CHECK(igraph_i_spectral_embedding(graph, no, weights, which,
+                 scaled, X, Y, D, /*cvec=*/ &deg, /*deg2=*/ 0,
+                 options, callback, 0, /*symmetric=*/ 1,
+                 /*eigen=*/ 1, /*zapsmall=*/ 1));
+
+    igraph_vector_destroy(&deg);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_lse_dir(const igraph_t *graph,
+                     igraph_integer_t no,
+                     const igraph_vector_t *weights,
+                     igraph_eigen_which_position_t which,
+                     igraph_neimode_t degmode,
+                     igraph_laplacian_spectral_embedding_type_t type,
+                     igraph_bool_t scaled,
+                     igraph_matrix_t *X,
+                     igraph_matrix_t *Y,
+                     igraph_vector_t *D,
+                     igraph_arpack_options_t *options) {
+
+    igraph_arpack_function_t *callback =
+        weights ? igraph_i_lseembedding_oapw : igraph_i_lseembedding_oap;
+    igraph_arpack_function_t *callback_right =
+        weights ? igraph_i_lseembedding_oapw_right :
+        igraph_i_lseembedding_oap_right;
+    igraph_vector_t deg_in, deg_out;
+    int i, n = igraph_vcount(graph);
+
+    if (type != IGRAPH_EMBEDDING_OAP) {
+        IGRAPH_ERROR("Invalid Laplacian spectral embedding type", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&deg_in, n);
+    IGRAPH_VECTOR_INIT_FINALLY(&deg_out, n);
+    igraph_strength(graph, &deg_in, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1,
+                    weights);
+    igraph_strength(graph, &deg_out, igraph_vss_all(), IGRAPH_OUT, /*loops=*/ 1,
+                    weights);
+
+    for (i = 0; i < n; i++) {
+        VECTOR(deg_in)[i] = 1.0 / sqrt(VECTOR(deg_in)[i]);
+        VECTOR(deg_out)[i] = 1.0 / sqrt(VECTOR(deg_out)[i]);
+    }
+
+    IGRAPH_CHECK(igraph_i_spectral_embedding(graph, no, weights, which,
+                 scaled, X, Y, D, /*cvec=*/ &deg_in,
+                 /*deg2=*/ &deg_out, options, callback,
+                 callback_right, /*symmetric=*/ 0, /*eigen=*/ 0,
+                 /*zapsmall=*/ 1));
+
+    igraph_vector_destroy(&deg_in);
+    igraph_vector_destroy(&deg_out);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_laplacian_spectral_embedding
+ * Spectral embedding of the Laplacian of a graph
+ *
+ * This function essentially does the same as
+ * \ref igraph_adjacency_spectral_embedding, but works on the Laplacian
+ * of the graph, instead of the adjacency matrix.
+ * \param graph The input graph.
+ * \param no The number of eigenvectors (or singular vectors if the graph
+ *        is directed) to use for the embedding.
+ * \param weights Optional edge weights. Supply a null pointer for
+ *        unweighted graphs.
+ * \param which Which eigenvalues (or singular values, for directed
+ *        graphs) to use, possible values:
+ *        \clist
+ *          \cli IGRAPH_EIGEN_LM
+ *          the ones with the largest magnitude
+ *          \cli IGRAPH_EIGEN_LA
+ *          the (algebraic) largest ones
+ *          \cli IGRAPH_EIGEN_SA
+ *          the (algebraic) smallest ones.
+ *        \endclist
+ *        For directed graphs, <code>IGRAPH_EIGEN_LM</code> and
+ *        <code>IGRAPH_EIGEN_LA</code> are the same because singular
+ *        values are used for the ordering instead of eigenvalues.
+ * \param type The type of the Laplacian to use. Various definitions
+ *        exist for the Laplacian of a graph, and one can choose
+ *        between them with this argument. Possible values:
+ *        \clist
+ *          \cli IGRAPH_EMBEDDING_D_A
+ *           means D - A where D is the
+ *           degree matrix and A is the adjacency matrix
+ *          \cli IGRAPH_EMBEDDING_DAD
+ *           means Di times A times Di,
+ *           where Di is the inverse of the square root of the degree matrix;
+ *          \cli IGRAPH_EMBEDDING_I_DAD
+ *          means I - Di A Di, where I
+ *          is the identity matrix.
+ *        \endclist
+ * \param scaled Whether to return X and Y (if scaled is non-zero), or
+ *        U and V.
+ * \param X Initialized matrix, the estimated latent positions are
+ *        stored here.
+ * \param Y Initialized matrix or a null pointer. If not a null
+ *        pointer, then the second half of the latent positions are
+ *        stored here. (For undirected graphs, this always equals X.)
+ * \param D Initialized vector or a null pointer. If not a null
+ *        pointer, then the eigenvalues (for undirected graphs) or the
+ *        singular values (for directed graphs) are stored here.
+ * \param options Options to ARPACK. See \ref igraph_arpack_options_t
+ *        for details. Note that the function overwrites the
+ *        <code>n</code> (number of vertices), <code>nev</code> and
+ *        <code>which</code> parameters and it always starts the
+ *        calculation from a random start vector.
+ * \return Error code.
+ *
+ * \sa \ref igraph_adjacency_spectral_embedding to embed the adjacency
+ * matrix.
+ */
+
+int igraph_laplacian_spectral_embedding(const igraph_t *graph,
+                                        igraph_integer_t no,
+                                        const igraph_vector_t *weights,
+                                        igraph_eigen_which_position_t which,
+                                        igraph_neimode_t degmode,
+                                        igraph_laplacian_spectral_embedding_type_t type,
+                                        igraph_bool_t scaled,
+                                        igraph_matrix_t *X,
+                                        igraph_matrix_t *Y,
+                                        igraph_vector_t *D,
+                                        igraph_arpack_options_t *options) {
+
+    if (igraph_is_directed(graph)) {
+        return igraph_i_lse_dir(graph, no, weights, which, degmode, type, scaled,
+                                X, Y, D, options);
+    } else {
+        return igraph_i_lse_und(graph, no, weights, which, degmode, type, scaled,
+                                X, Y, D, options);
+    }
+}
+
+/**
+ * \function igraph_dim_select
+ * Dimensionality selection
+ *
+ * Dimensionality selection for singular values using
+ * profile likelihood.
+ *
+ * </para><para>
+ * The input of the function is a numeric vector which contains
+ * the measure of "importance" for each dimension.
+ *
+ * </para><para>
+ * For spectral embedding, these are the singular values of the adjacency
+ * matrix. The singular values are assumed to be generated from a
+ * Gaussian mixture distribution with two components that have different
+ * means and same variance. The dimensionality d is chosen to
+ * maximize the likelihood when the d largest singular values are
+ * assigned to one component of the mixture and the rest of the singular
+ * values assigned to the other component.
+ *
+ * </para><para>
+ * This function can also be used for the general separation problem,
+ * where we assume that the left and the right of the vector are coming
+ * from two Normal distributions, with different means, and we want
+ * to know their border.
+ *
+ * \param sv A numeric vector, the ordered singular values.
+ * \param dim The result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(n), n is the number of values in sv.
+ *
+ * \sa \ref igraph_adjacency_spectral_embedding().
+ */
+
+int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) {
+
+    int i, n = igraph_vector_size(sv);
+    igraph_real_t x, x2, sum1 = 0.0, sum2 = igraph_vector_sum(sv);
+    igraph_real_t sumsq1 = 0.0, sumsq2 = 0.0; /* to be set */
+    igraph_real_t oldmean1, oldmean2, mean1 = 0.0, mean2 = sum2 / n;
+    igraph_real_t varsq1 = 0.0, varsq2 = 0.0; /* to be set */
+    igraph_real_t var1, var2, sd, profile, max = IGRAPH_NEGINFINITY;
+
+    if (n == 0) {
+        IGRAPH_ERROR("Need at least one singular value for dimensionality "
+                     "selection", IGRAPH_EINVAL);
+    }
+
+    if (n == 1) {
+        *dim = 1;
+        return 0;
+    }
+
+    for (i = 0; i < n; i++) {
+        x = VECTOR(*sv)[i];
+        sumsq2 += x * x;
+        varsq2 += (mean2 - x) * (mean2 - x);
+    }
+
+    for (i = 0; i < n - 1; i++) {
+        int n1 = i + 1, n2 = n - i - 1, n1m1 = n1 - 1, n2m1 = n2 - 1;
+        x = VECTOR(*sv)[i]; x2 = x * x;
+        sum1 += x; sum2 -= x;
+        sumsq1 += x2; sumsq2 -= x2;
+        oldmean1 = mean1; oldmean2 = mean2;
+        mean1 = sum1 / n1; mean2 = sum2 / n2;
+        varsq1 += (x - oldmean1) * (x - mean1);
+        varsq2 -= (x - oldmean2) * (x - mean2);
+        var1 = i == 0 ? 0 : varsq1 / n1m1;
+        var2 = i == n - 2 ? 0 : varsq2 / n2m1;
+        sd = sqrt(( n1m1 * var1 + n2m1 * var2) / (n - 2));
+        profile = /* - n * log(2.0*M_PI)/2.0 */ /* This is redundant */
+            - n * log(sd) -
+            ((sumsq1 - 2 * mean1 * sum1 + n1 * mean1 * mean1) +
+             (sumsq2 - 2 * mean2 * sum2 + n2 * mean2 * mean2)) / 2.0 / sd / sd;
+        if (profile > max) {
+            max = profile;
+            *dim = n1;
+        }
+    }
+
+    /* Plus the last case, all elements in one group */
+    x = VECTOR(*sv)[n - 1];
+    sum1 += x;
+    oldmean1 = mean1;
+    mean1 = sum1 / n;
+    sumsq1 += x * x;
+    varsq1 += (x - oldmean1) * (x - mean1);
+    var1 = varsq1 / (n - 1);
+    sd = sqrt(var1);
+    profile = /* - n * log(2.0*M_PI)/2.0 */ /* This is redundant */
+        - n * log(sd) -
+        (sumsq1 - 2 * mean1 * sum1 + n * mean1 * mean1) / 2.0 / sd / sd;
+    if (profile > max) {
+        max = profile;
+        *dim = n;
+    }
+
+    return 0;
+}
diff --git a/igraph/src/endfile.c b/igraph/src/endfile.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/endfile.c
@@ -0,0 +1,160 @@
+#include "f2c.h"
+#include "fio.h"
+
+/* Compile this with -DNO_TRUNCATE if unistd.h does not exist or */
+/* if it does not define int truncate(const char *name, off_t). */
+
+#ifdef MSDOS
+#undef NO_TRUNCATE
+#define NO_TRUNCATE
+#endif
+
+#ifndef NO_TRUNCATE
+#include "unistd.h"
+#endif
+
+#ifdef KR_headers
+extern char *strcpy();
+extern FILE *tmpfile();
+#else
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#include "string.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#endif
+
+extern char *f__r_mode[], *f__w_mode[];
+
+#ifdef KR_headers
+integer f_end(a) alist *a;
+#else
+integer f_end(alist *a)
+#endif
+{
+	unit *b;
+	FILE *tf;
+
+	if(a->aunit>=MXUNIT || a->aunit<0) err(a->aerr,101,"endfile");
+	b = &f__units[a->aunit];
+	if(b->ufd==NULL) {
+		char nbuf[10];
+		sprintf(nbuf,"fort.%ld",(long)a->aunit);
+		if (tf = FOPEN(nbuf, f__w_mode[0]))
+			fclose(tf);
+		return(0);
+		}
+	b->uend=1;
+	return(b->useek ? t_runc(a) : 0);
+}
+
+#ifdef NO_TRUNCATE
+ static int
+#ifdef KR_headers
+copy(from, len, to) FILE *from, *to; register long len;
+#else
+copy(FILE *from, register long len, FILE *to)
+#endif
+{
+	int len1;
+	char buf[BUFSIZ];
+
+	while(fread(buf, len1 = len > BUFSIZ ? BUFSIZ : (int)len, 1, from)) {
+		if (!fwrite(buf, len1, 1, to))
+			return 1;
+		if ((len -= len1) <= 0)
+			break;
+		}
+	return 0;
+	}
+#endif /* NO_TRUNCATE */
+
+ int
+#ifdef KR_headers
+t_runc(a) alist *a;
+#else
+t_runc(alist *a)
+#endif
+{
+	OFF_T loc, len;
+	unit *b;
+	int rc;
+	FILE *bf;
+#ifdef NO_TRUNCATE
+	FILE *tf;
+#endif
+
+	b = &f__units[a->aunit];
+	if(b->url)
+		return(0);	/*don't truncate direct files*/
+	loc=FTELL(bf = b->ufd);
+	FSEEK(bf,(OFF_T)0,SEEK_END);
+	len=FTELL(bf);
+	if (loc >= len || b->useek == 0)
+		return(0);
+#ifdef NO_TRUNCATE
+	if (b->ufnm == NULL)
+		return 0;
+	rc = 0;
+	fclose(b->ufd);
+	if (!loc) {
+		if (!(bf = FOPEN(b->ufnm, f__w_mode[b->ufmt])))
+			rc = 1;
+		if (b->uwrt)
+			b->uwrt = 1;
+		goto done;
+		}
+	if (!(bf = FOPEN(b->ufnm, f__r_mode[0]))
+	 || !(tf = tmpfile())) {
+#ifdef NON_UNIX_STDIO
+ bad:
+#endif
+		rc = 1;
+		goto done;
+		}
+	if (copy(bf, (long)loc, tf)) {
+ bad1:
+		rc = 1;
+		goto done1;
+		}
+	if (!(bf = FREOPEN(b->ufnm, f__w_mode[0], bf)))
+		goto bad1;
+	rewind(tf);
+	if (copy(tf, (long)loc, bf))
+		goto bad1;
+	b->uwrt = 1;
+	b->urw = 2;
+#ifdef NON_UNIX_STDIO
+	if (b->ufmt) {
+		fclose(bf);
+		if (!(bf = FOPEN(b->ufnm, f__w_mode[3])))
+			goto bad;
+		FSEEK(bf,(OFF_T)0,SEEK_END);
+		b->urw = 3;
+		}
+#endif
+done1:
+	fclose(tf);
+done:
+	f__cf = b->ufd = bf;
+#else /* NO_TRUNCATE */
+	if (b->urw & 2)
+		fflush(b->ufd); /* necessary on some Linux systems */
+#ifndef FTRUNCATE
+#define FTRUNCATE ftruncate
+#endif
+	rc = FTRUNCATE(fileno(b->ufd), loc);
+	/* The following FSEEK is unnecessary on some systems, */
+	/* but should be harmless. */
+	FSEEK(b->ufd, (OFF_T)0, SEEK_END);
+#endif /* NO_TRUNCATE */
+	if (rc)
+		err(a->aerr,111,"endfile");
+	return 0;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/erf_.c b/igraph/src/erf_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/erf_.c
@@ -0,0 +1,22 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef REAL
+#define REAL double
+#endif
+
+#ifdef KR_headers
+double erf();
+REAL erf_(x) real *x;
+#else
+extern double erf(double);
+REAL erf_(real *x)
+#endif
+{
+return( erf((double)*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/erfc_.c b/igraph/src/erfc_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/erfc_.c
@@ -0,0 +1,22 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef REAL
+#define REAL double
+#endif
+
+#ifdef KR_headers
+double erfc();
+REAL erfc_(x) real *x;
+#else
+extern double erfc(double);
+REAL erfc_(real *x)
+#endif
+{
+return( erfc((double)*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/err.c b/igraph/src/err.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/err.c
@@ -0,0 +1,293 @@
+#include "sysdep1.h"	/* here to get stat64 on some badly designed Linux systems */
+#include "f2c.h"
+#ifdef KR_headers
+#define Const /*nothing*/
+extern char *malloc();
+#else
+#define Const const
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#endif
+#include "fio.h"
+#include "fmt.h"	/* for struct syl */
+
+/* Compile this with -DNO_ISATTY if unistd.h does not exist or */
+/* if it does not define int isatty(int). */
+#ifdef NO_ISATTY
+#define isatty(x) 0
+#else
+#include <unistd.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*global definitions*/
+unit f__units[MXUNIT];	/*unit table*/
+flag f__init;	/*0 on entry, 1 after initializations*/
+cilist *f__elist;	/*active external io list*/
+icilist *f__svic;	/*active internal io list*/
+flag f__reading;	/*1 if reading, 0 if writing*/
+flag f__cplus,f__cblank;
+Const char *f__fmtbuf;
+flag f__external;	/*1 if external io, 0 if internal */
+#ifdef KR_headers
+int (*f__doed)(),(*f__doned)();
+int (*f__doend)(),(*f__donewrec)(),(*f__dorevert)();
+int (*f__getn)();	/* for formatted input */
+void (*f__putn)();	/* for formatted output */
+#else
+int (*f__getn)(void);	/* for formatted input */
+void (*f__putn)(int);	/* for formatted output */
+int (*f__doed)(struct syl*, char*, ftnlen),(*f__doned)(struct syl*);
+int (*f__dorevert)(void),(*f__donewrec)(void),(*f__doend)(void);
+#endif
+flag f__sequential;	/*1 if sequential io, 0 if direct*/
+flag f__formatted;	/*1 if formatted io, 0 if unformatted*/
+FILE *f__cf;	/*current file*/
+unit *f__curunit;	/*current unit*/
+int f__recpos;	/*place in current record*/
+OFF_T f__cursor, f__hiwater;
+int f__scale;
+char *f__icptr;
+
+/*error messages*/
+Const char *F_err[] =
+{
+	"error in format",				/* 100 */
+	"illegal unit number",				/* 101 */
+	"formatted io not allowed",			/* 102 */
+	"unformatted io not allowed",			/* 103 */
+	"direct io not allowed",			/* 104 */
+	"sequential io not allowed",			/* 105 */
+	"can't backspace file",				/* 106 */
+	"null file name",				/* 107 */
+	"can't stat file",				/* 108 */
+	"unit not connected",				/* 109 */
+	"off end of record",				/* 110 */
+	"truncation failed in endfile",			/* 111 */
+	"incomprehensible list input",			/* 112 */
+	"out of free space",				/* 113 */
+	"unit not connected",				/* 114 */
+	"read unexpected character",			/* 115 */
+	"bad logical input field",			/* 116 */
+	"bad variable type",				/* 117 */
+	"bad namelist name",				/* 118 */
+	"variable not in namelist",			/* 119 */
+	"no end record",				/* 120 */
+	"variable count incorrect",			/* 121 */
+	"subscript for scalar variable",		/* 122 */
+	"invalid array section",			/* 123 */
+	"substring out of bounds",			/* 124 */
+	"subscript out of bounds",			/* 125 */
+	"can't read file",				/* 126 */
+	"can't write file",				/* 127 */
+	"'new' file exists",				/* 128 */
+	"can't append to file",				/* 129 */
+	"non-positive record number",			/* 130 */
+	"nmLbuf overflow"				/* 131 */
+};
+#define MAXERR (sizeof(F_err)/sizeof(char *)+100)
+
+ int
+#ifdef KR_headers
+f__canseek(f) FILE *f; /*SYSDEP*/
+#else
+f__canseek(FILE *f) /*SYSDEP*/
+#endif
+{
+#ifdef NON_UNIX_STDIO
+	return !isatty(fileno(f));
+#else
+	struct STAT_ST x;
+
+	if (FSTAT(fileno(f),&x) < 0)
+		return(0);
+#ifdef S_IFMT
+	switch(x.st_mode & S_IFMT) {
+	case S_IFDIR:
+	case S_IFREG:
+		if(x.st_nlink > 0)	/* !pipe */
+			return(1);
+		else
+			return(0);
+	case S_IFCHR:
+		if(isatty(fileno(f)))
+			return(0);
+		return(1);
+#ifdef S_IFBLK
+	case S_IFBLK:
+		return(1);
+#endif
+	}
+#else
+#ifdef S_ISDIR
+	/* POSIX version */
+	if (S_ISREG(x.st_mode) || S_ISDIR(x.st_mode)) {
+		if(x.st_nlink > 0)	/* !pipe */
+			return(1);
+		else
+			return(0);
+		}
+	if (S_ISCHR(x.st_mode)) {
+		if(isatty(fileno(f)))
+			return(0);
+		return(1);
+		}
+	if (S_ISBLK(x.st_mode))
+		return(1);
+#else
+	Help! How does fstat work on this system?
+#endif
+#endif
+	return(0);	/* who knows what it is? */
+#endif
+}
+
+ void
+#ifdef KR_headers
+f__fatal(n,s) char *s;
+#else
+f__fatal(int n, const char *s)
+#endif
+{
+	if(n<100 && n>=0) perror(s); /*SYSDEP*/
+	else if(n >= (int)MAXERR || n < -1)
+	{	fprintf(stderr,"%s: illegal error number %d\n",s,n);
+	}
+	else if(n == -1) fprintf(stderr,"%s: end of file\n",s);
+	else
+		fprintf(stderr,"%s: %s\n",s,F_err[n-100]);
+	if (f__curunit) {
+		fprintf(stderr,"apparent state: unit %d ",
+			(int)(f__curunit-f__units));
+		fprintf(stderr, f__curunit->ufnm ? "named %s\n" : "(unnamed)\n",
+			f__curunit->ufnm);
+		}
+	else
+		fprintf(stderr,"apparent state: internal I/O\n");
+	if (f__fmtbuf)
+		fprintf(stderr,"last format: %s\n",f__fmtbuf);
+	fprintf(stderr,"lately %s %s %s %s",f__reading?"reading":"writing",
+		f__sequential?"sequential":"direct",f__formatted?"formatted":"unformatted",
+		f__external?"external":"internal");
+	sig_die(" IO", 1);
+}
+/*initialization routine*/
+ VOID
+f_init(Void)
+{	unit *p;
+
+	f__init=1;
+	p= &f__units[0];
+	p->ufd=stderr;
+	p->useek=f__canseek(stderr);
+	p->ufmt=1;
+	p->uwrt=1;
+	p = &f__units[5];
+	p->ufd=stdin;
+	p->useek=f__canseek(stdin);
+	p->ufmt=1;
+	p->uwrt=0;
+	p= &f__units[6];
+	p->ufd=stdout;
+	p->useek=f__canseek(stdout);
+	p->ufmt=1;
+	p->uwrt=1;
+}
+
+ int
+#ifdef KR_headers
+f__nowreading(x) unit *x;
+#else
+f__nowreading(unit *x)
+#endif
+{
+	OFF_T loc;
+	int ufmt, urw;
+	extern char *f__r_mode[], *f__w_mode[];
+
+	if (x->urw & 1)
+		goto done;
+	if (!x->ufnm)
+		goto cantread;
+	ufmt = x->url ? 0 : x->ufmt;
+	loc = FTELL(x->ufd);
+	urw = 3;
+	if (!FREOPEN(x->ufnm, f__w_mode[ufmt|2], x->ufd)) {
+		urw = 1;
+		if(!FREOPEN(x->ufnm, f__r_mode[ufmt], x->ufd)) {
+ cantread:
+			errno = 126;
+			return 1;
+			}
+		}
+	FSEEK(x->ufd,loc,SEEK_SET);
+	x->urw = urw;
+ done:
+	x->uwrt = 0;
+	return 0;
+}
+
+ int
+#ifdef KR_headers
+f__nowwriting(x) unit *x;
+#else
+f__nowwriting(unit *x)
+#endif
+{
+	OFF_T loc;
+	int ufmt;
+	extern char *f__w_mode[];
+
+	if (x->urw & 2) {
+		if (x->urw & 1)
+			FSEEK(x->ufd, (OFF_T)0, SEEK_CUR);
+		goto done;
+		}
+	if (!x->ufnm)
+		goto cantwrite;
+	ufmt = x->url ? 0 : x->ufmt;
+	if (x->uwrt == 3) { /* just did write, rewind */
+		if (!(f__cf = x->ufd =
+				FREOPEN(x->ufnm,f__w_mode[ufmt],x->ufd)))
+			goto cantwrite;
+		x->urw = 2;
+		}
+	else {
+		loc=FTELL(x->ufd);
+		if (!(f__cf = x->ufd =
+			FREOPEN(x->ufnm, f__w_mode[ufmt | 2], x->ufd)))
+			{
+			x->ufd = NULL;
+ cantwrite:
+			errno = 127;
+			return(1);
+			}
+		x->urw = 3;
+		FSEEK(x->ufd,loc,SEEK_SET);
+		}
+ done:
+	x->uwrt = 1;
+	return 0;
+}
+
+ int
+#ifdef KR_headers
+err__fl(f, m, s) int f, m; char *s;
+#else
+err__fl(int f, int m, const char *s)
+#endif
+{
+	if (!f)
+		f__fatal(m, s);
+	if (f__doend)
+		(*f__doend)();
+	return errno = m;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/error.c b/igraph/src/error.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/error.c
@@ -0,0 +1,74 @@
+/* error.c
+ *
+ * Copyright (C) 2010-2011 Tamas Nepusz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+
+static char *plfit_i_error_strings[] = {
+    "No error",
+    "Failed",
+    "Invalid value",
+    "Underflow",
+    "Overflow",
+    "Not enough memory"
+};
+
+#ifndef USING_R
+static plfit_error_handler_t* plfit_error_handler = plfit_error_handler_abort;
+#else
+/* This is overwritten, anyway */
+static plfit_error_handler_t* plfit_error_handler = plfit_error_handler_ignore;
+#endif
+
+const char* plfit_strerror(const int plfit_errno) {
+  return plfit_i_error_strings[plfit_errno];
+}
+
+plfit_error_handler_t* plfit_set_error_handler(plfit_error_handler_t* new_handler) {
+    plfit_error_handler_t* old_handler = plfit_error_handler;
+    plfit_error_handler = new_handler;
+    return old_handler;
+}
+
+void plfit_error(const char *reason, const char *file, int line,
+        int plfit_errno) {
+    plfit_error_handler(reason, file, line, plfit_errno);
+}
+
+#ifndef USING_R
+void plfit_error_handler_abort(const char *reason, const char *file, int line,
+        int plfit_errno) {
+    fprintf(stderr, "Error at %s:%i : %s, %s\n", file, line, reason,
+            plfit_strerror(plfit_errno));
+    abort();
+}
+#endif
+
+#ifndef USING_R
+void plfit_error_handler_printignore(const char *reason, const char *file, int line,
+        int plfit_errno) {
+    fprintf(stderr, "Error at %s:%i : %s, %s\n", file, line, reason,
+            plfit_strerror(plfit_errno));
+}
+#endif
+
+void plfit_error_handler_ignore(const char *reason, const char *file, int line,
+        int plfit_errno) {
+}
diff --git a/igraph/src/etime_.c b/igraph/src/etime_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/etime_.c
@@ -0,0 +1,57 @@
+#include "time.h"
+
+#ifdef MSDOS
+#undef USE_CLOCK
+#define USE_CLOCK
+#endif
+
+#ifndef REAL
+#define REAL double
+#endif
+
+#ifndef USE_CLOCK
+#define _INCLUDE_POSIX_SOURCE	/* for HP-UX */
+#define _INCLUDE_XOPEN_SOURCE	/* for HP-UX */
+#include "sys/types.h"
+#include "sys/times.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#endif
+
+#undef Hz
+#ifdef CLK_TCK
+#define Hz CLK_TCK
+#else
+#ifdef HZ
+#define Hz HZ
+#else
+#define Hz 60
+#endif
+#endif
+
+ REAL
+#ifdef KR_headers
+etime_(tarray) float *tarray;
+#else
+etime_(float *tarray)
+#endif
+{
+#ifdef USE_CLOCK
+#ifndef CLOCKS_PER_SECOND
+#define CLOCKS_PER_SECOND Hz
+#endif
+	double t = clock();
+	tarray[1] = 0;
+	return tarray[0] = t / CLOCKS_PER_SECOND;
+#else
+	struct tms t;
+
+	times(&t);
+	return	  (tarray[0] = (double)t.tms_utime/Hz)
+		+ (tarray[1] = (double)t.tms_stime/Hz);
+#endif
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/exit_.c b/igraph/src/exit_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/exit_.c
@@ -0,0 +1,43 @@
+/* This gives the effect of
+
+	subroutine exit(rc)
+	integer*4 rc
+	stop
+	end
+
+ * with the added side effect of supplying rc as the program's exit code.
+ */
+
+#include "f2c.h"
+#undef abs
+#undef min
+#undef max
+#ifndef KR_headers
+#include "stdlib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void f_exit(void);
+#endif
+
+ void
+#ifdef KR_headers
+exit_(rc) integer *rc;
+#else
+exit_(integer *rc)
+#endif
+{
+#ifdef NO_ONEXIT
+	f_exit();
+#endif
+	exit(*rc);
+	}
+#ifdef __cplusplus
+}
+#endif
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/f77_aloc.c b/igraph/src/f77_aloc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/f77_aloc.c
@@ -0,0 +1,44 @@
+#include "f2c.h"
+#undef abs
+#undef min
+#undef max
+#include "stdio.h"
+
+static integer memfailure = 3;
+
+#ifdef KR_headers
+extern char *malloc();
+extern void exit_();
+
+ char *
+F77_aloc(Len, whence) integer Len; char *whence;
+#else
+#include "stdlib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void exit_(integer*);
+#ifdef __cplusplus
+	}
+#endif
+
+ char *
+F77_aloc(integer Len, const char *whence)
+#endif
+{
+	char *rv;
+	unsigned int uLen = (unsigned int) Len;	/* for K&R C */
+
+	if (!(rv = (char*)malloc(uLen))) {
+		fprintf(stderr, "malloc(%u) failure in %s\n",
+			uLen, whence);
+		exit_(&memfailure);
+		}
+	return rv;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/f77vers.c b/igraph/src/f77vers.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/f77vers.c
@@ -0,0 +1,97 @@
+ char 
+_libf77_version_f2c[] = "\n@(#) LIBF77 VERSION (f2c) 20051004\n";
+
+/*
+2.00	11 June 1980.  File version.c added to library.
+2.01	31 May 1988.  s_paus() flushes stderr; names of hl_* fixed
+	[ d]erf[c ] added
+	 8 Aug. 1989: #ifdefs for f2c -i2 added to s_cat.c
+	29 Nov. 1989: s_cmp returns long (for f2c)
+	30 Nov. 1989: arg types from f2c.h
+	12 Dec. 1989: s_rnge allows long names
+	19 Dec. 1989: getenv_ allows unsorted environment
+	28 Mar. 1990: add exit(0) to end of main()
+	 2 Oct. 1990: test signal(...) == SIG_IGN rather than & 01 in main
+	17 Oct. 1990: abort() calls changed to sig_die(...,1)
+	22 Oct. 1990: separate sig_die from main
+	25 Apr. 1991: minor, theoretically invisible tweaks to s_cat, sig_die
+	31 May  1991: make system_ return status
+	18 Dec. 1991: change long to ftnlen (for -i2) many places
+	28 Feb. 1992: repair z_sqrt.c (scribbled on input, gave wrong answer)
+	18 July 1992: for n < 0, repair handling of 0**n in pow_[dr]i.c
+			and m**n in pow_hh.c and pow_ii.c;
+			catch SIGTRAP in main() for error msg before abort
+	23 July 1992: switch to ANSI prototypes unless KR_headers is #defined
+	23 Oct. 1992: fix botch in signal_.c (erroneous deref of 2nd arg);
+			change Cabs to f__cabs.
+	12 March 1993: various tweaks for C++
+	 2 June 1994: adjust so abnormal terminations invoke f_exit just once
+	16 Sept. 1994: s_cmp: treat characters as unsigned in comparisons.
+	19 Sept. 1994: s_paus: flush after end of PAUSE; add -DMSDOS
+	12 Jan. 1995:	pow_[dhiqrz][hiq]: adjust x**i to work on machines
+			that sign-extend right shifts when i is the most
+			negative integer.
+	26 Jan. 1995: adjust s_cat.c, s_copy.c to permit the left-hand side
+			of character assignments to appear on the right-hand
+			side (unless compiled with -DNO_OVERWRITE).
+	27 Jan. 1995: minor tweak to s_copy.c: copy forward whenever
+			possible (for better cache behavior).
+	30 May 1995:  added subroutine exit(rc) integer rc. Version not changed.
+	29 Aug. 1995: add F77_aloc.c; use it in s_cat.c and system_.c.
+	6 Sept. 1995: fix return type of system_ under -DKR_headers.
+	19 Dec. 1995: s_cat.c: fix bug when 2nd or later arg overlaps lhs.
+	19 Mar. 1996: s_cat.c: supply missing break after overlap detection.
+	13 May 1996:  add [lq]bitbits.c and [lq]bitshft.c (f90 bit intrinsics).
+	19 June 1996: add casts to unsigned in [lq]bitshft.c.
+	26 Feb. 1997: adjust functions with a complex output argument
+			to permit aliasing it with input arguments.
+			(For now, at least, this is just for possible
+			benefit of g77.)
+	4 April 1997: [cz]_div.c: tweaks invisible on most systems (that may
+			affect systems using gratuitous extra precision).
+	19 Sept. 1997: [de]time_.c (Unix systems only): change return
+			type to double.
+	2 May 1999:	getenv_.c: omit environ in favor of getenv().
+			c_cos.c, c_exp.c, c_sin.c, d_cnjg.c, r_cnjg.c,
+			z_cos.c, z_exp.c, z_log.c, z_sin.c: cope fully with
+			overlapping arguments caused by equivalence.
+	3 May 1999:	"invisible" tweaks to omit compiler warnings in
+			abort_.c, ef1asc_.c, s_rnge.c, s_stop.c.
+
+	7 Sept. 1999: [cz]_div.c: arrange for compilation under
+			-DIEEE_COMPLEX_DIVIDE to make these routines
+			avoid calling sig_die when the denominator
+			vanishes; instead, they return pairs of NaNs
+			or Infinities, depending whether the numerator
+			also vanishes or not.  VERSION not changed.
+	15 Nov. 1999: s_rnge.c: add casts for the case of
+			sizeof(ftnint) == sizeof(int) < sizeof(long).
+	10 March 2000: z_log.c: improve accuracy of Real(log(z)) for, e.g.,
+			z near (+-1,eps) with |eps| small.  For the old
+			evaluation, compile with -DPre20000310 .
+	20 April 2000: s_cat.c: tweak argument types to accord with
+			calls by f2c when ftnint and ftnlen are of
+			different sizes (different numbers of bits).
+	4 July 2000: adjustments to permit compilation by C++ compilers;
+			VERSION string remains unchanged.
+	29 Sept. 2000: dtime_.c, etime_.c: use floating-point divide.
+			dtime_.d, erf_.c, erfc_.c, etime.c: for use with
+			"f2c -R", compile with -DREAL=float.
+	23 June 2001: add uninit.c; [fi]77vers.c: make version strings
+			visible as extern char _lib[fi]77_version_f2c[].
+	5 July 2001: modify uninit.c for __mc68k__ under Linux.
+	16 Nov. 2001: uninit.c: Linux Power PC logic supplied by Alan Bain.
+	18 Jan. 2002: fix glitches in qbit_bits(): wrong return type,
+			missing ~ on y in return value.
+	14 March 2002: z_log.c: add code to cope with buggy compilers
+			(e.g., some versions of gcc under -O2 or -O3)
+			that do floating-point comparisons against values
+			computed into extended-precision registers on some
+			systems (such as Intel IA32 systems).  Compile with
+			-DNO_DOUBLE_EXTENDED to omit the new logic.
+	4 Oct. 2002: uninit.c: on IRIX systems, omit use of shell variables.
+	10 Oct 2005: uninit.c: on IA32 Linux systems, leave the rounding
+			precision alone rather than forcing it to 53 bits;
+			compile with -DUNINIT_F2C_PRECISION_53 to get the
+			former behavior.
+*/
diff --git a/igraph/src/fast_community.c b/igraph/src/fast_community.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/fast_community.c
@@ -0,0 +1,1067 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_community.h"
+#include "igraph_memory.h"
+#include "igraph_iterators.h"
+#include "igraph_interface.h"
+#include "igraph_progress.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_structural.h"
+#include "igraph_vector_ptr.h"
+#include "config.h"
+
+/* #define IGRAPH_FASTCOMM_DEBUG */
+
+#ifdef _MSC_VER
+/* MSVC does not support variadic macros */
+#include <stdarg.h>
+void debug(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+#ifdef IGRAPH_FASTCOMM_DEBUG
+    vfprintf(stderr, fmt, args);
+#endif
+    va_end(args);
+}
+#else
+#ifdef IGRAPH_FASTCOMM_DEBUG
+    #define debug(...) fprintf(stderr, __VA_ARGS__)
+#else
+    #define debug(...)
+#endif
+#endif
+
+/*
+ * Implementation of the community structure algorithm originally published
+ * by Clauset et al in:
+ *
+ * A. Clauset, M.E.J. Newman and C. Moore, "Finding community structure in
+ * very large networks.". Phys. Rev. E 70, 066111 (2004).
+ *
+ * The data structures being used are slightly different and they are described
+ * most closely in:
+ *
+ * K. Wakita, T. Tsurumi, "Finding community structure in mega-scale social
+ * networks.". arXiv:cs/0702048v1.
+ *
+ * We maintain a vector of communities, each of which containing a list of
+ * pointers to their neighboring communities along with the increase in the
+ * modularity score that could be achieved by joining the two communities.
+ * Each community has a pointer to one of its neighbors - the one which would
+ * result in the highest increase in modularity after a join. The local
+ * (community-level) maximums are also stored in an indexed max-heap. The
+ * max-heap itself stores its elements in an array which satisfies the heap
+ * property, but to allow us to access any of the elements in the array based
+ * on the community index (and not based on the array index - which depends on
+ * the element's actual position in the heap), we also maintain an index
+ * vector in the heap: the ith element of the index vector contains the
+ * position of community i in the array of the max-heap. When we perform
+ * sifting operations on the heap to restore the heap property, we also maintain
+ * the index vector.
+ */
+
+/* Structure storing a pair of communities along with their dQ values */
+typedef struct s_igraph_i_fastgreedy_commpair {
+    long int first;       /* first member of the community pair */
+    long int second;      /* second member of the community pair */
+    igraph_real_t *dq;    /* pointer to a member of the dq vector storing the */
+    /* increase in modularity achieved when joining */
+    struct s_igraph_i_fastgreedy_commpair *opposite;
+} igraph_i_fastgreedy_commpair;
+
+/* Structure storing a community */
+typedef struct {
+    igraph_integer_t id;      /* Identifier of the community (for merges matrix) */
+    igraph_integer_t size;    /* Size of the community */
+    igraph_vector_ptr_t neis; /* references to neighboring communities */
+    igraph_i_fastgreedy_commpair* maxdq; /* community pair with maximal dq */
+} igraph_i_fastgreedy_community;
+
+/* Global community list structure */
+typedef struct {
+    long int no_of_communities, n;  /* number of communities, number of vertices */
+    igraph_i_fastgreedy_community* e;     /* list of communities */
+    igraph_i_fastgreedy_community** heap; /* heap of communities */
+    igraph_integer_t *heapindex; /* heap index to speed up lookup by community idx */
+} igraph_i_fastgreedy_community_list;
+
+/* Scans the community neighborhood list for the new maximal dq value.
+ * Returns 1 if the maximum is different from the previous one,
+ * 0 otherwise. */
+int igraph_i_fastgreedy_community_rescan_max(
+    igraph_i_fastgreedy_community* comm) {
+    long int i, n;
+    igraph_i_fastgreedy_commpair *p, *best;
+    igraph_real_t bestdq, currdq;
+
+    n = igraph_vector_ptr_size(&comm->neis);
+    if (n == 0) {
+        comm->maxdq = 0;
+        return 1;
+    }
+
+    best = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[0];
+    bestdq = *best->dq;
+    for (i = 1; i < n; i++) {
+        p = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[i];
+        currdq = *p->dq;
+        if (currdq > bestdq) {
+            best = p;
+            bestdq = currdq;
+        }
+    }
+
+    if (best != comm->maxdq) {
+        comm->maxdq = best;
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+/* Destroys the global community list object */
+void igraph_i_fastgreedy_community_list_destroy(
+    igraph_i_fastgreedy_community_list* list) {
+    long int i;
+    for (i = 0; i < list->n; i++) {
+        igraph_vector_ptr_destroy(&list->e[i].neis);
+    }
+    free(list->e);
+    if (list->heapindex != 0) {
+        free(list->heapindex);
+    }
+    if (list->heap != 0) {
+        free(list->heap);
+    }
+}
+
+/* Community list heap maintenance: sift down */
+void igraph_i_fastgreedy_community_list_sift_down(
+    igraph_i_fastgreedy_community_list* list, long int idx) {
+    long int root, child, c1, c2;
+    igraph_i_fastgreedy_community* dummy;
+    igraph_integer_t dummy2;
+    igraph_i_fastgreedy_community** heap = list->heap;
+    igraph_integer_t* heapindex = list->heapindex;
+
+    root = idx;
+    while (root * 2 + 1 < list->no_of_communities) {
+        child = root * 2 + 1;
+        if (child + 1 < list->no_of_communities &&
+            *heap[child]->maxdq->dq < *heap[child + 1]->maxdq->dq) {
+            child++;
+        }
+        if (*heap[root]->maxdq->dq < *heap[child]->maxdq->dq) {
+            c1 = heap[root]->maxdq->first;
+            c2 = heap[child]->maxdq->first;
+
+            dummy = heap[root];
+            heap[root] = heap[child];
+            heap[child] = dummy;
+
+            dummy2 = heapindex[c1];
+            heapindex[c1] = heapindex[c2];
+            heapindex[c2] = dummy2;
+
+            root = child;
+        } else {
+            break;
+        }
+    }
+}
+
+/* Community list heap maintenance: sift up */
+void igraph_i_fastgreedy_community_list_sift_up(
+    igraph_i_fastgreedy_community_list* list, long int idx) {
+    long int root, parent, c1, c2;
+    igraph_i_fastgreedy_community* dummy;
+    igraph_integer_t dummy2;
+    igraph_i_fastgreedy_community** heap = list->heap;
+    igraph_integer_t* heapindex = list->heapindex;
+
+    root = idx;
+    while (root > 0) {
+        parent = (root - 1) / 2;
+        if (*heap[parent]->maxdq->dq < *heap[root]->maxdq->dq) {
+            c1 = heap[root]->maxdq->first;
+            c2 = heap[parent]->maxdq->first;
+
+            dummy = heap[parent];
+            heap[parent] = heap[root];
+            heap[root] = dummy;
+
+            dummy2 = heapindex[c1];
+            heapindex[c1] = heapindex[c2];
+            heapindex[c2] = dummy2;
+
+            root = parent;
+        } else {
+            break;
+        }
+    }
+}
+
+/* Builds the community heap for the first time */
+void igraph_i_fastgreedy_community_list_build_heap(
+    igraph_i_fastgreedy_community_list* list) {
+    long int i;
+    for (i = list->no_of_communities / 2 - 1; i >= 0; i--) {
+        igraph_i_fastgreedy_community_list_sift_down(list, i);
+    }
+}
+
+/* Finds the element belonging to a given community in the heap and return its
+ * index in the heap array */
+#define igraph_i_fastgreedy_community_list_find_in_heap(list, idx) (list)->heapindex[idx]
+
+/* Dumps the heap - for debugging purposes */
+void igraph_i_fastgreedy_community_list_dump_heap(
+    igraph_i_fastgreedy_community_list* list) {
+    long int i;
+    debug("Heap:\n");
+    for (i = 0; i < list->no_of_communities; i++) {
+        debug("(%ld, %p, %p)", i, list->heap[i],
+              list->heap[i]->maxdq);
+        if (list->heap[i]->maxdq) {
+            debug(" (%ld, %ld, %.7f)", list->heap[i]->maxdq->first,
+                  list->heap[i]->maxdq->second, *list->heap[i]->maxdq->dq);
+        }
+        debug("\n");
+    }
+    debug("Heap index:\n");
+    for (i = 0; i < list->no_of_communities; i++) {
+        debug("%ld ", (long)list->heapindex[i]);
+    }
+    debug("\nEND\n");
+}
+
+/* Checks if the community heap satisfies the heap property.
+ * Only useful for debugging. */
+void igraph_i_fastgreedy_community_list_check_heap(
+    igraph_i_fastgreedy_community_list* list) {
+    long int i;
+    for (i = 0; i < list->no_of_communities / 2; i++) {
+        if ((2 * i + 1 < list->no_of_communities && *list->heap[i]->maxdq->dq < *list->heap[2 * i + 1]->maxdq->dq) ||
+            (2 * i + 2 < list->no_of_communities && *list->heap[i]->maxdq->dq < *list->heap[2 * i + 2]->maxdq->dq)) {
+            IGRAPH_WARNING("Heap property violated");
+            debug("Position: %ld, %ld and %ld\n", i, 2 * i + 1, 2 * i + 2);
+            igraph_i_fastgreedy_community_list_dump_heap(list);
+        }
+    }
+}
+
+/* Removes a given element from the heap */
+void igraph_i_fastgreedy_community_list_remove(
+    igraph_i_fastgreedy_community_list* list, long int idx) {
+    igraph_real_t old;
+    long int commidx;
+
+    /* First adjust the index */
+    commidx = list->heap[list->no_of_communities - 1]->maxdq->first;
+    list->heapindex[commidx] = (igraph_integer_t) idx;
+    commidx = list->heap[idx]->maxdq->first;
+    list->heapindex[commidx] = -1;
+
+    /* Now remove the element */
+    old = *list->heap[idx]->maxdq->dq;
+    list->heap[idx] = list->heap[list->no_of_communities - 1];
+    list->no_of_communities--;
+
+    /* Recover heap property */
+    if (old > *list->heap[idx]->maxdq->dq) {
+        igraph_i_fastgreedy_community_list_sift_down(list, idx);
+    } else {
+        igraph_i_fastgreedy_community_list_sift_up(list, idx);
+    }
+}
+
+/* Removes a given element from the heap when there are no more neighbors
+ * for it (comm->maxdq is NULL) */
+void igraph_i_fastgreedy_community_list_remove2(
+    igraph_i_fastgreedy_community_list* list, long int idx, long int comm) {
+    long int i;
+
+    if (idx == list->no_of_communities - 1) {
+        /* We removed the rightmost element on the bottom level, no problem,
+         * there's nothing to be done */
+        list->heapindex[comm] = -1;
+        list->no_of_communities--;
+        return;
+    }
+
+    /* First adjust the index */
+    i = list->heap[list->no_of_communities - 1]->maxdq->first;
+    list->heapindex[i] = (igraph_integer_t) idx;
+    list->heapindex[comm] = -1;
+
+    /* Now remove the element */
+    list->heap[idx] = list->heap[list->no_of_communities - 1];
+    list->no_of_communities--;
+
+    /* Recover heap property */
+    for (i = list->no_of_communities / 2 - 1; i >= 0; i--) {
+        igraph_i_fastgreedy_community_list_sift_down(list, i);
+    }
+}
+
+/* Removes the pair belonging to community k from the neighborhood list
+ * of community c (that is, clist[c]) and recalculates maxdq */
+void igraph_i_fastgreedy_community_remove_nei(
+    igraph_i_fastgreedy_community_list* list, long int c, long int k) {
+    long int i, n;
+    igraph_bool_t rescan = 0;
+    igraph_i_fastgreedy_commpair *p;
+    igraph_i_fastgreedy_community *comm;
+    igraph_real_t olddq;
+
+    comm = &list->e[c];
+    n = igraph_vector_ptr_size(&comm->neis);
+    for (i = 0; i < n; i++) {
+        p = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[i];
+        if (p->second == k) {
+            /* Check current maxdq */
+            if (comm->maxdq == p) {
+                rescan = 1;
+            }
+            break;
+        }
+    }
+    if (i < n) {
+        olddq = *comm->maxdq->dq;
+        igraph_vector_ptr_remove(&comm->neis, i);
+        if (rescan) {
+            igraph_i_fastgreedy_community_rescan_max(comm);
+            i = igraph_i_fastgreedy_community_list_find_in_heap(list, c);
+            if (comm->maxdq) {
+                if (*comm->maxdq->dq > olddq) {
+                    igraph_i_fastgreedy_community_list_sift_up(list, i);
+                } else {
+                    igraph_i_fastgreedy_community_list_sift_down(list, i);
+                }
+            } else {
+                /* no more neighbors for this community. we should remove this
+                 * community from the heap and restore the heap property */
+                debug("REMOVING (NO MORE NEIS): %ld\n", i);
+                igraph_i_fastgreedy_community_list_remove2(list, i, c);
+            }
+        }
+    }
+}
+
+/* Auxiliary function to sort a community pair list with respect to the
+ * `second` field */
+int igraph_i_fastgreedy_commpair_cmp(const void* p1, const void* p2) {
+    igraph_i_fastgreedy_commpair *cp1, *cp2;
+    cp1 = *(igraph_i_fastgreedy_commpair**)p1;
+    cp2 = *(igraph_i_fastgreedy_commpair**)p2;
+    return (int) (cp1->second - cp2->second);
+}
+
+/* Sorts the neighbor list of the community with the given index, optionally
+ * optimizing the process if we know that the list is nearly sorted and only
+ * a given pair is in the wrong place. */
+void igraph_i_fastgreedy_community_sort_neighbors_of(
+    igraph_i_fastgreedy_community_list* list, long int index,
+    igraph_i_fastgreedy_commpair* changed_pair) {
+    igraph_vector_ptr_t* vec;
+    long int i, n;
+    igraph_bool_t can_skip_sort = 0;
+    igraph_i_fastgreedy_commpair *other_pair;
+
+    vec = &list->e[index].neis;
+    if (changed_pair != 0) {
+        /* Optimized sorting */
+
+        /* First we look for changed_pair in vec */
+        n = igraph_vector_ptr_size(vec);
+        for (i = 0; i < n; i++) {
+            if (VECTOR(*vec)[i] == changed_pair) {
+                break;
+            }
+        }
+
+        /* Did we find it? We should have -- otherwise it's a bug */
+        if (i >= n) {
+            IGRAPH_WARNING("changed_pair not found in neighbor vector while re-sorting "
+                           "the neighbors of a community; this is probably a bug. Falling back to "
+                           "full sort instead."
+                          );
+        } else {
+            /* Okay, the pair that changed is at index i. We need to figure out where
+             * its new place should be. We can simply try moving the item all the way
+             * to the left as long as the comparison function tells so (since the
+             * rest of the vector is sorted), and then move all the way to the right
+             * as long as the comparison function tells so, and we will be okay. */
+
+            /* Shifting to the left */
+            while (i > 0) {
+                other_pair = VECTOR(*vec)[i - 1];
+                if (other_pair->second > changed_pair->second) {
+                    VECTOR(*vec)[i] = other_pair;
+                    i--;
+                } else {
+                    break;
+                }
+            }
+            VECTOR(*vec)[i] = changed_pair;
+
+            /* Shifting to the right */
+            while (i < n - 1) {
+                other_pair = VECTOR(*vec)[i + 1];
+                if (other_pair->second < changed_pair->second) {
+                    VECTOR(*vec)[i] = other_pair;
+                    i++;
+                } else {
+                    break;
+                }
+            }
+            VECTOR(*vec)[i] = changed_pair;
+
+            /* Mark that we don't need a full sort */
+            can_skip_sort = 1;
+        }
+    }
+
+    if (!can_skip_sort) {
+        /* Fallback to full sorting */
+        igraph_vector_ptr_sort(vec, igraph_i_fastgreedy_commpair_cmp);
+    }
+}
+
+/* Updates the dq value of community pair p in the community with index p->first
+ * of the community list clist to newdq and restores the heap property
+ * in community c if necessary. Returns 1 if the maximum in the row had
+ * to be updated, zero otherwise */
+int igraph_i_fastgreedy_community_update_dq(
+    igraph_i_fastgreedy_community_list* list,
+    igraph_i_fastgreedy_commpair* p, igraph_real_t newdq) {
+    long int i, j, to, from;
+    igraph_real_t olddq;
+    igraph_i_fastgreedy_community *comm_to, *comm_from;
+    to = p->first; from = p->second;
+    comm_to = &list->e[to];
+    comm_from = &list->e[from];
+    if (comm_to->maxdq == p && newdq >= *p->dq) {
+        /* If we are adjusting the current maximum and it is increased, we don't
+         * have to re-scan for the new maximum */
+        *p->dq = newdq;
+        /* The maximum was increased, so perform a sift-up in the heap */
+        i = igraph_i_fastgreedy_community_list_find_in_heap(list, to);
+        igraph_i_fastgreedy_community_list_sift_up(list, i);
+        /* Let's check the opposite side. If the pair was not the maximal in
+         * the opposite side (the other community list)... */
+        if (comm_from->maxdq != p->opposite) {
+            if (*comm_from->maxdq->dq < newdq) {
+                /* ...and it will become the maximal, we need to adjust and sift up */
+                comm_from->maxdq = p->opposite;
+                j = igraph_i_fastgreedy_community_list_find_in_heap(list, from);
+                igraph_i_fastgreedy_community_list_sift_up(list, j);
+            } else {
+                /* The pair was not the maximal in the opposite side and it will
+                 * NOT become the maximal, there's nothing to do there */
+            }
+        } else {
+            /* The pair was maximal in the opposite side, so we need to sift it up
+             * with the new value */
+            j = igraph_i_fastgreedy_community_list_find_in_heap(list, from);
+            igraph_i_fastgreedy_community_list_sift_up(list, j);
+        }
+        return 1;
+    } else if (comm_to->maxdq != p && (newdq <= *comm_to->maxdq->dq)) {
+        /* If we are modifying an item which is not the current maximum, and the
+         * new value is less than the current maximum, we don't
+         * have to re-scan for the new maximum */
+        olddq = *p->dq;
+        *p->dq = newdq;
+        /* However, if the item was the maximum on the opposite side, we'd better
+         * re-scan it */
+        if (comm_from->maxdq == p->opposite) {
+            if (olddq > newdq) {
+                /* Decreased the maximum on the other side, we have to re-scan for the
+                 * new maximum */
+                igraph_i_fastgreedy_community_rescan_max(comm_from);
+                j = igraph_i_fastgreedy_community_list_find_in_heap(list, from);
+                igraph_i_fastgreedy_community_list_sift_down(list, j);
+            } else {
+                /* Increased the maximum on the other side, we don't have to re-scan
+                 * but we might have to sift up */
+                j = igraph_i_fastgreedy_community_list_find_in_heap(list, from);
+                igraph_i_fastgreedy_community_list_sift_up(list, j);
+            }
+        }
+        return 0;
+    } else {
+        /* We got here in two cases:
+         (1) the pair we are modifying right now is the maximum in the given
+             community and we are decreasing it
+         (2) the pair we are modifying right now is NOT the maximum in the
+             given community, but we increase it so much that it will become
+             the new maximum
+         */
+        *p->dq = newdq;
+        if (comm_to->maxdq != p) {
+            /* case (2) */
+            comm_to->maxdq = p;
+            /* The maximum was increased, so perform a sift-up in the heap */
+            i = igraph_i_fastgreedy_community_list_find_in_heap(list, to);
+            igraph_i_fastgreedy_community_list_sift_up(list, i);
+            /* Opposite side. Chances are that the new value became the maximum
+             * in the opposite side, but check it first */
+            if (comm_from->maxdq != p->opposite) {
+                if (*comm_from->maxdq->dq < newdq) {
+                    /* Yes, it will become the new maximum */
+                    comm_from->maxdq = p->opposite;
+                    j = igraph_i_fastgreedy_community_list_find_in_heap(list, from);
+                    igraph_i_fastgreedy_community_list_sift_up(list, j);
+                } else {
+                    /* No, nothing to do there */
+                }
+            } else {
+                /* Already increased the maximum on the opposite side, so sift it up */
+                j = igraph_i_fastgreedy_community_list_find_in_heap(list, from);
+                igraph_i_fastgreedy_community_list_sift_up(list, j);
+            }
+        } else {
+            /* case (1) */
+            /* This is the worst, we have to re-scan the whole community to find
+             * the new maximum and update the global maximum as well if necessary */
+            igraph_i_fastgreedy_community_rescan_max(comm_to);
+            /* The maximum was decreased, so perform a sift-down in the heap */
+            i = igraph_i_fastgreedy_community_list_find_in_heap(list, to);
+            igraph_i_fastgreedy_community_list_sift_down(list, i);
+            if (comm_from->maxdq != p->opposite) {
+                /* The one that we decreased on the opposite side is not the
+                 * maximal one. Nothing to do. */
+            } else {
+                /* We decreased the maximal on the opposite side as well. Re-scan
+                 * and sift down */
+                igraph_i_fastgreedy_community_rescan_max(comm_from);
+                j = igraph_i_fastgreedy_community_list_find_in_heap(list, from);
+                igraph_i_fastgreedy_community_list_sift_down(list, j);
+            }
+        }
+    }
+    return 1;
+}
+
+/**
+ * \function igraph_community_fastgreedy
+ * \brief Finding community structure by greedy optimization of modularity
+ *
+ * This function implements the fast greedy modularity optimization
+ * algorithm for finding community structure, see
+ * A Clauset, MEJ Newman, C Moore: Finding community structure in very
+ * large networks, http://www.arxiv.org/abs/cond-mat/0408187 for the
+ * details.
+ *
+ * </para><para>
+ * Some improvements proposed in K Wakita, T Tsurumi: Finding community
+ * structure in mega-scale social networks,
+ * http://www.arxiv.org/abs/cs.CY/0702048v1 have also been implemented.
+ *
+ * \param graph The input graph. It must be a graph without multiple edges.
+ *    This is checked and an error message is given for graphs with multiple
+ *    edges.
+ * \param weights Potentially a numeric vector containing edge
+ *    weights. Supply a null pointer here for unweighted graphs. The
+ *    weights are expected to be non-negative.
+ * \param merges Pointer to an initialized matrix or NULL, the result of the
+ *    computation is stored here. The matrix has two columns and each
+ *    merge corresponds to one merge, the ids of the two merged
+ *    components are stored. The component ids are numbered from zero and
+ *    the first \c n components are the individual vertices, \c n is
+ *    the number of vertices in the graph. Component \c n is created
+ *    in the first merge, component \c n+1 in the second merge, etc.
+ *    The matrix will be resized as needed. If this argument is NULL
+ *    then it is ignored completely.
+ * \param modularity Pointer to an initialized vector or NULL pointer,
+ *    in the former case the modularity scores along the stages of the
+ *    computation are recorded here. The vector will be resized as
+ *    needed.
+ * \param membership Pointer to a vector. If not a null pointer, then
+ *    the membership vector corresponding to the best split (in terms
+ *    of modularity) is stored here.
+ * \return Error code.
+ *
+ * \sa \ref igraph_community_walktrap(), \ref
+ * igraph_community_edge_betweenness() for other community detection
+ * algorithms, \ref igraph_community_to_membership() to convert the
+ * dendrogram to a membership vector.
+ *
+ * Time complexity: O(|E||V|log|V|) in the worst case,
+ * O(|E|+|V|log^2|V|) typically, |V| is the number of vertices, |E| is
+ * the number of edges.
+ *
+ * \example examples/simple/igraph_community_fastgreedy.c
+ */
+int igraph_community_fastgreedy(const igraph_t *graph,
+                                const igraph_vector_t *weights,
+                                igraph_matrix_t *merges,
+                                igraph_vector_t *modularity,
+                                igraph_vector_t *membership) {
+    long int no_of_edges, no_of_nodes, no_of_joins, total_joins;
+    long int i, j, k, n, m, from, to, dummy, best_no_of_joins;
+    igraph_integer_t ffrom, fto;
+    igraph_eit_t edgeit;
+    igraph_i_fastgreedy_commpair *pairs, *p1, *p2;
+    igraph_i_fastgreedy_community_list communities;
+    igraph_vector_t a;
+    igraph_real_t q, *dq, bestq, weight_sum, loop_weight_sum;
+    igraph_bool_t has_multiple;
+    igraph_matrix_t merges_local;
+
+    /*long int join_order[] = { 16,5, 5,6, 6,0, 4,0, 10,0, 26,29, 29,33, 23,33, 27,33, 25,24, 24,31, 12,3, 21,1, 30,8, 8,32, 9,2, 17,1, 11,0, 7,3, 3,2, 13,2, 1,2, 28,31, 31,33, 22,32, 18,32, 20,32, 32,33, 15,33, 14,33, 0,19, 19,2, -1,-1 };*/
+    /*long int join_order[] = { 43,42, 42,41, 44,41, 41,36, 35,36, 37,36, 36,29, 38,29, 34,29, 39,29, 33,29, 40,29, 32,29, 14,29, 30,29, 31,29, 6,18, 18,4, 23,4, 21,4, 19,4, 27,4, 20,4, 22,4, 26,4, 25,4, 24,4, 17,4, 0,13, 13,2, 1,2, 11,2, 8,2, 5,2, 3,2, 10,2, 9,2, 7,2, 2,28, 28,15, 12,15, 29,16, 4,15, -1,-1 };*/
+
+    no_of_nodes = igraph_vcount(graph);
+    no_of_edges = igraph_ecount(graph);
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("fast greedy community detection works for undirected graphs only", IGRAPH_UNIMPLEMENTED);
+    }
+
+    total_joins = no_of_nodes - 1;
+
+    if (weights != 0) {
+        if (igraph_vector_size(weights) < igraph_ecount(graph)) {
+            IGRAPH_ERROR("fast greedy community detection: weight vector too short", IGRAPH_EINVAL);
+        }
+        if (igraph_vector_any_smaller(weights, 0)) {
+            IGRAPH_ERROR("weights must be positive", IGRAPH_EINVAL);
+        }
+        weight_sum = igraph_vector_sum(weights);
+    } else {
+        weight_sum = no_of_edges;
+    }
+
+    IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple));
+    if (has_multiple) {
+        IGRAPH_ERROR("fast-greedy community finding works only on graphs without multiple edges", IGRAPH_EINVAL);
+    }
+
+    if (membership != 0 && merges == 0) {
+        /* We need the merge matrix because the user wants the membership
+         * vector, so we allocate one on our own */
+        IGRAPH_CHECK(igraph_matrix_init(&merges_local, total_joins, 2));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &merges_local);
+        merges = &merges_local;
+    }
+
+    if (merges != 0) {
+        IGRAPH_CHECK(igraph_matrix_resize(merges, total_joins, 2));
+        igraph_matrix_null(merges);
+    }
+
+    if (modularity != 0) {
+        IGRAPH_CHECK(igraph_vector_resize(modularity, total_joins + 1));
+    }
+
+    /* Create degree vector */
+    IGRAPH_VECTOR_INIT_FINALLY(&a, no_of_nodes);
+    if (weights) {
+        debug("Calculating weighted degrees\n");
+        for (i = 0; i < no_of_edges; i++) {
+            VECTOR(a)[(long int)IGRAPH_FROM(graph, i)] += VECTOR(*weights)[i];
+            VECTOR(a)[(long int)IGRAPH_TO(graph, i)] += VECTOR(*weights)[i];
+        }
+    } else {
+        debug("Calculating degrees\n");
+        IGRAPH_CHECK(igraph_degree(graph, &a, igraph_vss_all(), IGRAPH_ALL, 1));
+    }
+
+    /* Create list of communities */
+    debug("Creating community list\n");
+    communities.n = no_of_nodes;
+    communities.no_of_communities = no_of_nodes;
+    communities.e = (igraph_i_fastgreedy_community*)calloc((size_t) no_of_nodes, sizeof(igraph_i_fastgreedy_community));
+    if (communities.e == 0) {
+        IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, communities.e);
+    communities.heap = (igraph_i_fastgreedy_community**)calloc((size_t) no_of_nodes, sizeof(igraph_i_fastgreedy_community*));
+    if (communities.heap == 0) {
+        IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, communities.heap);
+    communities.heapindex = (igraph_integer_t*)calloc((size_t)no_of_nodes, sizeof(igraph_integer_t));
+    if (communities.heapindex == 0) {
+        IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY_CLEAN(2);
+    IGRAPH_FINALLY(igraph_i_fastgreedy_community_list_destroy, &communities);
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_vector_ptr_init(&communities.e[i].neis, 0);
+        communities.e[i].id = (igraph_integer_t) i;
+        communities.e[i].size = 1;
+    }
+
+    /* Create list of community pairs from edges */
+    debug("Allocating dq vector\n");
+    dq = (igraph_real_t*)calloc((size_t) no_of_edges, sizeof(igraph_real_t));
+    if (dq == 0) {
+        IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, dq);
+    debug("Creating community pair list\n");
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &edgeit);
+    pairs = (igraph_i_fastgreedy_commpair*)calloc(2 * (size_t) no_of_edges, sizeof(igraph_i_fastgreedy_commpair));
+    if (pairs == 0) {
+        IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, pairs);
+    loop_weight_sum = 0;
+    for (i = 0, j = 0; !IGRAPH_EIT_END(edgeit); i += 2, j++, IGRAPH_EIT_NEXT(edgeit)) {
+        long int eidx = IGRAPH_EIT_GET(edgeit);
+        igraph_edge(graph, (igraph_integer_t) eidx, &ffrom, &fto);
+
+        /* Create the pairs themselves */
+        from = (long int)ffrom; to = (long int)fto;
+        if (from == to) {
+            loop_weight_sum += weights ? 2 * VECTOR(*weights)[eidx] : 2;
+            continue;
+        }
+
+        if (from > to) {
+            dummy = from; from = to; to = dummy;
+        }
+        if (weights) {
+            dq[j] = 2 * (VECTOR(*weights)[eidx] / (weight_sum * 2.0) - VECTOR(a)[from] * VECTOR(a)[to] / (4.0 * weight_sum * weight_sum));
+        } else {
+            dq[j] = 2 * (1.0 / (no_of_edges * 2.0) - VECTOR(a)[from] * VECTOR(a)[to] / (4.0 * no_of_edges * no_of_edges));
+        }
+        pairs[i].first = from;
+        pairs[i].second = to;
+        pairs[i].dq = &dq[j];
+        pairs[i].opposite = &pairs[i + 1];
+        pairs[i + 1].first = to;
+        pairs[i + 1].second = from;
+        pairs[i + 1].dq = pairs[i].dq;
+        pairs[i + 1].opposite = &pairs[i];
+        /* Link the pair to the communities */
+        igraph_vector_ptr_push_back(&communities.e[from].neis, &pairs[i]);
+        igraph_vector_ptr_push_back(&communities.e[to].neis, &pairs[i + 1]);
+        /* Update maximums */
+        if (communities.e[from].maxdq == 0 || *communities.e[from].maxdq->dq < *pairs[i].dq) {
+            communities.e[from].maxdq = &pairs[i];
+        }
+        if (communities.e[to].maxdq == 0 || *communities.e[to].maxdq->dq < *pairs[i + 1].dq) {
+            communities.e[to].maxdq = &pairs[i + 1];
+        }
+    }
+    igraph_eit_destroy(&edgeit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Sorting community neighbor lists by community IDs */
+    debug("Sorting community neighbor lists\n");
+    for (i = 0, j = 0; i < no_of_nodes; i++) {
+        igraph_i_fastgreedy_community_sort_neighbors_of(&communities, i, 0);
+        /* Isolated vertices and vertices with loop edges only won't be stored in
+         * the heap (to avoid maxdq == 0) */
+        if (communities.e[i].maxdq != 0) {
+            communities.heap[j] = &communities.e[i];
+            communities.heapindex[i] = (igraph_integer_t) j;
+            j++;
+        } else {
+            communities.heapindex[i] = -1;
+        }
+    }
+    communities.no_of_communities = j;
+
+    /* Calculate proper vector a (see paper) and initial modularity */
+    q = 2.0 * (weights ? weight_sum : no_of_edges);
+    if (q == 0) {
+        /* All the weights are zero */
+    } else {
+        igraph_vector_scale(&a, 1.0 / q);
+        q = loop_weight_sum / q;
+        for (i = 0; i < no_of_nodes; i++) {
+            q -= VECTOR(a)[i] * VECTOR(a)[i];
+        }
+    }
+
+    /* Initialize "best modularity" value and best merge counter */
+    bestq = q;
+    best_no_of_joins = 0;
+
+    /* Initializing community heap */
+    debug("Initializing community heap\n");
+    igraph_i_fastgreedy_community_list_build_heap(&communities);
+
+    debug("Initial modularity: %.4f\n", q);
+
+    /* Let's rock ;) */
+    no_of_joins = 0;
+    while (no_of_joins < total_joins) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        IGRAPH_PROGRESS("fast greedy community detection", no_of_joins * 100.0 / total_joins, 0);
+
+        /* Store the modularity */
+        if (modularity) {
+            VECTOR(*modularity)[no_of_joins] = q;
+        }
+
+        /* Update best modularity if needed */
+        if (q >= bestq) {
+            bestq = q;
+            best_no_of_joins = no_of_joins;
+        }
+
+        /* Some debug info if needed */
+        /* igraph_i_fastgreedy_community_list_check_heap(&communities); */
+#ifdef DEBUG
+        debug("===========================================\n");
+        for (i = 0; i < communities.n; i++) {
+            if (communities.e[i].maxdq == 0) {
+                debug("Community #%ld: PASSIVE\n", i);
+                continue;
+            }
+            debug("Community #%ld\n ", i);
+            for (j = 0; j < igraph_vector_ptr_size(&communities.e[i].neis); j++) {
+                p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[i].neis)[j];
+                debug(" (%ld,%ld,%.4f)", p1->first, p1->second, *p1->dq);
+            }
+            p1 = communities.e[i].maxdq;
+            debug("\n  Maxdq: (%ld,%ld,%.4f)\n", p1->first, p1->second, *p1->dq);
+        }
+        debug("Global maxdq is: (%ld,%ld,%.4f)\n", communities.heap[0]->maxdq->first,
+              communities.heap[0]->maxdq->second, *communities.heap[0]->maxdq->dq);
+        for (i = 0; i < communities.no_of_communities; i++) {
+            debug("(%ld,%ld,%.4f) ", communities.heap[i]->maxdq->first, communities.heap[i]->maxdq->second, *communities.heap[0]->maxdq->dq);
+        }
+        debug("\n");
+#endif
+        if (communities.heap[0] == 0) {
+            break;    /* no more communities */
+        }
+        if (communities.heap[0]->maxdq == 0) {
+            break;    /* there are only isolated comms */
+        }
+        to = communities.heap[0]->maxdq->second;
+        from = communities.heap[0]->maxdq->first;
+
+        debug("Q[%ld] = %.7f\tdQ = %.7f\t |H| = %ld\n",
+              no_of_joins, q, *communities.heap[0]->maxdq->dq, no_of_nodes - no_of_joins - 1);
+
+        /* DEBUG */
+        /* from=join_order[no_of_joins*2]; to=join_order[no_of_joins*2+1];
+        if (to == -1) break;
+        for (i=0; i<igraph_vector_ptr_size(&communities.e[to].neis); i++) {
+          p1=(igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i];
+          if (p1->second == from) communities.maxdq = p1;
+        } */
+
+        n = igraph_vector_ptr_size(&communities.e[to].neis);
+        m = igraph_vector_ptr_size(&communities.e[from].neis);
+        /*if (n>m) {
+          dummy=n; n=m; m=dummy;
+          dummy=to; to=from; from=dummy;
+        }*/
+        debug("  joining: %ld <- %ld\n", to, from);
+        q += *communities.heap[0]->maxdq->dq;
+
+        /* Merge the second community into the first */
+        i = j = 0;
+        while (i < n && j < m) {
+            p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i];
+            p2 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[from].neis)[j];
+            debug("Pairs: %ld-%ld and %ld-%ld\n", p1->first, p1->second,
+                  p2->first, p2->second);
+            if (p1->second < p2->second) {
+                /* Considering p1 from now on */
+                debug("    Considering: %ld-%ld\n", p1->first, p1->second);
+                if (p1->second == from) {
+                    debug("    WILL REMOVE: %ld-%ld\n", to, from);
+                } else {
+                    /* chain, case 1 */
+                    debug("    CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n",
+                          to, p1->second, from, *p1->dq, -2 * VECTOR(a)[from]*VECTOR(a)[p1->second], p1->first, p1->second, *p1->dq - 2 * VECTOR(a)[from]*VECTOR(a)[p1->second]);
+                    igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq - 2 * VECTOR(a)[from]*VECTOR(a)[p1->second]);
+                }
+                i++;
+            } else if (p1->second == p2->second) {
+                /* p1->first, p1->second and p2->first form a triangle */
+                debug("    Considering: %ld-%ld and %ld-%ld\n", p1->first, p1->second,
+                      p2->first, p2->second);
+                /* Update dq value */
+                debug("    TRIANGLE: %ld-%ld-%ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n",
+                      to, p1->second, from, *p1->dq, *p2->dq, p1->first, p1->second, *p1->dq + *p2->dq);
+                igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq + *p2->dq);
+                igraph_i_fastgreedy_community_remove_nei(&communities, p1->second, from);
+                i++;
+                j++;
+            } else {
+                debug("    Considering: %ld-%ld\n", p2->first, p2->second);
+                if (p2->second == to) {
+                    debug("    WILL REMOVE: %ld-%ld\n", p2->second, p2->first);
+                } else {
+                    /* chain, case 2 */
+                    debug("    CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n",
+                          to, p2->second, from, to, p2->second, *p2->dq - 2 * VECTOR(a)[to]*VECTOR(a)[p2->second]);
+                    p2->opposite->second = to;
+                    /* p2->opposite->second changed, so it means that
+                     * communities.e[p2->second].neis (which contains p2->opposite) is
+                     * not sorted any more. We have to find the index of p2->opposite in
+                     * this vector and move it to the correct place. Moving should be an
+                     * O(n) operation; re-sorting would be O(n*logn) or even worse,
+                     * depending on the pivoting strategy used by qsort() since the
+                     * vector is nearly sorted */
+                    igraph_i_fastgreedy_community_sort_neighbors_of(
+                        &communities, p2->second, p2->opposite);
+                    /* link from.neis[j] to the current place in to.neis if
+                     * from.neis[j] != to */
+                    p2->first = to;
+                    IGRAPH_CHECK(igraph_vector_ptr_insert(&communities.e[to].neis, i, p2));
+                    n++; i++;
+                    if (*p2->dq > *communities.e[to].maxdq->dq) {
+                        communities.e[to].maxdq = p2;
+                        k = igraph_i_fastgreedy_community_list_find_in_heap(&communities, to);
+                        igraph_i_fastgreedy_community_list_sift_up(&communities, k);
+                    }
+                    igraph_i_fastgreedy_community_update_dq(&communities, p2, *p2->dq - 2 * VECTOR(a)[to]*VECTOR(a)[p2->second]);
+                }
+                j++;
+            }
+        }
+
+        while (i < n) {
+            p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i];
+            if (p1->second == from) {
+                debug("    WILL REMOVE: %ld-%ld\n", p1->first, from);
+            } else {
+                /* chain, case 1 */
+                debug("    CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n",
+                      to, p1->second, from, *p1->dq, -2 * VECTOR(a)[from]*VECTOR(a)[p1->second], p1->first, p1->second, *p1->dq - 2 * VECTOR(a)[from]*VECTOR(a)[p1->second]);
+                igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq - 2 * VECTOR(a)[from]*VECTOR(a)[p1->second]);
+            }
+            i++;
+        }
+        while (j < m) {
+            p2 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[from].neis)[j];
+            if (to == p2->second) {
+                j++;
+                continue;
+            }
+            /* chain, case 2 */
+            debug("    CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n",
+                  to, p2->second, from, p1->first, p2->second, *p2->dq - 2 * VECTOR(a)[to]*VECTOR(a)[p2->second]);
+            p2->opposite->second = to;
+            /* need to re-sort community nei list `p2->second` */
+            igraph_i_fastgreedy_community_sort_neighbors_of(&communities, p2->second, p2->opposite);
+            /* link from.neis[j] to the current place in to.neis if
+             * from.neis[j] != to */
+            p2->first = to;
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(&communities.e[to].neis, p2));
+            if (*p2->dq > *communities.e[to].maxdq->dq) {
+                communities.e[to].maxdq = p2;
+                k = igraph_i_fastgreedy_community_list_find_in_heap(&communities, to);
+                igraph_i_fastgreedy_community_list_sift_up(&communities, k);
+            }
+            igraph_i_fastgreedy_community_update_dq(&communities, p2, *p2->dq - 2 * VECTOR(a)[to]*VECTOR(a)[p2->second]);
+            j++;
+        }
+
+        /* Now, remove community `from` from the neighbors of community `to` */
+        if (communities.no_of_communities > 2) {
+            debug("    REMOVING: %ld-%ld\n", to, from);
+            igraph_i_fastgreedy_community_remove_nei(&communities, to, from);
+            i = igraph_i_fastgreedy_community_list_find_in_heap(&communities, from);
+            igraph_i_fastgreedy_community_list_remove(&communities, i);
+        }
+        communities.e[from].maxdq = 0;
+
+        /* Update community sizes */
+        communities.e[to].size += communities.e[from].size;
+        communities.e[from].size = 0;
+
+        /* record what has been merged */
+        /* igraph_vector_ptr_clear is not enough here as it won't free
+         * the memory consumed by communities.e[from].neis. Thanks
+         * to Tom Gregorovic for pointing that out. */
+        igraph_vector_ptr_destroy(&communities.e[from].neis);
+        if (merges) {
+            MATRIX(*merges, no_of_joins, 0) = communities.e[to].id;
+            MATRIX(*merges, no_of_joins, 1) = communities.e[from].id;
+            communities.e[to].id = (igraph_integer_t) (no_of_nodes + no_of_joins);
+        }
+
+        /* Update vector a */
+        VECTOR(a)[to] += VECTOR(a)[from];
+        VECTOR(a)[from] = 0.0;
+
+        no_of_joins++;
+    }
+    /* TODO: continue merging when some isolated communities remained. Always
+     * joining the communities with the least number of nodes results in the
+     * smallest decrease in modularity every step. Now we're simply deleting
+     * the excess rows from the merge matrix */
+    if (no_of_joins < total_joins) {
+        long int *ivec;
+        ivec = igraph_Calloc(igraph_matrix_nrow(merges), long int);
+        if (ivec == 0) {
+            IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(free, ivec);
+        for (i = 0; i < no_of_joins; i++) {
+            ivec[i] = i + 1;
+        }
+        igraph_matrix_permdelete_rows(merges, ivec, total_joins - no_of_joins);
+        free(ivec);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    IGRAPH_PROGRESS("fast greedy community detection", 100.0, 0);
+
+    if (modularity) {
+        VECTOR(*modularity)[no_of_joins] = q;
+        igraph_vector_resize(modularity, no_of_joins + 1);
+    }
+
+    debug("Freeing memory\n");
+    free(pairs);
+    free(dq);
+    igraph_i_fastgreedy_community_list_destroy(&communities);
+    igraph_vector_destroy(&a);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    if (membership) {
+        IGRAPH_CHECK(igraph_community_to_membership(merges,
+                     (igraph_integer_t) no_of_nodes,
+                     /*steps=*/ (igraph_integer_t) best_no_of_joins,
+                     membership,
+                     /*csize=*/ 0));
+    }
+
+    if (merges == &merges_local) {
+        igraph_matrix_destroy(&merges_local);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+#ifdef IGRAPH_FASTCOMM_DEBUG
+    #undef IGRAPH_FASTCOMM_DEBUG
+#endif
+
+
diff --git a/igraph/src/feedback_arc_set.c b/igraph/src/feedback_arc_set.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/feedback_arc_set.c
@@ -0,0 +1,665 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_centrality.h"
+#include "igraph_components.h"
+#include "igraph_constants.h"
+#include "igraph_datatype.h"
+#include "igraph_dqueue.h"
+#include "igraph_error.h"
+#include "igraph_glpk_support.h"
+#include "igraph_interface.h"
+#include "igraph_memory.h"
+#include "igraph_structural.h"
+#include "igraph_types.h"
+#include "igraph_visitor.h"
+
+int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result,
+                                 const igraph_vector_t *weights);
+
+
+/**
+ * \ingroup structural
+ * \function igraph_feedback_arc_set
+ * \brief Calculates a feedback arc set of the graph using different
+ *        algorithms.
+ *
+ * </para><para>
+ * A feedback arc set is a set of edges whose removal makes the graph acyclic.
+ * We are usually interested in \em minimum feedback arc sets, i.e. sets of edges
+ * whose total weight is minimal among all the feedback arc sets.
+ *
+ * </para><para>
+ * For undirected graphs, the problem is simple: one has to find a maximum weight
+ * spanning tree and then remove all the edges not in the spanning tree. For directed
+ * graphs, this is an NP-hard problem, and various heuristics are usually used to
+ * find an approximate solution to the problem. This function implements a few of
+ * these heuristics.
+ *
+ * \param graph  The graph object.
+ * \param result An initialized vector, the result will be returned here.
+ * \param weights Weight vector or NULL if no weights are specified.
+ * \param algo   The algorithm to use to solve the problem if the graph is directed.
+ *        Possible values:
+ *        \clist
+ *        \cli IGRAPH_FAS_EXACT_IP
+ *          Finds a \em minimum feedback arc set using integer programming (IP).
+ *          The complexity of this algorithm is exponential of course.
+ *        \cli IGRAPH_FAS_APPROX_EADES
+ *          Finds a feedback arc set using the heuristic of Eades, Lin and
+ *          Smyth (1993). This is guaranteed to be smaller than |E|/2 - |V|/6,
+ *          and it is linear in the number of edges (i.e. O(|E|)).
+ *          For more details, see Eades P, Lin X and Smyth WF: A fast and effective
+ *          heuristic for the feedback arc set problem. In: Proc Inf Process Lett
+ *          319-323, 1993.
+ *        \endclist
+ *
+ * \return Error code:
+ *         \c IGRAPH_EINVAL if an unknown method was specified or the weight vector
+ *            is invalid.
+ *
+ * \example examples/simple/igraph_feedback_arc_set.c
+ * \example examples/simple/igraph_feedback_arc_set_ip.c
+ *
+ * Time complexity: depends on \p algo, see the time complexities there.
+ */
+int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result,
+                            const igraph_vector_t *weights, igraph_fas_algorithm_t algo) {
+
+    if (weights && igraph_vector_size(weights) < igraph_ecount(graph))
+        IGRAPH_ERROR("cannot calculate feedback arc set, weight vector too short",
+                     IGRAPH_EINVAL);
+
+    if (!igraph_is_directed(graph)) {
+        return igraph_i_feedback_arc_set_undirected(graph, result, weights, 0);
+    }
+
+    switch (algo) {
+    case IGRAPH_FAS_EXACT_IP:
+        return igraph_i_feedback_arc_set_ip(graph, result, weights);
+
+    case IGRAPH_FAS_APPROX_EADES:
+        return igraph_i_feedback_arc_set_eades(graph, result, weights, 0);
+
+    default:
+        IGRAPH_ERROR("Invalid algorithm", IGRAPH_EINVAL);
+    }
+}
+
+/**
+ * Solves the feedback arc set problem for undirected graphs.
+ */
+int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t *result,
+        const igraph_vector_t *weights, igraph_vector_t *layering) {
+    igraph_vector_t edges;
+    long int i, j, n, no_of_nodes = igraph_vcount(graph);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_nodes - 1);
+    if (weights) {
+        /* Find a maximum weight spanning tree. igraph has a routine for minimum
+         * spanning trees, so we negate the weights */
+        igraph_vector_t vcopy;
+        IGRAPH_CHECK(igraph_vector_copy(&vcopy, weights));
+        IGRAPH_FINALLY(igraph_vector_destroy, &vcopy);
+        igraph_vector_scale(&vcopy, -1);
+        IGRAPH_CHECK(igraph_minimum_spanning_tree(graph, &edges, &vcopy));
+        igraph_vector_destroy(&vcopy);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        /* Any spanning tree will do */
+        IGRAPH_CHECK(igraph_minimum_spanning_tree(graph, &edges, 0));
+    }
+
+    /* Now we have a bunch of edges that constitute a spanning forest. We have
+     * to come up with a layering, and return those edges that are not in the
+     * spanning forest */
+    igraph_vector_sort(&edges);
+    IGRAPH_CHECK(igraph_vector_push_back(&edges, -1));  /* guard element */
+
+    if (result != 0) {
+        igraph_vector_clear(result);
+        n = igraph_ecount(graph);
+        for (i = 0, j = 0; i < n; i++) {
+            if (i == VECTOR(edges)[j]) {
+                j++;
+                continue;
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(result, i));
+        }
+    }
+
+    if (layering != 0) {
+        igraph_vector_t degrees;
+        igraph_vector_t roots;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&degrees, no_of_nodes);
+        IGRAPH_VECTOR_INIT_FINALLY(&roots, no_of_nodes);
+
+        IGRAPH_CHECK(igraph_strength(graph, &degrees, igraph_vss_all(),
+                                     IGRAPH_ALL, 0, weights));
+        IGRAPH_CHECK((int) igraph_vector_qsort_ind(&degrees, &roots,
+                     /* descending = */ 1));
+        IGRAPH_CHECK(igraph_bfs(graph,
+                                /* root = */ 0,
+                                /* roots = */ &roots,
+                                /* mode = */ IGRAPH_OUT,
+                                /* unreachable = */ 0,
+                                /* restricted = */ 0,
+                                /* order = */ 0,
+                                /* rank = */ 0,
+                                /* father = */ 0,
+                                /* pred = */ 0,
+                                /* succ = */ 0,
+                                /* dist = */ layering,
+                                /* callback = */ 0,
+                                /* extra = */ 0));
+
+        igraph_vector_destroy(&degrees);
+        igraph_vector_destroy(&roots);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * Solves the feedback arc set problem using the heuristics of Eades et al.
+ */
+int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *result,
+                                    const igraph_vector_t *weights, igraph_vector_t *layers) {
+    long int i, j, k, v, eid, no_of_nodes = igraph_vcount(graph), nodes_left;
+    igraph_dqueue_t sources, sinks;
+    igraph_vector_t neis;
+    igraph_vector_t indegrees, outdegrees;
+    igraph_vector_t instrengths, outstrengths;
+    long int* ordering;
+    long int order_next_pos = 0, order_next_neg = -1;
+    igraph_real_t diff, maxdiff;
+
+    ordering = igraph_Calloc(no_of_nodes, long int);
+    IGRAPH_FINALLY(igraph_free, ordering);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&indegrees, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&outdegrees, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&instrengths, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&outstrengths, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_dqueue_init(&sources, 0));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &sources);
+    IGRAPH_CHECK(igraph_dqueue_init(&sinks, 0));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &sinks);
+
+    IGRAPH_CHECK(igraph_degree(graph, &indegrees, igraph_vss_all(), IGRAPH_IN, 0));
+    IGRAPH_CHECK(igraph_degree(graph, &outdegrees, igraph_vss_all(), IGRAPH_OUT, 0));
+
+    if (weights) {
+        IGRAPH_CHECK(igraph_strength(graph, &instrengths, igraph_vss_all(), IGRAPH_IN, 0, weights));
+        IGRAPH_CHECK(igraph_strength(graph, &outstrengths, igraph_vss_all(), IGRAPH_OUT, 0, weights));
+    } else {
+        IGRAPH_CHECK(igraph_vector_update(&instrengths, &indegrees));
+        IGRAPH_CHECK(igraph_vector_update(&outstrengths, &outdegrees));
+    }
+
+    /* Find initial sources and sinks */
+    nodes_left = no_of_nodes;
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(indegrees)[i] == 0) {
+            if (VECTOR(outdegrees)[i] == 0) {
+                /* Isolated vertex, we simply ignore it */
+                nodes_left--;
+                ordering[i] = order_next_pos++;
+                VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1;
+            } else {
+                /* This is a source */
+                igraph_dqueue_push(&sources, i);
+            }
+        } else if (VECTOR(outdegrees)[i] == 0) {
+            /* This is a sink */
+            igraph_dqueue_push(&sinks, i);
+        }
+    }
+
+    /* While we have any nodes left... */
+    while (nodes_left > 0) {
+        /* (1) Remove the sources one by one */
+        while (!igraph_dqueue_empty(&sources)) {
+            i = (long)igraph_dqueue_pop(&sources);
+            /* Add the node to the ordering */
+            ordering[i] = order_next_pos++;
+            /* Exclude the node from further searches */
+            VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1;
+            /* Get the neighbors and decrease their degrees */
+            IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i,
+                                         IGRAPH_OUT));
+            j = igraph_vector_size(&neis);
+            for (i = 0; i < j; i++) {
+                eid = (long int) VECTOR(neis)[i];
+                k = IGRAPH_TO(graph, eid);
+                if (VECTOR(indegrees)[k] <= 0) {
+                    /* Already removed, continue */
+                    continue;
+                }
+                VECTOR(indegrees)[k]--;
+                VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0);
+                if (VECTOR(indegrees)[k] == 0) {
+                    IGRAPH_CHECK(igraph_dqueue_push(&sources, k));
+                }
+            }
+            nodes_left--;
+        }
+
+        /* (2) Remove the sinks one by one */
+        while (!igraph_dqueue_empty(&sinks)) {
+            i = (long)igraph_dqueue_pop(&sinks);
+            /* Maybe the vertex became sink and source at the same time, hence it
+             * was already removed in the previous iteration. Check it. */
+            if (VECTOR(indegrees)[i] < 0) {
+                continue;
+            }
+            /* Add the node to the ordering */
+            ordering[i] = order_next_neg--;
+            /* Exclude the node from further searches */
+            VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1;
+            /* Get the neighbors and decrease their degrees */
+            IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i,
+                                         IGRAPH_IN));
+            j = igraph_vector_size(&neis);
+            for (i = 0; i < j; i++) {
+                eid = (long int) VECTOR(neis)[i];
+                k = IGRAPH_FROM(graph, eid);
+                if (VECTOR(outdegrees)[k] <= 0) {
+                    /* Already removed, continue */
+                    continue;
+                }
+                VECTOR(outdegrees)[k]--;
+                VECTOR(outstrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0);
+                if (VECTOR(outdegrees)[k] == 0) {
+                    IGRAPH_CHECK(igraph_dqueue_push(&sinks, k));
+                }
+            }
+            nodes_left--;
+        }
+
+        /* (3) No more sources or sinks. Find the node with the largest
+         * difference between its out-strength and in-strength */
+        v = -1; maxdiff = -IGRAPH_INFINITY;
+        for (i = 0; i < no_of_nodes; i++) {
+            if (VECTOR(outdegrees)[i] < 0) {
+                continue;
+            }
+            diff = VECTOR(outstrengths)[i] - VECTOR(instrengths)[i];
+            if (diff > maxdiff) {
+                maxdiff = diff;
+                v = i;
+            }
+        }
+        if (v >= 0) {
+            /* Remove vertex v */
+            ordering[v] = order_next_pos++;
+            /* Remove outgoing edges */
+            IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v,
+                                         IGRAPH_OUT));
+            j = igraph_vector_size(&neis);
+            for (i = 0; i < j; i++) {
+                eid = (long int) VECTOR(neis)[i];
+                k = IGRAPH_TO(graph, eid);
+                if (VECTOR(indegrees)[k] <= 0) {
+                    /* Already removed, continue */
+                    continue;
+                }
+                VECTOR(indegrees)[k]--;
+                VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0);
+                if (VECTOR(indegrees)[k] == 0) {
+                    IGRAPH_CHECK(igraph_dqueue_push(&sources, k));
+                }
+            }
+            /* Remove incoming edges */
+            IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v,
+                                         IGRAPH_IN));
+            j = igraph_vector_size(&neis);
+            for (i = 0; i < j; i++) {
+                eid = (long int) VECTOR(neis)[i];
+                k = IGRAPH_FROM(graph, eid);
+                if (VECTOR(outdegrees)[k] <= 0) {
+                    /* Already removed, continue */
+                    continue;
+                }
+                VECTOR(outdegrees)[k]--;
+                VECTOR(outstrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0);
+                if (VECTOR(outdegrees)[k] == 0 && VECTOR(indegrees)[k] > 0) {
+                    IGRAPH_CHECK(igraph_dqueue_push(&sinks, k));
+                }
+            }
+
+            VECTOR(outdegrees)[v] = -1;
+            VECTOR(indegrees)[v] = -1;
+            nodes_left--;
+        }
+    }
+
+    igraph_dqueue_destroy(&sinks);
+    igraph_dqueue_destroy(&sources);
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&outstrengths);
+    igraph_vector_destroy(&instrengths);
+    igraph_vector_destroy(&outdegrees);
+    igraph_vector_destroy(&indegrees);
+    IGRAPH_FINALLY_CLEAN(7);
+
+    /* Tidy up the ordering */
+    for (i = 0; i < no_of_nodes; i++) {
+        if (ordering[i] < 0) {
+            ordering[i] += no_of_nodes;
+        }
+    }
+
+    /* Find the feedback edges based on the ordering */
+    if (result != 0) {
+        igraph_vector_clear(result);
+        j = igraph_ecount(graph);
+        for (i = 0; i < j; i++) {
+            long int from = IGRAPH_FROM(graph, i), to = IGRAPH_TO(graph, i);
+            if (from == to || ordering[from] > ordering[to]) {
+                IGRAPH_CHECK(igraph_vector_push_back(result, i));
+            }
+        }
+    }
+
+    /* If we have also requested a layering, return that as well */
+    if (layers != 0) {
+        igraph_vector_t ranks;
+        igraph_vector_long_t order_vec;
+
+        IGRAPH_CHECK(igraph_vector_resize(layers, no_of_nodes));
+        igraph_vector_null(layers);
+
+        igraph_vector_long_view(&order_vec, ordering, no_of_nodes);
+
+        IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&ranks, 0);
+
+        IGRAPH_CHECK((int) igraph_vector_long_qsort_ind(&order_vec, &ranks, 0));
+
+        for (i = 0; i < no_of_nodes; i++) {
+            long int from = (long int) VECTOR(ranks)[i];
+            IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) from,
+                                          IGRAPH_OUT));
+            k = igraph_vector_size(&neis);
+            for (j = 0; j < k; j++) {
+                long int to = (long int) VECTOR(neis)[j];
+                if (from == to) {
+                    continue;
+                }
+                if (ordering[from] > ordering[to]) {
+                    continue;
+                }
+                if (VECTOR(*layers)[to] < VECTOR(*layers)[from] + 1) {
+                    VECTOR(*layers)[to] = VECTOR(*layers)[from] + 1;
+                }
+            }
+        }
+
+        igraph_vector_destroy(&neis);
+        igraph_vector_destroy(&ranks);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    /* Free the ordering vector */
+    igraph_free(ordering);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * Solves the feedback arc set problem using integer programming.
+ */
+int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result,
+                                 const igraph_vector_t *weights) {
+#ifndef HAVE_GLPK
+    IGRAPH_ERROR("GLPK is not available", IGRAPH_UNIMPLEMENTED);
+#else
+
+    igraph_integer_t no_of_components;
+    igraph_integer_t no_of_vertices = igraph_vcount(graph);
+    igraph_integer_t no_of_edges = igraph_ecount(graph);
+    igraph_vector_t membership, ordering, vertex_remapping;
+    igraph_vector_ptr_t vertices_by_components, edges_by_components;
+    long int i, j, k, l, m, n, from, to;
+    igraph_real_t weight;
+    glp_prob *ip;
+    glp_iocp parm;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&membership, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&ordering, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&vertex_remapping, no_of_vertices);
+
+    igraph_vector_clear(result);
+
+    /* Decompose the graph into connected components */
+    IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components,
+                                 IGRAPH_WEAK));
+
+    /* Construct vertex and edge lists for each of the components */
+    IGRAPH_CHECK(igraph_vector_ptr_init(&vertices_by_components, no_of_components));
+    IGRAPH_CHECK(igraph_vector_ptr_init(&edges_by_components, no_of_components));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vertices_by_components);
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &edges_by_components);
+    for (i = 0; i < no_of_components; i++) {
+        igraph_vector_t* vptr;
+        vptr = igraph_Calloc(1, igraph_vector_t);
+        if (vptr == 0) {
+            IGRAPH_ERROR("cannot calculate feedback arc set using IP", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(free, vptr);
+        IGRAPH_CHECK(igraph_vector_init(vptr, 0));
+        IGRAPH_FINALLY_CLEAN(1);
+        VECTOR(vertices_by_components)[i] = vptr;
+    }
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&vertices_by_components, igraph_vector_destroy);
+    for (i = 0; i < no_of_components; i++) {
+        igraph_vector_t* vptr;
+        vptr = igraph_Calloc(1, igraph_vector_t);
+        if (vptr == 0) {
+            IGRAPH_ERROR("cannot calculate feedback arc set using IP", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(free, vptr);
+        IGRAPH_CHECK(igraph_vector_init(vptr, 0));
+        IGRAPH_FINALLY_CLEAN(1);
+        VECTOR(edges_by_components)[i] = vptr;
+    }
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&edges_by_components, igraph_vector_destroy);
+    for (i = 0; i < no_of_vertices; i++) {
+        j = (long int) VECTOR(membership)[i];
+        IGRAPH_CHECK(igraph_vector_push_back(VECTOR(vertices_by_components)[j], i));
+    }
+    for (i = 0; i < no_of_edges; i++) {
+        j = (long int) VECTOR(membership)[(long)IGRAPH_FROM(graph, i)];
+        IGRAPH_CHECK(igraph_vector_push_back(VECTOR(edges_by_components)[j], i));
+    }
+
+#define VAR2IDX(i, j) (i*(n-1)+j-(i+1)*i/2)
+
+    /* Configure GLPK */
+    glp_term_out(GLP_OFF);
+    glp_init_iocp(&parm);
+    parm.br_tech = GLP_BR_DTH;
+    parm.bt_tech = GLP_BT_BLB;
+    parm.pp_tech = GLP_PP_ALL;
+    parm.presolve = GLP_ON;
+    parm.binarize = GLP_OFF;
+    parm.cb_func = igraph_i_glpk_interruption_hook;
+
+    /* Solve an IP for feedback arc sets in each of the components */
+    for (i = 0; i < no_of_components; i++) {
+        igraph_vector_t* vertices_in_comp = (igraph_vector_t*)VECTOR(vertices_by_components)[i];
+        igraph_vector_t* edges_in_comp = (igraph_vector_t*)VECTOR(edges_by_components)[i];
+
+        /*
+         * Let x_ij denote whether layer(i) < layer(j).
+         *
+         * The standard formulation of the problem is as follows:
+         *
+         * max sum_{i,j} w_ij x_ij
+         *
+         * subject to
+         *
+         * (1) x_ij + x_ji = 1   (i.e. either layer(i) < layer(j) or layer(i) > layer(j))
+         *     for all i < j
+         * (2) x_ij + x_jk + x_ki <= 2 for all i < j, i < k, j != k
+         *
+         * Note that x_ij = 1 implies that x_ji = 0 and vice versa; in other words,
+         * x_ij = 1 - x_ji. Thus, we can get rid of the (1) constraints and half of the
+         * x_ij variables (where j < i) if we rewrite constraints of type (2) as follows:
+         *
+         * (2a) x_ij + x_jk - x_ik <= 1 for all i < j, i < k, j < k
+         * (2b) x_ij - x_kj - x_ik <= 0 for all i < j, i < k, j > k
+         *
+         * The goal function then becomes:
+         *
+         * max sum_{i<j} (w_ij-w_ji) x_ij
+         */
+        n = igraph_vector_size(vertices_in_comp);
+        ip = glp_create_prob();
+        IGRAPH_FINALLY(glp_delete_prob, ip);
+        glp_set_obj_dir(ip, GLP_MAX);
+
+        /* Construct a mapping from vertex IDs to the [0; n-1] range */
+        for (j = 0; j < n; j++) {
+            VECTOR(vertex_remapping)[(long)VECTOR(*vertices_in_comp)[j]] = j;
+        }
+
+        /* Set up variables */
+        k = n * (n - 1) / 2;
+        if (k > 0) {
+            glp_add_cols(ip, (int) k);
+            for (j = 1; j <= k; j++) {
+                glp_set_col_kind(ip, (int) j, GLP_BV);
+            }
+        }
+
+        /* Set up coefficients in the goal function */
+        k = igraph_vector_size(edges_in_comp);
+        for (j = 0; j < k; j++) {
+            l = (long int) VECTOR(*edges_in_comp)[j];
+            from = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_FROM(graph, l)];
+            to = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_TO(graph, l)];
+            if (from == to) {
+                continue;
+            }
+
+            weight = weights ? VECTOR(*weights)[l] : 1;
+
+            if (from < to) {
+                l = VAR2IDX(from, to);
+                glp_set_obj_coef(ip, (int) l, glp_get_obj_coef(ip, (int) l) + weight);
+            } else {
+                l = VAR2IDX(to, from);
+                glp_set_obj_coef(ip, (int) l, glp_get_obj_coef(ip, (int) l) - weight);
+            }
+        }
+
+        /* Add constraints */
+        if (n > 1) {
+            glp_add_rows(ip, (int)(n * (n - 1) / 2 + n * (n - 1) * (n - 2) / 3));
+            m = 1;
+            for (j = 0; j < n; j++) {
+                int ind[4];
+                double val[4] = {0, 1, 1, -1};
+                for (k = j + 1; k < n; k++) {
+                    ind[1] = (int) VAR2IDX(j, k);
+                    /* Type (2a) */
+                    val[2] = 1;
+                    for (l = k + 1; l < n; l++, m++) {
+                        ind[2] = (int) VAR2IDX(k, l);
+                        ind[3] = (int) VAR2IDX(j, l);
+                        glp_set_row_bnds(ip, (int) m, GLP_UP, 1, 1);
+                        glp_set_mat_row(ip, (int) m, 3, ind, val);
+                    }
+                    /* Type (2b) */
+                    val[2] = -1;
+                    for (l = j + 1; l < k; l++, m++) {
+                        ind[2] = (int) VAR2IDX(l, k);
+                        ind[3] = (int) VAR2IDX(j, l);
+                        glp_set_row_bnds(ip, (int) m, GLP_UP, 0, 0);
+                        glp_set_mat_row(ip, (int) m, 3, ind, val);
+                    }
+                }
+            }
+        }
+
+        /* Solve the problem */
+        IGRAPH_GLPK_CHECK(glp_intopt(ip, &parm), "Feedback arc set using IP failed");
+
+        /* Find the ordering of the vertices */
+        IGRAPH_CHECK(igraph_vector_resize(&ordering, n));
+        igraph_vector_null(&ordering);
+        m = n * (n - 1) / 2;
+        j = 0; k = 1;
+        for (l = 1; l <= m; l++) {
+            /* variable l always corresponds to the (j, k) vertex pair */
+            /* printf("(%ld, %ld) = %g\n", i, j, glp_mip_col_val(ip, l)); */
+            if (glp_mip_col_val(ip, (int) l) > 0) {
+                /* j comes earlier in the ordering than k */
+                VECTOR(ordering)[j]++;
+            } else {
+                /* k comes earlier in the ordering than j */
+                VECTOR(ordering)[k]++;
+            }
+            k++;
+            if (k == n) {
+                j++; k = j + 1;
+            }
+        }
+
+        /* Find the feedback edges */
+        k = igraph_vector_size(edges_in_comp);
+        for (j = 0; j < k; j++) {
+            l = (long int) VECTOR(*edges_in_comp)[j];
+            from = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_FROM(graph, l)];
+            to = (long int) VECTOR(vertex_remapping)[(long)IGRAPH_TO(graph, l)];
+            if (from == to || VECTOR(ordering)[from] < VECTOR(ordering)[to]) {
+                IGRAPH_CHECK(igraph_vector_push_back(result, l));
+            }
+        }
+
+        /* Clean up */
+        glp_delete_prob(ip);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_ptr_destroy_all(&vertices_by_components);
+    igraph_vector_ptr_destroy_all(&edges_by_components);
+    igraph_vector_destroy(&vertex_remapping);
+    igraph_vector_destroy(&ordering);
+    igraph_vector_destroy(&membership);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return IGRAPH_SUCCESS;
+#endif
+}
+
diff --git a/igraph/src/flow.c b/igraph/src/flow.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/flow.c
@@ -0,0 +1,2532 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_flow.h"
+#include "igraph_error.h"
+#include "igraph_memory.h"
+#include "igraph_constants.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_conversion.h"
+#include "igraph_constructors.h"
+#include "igraph_progress.h"
+#include "igraph_structural.h"
+#include "igraph_components.h"
+#include "igraph_types_internal.h"
+#include "config.h"
+#include "igraph_math.h"
+#include "igraph_dqueue.h"
+#include "igraph_visitor.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_topology.h"
+
+#include <limits.h>
+#include <stdio.h>
+
+/*
+ * Some general remarks about the functions in this file.
+ *
+ * The following measures can be calculated:
+ * ( 1) s-t maximum flow value, directed graph
+ * ( 2) s-t maximum flow value, undirected graph
+ * ( 3) s-t maximum flow, directed graph
+ * ( 4) s-t maximum flow, undirected graph
+ * ( 5) s-t minimum cut value, directed graph
+ * ( 6) s-t minimum cut value, undirected graph
+ * ( 7) minimum cut value, directed graph
+ * ( 8) minimum cut value, undirected graph
+ * ( 9) s-t minimum cut, directed graph
+ * (10) s-t minimum cut, undirected graph
+ * (11) minimum cut, directed graph
+ * (12) minimum cut, undirected graph
+ * (13) s-t edge connectivity, directed graph
+ * (14) s-t edge connectivity, undirected graph
+ * (15) edge connectivity, directed graph
+ * (16) edge connectivity, undirected graph
+ * (17) s-t vertex connectivity, directed graph
+ * (18) s-t vertex connectivity, undirected graph
+ * (19) vertex connectivity, directed graph
+ * (20) vertex connectivity, undirected graph
+ * (21) s-t number of edge disjoint paths, directed graph
+ * (22) s-t number of edge disjoint paths, undirected graph
+ * (23) s-t number of vertex disjoint paths, directed graph
+ * (24) s-t number of vertex disjoint paths, undirected graph
+ * (25) graph adhesion, directed graph
+ * (26) graph adhesion, undirected graph
+ * (27) graph cohesion, directed graph
+ * (28) graph cohesion, undirected graph
+ *
+ * This is how they are calculated:
+ * ( 1) igraph_maxflow_value, calls igraph_maxflow.
+ * ( 2) igraph_maxflow_value, calls igraph_maxflow, this calls
+ *      igraph_i_maxflow_undirected. This transforms the graph into a
+ *      directed graph, including two mutual edges instead of every
+ *      undirected edge, then igraph_maxflow is called again with the
+ *      directed graph.
+ * ( 3) igraph_maxflow, does the push-relabel algorithm, optionally
+ *      calculates the cut, the partitions and the flow itself.
+ * ( 4) igraph_maxflow calls igraph_i_maxflow_undirected, this converts
+ *      the undirected graph into a directed one, adding two mutual edges
+ *      for each undirected edge, then igraph_maxflow is called again,
+ *      with the directed graph. After igraph_maxflow returns, we need
+ *      to edit the flow (and the cut) to make it sense for the
+ *      original graph.
+ * ( 5) igraph_st_mincut_value, we just call igraph_maxflow_value
+ * ( 6) igraph_st_mincut_value, we just call igraph_maxflow_value
+ * ( 7) igraph_mincut_value, we call igraph_maxflow_value (|V|-1)*2
+ *      times, from vertex 0 to all other vertices and from all other
+ *      vertices to vertex 0
+ * ( 8) We call igraph_i_mincut_value_undirected, that calls
+ *      igraph_i_mincut_undirected with partition=partition2=cut=NULL
+ *      The Stoer-Wagner algorithm is used.
+ * ( 9) igraph_st_mincut, just calls igraph_maxflow.
+ * (10) igraph_st_mincut, just calls igraph_maxflow.
+ * (11) igraph_mincut, calls igraph_i_mincut_directed, which runs
+ *      the maximum flow algorithm 2(|V|-1) times, from vertex zero to
+ *      and from all other vertices and stores the smallest cut.
+ * (12) igraph_mincut, igraph_i_mincut_undirected is called,
+ *      this is the Stoer-Wagner algorithm
+ * (13) We just call igraph_maxflow_value, back to (1)
+ * (14) We just call igraph_maxflow_value, back to (2)
+ * (15) We just call igraph_mincut_value (possibly after some basic
+ *      checks). Back to (7)
+ * (16) We just call igraph_mincut_value (possibly after some basic
+ *      checks). Back to (8).
+ * (17) We call igraph_i_st_vertex_connectivity_directed.
+ *      That creates a new graph with 2*|V| vertices and smartly chosen
+ *      edges, so that the s-t edge connectivity of this graph is the
+ *      same as the s-t vertex connectivity of the original graph.
+ *      So finally it calls igraph_maxflow_value, go to (1)
+ * (18) We call igraph_i_st_vertex_connectivity_undirected.
+ *      We convert the graph to a directed one,
+ *      IGRAPH_TO_DIRECTED_MUTUAL method. Then we call
+ *      igraph_i_st_vertex_connectivity_directed, see (17).
+ * (19) We call igraph_i_vertex_connectivity_directed.
+ *      That calls igraph_st_vertex_connectivity for all pairs of
+ *      vertices. Back to (17).
+ * (20) We call igraph_i_vertex_connectivity_undirected.
+ *      That converts the graph into a directed one
+ *      (IGRAPH_TO_DIRECTED_MUTUAL) and calls the directed version,
+ *      igraph_i_vertex_connectivity_directed, see (19).
+ * (21) igraph_edge_disjoint_paths, we just call igraph_maxflow_value, (1).
+ * (22) igraph_edge_disjoint_paths, we just call igraph_maxflow_value, (2).
+ * (23) igraph_vertex_disjoint_paths, if there is a connection between
+ *      the two vertices, then we remove that (or all of them if there
+ *      are many), as this could mess up vertex connectivity
+ *      calculation. The we call
+ *      igraph_i_st_vertex_connectivity_directed, see (19).
+ * (24) igraph_vertex_disjoint_paths, if there is a connection between
+ *      the two vertices, then we remove that (or all of them if there
+ *      are many), as this could mess up vertex connectivity
+ *      calculation. The we call
+ *      igraph_i_st_vertex_connectivity_undirected, see (20).
+ * (25) We just call igraph_edge_connectivity, see (15).
+ * (26) We just call igraph_edge_connectivity, see (16).
+ * (27) We just call igraph_vertex_connectivity, see (19).
+ * (28) We just call igraph_vertex_connectivity, see (20).
+ */
+
+/*
+ * This is an internal function that calculates the maximum flow value
+ * on undirected graphs, either for an s-t vertex pair or for the
+ * graph (i.e. all vertex pairs).
+ *
+ * It does it by converting the undirected graph to a corresponding
+ * directed graph, including reciprocal directed edges instead of each
+ * undirected edge.
+ */
+
+int igraph_i_maxflow_undirected(const igraph_t *graph,
+                                igraph_real_t *value,
+                                igraph_vector_t *flow,
+                                igraph_vector_t *cut,
+                                igraph_vector_t *partition,
+                                igraph_vector_t *partition2,
+                                igraph_integer_t source,
+                                igraph_integer_t target,
+                                const igraph_vector_t *capacity,
+                                igraph_maxflow_stats_t *stats) {
+    igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph);
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    igraph_vector_t edges;
+    igraph_vector_t newcapacity;
+    igraph_t newgraph;
+    long int i;
+
+    /* We need to convert this to directed by hand, since we need to be
+       sure that the edge ids will be handled properly to build the new
+       capacity vector. */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges * 2);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 4));
+    IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
+    IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges * 4));
+    for (i = 0; i < no_of_edges; i++) {
+        VECTOR(edges)[no_of_edges * 2 + i * 2] = VECTOR(edges)[i * 2 + 1];
+        VECTOR(edges)[no_of_edges * 2 + i * 2 + 1] = VECTOR(edges)[i * 2];
+        VECTOR(newcapacity)[i] = VECTOR(newcapacity)[no_of_edges + i] =
+                                     capacity ? VECTOR(*capacity)[i] : 1.0;
+    }
+
+    IGRAPH_CHECK(igraph_create(&newgraph, &edges, no_of_nodes, IGRAPH_DIRECTED));
+    IGRAPH_FINALLY(igraph_destroy, &newgraph);
+
+    IGRAPH_CHECK(igraph_maxflow(&newgraph, value, flow, cut, partition,
+                                partition2, source, target, &newcapacity, stats));
+
+    if (cut) {
+        long int i, cs = igraph_vector_size(cut);
+        for (i = 0; i < cs; i++) {
+            if (VECTOR(*cut)[i] >= no_of_edges) {
+                VECTOR(*cut)[i] -= no_of_edges;
+            }
+        }
+    }
+
+    /* The flow has one non-zero value for each real-nonreal edge pair,
+       by definition, we convert it to a positive-negative vector. If
+       for an edge the flow is negative that means that it is going
+       from the bigger vertex id to the smaller one. For positive
+       values the direction is the opposite. */
+    if (flow) {
+        long int i;
+        for (i = 0; i < no_of_edges; i++) {
+            VECTOR(*flow)[i] -= VECTOR(*flow)[i + no_of_edges];
+        }
+        IGRAPH_CHECK(igraph_vector_resize(flow, no_of_edges));
+    }
+
+    igraph_destroy(&newgraph);
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&newcapacity);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+#define FIRST(i)       (VECTOR(*first)[(i)])
+#define LAST(i)        (VECTOR(*first)[(i)+1])
+#define CURRENT(i)     (VECTOR(*current)[(i)])
+#define RESCAP(i)      (VECTOR(*rescap)[(i)])
+#define REV(i)         (VECTOR(*rev)[(i)])
+#define HEAD(i)        (VECTOR(*to)[(i)])
+#define EXCESS(i)      (VECTOR(*excess)[(i)])
+#define DIST(i)        (VECTOR(*distance)[(i)])
+#define DISCHARGE(v)   (igraph_i_mf_discharge((v), &current, &first, &rescap, \
+                        &to, &distance, &excess,        \
+                        no_of_nodes, source, target,    \
+                        &buckets, &ibuckets,        \
+                        &rev, stats, &npushsince,       \
+                        &nrelabelsince))
+#define PUSH(v,e,n)    (igraph_i_mf_push((v), (e), (n), current, rescap,      \
+                        excess, target, source, buckets,     \
+                        ibuckets, distance, rev, stats,      \
+                        npushsince))
+#define RELABEL(v)     (igraph_i_mf_relabel((v), no_of_nodes, distance,       \
+                        first, rescap, to, current,       \
+                        stats, nrelabelsince))
+#define GAP(b)         (igraph_i_mf_gap((b), stats, buckets, ibuckets,        \
+                                        no_of_nodes, distance))
+#define BFS()          (igraph_i_mf_bfs(&bfsq, source, target, no_of_nodes,   \
+                                        &buckets, &ibuckets, &distance,       \
+                                        &first, &current, &to, &excess,       \
+                                        &rescap, &rev))
+
+void igraph_i_mf_gap(long int b, igraph_maxflow_stats_t *stats,
+                     igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets,
+                     long int no_of_nodes,
+                     igraph_vector_long_t *distance) {
+
+    long int bo;
+    (stats->nogap)++;
+    for (bo = b + 1; bo <= no_of_nodes; bo++) {
+        while (!igraph_dbuckets_empty_bucket(ibuckets, bo)) {
+            long int n = igraph_dbuckets_pop(ibuckets, bo);
+            (stats->nogapnodes)++;
+            DIST(n) = no_of_nodes;
+        }
+    }
+}
+
+void igraph_i_mf_relabel(long int v, long int no_of_nodes,
+                         igraph_vector_long_t *distance,
+                         igraph_vector_long_t *first,
+                         igraph_vector_t *rescap, igraph_vector_long_t *to,
+                         igraph_vector_long_t *current,
+                         igraph_maxflow_stats_t *stats, int *nrelabelsince) {
+
+    long int min = no_of_nodes;
+    long int k, l, min_edge = 0;
+    (stats->norelabel)++; (*nrelabelsince)++;
+    DIST(v) = no_of_nodes;
+    for (k = FIRST(v), l = LAST(v); k < l; k++) {
+        if (RESCAP(k) > 0 && DIST(HEAD(k)) < min) {
+            min = DIST(HEAD(k));
+            min_edge = k;
+        }
+    }
+    min++;
+    if (min < no_of_nodes) {
+        DIST(v) = min;
+        CURRENT(v) = min_edge;
+    }
+}
+
+void igraph_i_mf_push(long int v, long int e, long int n,
+                      igraph_vector_long_t *current,
+                      igraph_vector_t *rescap, igraph_vector_t *excess,
+                      long int target, long int source,
+                      igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets,
+                      igraph_vector_long_t *distance,
+                      igraph_vector_long_t *rev, igraph_maxflow_stats_t *stats,
+                      int *npushsince) {
+    igraph_real_t delta =
+        RESCAP(e) < EXCESS(v) ? RESCAP(e) : EXCESS(v);
+    (stats->nopush)++; (*npushsince)++;
+    if (EXCESS(n) == 0 && n != target) {
+        igraph_dbuckets_delete(ibuckets, DIST(n), n);
+        igraph_buckets_add(buckets, (long int) DIST(n), n);
+    }
+    RESCAP(e) -= delta;
+    RESCAP(REV(e)) += delta;
+    EXCESS(n) += delta;
+    EXCESS(v) -= delta;
+}
+
+void igraph_i_mf_discharge(long int v,
+                           igraph_vector_long_t *current,
+                           igraph_vector_long_t *first,
+                           igraph_vector_t *rescap,
+                           igraph_vector_long_t *to,
+                           igraph_vector_long_t *distance,
+                           igraph_vector_t *excess,
+                           long int no_of_nodes, long int source,
+                           long int target, igraph_buckets_t *buckets,
+                           igraph_dbuckets_t *ibuckets,
+                           igraph_vector_long_t *rev,
+                           igraph_maxflow_stats_t *stats,
+                           int *npushsince, int *nrelabelsince) {
+    do {
+        long int i;
+        long int start = (long int) CURRENT(v);
+        long int stop = (long int) LAST(v);
+        for (i = start; i < stop; i++) {
+            if (RESCAP(i) > 0) {
+                long int nei = HEAD(i);
+                if (DIST(v) == DIST(nei) + 1) {
+                    PUSH((v), i, nei);
+                    if (EXCESS(v) == 0) {
+                        break;
+                    }
+                }
+            }
+        }
+        if (i == stop) {
+            long int origdist = DIST(v);
+            RELABEL(v);
+            if (igraph_buckets_empty_bucket(buckets, origdist) &&
+                igraph_dbuckets_empty_bucket(ibuckets, origdist)) {
+                GAP(origdist);
+            }
+            if (DIST(v) == no_of_nodes) {
+                break;
+            }
+        } else {
+            CURRENT(v) = i;
+            igraph_dbuckets_add(ibuckets, DIST(v), v);
+            break;
+        }
+    } while (1);
+}
+
+void igraph_i_mf_bfs(igraph_dqueue_long_t *bfsq,
+                     long int source, long int target,
+                     long int no_of_nodes, igraph_buckets_t *buckets,
+                     igraph_dbuckets_t *ibuckets,
+                     igraph_vector_long_t *distance,
+                     igraph_vector_long_t *first, igraph_vector_long_t *current,
+                     igraph_vector_long_t *to, igraph_vector_t *excess,
+                     igraph_vector_t *rescap, igraph_vector_long_t *rev) {
+
+    long int k, l;
+
+    igraph_buckets_clear(buckets);
+    igraph_dbuckets_clear(ibuckets);
+    igraph_vector_long_fill(distance, no_of_nodes);
+    DIST(target) = 0;
+
+    igraph_dqueue_long_push(bfsq, target);
+    while (!igraph_dqueue_long_empty(bfsq)) {
+        long int node = igraph_dqueue_long_pop(bfsq);
+        long int ndist = DIST(node) + 1;
+        for (k = FIRST(node), l = LAST(node); k < l; k++) {
+            if (RESCAP(REV(k)) > 0) {
+                long int nei = HEAD(k);
+                if (DIST(nei) == no_of_nodes) {
+                    DIST(nei) = ndist;
+                    CURRENT(nei) = FIRST(nei);
+                    if (EXCESS(nei) > 0) {
+                        igraph_buckets_add(buckets, ndist, nei);
+                    } else {
+                        igraph_dbuckets_add(ibuckets, ndist, nei);
+                    }
+                    igraph_dqueue_long_push(bfsq, nei);
+                }
+            }
+        }
+    }
+}
+
+/**
+ * \function igraph_maxflow
+ * Maximum network flow between a pair of vertices
+ *
+ * </para><para>This function implements the Goldberg-Tarjan algorithm for
+ * calculating value of the maximum flow in a directed or undirected
+ * graph. The algorithm was given in Andrew V. Goldberg, Robert
+ * E. Tarjan: A New Approach to the Maximum-Flow Problem, Journal of
+ * the ACM, 35(4), 921-940, 1988. </para>
+ *
+ * <para> The input of the function is a graph, a vector
+ * of real numbers giving the capacity of the edges and two vertices
+ * of the graph, the source and the target. A flow is a function
+ * assigning positive real numbers to the edges and satisfying two
+ * requirements: (1) the flow value is less than the capacity of the
+ * edge and (2) at each vertex except the source and the target, the
+ * incoming flow (ie. the sum of the flow on the incoming edges) is
+ * the same as the outgoing flow (ie. the sum of the flow on the
+ * outgoing edges). The value of the flow is the incoming flow at the
+ * target vertex. The maximum flow is the flow with the maximum
+ * value.
+ *
+ * \param graph The input graph, either directed or undirected.
+ * \param value Pointer to a real number, the value of the maximum
+ *        will be placed here, unless it is a null pointer.
+ * \param flow If not a null pointer, then it must be a pointer to an
+ *        initialized vector. The vector will be resized, and the flow
+ *        on each edge will be placed in it, in the order of the edge
+ *        ids. For undirected graphs this argument is bit trickier,
+ *        since for these the flow direction is not predetermined by
+ *        the edge direction. For these graphs the elements of the
+ *        \p flow vector can be negative, this means that the flow
+ *        goes from the bigger vertex id to the smaller one. Positive
+ *        values mean that the flow goes from the smaller vertex id to
+ *        the bigger one.
+ * \param cut A null pointer or a pointer to an initialized vector.
+ *        If not a null pointer, then the minimum cut corresponding to
+ *        the maximum flow is stored here, i.e. all edge ids that are
+ *        part of the minimum cut are stored in the vector.
+ * \param partition A null pointer or a pointer to an initialized
+ *        vector. If not a null pointer, then the first partition of
+ *        the minimum cut that corresponds to the maximum flow will be
+ *        placed here. The first partition is always the one that
+ *        contains the source vertex.
+ * \param partition2 A null pointer or a pointer to an initialized
+ *        vector. If not a null pointer, then the second partition of
+ *        the minimum cut that corresponds to the maximum flow will be
+ *        placed here. The second partition is always the one that
+ *        contains the target vertex.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \param capacity Vector containing the capacity of the edges. If NULL, then
+ *        every edge is considered to have capacity 1.0.
+ * \param stats Counts of the number of different operations
+ *        preformed by the algorithm are stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^3). In practice it is much faster, but i
+ * cannot prove a better lower bound for the data structure i've
+ * used. In fact, this implementation runs much faster than the
+ * \c hi_pr implementation discussed in
+ * B. V. Cherkassky and A. V. Goldberg: On implementing the
+ * push-relabel method for the maximum flow problem, (Algorithmica,
+ * 19:390--410, 1997) on all the graph classes i've tried.
+ *
+ * \sa \ref igraph_mincut_value(), \ref igraph_edge_connectivity(),
+ * \ref igraph_vertex_connectivity() for
+ * properties based on the maximum flow.
+ *
+ * \example examples/simple/flow.c
+ * \example examples/simple/flow2.c
+ */
+
+int igraph_maxflow(const igraph_t *graph, igraph_real_t *value,
+                   igraph_vector_t *flow, igraph_vector_t *cut,
+                   igraph_vector_t *partition, igraph_vector_t *partition2,
+                   igraph_integer_t source, igraph_integer_t target,
+                   const igraph_vector_t *capacity,
+                   igraph_maxflow_stats_t *stats) {
+
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    igraph_integer_t no_of_orig_edges = (igraph_integer_t) igraph_ecount(graph);
+    igraph_integer_t no_of_edges = 2 * no_of_orig_edges;
+
+    igraph_vector_t rescap, excess;
+    igraph_vector_long_t from, to, rev, distance;
+    igraph_vector_t edges, rank;
+    igraph_vector_long_t current, first;
+    igraph_buckets_t buckets;
+    igraph_dbuckets_t ibuckets;
+
+    igraph_dqueue_long_t bfsq;
+
+    long int i, j, idx;
+    int npushsince = 0, nrelabelsince = 0;
+
+    igraph_maxflow_stats_t local_stats;   /* used if the user passed a null pointer for stats */
+
+    if (stats == 0) {
+        stats = &local_stats;
+    }
+
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_CHECK(igraph_i_maxflow_undirected(graph, value, flow, cut,
+                     partition, partition2, source,
+                     target, capacity, stats));
+        return 0;
+    }
+
+    if (capacity && igraph_vector_size(capacity) != no_of_orig_edges) {
+        IGRAPH_ERROR("Invalid capacity vector", IGRAPH_EINVAL);
+    }
+    if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL);
+    }
+
+    stats->nopush = stats->norelabel = stats->nogap = stats->nogapnodes =
+                                           stats->nobfs = 0;
+
+    /*
+     * The data structure:
+     * - First of all, we consider every edge twice, first the edge
+     *   itself, but also its opposite.
+     * - (from, to) contain all edges (original + opposite), ordered by
+     *   the id of the source vertex. During the algorithm we just need
+     *   'to', so from is destroyed soon. We only need it in the
+     *   beginning, to create the 'first' pointers.
+     * - 'first' is a pointer vector for 'to', first[i] points to the
+     *   first neighbor of vertex i and first[i+1]-1 is the last
+     *   neighbor of vertex i. (Unless vertex i is isolate, in which
+     *   case first[i]==first[i+1]).
+     * - 'rev' contains a mapping from an edge to its opposite pair
+     * - 'rescap' contains the residual capacities of the edges, this is
+     *   initially equal to the capacity of the edges for the original
+     *   edges and it is zero for the opposite edges.
+     * - 'excess' contains the excess flow for the vertices. I.e. the flow
+     *   that is coming in, but it is not going out.
+     * - 'current' stores the next neighboring vertex to check, for every
+     *   vertex, when excess flow is being pushed to neighbors.
+     * - 'distance' stores the distance of the vertices from the source.
+     * - 'rank' and 'edges' are only needed temporarily, for ordering and
+     *   storing the edges.
+     * - we use an igraph_buckets_t data structure ('buckets') to find
+     *   the vertices with the highest 'distance' values quickly.
+     *   This always contains the vertices that have a positive excess
+     *   flow.
+     */
+#undef FIRST
+#undef LAST
+#undef CURRENT
+#undef RESCAP
+#undef REV
+#undef HEAD
+#undef EXCESS
+#undef DIST
+#define FIRST(i)       (VECTOR(first)[(i)])
+#define LAST(i)        (VECTOR(first)[(i)+1])
+#define CURRENT(i)     (VECTOR(current)[(i)])
+#define RESCAP(i)      (VECTOR(rescap)[(i)])
+#define REV(i)         (VECTOR(rev)[(i)])
+#define HEAD(i)        (VECTOR(to)[(i)])
+#define EXCESS(i)      (VECTOR(excess)[(i)])
+#define DIST(i)        (VECTOR(distance)[(i)])
+
+    igraph_dqueue_long_init(&bfsq,             no_of_nodes);
+    IGRAPH_FINALLY(igraph_dqueue_long_destroy, &bfsq);
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&to,       no_of_edges);
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&rev,      no_of_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&rescap,        no_of_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&excess,        no_of_nodes);
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&distance, no_of_nodes);
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&first,    no_of_nodes + 1);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&rank,          no_of_edges);
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&from,     no_of_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges,         no_of_edges);
+
+    /* Create the basic data structure */
+    IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
+    IGRAPH_CHECK(igraph_vector_rank(&edges, &rank, no_of_nodes));
+
+    for (i = 0; i < no_of_edges; i += 2) {
+        long int pos = (long int) VECTOR(rank)[i];
+        long int pos2 = (long int) VECTOR(rank)[i + 1];
+        VECTOR(from)[pos] = VECTOR(edges)[i];
+        VECTOR(to)[pos]   = VECTOR(edges)[i + 1];
+        VECTOR(from)[pos2] = VECTOR(edges)[i + 1];
+        VECTOR(to)[pos2]   = VECTOR(edges)[i];
+        VECTOR(rev)[pos] = pos2;
+        VECTOR(rev)[pos2] = pos;
+        VECTOR(rescap)[pos] = capacity ? VECTOR(*capacity)[i / 2] : 1.0;
+        VECTOR(rescap)[pos2] = 0.0;
+    }
+
+    /* The first pointers. This is a but trickier, than one would
+       think, because of the possible isolate vertices. */
+
+    idx = -1;
+    for (i = 0; i <= VECTOR(from)[0]; i++) {
+        idx++; VECTOR(first)[idx] = 0;
+    }
+    for (i = 1; i < no_of_edges; i++) {
+        long int n = (long int) (VECTOR(from)[i] -
+                                 VECTOR(from)[ (long int) VECTOR(first)[idx] ]);
+        for (j = 0; j < n; j++) {
+            idx++; VECTOR(first)[idx] = i;
+        }
+    }
+    idx++;
+    while (idx < no_of_nodes + 1) {
+        VECTOR(first)[idx++] = no_of_edges;
+    }
+
+    igraph_vector_long_destroy(&from);
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    if (!flow) {
+        igraph_vector_destroy(&rank);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* And the current pointers, initially the same as the first */
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&current, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(current)[i] = VECTOR(first)[i];
+    }
+
+    /* OK, the graph is set up, initialization */
+
+    IGRAPH_CHECK(igraph_buckets_init(&buckets, no_of_nodes + 1, no_of_nodes));
+    IGRAPH_FINALLY(igraph_buckets_destroy, &buckets);
+    IGRAPH_CHECK(igraph_dbuckets_init(&ibuckets, no_of_nodes + 1, no_of_nodes));
+    IGRAPH_FINALLY(igraph_dbuckets_destroy, &ibuckets);
+
+    /* Send as much flow as possible from the source to its neighbors */
+    for (i = FIRST(source), j = LAST(source); i < j; i++) {
+        if (HEAD(i) != source) {
+            igraph_real_t delta = RESCAP(i);
+            RESCAP(i) = 0;
+            RESCAP(REV(i)) += delta;
+            EXCESS(HEAD(i)) += delta;
+        }
+    }
+
+    BFS();
+    (stats->nobfs)++;
+
+    while (!igraph_buckets_empty(&buckets)) {
+        long int vertex = igraph_buckets_popmax(&buckets);
+        DISCHARGE(vertex);
+        if (npushsince > no_of_nodes / 2 && nrelabelsince > no_of_nodes) {
+            (stats->nobfs)++;
+            BFS();
+            npushsince = nrelabelsince = 0;
+        }
+    }
+
+    /* Store the result */
+    if (value) {
+        *value = EXCESS(target);
+    }
+
+    /* If we also need the minimum cut */
+    if (cut || partition || partition2) {
+        /* We need to find all vertices from which the target is reachable
+           in the residual graph. We do a breadth-first search, going
+           backwards. */
+        igraph_dqueue_t Q;
+        igraph_vector_bool_t added;
+        long int marked = 0;
+
+        IGRAPH_CHECK(igraph_vector_bool_init(&added, no_of_nodes));
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, &added);
+
+        IGRAPH_CHECK(igraph_dqueue_init(&Q, 100));
+        IGRAPH_FINALLY(igraph_dqueue_destroy, &Q);
+
+        igraph_dqueue_push(&Q, target);
+        VECTOR(added)[(long int)target] = 1;
+        marked++;
+        while (!igraph_dqueue_empty(&Q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&Q);
+            for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) {
+                long int nei = HEAD(i);
+                if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) {
+                    VECTOR(added)[nei] = 1;
+                    marked++;
+                    IGRAPH_CHECK(igraph_dqueue_push(&Q, nei));
+                }
+            }
+        }
+        igraph_dqueue_destroy(&Q);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        /* Now we marked each vertex that is on one side of the cut,
+           check the crossing edges */
+
+        if (cut) {
+            igraph_vector_clear(cut);
+            for (i = 0; i < no_of_orig_edges; i++) {
+                long int f = IGRAPH_FROM(graph, i);
+                long int t = IGRAPH_TO(graph, i);
+                if (!VECTOR(added)[f] && VECTOR(added)[t]) {
+                    IGRAPH_CHECK(igraph_vector_push_back(cut, i));
+                }
+            }
+        }
+
+        if (partition2) {
+            long int x = 0;
+            IGRAPH_CHECK(igraph_vector_resize(partition2, marked));
+            for (i = 0; i < no_of_nodes; i++) {
+                if (VECTOR(added)[i]) {
+                    VECTOR(*partition2)[x++] = i;
+                }
+            }
+        }
+
+        if (partition) {
+            long int x = 0;
+            IGRAPH_CHECK(igraph_vector_resize(partition,
+                                              no_of_nodes - marked));
+            for (i = 0; i < no_of_nodes; i++) {
+                if (!VECTOR(added)[i]) {
+                    VECTOR(*partition)[x++] = i;
+                }
+            }
+        }
+
+        igraph_vector_bool_destroy(&added);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (flow) {
+        /* Initialize the backward distances, with a breadth-first search
+           from the source */
+        igraph_dqueue_t Q;
+        igraph_vector_int_t added;
+        long int j, k, l;
+        igraph_t flow_graph;
+        igraph_vector_t flow_edges;
+        igraph_bool_t dag;
+
+        IGRAPH_CHECK(igraph_vector_int_init(&added, no_of_nodes));
+        IGRAPH_FINALLY(igraph_vector_int_destroy, &added);
+        IGRAPH_CHECK(igraph_dqueue_init(&Q, 100));
+        IGRAPH_FINALLY(igraph_dqueue_destroy, &added);
+
+        igraph_dqueue_push(&Q, source);
+        igraph_dqueue_push(&Q, 0);
+        VECTOR(added)[(long int)source] = 1;
+        while (!igraph_dqueue_empty(&Q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&Q);
+            long int actdist = (long int) igraph_dqueue_pop(&Q);
+            DIST(actnode) = actdist;
+
+            for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) {
+                long int nei = HEAD(i);
+                if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) {
+                    VECTOR(added)[nei] = 1;
+                    IGRAPH_CHECK(igraph_dqueue_push(&Q, nei));
+                    IGRAPH_CHECK(igraph_dqueue_push(&Q, actdist + 1));
+                }
+            }
+        } /* !igraph_dqueue_empty(&Q) */
+
+        igraph_vector_int_destroy(&added);
+        igraph_dqueue_destroy(&Q);
+        IGRAPH_FINALLY_CLEAN(2);
+
+        /* Reinitialize the buckets */
+        igraph_buckets_clear(&buckets);
+        for (i = 0; i < no_of_nodes; i++) {
+            if (EXCESS(i) > 0.0 && i != source && i != target) {
+                igraph_buckets_add(&buckets, (long int) DIST(i), i);
+            }
+        }
+
+        /* Now we return the flow to the source */
+        while (!igraph_buckets_empty(&buckets)) {
+            long int vertex = igraph_buckets_popmax(&buckets);
+
+            /* DISCHARGE(vertex) comes here */
+            do {
+                for (i = (long int) CURRENT(vertex), j = LAST(vertex); i < j; i++) {
+                    if (RESCAP(i) > 0) {
+                        long int nei = HEAD(i);
+
+                        if (DIST(vertex) == DIST(nei) + 1) {
+                            igraph_real_t delta =
+                                RESCAP(i) < EXCESS(vertex) ? RESCAP(i) : EXCESS(vertex);
+                            RESCAP(i) -= delta;
+                            RESCAP(REV(i)) += delta;
+
+                            if (nei != source && EXCESS(nei) == 0.0 &&
+                                DIST(nei) != no_of_nodes) {
+                                igraph_buckets_add(&buckets, (long int) DIST(nei), nei);
+                            }
+
+                            EXCESS(nei) += delta;
+                            EXCESS(vertex) -= delta;
+
+                            if (EXCESS(vertex) == 0) {
+                                break;
+                            }
+
+                        }
+                    }
+                }
+
+                if (i == j) {
+
+                    /* RELABEL(vertex) comes here */
+                    igraph_real_t min;
+                    long int min_edge = 0;
+                    DIST(vertex) = min = no_of_nodes;
+                    for (k = FIRST(vertex), l = LAST(vertex); k < l; k++) {
+                        if (RESCAP(k) > 0) {
+                            if (DIST(HEAD(k)) < min) {
+                                min = DIST(HEAD(k));
+                                min_edge = k;
+                            }
+                        }
+                    }
+
+                    min++;
+
+                    if (min < no_of_nodes) {
+                        DIST(vertex) = min;
+                        CURRENT(vertex) = min_edge;
+                        /* Vertex is still active */
+                        igraph_buckets_add(&buckets, (long int) DIST(vertex), vertex);
+                    }
+
+                    /* TODO: gap heuristics here ??? */
+
+                } else {
+                    CURRENT(vertex) = FIRST(vertex);
+                }
+
+                break;
+
+            } while (1);
+        }
+
+        /* We need to eliminate flow cycles now. Before that we check that
+           there is a cycle in the flow graph.
+
+           First we do a couple of DFSes from the source vertex to the
+           target and factor out the paths we find. If there is no more
+           path to the target, then all remaining flow must be in flow
+           cycles, so we don't need it at all.
+
+           Some details. 'stack' contains the whole path of the DFS, both
+           the vertices and the edges, they are alternating in the stack.
+           'current' helps finding the next outgoing edge of a vertex
+           quickly, the next edge of 'v' is FIRST(v)+CURRENT(v). If this
+           is LAST(v), then there are no more edges to try.
+
+           The 'added' vector contains 0 if the vertex was not visited
+           before, 1 if it is currently in 'stack', and 2 if it is not in
+           'stack', but it was visited before. */
+
+        IGRAPH_VECTOR_INIT_FINALLY(&flow_edges, 0);
+        for (i = 0, j = 0; i < no_of_edges; i += 2, j++) {
+            long int pos = (long int) VECTOR(rank)[i];
+            if ((capacity ? VECTOR(*capacity)[j] : 1.0) > RESCAP(pos)) {
+                IGRAPH_CHECK(igraph_vector_push_back(&flow_edges,
+                                                     IGRAPH_FROM(graph, j)));
+                IGRAPH_CHECK(igraph_vector_push_back(&flow_edges,
+                                                     IGRAPH_TO(graph, j)));
+            }
+        }
+        IGRAPH_CHECK(igraph_create(&flow_graph, &flow_edges, no_of_nodes,
+                                   IGRAPH_DIRECTED));
+        igraph_vector_destroy(&flow_edges);
+        IGRAPH_FINALLY_CLEAN(1);
+        IGRAPH_FINALLY(igraph_destroy, &flow_graph);
+        IGRAPH_CHECK(igraph_is_dag(&flow_graph, &dag));
+        igraph_destroy(&flow_graph);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        if (!dag) {
+            igraph_vector_long_t stack;
+            igraph_vector_t mycap;
+
+            IGRAPH_CHECK(igraph_vector_long_init(&stack, 0));
+            IGRAPH_FINALLY(igraph_vector_long_destroy, &stack);
+            IGRAPH_CHECK(igraph_vector_int_init(&added, no_of_nodes));
+            IGRAPH_FINALLY(igraph_vector_int_destroy, &added);
+            IGRAPH_VECTOR_INIT_FINALLY(&mycap, no_of_edges);
+
+#define MYCAP(i)      (VECTOR(mycap)[(i)])
+
+            for (i = 0; i < no_of_edges; i += 2) {
+                long int pos = (long int) VECTOR(rank)[i];
+                long int pos2 = (long int) VECTOR(rank)[i + 1];
+                MYCAP(pos) = (capacity ? VECTOR(*capacity)[i / 2] : 1.0) - RESCAP(pos);
+                MYCAP(pos2) = 0.0;
+            }
+
+            do {
+                igraph_vector_long_null(&current);
+                igraph_vector_long_clear(&stack);
+                igraph_vector_int_null(&added);
+
+                IGRAPH_CHECK(igraph_vector_long_push_back(&stack, -1));
+                IGRAPH_CHECK(igraph_vector_long_push_back(&stack, source));
+                VECTOR(added)[(long int)source] = 1;
+                while (!igraph_vector_long_empty(&stack) &&
+                       igraph_vector_long_tail(&stack) != target) {
+                    long int actnode = igraph_vector_long_tail(&stack);
+                    long int edge = FIRST(actnode) + (long int) CURRENT(actnode);
+                    long int nei;
+                    while (edge < LAST(actnode) && MYCAP(edge) == 0.0) {
+                        edge++;
+                    }
+                    nei = edge < LAST(actnode) ? HEAD(edge) : -1;
+
+                    if (edge < LAST(actnode) && !VECTOR(added)[nei]) {
+                        /* Go forward along next edge, if the vertex was not
+                           visited before */
+                        IGRAPH_CHECK(igraph_vector_long_push_back(&stack, edge));
+                        IGRAPH_CHECK(igraph_vector_long_push_back(&stack, nei));
+                        VECTOR(added)[nei] = 1;
+                        CURRENT(actnode) += 1;
+                    } else if (edge < LAST(actnode) && VECTOR(added)[nei] == 1) {
+                        /* We found a flow cycle, factor it out. Go back in stack
+                           until we find 'nei' again, determine the flow along the
+                           cycle. */
+                        igraph_real_t thisflow = MYCAP(edge);
+                        long int idx;
+                        for (idx = igraph_vector_long_size(&stack) - 2;
+                             idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) {
+                            long int e = VECTOR(stack)[idx];
+                            igraph_real_t rcap = e >= 0 ? MYCAP(e) : MYCAP(edge);
+                            if (rcap < thisflow) {
+                                thisflow = rcap;
+                            }
+                        }
+                        MYCAP(edge) -= thisflow; RESCAP(edge) += thisflow;
+                        for (idx = igraph_vector_long_size(&stack) - 2;
+                             idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) {
+                            long int e = VECTOR(stack)[idx];
+                            if (e >= 0) {
+                                MYCAP(e) -= thisflow;
+                                RESCAP(e) += thisflow;
+                            }
+                        }
+                        CURRENT(actnode) += 1;
+                    } else if (edge < LAST(actnode)) { /* && VECTOR(added)[nei]==2 */
+                        /* The next edge leads to a vertex that was visited before,
+                           but it is currently not in 'stack' */
+                        CURRENT(actnode) += 1;
+                    } else {
+                        /* Go backward, take out the node and the edge that leads to it */
+                        igraph_vector_long_pop_back(&stack);
+                        igraph_vector_long_pop_back(&stack);
+                        VECTOR(added)[actnode] = 2;
+                    }
+                }
+
+                /* If non-empty, then it contains a path from source to target
+                   in the residual graph. We factor out this path from the flow. */
+                if (!igraph_vector_long_empty(&stack)) {
+                    long int pl = igraph_vector_long_size(&stack);
+                    igraph_real_t thisflow = EXCESS(target);
+                    for (i = 2; i < pl; i += 2) {
+                        long int edge = VECTOR(stack)[i];
+                        igraph_real_t rcap = MYCAP(edge);
+                        if (rcap < thisflow) {
+                            thisflow = rcap;
+                        }
+                    }
+                    for (i = 2; i < pl; i += 2) {
+                        long int edge = VECTOR(stack)[i];
+                        MYCAP(edge) -= thisflow;
+                    }
+                }
+
+            } while (!igraph_vector_long_empty(&stack));
+
+            igraph_vector_destroy(&mycap);
+            igraph_vector_int_destroy(&added);
+            igraph_vector_long_destroy(&stack);
+            IGRAPH_FINALLY_CLEAN(3);
+        }
+
+        /* ----------------------------------------------------------- */
+
+        IGRAPH_CHECK(igraph_vector_resize(flow, no_of_orig_edges));
+        for (i = 0, j = 0; i < no_of_edges; i += 2, j++) {
+            long int pos = (long int) VECTOR(rank)[i];
+            VECTOR(*flow)[j] = (capacity ? VECTOR(*capacity)[j] : 1.0) -
+                               RESCAP(pos);
+        }
+
+        igraph_vector_destroy(&rank);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_dbuckets_destroy(&ibuckets);
+    igraph_buckets_destroy(&buckets);
+    igraph_vector_long_destroy(&current);
+    igraph_vector_long_destroy(&first);
+    igraph_vector_long_destroy(&distance);
+    igraph_vector_destroy(&excess);
+    igraph_vector_destroy(&rescap);
+    igraph_vector_long_destroy(&rev);
+    igraph_vector_long_destroy(&to);
+    igraph_dqueue_long_destroy(&bfsq);
+    IGRAPH_FINALLY_CLEAN(10);
+
+    return 0;
+}
+
+/**
+ * \function igraph_maxflow_value
+ * \brief Maximum flow in a network with the push/relabel algorithm
+ *
+ * </para><para>This function implements the Goldberg-Tarjan algorithm for
+ * calculating value of the maximum flow in a directed or undirected
+ * graph. The algorithm was given in Andrew V. Goldberg, Robert
+ * E. Tarjan: A New Approach to the Maximum-Flow Problem, Journal of
+ * the ACM, 35(4), 921-940, 1988. </para>
+ *
+ * <para> The input of the function is a graph, a vector
+ * of real numbers giving the capacity of the edges and two vertices
+ * of the graph, the source and the target. A flow is a function
+ * assigning positive real numbers to the edges and satisfying two
+ * requirements: (1) the flow value is less than the capacity of the
+ * edge and (2) at each vertex except the source and the target, the
+ * incoming flow (ie. the sum of the flow on the incoming edges) is
+ * the same as the outgoing flow (ie. the sum of the flow on the
+ * outgoing edges). The value of the flow is the incoming flow at the
+ * target vertex. The maximum flow is the flow with the maximum
+ * value. </para>
+ *
+ * <para> According to a theorem by Ford and Fulkerson
+ * (L. R. Ford Jr. and D. R. Fulkerson. Maximal flow through a
+ * network. Canadian J. Math., 8:399-404, 1956.) the maximum flow
+ * between two vertices is the same as the
+ * minimum cut between them (also called the minimum s-t cut). So \ref
+ * igraph_st_mincut_value() gives the same result in all cases as \c
+ * igraph_maxflow_value().</para>
+ *
+ * <para> Note that the value of the maximum flow is the same as the
+ * minimum cut in the graph.
+ * \param graph The input graph, either directed or undirected.
+ * \param value Pointer to a real number, the result will be placed here.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \param capacity Vector containing the capacity of the edges. If NULL, then
+ *        every edge is considered to have capacity 1.0.
+ * \param stats Counts of the number of different operations
+ *        preformed by the algorithm are stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^3).
+ *
+ * \sa \ref igraph_maxflow() to calculate the actual flow.
+ * \ref igraph_mincut_value(), \ref igraph_edge_connectivity(),
+ * \ref igraph_vertex_connectivity() for
+ * properties based on the maximum flow.
+ */
+
+int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value,
+                         igraph_integer_t source, igraph_integer_t target,
+                         const igraph_vector_t *capacity,
+                         igraph_maxflow_stats_t *stats) {
+
+    return igraph_maxflow(graph, value, /*flow=*/ 0, /*cut=*/ 0,
+                          /*partition=*/ 0, /*partition1=*/ 0,
+                          source, target, capacity, stats);
+}
+
+/**
+ * \function igraph_st_mincut_value
+ * \brief The minimum s-t cut in a graph
+ *
+ * </para><para> The minimum s-t cut in a weighted (=valued) graph is the
+ * total minimum edge weight needed to remove from the graph to
+ * eliminate all paths from a given vertex (\c source) to
+ * another vertex (\c target). Directed paths are considered in
+ * directed graphs, and undirected paths in undirected graphs.  </para>
+ *
+ * <para> The minimum s-t cut between two vertices is known to be same
+ * as the maximum flow between these two vertices. So this function
+ * calls \ref igraph_maxflow_value() to do the calculation.
+ * \param graph The input graph.
+ * \param value Pointer to a real variable, the result will be stored
+ *        here.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \param capacity Pointer to the capacity vector, it should contain
+ *        non-negative numbers and its length should be the same the
+ *        the number of edges in the graph. It can be a null pointer, then
+ *        every edge has unit capacity.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^3), see also the discussion for \ref
+ * igraph_maxflow_value(), |V| is the number of vertices.
+ */
+
+int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *value,
+                           igraph_integer_t source, igraph_integer_t target,
+                           const igraph_vector_t *capacity) {
+
+    if (source == target) {
+        IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_maxflow_value(graph, value, source, target, capacity, 0));
+
+    return 0;
+}
+
+/**
+ * \function igraph_st_mincut
+ * Minimum cut between a source and a target vertex
+ *
+ * Finds the edge set that has the smallest total capacity among all
+ * edge sets that disconnect the source and target vertices.
+ *
+ * </para><para>The calculation is performed using maximum flow
+ * techniques, by calling \ref igraph_maxflow().
+ * \param graph The input graph.
+ * \param value Pointer to a real variable, the value of the cut is
+ *        stored here.
+ * \param cut Pointer to a real vector, the edge ids that are included
+ *        in the cut are stored here. This argument is ignored if it
+ *        is a null pointer.
+ * \param partition Pointer to a real vector, the vertex ids of the
+ *        vertices in the first partition of the cut are stored
+ *        here. The first partition is always the one that contains the
+ *        source vertex. This argument is ignored if it is a null pointer.
+ * \param partition2 Pointer to a real vector, the vertex ids of the
+ *        vertices in the second partition of the cut are stored here.
+ *        The second partition is always the one that contains the
+ *        target vertex. This argument is ignored if it is a null pointer.
+ * \param source Integer, the id of the source vertex.
+ * \param target Integer, the id of the target vertex.
+ * \param capacity Vector containing the capacity of the edges. If a
+ *        null pointer, then every edge is considered to have capacity
+ *        1.0.
+ * \return Error code.
+ *
+ * \sa \ref igraph_maxflow().
+ *
+ * Time complexity: see \ref igraph_maxflow().
+ */
+
+int igraph_st_mincut(const igraph_t *graph, igraph_real_t *value,
+                     igraph_vector_t *cut, igraph_vector_t *partition,
+                     igraph_vector_t *partition2,
+                     igraph_integer_t source, igraph_integer_t target,
+                     const igraph_vector_t *capacity) {
+
+    return igraph_maxflow(graph, value, /*flow=*/ 0,
+                          cut, partition, partition2,
+                          source, target, capacity, 0);
+}
+
+/* This is a flow-based version, but there is a better one
+   for undirected graphs */
+
+/* int igraph_i_mincut_value_undirected(const igraph_t *graph, */
+/*                   igraph_real_t *res, */
+/*                   const igraph_vector_t *capacity) { */
+
+/*   long int no_of_edges=igraph_ecount(graph); */
+/*   long int no_of_nodes=igraph_vcount(graph); */
+/*   igraph_vector_t edges; */
+/*   igraph_vector_t newcapacity; */
+/*   igraph_t newgraph; */
+/*   long int i; */
+
+/*   /\* We need to convert this to directed by hand, since we need to be */
+/*      sure that the edge ids will be handled properly to build the new */
+/*      capacity vector. *\/ */
+
+/*   IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); */
+/*   IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges*2); */
+/*   IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges*4)); */
+/*   IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); */
+/*   IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges*4)); */
+/*   for (i=0; i<no_of_edges; i++) { */
+/*     VECTOR(edges)[no_of_edges*2+i*2] = VECTOR(edges)[i*2+1]; */
+/*     VECTOR(edges)[no_of_edges*2+i*2+1] = VECTOR(edges)[i*2]; */
+/*     VECTOR(newcapacity)[i] = VECTOR(newcapacity)[no_of_edges+i] =  */
+/*       capacity ? VECTOR(*capacity)[i] : 1.0 ; */
+/*   } */
+
+/*   IGRAPH_CHECK(igraph_create(&newgraph, &edges, no_of_nodes, IGRAPH_DIRECTED)); */
+/*   IGRAPH_FINALLY(igraph_destroy, &newgraph); */
+
+/*   IGRAPH_CHECK(igraph_mincut_value(&newgraph, res, &newcapacity)); */
+
+/*   igraph_destroy(&newgraph); */
+/*   igraph_vector_destroy(&edges); */
+/*   igraph_vector_destroy(&newcapacity); */
+/*   IGRAPH_FINALLY_CLEAN(3); */
+
+/*   return 0; */
+/* } */
+
+/*
+ * This is the Stoer-Wagner algorithm, it works for calculating the
+ * minimum cut for undirected graphs, for the whole graph.
+ * I.e. this is basically the edge-connectivity of the graph.
+ * It can also calculate the cut itself, not just the cut value.
+ */
+
+int igraph_i_mincut_undirected(const igraph_t *graph,
+                               igraph_real_t *res,
+                               igraph_vector_t *partition,
+                               igraph_vector_t *partition2,
+                               igraph_vector_t *cut,
+                               const igraph_vector_t *capacity) {
+
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph);
+
+    igraph_i_cutheap_t heap;
+    igraph_real_t mincut = IGRAPH_INFINITY; /* infinity */
+    long int i;
+
+    igraph_adjlist_t adjlist;
+    igraph_inclist_t inclist;
+
+    igraph_vector_t mergehist;
+    igraph_bool_t calc_cut = partition || partition2 || cut;
+    long int act_step = 0, mincut_step = 0;
+
+    if (capacity && igraph_vector_size(capacity) != no_of_edges) {
+        IGRAPH_ERROR("Invalid capacity vector size", IGRAPH_EINVAL);
+    }
+
+    /* Check if the graph is connected at all */
+    {
+        igraph_vector_t memb, csize;
+        igraph_integer_t no;
+        IGRAPH_VECTOR_INIT_FINALLY(&memb, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&csize, 0);
+        IGRAPH_CHECK(igraph_clusters(graph, &memb, &csize, &no,
+                                     /*mode=*/ IGRAPH_WEAK));
+        if (no != 1) {
+            if (res) {
+                *res = 0;
+            }
+            if (cut) {
+                igraph_vector_clear(cut);
+            }
+            if (partition) {
+                int j = 0;
+                IGRAPH_CHECK(igraph_vector_resize(partition,
+                                                  (long int) VECTOR(csize)[0]));
+                for (i = 0; i < no_of_nodes; i++) {
+                    if (VECTOR(memb)[i] == 0) {
+                        VECTOR(*partition)[j++] = i;
+                    }
+                }
+            }
+            if (partition2) {
+                int j = 0;
+                IGRAPH_CHECK(igraph_vector_resize(partition2, no_of_nodes -
+                                                  (long int) VECTOR(csize)[0]));
+                for (i = 0; i < no_of_nodes; i++) {
+                    if (VECTOR(memb)[i] != 0) {
+                        VECTOR(*partition2)[j++] = i;
+                    }
+                }
+            }
+        }
+        igraph_vector_destroy(&csize);
+        igraph_vector_destroy(&memb);
+        IGRAPH_FINALLY_CLEAN(2);
+
+        if (no != 1) {
+            return 0;
+        }
+    }
+
+    if (calc_cut) {
+        IGRAPH_VECTOR_INIT_FINALLY(&mergehist, 0);
+        IGRAPH_CHECK(igraph_vector_reserve(&mergehist, no_of_nodes * 2));
+    }
+
+    IGRAPH_CHECK(igraph_i_cutheap_init(&heap, no_of_nodes));
+    IGRAPH_FINALLY(igraph_i_cutheap_destroy, &heap);
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    while (igraph_i_cutheap_size(&heap) >= 2) {
+
+        long int last;
+        igraph_real_t acut;
+        long int a, n;
+
+        igraph_vector_int_t *edges, *edges2;
+        igraph_vector_int_t *neis, *neis2;
+
+        do {
+            a = igraph_i_cutheap_popmax(&heap);
+
+            /* update the weights of the active vertices connected to a */
+            edges = igraph_inclist_get(&inclist, a);
+            neis = igraph_adjlist_get(&adjlist, a);
+            n = igraph_vector_int_size(edges);
+            for (i = 0; i < n; i++) {
+                igraph_integer_t edge = (igraph_integer_t) VECTOR(*edges)[i];
+                igraph_integer_t to = (igraph_integer_t) VECTOR(*neis)[i];
+                igraph_real_t weight = capacity ? VECTOR(*capacity)[(long int)edge] : 1.0;
+                igraph_i_cutheap_update(&heap, to, weight);
+            }
+
+        } while (igraph_i_cutheap_active_size(&heap) > 1);
+
+        /* Now, there is only one active vertex left,
+           calculate the cut of the phase */
+        acut = igraph_i_cutheap_maxvalue(&heap);
+        last = igraph_i_cutheap_popmax(&heap);
+
+        if (acut < mincut) {
+            mincut = acut;
+            mincut_step = act_step;
+        }
+
+        if (mincut == 0) {
+            break;
+        }
+
+        /* And contract the last and the remaining vertex (a and last) */
+        /* Before actually doing that, make some notes */
+        act_step++;
+        if (calc_cut) {
+            IGRAPH_CHECK(igraph_vector_push_back(&mergehist, a));
+            IGRAPH_CHECK(igraph_vector_push_back(&mergehist, last));
+        }
+        /* First remove the a--last edge if there is one, a is still the
+           last deactivated vertex */
+        edges = igraph_inclist_get(&inclist, a);
+        neis = igraph_adjlist_get(&adjlist, a);
+        n = igraph_vector_int_size(edges);
+        for (i = 0; i < n; ) {
+            if (VECTOR(*neis)[i] == last) {
+                VECTOR(*neis)[i] = VECTOR(*neis)[n - 1];
+                VECTOR(*edges)[i] = VECTOR(*edges)[n - 1];
+                igraph_vector_int_pop_back(neis);
+                igraph_vector_int_pop_back(edges);
+                n--;
+            } else {
+                i++;
+            }
+        }
+
+        edges = igraph_inclist_get(&inclist, last);
+        neis = igraph_adjlist_get(&adjlist, last);
+        n = igraph_vector_int_size(edges);
+        for (i = 0; i < n; ) {
+            if (VECTOR(*neis)[i] == a) {
+                VECTOR(*neis)[i] = VECTOR(*neis)[n - 1];
+                VECTOR(*edges)[i] = VECTOR(*edges)[n - 1];
+                igraph_vector_int_pop_back(neis);
+                igraph_vector_int_pop_back(edges);
+                n--;
+            } else {
+                i++;
+            }
+        }
+
+        /* Now rewrite the edge lists of last's neighbors */
+        neis = igraph_adjlist_get(&adjlist, last);
+        n = igraph_vector_int_size(neis);
+        for (i = 0; i < n; i++) {
+            igraph_integer_t nei = (igraph_integer_t) VECTOR(*neis)[i];
+            long int n2, j;
+            neis2 = igraph_adjlist_get(&adjlist, nei);
+            n2 = igraph_vector_int_size(neis2);
+            for (j = 0; j < n2; j++) {
+                if (VECTOR(*neis2)[j] == last) {
+                    VECTOR(*neis2)[j] = a;
+                }
+            }
+        }
+
+        /* And append the lists of last to the lists of a */
+        edges = igraph_inclist_get(&inclist, a);
+        neis = igraph_adjlist_get(&adjlist, a);
+        edges2 = igraph_inclist_get(&inclist, last);
+        neis2 = igraph_adjlist_get(&adjlist, last);
+        IGRAPH_CHECK(igraph_vector_int_append(edges, edges2));
+        IGRAPH_CHECK(igraph_vector_int_append(neis, neis2));
+        igraph_vector_int_clear(edges2); /* TODO: free it */
+        igraph_vector_int_clear(neis2);  /* TODO: free it */
+
+        /* Remove the deleted vertex from the heap entirely */
+        igraph_i_cutheap_reset_undefine(&heap, last);
+    }
+
+    *res = mincut;
+
+    igraph_inclist_destroy(&inclist);
+    igraph_adjlist_destroy(&adjlist);
+    igraph_i_cutheap_destroy(&heap);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (calc_cut) {
+        long int bignode = (long int) VECTOR(mergehist)[2 * mincut_step + 1];
+        long int i, idx;
+        long int size = 1;
+        char *mark;
+        mark = igraph_Calloc(no_of_nodes, char);
+        if (!mark) {
+            IGRAPH_ERROR("Not enough memory for minimum cut", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, mark);
+
+        /* first count the vertices in the partition */
+        mark[bignode] = 1;
+        for (i = mincut_step - 1; i >= 0; i--) {
+            if ( mark[ (long int) VECTOR(mergehist)[2 * i] ] ) {
+                size++;
+                mark [ (long int) VECTOR(mergehist)[2 * i + 1] ] = 1;
+            }
+        }
+
+        /* now store them, if requested */
+        if (partition) {
+            IGRAPH_CHECK(igraph_vector_resize(partition, size));
+            idx = 0;
+            VECTOR(*partition)[idx++] = bignode;
+            for (i = mincut_step - 1; i >= 0; i--) {
+                if (mark[ (long int) VECTOR(mergehist)[2 * i] ]) {
+                    VECTOR(*partition)[idx++] = VECTOR(mergehist)[2 * i + 1];
+                }
+            }
+        }
+
+        /* The other partition too? */
+        if (partition2) {
+            IGRAPH_CHECK(igraph_vector_resize(partition2, no_of_nodes - size));
+            idx = 0;
+            for (i = 0; i < no_of_nodes; i++) {
+                if (!mark[i]) {
+                    VECTOR(*partition2)[idx++] = i;
+                }
+            }
+        }
+
+        /* The edges in the cut are also requested? */
+        /* We want as few memory allocated for 'cut' as possible,
+           so we first collect the edges in mergehist, we don't
+           need that anymore. Then we copy it to 'cut';  */
+        if (cut) {
+            igraph_integer_t from, to;
+            igraph_vector_clear(&mergehist);
+            for (i = 0; i < no_of_edges; i++) {
+                igraph_edge(graph, (igraph_integer_t) i, &from, &to);
+                if ((mark[(long int)from] && !mark[(long int)to]) ||
+                    (mark[(long int)to] && !mark[(long int)from])) {
+                    IGRAPH_CHECK(igraph_vector_push_back(&mergehist, i));
+                }
+            }
+            igraph_vector_clear(cut);
+            IGRAPH_CHECK(igraph_vector_append(cut, &mergehist));
+        }
+
+        igraph_free(mark);
+        igraph_vector_destroy(&mergehist);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    return 0;
+}
+
+int igraph_i_mincut_directed(const igraph_t *graph,
+                             igraph_real_t *value,
+                             igraph_vector_t *partition,
+                             igraph_vector_t *partition2,
+                             igraph_vector_t *cut,
+                             const igraph_vector_t *capacity) {
+    long int i;
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_real_t flow;
+    igraph_real_t minmaxflow = IGRAPH_INFINITY;
+    igraph_vector_t mypartition, mypartition2, mycut;
+    igraph_vector_t *ppartition = 0, *ppartition2 = 0, *pcut = 0;
+    igraph_vector_t bestpartition, bestpartition2, bestcut;
+
+    if (partition) {
+        IGRAPH_VECTOR_INIT_FINALLY(&bestpartition, 0);
+    }
+    if (partition2) {
+        IGRAPH_VECTOR_INIT_FINALLY(&bestpartition2, 0);
+    }
+    if (cut) {
+        IGRAPH_VECTOR_INIT_FINALLY(&bestcut, 0);
+    }
+
+    if (partition) {
+        IGRAPH_VECTOR_INIT_FINALLY(&mypartition, 0);
+        ppartition = &mypartition;
+    }
+    if (partition2) {
+        IGRAPH_VECTOR_INIT_FINALLY(&mypartition2, 0);
+        ppartition2 = &mypartition2;
+    }
+    if (cut) {
+        IGRAPH_VECTOR_INIT_FINALLY(&mycut, 0);
+        pcut = &mycut;
+    }
+
+    for (i = 1; i < no_of_nodes; i++) {
+        IGRAPH_CHECK(igraph_maxflow(graph, /*value=*/ &flow, /*flow=*/ 0,
+                                    pcut, ppartition, ppartition2, /*source=*/ 0,
+                                    /*target=*/ (igraph_integer_t) i, capacity, 0));
+        if (flow < minmaxflow) {
+            minmaxflow = flow;
+            if (cut) {
+                IGRAPH_CHECK(igraph_vector_update(&bestcut, &mycut));
+            }
+            if (partition) {
+                IGRAPH_CHECK(igraph_vector_update(&bestpartition, &mypartition));
+            }
+            if (partition2) {
+                IGRAPH_CHECK(igraph_vector_update(&bestpartition2, &mypartition2));
+            }
+
+            if (minmaxflow == 0) {
+                break;
+            }
+        }
+        IGRAPH_CHECK(igraph_maxflow(graph, /*value=*/ &flow, /*flow=*/ 0,
+                                    pcut, ppartition, ppartition2,
+                                    /*source=*/ (igraph_integer_t) i,
+                                    /*target=*/ 0, capacity, 0));
+        if (flow < minmaxflow) {
+            minmaxflow = flow;
+            if (cut) {
+                IGRAPH_CHECK(igraph_vector_update(&bestcut, &mycut));
+            }
+            if (partition) {
+                IGRAPH_CHECK(igraph_vector_update(&bestpartition, &mypartition));
+            }
+            if (partition2) {
+                IGRAPH_CHECK(igraph_vector_update(&bestpartition2, &mypartition2));
+            }
+
+            if (minmaxflow == 0) {
+                break;
+            }
+        }
+    }
+
+    if (value) {
+        *value = minmaxflow;
+    }
+
+    if (cut) {
+        igraph_vector_destroy(&mycut);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (partition) {
+        igraph_vector_destroy(&mypartition);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (partition2) {
+        igraph_vector_destroy(&mypartition2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (cut) {
+        IGRAPH_CHECK(igraph_vector_update(cut, &bestcut));
+        igraph_vector_destroy(&bestcut);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (partition2) {
+        IGRAPH_CHECK(igraph_vector_update(partition2, &bestpartition2));
+        igraph_vector_destroy(&bestpartition2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (partition) {
+        IGRAPH_CHECK(igraph_vector_update(partition, &bestpartition));
+        igraph_vector_destroy(&bestpartition);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_mincut
+ * \brief Calculates the minimum cut in a graph.
+ *
+ * This function calculates the minimum cut in a graph.
+ * The minimum cut is the minimum set of edges which needs to be
+ * removed to disconnect the graph. The minimum is calculated using
+ * the weights (\p capacity) of the edges, so the cut with the minimum
+ * total capacity is calculated.
+ *
+ * </para><para> For directed graphs an implementation based on
+ * calculating 2|V|-2 maximum flows is used.
+ * For undirected graphs we use the Stoer-Wagner
+ * algorithm, as described in M. Stoer and F. Wagner: A simple min-cut
+ * algorithm, Journal of the ACM, 44 585-591, 1997.
+ *
+ * </para><para>
+ * The first implementation of the actual cut calculation for
+ * undirected graphs was made by Gregory Benison, thanks Greg.
+ * \param graph The input graph.
+ * \param value Pointer to a float, the value of the cut will be
+ *    stored here.
+ * \param partition Pointer to an initialized vector, the ids
+ *    of the vertices in the first partition after separating the
+ *    graph will be stored here. The vector will be resized as
+ *    needed. This argument is ignored if it is a NULL pointer.
+ * \param partition2 Pointer to an initialized vector the ids
+ *    of the vertices in the second partition will be stored here.
+ *    The vector will be resized as needed. This argument is ignored
+ *    if it is a NULL pointer.
+ * \param cut Pointer to an initialized vector, the ids of the edges
+ *    in the cut will be stored here. This argument is ignored if it
+ *    is a NULL pointer.
+ * \param capacity A numeric vector giving the capacities of the
+ *    edges. If a null pointer then all edges have unit capacity.
+ * \return Error code.
+ *
+ * \sa \ref igraph_mincut_value(), a simpler interface for calculating
+ * the value of the cut only.
+ *
+ * Time complexity: for directed graphs it is O(|V|^4), but see the
+ * remarks at \ref igraph_maxflow(). For undirected graphs it is
+ * O(|V||E|+|V|^2 log|V|). |V| and |E| are the number of vertices and
+ * edges respectively.
+ *
+ * \example examples/simple/igraph_mincut.c
+ */
+
+int igraph_mincut(const igraph_t *graph,
+                  igraph_real_t *value,
+                  igraph_vector_t *partition,
+                  igraph_vector_t *partition2,
+                  igraph_vector_t *cut,
+                  const igraph_vector_t *capacity) {
+
+    if (igraph_is_directed(graph)) {
+        if (partition || partition2 || cut) {
+            igraph_i_mincut_directed(graph, value, partition, partition2, cut,
+                                     capacity);
+        } else {
+            return igraph_mincut_value(graph, value, capacity);
+        }
+    } else {
+        IGRAPH_CHECK(igraph_i_mincut_undirected(graph, value, partition,
+                                                partition2, cut, capacity));
+        return IGRAPH_SUCCESS;
+    }
+
+    return 0;
+}
+
+
+int igraph_i_mincut_value_undirected(const igraph_t *graph,
+                                     igraph_real_t *res,
+                                     const igraph_vector_t *capacity) {
+    return igraph_i_mincut_undirected(graph, res, 0, 0, 0, capacity);
+}
+
+/**
+ * \function igraph_mincut_value
+ * \brief The minimum edge cut in a graph
+ *
+ * </para><para> The minimum edge cut in a graph is the total minimum
+ * weight of the edges needed to remove from the graph to make the
+ * graph \em not strongly connected. (If the original graph is not
+ * strongly connected then this is zero.) Note that in undirected
+ * graphs strong connectedness is the same as weak connectedness. </para>
+ *
+ * <para> The minimum cut can be calculated with maximum flow
+ * techniques, although the current implementation does this only for
+ * directed graphs and a separate non-flow based implementation is
+ * used for undirected graphs. See Mechthild Stoer and Frank Wagner: A
+ * simple min-cut algorithm, Journal of the ACM 44 585--591, 1997.
+ * For directed graphs
+ * the maximum flow is calculated between a fixed vertex and all the
+ * other vertices in the graph and this is done in both
+ * directions. Then the minimum is taken to get the minimum cut.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a real variable, the result will be stored
+ *    here.
+ * \param capacity Pointer to the capacity vector, it should contain
+ *    the same number of non-negative numbers as the number of edges in
+ *    the graph. If a null pointer then all edges will have unit capacity.
+ * \return Error code.
+ *
+ * \sa \ref igraph_mincut(), \ref igraph_maxflow_value(), \ref
+ * igraph_st_mincut_value().
+ *
+ * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and
+ * O(|V|^4) for directed graphs, but see also the discussion at the
+ * documentation of \ref igraph_maxflow_value().
+ */
+
+int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res,
+                        const igraph_vector_t *capacity) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_real_t minmaxflow, flow;
+    long int i;
+
+    minmaxflow = IGRAPH_INFINITY;
+
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_CHECK(igraph_i_mincut_value_undirected(graph, res, capacity));
+        return 0;
+    }
+
+    for (i = 1; i < no_of_nodes; i++) {
+        IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, 0, (igraph_integer_t) i,
+                                          capacity, 0));
+        if (flow < minmaxflow) {
+            minmaxflow = flow;
+            if (flow == 0) {
+                break;
+            }
+        }
+        IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, (igraph_integer_t) i, 0,
+                                          capacity, 0));
+        if (flow < minmaxflow) {
+            minmaxflow = flow;
+            if (flow == 0) {
+                break;
+            }
+        }
+    }
+
+    if (res) {
+        *res = minmaxflow;
+    }
+
+    return 0;
+}
+
+int igraph_i_st_vertex_connectivity_directed(const igraph_t *graph,
+        igraph_integer_t *res,
+        igraph_integer_t source,
+        igraph_integer_t target,
+        igraph_vconn_nei_t neighbors) {
+
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph);
+    igraph_vector_t edges;
+    igraph_real_t real_res;
+    igraph_t newgraph;
+    long int i;
+    igraph_bool_t conn1;
+
+    if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL);
+    }
+
+    switch (neighbors) {
+    case IGRAPH_VCONN_NEI_ERROR:
+        IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1));
+        if (conn1) {
+            IGRAPH_ERROR("vertices connected", IGRAPH_EINVAL);
+            return 0;
+        }
+        break;
+    case IGRAPH_VCONN_NEI_NEGATIVE:
+        IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1));
+        if (conn1) {
+            *res = -1;
+            return 0;
+        }
+        break;
+    case IGRAPH_VCONN_NEI_NUMBER_OF_NODES:
+        IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1));
+        if (conn1) {
+            *res = no_of_nodes;
+            return 0;
+        }
+        break;
+    case IGRAPH_VCONN_NEI_IGNORE:
+        break;
+    default:
+        IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'", IGRAPH_EINVAL);
+        break;
+    }
+
+    /* Create the new graph */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * (no_of_edges + no_of_nodes)));
+    IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
+    IGRAPH_CHECK(igraph_vector_resize(&edges, 2 * (no_of_edges + no_of_nodes)));
+
+    for (i = 0; i < 2 * no_of_edges; i += 2) {
+        igraph_integer_t to = (igraph_integer_t) VECTOR(edges)[i + 1];
+        if (to != source && to != target) {
+            VECTOR(edges)[i + 1] = no_of_nodes + to;
+        }
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(edges)[ 2 * (no_of_edges + i)   ] = no_of_nodes + i;
+        VECTOR(edges)[ 2 * (no_of_edges + i) + 1 ] = i;
+    }
+
+    IGRAPH_CHECK(igraph_create(&newgraph, &edges, 2 * no_of_nodes,
+                               igraph_is_directed(graph)));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_destroy, &newgraph);
+
+    /* Do the maximum flow */
+
+    no_of_nodes = igraph_vcount(&newgraph);
+    no_of_edges = igraph_ecount(&newgraph);
+
+    IGRAPH_CHECK(igraph_maxflow_value(&newgraph, &real_res,
+                                      source, target, 0, 0));
+    *res = (igraph_integer_t)real_res;
+
+    igraph_destroy(&newgraph);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_st_vertex_connectivity_undirected(const igraph_t *graph,
+        igraph_integer_t *res,
+        igraph_integer_t source,
+        igraph_integer_t target,
+        igraph_vconn_nei_t neighbors) {
+
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    igraph_t newgraph;
+    igraph_bool_t conn;
+
+    if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL);
+    }
+
+    switch (neighbors) {
+    case IGRAPH_VCONN_NEI_ERROR:
+        IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn));
+        if (conn) {
+            IGRAPH_ERROR("vertices connected", IGRAPH_EINVAL);
+            return 0;
+        }
+        break;
+    case IGRAPH_VCONN_NEI_NEGATIVE:
+        IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn));
+        if (conn) {
+            *res = -1;
+            return 0;
+        }
+        break;
+    case IGRAPH_VCONN_NEI_NUMBER_OF_NODES:
+        IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn));
+        if (conn) {
+            *res = no_of_nodes;
+            return 0;
+        }
+        break;
+    case IGRAPH_VCONN_NEI_IGNORE:
+        break;
+    default:
+        IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'", IGRAPH_EINVAL);
+        break;
+    }
+
+    IGRAPH_CHECK(igraph_copy(&newgraph, graph));
+    IGRAPH_FINALLY(igraph_destroy, &newgraph);
+    IGRAPH_CHECK(igraph_to_directed(&newgraph, IGRAPH_TO_DIRECTED_MUTUAL));
+
+    IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(&newgraph, res,
+                 source, target,
+                 IGRAPH_VCONN_NEI_IGNORE));
+
+    igraph_destroy(&newgraph);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_st_vertex_connectivity
+ * \brief The vertex connectivity of a pair of vertices
+ *
+ * </para><para>The vertex connectivity of two vertices (\c source and
+ * \c target) is the minimum number of vertices that have to be
+ * deleted to eliminate all paths from \c source to \c
+ * target. Directed paths are considered in directed graphs.</para>
+ *
+ * <para>The vertex connectivity of a pair is the same as the number
+ * of different (ie. node-independent) paths from source to
+ * target.</para>
+ *
+ * <para>The current implementation uses maximum flow calculations to
+ * obtain the result.
+ * \param graph The input graph.
+ * \param res Pointer to an integer, the result will be stored here.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \param neighbors A constant giving what to do if the two vertices
+ *     are connected. Possible values:
+ *     \c IGRAPH_VCONN_NEI_ERROR, stop with an error message,
+ *     \c IGRAPH_VCONN_NEGATIVE, return -1.
+ *     \c IGRAPH_VCONN_NUMBER_OF_NODES, return the number of nodes.
+ *     \c IGRAPH_VCONN_IGNORE, ignore the fact that the two vertices
+ *        are connected and calculated the number of vertices needed
+ *        to eliminate all paths except for the trivial (direct) paths
+ *        between \c source and \c vertex. TOOD: what about neighbors?
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^3), but see the discussion at \ref
+ * igraph_maxflow_value().
+ *
+ * \sa \ref igraph_vertex_connectivity(),
+ * \ref igraph_edge_connectivity(),
+ * \ref igraph_maxflow_value().
+ */
+
+int igraph_st_vertex_connectivity(const igraph_t *graph,
+                                  igraph_integer_t *res,
+                                  igraph_integer_t source,
+                                  igraph_integer_t target,
+                                  igraph_vconn_nei_t neighbors) {
+
+    if (source == target) {
+        IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL);
+    }
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(graph, res,
+                     source, target,
+                     neighbors));
+    } else {
+        IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(graph, res,
+                     source, target,
+                     neighbors));
+    }
+
+    return 0;
+}
+
+int igraph_i_vertex_connectivity_directed(const igraph_t *graph,
+        igraph_integer_t *res) {
+
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    long int i, j;
+    igraph_integer_t minconn = no_of_nodes - 1, conn;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j < no_of_nodes; j++) {
+            if (i == j) {
+                continue;
+            }
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            IGRAPH_CHECK(igraph_st_vertex_connectivity(graph, &conn,
+                         (igraph_integer_t) i,
+                         (igraph_integer_t) j,
+                         IGRAPH_VCONN_NEI_NUMBER_OF_NODES));
+            if (conn < minconn) {
+                minconn = conn;
+                if (conn == 0) {
+                    break;
+                }
+            }
+        }
+        if (conn == 0) {
+            break;
+        }
+    }
+
+    if (res) {
+        *res = minconn;
+    }
+
+    return 0;
+}
+
+int igraph_i_vertex_connectivity_undirected(const igraph_t *graph,
+        igraph_integer_t *res) {
+    igraph_t newgraph;
+
+    IGRAPH_CHECK(igraph_copy(&newgraph, graph));
+    IGRAPH_FINALLY(igraph_destroy, &newgraph);
+    IGRAPH_CHECK(igraph_to_directed(&newgraph, IGRAPH_TO_DIRECTED_MUTUAL));
+
+    IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(&newgraph, res));
+
+    igraph_destroy(&newgraph);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */
+int igraph_i_connectivity_checks(const igraph_t *graph,
+                                 igraph_integer_t *res,
+                                 igraph_bool_t *found) {
+    igraph_bool_t conn;
+    *found = 0;
+
+    if (igraph_vcount(graph) == 0) {
+        *res = 0;
+        *found = 1;
+        return 0;
+    }
+
+    IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_STRONG));
+    if (!conn) {
+        *res = 0;
+        *found = 1;
+    } else {
+        igraph_vector_t degree;
+        IGRAPH_VECTOR_INIT_FINALLY(&degree, 0);
+        if (!igraph_is_directed(graph)) {
+            IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(),
+                                       IGRAPH_OUT, IGRAPH_LOOPS));
+            if (igraph_vector_min(&degree) == 1) {
+                *res = 1;
+                *found = 1;
+            }
+        } else {
+            /* directed, check both in- & out-degree */
+            IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(),
+                                       IGRAPH_OUT, IGRAPH_LOOPS));
+            if (igraph_vector_min(&degree) == 1) {
+                *res = 1;
+                *found = 1;
+            } else {
+                IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(),
+                                           IGRAPH_IN, IGRAPH_LOOPS));
+                if (igraph_vector_min(&degree) == 1) {
+                    *res = 1;
+                    *found = 1;
+                }
+            }
+        }
+        igraph_vector_destroy(&degree);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_vertex_connectivity
+ * The vertex connectivity of a graph
+ *
+ * </para><para> The vertex connectivity of a graph is the minimum
+ * vertex connectivity along each pairs of vertices in the graph.
+ * </para>
+ * <para> The vertex connectivity of a graph is the same as group
+ * cohesion as defined in Douglas R. White and Frank Harary: The
+ * cohesiveness of blocks in social networks: node connectivity and
+ * conditional density, Sociological Methodology 31:305--359, 2001.
+ * \param graph The input graph.
+ * \param res Pointer to an integer, the result will be stored here.
+ * \param checks Logical constant. Whether to check that the graph is
+ *    connected and also the degree of the vertices. If the graph is
+ *    not (strongly) connected then the connectivity is obviously zero. Otherwise
+ *    if the minimum degree is one then the vertex connectivity is also
+ *    one. It is a good idea to perform these checks, as they can be
+ *    done quickly compared to the connectivity calculation itself.
+ *    They were suggested by Peter McMahan, thanks Peter.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^5).
+ *
+ * \sa \ref igraph_st_vertex_connectivity(), \ref igraph_maxflow_value(),
+ * and \ref igraph_edge_connectivity().
+ */
+
+int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res,
+                               igraph_bool_t checks) {
+
+    igraph_bool_t ret = 0;
+
+    if (checks) {
+        IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret));
+    }
+
+    /* Are we done yet? */
+    if (!ret) {
+        if (igraph_is_directed(graph)) {
+            IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(graph, res));
+        } else {
+            IGRAPH_CHECK(igraph_i_vertex_connectivity_undirected(graph, res));
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_st_edge_connectivity
+ * \brief Edge connectivity of a pair of vertices
+ *
+ * </para><para> The edge connectivity of two vertices (\c source and
+ * \c target) in a graph is the minimum number of edges that
+ * have to be deleted from the graph to eliminate all paths from \c
+ * source to \c target.</para>
+ *
+ * <para>This function uses the maximum flow algorithm to calculate
+ * the edge connectivity.
+ * \param graph The input graph, it has to be directed.
+ * \param res Pointer to an integer, the result will be stored here.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^3).
+ *
+ * \sa \ref igraph_maxflow_value(), \ref igraph_edge_connectivity(),
+ * \ref igraph_st_vertex_connectivity(), \ref
+ * igraph_vertex_connectivity().
+ */
+
+int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res,
+                                igraph_integer_t source,
+                                igraph_integer_t target) {
+    igraph_real_t flow;
+
+    if (source == target) {
+        IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0));
+    *res = (igraph_integer_t) flow;
+
+    return 0;
+}
+
+
+/**
+ * \function igraph_edge_connectivity
+ * \brief The minimum edge connectivity in a graph.
+ *
+ * </para><para> This is the minimum of the edge connectivity over all
+ * pairs of vertices in the graph. </para>
+ *
+ * <para>
+ * The edge connectivity of a graph is the same as group adhesion as
+ * defined in Douglas R. White and Frank Harary: The cohesiveness of
+ * blocks in social networks: node connectivity and conditional
+ * density, Sociological Methodology 31:305--359, 2001.
+ * \param graph The input graph.
+ * \param res Pointer to an integer, the result will be stored here.
+ * \param checks Logical constant. Whether to check that the graph is
+ *    connected and also the degree of the vertices. If the graph is
+ *    not (strongly) connected then the connectivity is obviously zero. Otherwise
+ *    if the minimum degree is one then the edge connectivity is also
+ *    one. It is a good idea to perform these checks, as they can be
+ *    done quickly compared to the connectivity calculation itself.
+ *    They were suggested by Peter McMahan, thanks Peter.
+ * \return Error code.
+ *
+ * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and
+ * O(|V|^4) for directed graphs, but see also the discussion at the
+ * documentation of \ref igraph_maxflow_value().
+ *
+ * \sa \ref igraph_st_edge_connectivity(), \ref igraph_maxflow_value(),
+ * \ref igraph_vertex_connectivity().
+ */
+
+int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res,
+                             igraph_bool_t checks) {
+    igraph_bool_t ret = 0;
+    igraph_integer_t number_of_nodes = igraph_vcount(graph);
+
+    /* igraph_mincut_value returns infinity for the singleton graph,
+     * which cannot be cast to an integer. We catch this case early
+     * and postulate the edge-connectivity of this graph to be 0.
+     * This is consistent with what other software packages return. */
+    if (number_of_nodes <= 1) {
+        *res = 0;
+        return 0;
+    }
+
+    /* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */
+    if (checks) {
+        IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret));
+    }
+
+    if (!ret) {
+        igraph_real_t real_res;
+        IGRAPH_CHECK(igraph_mincut_value(graph, &real_res, 0));
+        *res = (igraph_integer_t)real_res;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_edge_disjoint_paths
+ * \brief The maximum number of edge-disjoint paths between two vertices.
+ *
+ * </para><para> A set of paths between two vertices is called
+ * edge-disjoint if they do not share any edges. The maximum number of
+ * edge-disjoint paths are calculated by this function using maximum
+ * flow techniques. Directed paths are considered in directed
+ * graphs. </para>
+ *
+ * <para> Note that the number of disjoint paths is the same as the
+ * edge connectivity of the two vertices using uniform edge weights.
+ * \param graph The input graph, can be directed or undirected.
+ * \param res Pointer to an integer variable, the result will be
+ *        stored here.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^3), but see the discussion at \ref
+ * igraph_maxflow_value().
+ *
+ * \sa \ref igraph_vertex_disjoint_paths(), \ref
+ * igraph_st_edge_connectivity(), \ref igraph_maxflow_value().
+ */
+
+int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res,
+                               igraph_integer_t source,
+                               igraph_integer_t target) {
+
+    igraph_real_t flow;
+
+    if (source == target) {
+        IGRAPH_ERROR("Not implemented for source=target", IGRAPH_UNIMPLEMENTED);
+    }
+
+    IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0));
+
+    *res = (igraph_integer_t) flow;
+
+    return 0;
+}
+
+/**
+ * \function igraph_vertex_disjoint_paths
+ * \brief Maximum number of vertex-disjoint paths between two vertices.
+ *
+ * </para><para> A set of paths between two vertices is called
+ * vertex-disjoint if they share no vertices. The calculation is
+ * performed by using maximum flow techniques. </para>
+ *
+ * <para> Note that the number of vertex-disjoint paths is the same as
+ * the vertex connectivity of the two vertices in most cases (if the
+ * two vertices are not connected by an edge).
+ * \param graph The input graph.
+ * \param res Pointer to an integer variable, the result will be
+ *        stored here.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^3).
+ *
+ * \sa \ref igraph_edge_disjoint_paths(), \ref
+ * igraph_vertex_connectivity(), \ref igraph_maxflow_value().
+ */
+
+int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res,
+                                 igraph_integer_t source,
+                                 igraph_integer_t target) {
+
+    igraph_bool_t conn;
+
+    if (source == target) {
+        IGRAPH_ERROR("The source==target case is not implemented",
+                     IGRAPH_UNIMPLEMENTED);
+    }
+
+    igraph_are_connected(graph, source, target, &conn);
+    if (conn) {
+        /* We need to remove every (possibly directed) edge between source
+           and target and calculate the disjoint paths on the new
+           graph. Finally we add 1 for the removed connection(s).  */
+        igraph_es_t es;
+        igraph_vector_t v;
+        igraph_t newgraph;
+        IGRAPH_VECTOR_INIT_FINALLY(&v, 2);
+        VECTOR(v)[0] = source;
+        VECTOR(v)[1] = target;
+        IGRAPH_CHECK(igraph_es_multipairs(&es, &v, IGRAPH_DIRECTED));
+        IGRAPH_FINALLY(igraph_es_destroy, &es);
+
+        IGRAPH_CHECK(igraph_copy(&newgraph, graph));
+        IGRAPH_FINALLY(igraph_destroy, &newgraph);
+        IGRAPH_CHECK(igraph_delete_edges(&newgraph, es));
+
+        if (igraph_is_directed(graph)) {
+            IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(&newgraph, res,
+                         source, target,
+                         IGRAPH_VCONN_NEI_IGNORE));
+        } else {
+            IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(&newgraph, res,
+                         source, target,
+                         IGRAPH_VCONN_NEI_IGNORE));
+        }
+
+        if (res) {
+            *res += 1;
+        }
+
+        IGRAPH_FINALLY_CLEAN(3);
+        igraph_destroy(&newgraph);
+        igraph_es_destroy(&es);
+        igraph_vector_destroy(&v);
+    }
+
+    /* These do nothing if the two vertices are connected,
+       so it is safe to call them. */
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(graph, res,
+                     source, target,
+                     IGRAPH_VCONN_NEI_IGNORE));
+    } else {
+        IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(graph, res,
+                     source, target,
+                     IGRAPH_VCONN_NEI_IGNORE));
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_adhesion
+ * \brief Graph adhesion, this is (almost) the same as edge connectivity.
+ *
+ * </para><para> This quantity is defined by White and Harary in
+ * The cohesiveness of blocks in social networks: node connectivity and
+ * conditional density, (Sociological Methodology 31:305--359, 2001)
+ * and basically it is the edge connectivity of the graph
+ * with uniform edge weights.
+ * \param graph The input graph, either directed or undirected.
+ * \param res Pointer to an integer, the result will be stored here.
+ * \param checks Logical constant. Whether to check that the graph is
+ *    connected and also the degree of the vertices. If the graph is
+ *    not (strongly) connected then the adhesion is obviously zero. Otherwise
+ *    if the minimum degree is one then the adhesion is also
+ *    one. It is a good idea to perform these checks, as they can be
+ *    done quickly compared to the edge connectivity calculation itself.
+ *    They were suggested by Peter McMahan, thanks Peter.
+* \return Error code.
+ *
+ * Time complexity: O(log(|V|)*|V|^2) for undirected graphs and
+ * O(|V|^4) for directed graphs, but see also the discussion at the
+ * documentation of \ref igraph_maxflow_value().
+ *
+ * \sa \ref igraph_cohesion(), \ref igraph_maxflow_value(), \ref
+ * igraph_edge_connectivity(), \ref igraph_mincut_value().
+ */
+
+int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res,
+                    igraph_bool_t checks) {
+    return igraph_edge_connectivity(graph, res, checks);
+}
+
+/**
+ * \function igraph_cohesion
+ * \brief Graph cohesion, this is the same as vertex connectivity.
+ *
+ * </para><para> This quantity was defined by White and Harary in <quote>The
+ * cohesiveness of blocks in social networks: node connectivity and
+ * conditional density</quote>, (Sociological Methodology 31:305--359, 2001)
+ * and it is the same as the vertex connectivity of a
+ * graph.
+ * \param graph The input graph.
+ * \param res Pointer to an integer variable, the result will be
+ *        stored here.
+ * \param checks Logical constant. Whether to check that the graph is
+ *    connected and also the degree of the vertices. If the graph is
+ *    not (strongly) connected then the cohesion is obviously zero. Otherwise
+ *    if the minimum degree is one then the cohesion is also
+ *    one. It is a good idea to perform these checks, as they can be
+ *    done quickly compared to the vertex connectivity calculation itself.
+ *    They were suggested by Peter McMahan, thanks Peter.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^4), |V| is the number of vertices. In
+ * practice it is more like O(|V|^2), see \ref igraph_maxflow_value().
+ *
+ * \sa \ref igraph_vertex_connectivity(), \ref igraph_adhesion(),
+ * \ref igraph_maxflow_value().
+ */
+
+int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res,
+                    igraph_bool_t checks) {
+
+    IGRAPH_CHECK(igraph_vertex_connectivity(graph, res, checks));
+    return 0;
+}
+
+/**
+ * \function igraph_gomory_hu_tree
+ * \brief Gomory-Hu tree of a graph.
+ *
+ * </para><para>
+ * The Gomory-Hu tree is a concise representation of the value of all the
+ * maximum flows (or minimum cuts) in a graph. The vertices of the tree
+ * correspond exactly to the vertices of the original graph in the same order.
+ * Edges of the Gomory-Hu tree are annotated by flow values.  The value of
+ * the maximum flow (or minimum cut) between an arbitrary (u,v) vertex
+ * pair in the original graph is then given by the minimum flow value (i.e.
+ * edge annotation) along the shortest path between u and v in the
+ * Gomory-Hu tree.
+ *
+ * </para><para>This implementation uses Gusfield's algorithm to construct the
+ * Gomory-Hu tree. See the following paper for more details:
+ *
+ * </para><para>
+ * Gusfield D: Very simple methods for all pairs network flow analysis. SIAM J
+ * Comput 19(1):143-155, 1990.
+ *
+ * \param graph The input graph.
+ * \param tree  Pointer to an uninitialized graph; the result will be
+ *              stored here.
+ * \param flows Pointer to an uninitialized vector; the flow values
+ *              corresponding to each edge in the Gomory-Hu tree will
+ *              be returned here. You may pass a NULL pointer here if you are
+ *              not interested in the flow values.
+ * \param capacity Vector containing the capacity of the edges. If NULL, then
+ *        every edge is considered to have capacity 1.0.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^4) since it performs a max-flow calculation
+ * between vertex zero and every other vertex and max-flow is
+ * O(|V|^3).
+ *
+ * \sa \ref igraph_maxflow()
+ */
+int igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree,
+                          igraph_vector_t *flows, const igraph_vector_t *capacity) {
+
+    igraph_integer_t no_of_nodes = igraph_vcount(graph);
+    igraph_integer_t source, target, mid, i, n;
+    igraph_vector_t neighbors;
+    igraph_vector_t flow_values;
+    igraph_vector_t partition;
+    igraph_vector_t partition2;
+    igraph_real_t flow_value;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Gomory-Hu tree can only be calculated for undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    /* Allocate memory */
+    IGRAPH_VECTOR_INIT_FINALLY(&neighbors, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&flow_values, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&partition, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&partition2, 0);
+
+    /* Initialize the tree: every edge points to node 0 */
+    /* Actually, this is done implicitly since both 'neighbors' and 'flow_values' are
+     * initialized to zero already */
+
+    /* For each source vertex except vertex zero... */
+    for (source = 1; source < no_of_nodes; source++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        IGRAPH_PROGRESS("Gomory-Hu tree", (100.0 * (source - 1)) / (no_of_nodes - 1), 0);
+
+        /* Find its current neighbor in the tree */
+        target = VECTOR(neighbors)[(long int)source];
+
+        /* Find the maximum flow between source and target */
+        IGRAPH_CHECK(igraph_maxflow(graph, &flow_value, 0, 0, &partition, &partition2,
+                                    source, target, capacity, 0));
+
+        /* Store the maximum flow and determine which side each node is on */
+        VECTOR(flow_values)[(long int)source] = flow_value;
+
+        /* Update the tree */
+        /* igraph_maxflow() guarantees that the source vertex will be in &partition
+         * and not in &partition2 */
+        n = igraph_vector_size(&partition);
+        for (i = 0; i < n; i++) {
+            mid = VECTOR(partition)[i];
+            if (mid > source && VECTOR(neighbors)[(long int)mid] == target) {
+                VECTOR(neighbors)[(long int)mid] = source;
+            }
+        }
+    }
+
+    IGRAPH_PROGRESS("Gomory-Hu tree", 100.0, 0);
+
+    /* Re-use the 'partition' vector as an edge list now */
+    IGRAPH_CHECK(igraph_vector_resize(&partition, 2 * (no_of_nodes - 1)));
+    for (i = 1, mid = 0; i < no_of_nodes; i++, mid += 2) {
+        VECTOR(partition)[(long int)mid]   = i;
+        VECTOR(partition)[(long int)mid + 1] = VECTOR(neighbors)[(long int)i];
+    }
+
+    /* Create the tree graph; we use igraph_subgraph_edges here to keep the
+     * graph and vertex attributes */
+    IGRAPH_CHECK(igraph_subgraph_edges(graph, tree, igraph_ess_none(), 0));
+    IGRAPH_CHECK(igraph_add_edges(tree, &partition, 0));
+
+    /* Free the allocated memory */
+    igraph_vector_destroy(&partition2);
+    igraph_vector_destroy(&partition);
+    igraph_vector_destroy(&neighbors);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    /* Return the flow values to the caller */
+    if (flows != 0) {
+        IGRAPH_CHECK(igraph_vector_update(flows, &flow_values));
+        if (no_of_nodes > 0) {
+            igraph_vector_remove(flows, 0);
+        }
+    }
+
+    /* Free the remaining allocated memory */
+    igraph_vector_destroy(&flow_values);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
diff --git a/igraph/src/fmt.c b/igraph/src/fmt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/fmt.c
@@ -0,0 +1,530 @@
+#include "f2c.h"
+#include "fio.h"
+#include "fmt.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define skip(s) while(*s==' ') s++
+#ifdef interdata
+#define SYLMX 300
+#endif
+#ifdef pdp11
+#define SYLMX 300
+#endif
+#ifdef vax
+#define SYLMX 300
+#endif
+#ifndef SYLMX
+#define SYLMX 300
+#endif
+#define GLITCH '\2'
+	/* special quote character for stu */
+extern flag f__cblank,f__cplus;	/*blanks in I and compulsory plus*/
+static struct syl f__syl[SYLMX];
+int f__parenlvl,f__pc,f__revloc;
+#ifdef KR_headers
+#define Const /*nothing*/
+#else
+#define Const const
+#endif
+
+ static
+#ifdef KR_headers
+char *ap_end(s) char *s;
+#else
+const char *ap_end(const char *s)
+#endif
+{	char quote;
+	quote= *s++;
+	for(;*s;s++)
+	{	if(*s!=quote) continue;
+		if(*++s!=quote) return(s);
+	}
+	if(f__elist->cierr) {
+		errno = 100;
+		return(NULL);
+	}
+	f__fatal(100, "bad string");
+	/*NOTREACHED*/ return 0;
+}
+ static int
+#ifdef KR_headers
+op_gen(a,b,c,d)
+#else
+op_gen(int a, int b, int c, int d)
+#endif
+{	struct syl *p= &f__syl[f__pc];
+	if(f__pc>=SYLMX)
+	{	fprintf(stderr,"format too complicated:\n");
+		sig_die(f__fmtbuf, 1);
+	}
+	p->op=a;
+	p->p1=b;
+	p->p2.i[0]=c;
+	p->p2.i[1]=d;
+	return(f__pc++);
+}
+#ifdef KR_headers
+static char *f_list();
+static char *gt_num(s,n,n1) char *s; int *n, n1;
+#else
+static const char *f_list(const char*);
+static const char *gt_num(const char *s, int *n, int n1)
+#endif
+{	int m=0,f__cnt=0;
+	char c;
+	for(c= *s;;c = *s)
+	{	if(c==' ')
+		{	s++;
+			continue;
+		}
+		if(c>'9' || c<'0') break;
+		m=10*m+c-'0';
+		f__cnt++;
+		s++;
+	}
+	if(f__cnt==0) {
+		if (!n1)
+			s = 0;
+		*n=n1;
+		}
+	else *n=m;
+	return(s);
+}
+
+ static
+#ifdef KR_headers
+char *f_s(s,curloc) char *s;
+#else
+const char *f_s(const char *s, int curloc)
+#endif
+{
+	skip(s);
+	if(*s++!='(')
+	{
+		return(NULL);
+	}
+	if(f__parenlvl++ ==1) f__revloc=curloc;
+	if(op_gen(RET1,curloc,0,0)<0 ||
+		(s=f_list(s))==NULL)
+	{
+		return(NULL);
+	}
+	skip(s);
+	return(s);
+}
+
+ static int
+#ifdef KR_headers
+ne_d(s,p) char *s,**p;
+#else
+ne_d(const char *s, const char **p)
+#endif
+{	int n,x,sign=0;
+	struct syl *sp;
+	switch(*s)
+	{
+	default:
+		return(0);
+	case ':': (void) op_gen(COLON,0,0,0); break;
+	case '$':
+		(void) op_gen(NONL, 0, 0, 0); break;
+	case 'B':
+	case 'b':
+		if(*++s=='z' || *s == 'Z') (void) op_gen(BZ,0,0,0);
+		else (void) op_gen(BN,0,0,0);
+		break;
+	case 'S':
+	case 's':
+		if(*(s+1)=='s' || *(s+1) == 'S')
+		{	x=SS;
+			s++;
+		}
+		else if(*(s+1)=='p' || *(s+1) == 'P')
+		{	x=SP;
+			s++;
+		}
+		else x=S;
+		(void) op_gen(x,0,0,0);
+		break;
+	case '/': (void) op_gen(SLASH,0,0,0); break;
+	case '-': sign=1;
+	case '+':	s++;	/*OUTRAGEOUS CODING TRICK*/
+	case '0': case '1': case '2': case '3': case '4':
+	case '5': case '6': case '7': case '8': case '9':
+		if (!(s=gt_num(s,&n,0))) {
+ bad:			*p = 0;
+			return 1;
+			}
+		switch(*s)
+		{
+		default:
+			return(0);
+		case 'P':
+		case 'p': if(sign) n= -n; (void) op_gen(P,n,0,0); break;
+		case 'X':
+		case 'x': (void) op_gen(X,n,0,0); break;
+		case 'H':
+		case 'h':
+			sp = &f__syl[op_gen(H,n,0,0)];
+			sp->p2.s = (char*)s + 1;
+			s+=n;
+			break;
+		}
+		break;
+	case GLITCH:
+	case '"':
+	case '\'':
+		sp = &f__syl[op_gen(APOS,0,0,0)];
+		sp->p2.s = (char*)s;
+		if((*p = ap_end(s)) == NULL)
+			return(0);
+		return(1);
+	case 'T':
+	case 't':
+		if(*(s+1)=='l' || *(s+1) == 'L')
+		{	x=TL;
+			s++;
+		}
+		else if(*(s+1)=='r'|| *(s+1) == 'R')
+		{	x=TR;
+			s++;
+		}
+		else x=T;
+		if (!(s=gt_num(s+1,&n,0)))
+			goto bad;
+		s--;
+		(void) op_gen(x,n,0,0);
+		break;
+	case 'X':
+	case 'x': (void) op_gen(X,1,0,0); break;
+	case 'P':
+	case 'p': (void) op_gen(P,1,0,0); break;
+	}
+	s++;
+	*p=s;
+	return(1);
+}
+
+ static int
+#ifdef KR_headers
+e_d(s,p) char *s,**p;
+#else
+e_d(const char *s, const char **p)
+#endif
+{	int i,im,n,w,d,e,found=0,x=0;
+	Const char *sv=s;
+	s=gt_num(s,&n,1);
+	(void) op_gen(STACK,n,0,0);
+	switch(*s++)
+	{
+	default: break;
+	case 'E':
+	case 'e':	x=1;
+	case 'G':
+	case 'g':
+		found=1;
+		if (!(s=gt_num(s,&w,0))) {
+ bad:
+			*p = 0;
+			return 1;
+			}
+		if(w==0) break;
+		if(*s=='.') {
+			if (!(s=gt_num(s+1,&d,0)))
+				goto bad;
+			}
+		else d=0;
+		if(*s!='E' && *s != 'e')
+			(void) op_gen(x==1?E:G,w,d,0);	/* default is Ew.dE2 */
+		else {
+			if (!(s=gt_num(s+1,&e,0)))
+				goto bad;
+			(void) op_gen(x==1?EE:GE,w,d,e);
+			}
+		break;
+	case 'O':
+	case 'o':
+		i = O;
+		im = OM;
+		goto finish_I;
+	case 'Z':
+	case 'z':
+		i = Z;
+		im = ZM;
+		goto finish_I;
+	case 'L':
+	case 'l':
+		found=1;
+		if (!(s=gt_num(s,&w,0)))
+			goto bad;
+		if(w==0) break;
+		(void) op_gen(L,w,0,0);
+		break;
+	case 'A':
+	case 'a':
+		found=1;
+		skip(s);
+		if(*s>='0' && *s<='9')
+		{	s=gt_num(s,&w,1);
+			if(w==0) break;
+			(void) op_gen(AW,w,0,0);
+			break;
+		}
+		(void) op_gen(A,0,0,0);
+		break;
+	case 'F':
+	case 'f':
+		if (!(s=gt_num(s,&w,0)))
+			goto bad;
+		found=1;
+		if(w==0) break;
+		if(*s=='.') {
+			if (!(s=gt_num(s+1,&d,0)))
+				goto bad;
+			}
+		else d=0;
+		(void) op_gen(F,w,d,0);
+		break;
+	case 'D':
+	case 'd':
+		found=1;
+		if (!(s=gt_num(s,&w,0)))
+			goto bad;
+		if(w==0) break;
+		if(*s=='.') {
+			if (!(s=gt_num(s+1,&d,0)))
+				goto bad;
+			}
+		else d=0;
+		(void) op_gen(D,w,d,0);
+		break;
+	case 'I':
+	case 'i':
+		i = I;
+		im = IM;
+ finish_I:
+		if (!(s=gt_num(s,&w,0)))
+			goto bad;
+		found=1;
+		if(w==0) break;
+		if(*s!='.')
+		{	(void) op_gen(i,w,0,0);
+			break;
+		}
+		if (!(s=gt_num(s+1,&d,0)))
+			goto bad;
+		(void) op_gen(im,w,d,0);
+		break;
+	}
+	if(found==0)
+	{	f__pc--; /*unSTACK*/
+		*p=sv;
+		return(0);
+	}
+	*p=s;
+	return(1);
+}
+ static
+#ifdef KR_headers
+char *i_tem(s) char *s;
+#else
+const char *i_tem(const char *s)
+#endif
+{	const char *t;
+	int n,curloc;
+	if(*s==')') return(s);
+	if(ne_d(s,&t)) return(t);
+	if(e_d(s,&t)) return(t);
+	s=gt_num(s,&n,1);
+	if((curloc=op_gen(STACK,n,0,0))<0) return(NULL);
+	return(f_s(s,curloc));
+}
+
+ static
+#ifdef KR_headers
+char *f_list(s) char *s;
+#else
+const char *f_list(const char *s)
+#endif
+{
+	for(;*s!=0;)
+	{	skip(s);
+		if((s=i_tem(s))==NULL) return(NULL);
+		skip(s);
+		if(*s==',') s++;
+		else if(*s==')')
+		{	if(--f__parenlvl==0)
+			{
+				(void) op_gen(REVERT,f__revloc,0,0);
+				return(++s);
+			}
+			(void) op_gen(GOTO,0,0,0);
+			return(++s);
+		}
+	}
+	return(NULL);
+}
+
+ int
+#ifdef KR_headers
+pars_f(s) char *s;
+#else
+pars_f(const char *s)
+#endif
+{
+	f__parenlvl=f__revloc=f__pc=0;
+	if(f_s(s,0) == NULL)
+	{
+		return(-1);
+	}
+	return(0);
+}
+#define STKSZ 10
+int f__cnt[STKSZ],f__ret[STKSZ],f__cp,f__rp;
+flag f__workdone, f__nonl;
+
+ static int
+#ifdef KR_headers
+type_f(n)
+#else
+type_f(int n)
+#endif
+{
+	switch(n)
+	{
+	default:
+		return(n);
+	case RET1:
+		return(RET1);
+	case REVERT: return(REVERT);
+	case GOTO: return(GOTO);
+	case STACK: return(STACK);
+	case X:
+	case SLASH:
+	case APOS: case H:
+	case T: case TL: case TR:
+		return(NED);
+	case F:
+	case I:
+	case IM:
+	case A: case AW:
+	case O: case OM:
+	case L:
+	case E: case EE: case D:
+	case G: case GE:
+	case Z: case ZM:
+		return(ED);
+	}
+}
+#ifdef KR_headers
+integer do_fio(number,ptr,len) ftnint *number; ftnlen len; char *ptr;
+#else
+integer do_fio(ftnint *number, char *ptr, ftnlen len)
+#endif
+{	struct syl *p;
+	int n,i;
+	for(i=0;i<*number;i++,ptr+=len)
+	{
+loop:	switch(type_f((p= &f__syl[f__pc])->op))
+	{
+	default:
+		fprintf(stderr,"unknown code in do_fio: %d\n%s\n",
+			p->op,f__fmtbuf);
+		err(f__elist->cierr,100,"do_fio");
+	case NED:
+		if((*f__doned)(p))
+		{	f__pc++;
+			goto loop;
+		}
+		f__pc++;
+		continue;
+	case ED:
+		if(f__cnt[f__cp]<=0)
+		{	f__cp--;
+			f__pc++;
+			goto loop;
+		}
+		if(ptr==NULL)
+			return((*f__doend)());
+		f__cnt[f__cp]--;
+		f__workdone=1;
+		if((n=(*f__doed)(p,ptr,len))>0)
+			errfl(f__elist->cierr,errno,"fmt");
+		if(n<0)
+			err(f__elist->ciend,(EOF),"fmt");
+		continue;
+	case STACK:
+		f__cnt[++f__cp]=p->p1;
+		f__pc++;
+		goto loop;
+	case RET1:
+		f__ret[++f__rp]=p->p1;
+		f__pc++;
+		goto loop;
+	case GOTO:
+		if(--f__cnt[f__cp]<=0)
+		{	f__cp--;
+			f__rp--;
+			f__pc++;
+			goto loop;
+		}
+		f__pc=1+f__ret[f__rp--];
+		goto loop;
+	case REVERT:
+		f__rp=f__cp=0;
+		f__pc = p->p1;
+		if(ptr==NULL)
+			return((*f__doend)());
+		if(!f__workdone) return(0);
+		if((n=(*f__dorevert)()) != 0) return(n);
+		goto loop;
+	case COLON:
+		if(ptr==NULL)
+			return((*f__doend)());
+		f__pc++;
+		goto loop;
+	case NONL:
+		f__nonl = 1;
+		f__pc++;
+		goto loop;
+	case S:
+	case SS:
+		f__cplus=0;
+		f__pc++;
+		goto loop;
+	case SP:
+		f__cplus = 1;
+		f__pc++;
+		goto loop;
+	case P:	f__scale=p->p1;
+		f__pc++;
+		goto loop;
+	case BN:
+		f__cblank=0;
+		f__pc++;
+		goto loop;
+	case BZ:
+		f__cblank=1;
+		f__pc++;
+		goto loop;
+	}
+	}
+	return(0);
+}
+
+ int
+en_fio(Void)
+{	ftnint one=1;
+	return(do_fio(&one,(char *)NULL,(ftnint)0));
+}
+
+ VOID
+fmt_bg(Void)
+{
+	f__workdone=f__cp=f__rp=f__pc=f__cursor=0;
+	f__cnt[0]=f__ret[0]=0;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/fmtlib.c b/igraph/src/fmtlib.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/fmtlib.c
@@ -0,0 +1,51 @@
+/*	@(#)fmtlib.c	1.2	*/
+#define MAXINTLENGTH 23
+
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifndef Allow_TYQUAD
+#undef longint
+#define longint long
+#undef ulongint
+#define ulongint unsigned long
+#endif
+
+#ifdef KR_headers
+char *f__icvt(value,ndigit,sign, base) longint value; int *ndigit,*sign;
+ register int base;
+#else
+char *f__icvt(longint value, int *ndigit, int *sign, int base)
+#endif
+{
+	static char buf[MAXINTLENGTH+1];
+	register int i;
+	ulongint uvalue;
+
+	if(value > 0) {
+		uvalue = value;
+		*sign = 0;
+		}
+	else if (value < 0) {
+		uvalue = -value;
+		*sign = 1;
+		}
+	else {
+		*sign = 0;
+		*ndigit = 1;
+		buf[MAXINTLENGTH-1] = '0';
+		return &buf[MAXINTLENGTH-1];
+		}
+	i = MAXINTLENGTH;
+	do {
+		buf[--i] = (uvalue%base) + '0';
+		uvalue /= base;
+		}
+		while(uvalue > 0);
+	*ndigit = MAXINTLENGTH - i;
+	return &buf[i];
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/foreign-dl-lexer.c b/igraph/src/foreign-dl-lexer.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-dl-lexer.c
@@ -0,0 +1,2232 @@
+#line 2 "foreign-dl-lexer.c"
+
+#line 4 "foreign-dl-lexer.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE igraph_dl_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = yyg->yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	yy_size_t yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via igraph_dl_yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+                          ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void igraph_dl_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void igraph_dl_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_dl_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void igraph_dl_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_dl_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_dl_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void igraph_dl_yypop_buffer_state (yyscan_t yyscanner );
+
+static void igraph_dl_yyensure_buffer_stack (yyscan_t yyscanner );
+static void igraph_dl_yy_load_buffer_state (yyscan_t yyscanner );
+static void igraph_dl_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER igraph_dl_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE igraph_dl_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_dl_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_dl_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *igraph_dl_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *igraph_dl_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void igraph_dl_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer igraph_dl_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        igraph_dl_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_dl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        igraph_dl_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_dl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+#define igraph_dl_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yyg->yytext_ptr = yy_bp; \
+	yyleng = (yy_size_t) (yy_cp - yy_bp); \
+	yyg->yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 25
+#define YY_END_OF_BUFFER 26
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[131] =
+    {   0,
+        0,    0,    0,    0,    0,    0,   18,   18,   21,   21,
+       26,   23,   22,    1,    1,    4,   23,   23,   23,   23,
+       12,   23,    1,   11,   12,   12,   14,   15,   13,   17,
+       18,   17,   16,   20,   21,   19,   22,    1,    4,    0,
+        0,    0,    0,    0,    3,   12,   12,   12,   12,   14,
+       13,   17,   18,   16,   17,   17,   20,   21,   19,    0,
+        2,    0,    0,    3,   12,   12,   16,   17,   16,    0,
+        0,    0,   12,   12,    5,    0,    0,    5,   12,    0,
+        0,   12,    0,    0,    0,    6,   12,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    7,
+        9,    0,   10,    7,    7,    9,    8,   10,    8,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        2,    2,    4,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    5,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    6,    7,    8,    9,    1,   10,   11,   10,
+       10,   10,   10,   10,   10,   10,   10,   12,    1,    1,
+       13,    1,    1,    1,   14,   15,    1,   16,   17,   18,
+       19,    1,   20,    1,    1,   21,   22,   23,   24,    1,
+        1,   25,   26,   27,   28,    1,    1,   29,    1,    1,
+        1,    1,    1,    1,    1,    1,   14,   15,    1,   16,
+
+       17,   18,   19,    1,   20,    1,    1,   21,   22,   23,
+       24,    1,    1,   25,   26,   27,   28,    1,    1,   29,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[30] =
+    {   0,
+        1,    2,    3,    3,    2,    1,    3,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int16_t yy_base[140] =
+    {   0,
+        0,   22,   44,   64,   84,   94,  104,  114,  124,  134,
+      288,  289,    4,  283,  283,    2,    1,  261,  270,   15,
+       29,  289,  280,  289,   39,   50,    0,  289,   34,    0,
+       52,   19,   64,    0,   54,   51,   74,  289,   67,  255,
+       88,  256,  265,  138,   98,  108,  118,  128,  144,    0,
+      145,    0,  151,  151,   72,  159,    0,  152,  153,  265,
+      169,  256,  260,  170,  171,  175,  171,  168,  173,  264,
+      261,  253,  184,  185,  289,  246,  246,  189,  193,  195,
+      197,  199,  205,  218,  209,  289,  210,    0,  255,  242,
+      245,  246,  248,  245,  249,  231,  228,  217,  211,  200,
+
+      184,  181,  172,  150,  138,  138,  128,  126,  106,   75,
+       66,   67,   45,   45,   36,   42,   39,   22,   26,  219,
+      211,    6,  220,  227,  228,  232,  237,  238,  242,  289,
+      247,  250,  253,  256,  259,  262,    7,    6,    0
+    } ;
+
+static yyconst flex_int16_t yy_def[140] =
+    {   0,
+      131,  131,  132,  132,  133,  133,  134,  134,  135,  135,
+      130,  130,  130,  130,  130,  130,  130,  130,  130,  130,
+      136,  130,  130,  130,  136,  136,  137,  130,  137,  138,
+      130,  138,  138,  139,  130,  139,  130,  130,  130,  130,
+      130,  130,  130,  130,  130,  136,  130,  136,  136,  137,
+      130,  138,  130,  138,  138,  138,  139,  130,  139,  130,
+      130,  130,  130,  130,  136,  136,  138,  138,  138,  130,
+      130,  130,  136,  136,  130,  130,  130,  136,  136,  130,
+      130,  136,  130,  130,  130,  130,  130,   84,  130,  130,
+      130,  130,  130,  130,  130,  130,  130,  130,  130,  130,
+
+      130,  130,  130,  130,  130,  130,  130,  130,  130,  130,
+      130,  130,  130,  130,  130,  130,  130,  130,  130,  130,
+      130,  130,  130,  130,  130,  130,  130,  130,  130,    0,
+      130,  130,  130,  130,  130,  130,  130,  130,  130
+    } ;
+
+static yyconst flex_int16_t yy_nxt[319] =
+    {   0,
+       57,   13,   14,   15,   13,   37,   52,   50,   37,   16,
+       16,   39,   39,  130,   40,   17,   44,   18,  130,   44,
+       19,   41,   20,   13,   14,   15,   13,   45,   54,   54,
+       47,   16,   16,   47,  127,   51,  123,   17,   51,   18,
+       47,  122,   19,   47,   20,   22,   14,   23,   24,  121,
+       24,   47,   48,   53,   47,   58,   53,  120,   58,   25,
+       59,   59,  119,   49,   26,   22,   14,   23,   24,  118,
+       24,  117,   55,   54,   54,   37,   39,   39,   37,   25,
+       56,   67,   67,  116,   26,   28,   14,   23,   28,   61,
+       22,  115,   61,   29,   29,   28,   14,   23,   28,   64,
+
+       22,  114,   64,   29,   29,   31,   14,   23,   31,   47,
+       22,   32,   47,   33,   33,   31,   14,   23,   31,   47,
+       22,   32,   47,   33,   33,   35,   14,   23,   35,   47,
+       22,  113,   47,   36,   36,   35,   14,   23,   35,   44,
+       22,  112,   44,   36,   36,   47,   51,  111,   47,   51,
+       45,  110,   53,   58,   65,   53,   58,  109,   66,   55,
+       54,   54,   59,   59,   68,  108,   68,   56,   69,   69,
+       61,   64,   47,   61,   64,   47,   47,   69,   69,   47,
+       67,   67,   69,   69,   73,   47,   47,   56,   47,   47,
+       47,   74,  107,   47,   47,   78,   83,   47,   85,   83,
+
+       87,   85,  106,   87,  105,   79,   83,   84,   86,   83,
+       85,   87,  126,   85,   87,  126,  104,   84,   82,   88,
+      124,  128,   88,  124,  128,   92,   92,  103,  124,  124,
+      125,  124,  124,  126,   89,   90,  126,  102,  129,  128,
+       91,  129,  128,  129,  101,  100,  129,   12,   12,   12,
+       21,   21,   21,   27,   27,   27,   30,   30,   30,   34,
+       34,   34,   46,   46,   99,   98,   97,   96,   95,   94,
+       93,   81,   80,   77,   76,   75,   72,   71,   70,   63,
+       62,   60,   38,   43,   42,   38,   38,  130,   11,  130,
+      130,  130,  130,  130,  130,  130,  130,  130,  130,  130,
+
+      130,  130,  130,  130,  130,  130,  130,  130,  130,  130,
+      130,  130,  130,  130,  130,  130,  130,  130
+    } ;
+
+static yyconst flex_int16_t yy_chk[319] =
+    {   0,
+      139,    1,    1,    1,    1,   13,  138,  137,   13,    1,
+        1,   16,   16,    0,   17,    1,   20,    1,    0,   20,
+        1,   17,    1,    2,    2,    2,    2,   20,   32,   32,
+       21,    2,    2,   21,  122,   29,  119,    2,   29,    2,
+       25,  118,    2,   25,    2,    3,    3,    3,    3,  117,
+        3,   26,   25,   31,   26,   35,   31,  116,   35,    3,
+       36,   36,  115,   26,    3,    4,    4,    4,    4,  114,
+        4,  113,   33,   33,   33,   37,   39,   39,   37,    4,
+       33,   55,   55,  112,    4,    5,    5,    5,    5,   41,
+        5,  111,   41,    5,    5,    6,    6,    6,    6,   45,
+
+        6,  110,   45,    6,    6,    7,    7,    7,    7,   46,
+        7,    7,   46,    7,    7,    8,    8,    8,    8,   47,
+        8,    8,   47,    8,    8,    9,    9,    9,    9,   48,
+        9,  109,   48,    9,    9,   10,   10,   10,   10,   44,
+       10,  108,   44,   10,   10,   49,   51,  107,   49,   51,
+       44,  106,   53,   58,   48,   53,   58,  105,   49,   54,
+       54,   54,   59,   59,   56,  104,   56,   54,   56,   56,
+       61,   64,   65,   61,   64,   65,   66,   68,   68,   66,
+       67,   67,   69,   69,   65,   73,   74,   67,   73,   74,
+       78,   66,  103,   78,   79,   73,   80,   79,   81,   80,
+
+       82,   81,  102,   82,  101,   74,   83,   80,   81,   83,
+       85,   87,  121,   85,   87,  121,  100,   83,   79,   84,
+      120,  123,   84,  120,  123,   85,   87,   99,  124,  125,
+      120,  124,  125,  126,   84,   84,  126,   98,  127,  128,
+       84,  127,  128,  129,   97,   96,  129,  131,  131,  131,
+      132,  132,  132,  133,  133,  133,  134,  134,  134,  135,
+      135,  135,  136,  136,   95,   94,   93,   92,   91,   90,
+       89,   77,   76,   72,   71,   70,   63,   62,   60,   43,
+       42,   40,   23,   19,   18,   15,   14,   11,  130,  130,
+      130,  130,  130,  130,  130,  130,  130,  130,  130,  130,
+
+      130,  130,  130,  130,  130,  130,  130,  130,  130,  130,
+      130,  130,  130,  130,  130,  130,  130,  130
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "../../src/foreign-dl-lexer.l"
+/* 
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+#line 24 "../../src/foreign-dl-lexer.l"
+
+/* 
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include "foreign-dl-header.h"
+#include "foreign-dl-parser.h"
+#define YY_EXTRA_TYPE igraph_i_dl_parsedata_t*
+#define YY_USER_ACTION yylloc->first_line = yylineno;
+/* We assume that 'file' is 'stderr' here. */
+#ifdef USING_R
+#define fprintf(file, msg, ...) (1)
+#endif
+#ifdef stdout 
+#  undef stdout
+#endif
+#define stdout 0
+#define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \
+				__LINE__, IGRAPH_PARSEERROR);
+#define YY_NO_INPUT 1
+
+#line 606 "foreign-dl-lexer.c"
+
+#define INITIAL 0
+#define LABELM 1
+#define FULLMATRIX 2
+#define EDGELIST 3
+#define NODELIST 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+    {
+
+    /* User-defined. Not touched by flex. */
+    YY_EXTRA_TYPE yyextra_r;
+
+    /* The rest are the same as the globals declared in the non-reentrant scanner. */
+    FILE *yyin_r, *yyout_r;
+    size_t yy_buffer_stack_top; /**< index of top of stack. */
+    size_t yy_buffer_stack_max; /**< capacity of stack. */
+    YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+    char yy_hold_char;
+    yy_size_t yy_n_chars;
+    yy_size_t yyleng_r;
+    char *yy_c_buf_p;
+    int yy_init;
+    int yy_start;
+    int yy_did_buffer_switch_on_eof;
+    int yy_start_stack_ptr;
+    int yy_start_stack_depth;
+    int *yy_start_stack;
+    yy_state_type yy_last_accepting_state;
+    char* yy_last_accepting_cpos;
+
+    int yylineno_r;
+    int yy_flex_debug_r;
+
+    char *yytext_r;
+    int yy_more_flag;
+    int yy_more_len;
+
+    YYSTYPE * yylval_r;
+
+    YYLTYPE * yylloc_r;
+
+    }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+    /* This must go here because YYSTYPE and YYLTYPE are included
+     * from bison output in section 1.*/
+    #    define yylval yyg->yylval_r
+    
+    #    define yylloc yyg->yylloc_r
+    
+int igraph_dl_yylex_init (yyscan_t* scanner);
+
+int igraph_dl_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int igraph_dl_yylex_destroy (yyscan_t yyscanner );
+
+int igraph_dl_yyget_debug (yyscan_t yyscanner );
+
+void igraph_dl_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE igraph_dl_yyget_extra (yyscan_t yyscanner );
+
+void igraph_dl_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *igraph_dl_yyget_in (yyscan_t yyscanner );
+
+void igraph_dl_yyset_in  (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *igraph_dl_yyget_out (yyscan_t yyscanner );
+
+void igraph_dl_yyset_out  (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t igraph_dl_yyget_leng (yyscan_t yyscanner );
+
+char *igraph_dl_yyget_text (yyscan_t yyscanner );
+
+int igraph_dl_yyget_lineno (yyscan_t yyscanner );
+
+void igraph_dl_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * igraph_dl_yyget_lval (yyscan_t yyscanner );
+
+void igraph_dl_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+       YYLTYPE *igraph_dl_yyget_lloc (yyscan_t yyscanner );
+    
+        void igraph_dl_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+    
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int igraph_dl_yywrap (yyscan_t yyscanner );
+#else
+extern int igraph_dl_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		yy_size_t n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int igraph_dl_yylex \
+               (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int igraph_dl_yylex \
+               (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 81 "../../src/foreign-dl-lexer.l"
+
+
+#line 852 "foreign-dl-lexer.c"
+
+    yylval = yylval_param;
+
+    yylloc = yylloc_param;
+
+	if ( !yyg->yy_init )
+		{
+		yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yyg->yy_start )
+			yyg->yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			igraph_dl_yyensure_buffer_stack (yyscanner);
+			YY_CURRENT_BUFFER_LVALUE =
+				igraph_dl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+		}
+
+		igraph_dl_yy_load_buffer_state(yyscanner );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yyg->yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yyg->yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yyg->yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yyg->yy_last_accepting_state = yy_current_state;
+				yyg->yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 131 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 289 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yyg->yy_hold_char;
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+#line 83 "../../src/foreign-dl-lexer.l"
+{ return NEWLINE; }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 85 "../../src/foreign-dl-lexer.l"
+{ return DL; }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 86 "../../src/foreign-dl-lexer.l"
+{
+  return NEQ; }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 88 "../../src/foreign-dl-lexer.l"
+{ return NUM; }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 90 "../../src/foreign-dl-lexer.l"
+{ 
+  switch (yyextra->mode) { 
+  case 0: BEGIN(FULLMATRIX); 
+    break;
+  case 1: BEGIN(EDGELIST);
+    break;
+  case 2: BEGIN(NODELIST);
+    break;
+  } 
+  return DATA; }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 101 "../../src/foreign-dl-lexer.l"
+{ BEGIN(LABELM); return LABELS; }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 102 "../../src/foreign-dl-lexer.l"
+{
+  return LABELSEMBEDDED; }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 104 "../../src/foreign-dl-lexer.l"
+{
+  yyextra->mode=0; return FORMATFULLMATRIX; }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 106 "../../src/foreign-dl-lexer.l"
+{
+  yyextra->mode=1; return FORMATEDGELIST1; }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 108 "../../src/foreign-dl-lexer.l"
+{
+  yyextra->mode=2; return FORMATNODELIST1; }
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 111 "../../src/foreign-dl-lexer.l"
+{ /* eaten up */ }
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 112 "../../src/foreign-dl-lexer.l"
+{ return LABEL; }
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 114 "../../src/foreign-dl-lexer.l"
+{ return DIGIT; }
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 115 "../../src/foreign-dl-lexer.l"
+{ return LABEL; }
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 116 "../../src/foreign-dl-lexer.l"
+{ }
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 118 "../../src/foreign-dl-lexer.l"
+{ return NUM; }
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 119 "../../src/foreign-dl-lexer.l"
+{ return LABEL; }
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 120 "../../src/foreign-dl-lexer.l"
+{ }
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 122 "../../src/foreign-dl-lexer.l"
+{ return NUM; }
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 123 "../../src/foreign-dl-lexer.l"
+{ return LABEL; }
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 124 "../../src/foreign-dl-lexer.l"
+{ }
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 126 "../../src/foreign-dl-lexer.l"
+{ /* eaten up */ }
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(LABELM):
+case YY_STATE_EOF(FULLMATRIX):
+case YY_STATE_EOF(EDGELIST):
+case YY_STATE_EOF(NODELIST):
+#line 128 "../../src/foreign-dl-lexer.l"
+{ 
+                          if (yyextra->eof) {
+			    yyterminate();
+			  } else {
+			    yyextra->eof=1;
+			    BEGIN(INITIAL);
+			    return EOFF;
+			  }			  
+                        }
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 138 "../../src/foreign-dl-lexer.l"
+{ return 0; }
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 140 "../../src/foreign-dl-lexer.l"
+{ return ERROR; }
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 141 "../../src/foreign-dl-lexer.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+#line 1095 "foreign-dl-lexer.c"
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yyg->yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * igraph_dl_yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state( yyscanner );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+			yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yyg->yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yyg->yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer( yyscanner ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yyg->yy_did_buffer_switch_on_eof = 0;
+
+				if ( igraph_dl_yywrap(yyscanner ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yyg->yy_c_buf_p =
+					yyg->yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yyg->yy_c_buf_p =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of igraph_dl_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = yyg->yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+	else
+		{
+			yy_size_t num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				yy_size_t new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					igraph_dl_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			yyg->yy_n_chars, num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	if ( yyg->yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			igraph_dl_yyrestart(yyin  ,yyscanner);
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_dl_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	yyg->yy_n_chars += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_current_state = yyg->yy_start;
+
+	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yyg->yy_last_accepting_state = yy_current_state;
+			yyg->yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 131 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+	register int yy_is_jam;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+	register char *yy_cp = yyg->yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yyg->yy_last_accepting_state = yy_current_state;
+		yyg->yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 131 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 130);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (yyscan_t yyscanner)
+#else
+    static int input  (yyscan_t yyscanner)
+#endif
+
+{
+	int c;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	*yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+	if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			/* This was really a NUL. */
+			*yyg->yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			++yyg->yy_c_buf_p;
+
+			switch ( yy_get_next_buffer( yyscanner ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					igraph_dl_yyrestart(yyin ,yyscanner);
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( igraph_dl_yywrap(yyscanner ) )
+						return 0;
+
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput(yyscanner);
+#else
+					return input(yyscanner);
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yyg->yy_c_buf_p;	/* cast for 8-bit char's */
+	*yyg->yy_c_buf_p = '\0';	/* preserve yytext */
+	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void igraph_dl_yyrestart  (FILE * input_file , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! YY_CURRENT_BUFFER ){
+        igraph_dl_yyensure_buffer_stack (yyscanner);
+		YY_CURRENT_BUFFER_LVALUE =
+            igraph_dl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+	}
+
+	igraph_dl_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+	igraph_dl_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_dl_yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		igraph_dl_yypop_buffer_state();
+	 *		igraph_dl_yypush_buffer_state(new_buffer);
+     */
+	igraph_dl_yyensure_buffer_stack (yyscanner);
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	igraph_dl_yy_load_buffer_state(yyscanner );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (igraph_dl_yywrap()) processing, but the only time this flag
+	 * is looked at is after igraph_dl_yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void igraph_dl_yy_load_buffer_state  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE igraph_dl_yy_create_buffer  (FILE * file, int  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) igraph_dl_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) igraph_dl_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	igraph_dl_yy_init_buffer(b,file ,yyscanner);
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with igraph_dl_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+    void igraph_dl_yy_delete_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		igraph_dl_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+	igraph_dl_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a igraph_dl_yyrestart() or at EOF.
+ */
+    static void igraph_dl_yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file , yyscan_t yyscanner)
+
+{
+	int oerrno = errno;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	igraph_dl_yy_flush_buffer(b ,yyscanner);
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then igraph_dl_yy_init_buffer was _probably_
+     * called from igraph_dl_yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_dl_yy_flush_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		igraph_dl_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_dl_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (new_buffer == NULL)
+		return;
+
+	igraph_dl_yyensure_buffer_stack(yyscanner);
+
+	/* This block is copied from igraph_dl_yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		yyg->yy_buffer_stack_top++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from igraph_dl_yy_switch_to_buffer. */
+	igraph_dl_yy_load_buffer_state(yyscanner );
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_dl_yypop_buffer_state (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (!YY_CURRENT_BUFFER)
+		return;
+
+	igraph_dl_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if (yyg->yy_buffer_stack_top > 0)
+		--yyg->yy_buffer_stack_top;
+
+	if (YY_CURRENT_BUFFER) {
+		igraph_dl_yy_load_buffer_state(yyscanner );
+		yyg->yy_did_buffer_switch_on_eof = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void igraph_dl_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+	yy_size_t num_to_alloc;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if (!yyg->yy_buffer_stack) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_dl_yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yyensure_buffer_stack()" );
+								  
+		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		yyg->yy_buffer_stack_max = num_to_alloc;
+		yyg->yy_buffer_stack_top = 0;
+		return;
+	}
+
+	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_dl_yyrealloc
+								(yyg->yy_buffer_stack,
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+		yyg->yy_buffer_stack_max = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE igraph_dl_yy_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) igraph_dl_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	igraph_dl_yy_switch_to_buffer(b ,yyscanner );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to igraph_dl_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       igraph_dl_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE igraph_dl_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+    
+	return igraph_dl_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to igraph_dl_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE igraph_dl_yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n, i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) igraph_dl_yyalloc(n ,yyscanner );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_dl_yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = igraph_dl_yy_scan_buffer(buf,n ,yyscanner);
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in igraph_dl_yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = yyg->yy_hold_char; \
+		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+		yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+		*yyg->yy_c_buf_p = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE igraph_dl_yyget_extra  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_dl_yyget_lineno  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_dl_yyget_column  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_dl_yyget_in  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_dl_yyget_out  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+yy_size_t igraph_dl_yyget_leng  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *igraph_dl_yyget_text  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void igraph_dl_yyset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_dl_yyset_lineno (int  line_number , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* lineno is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_dl_yyset_lineno called with no buffer" , yyscanner); 
+    
+    yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_dl_yyset_column (int  column_no , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* column is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_dl_yyset_column called with no buffer" , yyscanner); 
+    
+    yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see igraph_dl_yy_switch_to_buffer
+ */
+void igraph_dl_yyset_in (FILE *  in_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyin = in_str ;
+}
+
+void igraph_dl_yyset_out (FILE *  out_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyout = out_str ;
+}
+
+int igraph_dl_yyget_debug  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yy_flex_debug;
+}
+
+void igraph_dl_yyset_debug (int  bdebug , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * igraph_dl_yyget_lval  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylval;
+}
+
+void igraph_dl_yyset_lval (YYSTYPE *  yylval_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylval = yylval_param;
+}
+
+YYLTYPE *igraph_dl_yyget_lloc  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylloc;
+}
+    
+void igraph_dl_yyset_lloc (YYLTYPE *  yylloc_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylloc = yylloc_param;
+}
+    
+/* User-visible API */
+
+/* igraph_dl_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int igraph_dl_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) igraph_dl_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* igraph_dl_yylex_init_extra has the same functionality as igraph_dl_yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to igraph_dl_yyalloc in
+ * the yyextra field.
+ */
+
+int igraph_dl_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+    struct yyguts_t dummy_yyguts;
+
+    igraph_dl_yyset_extra (yy_user_defined, &dummy_yyguts);
+
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+	
+    *ptr_yy_globals = (yyscan_t) igraph_dl_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+	
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+    
+    /* By setting to 0xAA, we expose bugs in
+    yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+    
+    igraph_dl_yyset_extra (yy_user_defined, *ptr_yy_globals);
+    
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from igraph_dl_yylex_destroy(), so don't allocate here.
+     */
+
+    yyg->yy_buffer_stack = 0;
+    yyg->yy_buffer_stack_top = 0;
+    yyg->yy_buffer_stack_max = 0;
+    yyg->yy_c_buf_p = (char *) 0;
+    yyg->yy_init = 0;
+    yyg->yy_start = 0;
+
+    yyg->yy_start_stack_ptr = 0;
+    yyg->yy_start_stack_depth = 0;
+    yyg->yy_start_stack =  NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * igraph_dl_yylex_init()
+     */
+    return 0;
+}
+
+/* igraph_dl_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int igraph_dl_yylex_destroy  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		igraph_dl_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		igraph_dl_yypop_buffer_state(yyscanner);
+	}
+
+	/* Destroy the stack itself. */
+	igraph_dl_yyfree(yyg->yy_buffer_stack ,yyscanner);
+	yyg->yy_buffer_stack = NULL;
+
+    /* Destroy the start condition stack. */
+        igraph_dl_yyfree(yyg->yy_start_stack ,yyscanner );
+        yyg->yy_start_stack = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * igraph_dl_yylex() is called, initialization will occur. */
+    yy_init_globals( yyscanner);
+
+    /* Destroy the main struct (reentrant only). */
+    igraph_dl_yyfree ( yyscanner , yyscanner );
+    yyscanner = NULL;
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *igraph_dl_yyalloc (yy_size_t  size , yyscan_t yyscanner)
+{
+	return (void *) malloc( size );
+}
+
+void *igraph_dl_yyrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void igraph_dl_yyfree (void * ptr , yyscan_t yyscanner)
+{
+	free( (char *) ptr );	/* see igraph_dl_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 141 "../../src/foreign-dl-lexer.l"
diff --git a/igraph/src/foreign-dl-parser.c b/igraph/src/foreign-dl-parser.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-dl-parser.c
@@ -0,0 +1,2145 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names.  */
+#define yyparse igraph_dl_yyparse
+#define yylex   igraph_dl_yylex
+#define yyerror igraph_dl_yyerror
+#define yylval  igraph_dl_yylval
+#define yychar  igraph_dl_yychar
+#define yydebug igraph_dl_yydebug
+#define yynerrs igraph_dl_yynerrs
+#define yylloc igraph_dl_yylloc
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     NUM = 258,
+     NEWLINE = 259,
+     DL = 260,
+     NEQ = 261,
+     DATA = 262,
+     LABELS = 263,
+     LABELSEMBEDDED = 264,
+     FORMATFULLMATRIX = 265,
+     FORMATEDGELIST1 = 266,
+     FORMATNODELIST1 = 267,
+     DIGIT = 268,
+     LABEL = 269,
+     EOFF = 270,
+     ERROR = 271
+   };
+#endif
+/* Tokens.  */
+#define NUM 258
+#define NEWLINE 259
+#define DL 260
+#define NEQ 261
+#define DATA 262
+#define LABELS 263
+#define LABELSEMBEDDED 264
+#define FORMATFULLMATRIX 265
+#define FORMATEDGELIST1 266
+#define FORMATNODELIST1 267
+#define DIGIT 268
+#define LABEL 269
+#define EOFF 270
+#define ERROR 271
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 23 "../../src/foreign-dl-parser.y"
+
+
+/* 
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include "igraph_hacks_internal.h"
+#include "igraph_math.h"
+#include "igraph_types_internal.h"
+#include "foreign-dl-header.h"
+#include "foreign-dl-parser.h"
+#include <stdio.h>
+
+#define yyscan_t void*
+
+int igraph_dl_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner);
+int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, 
+		      const char *s);
+char *igraph_dl_yyget_text (yyscan_t yyscanner );
+int igraph_dl_yyget_leng (yyscan_t yyscanner );
+
+int igraph_i_dl_add_str(char *newstr, int length, 
+			igraph_i_dl_parsedata_t *context);
+int igraph_i_dl_add_edge(long int from, long int to,
+			 igraph_i_dl_parsedata_t *context);
+int igraph_i_dl_add_edge_w(long int from, long int to, 
+			   igraph_real_t weight,
+			   igraph_i_dl_parsedata_t *context);
+
+extern igraph_real_t igraph_pajek_get_number(const char *str, long int len);
+
+#define scanner context->scanner
+ 
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 86 "../../src/foreign-dl-parser.y"
+{
+  long int integer;
+  igraph_real_t real;
+}
+/* Line 193 of yacc.c.  */
+#line 195 "foreign-dl-parser.c"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 216 of yacc.c.  */
+#line 220 "foreign-dl-parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+    int i;
+#endif
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+	     && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+    YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+      + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  4
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   118
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  17
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  37
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  66
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  138
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   271
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint8 yyprhs[] =
+{
+       0,     0,     3,    11,    12,    15,    16,    18,    20,    22,
+      24,    28,    30,    31,    33,    37,    45,    51,    52,    56,
+      57,    61,    62,    65,    67,    69,    73,    74,    78,    80,
+      82,    85,    89,    93,    97,   105,   111,   121,   131,   132,
+     135,   140,   144,   146,   147,   150,   155,   159,   161,   163,
+     167,   170,   178,   184,   194,   204,   205,   208,   212,   214,
+     215,   218,   219,   222,   226,   228,   229
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+      18,     0,    -1,     5,     6,    39,     4,    21,    19,    20,
+      -1,    -1,    19,    23,    -1,    -1,    15,    -1,    22,    -1,
+      35,    -1,    44,    -1,    10,    23,    24,    -1,    24,    -1,
+      -1,     4,    -1,     7,    23,    26,    -1,     8,    23,    25,
+      23,     7,    23,    26,    -1,     9,    23,     7,    23,    29,
+      -1,    -1,    25,    23,    14,    -1,    -1,    26,    27,     4,
+      -1,    -1,    27,    28,    -1,    13,    -1,    30,    -1,    31,
+       4,    33,    -1,    -1,    31,    23,    32,    -1,    14,    -1,
+      34,    -1,    33,    34,    -1,    14,    27,     4,    -1,    11,
+      23,    36,    -1,     7,    23,    37,    -1,     8,    23,    25,
+      23,     7,    23,    37,    -1,     9,    23,     7,    23,    40,
+      -1,     8,    23,    25,    23,     9,    23,     7,    23,    40,
+      -1,     9,    23,     8,    23,    25,    23,     7,    23,    40,
+      -1,    -1,    37,    38,    -1,    39,    39,    42,     4,    -1,
+      39,    39,     4,    -1,     3,    -1,    -1,    40,    41,    -1,
+      43,    43,    42,     4,    -1,    43,    43,     4,    -1,     3,
+      -1,    14,    -1,    12,    23,    45,    -1,     7,    46,    -1,
+       8,    23,    25,    23,     7,    23,    46,    -1,     9,    23,
+       7,    23,    50,    -1,     8,    23,    25,    23,     9,    23,
+       7,    23,    50,    -1,     9,    23,     8,    23,    25,    23,
+       7,    23,    50,    -1,    -1,    46,    47,    -1,    48,    49,
+       4,    -1,     3,    -1,    -1,    49,    39,    -1,    -1,    50,
+      51,    -1,    52,    53,     4,    -1,    43,    -1,    -1,    53,
+      43,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   111,   111,   113,   113,   115,   115,   117,   118,   119,
+     122,   122,   124,   124,   126,   127,   128,   131,   132,   138,
+     138,   143,   143,   145,   155,   157,   159,   159,   161,   165,
+     169,   174,   178,   180,   181,   182,   183,   184,   187,   188,
+     191,   193,   197,   200,   201,   204,   206,   210,   213,   229,
+     231,   232,   233,   234,   235,   238,   239,   242,   244,   247,
+     247,   253,   254,   257,   259,   263,   263
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "NUM", "NEWLINE", "DL", "NEQ", "DATA",
+  "LABELS", "LABELSEMBEDDED", "FORMATFULLMATRIX", "FORMATEDGELIST1",
+  "FORMATNODELIST1", "DIGIT", "LABEL", "EOFF", "ERROR", "$accept", "input",
+  "trail", "eof", "rest", "formfullmatrix", "newline", "fullmatrix",
+  "labels", "fullmatrixdata", "zerooneseq", "zeroone",
+  "labeledfullmatrixdata", "reallabeledfullmatrixdata", "labelseq",
+  "label", "labeledmatrixlines", "labeledmatrixline", "edgelist1",
+  "edgelist1rest", "edgelist1data", "edgelist1dataline", "integer",
+  "labelededgelist1data", "labelededgelist1dataline", "weight", "elabel",
+  "nodelist1", "nodelist1rest", "nodelist1data", "nodelist1dataline",
+  "from", "tolist", "labelednodelist1data", "labelednodelist1dataline",
+  "fromelabel", "labeltolist", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    17,    18,    19,    19,    20,    20,    21,    21,    21,
+      22,    22,    23,    23,    24,    24,    24,    25,    25,    26,
+      26,    27,    27,    28,    29,    30,    31,    31,    32,    33,
+      33,    34,    35,    36,    36,    36,    36,    36,    37,    37,
+      38,    38,    39,    40,    40,    41,    41,    42,    43,    44,
+      45,    45,    45,    45,    45,    46,    46,    47,    48,    49,
+      49,    50,    50,    51,    52,    53,    53
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     7,     0,     2,     0,     1,     1,     1,     1,
+       3,     1,     0,     1,     3,     7,     5,     0,     3,     0,
+       3,     0,     2,     1,     1,     3,     0,     3,     1,     1,
+       2,     3,     3,     3,     7,     5,     9,     9,     0,     2,
+       4,     3,     1,     0,     2,     4,     3,     1,     1,     3,
+       2,     7,     5,     9,     9,     0,     2,     3,     1,     0,
+       2,     0,     2,     3,     1,     0,     2
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       0,     0,     0,     0,     1,    42,     0,     0,    12,    12,
+      12,    12,    12,    12,     3,     7,    11,     8,     9,    13,
+      19,    17,     0,     0,     0,     0,     5,    14,    12,    12,
+      10,    12,    12,    12,    32,    55,    12,    12,    49,     6,
+       2,     4,     0,     0,    26,    38,    17,     0,    50,    17,
+       0,    20,    23,    22,    12,    18,    16,    24,    12,    33,
+      12,    12,    12,    58,    56,    59,    12,    12,    12,    19,
+       0,     0,    39,     0,     0,    43,    17,     0,     0,    61,
+      17,    15,    21,    25,    29,    28,    27,     0,    12,    12,
+      35,    12,    57,    60,    12,    12,    52,    12,     0,    30,
+      47,    41,     0,    38,     0,    48,    44,     0,     0,    55,
+       0,    64,    62,    65,     0,    31,    40,    34,    12,     0,
+      12,    51,    12,     0,    12,    43,    46,     0,    43,    61,
+      63,    66,    61,    36,    45,    37,    53,    54
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     2,    26,    40,    14,    15,    20,    16,    28,    27,
+      42,    53,    56,    57,    58,    86,    83,    84,    17,    34,
+      59,    72,    73,    90,   106,   102,   107,    18,    38,    48,
+      64,    65,    77,    96,   112,   113,   123
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -114
+static const yytype_int8 yypact[] =
+{
+       8,    38,    11,    43,  -114,  -114,    44,    57,    46,    46,
+      46,    46,    46,    46,  -114,  -114,  -114,  -114,  -114,  -114,
+    -114,  -114,    69,    53,    63,    66,     6,    65,    46,    46,
+    -114,    46,    46,    46,  -114,  -114,    46,    46,  -114,  -114,
+    -114,  -114,     5,    19,  -114,  -114,  -114,    76,    84,  -114,
+      82,  -114,  -114,  -114,    46,  -114,  -114,  -114,    93,    43,
+      46,    46,    46,  -114,  -114,  -114,    46,    46,    46,  -114,
+      85,    86,  -114,    43,    23,  -114,  -114,    88,    33,  -114,
+    -114,    65,  -114,    85,  -114,  -114,  -114,    90,    46,    46,
+      87,    46,  -114,  -114,    46,    46,    87,    46,    25,  -114,
+    -114,  -114,    94,  -114,    95,  -114,  -114,    87,    29,  -114,
+      96,  -114,  -114,  -114,    49,  -114,  -114,    43,    46,    92,
+      46,    84,    46,     2,    46,  -114,  -114,   100,  -114,  -114,
+    -114,  -114,  -114,    87,  -114,    87,    87,    87
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+    -114,  -114,  -114,  -114,  -114,  -114,    -9,    83,   -41,    36,
+      26,  -114,  -114,  -114,  -114,  -114,  -114,    24,  -114,  -114,
+       7,  -114,     4,  -113,  -114,    -7,   -82,  -114,  -114,     9,
+    -114,  -114,  -114,   -98,  -114,  -114,  -114
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -22
+static const yytype_int16 yytable[] =
+{
+      21,    22,    23,    24,    25,    60,   130,     6,    66,    51,
+      19,     4,   133,     1,   111,   135,   105,    41,    52,    43,
+      44,    39,    45,    46,    47,   119,    54,    49,    50,   115,
+      88,   136,    89,    55,   137,    91,   120,    55,    52,    97,
+      94,   131,    95,    55,     3,    69,     5,    55,     7,    71,
+      19,    74,    75,    76,   111,   111,   124,    78,    79,    80,
+       8,     9,    10,    55,     8,     9,    10,    11,    12,    13,
+      31,    32,    33,    35,    36,    37,    29,    87,   -21,   103,
+     104,    93,   108,    61,    62,   109,   110,    63,   114,    67,
+      68,     5,    92,   100,   101,   100,   126,    70,   116,    82,
+      85,   105,   118,   122,   134,    81,    30,    99,    98,   125,
+     117,   128,   127,   129,     0,   132,     0,     0,   121
+};
+
+static const yytype_int16 yycheck[] =
+{
+       9,    10,    11,    12,    13,    46,     4,     3,    49,     4,
+       4,     0,   125,     5,    96,   128,    14,    26,    13,    28,
+      29,    15,    31,    32,    33,   107,     7,    36,    37,     4,
+       7,   129,     9,    14,   132,    76,     7,    14,    13,    80,
+       7,   123,     9,    14,     6,    54,     3,    14,     4,    58,
+       4,    60,    61,    62,   136,   137,     7,    66,    67,    68,
+       7,     8,     9,    14,     7,     8,     9,    10,    11,    12,
+       7,     8,     9,     7,     8,     9,     7,    73,    13,    88,
+      89,    77,    91,     7,     8,    94,    95,     3,    97,     7,
+       8,     3,     4,     3,     4,     3,     4,     4,     4,    14,
+      14,    14,     7,     7,     4,    69,    23,    83,    82,   118,
+     103,   120,   119,   122,    -1,   124,    -1,    -1,   109
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     5,    18,     6,     0,     3,    39,     4,     7,     8,
+       9,    10,    11,    12,    21,    22,    24,    35,    44,     4,
+      23,    23,    23,    23,    23,    23,    19,    26,    25,     7,
+      24,     7,     8,     9,    36,     7,     8,     9,    45,    15,
+      20,    23,    27,    23,    23,    23,    23,    23,    46,    23,
+      23,     4,    13,    28,     7,    14,    29,    30,    31,    37,
+      25,     7,     8,     3,    47,    48,    25,     7,     8,    23,
+       4,    23,    38,    39,    23,    23,    23,    49,    23,    23,
+      23,    26,    14,    33,    34,    14,    32,    39,     7,     9,
+      40,    25,     4,    39,     7,     9,    50,    25,    27,    34,
+       3,     4,    42,    23,    23,    14,    41,    43,    23,    23,
+      23,    43,    51,    52,    23,     4,     4,    37,     7,    43,
+       7,    46,     7,    53,     7,    23,     4,    42,    23,    23,
+       4,    43,    23,    40,     4,    40,    50,    50
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+	      (Loc).first_line, (Loc).first_column,	\
+	      (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc, scanner)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value, Location, context); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_dl_parsedata_t* context;
+#endif
+{
+  if (!yyvaluep)
+    return;
+  YYUSE (yylocationp);
+  YYUSE (context);
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_dl_parsedata_t* context;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  YY_LOCATION_PRINT (yyoutput, *yylocationp);
+  YYFPRINTF (yyoutput, ": ");
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    yytype_int16 *bottom;
+    yytype_int16 *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_dl_parsedata_t* context)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule, context)
+    YYSTYPE *yyvsp;
+    YYLTYPE *yylsp;
+    int yyrule;
+    igraph_i_dl_parsedata_t* context;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       , &(yylsp[(yyi + 1) - (yynrhs)])		       , context);
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, yylsp, Rule, context); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+	 constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+		    + sizeof yyexpecting - 1
+		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+		       * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+	 YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	  {
+	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+	      {
+		yycount = 1;
+		yysize = yysize0;
+		yyformat[sizeof yyunexpected - 1] = '\0';
+		break;
+	      }
+	    yyarg[yycount++] = yytname[yyx];
+	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+	    yysize_overflow |= (yysize1 < yysize);
+	    yysize = yysize1;
+	    yyfmt = yystpcpy (yyfmt, yyprefix);
+	    yyprefix = yyor;
+	  }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+	return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+	{
+	  /* Avoid sprintf, as that infringes on the user's name space.
+	     Don't have undefined behavior even if the translation
+	     produced a string with the wrong number of "%s"s.  */
+	  char *yyp = yyresult;
+	  int yyi = 0;
+	  while ((*yyp = *yyf) != '\0')
+	    {
+	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+		{
+		  yyp += yytnamerr (yyp, yyarg[yyi++]);
+		  yyf += 2;
+		}
+	      else
+		{
+		  yyp++;
+		  yyf++;
+		}
+	    }
+	}
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_dl_parsedata_t* context)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp, context)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+    YYLTYPE *yylocationp;
+    igraph_i_dl_parsedata_t* context;
+#endif
+{
+  YYUSE (yyvaluep);
+  YYUSE (yylocationp);
+  YYUSE (context);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (igraph_i_dl_parsedata_t* context);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (igraph_i_dl_parsedata_t* context)
+#else
+int
+yyparse (context)
+    igraph_i_dl_parsedata_t* context;
+#endif
+#endif
+{
+  /* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+/* Location data for the look-ahead symbol.  */
+YYLTYPE yylloc;
+
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+  /* The location stack.  */
+  YYLTYPE yylsa[YYINITDEPTH];
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+  /* The locations where the error started and ended.  */
+  YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+  YYLTYPE yyloc;
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+  yylsp = yyls;
+#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+  /* Initialize the default location before parsing starts.  */
+  yylloc.first_line   = yylloc.last_line   = 1;
+  yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+	YYLTYPE *yyls1 = yyls;
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yyls1, yysize * sizeof (*yylsp),
+		    &yystacksize);
+	yyls = yyls1;
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+	YYSTACK_RELOCATE (yyls);
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+      yylsp = yyls + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+  *++yylsp = yylloc;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+  /* Default location.  */
+  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 111 "../../src/foreign-dl-parser.y"
+    { context->n=(yyvsp[(3) - (7)].integer); }
+    break;
+
+  case 7:
+#line 117 "../../src/foreign-dl-parser.y"
+    { context->type=IGRAPH_DL_MATRIX; }
+    break;
+
+  case 8:
+#line 118 "../../src/foreign-dl-parser.y"
+    { context->type=IGRAPH_DL_EDGELIST1; }
+    break;
+
+  case 9:
+#line 119 "../../src/foreign-dl-parser.y"
+    { context->type=IGRAPH_DL_NODELIST1; }
+    break;
+
+  case 10:
+#line 122 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 11:
+#line 122 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 14:
+#line 126 "../../src/foreign-dl-parser.y"
+    { }
+    break;
+
+  case 15:
+#line 127 "../../src/foreign-dl-parser.y"
+    { }
+    break;
+
+  case 16:
+#line 128 "../../src/foreign-dl-parser.y"
+    { }
+    break;
+
+  case 17:
+#line 131 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 18:
+#line 132 "../../src/foreign-dl-parser.y"
+    { 
+	      igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), 
+                                  igraph_dl_yyget_leng(scanner), 
+				  context); }
+    break;
+
+  case 19:
+#line 138 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 20:
+#line 138 "../../src/foreign-dl-parser.y"
+    {
+  context->from += 1;
+  context->to = 0;
+ }
+    break;
+
+  case 22:
+#line 143 "../../src/foreign-dl-parser.y"
+    { }
+    break;
+
+  case 23:
+#line 145 "../../src/foreign-dl-parser.y"
+    {
+  if (igraph_dl_yyget_text(scanner)[0]=='1') {
+    IGRAPH_CHECK(igraph_vector_push_back(&context->edges, 
+					 context->from));
+    IGRAPH_CHECK(igraph_vector_push_back(&context->edges, 
+					 context->to));
+  }
+  context->to += 1;
+}
+    break;
+
+  case 24:
+#line 155 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 25:
+#line 157 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 28:
+#line 161 "../../src/foreign-dl-parser.y"
+    { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), 
+                                   igraph_dl_yyget_leng(scanner), 
+				   context); }
+    break;
+
+  case 29:
+#line 165 "../../src/foreign-dl-parser.y"
+    {
+	         context->from += 1; 
+		 context->to = 0;
+               }
+    break;
+
+  case 30:
+#line 169 "../../src/foreign-dl-parser.y"
+    { 
+	         context->from += 1; 
+		 context->to = 0;
+               }
+    break;
+
+  case 31:
+#line 174 "../../src/foreign-dl-parser.y"
+    { }
+    break;
+
+  case 32:
+#line 178 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 33:
+#line 180 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 34:
+#line 181 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 35:
+#line 182 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 36:
+#line 183 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 37:
+#line 184 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 38:
+#line 187 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 39:
+#line 188 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 40:
+#line 191 "../../src/foreign-dl-parser.y"
+    {
+                   igraph_i_dl_add_edge_w((yyvsp[(1) - (4)].integer)-1, (yyvsp[(2) - (4)].integer)-1, (yyvsp[(3) - (4)].real), context); }
+    break;
+
+  case 41:
+#line 193 "../../src/foreign-dl-parser.y"
+    {
+		   igraph_i_dl_add_edge((yyvsp[(1) - (3)].integer)-1, (yyvsp[(2) - (3)].integer)-1, context);
+}
+    break;
+
+  case 42:
+#line 197 "../../src/foreign-dl-parser.y"
+    { (yyval.integer)=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), 
+					  igraph_dl_yyget_leng(scanner)); }
+    break;
+
+  case 43:
+#line 200 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 44:
+#line 201 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 45:
+#line 204 "../../src/foreign-dl-parser.y"
+    {
+                          igraph_i_dl_add_edge_w((yyvsp[(1) - (4)].integer), (yyvsp[(2) - (4)].integer), (yyvsp[(3) - (4)].real), context); }
+    break;
+
+  case 46:
+#line 206 "../../src/foreign-dl-parser.y"
+    {
+			  igraph_i_dl_add_edge((yyvsp[(1) - (3)].integer), (yyvsp[(2) - (3)].integer), context);
+ }
+    break;
+
+  case 47:
+#line 210 "../../src/foreign-dl-parser.y"
+    { (yyval.real)=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), 
+					 igraph_dl_yyget_leng(scanner)); }
+    break;
+
+  case 48:
+#line 213 "../../src/foreign-dl-parser.y"
+    {
+  /* Copy label list to trie, if needed */
+  if (igraph_strvector_size(&context->labels) != 0) {
+    long int i, id, n=igraph_strvector_size(&context->labels);
+    for (i=0; i<n; i++) {
+      igraph_trie_get(&context->trie,
+		      STR(context->labels, i), &id);
+    }
+    igraph_strvector_clear(&context->labels);
+  }
+  igraph_trie_get2(&context->trie, igraph_dl_yyget_text(scanner), 
+		   igraph_dl_yyget_leng(scanner), &(yyval.integer));
+ }
+    break;
+
+  case 49:
+#line 229 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 50:
+#line 231 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 51:
+#line 232 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 52:
+#line 233 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 53:
+#line 234 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 54:
+#line 235 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 55:
+#line 238 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 56:
+#line 239 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 57:
+#line 242 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 58:
+#line 244 "../../src/foreign-dl-parser.y"
+    { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner),
+							  igraph_dl_yyget_leng(scanner)); }
+    break;
+
+  case 59:
+#line 247 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 60:
+#line 247 "../../src/foreign-dl-parser.y"
+    { 
+  IGRAPH_CHECK(igraph_vector_push_back(&context->edges, 
+				       context->from-1)); 
+  IGRAPH_CHECK(igraph_vector_push_back(&context->edges, (yyvsp[(2) - (2)].integer)-1));
+ }
+    break;
+
+  case 61:
+#line 253 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 62:
+#line 254 "../../src/foreign-dl-parser.y"
+    {}
+    break;
+
+  case 63:
+#line 257 "../../src/foreign-dl-parser.y"
+    { }
+    break;
+
+  case 64:
+#line 259 "../../src/foreign-dl-parser.y"
+    {
+  context->from=(yyvsp[(1) - (1)].integer);
+ }
+    break;
+
+  case 66:
+#line 263 "../../src/foreign-dl-parser.y"
+    {
+  IGRAPH_CHECK(igraph_vector_push_back(&context->edges, 
+				       context->from));
+  IGRAPH_CHECK(igraph_vector_push_back(&context->edges, (yyvsp[(2) - (2)].integer)));
+ }
+    break;
+
+
+/* Line 1267 of yacc.c.  */
+#line 1884 "foreign-dl-parser.c"
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+  *++yylsp = yyloc;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (&yylloc, context, YY_("syntax error"));
+#else
+      {
+	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+	  {
+	    YYSIZE_T yyalloc = 2 * yysize;
+	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
+	    if (yymsg != yymsgbuf)
+	      YYSTACK_FREE (yymsg);
+	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+	    if (yymsg)
+	      yymsg_alloc = yyalloc;
+	    else
+	      {
+		yymsg = yymsgbuf;
+		yymsg_alloc = sizeof yymsgbuf;
+	      }
+	  }
+
+	if (0 < yysize && yysize <= yymsg_alloc)
+	  {
+	    (void) yysyntax_error (yymsg, yystate, yychar);
+	    yyerror (&yylloc, context, yymsg);
+	  }
+	else
+	  {
+	    yyerror (&yylloc, context, YY_("syntax error"));
+	    if (yysize != 0)
+	      goto yyexhaustedlab;
+	  }
+      }
+#endif
+    }
+
+  yyerror_range[0] = yylloc;
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval, &yylloc, context);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  yyerror_range[0] = yylsp[1-yylen];
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      yyerror_range[0] = *yylsp;
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+  yyerror_range[1] = yylloc;
+  /* Using YYLLOC is tempting, but would change the location of
+     the look-ahead.  YYLOC is available though.  */
+  YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+  *++yylsp = yyloc;
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (&yylloc, context, YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+		 yytoken, &yylval, &yylloc, context);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+#line 269 "../../src/foreign-dl-parser.y"
+
+
+int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, 
+		      const char *s) {
+  snprintf(context->errmsg, 
+	   sizeof(context->errmsg)/sizeof(char)-1, 
+	   "%s in line %i", s, locp->first_line);
+  return 0;
+}
+
+int igraph_i_dl_add_str(char *newstr, int length, 
+			igraph_i_dl_parsedata_t *context) {
+  int tmp=newstr[length];
+  newstr[length]='\0';
+  IGRAPH_CHECK(igraph_strvector_add(&context->labels, newstr));
+  newstr[length]=tmp;
+  return 0;
+}
+
+int igraph_i_dl_add_edge(long int from, long int to, 
+			 igraph_i_dl_parsedata_t *context) {
+  IGRAPH_CHECK(igraph_vector_push_back(&context->edges, from));
+  IGRAPH_CHECK(igraph_vector_push_back(&context->edges, to));
+  return 0;
+}
+
+int igraph_i_dl_add_edge_w(long int from, long int to, 
+			   igraph_real_t weight,
+			   igraph_i_dl_parsedata_t *context) {
+  long int n=igraph_vector_size(&context->weights);
+  long int n2=igraph_vector_size(&context->edges)/2;
+  if (n != n2) {
+    igraph_vector_resize(&context->weights, n2);
+    for (; n<n2; n++) {
+      VECTOR(context->weights)[n]=IGRAPH_NAN;
+    }
+  }
+  IGRAPH_CHECK(igraph_i_dl_add_edge(from, to, context));
+  IGRAPH_CHECK(igraph_vector_push_back(&context->weights, weight));
+  return 0;
+}
+
diff --git a/igraph/src/foreign-gml-lexer.c b/igraph/src/foreign-gml-lexer.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-gml-lexer.c
@@ -0,0 +1,2055 @@
+#line 2 "foreign-gml-lexer.c"
+
+#line 4 "foreign-gml-lexer.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE igraph_gml_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = yyg->yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	yy_size_t yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via igraph_gml_yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+                          ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void igraph_gml_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void igraph_gml_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_gml_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void igraph_gml_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_gml_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_gml_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void igraph_gml_yypop_buffer_state (yyscan_t yyscanner );
+
+static void igraph_gml_yyensure_buffer_stack (yyscan_t yyscanner );
+static void igraph_gml_yy_load_buffer_state (yyscan_t yyscanner );
+static void igraph_gml_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER igraph_gml_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE igraph_gml_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_gml_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_gml_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *igraph_gml_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *igraph_gml_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void igraph_gml_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer igraph_gml_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        igraph_gml_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_gml_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        igraph_gml_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_gml_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define igraph_gml_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yyg->yytext_ptr = yy_bp; \
+	yyleng = (yy_size_t) (yy_cp - yy_bp); \
+	yyg->yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 10
+#define YY_END_OF_BUFFER 11
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[29] =
+    {   0,
+        0,    0,   11,    9,    8,    7,    7,    9,    9,    3,
+        4,    5,    6,    1,    9,    7,    0,    2,    3,    0,
+        0,    4,    0,    1,    3,    0,    3,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    4,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    5,    6,    1,    1,    1,    1,    1,
+        1,    1,    7,    1,    8,    9,    1,   10,   10,   10,
+       10,   10,   10,   10,   10,   10,   10,    1,    1,    1,
+        1,    1,    1,    1,   11,   11,   11,   11,   12,   11,
+       11,   11,   11,   11,   11,   11,   11,   11,   11,   11,
+       11,   11,   11,   11,   11,   11,   11,   11,   11,   11,
+       13,    1,   14,    1,   11,    1,   11,   11,   11,   11,
+
+       12,   11,   11,   11,   11,   11,   11,   11,   11,   11,
+       11,   11,   11,   11,   11,   11,   11,   11,   11,   11,
+       11,   11,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[15] =
+    {   0,
+        1,    1,    1,    2,    1,    1,    1,    1,    1,    3,
+        3,    3,    1,    1
+    } ;
+
+static yyconst flex_int16_t yy_base[32] =
+    {   0,
+        0,   11,   42,   43,   43,   37,   37,   34,   28,    9,
+        0,   43,   43,   34,   33,   43,   30,   43,    0,   24,
+       15,    0,   30,   43,   14,   21,   10,   43,   26,   13,
+       29
+    } ;
+
+static yyconst flex_int16_t yy_def[32] =
+    {   0,
+       28,    1,   28,   28,   28,   28,   28,   29,   28,   28,
+       30,   28,   28,   28,   31,   28,   29,   28,   10,   28,
+       28,   30,   31,   28,   28,   28,   28,    0,   28,   28,
+       28
+    } ;
+
+static yyconst flex_int16_t yy_nxt[58] =
+    {   0,
+        4,    5,    6,    7,    8,    4,    4,    9,    4,   10,
+       11,   11,   12,   13,   14,   22,   15,   20,   19,   27,
+       21,   26,   26,   25,   27,   21,   17,   17,   17,   23,
+       27,   23,   24,   25,   18,   24,   16,   19,   18,   16,
+       16,   28,    3,   28,   28,   28,   28,   28,   28,   28,
+       28,   28,   28,   28,   28,   28,   28
+    } ;
+
+static yyconst flex_int16_t yy_chk[58] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    2,   30,    2,   10,   10,   27,
+       10,   21,   21,   25,   21,   25,   29,   29,   29,   31,
+       26,   31,   23,   20,   17,   15,   14,    9,    8,    7,
+        6,    3,   28,   28,   28,   28,   28,   28,   28,   28,
+       28,   28,   28,   28,   28,   28,   28
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "../../src/foreign-gml-lexer.l"
+/* 
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+#line 24 "../../src/foreign-gml-lexer.l"
+
+/* 
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include <stdlib.h>
+#include "foreign-gml-header.h"
+#include "foreign-gml-parser.h"
+#define YY_EXTRA_TYPE igraph_i_gml_parsedata_t*
+#define YY_USER_ACTION yylloc->first_line = yylineno;
+/* We assume that 'file' is 'stderr' here. */
+#ifdef USING_R
+#define fprintf(file, msg, ...) (1)
+#endif
+#ifdef stdout 
+#  undef stdout
+#endif
+#define stdout 0
+#define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \
+				__LINE__, IGRAPH_PARSEERROR);
+#define YY_NO_INPUT 1
+#line 514 "foreign-gml-lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+    {
+
+    /* User-defined. Not touched by flex. */
+    YY_EXTRA_TYPE yyextra_r;
+
+    /* The rest are the same as the globals declared in the non-reentrant scanner. */
+    FILE *yyin_r, *yyout_r;
+    size_t yy_buffer_stack_top; /**< index of top of stack. */
+    size_t yy_buffer_stack_max; /**< capacity of stack. */
+    YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+    char yy_hold_char;
+    yy_size_t yy_n_chars;
+    yy_size_t yyleng_r;
+    char *yy_c_buf_p;
+    int yy_init;
+    int yy_start;
+    int yy_did_buffer_switch_on_eof;
+    int yy_start_stack_ptr;
+    int yy_start_stack_depth;
+    int *yy_start_stack;
+    yy_state_type yy_last_accepting_state;
+    char* yy_last_accepting_cpos;
+
+    int yylineno_r;
+    int yy_flex_debug_r;
+
+    char *yytext_r;
+    int yy_more_flag;
+    int yy_more_len;
+
+    YYSTYPE * yylval_r;
+
+    YYLTYPE * yylloc_r;
+
+    }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+    /* This must go here because YYSTYPE and YYLTYPE are included
+     * from bison output in section 1.*/
+    #    define yylval yyg->yylval_r
+    
+    #    define yylloc yyg->yylloc_r
+    
+int igraph_gml_yylex_init (yyscan_t* scanner);
+
+int igraph_gml_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int igraph_gml_yylex_destroy (yyscan_t yyscanner );
+
+int igraph_gml_yyget_debug (yyscan_t yyscanner );
+
+void igraph_gml_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE igraph_gml_yyget_extra (yyscan_t yyscanner );
+
+void igraph_gml_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *igraph_gml_yyget_in (yyscan_t yyscanner );
+
+void igraph_gml_yyset_in  (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *igraph_gml_yyget_out (yyscan_t yyscanner );
+
+void igraph_gml_yyset_out  (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t igraph_gml_yyget_leng (yyscan_t yyscanner );
+
+char *igraph_gml_yyget_text (yyscan_t yyscanner );
+
+int igraph_gml_yyget_lineno (yyscan_t yyscanner );
+
+void igraph_gml_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * igraph_gml_yyget_lval (yyscan_t yyscanner );
+
+void igraph_gml_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+       YYLTYPE *igraph_gml_yyget_lloc (yyscan_t yyscanner );
+    
+        void igraph_gml_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+    
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int igraph_gml_yywrap (yyscan_t yyscanner );
+#else
+extern int igraph_gml_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		yy_size_t n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int igraph_gml_yylex \
+               (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int igraph_gml_yylex \
+               (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	if ( yyleng > 0 ) \
+		YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \
+				(yytext[yyleng - 1] == '\n'); \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 78 "../../src/foreign-gml-lexer.l"
+
+
+#line 759 "foreign-gml-lexer.c"
+
+    yylval = yylval_param;
+
+    yylloc = yylloc_param;
+
+	if ( !yyg->yy_init )
+		{
+		yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yyg->yy_start )
+			yyg->yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			igraph_gml_yyensure_buffer_stack (yyscanner);
+			YY_CURRENT_BUFFER_LVALUE =
+				igraph_gml_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+		}
+
+		igraph_gml_yy_load_buffer_state(yyscanner );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yyg->yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yyg->yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yyg->yy_start;
+		yy_current_state += YY_AT_BOL();
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yyg->yy_last_accepting_state = yy_current_state;
+				yyg->yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 29 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 43 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yyg->yy_hold_char;
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+#line 80 "../../src/foreign-gml-lexer.l"
+{ /* comments ignored */ }
+	YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 82 "../../src/foreign-gml-lexer.l"
+{ return STRING; }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 83 "../../src/foreign-gml-lexer.l"
+{ return NUM; }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 84 "../../src/foreign-gml-lexer.l"
+{ return KEYWORD; }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 85 "../../src/foreign-gml-lexer.l"
+{ return LISTOPEN; }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 86 "../../src/foreign-gml-lexer.l"
+{ return LISTCLOSE; }
+	YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+#line 87 "../../src/foreign-gml-lexer.l"
+{ }
+	YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+#line 88 "../../src/foreign-gml-lexer.l"
+{ /* other whitespace ignored */ }
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+#line 90 "../../src/foreign-gml-lexer.l"
+{ 
+                          if (yyextra->eof) {
+			    yyterminate();
+			  } else {
+			    yyextra->eof=1;
+			    return EOFF;
+			  }			  
+                        }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 98 "../../src/foreign-gml-lexer.l"
+{ return ERROR; }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 99 "../../src/foreign-gml-lexer.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+#line 912 "foreign-gml-lexer.c"
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yyg->yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * igraph_gml_yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state( yyscanner );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+			yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yyg->yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yyg->yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer( yyscanner ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yyg->yy_did_buffer_switch_on_eof = 0;
+
+				if ( igraph_gml_yywrap(yyscanner ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yyg->yy_c_buf_p =
+					yyg->yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yyg->yy_c_buf_p =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of igraph_gml_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = yyg->yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+	else
+		{
+			yy_size_t num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				yy_size_t new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					igraph_gml_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			yyg->yy_n_chars, num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	if ( yyg->yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			igraph_gml_yyrestart(yyin  ,yyscanner);
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_gml_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	yyg->yy_n_chars += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_current_state = yyg->yy_start;
+	yy_current_state += YY_AT_BOL();
+
+	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yyg->yy_last_accepting_state = yy_current_state;
+			yyg->yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 29 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+	register int yy_is_jam;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+	register char *yy_cp = yyg->yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yyg->yy_last_accepting_state = yy_current_state;
+		yyg->yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 29 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 28);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (yyscan_t yyscanner)
+#else
+    static int input  (yyscan_t yyscanner)
+#endif
+
+{
+	int c;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	*yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+	if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			/* This was really a NUL. */
+			*yyg->yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			++yyg->yy_c_buf_p;
+
+			switch ( yy_get_next_buffer( yyscanner ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					igraph_gml_yyrestart(yyin ,yyscanner);
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( igraph_gml_yywrap(yyscanner ) )
+						return 0;
+
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput(yyscanner);
+#else
+					return input(yyscanner);
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yyg->yy_c_buf_p;	/* cast for 8-bit char's */
+	*yyg->yy_c_buf_p = '\0';	/* preserve yytext */
+	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n');
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void igraph_gml_yyrestart  (FILE * input_file , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! YY_CURRENT_BUFFER ){
+        igraph_gml_yyensure_buffer_stack (yyscanner);
+		YY_CURRENT_BUFFER_LVALUE =
+            igraph_gml_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+	}
+
+	igraph_gml_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+	igraph_gml_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_gml_yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		igraph_gml_yypop_buffer_state();
+	 *		igraph_gml_yypush_buffer_state(new_buffer);
+     */
+	igraph_gml_yyensure_buffer_stack (yyscanner);
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	igraph_gml_yy_load_buffer_state(yyscanner );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (igraph_gml_yywrap()) processing, but the only time this flag
+	 * is looked at is after igraph_gml_yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void igraph_gml_yy_load_buffer_state  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE igraph_gml_yy_create_buffer  (FILE * file, int  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) igraph_gml_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) igraph_gml_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	igraph_gml_yy_init_buffer(b,file ,yyscanner);
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with igraph_gml_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+    void igraph_gml_yy_delete_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		igraph_gml_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+	igraph_gml_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a igraph_gml_yyrestart() or at EOF.
+ */
+    static void igraph_gml_yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file , yyscan_t yyscanner)
+
+{
+	int oerrno = errno;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	igraph_gml_yy_flush_buffer(b ,yyscanner);
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then igraph_gml_yy_init_buffer was _probably_
+     * called from igraph_gml_yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_gml_yy_flush_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		igraph_gml_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_gml_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (new_buffer == NULL)
+		return;
+
+	igraph_gml_yyensure_buffer_stack(yyscanner);
+
+	/* This block is copied from igraph_gml_yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		yyg->yy_buffer_stack_top++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from igraph_gml_yy_switch_to_buffer. */
+	igraph_gml_yy_load_buffer_state(yyscanner );
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_gml_yypop_buffer_state (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (!YY_CURRENT_BUFFER)
+		return;
+
+	igraph_gml_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if (yyg->yy_buffer_stack_top > 0)
+		--yyg->yy_buffer_stack_top;
+
+	if (YY_CURRENT_BUFFER) {
+		igraph_gml_yy_load_buffer_state(yyscanner );
+		yyg->yy_did_buffer_switch_on_eof = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void igraph_gml_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+	yy_size_t num_to_alloc;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if (!yyg->yy_buffer_stack) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_gml_yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yyensure_buffer_stack()" );
+								  
+		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		yyg->yy_buffer_stack_max = num_to_alloc;
+		yyg->yy_buffer_stack_top = 0;
+		return;
+	}
+
+	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_gml_yyrealloc
+								(yyg->yy_buffer_stack,
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+		yyg->yy_buffer_stack_max = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE igraph_gml_yy_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) igraph_gml_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	igraph_gml_yy_switch_to_buffer(b ,yyscanner );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to igraph_gml_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       igraph_gml_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE igraph_gml_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+    
+	return igraph_gml_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to igraph_gml_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE igraph_gml_yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n, i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) igraph_gml_yyalloc(n ,yyscanner );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_gml_yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = igraph_gml_yy_scan_buffer(buf,n ,yyscanner);
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in igraph_gml_yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = yyg->yy_hold_char; \
+		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+		yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+		*yyg->yy_c_buf_p = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE igraph_gml_yyget_extra  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_gml_yyget_lineno  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_gml_yyget_column  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_gml_yyget_in  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_gml_yyget_out  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+yy_size_t igraph_gml_yyget_leng  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *igraph_gml_yyget_text  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void igraph_gml_yyset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_gml_yyset_lineno (int  line_number , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* lineno is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_gml_yyset_lineno called with no buffer" , yyscanner); 
+    
+    yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_gml_yyset_column (int  column_no , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* column is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_gml_yyset_column called with no buffer" , yyscanner); 
+    
+    yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see igraph_gml_yy_switch_to_buffer
+ */
+void igraph_gml_yyset_in (FILE *  in_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyin = in_str ;
+}
+
+void igraph_gml_yyset_out (FILE *  out_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyout = out_str ;
+}
+
+int igraph_gml_yyget_debug  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yy_flex_debug;
+}
+
+void igraph_gml_yyset_debug (int  bdebug , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * igraph_gml_yyget_lval  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylval;
+}
+
+void igraph_gml_yyset_lval (YYSTYPE *  yylval_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylval = yylval_param;
+}
+
+YYLTYPE *igraph_gml_yyget_lloc  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylloc;
+}
+    
+void igraph_gml_yyset_lloc (YYLTYPE *  yylloc_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylloc = yylloc_param;
+}
+    
+/* User-visible API */
+
+/* igraph_gml_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int igraph_gml_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) igraph_gml_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* igraph_gml_yylex_init_extra has the same functionality as igraph_gml_yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to igraph_gml_yyalloc in
+ * the yyextra field.
+ */
+
+int igraph_gml_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+    struct yyguts_t dummy_yyguts;
+
+    igraph_gml_yyset_extra (yy_user_defined, &dummy_yyguts);
+
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+	
+    *ptr_yy_globals = (yyscan_t) igraph_gml_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+	
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+    
+    /* By setting to 0xAA, we expose bugs in
+    yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+    
+    igraph_gml_yyset_extra (yy_user_defined, *ptr_yy_globals);
+    
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from igraph_gml_yylex_destroy(), so don't allocate here.
+     */
+
+    yyg->yy_buffer_stack = 0;
+    yyg->yy_buffer_stack_top = 0;
+    yyg->yy_buffer_stack_max = 0;
+    yyg->yy_c_buf_p = (char *) 0;
+    yyg->yy_init = 0;
+    yyg->yy_start = 0;
+
+    yyg->yy_start_stack_ptr = 0;
+    yyg->yy_start_stack_depth = 0;
+    yyg->yy_start_stack =  NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * igraph_gml_yylex_init()
+     */
+    return 0;
+}
+
+/* igraph_gml_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int igraph_gml_yylex_destroy  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		igraph_gml_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		igraph_gml_yypop_buffer_state(yyscanner);
+	}
+
+	/* Destroy the stack itself. */
+	igraph_gml_yyfree(yyg->yy_buffer_stack ,yyscanner);
+	yyg->yy_buffer_stack = NULL;
+
+    /* Destroy the start condition stack. */
+        igraph_gml_yyfree(yyg->yy_start_stack ,yyscanner );
+        yyg->yy_start_stack = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * igraph_gml_yylex() is called, initialization will occur. */
+    yy_init_globals( yyscanner);
+
+    /* Destroy the main struct (reentrant only). */
+    igraph_gml_yyfree ( yyscanner , yyscanner );
+    yyscanner = NULL;
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *igraph_gml_yyalloc (yy_size_t  size , yyscan_t yyscanner)
+{
+	return (void *) malloc( size );
+}
+
+void *igraph_gml_yyrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void igraph_gml_yyfree (void * ptr , yyscan_t yyscanner)
+{
+	free( (char *) ptr );	/* see igraph_gml_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 99 "../../src/foreign-gml-lexer.l"
+
+
+
diff --git a/igraph/src/foreign-gml-parser.c b/igraph/src/foreign-gml-parser.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-gml-parser.c
@@ -0,0 +1,1856 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names.  */
+#define yyparse igraph_gml_yyparse
+#define yylex   igraph_gml_yylex
+#define yyerror igraph_gml_yyerror
+#define yylval  igraph_gml_yylval
+#define yychar  igraph_gml_yychar
+#define yydebug igraph_gml_yydebug
+#define yynerrs igraph_gml_yynerrs
+#define yylloc igraph_gml_yylloc
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     STRING = 258,
+     NUM = 259,
+     KEYWORD = 260,
+     LISTOPEN = 261,
+     LISTCLOSE = 262,
+     EOFF = 263,
+     ERROR = 264
+   };
+#endif
+/* Tokens.  */
+#define STRING 258
+#define NUM 259
+#define KEYWORD 260
+#define LISTOPEN 261
+#define LISTCLOSE 262
+#define EOFF 263
+#define ERROR 264
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 23 "../../src/foreign-gml-parser.y"
+
+
+/* 
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "igraph_error.h"
+#include "igraph_memory.h"
+#include "config.h"
+#include "igraph_hacks_internal.h"
+#include "igraph_math.h"
+#include "igraph_gml_tree.h"
+#include "foreign-gml-header.h"
+#include "foreign-gml-parser.h"
+
+#define yyscan_t void*
+
+int igraph_gml_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, void *scanner);
+int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, 
+		       const char *s);
+char *igraph_gml_yyget_text (yyscan_t yyscanner );
+int igraph_gml_yyget_leng (yyscan_t yyscanner );
+void igraph_i_gml_get_keyword(char *s, int len, void *res);
+void igraph_i_gml_get_string(char *s, int len, void *res);
+double igraph_i_gml_get_real(char *s, int len);
+igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value);
+igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, 
+					      char *v, int vlen);
+igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, 
+					    char *value, int valuelen);
+igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, 
+					  igraph_gml_tree_t *list);
+igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2);
+
+#define scanner context->scanner
+#define USE(x) /*(x)*/
+
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 93 "../../src/foreign-gml-parser.y"
+{
+   struct {
+      char *s;
+      int len;
+   } str;
+   void *tree;
+   double real;
+}
+/* Line 193 of yacc.c.  */
+#line 192 "foreign-gml-parser.c"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 216 of yacc.c.  */
+#line 217 "foreign-gml-parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+    int i;
+#endif
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+	     && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+    YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+      + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  6
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   14
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  10
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  7
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  12
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  17
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   264
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint8 yyprhs[] =
+{
+       0,     0,     3,     5,     8,    10,    13,    16,    19,    24,
+      27,    29,    31
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+      11,     0,    -1,    12,    -1,    12,     8,    -1,    13,    -1,
+      12,    13,    -1,    14,    15,    -1,    14,    16,    -1,    14,
+       6,    12,     7,    -1,    14,    14,    -1,     5,    -1,     4,
+      -1,     3,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,   121,   121,   122,   125,   126,   128,   130,   132,   134,
+     138,   141,   144
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "STRING", "NUM", "KEYWORD", "LISTOPEN",
+  "LISTCLOSE", "EOFF", "ERROR", "$accept", "input", "list", "keyvalue",
+  "key", "num", "string", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    10,    11,    11,    12,    12,    13,    13,    13,    13,
+      14,    15,    16
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     1,     2,     1,     2,     2,     2,     4,     2,
+       1,     1,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       0,    10,     0,     2,     4,     0,     1,     3,     5,    12,
+      11,     0,     9,     6,     7,     0,     8
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     2,     3,     4,     5,    13,    14
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -4
+static const yytype_int8 yypact[] =
+{
+       1,    -4,    10,     0,    -4,    -2,    -4,    -4,    -4,    -4,
+      -4,     1,    -4,    -4,    -4,     2,    -4
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+      -4,    -4,     3,    -3,     6,    -4,    -4
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+       8,     9,    10,     1,    11,     1,     1,     1,     7,    16,
+       6,    12,     8,     0,    15
+};
+
+static const yytype_int8 yycheck[] =
+{
+       3,     3,     4,     5,     6,     5,     5,     5,     8,     7,
+       0,     5,    15,    -1,    11
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     5,    11,    12,    13,    14,     0,     8,    13,     3,
+       4,     6,    14,    15,    16,    12,     7
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+	      (Loc).first_line, (Loc).first_column,	\
+	      (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc, scanner)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value, Location, context); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_gml_parsedata_t* context;
+#endif
+{
+  if (!yyvaluep)
+    return;
+  YYUSE (yylocationp);
+  YYUSE (context);
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_gml_parsedata_t* context;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  YY_LOCATION_PRINT (yyoutput, *yylocationp);
+  YYFPRINTF (yyoutput, ": ");
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    yytype_int16 *bottom;
+    yytype_int16 *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_gml_parsedata_t* context)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule, context)
+    YYSTYPE *yyvsp;
+    YYLTYPE *yylsp;
+    int yyrule;
+    igraph_i_gml_parsedata_t* context;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       , &(yylsp[(yyi + 1) - (yynrhs)])		       , context);
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, yylsp, Rule, context); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+	 constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+		    + sizeof yyexpecting - 1
+		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+		       * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+	 YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	  {
+	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+	      {
+		yycount = 1;
+		yysize = yysize0;
+		yyformat[sizeof yyunexpected - 1] = '\0';
+		break;
+	      }
+	    yyarg[yycount++] = yytname[yyx];
+	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+	    yysize_overflow |= (yysize1 < yysize);
+	    yysize = yysize1;
+	    yyfmt = yystpcpy (yyfmt, yyprefix);
+	    yyprefix = yyor;
+	  }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+	return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+	{
+	  /* Avoid sprintf, as that infringes on the user's name space.
+	     Don't have undefined behavior even if the translation
+	     produced a string with the wrong number of "%s"s.  */
+	  char *yyp = yyresult;
+	  int yyi = 0;
+	  while ((*yyp = *yyf) != '\0')
+	    {
+	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+		{
+		  yyp += yytnamerr (yyp, yyarg[yyi++]);
+		  yyf += 2;
+		}
+	      else
+		{
+		  yyp++;
+		  yyf++;
+		}
+	    }
+	}
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_gml_parsedata_t* context)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp, context)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+    YYLTYPE *yylocationp;
+    igraph_i_gml_parsedata_t* context;
+#endif
+{
+  YYUSE (yyvaluep);
+  YYUSE (yylocationp);
+  YYUSE (context);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+      case 5: /* "KEYWORD" */
+#line 116 "../../src/foreign-gml-parser.y"
+	{ igraph_Free((yyvaluep->str).s); };
+#line 1121 "foreign-gml-parser.c"
+	break;
+      case 12: /* "list" */
+#line 117 "../../src/foreign-gml-parser.y"
+	{ igraph_gml_tree_destroy((yyvaluep->tree)); };
+#line 1126 "foreign-gml-parser.c"
+	break;
+      case 13: /* "keyvalue" */
+#line 117 "../../src/foreign-gml-parser.y"
+	{ igraph_gml_tree_destroy((yyvaluep->tree)); };
+#line 1131 "foreign-gml-parser.c"
+	break;
+      case 14: /* "key" */
+#line 116 "../../src/foreign-gml-parser.y"
+	{ igraph_Free((yyvaluep->str).s); };
+#line 1136 "foreign-gml-parser.c"
+	break;
+      case 16: /* "string" */
+#line 116 "../../src/foreign-gml-parser.y"
+	{ igraph_Free((yyvaluep->str).s); };
+#line 1141 "foreign-gml-parser.c"
+	break;
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (igraph_i_gml_parsedata_t* context);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (igraph_i_gml_parsedata_t* context)
+#else
+int
+yyparse (context)
+    igraph_i_gml_parsedata_t* context;
+#endif
+#endif
+{
+  /* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+/* Location data for the look-ahead symbol.  */
+YYLTYPE yylloc;
+
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+  /* The location stack.  */
+  YYLTYPE yylsa[YYINITDEPTH];
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+  /* The locations where the error started and ended.  */
+  YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+  YYLTYPE yyloc;
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+  yylsp = yyls;
+#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+  /* Initialize the default location before parsing starts.  */
+  yylloc.first_line   = yylloc.last_line   = 1;
+  yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+	YYLTYPE *yyls1 = yyls;
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yyls1, yysize * sizeof (*yylsp),
+		    &yystacksize);
+	yyls = yyls1;
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+	YYSTACK_RELOCATE (yyls);
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+      yylsp = yyls + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+  *++yylsp = yylloc;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+  /* Default location.  */
+  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 121 "../../src/foreign-gml-parser.y"
+    { context->tree=(yyvsp[(1) - (1)].tree); }
+    break;
+
+  case 3:
+#line 122 "../../src/foreign-gml-parser.y"
+    { context->tree=(yyvsp[(1) - (2)].tree); }
+    break;
+
+  case 4:
+#line 125 "../../src/foreign-gml-parser.y"
+    { (yyval.tree)=(yyvsp[(1) - (1)].tree); }
+    break;
+
+  case 5:
+#line 126 "../../src/foreign-gml-parser.y"
+    { (yyval.tree)=igraph_i_gml_merge((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree)); }
+    break;
+
+  case 6:
+#line 129 "../../src/foreign-gml-parser.y"
+    { (yyval.tree)=igraph_i_gml_make_numeric((yyvsp[(1) - (2)].str).s, (yyvsp[(1) - (2)].str).len, (yyvsp[(2) - (2)].real)); }
+    break;
+
+  case 7:
+#line 131 "../../src/foreign-gml-parser.y"
+    { (yyval.tree)=igraph_i_gml_make_string((yyvsp[(1) - (2)].str).s, (yyvsp[(1) - (2)].str).len, (yyvsp[(2) - (2)].str).s, (yyvsp[(2) - (2)].str).len); }
+    break;
+
+  case 8:
+#line 133 "../../src/foreign-gml-parser.y"
+    { (yyval.tree)=igraph_i_gml_make_list((yyvsp[(1) - (4)].str).s, (yyvsp[(1) - (4)].str).len, (yyvsp[(3) - (4)].tree)); }
+    break;
+
+  case 9:
+#line 135 "../../src/foreign-gml-parser.y"
+    { (yyval.tree)=igraph_i_gml_make_numeric2((yyvsp[(1) - (2)].str).s, (yyvsp[(1) - (2)].str).len, (yyvsp[(2) - (2)].str).s, (yyvsp[(2) - (2)].str).len); }
+    break;
+
+  case 10:
+#line 138 "../../src/foreign-gml-parser.y"
+    { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), 
+					igraph_gml_yyget_leng(scanner), 
+					&(yyval.str)); USE((yyvsp[(1) - (1)].str)) }
+    break;
+
+  case 11:
+#line 141 "../../src/foreign-gml-parser.y"
+    { (yyval.real)=igraph_i_gml_get_real(igraph_gml_yyget_text(scanner), 
+				     igraph_gml_yyget_leng(scanner)); }
+    break;
+
+  case 12:
+#line 144 "../../src/foreign-gml-parser.y"
+    { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), 
+					 igraph_gml_yyget_leng(scanner), 
+					 &(yyval.str)); }
+    break;
+
+
+/* Line 1267 of yacc.c.  */
+#line 1525 "foreign-gml-parser.c"
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+  *++yylsp = yyloc;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (&yylloc, context, YY_("syntax error"));
+#else
+      {
+	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+	  {
+	    YYSIZE_T yyalloc = 2 * yysize;
+	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
+	    if (yymsg != yymsgbuf)
+	      YYSTACK_FREE (yymsg);
+	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+	    if (yymsg)
+	      yymsg_alloc = yyalloc;
+	    else
+	      {
+		yymsg = yymsgbuf;
+		yymsg_alloc = sizeof yymsgbuf;
+	      }
+	  }
+
+	if (0 < yysize && yysize <= yymsg_alloc)
+	  {
+	    (void) yysyntax_error (yymsg, yystate, yychar);
+	    yyerror (&yylloc, context, yymsg);
+	  }
+	else
+	  {
+	    yyerror (&yylloc, context, YY_("syntax error"));
+	    if (yysize != 0)
+	      goto yyexhaustedlab;
+	  }
+      }
+#endif
+    }
+
+  yyerror_range[0] = yylloc;
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval, &yylloc, context);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  yyerror_range[0] = yylsp[1-yylen];
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      yyerror_range[0] = *yylsp;
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+  yyerror_range[1] = yylloc;
+  /* Using YYLLOC is tempting, but would change the location of
+     the look-ahead.  YYLOC is available though.  */
+  YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+  *++yylsp = yyloc;
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (&yylloc, context, YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+		 yytoken, &yylval, &yylloc, context);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+#line 148 "../../src/foreign-gml-parser.y"
+
+
+int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, 
+		       const char *s) {
+  snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, 
+	   "Parse error in GML file, line %i (%s)", 
+	   locp->first_line, s);
+  return 0;
+}
+
+void igraph_i_gml_get_keyword(char *s, int len, void *res) {
+  struct { char *s; int len; } *p=res;
+  p->s=igraph_Calloc(len+1, char);
+  if (!p->s) { 
+    igraph_error("Cannot read GML file", __FILE__, __LINE__, IGRAPH_PARSEERROR);
+  }
+  memcpy(p->s, s, sizeof(char)*len);
+  p->s[len]='\0';
+  p->len=len;
+}
+
+void igraph_i_gml_get_string(char *s, int len, void *res) {
+  struct { char *s; int len; } *p=res;
+  p->s=igraph_Calloc(len-1, char);
+  if (!p->s) { 
+    igraph_error("Cannot read GML file", __FILE__, __LINE__, IGRAPH_PARSEERROR);
+  }
+  memcpy(p->s, s+1, sizeof(char)*(len-2));
+  p->s[len-2]='\0';
+  p->len=len-2;
+}
+
+double igraph_i_gml_get_real(char *s, int len) {
+  igraph_real_t num;
+  char tmp=s[len];
+  s[len]='\0';
+  sscanf(s, "%lf", &num);
+  s[len]=tmp;
+  return num;
+} 
+
+igraph_gml_tree_t *igraph_i_gml_make_numeric(char* s, int len, double value) {
+  igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t);
+  if (!t) { 
+    igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM);
+    return 0;
+  }
+  if (floor(value)==value) {
+    igraph_gml_tree_init_integer(t, s, len, value);
+  } else {
+    igraph_gml_tree_init_real(t, s, len, value);
+  }
+  
+  return t;
+}
+
+igraph_gml_tree_t *igraph_i_gml_make_numeric2(char* s, int len, 
+					      char *v, int vlen) {
+  igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t);
+  char tmp=v[vlen];
+  igraph_real_t value=0;
+  if (!t) { 
+    igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM);
+    return 0;
+  }
+  v[vlen]='\0';
+  if (strcasecmp(v, "inf")) {
+    value=IGRAPH_INFINITY;
+  } else if (strcasecmp(v, "nan")) {
+    value=IGRAPH_NAN;
+  } else {
+    igraph_error("Parse error", __FILE__, __LINE__, IGRAPH_PARSEERROR);
+  }
+  v[vlen]=tmp;
+  igraph_gml_tree_init_real(t, s, len, value);  
+
+  return t;
+}
+
+igraph_gml_tree_t *igraph_i_gml_make_string(char* s, int len, 
+					    char *value, int valuelen) {
+  igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t);
+  if (!t) { 
+    igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM);
+    return 0;
+  }
+  igraph_gml_tree_init_string(t, s, len, value, valuelen);
+
+  return t;
+}
+
+igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, 
+					  igraph_gml_tree_t *list) {
+  
+  igraph_gml_tree_t *t=igraph_Calloc(1, igraph_gml_tree_t);
+  if (!t) { 
+    igraph_error("Cannot build GML tree", __FILE__, __LINE__, IGRAPH_ENOMEM);
+    return 0;
+  }
+  igraph_gml_tree_init_tree(t, s, len, list);
+
+  return t;
+}
+
+igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) {
+
+  igraph_gml_tree_mergedest(t1, t2);
+  igraph_Free(t2);
+
+  return t1;
+}
+
diff --git a/igraph/src/foreign-graphml.c b/igraph/src/foreign-graphml.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-graphml.c
@@ -0,0 +1,1846 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph R package.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include <locale.h>
+#include "igraph_foreign.h"
+#include "config.h"
+#include <math.h>               /* isnan */
+#include "igraph_math.h"
+#include "igraph_attributes.h"
+#include "igraph_interface.h"
+#include "igraph_types_internal.h"
+
+#include <ctype.h>      /* isspace */
+#include <string.h>
+#include "igraph_memory.h"
+#include <stdarg.h>         /* va_start & co */
+
+#define GRAPHML_NAMESPACE_URI "http://graphml.graphdrawing.org/xmlns"
+
+#if HAVE_LIBXML == 1
+#include <libxml/encoding.h>
+#include <libxml/parser.h>
+
+xmlEntity blankEntityStruct = {
+#ifndef XML_WITHOUT_CORBA
+    0,
+#endif
+    XML_ENTITY_DECL,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    XML_EXTERNAL_GENERAL_PARSED_ENTITY,
+    0,
+    0,
+    0,
+    0,
+    0,
+    1
+};
+
+xmlEntityPtr blankEntity = &blankEntityStruct;
+
+#define GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) do {  \
+        if (state->successful) {                                    \
+            igraph_error(msg, __FILE__, __LINE__, code);              \
+            igraph_i_graphml_sax_handler_error(state, msg);           \
+        }                                                           \
+    } while (0)
+#define GRAPHML_PARSE_ERROR(state, msg) \
+    GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, IGRAPH_PARSEERROR)
+#define RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) do {  \
+        GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code);            \
+        return;                                                     \
+    } while (1)
+#define RETURN_GRAPHML_PARSE_ERROR(state, msg) do {           \
+        GRAPHML_PARSE_ERROR(state, msg);                            \
+        return;                                                     \
+    } while (1)
+
+/* TODO: proper error handling */
+
+typedef struct igraph_i_graphml_attribute_record_t {
+    const char *id;           /* GraphML id */
+    enum { I_GRAPHML_BOOLEAN, I_GRAPHML_INTEGER, I_GRAPHML_LONG,
+           I_GRAPHML_FLOAT, I_GRAPHML_DOUBLE, I_GRAPHML_STRING,
+           I_GRAPHML_UNKNOWN_TYPE
+         } type; /* GraphML type */
+    union {
+        igraph_real_t as_numeric;
+        igraph_bool_t as_boolean;
+        char* as_string;
+    } default_value;   /* Default value of the attribute, if any */
+    igraph_attribute_record_t record;
+} igraph_i_graphml_attribute_record_t;
+
+struct igraph_i_graphml_parser_state {
+    enum { START, INSIDE_GRAPHML, INSIDE_GRAPH, INSIDE_NODE, INSIDE_EDGE,
+           INSIDE_KEY, INSIDE_DEFAULT, INSIDE_DATA, FINISH, UNKNOWN, ERROR
+         } st;
+    igraph_t *g;
+    igraph_trie_t node_trie;
+    igraph_strvector_t edgeids;
+    igraph_vector_t edgelist;
+    igraph_vector_int_t prev_state_stack;
+    unsigned int unknown_depth;
+    int index;
+    igraph_bool_t successful, edges_directed, destroyed;
+    igraph_trie_t v_names;
+    igraph_vector_ptr_t v_attrs;
+    igraph_trie_t e_names;
+    igraph_vector_ptr_t e_attrs;
+    igraph_trie_t g_names;
+    igraph_vector_ptr_t g_attrs;
+    igraph_i_graphml_attribute_record_t* current_attr_record;
+    xmlChar *data_key;
+    igraph_attribute_elemtype_t data_type;
+    char *error_message;
+    char *data_char;
+    long int act_node;
+    igraph_bool_t ignore_namespaces;
+};
+
+static void igraph_i_report_unhandled_attribute_target(const char* target,
+        const char* file, int line) {
+    igraph_warningf("Attribute target '%s' is not handled; ignoring corresponding "
+                    "attribute specifications", file, line, 0, target);
+}
+
+igraph_real_t igraph_i_graphml_parse_numeric(const char* char_data,
+        igraph_real_t default_value) {
+    double result;
+
+    if (char_data == 0) {
+        return default_value;
+    }
+
+    if (sscanf(char_data, "%lf", &result) == 0) {
+        return default_value;
+    }
+
+    return result;
+}
+
+igraph_bool_t igraph_i_graphml_parse_boolean(const char* char_data,
+        igraph_bool_t default_value) {
+    int value;
+    if (char_data == 0) {
+        return default_value;
+    }
+    if (!strcasecmp("true", char_data)) {
+        return 1;
+    }
+    if (!strcasecmp("yes", char_data)) {
+        return 1;
+    }
+    if (!strcasecmp("false", char_data)) {
+        return 0;
+    }
+    if (!strcasecmp("no", char_data)) {
+        return 0;
+    }
+    if (sscanf(char_data, "%d", &value) == 0) {
+        return default_value;
+    }
+    return value != 0;
+}
+
+void igraph_i_graphml_attribute_record_destroy(igraph_i_graphml_attribute_record_t* rec) {
+    if (rec->record.type == IGRAPH_ATTRIBUTE_NUMERIC) {
+        if (rec->record.value != 0) {
+            igraph_vector_destroy((igraph_vector_t*)rec->record.value);
+            igraph_Free(rec->record.value);
+        }
+    } else if (rec->record.type == IGRAPH_ATTRIBUTE_STRING) {
+        if (rec->record.value != 0) {
+            igraph_strvector_destroy((igraph_strvector_t*)rec->record.value);
+            if (rec->default_value.as_string != 0) {
+                igraph_Free(rec->default_value.as_string);
+            }
+            igraph_Free(rec->record.value);
+        }
+    } else if (rec->record.type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+        if (rec->record.value != 0) {
+            igraph_vector_bool_destroy((igraph_vector_bool_t*)rec->record.value);
+            igraph_Free(rec->record.value);
+        }
+    }
+    if (rec->id != 0) {
+        igraph_Free(rec->id);
+    }
+    if (rec->record.name != 0) {
+        igraph_Free(rec->record.name);
+    }
+}
+
+void igraph_i_graphml_destroy_state(struct igraph_i_graphml_parser_state* state) {
+    if (state->destroyed) {
+        return;
+    }
+    state->destroyed = 1;
+
+    igraph_trie_destroy(&state->node_trie);
+    igraph_strvector_destroy(&state->edgeids);
+    igraph_trie_destroy(&state->v_names);
+    igraph_trie_destroy(&state->e_names);
+    igraph_trie_destroy(&state->g_names);
+    igraph_vector_destroy(&state->edgelist);
+    igraph_vector_int_destroy(&state->prev_state_stack);
+
+    if (state->error_message) {
+        free(state->error_message);
+    }
+    if (state->data_key) {
+        free(state->data_key);
+    }
+    if (state->data_char) {
+        free(state->data_char);
+    }
+
+    igraph_vector_ptr_destroy_all(&state->v_attrs);
+    igraph_vector_ptr_destroy_all(&state->e_attrs);
+    igraph_vector_ptr_destroy_all(&state->g_attrs);
+
+    IGRAPH_FINALLY_CLEAN(1);
+}
+
+void igraph_i_graphml_sax_handler_error(void *state0, const char* msg, ...) {
+    struct igraph_i_graphml_parser_state *state =
+        (struct igraph_i_graphml_parser_state*)state0;
+    va_list ap;
+
+    va_start(ap, msg);
+
+    if (state->error_message == 0) {
+        state->error_message = igraph_Calloc(4096, char);
+    }
+
+    state->successful = 0;
+    state->st = ERROR;
+    vsnprintf(state->error_message, 4096, msg, ap);
+
+    va_end(ap);
+}
+
+xmlEntityPtr igraph_i_graphml_sax_handler_get_entity(void *state0,
+        const xmlChar* name) {
+    xmlEntityPtr predef = xmlGetPredefinedEntity(name);
+    IGRAPH_UNUSED(state0);
+    if (predef != NULL) {
+        return predef;
+    }
+    IGRAPH_WARNING("unknown XML entity found\n");
+    return blankEntity;
+}
+
+void igraph_i_graphml_handle_unknown_start_tag(struct igraph_i_graphml_parser_state *state) {
+    if (state->st != UNKNOWN) {
+        igraph_vector_int_push_back(&state->prev_state_stack, state->st);
+        state->st = UNKNOWN;
+        state->unknown_depth = 1;
+    } else {
+        state->unknown_depth++;
+    }
+}
+
+void igraph_i_graphml_sax_handler_start_document(void *state0) {
+    struct igraph_i_graphml_parser_state *state =
+        (struct igraph_i_graphml_parser_state*)state0;
+    int ret;
+
+    state->st = START;
+    state->successful = 1;
+    state->edges_directed = 0;
+    state->destroyed = 0;
+    state->data_key = 0;
+    state->error_message = 0;
+    state->data_char = 0;
+    state->unknown_depth = 0;
+    state->ignore_namespaces = 0;
+
+    ret = igraph_vector_int_init(&state->prev_state_stack, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    ret = igraph_vector_int_reserve(&state->prev_state_stack, 32);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &state->prev_state_stack);
+
+    ret = igraph_vector_ptr_init(&state->v_attrs, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->v_attrs,
+                                          igraph_i_graphml_attribute_record_destroy);
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &state->v_attrs);
+
+    ret = igraph_vector_ptr_init(&state->e_attrs, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->e_attrs,
+                                          igraph_i_graphml_attribute_record_destroy);
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &state->e_attrs);
+
+    ret = igraph_vector_ptr_init(&state->g_attrs, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&state->g_attrs,
+                                          igraph_i_graphml_attribute_record_destroy);
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &state->g_attrs);
+
+    ret = igraph_vector_init(&state->edgelist, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_FINALLY(igraph_vector_destroy, &state->edgelist);
+
+    ret = igraph_trie_init(&state->node_trie, 1);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_FINALLY(igraph_trie_destroy, &state->node_trie);
+
+    ret = igraph_strvector_init(&state->edgeids, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_FINALLY(igraph_strvector_destroy, &state->edgeids);
+
+    ret = igraph_trie_init(&state->v_names, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_FINALLY(igraph_trie_destroy, &state->v_names);
+
+    ret = igraph_trie_init(&state->e_names, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_FINALLY(igraph_trie_destroy, &state->e_names);
+
+    ret = igraph_trie_init(&state->g_names, 0);
+    if (ret) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+    }
+    IGRAPH_FINALLY(igraph_trie_destroy, &state->g_names);
+
+    IGRAPH_FINALLY_CLEAN(10);
+    IGRAPH_FINALLY(igraph_i_graphml_destroy_state, state);
+}
+
+void igraph_i_graphml_sax_handler_end_document(void *state0) {
+    struct igraph_i_graphml_parser_state *state =
+        (struct igraph_i_graphml_parser_state*)state0;
+    long i, l;
+    int r;
+    igraph_attribute_record_t idrec, eidrec;
+    const char *idstr = "id";
+    igraph_bool_t already_has_vertex_id = 0, already_has_edge_id = 0;
+
+    if (!state->successful) {
+        return;
+    }
+
+    if (state->index < 0) {
+
+        igraph_vector_ptr_t vattr, eattr, gattr;
+        long int esize = igraph_vector_ptr_size(&state->e_attrs);
+        const void **tmp;
+        r = igraph_vector_ptr_init(&vattr,
+                                   igraph_vector_ptr_size(&state->v_attrs) + 1);
+        if (r) {
+            igraph_error("Cannot parse GraphML file", __FILE__, __LINE__, r);
+            igraph_i_graphml_sax_handler_error(state, "Cannot parse GraphML file");
+            return;
+        }
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattr);
+        if (igraph_strvector_size(&state->edgeids) != 0) {
+            esize++;
+        }
+        r = igraph_vector_ptr_init(&eattr, esize);
+        if (r) {
+            igraph_error("Cannot parse GraphML file", __FILE__, __LINE__, r);
+            igraph_i_graphml_sax_handler_error(state, "Cannot parse GraphML file");
+            return;
+        }
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &eattr);
+        r = igraph_vector_ptr_init(&gattr, igraph_vector_ptr_size(&state->g_attrs));
+        if (r) {
+            igraph_error("Cannot parse GraphML file", __FILE__, __LINE__, r);
+            igraph_i_graphml_sax_handler_error(state, "Cannot parse GraphML file");
+            return;
+        }
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &gattr);
+
+        for (i = 0; i < igraph_vector_ptr_size(&state->v_attrs); i++) {
+            igraph_i_graphml_attribute_record_t *graphmlrec =
+                VECTOR(state->v_attrs)[i];
+            igraph_attribute_record_t *rec = &graphmlrec->record;
+
+            /* Check that the name of the vertex attribute is not 'id'.
+            If it is then we cannot the complimentary 'id' attribute. */
+            if (! strcmp(rec->name, idstr)) {
+                already_has_vertex_id = 1;
+            }
+
+            if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_vector_t *vec = (igraph_vector_t*)rec->value;
+                long int origsize = igraph_vector_size(vec);
+                long int nodes = igraph_trie_size(&state->node_trie);
+                igraph_vector_resize(vec, nodes);
+                for (l = origsize; l < nodes; l++) {
+                    VECTOR(*vec)[l] = graphmlrec->default_value.as_numeric;
+                }
+            } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+                igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value;
+                long int origsize = igraph_strvector_size(strvec);
+                long int nodes = igraph_trie_size(&state->node_trie);
+                igraph_strvector_resize(strvec, nodes);
+                for (l = origsize; l < nodes; l++) {
+                    igraph_strvector_set(strvec, l, graphmlrec->default_value.as_string);
+                }
+            } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                igraph_vector_bool_t *boolvec = (igraph_vector_bool_t*)rec->value;
+                long int origsize = igraph_vector_bool_size(boolvec);
+                long int nodes = igraph_trie_size(&state->node_trie);
+                igraph_vector_bool_resize(boolvec, nodes);
+                for (l = origsize; l < nodes; l++) {
+                    VECTOR(*boolvec)[l] = graphmlrec->default_value.as_boolean;
+                }
+            }
+            VECTOR(vattr)[i] = rec;
+        }
+        if (!already_has_vertex_id) {
+            idrec.name = idstr;
+            idrec.type = IGRAPH_ATTRIBUTE_STRING;
+            tmp = &idrec.value;
+            igraph_trie_getkeys(&state->node_trie, (const igraph_strvector_t **)tmp);
+            VECTOR(vattr)[i] = &idrec;
+        } else {
+            igraph_vector_ptr_pop_back(&vattr);
+        }
+
+        for (i = 0; i < igraph_vector_ptr_size(&state->e_attrs); i++) {
+            igraph_i_graphml_attribute_record_t *graphmlrec =
+                VECTOR(state->e_attrs)[i];
+            igraph_attribute_record_t *rec = &graphmlrec->record;
+
+            if (! strcmp(rec->name, idstr)) {
+                already_has_edge_id = 1;
+            }
+
+            if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_vector_t *vec = (igraph_vector_t*)rec->value;
+                long int origsize = igraph_vector_size(vec);
+                long int edges = igraph_vector_size(&state->edgelist) / 2;
+                igraph_vector_resize(vec, edges);
+                for (l = origsize; l < edges; l++) {
+                    VECTOR(*vec)[l] = graphmlrec->default_value.as_numeric;
+                }
+            } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+                igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value;
+                long int origsize = igraph_strvector_size(strvec);
+                long int edges = igraph_vector_size(&state->edgelist) / 2;
+                igraph_strvector_resize(strvec, edges);
+                for (l = origsize; l < edges; l++) {
+                    igraph_strvector_set(strvec, l, graphmlrec->default_value.as_string);
+                }
+            } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                igraph_vector_bool_t *boolvec = (igraph_vector_bool_t*)rec->value;
+                long int origsize = igraph_vector_bool_size(boolvec);
+                long int edges = igraph_vector_size(&state->edgelist) / 2;
+                igraph_vector_bool_resize(boolvec, edges);
+                for (l = origsize; l < edges; l++) {
+                    VECTOR(*boolvec)[l] = graphmlrec->default_value.as_boolean;
+                }
+            }
+            VECTOR(eattr)[i] = rec;
+        }
+        if (igraph_strvector_size(&state->edgeids) != 0) {
+            if (!already_has_edge_id) {
+                long int origsize = igraph_strvector_size(&state->edgeids);
+                eidrec.name = idstr;
+                eidrec.type = IGRAPH_ATTRIBUTE_STRING;
+                igraph_strvector_resize(&state->edgeids,
+                                        igraph_vector_size(&state->edgelist) / 2);
+                for (; origsize < igraph_strvector_size(&state->edgeids); origsize++) {
+                    igraph_strvector_set(&state->edgeids, origsize, "");
+                }
+                eidrec.value = &state->edgeids;
+                VECTOR(eattr)[(long int)igraph_vector_ptr_size(&eattr) - 1] = &eidrec;
+            } else {
+                igraph_vector_ptr_pop_back(&eattr);
+                IGRAPH_WARNING("Could not add edge ids, "
+                               "there is already an 'id' edge attribute");
+            }
+        }
+
+        for (i = 0; i < igraph_vector_ptr_size(&state->g_attrs); i++) {
+            igraph_i_graphml_attribute_record_t *graphmlrec =
+                VECTOR(state->g_attrs)[i];
+            igraph_attribute_record_t *rec = &graphmlrec->record;
+            if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_vector_t *vec = (igraph_vector_t*)rec->value;
+                long int origsize = igraph_vector_size(vec);
+                igraph_vector_resize(vec, 1);
+                for (l = origsize; l < 1; l++) {
+                    VECTOR(*vec)[l] = graphmlrec->default_value.as_numeric;
+                }
+            } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+                igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value;
+                long int origsize = igraph_strvector_size(strvec);
+                igraph_strvector_resize(strvec, 1);
+                for (l = origsize; l < 1; l++) {
+                    igraph_strvector_set(strvec, l, graphmlrec->default_value.as_string);
+                }
+            } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                igraph_vector_bool_t *boolvec = (igraph_vector_bool_t*)rec->value;
+                long int origsize = igraph_vector_bool_size(boolvec);
+                igraph_vector_bool_resize(boolvec, 1);
+                for (l = origsize; l < 1; l++) {
+                    VECTOR(*boolvec)[l] = graphmlrec->default_value.as_boolean;
+                }
+            }
+            VECTOR(gattr)[i] = rec;
+        }
+
+        igraph_empty_attrs(state->g, 0, state->edges_directed, &gattr);
+        igraph_add_vertices(state->g, (igraph_integer_t)
+                            igraph_trie_size(&state->node_trie), &vattr);
+        igraph_add_edges(state->g, &state->edgelist, &eattr);
+
+        igraph_vector_ptr_destroy(&vattr);
+        igraph_vector_ptr_destroy(&eattr);
+        igraph_vector_ptr_destroy(&gattr);
+        IGRAPH_FINALLY_CLEAN(3);
+    }
+
+    igraph_i_graphml_destroy_state(state);
+}
+
+#define toXmlChar(a)   (BAD_CAST(a))
+#define fromXmlChar(a) ((char *)(a)) /* not the most elegant way... */
+
+#define XML_ATTR_LOCALNAME(it) (*(it))
+#define XML_ATTR_PREFIX(it) (*(it+1))
+#define XML_ATTR_URI(it) (*(it+2))
+#define XML_ATTR_VALUE_START(it) (*(it+3))
+#define XML_ATTR_VALUE_END(it) (*(it+4))
+#define XML_ATTR_VALUE(it) *(it+3), (*(it+4))-(*(it+3))
+
+igraph_i_graphml_attribute_record_t* igraph_i_graphml_add_attribute_key(
+    const xmlChar** attrs, int nb_attrs,
+    struct igraph_i_graphml_parser_state *state) {
+    xmlChar **it;
+    xmlChar *localname;
+    igraph_trie_t *trie = 0;
+    igraph_vector_ptr_t *ptrvector = 0;
+    long int id;
+    unsigned short int skip = 0;
+    int i, ret;
+    igraph_i_graphml_attribute_record_t *rec;
+
+    if (!state->successful) {
+        return 0;
+    }
+
+    rec = igraph_Calloc(1, igraph_i_graphml_attribute_record_t);
+    if (rec == 0) {
+        GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM);
+        return 0;
+    }
+    IGRAPH_FINALLY(igraph_free, rec);
+
+    rec->type = I_GRAPHML_UNKNOWN_TYPE;
+
+    for (i = 0, it = (xmlChar**)attrs; i < nb_attrs; i++, it += 5) {
+        if (XML_ATTR_URI(it) != 0 &&
+            !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) {
+            continue;
+        }
+
+        localname = XML_ATTR_LOCALNAME(it);
+
+        if (xmlStrEqual(localname, toXmlChar("id"))) {
+            rec->id = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it)));
+        } else if (xmlStrEqual(localname, toXmlChar("attr.name"))) {
+            rec->record.name = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it)));
+        } else if (xmlStrEqual(localname, toXmlChar("attr.type"))) {
+            if (!xmlStrncmp(toXmlChar("boolean"), XML_ATTR_VALUE(it))) {
+                rec->type = I_GRAPHML_BOOLEAN;
+                rec->record.type = IGRAPH_ATTRIBUTE_BOOLEAN;
+                rec->default_value.as_boolean = 0;
+            } else if (!xmlStrncmp(toXmlChar("string"), XML_ATTR_VALUE(it))) {
+                rec->type = I_GRAPHML_STRING;
+                rec->record.type = IGRAPH_ATTRIBUTE_STRING;
+                rec->default_value.as_string = strdup("");
+            } else if (!xmlStrncmp(toXmlChar("float"), XML_ATTR_VALUE(it))) {
+                rec->type = I_GRAPHML_FLOAT;
+                rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC;
+                rec->default_value.as_numeric = IGRAPH_NAN;
+            } else if (!xmlStrncmp(toXmlChar("double"), XML_ATTR_VALUE(it))) {
+                rec->type = I_GRAPHML_DOUBLE;
+                rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC;
+                rec->default_value.as_numeric = IGRAPH_NAN;
+            } else if (!xmlStrncmp(toXmlChar("int"), XML_ATTR_VALUE(it))) {
+                rec->type = I_GRAPHML_INTEGER;
+                rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC;
+                rec->default_value.as_numeric = IGRAPH_NAN;
+            } else if (!xmlStrncmp(toXmlChar("long"), XML_ATTR_VALUE(it))) {
+                rec->type = I_GRAPHML_LONG;
+                rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC;
+                rec->default_value.as_numeric = IGRAPH_NAN;
+            } else {
+                GRAPHML_PARSE_ERROR(state,
+                                    "Cannot parse GraphML file, unknown attribute type");
+                return 0;
+            }
+        } else if (xmlStrEqual(*it, toXmlChar("for"))) {
+            /* graph, vertex or edge attribute? */
+            if (!xmlStrncmp(toXmlChar("graph"), XML_ATTR_VALUE(it))) {
+                trie = &state->g_names;
+                ptrvector = &state->g_attrs;
+            } else if (!xmlStrncmp(toXmlChar("node"), XML_ATTR_VALUE(it))) {
+                trie = &state->v_names;
+                ptrvector = &state->v_attrs;
+            } else if (!xmlStrncmp(toXmlChar("edge"), XML_ATTR_VALUE(it))) {
+                trie = &state->e_names;
+                ptrvector = &state->e_attrs;
+            } else if (!xmlStrncmp(toXmlChar("graphml"), XML_ATTR_VALUE(it))) {
+                igraph_i_report_unhandled_attribute_target("graphml", __FILE__, __LINE__);
+                skip = 1;
+            } else if (!xmlStrncmp(toXmlChar("hyperedge"), XML_ATTR_VALUE(it))) {
+                igraph_i_report_unhandled_attribute_target("hyperedge", __FILE__, __LINE__);
+                skip = 1;
+            } else if (!xmlStrncmp(toXmlChar("port"), XML_ATTR_VALUE(it))) {
+                igraph_i_report_unhandled_attribute_target("port", __FILE__, __LINE__);
+                skip = 1;
+            } else if (!xmlStrncmp(toXmlChar("endpoint"), XML_ATTR_VALUE(it))) {
+                igraph_i_report_unhandled_attribute_target("endpoint", __FILE__, __LINE__);
+                skip = 1;
+            } else if (!xmlStrncmp(toXmlChar("all"), XML_ATTR_VALUE(it))) {
+                /* TODO: we should handle this */
+                igraph_i_report_unhandled_attribute_target("all", __FILE__, __LINE__);
+                skip = 1;
+            } else {
+                GRAPHML_PARSE_ERROR(state,
+                                    "Cannot parse GraphML file, unknown value in the 'for' attribute of a <key> tag");
+                return 0;
+            }
+        }
+    }
+
+    /* throw an error if there is no ID; this is a clear violation of the GraphML
+     * DTD */
+    if (rec->id == 0) {
+        GRAPHML_PARSE_ERROR(state, "Found <key> tag with no 'id' attribute");
+        return 0;
+    }
+
+    /* in case of a missing attr.name attribute, use the id as the attribute name */
+    if (rec->record.name == 0) {
+        rec->record.name = strdup(rec->id);
+    }
+
+    /* if the attribute type is missing, throw an error */
+    if (!skip && rec->type == I_GRAPHML_UNKNOWN_TYPE) {
+        igraph_warningf("Ignoring <key id=\"%s\"> because of a missing or unknown 'attr.type' attribute", __FILE__, __LINE__, 0, rec->id);
+        skip = 1;
+    }
+
+    /* if the value of the 'for' attribute was unknown, throw an error */
+    if (!skip && trie == 0) {
+        GRAPHML_PARSE_ERROR(state,
+                            "Cannot parse GraphML file, missing 'for' attribute in a <key> tag");
+        return 0;
+    }
+
+    /* if the code above requested skipping the attribute, free everything and
+     * return */
+    if (skip) {
+        igraph_free(rec);
+        IGRAPH_FINALLY_CLEAN(1);
+        return 0;
+    }
+
+    /* add to trie, attribues */
+    igraph_trie_get(trie, rec->id, &id);
+    if (id != igraph_trie_size(trie) - 1) {
+        GRAPHML_PARSE_ERROR(state, "Cannot parse GraphML file, duplicate attribute");
+        return 0;
+    }
+
+    ret = igraph_vector_ptr_push_back(ptrvector, rec);
+    if (ret) {
+        GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot read GraphML file", ret);
+        return 0;
+    }
+
+    /* Ownership of 'rec' is now taken by ptrvector so we can clean the
+     * finally stack */
+    IGRAPH_FINALLY_CLEAN(1);  /* rec */
+
+    /* create the attribute values */
+    switch (rec->record.type) {
+        igraph_vector_t *vec;
+        igraph_vector_bool_t *boolvec;
+        igraph_strvector_t *strvec;
+    case IGRAPH_ATTRIBUTE_BOOLEAN:
+        boolvec = igraph_Calloc(1, igraph_vector_bool_t);
+        if (boolvec == 0) {
+            GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM);
+            return 0;
+        }
+        rec->record.value = boolvec;
+        igraph_vector_bool_init(boolvec, 0);
+        break;
+    case IGRAPH_ATTRIBUTE_NUMERIC:
+        vec = igraph_Calloc(1, igraph_vector_t);
+        if (vec == 0) {
+            GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM);
+            return 0;
+        }
+        rec->record.value = vec;
+        igraph_vector_init(vec, 0);
+        break;
+    case IGRAPH_ATTRIBUTE_STRING:
+        strvec = igraph_Calloc(1, igraph_strvector_t);
+        if (strvec == 0) {
+            GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM);
+            return 0;
+        }
+        rec->record.value = strvec;
+        igraph_strvector_init(strvec, 0);
+        break;
+    default: break;
+    }
+
+    return rec;
+}
+
+void igraph_i_graphml_attribute_data_setup(struct igraph_i_graphml_parser_state *state,
+        const xmlChar **attrs,
+        int nb_attrs,
+        igraph_attribute_elemtype_t type) {
+    xmlChar **it;
+    int i;
+
+    if (!state->successful) {
+        return;
+    }
+
+    for (i = 0, it = (xmlChar**)attrs; i < nb_attrs; i++, it += 5) {
+        if (XML_ATTR_URI(it) != 0 &&
+            !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) {
+            continue;
+        }
+
+        if (xmlStrEqual(*it, toXmlChar("key"))) {
+            if (state->data_key) {
+                free(state->data_key);
+            }
+            state->data_key = xmlStrndup(XML_ATTR_VALUE(it));
+            if (state->data_char) {
+                free(state->data_char);
+            }
+            state->data_char = 0;
+            state->data_type = type;
+        } else {
+            /* ignore */
+        }
+    }
+}
+
+void igraph_i_graphml_append_to_data_char(struct igraph_i_graphml_parser_state *state,
+        const xmlChar *data, int len) {
+    long int data_char_new_start = 0;
+
+    if (!state->successful) {
+        return;
+    }
+
+    if (state->data_char) {
+        data_char_new_start = (long int) strlen(state->data_char);
+        state->data_char = igraph_Realloc(state->data_char,
+                                          (size_t)(data_char_new_start + len + 1), char);
+    } else {
+        state->data_char = igraph_Calloc((size_t) len + 1, char);
+    }
+    if (state->data_char == 0) {
+        RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM);
+    }
+    memcpy(state->data_char + data_char_new_start, data,
+           (size_t) len * sizeof(xmlChar));
+    state->data_char[data_char_new_start + len] = '\0';
+}
+
+void igraph_i_graphml_attribute_data_finish(struct igraph_i_graphml_parser_state *state) {
+    const char *key = fromXmlChar(state->data_key);
+    igraph_attribute_elemtype_t type = state->data_type;
+    igraph_trie_t *trie = 0;
+    igraph_vector_ptr_t *ptrvector = 0;
+    igraph_i_graphml_attribute_record_t *graphmlrec;
+    igraph_attribute_record_t *rec;
+    long int recid, id = 0;
+    int ret;
+
+    switch (type) {
+    case IGRAPH_ATTRIBUTE_GRAPH:
+        trie = &state->g_names;
+        ptrvector = &state->g_attrs;
+        id = 0;
+        break;
+    case IGRAPH_ATTRIBUTE_VERTEX:
+        trie = &state->v_names;
+        ptrvector = &state->v_attrs;
+        id = state->act_node;
+        break;
+    case IGRAPH_ATTRIBUTE_EDGE:
+        trie = &state->e_names;
+        ptrvector = &state->e_attrs;
+        id = igraph_vector_size(&state->edgelist) / 2 - 1; /* hack */
+        break;
+    default:
+        /* impossible */
+        break;
+    }
+
+    if (key == 0) {
+        /* no key specified, issue a warning */
+        igraph_warningf(
+            "missing attribute key in a <data> tag, ignoring attribute",
+            __FILE__, __LINE__, 0,
+            key
+        );
+        igraph_Free(state->data_char);
+        return;
+    }
+
+    igraph_trie_check(trie, key, &recid);
+    if (recid < 0) {
+        /* no such attribute key, issue a warning */
+        igraph_warningf(
+            "unknown attribute key '%s' in a <data> tag, ignoring attribute",
+            __FILE__, __LINE__, 0,
+            key
+        );
+        igraph_Free(state->data_char);
+        return;
+    }
+
+    graphmlrec = VECTOR(*ptrvector)[recid];
+    rec = &graphmlrec->record;
+
+    switch (rec->type) {
+        igraph_vector_bool_t *boolvec;
+        igraph_vector_t *vec;
+        igraph_strvector_t *strvec;
+        long int s, i;
+        const char* strvalue;
+    case IGRAPH_ATTRIBUTE_BOOLEAN:
+        boolvec = (igraph_vector_bool_t *)rec->value;
+        s = igraph_vector_bool_size(boolvec);
+        if (id >= s) {
+            ret = igraph_vector_bool_resize(boolvec, id + 1);
+            if (ret) {
+                RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+            }
+            for (i = s; i < id; i++) {
+                VECTOR(*boolvec)[i] = graphmlrec->default_value.as_boolean;
+            }
+        }
+        VECTOR(*boolvec)[id] = igraph_i_graphml_parse_boolean(state->data_char,
+                               graphmlrec->default_value.as_boolean);
+        break;
+    case IGRAPH_ATTRIBUTE_NUMERIC:
+        vec = (igraph_vector_t *)rec->value;
+        s = igraph_vector_size(vec);
+        if (id >= s) {
+            ret = igraph_vector_resize(vec, id + 1);
+            if (ret) {
+                RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+            }
+            for (i = s; i < id; i++) {
+                VECTOR(*vec)[i] = graphmlrec->default_value.as_numeric;
+            }
+        }
+        VECTOR(*vec)[id] = igraph_i_graphml_parse_numeric(state->data_char,
+                           graphmlrec->default_value.as_numeric);
+        break;
+    case IGRAPH_ATTRIBUTE_STRING:
+        strvec = (igraph_strvector_t *)rec->value;
+        s = igraph_strvector_size(strvec);
+        if (id >= s) {
+            ret = igraph_strvector_resize(strvec, id + 1);
+            if (ret) {
+                RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+            }
+            strvalue = graphmlrec->default_value.as_string;
+            for (i = s; i < id; i++) {
+                igraph_strvector_set(strvec, i, strvalue);
+            }
+        }
+        if (state->data_char) {
+            strvalue = state->data_char;
+        } else {
+            strvalue = graphmlrec->default_value.as_string;
+        }
+        ret = igraph_strvector_set(strvec, id, strvalue);
+        if (ret) {
+            RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret);
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (state->data_char) {
+        igraph_Free(state->data_char);
+    }
+}
+
+void igraph_i_graphml_attribute_default_value_finish(
+    struct igraph_i_graphml_parser_state *state) {
+    igraph_i_graphml_attribute_record_t *graphmlrec = state->current_attr_record;
+
+    if (graphmlrec == 0) {
+        igraph_warning("state->current_attr_record was null where it should have been "
+                       "non-null; this is probably a bug. Please notify the developers!",
+                       __FILE__, __LINE__, 0);
+        return;
+    }
+
+    if (state->data_char == 0) {
+        return;
+    }
+
+    switch (graphmlrec->record.type) {
+    case IGRAPH_ATTRIBUTE_BOOLEAN:
+        graphmlrec->default_value.as_boolean = igraph_i_graphml_parse_boolean(
+                state->data_char, 0);
+        break;
+    case IGRAPH_ATTRIBUTE_NUMERIC:
+        graphmlrec->default_value.as_numeric = igraph_i_graphml_parse_numeric(
+                state->data_char, IGRAPH_NAN);
+        break;
+    case IGRAPH_ATTRIBUTE_STRING:
+        if (state->data_char) {
+            if (graphmlrec->default_value.as_string != 0) {
+                free(graphmlrec->default_value.as_string);
+            }
+            graphmlrec->default_value.as_string = strdup(state->data_char);
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (state->data_char) {
+        igraph_Free(state->data_char);
+    }
+}
+
+void igraph_i_graphml_sax_handler_start_element_ns(
+    void *state0, const xmlChar* localname, const xmlChar* prefix,
+    const xmlChar* uri, int nb_namespaces, const xmlChar** namespaces,
+    int nb_attributes, int nb_defaulted, const xmlChar** attributes) {
+    struct igraph_i_graphml_parser_state *state =
+        (struct igraph_i_graphml_parser_state*)state0;
+    xmlChar** it;
+    char* attr_value;
+    long int id1, id2;
+    int i;
+    igraph_bool_t tag_is_unknown = 0;
+
+    if (!state->successful) {
+        return;
+    }
+
+    if (uri) {
+        if (!xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), uri)) {
+            /* Tag is in a different namespace, so treat it as an unknown start
+             * tag irrespectively of our state */
+            tag_is_unknown = 1;
+        }
+    } else {
+        /* No namespace URI. If we are in lenient mode, accept it and proceed
+         * as if we are in the GraphML namespace to handle lots of naive
+         * non-namespace-aware GraphML files floating out there. If we are not
+         * in lenient mode _but_ we are in the START state, accept it as well
+         * and see whether the root tag is <graphml> (in which case we will
+         * enter lenient mode). Otherwise, reject the tag */
+        if (!state->ignore_namespaces && state->st != START) {
+            tag_is_unknown = 1;
+        }
+    }
+
+    if (tag_is_unknown) {
+        igraph_i_graphml_handle_unknown_start_tag(state);
+        return;
+    }
+
+    switch (state->st) {
+    case START:
+        /* If we are in the START state and received a graphml tag,
+         * change to INSIDE_GRAPHML state. Otherwise, change to UNKNOWN. */
+        if (xmlStrEqual(localname, toXmlChar("graphml"))) {
+            if (uri == 0) {
+                state->ignore_namespaces = 1;
+            }
+            state->st = INSIDE_GRAPHML;
+        } else {
+            igraph_i_graphml_handle_unknown_start_tag(state);
+        }
+        break;
+
+    case INSIDE_GRAPHML:
+        /* If we are in the INSIDE_GRAPHML state and received a graph tag,
+         * change to INSIDE_GRAPH state if the state->index counter reached
+         * zero (this is to handle multiple graphs in the same file).
+         * Otherwise, change to UNKNOWN. */
+        if (xmlStrEqual(localname, toXmlChar("graph"))) {
+            if (state->index == 0) {
+                state->st = INSIDE_GRAPH;
+                for (i = 0, it = (xmlChar**)attributes; i < nb_attributes; i++, it += 5) {
+                    if (XML_ATTR_URI(it) != 0 &&
+                        !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) {
+                        /* Attribute is from a different namespace, so skip it */
+                        continue;
+                    }
+                    if (xmlStrEqual(*it, toXmlChar("edgedefault"))) {
+                        if (!xmlStrncmp(toXmlChar("directed"), XML_ATTR_VALUE(it))) {
+                            state->edges_directed = 1;
+                        } else if (!xmlStrncmp(toXmlChar("undirected"), XML_ATTR_VALUE(it))) {
+                            state->edges_directed = 0;
+                        }
+                    }
+                }
+            }
+            state->index--;
+        } else if (xmlStrEqual(localname, toXmlChar("key"))) {
+            state->current_attr_record =
+                igraph_i_graphml_add_attribute_key(attributes, nb_attributes, state);
+            state->st = INSIDE_KEY;
+        } else {
+            igraph_i_graphml_handle_unknown_start_tag(state);
+        }
+        break;
+
+    case INSIDE_KEY:
+        /* If we are in the INSIDE_KEY state, check for default tag */
+        if (xmlStrEqual(localname, toXmlChar("default"))) {
+            state->st = INSIDE_DEFAULT;
+        } else {
+            igraph_i_graphml_handle_unknown_start_tag(state);
+        }
+        break;
+
+    case INSIDE_DEFAULT:
+        /* If we are in the INSIDE_DEFAULT state, every further tag will be unknown */
+        igraph_i_graphml_handle_unknown_start_tag(state);
+        break;
+
+    case INSIDE_GRAPH:
+        /* If we are in the INSIDE_GRAPH state, check for node and edge tags */
+        if (xmlStrEqual(localname, toXmlChar("edge"))) {
+            id1 = -1; id2 = -1;
+            for (i = 0, it = (xmlChar**)attributes; i < nb_attributes; i++, it += 5) {
+                if (XML_ATTR_URI(it) != 0 &&
+                    !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) {
+                    /* Attribute is from a different namespace, so skip it */
+                    continue;
+                }
+                if (xmlStrEqual(*it, toXmlChar("source"))) {
+                    attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it)));
+                    igraph_trie_get(&state->node_trie, attr_value, &id1);
+                    free(attr_value);
+                } else if (xmlStrEqual(*it, toXmlChar("target"))) {
+                    attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it)));
+                    igraph_trie_get(&state->node_trie, attr_value, &id2);
+                    free(attr_value);
+                } else if (xmlStrEqual(*it, toXmlChar("id"))) {
+                    long int edges = igraph_vector_size(&state->edgelist) / 2 + 1;
+                    long int origsize = igraph_strvector_size(&state->edgeids);
+                    attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it)));
+                    igraph_strvector_resize(&state->edgeids, edges);
+                    for (; origsize < edges - 1; origsize++) {
+                        igraph_strvector_set(&state->edgeids, origsize, "");
+                    }
+                    igraph_strvector_set(&state->edgeids, edges - 1, attr_value);
+                    free(attr_value);
+                }
+            }
+            if (id1 >= 0 && id2 >= 0) {
+                igraph_vector_push_back(&state->edgelist, id1);
+                igraph_vector_push_back(&state->edgelist, id2);
+            } else {
+                igraph_i_graphml_sax_handler_error(state, "Edge with missing source or target encountered");
+                return;
+            }
+            state->st = INSIDE_EDGE;
+        } else if (xmlStrEqual(localname, toXmlChar("node"))) {
+            id1 = -1;
+            for (i = 0, it = (xmlChar**)attributes; i < nb_attributes; i++, it += 5) {
+                if (XML_ATTR_URI(it) != 0 &&
+                    !xmlStrEqual(toXmlChar(GRAPHML_NAMESPACE_URI), XML_ATTR_URI(it))) {
+                    /* Attribute is from a different namespace, so skip it */
+                    continue;
+                }
+                if (xmlStrEqual(XML_ATTR_LOCALNAME(it), toXmlChar("id"))) {
+                    attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it)));
+                    igraph_trie_get(&state->node_trie, attr_value, &id1);
+                    free(attr_value);
+                    break;
+                }
+            }
+            if (id1 >= 0) {
+                state->act_node = id1;
+            } else {
+                state->act_node = -1;
+                igraph_i_graphml_sax_handler_error(state, "Node with missing id encountered");
+                return;
+            }
+            state->st = INSIDE_NODE;
+        } else if (xmlStrEqual(localname, toXmlChar("data"))) {
+            igraph_i_graphml_attribute_data_setup(state, attributes, nb_attributes,
+                                                  IGRAPH_ATTRIBUTE_GRAPH);
+            igraph_vector_int_push_back(&state->prev_state_stack, state->st);
+            state->st = INSIDE_DATA;
+        } else {
+            igraph_i_graphml_handle_unknown_start_tag(state);
+        }
+        break;
+
+    case INSIDE_NODE:
+        if (xmlStrEqual(localname, toXmlChar("data"))) {
+            igraph_i_graphml_attribute_data_setup(state, attributes, nb_attributes,
+                                                  IGRAPH_ATTRIBUTE_VERTEX);
+            igraph_vector_int_push_back(&state->prev_state_stack, state->st);
+            state->st = INSIDE_DATA;
+        }
+        break;
+
+    case INSIDE_EDGE:
+        if (xmlStrEqual(localname, toXmlChar("data"))) {
+            igraph_i_graphml_attribute_data_setup(state, attributes, nb_attributes,
+                                                  IGRAPH_ATTRIBUTE_EDGE);
+            igraph_vector_int_push_back(&state->prev_state_stack, state->st);
+            state->st = INSIDE_DATA;
+        }
+        break;
+
+    case INSIDE_DATA:
+        /* We do not expect any new tags within a <data> tag */
+        igraph_i_graphml_handle_unknown_start_tag(state);
+        break;
+
+    case UNKNOWN:
+        igraph_i_graphml_handle_unknown_start_tag(state);
+        break;
+
+    default:
+        break;
+    }
+}
+
+void igraph_i_graphml_sax_handler_end_element_ns(void *state0,
+        const xmlChar* localname, const xmlChar* prefix,
+        const xmlChar* uri) {
+    struct igraph_i_graphml_parser_state *state =
+        (struct igraph_i_graphml_parser_state*)state0;
+
+    if (!state->successful) {
+        return;
+    }
+
+    IGRAPH_UNUSED(localname);
+    IGRAPH_UNUSED(prefix);
+    IGRAPH_UNUSED(uri);
+
+    switch (state->st) {
+    case INSIDE_GRAPHML:
+        state->st = FINISH;
+        break;
+
+    case INSIDE_GRAPH:
+        state->st = INSIDE_GRAPHML;
+        break;
+
+    case INSIDE_KEY:
+        state->current_attr_record = 0;
+        state->st = INSIDE_GRAPHML;
+        break;
+
+    case INSIDE_DEFAULT:
+        igraph_i_graphml_attribute_default_value_finish(state);
+        state->st = INSIDE_KEY;
+        break;
+
+    case INSIDE_NODE:
+        state->st = INSIDE_GRAPH;
+        break;
+
+    case INSIDE_EDGE:
+        state->st = INSIDE_GRAPH;
+        break;
+
+    case INSIDE_DATA:
+        igraph_i_graphml_attribute_data_finish(state);
+        state->st = igraph_vector_int_pop_back(&state->prev_state_stack);
+        break;
+
+    case UNKNOWN:
+        state->unknown_depth--;
+        if (!state->unknown_depth) {
+            state->st = igraph_vector_int_pop_back(&state->prev_state_stack);
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+void igraph_i_graphml_sax_handler_chars(void* state0, const xmlChar* ch, int len) {
+    struct igraph_i_graphml_parser_state *state =
+        (struct igraph_i_graphml_parser_state*)state0;
+
+    if (!state->successful) {
+        return;
+    }
+
+    switch (state->st) {
+    case INSIDE_KEY:
+        break;
+
+    case INSIDE_DATA:
+    case INSIDE_DEFAULT:
+        igraph_i_graphml_append_to_data_char(state, ch, len);
+        break;
+
+    default:
+        /* just ignore it */
+        break;
+    }
+}
+
+static xmlSAXHandler igraph_i_graphml_sax_handler = {
+    /* internalSubset = */ 0,
+    /* isStandalone = */ 0,
+    /* hasInternalSubset = */ 0,
+    /* hasExternalSubset = */ 0,
+    /* resolveEntity = */ 0,
+    /* getEntity = */ igraph_i_graphml_sax_handler_get_entity,
+    /* entityDecl = */ 0,
+    /* notationDecl = */ 0,
+    /* attributeDecl = */ 0,
+    /* elementDecl = */ 0,
+    /* unparsedEntityDecl = */ 0,
+    /* setDocumentLocator = */ 0,
+    /* startDocument = */ igraph_i_graphml_sax_handler_start_document,
+    /* endDocument = */ igraph_i_graphml_sax_handler_end_document,
+    /* startElement = */ 0,
+    /* endElement = */ 0,
+    /* reference = */ 0,
+    /* characters = */ igraph_i_graphml_sax_handler_chars,
+    /* ignorableWhitespaceFunc = */ 0,
+    /* processingInstruction = */ 0,
+    /* comment = */ 0,
+    /* warning = */ igraph_i_graphml_sax_handler_error,
+    /* error = */ igraph_i_graphml_sax_handler_error,
+    /* fatalError = */ igraph_i_graphml_sax_handler_error,
+    /* getParameterEntity = */ 0,
+    /* cdataBlock = */ 0,
+    /* externalSubset = */ 0,
+    /* initialized = */ XML_SAX2_MAGIC,
+    /* _private = */ 0,
+    /* startElementNs = */ igraph_i_graphml_sax_handler_start_element_ns,
+    /* endElementNs = */ igraph_i_graphml_sax_handler_end_element_ns,
+    /* serror = */ 0
+};
+
+#endif
+
+#define IS_FORBIDDEN_CONTROL_CHAR(x) ((x) < ' ' && (x) != '\t' && (x) != '\r' && (x) != '\n')
+
+int igraph_i_xml_escape(char* src, char** dest) {
+    long int destlen = 0;
+    char *s, *d;
+    unsigned char ch;
+
+    for (s = src; *s; s++, destlen++) {
+        ch = (unsigned char)(*s);
+        if (ch == '&') {
+            destlen += 4;
+        } else if (ch == '<') {
+            destlen += 3;
+        } else if (ch == '>') {
+            destlen += 3;
+        } else if (ch == '"') {
+            destlen += 5;
+        } else if (ch == '\'') {
+            destlen += 5;
+        } else if (IS_FORBIDDEN_CONTROL_CHAR(ch)) {
+            char msg[4096];
+            snprintf(msg, 4096, "Forbidden control character 0x%02X found in igraph_i_xml_escape",
+                     ch);
+            IGRAPH_ERROR(msg, IGRAPH_EINVAL);
+        }
+    }
+    *dest = igraph_Calloc(destlen + 1, char);
+    if (!*dest) {
+        IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM);
+    }
+    for (s = src, d = *dest; *s; s++, d++) {
+        ch = (unsigned char)(*s);
+        switch (ch) {
+        case '&':
+            strcpy(d, "&amp;"); d += 4; break;
+        case '<':
+            strcpy(d, "&lt;"); d += 3; break;
+        case '>':
+            strcpy(d, "&gt;"); d += 3; break;
+        case '"':
+            strcpy(d, "&quot;"); d += 5; break;
+        case '\'':
+            strcpy(d, "&apos;"); d += 5; break;
+        default:
+            *d = ch;
+        }
+    }
+    *d = 0;
+    return 0;
+}
+
+/**
+ * \ingroup loadsave
+ * \function igraph_read_graph_graphml
+ * \brief Reads a graph from a GraphML file.
+ *
+ * </para><para>
+ * GraphML is an XML-based file format for representing various types of
+ * graphs. Currently only the most basic import functionality is implemented
+ * in igraph: it can read GraphML files without nested graphs and hyperedges.
+ * Attributes of the graph are loaded only if an attribute interface
+ * is attached, ie. if you use igraph from R or Python.
+ *
+ * </para><para>
+ * Graph attribute names are taken from the \c attr.name attributes of the
+ * \c key tags in the GraphML file. Since \c attr.name is not mandatory,
+ * igraph will fall back to the \c id attribute of the \c key tag if
+ * \c attr.name is missing.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param instream A stream, it should be readable.
+ * \param index If the GraphML file contains more than one graph, the one
+ *              specified by this index will be loaded. Indices start from
+ *              zero, so supply zero here if your GraphML file contains only
+ *              a single graph.
+ *
+ * \return Error code:
+ *         \c IGRAPH_PARSEERROR: if there is a
+ *         problem reading the file, or the file is syntactically
+ *         incorrect.
+ *         \c IGRAPH_UNIMPLEMENTED: the GraphML functionality was disabled
+ *         at compile-time
+ *
+ * \example examples/simple/graphml.c
+ */
+int igraph_read_graph_graphml(igraph_t *graph, FILE *instream,
+                              int index) {
+
+#if HAVE_LIBXML == 1
+    xmlParserCtxtPtr ctxt;
+    struct igraph_i_graphml_parser_state state;
+    int res;
+    char buffer[4096];
+
+    if (index < 0) {
+        IGRAPH_ERROR("Graph index must be non-negative", IGRAPH_EINVAL);
+    }
+
+    xmlInitParser();
+
+    /* Create a progressive parser context */
+    state.g = graph;
+    state.index = index < 0 ? 0 : index;
+    res = (int) fread(buffer, 1, 4096, instream);
+    ctxt = xmlCreatePushParserCtxt(&igraph_i_graphml_sax_handler,
+                                   &state,
+                                   buffer,
+                                   res,
+                                   NULL);
+    /*   ctxt=xmlCreateIOParserCtxt(&igraph_i_graphml_sax_handler, &state, */
+    /*               igraph_i_libxml2_read_callback, */
+    /*               igraph_i_libxml2_close_callback, */
+    /*               instream, XML_CHAR_ENCODING_NONE); */
+    if (ctxt == NULL) {
+        IGRAPH_ERROR("Can't create progressive parser context", IGRAPH_PARSEERROR);
+    }
+
+    /* Set parsing options */
+    if (xmlCtxtUseOptions(ctxt,
+                          XML_PARSE_NOENT | XML_PARSE_NOBLANKS |
+                          XML_PARSE_NONET | XML_PARSE_NSCLEAN |
+                          XML_PARSE_NOCDATA | XML_PARSE_HUGE
+                         )) {
+        IGRAPH_ERROR("Cannot set options for the parser context", IGRAPH_EINVAL);
+    }
+
+    /* Parse the file */
+    while ((res = (int) fread(buffer, 1, 4096, instream)) > 0) {
+        xmlParseChunk(ctxt, buffer, res, 0);
+        if (!state.successful) {
+            break;
+        }
+    }
+    xmlParseChunk(ctxt, buffer, res, 1);
+
+    /* Free the context */
+    xmlFreeParserCtxt(ctxt);
+    if (!state.successful) {
+        if (state.error_message != 0) {
+            IGRAPH_ERROR(state.error_message, IGRAPH_PARSEERROR);
+        } else {
+            IGRAPH_ERROR("Malformed GraphML file", IGRAPH_PARSEERROR);
+        }
+    }
+    if (state.index >= 0) {
+        IGRAPH_ERROR("Graph index was too large", IGRAPH_EINVAL);
+    }
+
+    return 0;
+#else
+    IGRAPH_ERROR("GraphML support is disabled", IGRAPH_UNIMPLEMENTED);
+#endif
+}
+
+/**
+ * \ingroup loadsave
+ * \function igraph_write_graph_graphml
+ * \brief Writes the graph to a file in GraphML format
+ *
+ * </para><para>
+ * GraphML is an XML-based file format for representing various types of
+ * graphs. See the GraphML Primer (http://graphml.graphdrawing.org/primer/graphml-primer.html)
+ * for detailed format description.
+ *
+ * \param graph The graph to write.
+ * \param outstream The stream object to write to, it should be
+ *        writable.
+ * \param prefixattr Logical value, whether to put a prefix in front of the
+ *        attribute names to ensure uniqueness if the graph has vertex and
+ *        edge (or graph) attributes with the same name.
+ * \return Error code:
+ *         \c IGRAPH_EFILE if there is an error
+ *         writing the file.
+ *
+ * Time complexity: O(|V|+|E|) otherwise. All
+ * file operations are expected to have time complexity
+ * O(1).
+ *
+ * \example examples/simple/graphml.c
+ */
+int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream,
+                               igraph_bool_t prefixattr) {
+    int ret;
+    igraph_integer_t l, vc;
+    igraph_eit_t it;
+    igraph_strvector_t gnames, vnames, enames;
+    igraph_vector_t gtypes, vtypes, etypes;
+    long int i;
+    igraph_vector_t numv;
+    igraph_strvector_t strv;
+    igraph_vector_bool_t boolv;
+    const char *gprefix = prefixattr ? "g_" : "";
+    const char *vprefix = prefixattr ? "v_" : "";
+    const char *eprefix = prefixattr ? "e_" : "";
+
+    /* set standard C locale lest we sometimes get commas instead of dots */
+    char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL));
+    if (saved_locale == NULL) {
+        IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, saved_locale);
+    setlocale(LC_NUMERIC, "C");
+
+    ret = fprintf(outstream, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+    ret = fprintf(outstream, "<graphml xmlns=\"%s\"\n", GRAPHML_NAMESPACE_URI);
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+    ret = fprintf(outstream, "         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+    ret = fprintf(outstream, "         xsi:schemaLocation=\"%s\n", GRAPHML_NAMESPACE_URI);
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+    ret = fprintf(outstream, "         %s/1.0/graphml.xsd\">\n", GRAPHML_NAMESPACE_URI);
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+    ret = fprintf(outstream, "<!-- Created by igraph -->\n");
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+
+    /* dump the <key> elements if any */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&numv, 1);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1);
+    IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1);
+
+    IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&gtypes, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0);
+    igraph_i_attribute_get_info(graph,
+                                &gnames, &gtypes,
+                                &vnames, &vtypes,
+                                &enames, &etypes);
+
+    /* graph attributes */
+    for (i = 0; i < igraph_vector_size(&gtypes); i++) {
+        char *name, *name_escaped;
+        igraph_strvector_get(&gnames, i, &name);
+        IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+        if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"graph\" attr.name=\"%s\" attr.type=\"string\"/>\n", gprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"graph\" attr.name=\"%s\" attr.type=\"double\"/>\n", gprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"graph\" attr.name=\"%s\" attr.type=\"boolean\"/>\n", gprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        }
+        igraph_Free(name_escaped);
+    }
+
+    /* vertex attributes */
+    for (i = 0; i < igraph_vector_size(&vtypes); i++) {
+        char *name, *name_escaped;
+        igraph_strvector_get(&vnames, i, &name);
+        IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+        if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"node\" attr.name=\"%s\" attr.type=\"string\"/>\n", vprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"node\" attr.name=\"%s\" attr.type=\"double\"/>\n", vprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"node\" attr.name=\"%s\" attr.type=\"boolean\"/>\n", vprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        }
+        igraph_Free(name_escaped);
+    }
+
+    /* edge attributes */
+    for (i = 0; i < igraph_vector_size(&etypes); i++) {
+        char *name, *name_escaped;
+        igraph_strvector_get(&enames, i, &name);
+        IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+        if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"edge\" attr.name=\"%s\" attr.type=\"string\"/>\n", eprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"edge\" attr.name=\"%s\" attr.type=\"double\"/>\n", eprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            ret = fprintf(outstream, "  <key id=\"%s%s\" for=\"edge\" attr.name=\"%s\" attr.type=\"boolean\"/>\n", eprefix, name_escaped, name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        }
+        igraph_Free(name_escaped);
+    }
+
+    ret = fprintf(outstream, "  <graph id=\"G\" edgedefault=\"%s\">\n", (igraph_is_directed(graph) ? "directed" : "undirected"));
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+
+    /* Write the graph atributes before anything else */
+
+    for (i = 0; i < igraph_vector_size(&gtypes); i++) {
+        char *name, *name_escaped;
+        if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+            igraph_strvector_get(&gnames, i, &name);
+            IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv));
+            if (!isnan(VECTOR(numv)[0])) {
+                IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+                ret = fprintf(outstream, "    <data key=\"%s%s\">", gprefix, name_escaped);
+                igraph_Free(name_escaped);
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+                ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+                ret = fprintf(outstream, "</data>\n");
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+            }
+        } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
+            char *s, *s_escaped;
+            igraph_strvector_get(&gnames, i, &name);
+            IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+            ret = fprintf(outstream, "    <data key=\"%s%s\">", gprefix,
+                          name_escaped);
+            igraph_Free(name_escaped);
+            IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv));
+            igraph_strvector_get(&strv, 0, &s);
+            IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped));
+            ret = fprintf(outstream, "%s", s_escaped);
+            igraph_Free(s_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            ret = fprintf(outstream, "</data>\n");
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            igraph_strvector_get(&gnames, i, &name);
+            IGRAPH_CHECK(igraph_i_attribute_get_bool_graph_attr(graph, name, &boolv));
+            IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+            ret = fprintf(outstream, "    <data key=\"%s%s\">%s</data>\n",
+                          gprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false");
+            igraph_Free(name_escaped);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        }
+    }
+
+    /* Let's dump the nodes first */
+    vc = igraph_vcount(graph);
+    for (l = 0; l < vc; l++) {
+        char *name, *name_escaped;
+        ret = fprintf(outstream, "    <node id=\"n%ld\">\n", (long)l);
+
+        if (ret < 0) {
+            IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+        }
+
+        for (i = 0; i < igraph_vector_size(&vtypes); i++) {
+            if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_strvector_get(&vnames, i, &name);
+                IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name,
+                             igraph_vss_1(l), &numv));
+                if (!isnan(VECTOR(numv)[0])) {
+                    IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+                    ret = fprintf(outstream, "      <data key=\"%s%s\">", vprefix, name_escaped);
+                    igraph_Free(name_escaped);
+                    if (ret < 0) {
+                        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                    }
+                    ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+                    if (ret < 0) {
+                        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                    }
+                    ret = fprintf(outstream, "</data>\n");
+                    if (ret < 0) {
+                        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                    }
+                }
+            } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
+                char *s, *s_escaped;
+                igraph_strvector_get(&vnames, i, &name);
+                IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+                ret = fprintf(outstream, "      <data key=\"%s%s\">", vprefix,
+                              name_escaped);
+                igraph_Free(name_escaped);
+                IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name,
+                             igraph_vss_1(l), &strv));
+                igraph_strvector_get(&strv, 0, &s);
+                IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped));
+                ret = fprintf(outstream, "%s", s_escaped);
+                igraph_Free(s_escaped);
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+                ret = fprintf(outstream, "</data>\n");
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+            } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                igraph_strvector_get(&vnames, i, &name);
+                IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name,
+                             igraph_vss_1(l), &boolv));
+                IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+                ret = fprintf(outstream, "      <data key=\"%s%s\">%s</data>\n",
+                              vprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false");
+                igraph_Free(name_escaped);
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+            }
+        }
+
+        ret = fprintf(outstream, "    </node>\n");
+        if (ret < 0) {
+            IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+        }
+    }
+
+    /* Now the edges */
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &it));
+    IGRAPH_FINALLY(igraph_eit_destroy, &it);
+    while (!IGRAPH_EIT_END(it)) {
+        igraph_integer_t from, to;
+        char *name, *name_escaped;
+        long int edge = IGRAPH_EIT_GET(it);
+        igraph_edge(graph, (igraph_integer_t) edge, &from, &to);
+        ret = fprintf(outstream, "    <edge source=\"n%ld\" target=\"n%ld\">\n",
+                      (long int)from, (long int)to);
+        if (ret < 0) {
+            IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+        }
+
+        for (i = 0; i < igraph_vector_size(&etypes); i++) {
+            if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_strvector_get(&enames, i, &name);
+                IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name,
+                             igraph_ess_1((igraph_integer_t) edge), &numv));
+                if (!isnan(VECTOR(numv)[0])) {
+                    IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+                    ret = fprintf(outstream, "      <data key=\"%s%s\">", eprefix, name_escaped);
+                    igraph_Free(name_escaped);
+                    if (ret < 0) {
+                        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                    }
+                    ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+                    if (ret < 0) {
+                        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                    }
+                    ret = fprintf(outstream, "</data>\n");
+                    if (ret < 0) {
+                        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                    }
+                }
+            } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
+                char *s, *s_escaped;
+                igraph_strvector_get(&enames, i, &name);
+                IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+                ret = fprintf(outstream, "      <data key=\"%s%s\">", eprefix,
+                              name_escaped);
+                igraph_Free(name_escaped);
+                IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name,
+                             igraph_ess_1((igraph_integer_t) edge), &strv));
+                igraph_strvector_get(&strv, 0, &s);
+                IGRAPH_CHECK(igraph_i_xml_escape(s, &s_escaped));
+                ret = fprintf(outstream, "%s", s_escaped);
+                igraph_Free(s_escaped);
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+                ret = fprintf(outstream, "</data>\n");
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+            } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                igraph_strvector_get(&enames, i, &name);
+                IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, name,
+                             igraph_ess_1((igraph_integer_t) edge), &boolv));
+                IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
+                ret = fprintf(outstream, "      <data key=\"%s%s\">%s</data>\n",
+                              eprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false");
+                igraph_Free(name_escaped);
+                if (ret < 0) {
+                    IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+                }
+            }
+        }
+
+        ret = fprintf(outstream, "    </edge>\n");
+        if (ret < 0) {
+            IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+        }
+        IGRAPH_EIT_NEXT(it);
+    }
+    igraph_eit_destroy(&it);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    ret = fprintf(outstream, "  </graph>\n");
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+    fprintf(outstream, "</graphml>\n");
+    if (ret < 0) {
+        IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+    }
+
+    /* reset locale to whatever was before this function */
+    setlocale(LC_NUMERIC, saved_locale);
+
+    igraph_free(saved_locale);
+    igraph_strvector_destroy(&gnames);
+    igraph_strvector_destroy(&vnames);
+    igraph_strvector_destroy(&enames);
+    igraph_vector_destroy(&gtypes);
+    igraph_vector_destroy(&vtypes);
+    igraph_vector_destroy(&etypes);
+    igraph_vector_destroy(&numv);
+    igraph_strvector_destroy(&strv);
+    igraph_vector_bool_destroy(&boolv);
+    IGRAPH_FINALLY_CLEAN(10);
+
+    return 0;
+}
diff --git a/igraph/src/foreign-lgl-lexer.c b/igraph/src/foreign-lgl-lexer.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-lgl-lexer.c
@@ -0,0 +1,2014 @@
+#line 2 "foreign-lgl-lexer.c"
+
+#line 4 "foreign-lgl-lexer.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE igraph_lgl_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = yyg->yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	yy_size_t yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via igraph_lgl_yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+                          ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void igraph_lgl_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void igraph_lgl_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_lgl_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void igraph_lgl_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_lgl_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_lgl_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void igraph_lgl_yypop_buffer_state (yyscan_t yyscanner );
+
+static void igraph_lgl_yyensure_buffer_stack (yyscan_t yyscanner );
+static void igraph_lgl_yy_load_buffer_state (yyscan_t yyscanner );
+static void igraph_lgl_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER igraph_lgl_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE igraph_lgl_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_lgl_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_lgl_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *igraph_lgl_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *igraph_lgl_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void igraph_lgl_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer igraph_lgl_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        igraph_lgl_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_lgl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        igraph_lgl_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_lgl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define igraph_lgl_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yyg->yytext_ptr = yy_bp; \
+	yyleng = (yy_size_t) (yy_cp - yy_bp); \
+	yyg->yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 6
+#define YY_END_OF_BUFFER 7
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[13] =
+    {   0,
+        2,    2,    7,    4,    2,    3,    3,    1,    4,    2,
+        3,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    4,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    1,    5,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[6] =
+    {   0,
+        1,    2,    3,    4,    5
+    } ;
+
+static yyconst flex_int16_t yy_base[17] =
+    {   0,
+        0,    0,   10,    0,    0,    0,    0,   11,    0,    0,
+       11,   11,    8,    6,    3,    3
+    } ;
+
+static yyconst flex_int16_t yy_def[17] =
+    {   0,
+       12,    1,   12,   13,   14,   15,   16,   12,   13,   14,
+       12,    0,   12,   12,   12,   12
+    } ;
+
+static yyconst flex_int16_t yy_nxt[17] =
+    {   0,
+        4,    5,    6,    7,    8,   11,   11,   10,    9,   12,
+        3,   12,   12,   12,   12,   12
+    } ;
+
+static yyconst flex_int16_t yy_chk[17] =
+    {   0,
+        1,    1,    1,    1,    1,   16,   15,   14,   13,    3,
+       12,   12,   12,   12,   12,   12
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "../../src/foreign-lgl-lexer.l"
+/* 
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+#line 24 "../../src/foreign-lgl-lexer.l"
+
+/* 
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include <stdlib.h>
+#include "foreign-lgl-header.h"
+#include "foreign-lgl-parser.h"
+#define YY_EXTRA_TYPE igraph_i_lgl_parsedata_t*
+#define YY_USER_ACTION yylloc->first_line = yylineno;
+/* We assume that 'file' is 'stderr' here. */
+#ifdef USING_R
+#define fprintf(file, msg, ...) (1)
+#endif
+#ifdef stdout 
+#  undef stdout
+#endif
+#define stdout 0
+#define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \
+				__LINE__, IGRAPH_PARSEERROR);
+#define YY_NO_INPUT 1
+#line 500 "foreign-lgl-lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+    {
+
+    /* User-defined. Not touched by flex. */
+    YY_EXTRA_TYPE yyextra_r;
+
+    /* The rest are the same as the globals declared in the non-reentrant scanner. */
+    FILE *yyin_r, *yyout_r;
+    size_t yy_buffer_stack_top; /**< index of top of stack. */
+    size_t yy_buffer_stack_max; /**< capacity of stack. */
+    YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+    char yy_hold_char;
+    yy_size_t yy_n_chars;
+    yy_size_t yyleng_r;
+    char *yy_c_buf_p;
+    int yy_init;
+    int yy_start;
+    int yy_did_buffer_switch_on_eof;
+    int yy_start_stack_ptr;
+    int yy_start_stack_depth;
+    int *yy_start_stack;
+    yy_state_type yy_last_accepting_state;
+    char* yy_last_accepting_cpos;
+
+    int yylineno_r;
+    int yy_flex_debug_r;
+
+    char *yytext_r;
+    int yy_more_flag;
+    int yy_more_len;
+
+    YYSTYPE * yylval_r;
+
+    YYLTYPE * yylloc_r;
+
+    }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+    /* This must go here because YYSTYPE and YYLTYPE are included
+     * from bison output in section 1.*/
+    #    define yylval yyg->yylval_r
+    
+    #    define yylloc yyg->yylloc_r
+    
+int igraph_lgl_yylex_init (yyscan_t* scanner);
+
+int igraph_lgl_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int igraph_lgl_yylex_destroy (yyscan_t yyscanner );
+
+int igraph_lgl_yyget_debug (yyscan_t yyscanner );
+
+void igraph_lgl_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE igraph_lgl_yyget_extra (yyscan_t yyscanner );
+
+void igraph_lgl_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *igraph_lgl_yyget_in (yyscan_t yyscanner );
+
+void igraph_lgl_yyset_in  (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *igraph_lgl_yyget_out (yyscan_t yyscanner );
+
+void igraph_lgl_yyset_out  (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t igraph_lgl_yyget_leng (yyscan_t yyscanner );
+
+char *igraph_lgl_yyget_text (yyscan_t yyscanner );
+
+int igraph_lgl_yyget_lineno (yyscan_t yyscanner );
+
+void igraph_lgl_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * igraph_lgl_yyget_lval (yyscan_t yyscanner );
+
+void igraph_lgl_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+       YYLTYPE *igraph_lgl_yyget_lloc (yyscan_t yyscanner );
+    
+        void igraph_lgl_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+    
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int igraph_lgl_yywrap (yyscan_t yyscanner );
+#else
+extern int igraph_lgl_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		yy_size_t n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int igraph_lgl_yylex \
+               (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int igraph_lgl_yylex \
+               (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 77 "../../src/foreign-lgl-lexer.l"
+
+
+ /* --------------------------------------------------hashmark------*/
+#line 743 "foreign-lgl-lexer.c"
+
+    yylval = yylval_param;
+
+    yylloc = yylloc_param;
+
+	if ( !yyg->yy_init )
+		{
+		yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yyg->yy_start )
+			yyg->yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			igraph_lgl_yyensure_buffer_stack (yyscanner);
+			YY_CURRENT_BUFFER_LVALUE =
+				igraph_lgl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+		}
+
+		igraph_lgl_yy_load_buffer_state(yyscanner );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yyg->yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yyg->yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yyg->yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yyg->yy_last_accepting_state = yy_current_state;
+				yyg->yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 13 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 11 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yyg->yy_hold_char;
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 80 "../../src/foreign-lgl-lexer.l"
+{ return HASH; }
+	YY_BREAK
+/* ------------------------------------------------whitespace------*/
+case 2:
+YY_RULE_SETUP
+#line 83 "../../src/foreign-lgl-lexer.l"
+{ }
+	YY_BREAK
+/* ---------------------------------------------------newline------*/
+case 3:
+/* rule 3 can match eol */
+YY_RULE_SETUP
+#line 86 "../../src/foreign-lgl-lexer.l"
+{ return NEWLINE; }
+	YY_BREAK
+/* ----------------------------------------------alphanumeric------*/
+case 4:
+YY_RULE_SETUP
+#line 89 "../../src/foreign-lgl-lexer.l"
+{ return ALNUM; }
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+#line 91 "../../src/foreign-lgl-lexer.l"
+{ if (yyextra->eof) {
+                       yyterminate();
+                    } else {
+                       yyextra->eof=1;
+                       return NEWLINE; 
+                    }
+                  }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 99 "../../src/foreign-lgl-lexer.l"
+{ return ERROR; }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 101 "../../src/foreign-lgl-lexer.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+#line 874 "foreign-lgl-lexer.c"
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yyg->yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * igraph_lgl_yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state( yyscanner );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+			yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yyg->yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yyg->yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer( yyscanner ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yyg->yy_did_buffer_switch_on_eof = 0;
+
+				if ( igraph_lgl_yywrap(yyscanner ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yyg->yy_c_buf_p =
+					yyg->yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yyg->yy_c_buf_p =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of igraph_lgl_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = yyg->yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+	else
+		{
+			yy_size_t num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				yy_size_t new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					igraph_lgl_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			yyg->yy_n_chars, num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	if ( yyg->yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			igraph_lgl_yyrestart(yyin  ,yyscanner);
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_lgl_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	yyg->yy_n_chars += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_current_state = yyg->yy_start;
+
+	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yyg->yy_last_accepting_state = yy_current_state;
+			yyg->yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 13 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+	register int yy_is_jam;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+	register char *yy_cp = yyg->yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yyg->yy_last_accepting_state = yy_current_state;
+		yyg->yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 13 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 12);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (yyscan_t yyscanner)
+#else
+    static int input  (yyscan_t yyscanner)
+#endif
+
+{
+	int c;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	*yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+	if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			/* This was really a NUL. */
+			*yyg->yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			++yyg->yy_c_buf_p;
+
+			switch ( yy_get_next_buffer( yyscanner ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					igraph_lgl_yyrestart(yyin ,yyscanner);
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( igraph_lgl_yywrap(yyscanner ) )
+						return 0;
+
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput(yyscanner);
+#else
+					return input(yyscanner);
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yyg->yy_c_buf_p;	/* cast for 8-bit char's */
+	*yyg->yy_c_buf_p = '\0';	/* preserve yytext */
+	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void igraph_lgl_yyrestart  (FILE * input_file , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! YY_CURRENT_BUFFER ){
+        igraph_lgl_yyensure_buffer_stack (yyscanner);
+		YY_CURRENT_BUFFER_LVALUE =
+            igraph_lgl_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+	}
+
+	igraph_lgl_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+	igraph_lgl_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_lgl_yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		igraph_lgl_yypop_buffer_state();
+	 *		igraph_lgl_yypush_buffer_state(new_buffer);
+     */
+	igraph_lgl_yyensure_buffer_stack (yyscanner);
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	igraph_lgl_yy_load_buffer_state(yyscanner );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (igraph_lgl_yywrap()) processing, but the only time this flag
+	 * is looked at is after igraph_lgl_yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void igraph_lgl_yy_load_buffer_state  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE igraph_lgl_yy_create_buffer  (FILE * file, int  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) igraph_lgl_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) igraph_lgl_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	igraph_lgl_yy_init_buffer(b,file ,yyscanner);
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with igraph_lgl_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+    void igraph_lgl_yy_delete_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		igraph_lgl_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+	igraph_lgl_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a igraph_lgl_yyrestart() or at EOF.
+ */
+    static void igraph_lgl_yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file , yyscan_t yyscanner)
+
+{
+	int oerrno = errno;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	igraph_lgl_yy_flush_buffer(b ,yyscanner);
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then igraph_lgl_yy_init_buffer was _probably_
+     * called from igraph_lgl_yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_lgl_yy_flush_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		igraph_lgl_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_lgl_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (new_buffer == NULL)
+		return;
+
+	igraph_lgl_yyensure_buffer_stack(yyscanner);
+
+	/* This block is copied from igraph_lgl_yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		yyg->yy_buffer_stack_top++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from igraph_lgl_yy_switch_to_buffer. */
+	igraph_lgl_yy_load_buffer_state(yyscanner );
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_lgl_yypop_buffer_state (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (!YY_CURRENT_BUFFER)
+		return;
+
+	igraph_lgl_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if (yyg->yy_buffer_stack_top > 0)
+		--yyg->yy_buffer_stack_top;
+
+	if (YY_CURRENT_BUFFER) {
+		igraph_lgl_yy_load_buffer_state(yyscanner );
+		yyg->yy_did_buffer_switch_on_eof = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void igraph_lgl_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+	yy_size_t num_to_alloc;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if (!yyg->yy_buffer_stack) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_lgl_yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yyensure_buffer_stack()" );
+								  
+		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		yyg->yy_buffer_stack_max = num_to_alloc;
+		yyg->yy_buffer_stack_top = 0;
+		return;
+	}
+
+	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_lgl_yyrealloc
+								(yyg->yy_buffer_stack,
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+		yyg->yy_buffer_stack_max = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE igraph_lgl_yy_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) igraph_lgl_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	igraph_lgl_yy_switch_to_buffer(b ,yyscanner );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to igraph_lgl_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       igraph_lgl_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE igraph_lgl_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+    
+	return igraph_lgl_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to igraph_lgl_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE igraph_lgl_yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n, i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) igraph_lgl_yyalloc(n ,yyscanner );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_lgl_yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = igraph_lgl_yy_scan_buffer(buf,n ,yyscanner);
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in igraph_lgl_yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = yyg->yy_hold_char; \
+		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+		yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+		*yyg->yy_c_buf_p = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE igraph_lgl_yyget_extra  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_lgl_yyget_lineno  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_lgl_yyget_column  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_lgl_yyget_in  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_lgl_yyget_out  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+yy_size_t igraph_lgl_yyget_leng  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *igraph_lgl_yyget_text  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void igraph_lgl_yyset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_lgl_yyset_lineno (int  line_number , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* lineno is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_lgl_yyset_lineno called with no buffer" , yyscanner); 
+    
+    yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_lgl_yyset_column (int  column_no , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* column is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_lgl_yyset_column called with no buffer" , yyscanner); 
+    
+    yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see igraph_lgl_yy_switch_to_buffer
+ */
+void igraph_lgl_yyset_in (FILE *  in_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyin = in_str ;
+}
+
+void igraph_lgl_yyset_out (FILE *  out_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyout = out_str ;
+}
+
+int igraph_lgl_yyget_debug  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yy_flex_debug;
+}
+
+void igraph_lgl_yyset_debug (int  bdebug , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * igraph_lgl_yyget_lval  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylval;
+}
+
+void igraph_lgl_yyset_lval (YYSTYPE *  yylval_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylval = yylval_param;
+}
+
+YYLTYPE *igraph_lgl_yyget_lloc  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylloc;
+}
+    
+void igraph_lgl_yyset_lloc (YYLTYPE *  yylloc_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylloc = yylloc_param;
+}
+    
+/* User-visible API */
+
+/* igraph_lgl_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int igraph_lgl_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) igraph_lgl_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* igraph_lgl_yylex_init_extra has the same functionality as igraph_lgl_yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to igraph_lgl_yyalloc in
+ * the yyextra field.
+ */
+
+int igraph_lgl_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+    struct yyguts_t dummy_yyguts;
+
+    igraph_lgl_yyset_extra (yy_user_defined, &dummy_yyguts);
+
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+	
+    *ptr_yy_globals = (yyscan_t) igraph_lgl_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+	
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+    
+    /* By setting to 0xAA, we expose bugs in
+    yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+    
+    igraph_lgl_yyset_extra (yy_user_defined, *ptr_yy_globals);
+    
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from igraph_lgl_yylex_destroy(), so don't allocate here.
+     */
+
+    yyg->yy_buffer_stack = 0;
+    yyg->yy_buffer_stack_top = 0;
+    yyg->yy_buffer_stack_max = 0;
+    yyg->yy_c_buf_p = (char *) 0;
+    yyg->yy_init = 0;
+    yyg->yy_start = 0;
+
+    yyg->yy_start_stack_ptr = 0;
+    yyg->yy_start_stack_depth = 0;
+    yyg->yy_start_stack =  NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * igraph_lgl_yylex_init()
+     */
+    return 0;
+}
+
+/* igraph_lgl_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int igraph_lgl_yylex_destroy  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		igraph_lgl_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		igraph_lgl_yypop_buffer_state(yyscanner);
+	}
+
+	/* Destroy the stack itself. */
+	igraph_lgl_yyfree(yyg->yy_buffer_stack ,yyscanner);
+	yyg->yy_buffer_stack = NULL;
+
+    /* Destroy the start condition stack. */
+        igraph_lgl_yyfree(yyg->yy_start_stack ,yyscanner );
+        yyg->yy_start_stack = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * igraph_lgl_yylex() is called, initialization will occur. */
+    yy_init_globals( yyscanner);
+
+    /* Destroy the main struct (reentrant only). */
+    igraph_lgl_yyfree ( yyscanner , yyscanner );
+    yyscanner = NULL;
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *igraph_lgl_yyalloc (yy_size_t  size , yyscan_t yyscanner)
+{
+	return (void *) malloc( size );
+}
+
+void *igraph_lgl_yyrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void igraph_lgl_yyfree (void * ptr , yyscan_t yyscanner)
+{
+	free( (char *) ptr );	/* see igraph_lgl_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 101 "../../src/foreign-lgl-lexer.l"
+
+
+
diff --git a/igraph/src/foreign-lgl-parser.c b/igraph/src/foreign-lgl-parser.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-lgl-parser.c
@@ -0,0 +1,1696 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names.  */
+#define yyparse igraph_lgl_yyparse
+#define yylex   igraph_lgl_yylex
+#define yyerror igraph_lgl_yyerror
+#define yylval  igraph_lgl_yylval
+#define yychar  igraph_lgl_yychar
+#define yydebug igraph_lgl_yydebug
+#define yynerrs igraph_lgl_yynerrs
+#define yylloc igraph_lgl_yylloc
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     ALNUM = 258,
+     NEWLINE = 259,
+     HASH = 260,
+     ERROR = 261
+   };
+#endif
+/* Tokens.  */
+#define ALNUM 258
+#define NEWLINE 259
+#define HASH 260
+#define ERROR 261
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 23 "../../src/foreign-lgl-parser.y"
+
+
+/* 
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "igraph_hacks_internal.h"
+#include "igraph_types.h" 
+#include "igraph_types_internal.h"
+#include "igraph_math.h"
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "config.h"
+#include "foreign-lgl-header.h"
+#include "foreign-lgl-parser.h"
+
+#define yyscan_t void*
+
+int igraph_lgl_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, 
+		     void* scanner);
+int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, 
+		       const char *s);
+char *igraph_lgl_yyget_text (yyscan_t yyscanner );
+int igraph_lgl_yyget_leng (yyscan_t yyscanner );
+igraph_real_t igraph_lgl_get_number(const char *str, long int len);
+
+#define scanner context->scanner
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 81 "../../src/foreign-lgl-parser.y"
+{
+  long int edgenum;
+  double weightnum;
+}
+/* Line 193 of yacc.c.  */
+#line 170 "foreign-lgl-parser.c"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 216 of yacc.c.  */
+#line 195 "foreign-lgl-parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+    int i;
+#endif
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+	     && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+    YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+      + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  2
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   10
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  7
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  8
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  12
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  17
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   261
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint8 yyprhs[] =
+{
+       0,     0,     3,     4,     7,    10,    13,    17,    18,    21,
+      24,    28,    30
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+       8,     0,    -1,    -1,     8,     4,    -1,     8,     9,    -1,
+      10,    11,    -1,     5,    13,     4,    -1,    -1,    11,    12,
+      -1,    13,     4,    -1,    13,    14,     4,    -1,     3,    -1,
+       3,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,    96,    96,    97,    98,   101,   103,   105,   105,   107,
+     112,   121,   126
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "ALNUM", "NEWLINE", "HASH", "ERROR",
+  "$accept", "input", "vertex", "vertexdef", "edges", "edge", "edgeid",
+  "weight", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,     7,     8,     8,     8,     9,    10,    11,    11,    12,
+      12,    13,    14
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     0,     2,     2,     2,     3,     0,     2,     2,
+       3,     1,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       2,     0,     1,     3,     0,     4,     7,    11,     0,     5,
+       6,     8,     0,    12,     9,     0,    10
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     1,     5,     6,     9,    11,     8,    15
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -3
+static const yytype_int8 yypact[] =
+{
+      -3,     0,    -3,    -3,     3,    -3,    -3,    -3,    -1,     3,
+      -3,    -3,    -2,    -3,    -3,     4,    -3
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+      -3,    -3,    -3,    -3,    -3,    -3,     1,    -3
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+       2,    13,    14,    10,     3,     4,     7,     0,    16,     0,
+      12
+};
+
+static const yytype_int8 yycheck[] =
+{
+       0,     3,     4,     4,     4,     5,     3,    -1,     4,    -1,
+       9
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     8,     0,     4,     5,     9,    10,     3,    13,    11,
+       4,    12,    13,     3,     4,    14,     4
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+	      (Loc).first_line, (Loc).first_column,	\
+	      (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc, scanner)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value, Location, context); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_lgl_parsedata_t* context;
+#endif
+{
+  if (!yyvaluep)
+    return;
+  YYUSE (yylocationp);
+  YYUSE (context);
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_lgl_parsedata_t* context;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  YY_LOCATION_PRINT (yyoutput, *yylocationp);
+  YYFPRINTF (yyoutput, ": ");
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    yytype_int16 *bottom;
+    yytype_int16 *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_lgl_parsedata_t* context)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule, context)
+    YYSTYPE *yyvsp;
+    YYLTYPE *yylsp;
+    int yyrule;
+    igraph_i_lgl_parsedata_t* context;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       , &(yylsp[(yyi + 1) - (yynrhs)])		       , context);
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, yylsp, Rule, context); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+	 constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+		    + sizeof yyexpecting - 1
+		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+		       * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+	 YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	  {
+	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+	      {
+		yycount = 1;
+		yysize = yysize0;
+		yyformat[sizeof yyunexpected - 1] = '\0';
+		break;
+	      }
+	    yyarg[yycount++] = yytname[yyx];
+	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+	    yysize_overflow |= (yysize1 < yysize);
+	    yysize = yysize1;
+	    yyfmt = yystpcpy (yyfmt, yyprefix);
+	    yyprefix = yyor;
+	  }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+	return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+	{
+	  /* Avoid sprintf, as that infringes on the user's name space.
+	     Don't have undefined behavior even if the translation
+	     produced a string with the wrong number of "%s"s.  */
+	  char *yyp = yyresult;
+	  int yyi = 0;
+	  while ((*yyp = *yyf) != '\0')
+	    {
+	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+		{
+		  yyp += yytnamerr (yyp, yyarg[yyi++]);
+		  yyf += 2;
+		}
+	      else
+		{
+		  yyp++;
+		  yyf++;
+		}
+	    }
+	}
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_lgl_parsedata_t* context)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp, context)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+    YYLTYPE *yylocationp;
+    igraph_i_lgl_parsedata_t* context;
+#endif
+{
+  YYUSE (yyvaluep);
+  YYUSE (yylocationp);
+  YYUSE (context);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (igraph_i_lgl_parsedata_t* context);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (igraph_i_lgl_parsedata_t* context)
+#else
+int
+yyparse (context)
+    igraph_i_lgl_parsedata_t* context;
+#endif
+#endif
+{
+  /* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+/* Location data for the look-ahead symbol.  */
+YYLTYPE yylloc;
+
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+  /* The location stack.  */
+  YYLTYPE yylsa[YYINITDEPTH];
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+  /* The locations where the error started and ended.  */
+  YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+  YYLTYPE yyloc;
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+  yylsp = yyls;
+#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+  /* Initialize the default location before parsing starts.  */
+  yylloc.first_line   = yylloc.last_line   = 1;
+  yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+	YYLTYPE *yyls1 = yyls;
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yyls1, yysize * sizeof (*yylsp),
+		    &yystacksize);
+	yyls = yyls1;
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+	YYSTACK_RELOCATE (yyls);
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+      yylsp = yyls + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+  *++yylsp = yylloc;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+  /* Default location.  */
+  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 6:
+#line 103 "../../src/foreign-lgl-parser.y"
+    { context->actvertex=(yyvsp[(2) - (3)].edgenum); }
+    break;
+
+  case 9:
+#line 107 "../../src/foreign-lgl-parser.y"
+    { 
+             igraph_vector_push_back(context->vector, context->actvertex);
+             igraph_vector_push_back(context->vector, (yyvsp[(1) - (2)].edgenum));
+             igraph_vector_push_back(context->weights, 0);
+           }
+    break;
+
+  case 10:
+#line 112 "../../src/foreign-lgl-parser.y"
+    { 
+	     igraph_vector_push_back(context->vector, context->actvertex);
+             igraph_vector_push_back(context->vector, (yyvsp[(1) - (3)].edgenum));
+             igraph_vector_push_back(context->weights, (yyvsp[(2) - (3)].weightnum));
+	     context->has_weights = 1;
+           }
+    break;
+
+  case 11:
+#line 121 "../../src/foreign-lgl-parser.y"
+    { igraph_trie_get2(context->trie, 
+				   igraph_lgl_yyget_text(scanner), 
+				   igraph_lgl_yyget_leng(scanner), 
+				   &(yyval.edgenum)); }
+    break;
+
+  case 12:
+#line 126 "../../src/foreign-lgl-parser.y"
+    { (yyval.weightnum)=igraph_lgl_get_number(igraph_lgl_yyget_text(scanner), 
+					   igraph_lgl_yyget_leng(scanner)); }
+    break;
+
+
+/* Line 1267 of yacc.c.  */
+#line 1456 "foreign-lgl-parser.c"
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+  *++yylsp = yyloc;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (&yylloc, context, YY_("syntax error"));
+#else
+      {
+	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+	  {
+	    YYSIZE_T yyalloc = 2 * yysize;
+	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
+	    if (yymsg != yymsgbuf)
+	      YYSTACK_FREE (yymsg);
+	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+	    if (yymsg)
+	      yymsg_alloc = yyalloc;
+	    else
+	      {
+		yymsg = yymsgbuf;
+		yymsg_alloc = sizeof yymsgbuf;
+	      }
+	  }
+
+	if (0 < yysize && yysize <= yymsg_alloc)
+	  {
+	    (void) yysyntax_error (yymsg, yystate, yychar);
+	    yyerror (&yylloc, context, yymsg);
+	  }
+	else
+	  {
+	    yyerror (&yylloc, context, YY_("syntax error"));
+	    if (yysize != 0)
+	      goto yyexhaustedlab;
+	  }
+      }
+#endif
+    }
+
+  yyerror_range[0] = yylloc;
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval, &yylloc, context);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  yyerror_range[0] = yylsp[1-yylen];
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      yyerror_range[0] = *yylsp;
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+  yyerror_range[1] = yylloc;
+  /* Using YYLLOC is tempting, but would change the location of
+     the look-ahead.  YYLOC is available though.  */
+  YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+  *++yylsp = yyloc;
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (&yylloc, context, YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+		 yytoken, &yylval, &yylloc, context);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+#line 129 "../../src/foreign-lgl-parser.y"
+
+
+int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, 
+		       const char *s) {
+  snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char), 
+	   "Parse error in LGL file, line %i (%s)", 
+	   locp->first_line, s);
+  return 0;
+}
+
+igraph_real_t igraph_lgl_get_number(const char *str, long int length) {
+  igraph_real_t num;
+  char *tmp=igraph_Calloc(length+1, char);
+  
+  strncpy(tmp, str, length);
+  tmp[length]='\0';
+  sscanf(tmp, "%lf", &num);
+  igraph_Free(tmp);
+  return num;
+} 
+
diff --git a/igraph/src/foreign-ncol-lexer.c b/igraph/src/foreign-ncol-lexer.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-ncol-lexer.c
@@ -0,0 +1,2010 @@
+#line 2 "foreign-ncol-lexer.c"
+
+#line 4 "foreign-ncol-lexer.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE igraph_ncol_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = yyg->yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	yy_size_t yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via igraph_ncol_yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+                          ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void igraph_ncol_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void igraph_ncol_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_ncol_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void igraph_ncol_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_ncol_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_ncol_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void igraph_ncol_yypop_buffer_state (yyscan_t yyscanner );
+
+static void igraph_ncol_yyensure_buffer_stack (yyscan_t yyscanner );
+static void igraph_ncol_yy_load_buffer_state (yyscan_t yyscanner );
+static void igraph_ncol_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER igraph_ncol_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE igraph_ncol_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_ncol_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_ncol_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *igraph_ncol_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *igraph_ncol_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void igraph_ncol_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer igraph_ncol_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        igraph_ncol_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_ncol_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        igraph_ncol_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_ncol_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define igraph_ncol_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yyg->yytext_ptr = yy_bp; \
+	yyleng = (yy_size_t) (yy_cp - yy_bp); \
+	yyg->yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 5
+#define YY_END_OF_BUFFER 6
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[12] =
+    {   0,
+        1,    1,    6,    3,    1,    2,    2,    3,    1,    2,
+        0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    4,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[5] =
+    {   0,
+        1,    2,    3,    4
+    } ;
+
+static yyconst flex_int16_t yy_base[16] =
+    {   0,
+        0,    0,    9,    0,    0,    0,    0,    0,    0,   10,
+       10,    7,    5,    2,    2
+    } ;
+
+static yyconst flex_int16_t yy_def[16] =
+    {   0,
+       11,    1,   11,   12,   13,   14,   15,   12,   13,   11,
+        0,   11,   11,   11,   11
+    } ;
+
+static yyconst flex_int16_t yy_nxt[15] =
+    {   0,
+        4,    5,    6,    7,   10,   10,    9,    8,   11,    3,
+       11,   11,   11,   11
+    } ;
+
+static yyconst flex_int16_t yy_chk[15] =
+    {   0,
+        1,    1,    1,    1,   15,   14,   13,   12,    3,   11,
+       11,   11,   11,   11
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "../../src/foreign-ncol-lexer.l"
+/* 
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+#line 24 "../../src/foreign-ncol-lexer.l"
+
+/* 
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include <stdlib.h>
+#include "foreign-ncol-header.h"
+#include "foreign-ncol-parser.h"
+#define YY_EXTRA_TYPE igraph_i_ncol_parsedata_t*
+#define YY_USER_ACTION yylloc->first_line = yylineno;
+/* We assume that 'file' is 'stderr' here. */
+#ifdef USING_R
+#define fprintf(file, msg, ...) (1)
+#endif
+#ifdef stdout 
+#  undef stdout
+#endif
+#define stdout 0
+#define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \
+				__LINE__, IGRAPH_PARSEERROR);
+#define YY_NO_INPUT 1
+#line 500 "foreign-ncol-lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+    {
+
+    /* User-defined. Not touched by flex. */
+    YY_EXTRA_TYPE yyextra_r;
+
+    /* The rest are the same as the globals declared in the non-reentrant scanner. */
+    FILE *yyin_r, *yyout_r;
+    size_t yy_buffer_stack_top; /**< index of top of stack. */
+    size_t yy_buffer_stack_max; /**< capacity of stack. */
+    YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+    char yy_hold_char;
+    yy_size_t yy_n_chars;
+    yy_size_t yyleng_r;
+    char *yy_c_buf_p;
+    int yy_init;
+    int yy_start;
+    int yy_did_buffer_switch_on_eof;
+    int yy_start_stack_ptr;
+    int yy_start_stack_depth;
+    int *yy_start_stack;
+    yy_state_type yy_last_accepting_state;
+    char* yy_last_accepting_cpos;
+
+    int yylineno_r;
+    int yy_flex_debug_r;
+
+    char *yytext_r;
+    int yy_more_flag;
+    int yy_more_len;
+
+    YYSTYPE * yylval_r;
+
+    YYLTYPE * yylloc_r;
+
+    }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+    /* This must go here because YYSTYPE and YYLTYPE are included
+     * from bison output in section 1.*/
+    #    define yylval yyg->yylval_r
+    
+    #    define yylloc yyg->yylloc_r
+    
+int igraph_ncol_yylex_init (yyscan_t* scanner);
+
+int igraph_ncol_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int igraph_ncol_yylex_destroy (yyscan_t yyscanner );
+
+int igraph_ncol_yyget_debug (yyscan_t yyscanner );
+
+void igraph_ncol_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE igraph_ncol_yyget_extra (yyscan_t yyscanner );
+
+void igraph_ncol_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *igraph_ncol_yyget_in (yyscan_t yyscanner );
+
+void igraph_ncol_yyset_in  (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *igraph_ncol_yyget_out (yyscan_t yyscanner );
+
+void igraph_ncol_yyset_out  (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t igraph_ncol_yyget_leng (yyscan_t yyscanner );
+
+char *igraph_ncol_yyget_text (yyscan_t yyscanner );
+
+int igraph_ncol_yyget_lineno (yyscan_t yyscanner );
+
+void igraph_ncol_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * igraph_ncol_yyget_lval (yyscan_t yyscanner );
+
+void igraph_ncol_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+       YYLTYPE *igraph_ncol_yyget_lloc (yyscan_t yyscanner );
+    
+        void igraph_ncol_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+    
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int igraph_ncol_yywrap (yyscan_t yyscanner );
+#else
+extern int igraph_ncol_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		yy_size_t n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int igraph_ncol_yylex \
+               (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int igraph_ncol_yylex \
+               (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 77 "../../src/foreign-ncol-lexer.l"
+
+
+ /* ------------------------------------------------whitespace------*/
+#line 743 "foreign-ncol-lexer.c"
+
+    yylval = yylval_param;
+
+    yylloc = yylloc_param;
+
+	if ( !yyg->yy_init )
+		{
+		yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yyg->yy_start )
+			yyg->yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			igraph_ncol_yyensure_buffer_stack (yyscanner);
+			YY_CURRENT_BUFFER_LVALUE =
+				igraph_ncol_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+		}
+
+		igraph_ncol_yy_load_buffer_state(yyscanner );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yyg->yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yyg->yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yyg->yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yyg->yy_last_accepting_state = yy_current_state;
+				yyg->yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 12 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 10 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yyg->yy_hold_char;
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 80 "../../src/foreign-ncol-lexer.l"
+{ }
+	YY_BREAK
+/* ---------------------------------------------------newline------*/
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 83 "../../src/foreign-ncol-lexer.l"
+{ return NEWLINE; }
+	YY_BREAK
+/* ----------------------------------------------alphanumeric------*/
+case 3:
+YY_RULE_SETUP
+#line 86 "../../src/foreign-ncol-lexer.l"
+{ return ALNUM; }
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+#line 88 "../../src/foreign-ncol-lexer.l"
+{ if (yyextra->eof) {
+                       yyterminate();
+                    } else {
+                       yyextra->eof=1;
+                       return NEWLINE; 
+                    }
+                  }
+	YY_BREAK
+/* ---------------------------------------------anything else------*/
+case 4:
+YY_RULE_SETUP
+#line 97 "../../src/foreign-ncol-lexer.l"
+{ return ERROR; }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 99 "../../src/foreign-ncol-lexer.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+#line 869 "foreign-ncol-lexer.c"
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yyg->yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * igraph_ncol_yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state( yyscanner );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+			yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yyg->yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yyg->yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer( yyscanner ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yyg->yy_did_buffer_switch_on_eof = 0;
+
+				if ( igraph_ncol_yywrap(yyscanner ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yyg->yy_c_buf_p =
+					yyg->yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yyg->yy_c_buf_p =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of igraph_ncol_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = yyg->yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+	else
+		{
+			yy_size_t num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				yy_size_t new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					igraph_ncol_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			yyg->yy_n_chars, num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	if ( yyg->yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			igraph_ncol_yyrestart(yyin  ,yyscanner);
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_ncol_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	yyg->yy_n_chars += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_current_state = yyg->yy_start;
+
+	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yyg->yy_last_accepting_state = yy_current_state;
+			yyg->yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 12 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+	register int yy_is_jam;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+	register char *yy_cp = yyg->yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yyg->yy_last_accepting_state = yy_current_state;
+		yyg->yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 12 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 11);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (yyscan_t yyscanner)
+#else
+    static int input  (yyscan_t yyscanner)
+#endif
+
+{
+	int c;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	*yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+	if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			/* This was really a NUL. */
+			*yyg->yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			++yyg->yy_c_buf_p;
+
+			switch ( yy_get_next_buffer( yyscanner ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					igraph_ncol_yyrestart(yyin ,yyscanner);
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( igraph_ncol_yywrap(yyscanner ) )
+						return 0;
+
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput(yyscanner);
+#else
+					return input(yyscanner);
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yyg->yy_c_buf_p;	/* cast for 8-bit char's */
+	*yyg->yy_c_buf_p = '\0';	/* preserve yytext */
+	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void igraph_ncol_yyrestart  (FILE * input_file , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! YY_CURRENT_BUFFER ){
+        igraph_ncol_yyensure_buffer_stack (yyscanner);
+		YY_CURRENT_BUFFER_LVALUE =
+            igraph_ncol_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+	}
+
+	igraph_ncol_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+	igraph_ncol_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_ncol_yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		igraph_ncol_yypop_buffer_state();
+	 *		igraph_ncol_yypush_buffer_state(new_buffer);
+     */
+	igraph_ncol_yyensure_buffer_stack (yyscanner);
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	igraph_ncol_yy_load_buffer_state(yyscanner );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (igraph_ncol_yywrap()) processing, but the only time this flag
+	 * is looked at is after igraph_ncol_yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void igraph_ncol_yy_load_buffer_state  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE igraph_ncol_yy_create_buffer  (FILE * file, int  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) igraph_ncol_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) igraph_ncol_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	igraph_ncol_yy_init_buffer(b,file ,yyscanner);
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with igraph_ncol_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+    void igraph_ncol_yy_delete_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		igraph_ncol_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+	igraph_ncol_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a igraph_ncol_yyrestart() or at EOF.
+ */
+    static void igraph_ncol_yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file , yyscan_t yyscanner)
+
+{
+	int oerrno = errno;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	igraph_ncol_yy_flush_buffer(b ,yyscanner);
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then igraph_ncol_yy_init_buffer was _probably_
+     * called from igraph_ncol_yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_ncol_yy_flush_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		igraph_ncol_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_ncol_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (new_buffer == NULL)
+		return;
+
+	igraph_ncol_yyensure_buffer_stack(yyscanner);
+
+	/* This block is copied from igraph_ncol_yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		yyg->yy_buffer_stack_top++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from igraph_ncol_yy_switch_to_buffer. */
+	igraph_ncol_yy_load_buffer_state(yyscanner );
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_ncol_yypop_buffer_state (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (!YY_CURRENT_BUFFER)
+		return;
+
+	igraph_ncol_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if (yyg->yy_buffer_stack_top > 0)
+		--yyg->yy_buffer_stack_top;
+
+	if (YY_CURRENT_BUFFER) {
+		igraph_ncol_yy_load_buffer_state(yyscanner );
+		yyg->yy_did_buffer_switch_on_eof = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void igraph_ncol_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+	yy_size_t num_to_alloc;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if (!yyg->yy_buffer_stack) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_ncol_yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yyensure_buffer_stack()" );
+								  
+		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		yyg->yy_buffer_stack_max = num_to_alloc;
+		yyg->yy_buffer_stack_top = 0;
+		return;
+	}
+
+	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_ncol_yyrealloc
+								(yyg->yy_buffer_stack,
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+		yyg->yy_buffer_stack_max = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE igraph_ncol_yy_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) igraph_ncol_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	igraph_ncol_yy_switch_to_buffer(b ,yyscanner );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to igraph_ncol_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       igraph_ncol_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE igraph_ncol_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+    
+	return igraph_ncol_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to igraph_ncol_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE igraph_ncol_yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n, i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) igraph_ncol_yyalloc(n ,yyscanner );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_ncol_yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = igraph_ncol_yy_scan_buffer(buf,n ,yyscanner);
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in igraph_ncol_yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = yyg->yy_hold_char; \
+		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+		yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+		*yyg->yy_c_buf_p = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE igraph_ncol_yyget_extra  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_ncol_yyget_lineno  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_ncol_yyget_column  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_ncol_yyget_in  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_ncol_yyget_out  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+yy_size_t igraph_ncol_yyget_leng  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *igraph_ncol_yyget_text  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void igraph_ncol_yyset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_ncol_yyset_lineno (int  line_number , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* lineno is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_ncol_yyset_lineno called with no buffer" , yyscanner); 
+    
+    yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_ncol_yyset_column (int  column_no , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* column is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_ncol_yyset_column called with no buffer" , yyscanner); 
+    
+    yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see igraph_ncol_yy_switch_to_buffer
+ */
+void igraph_ncol_yyset_in (FILE *  in_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyin = in_str ;
+}
+
+void igraph_ncol_yyset_out (FILE *  out_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyout = out_str ;
+}
+
+int igraph_ncol_yyget_debug  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yy_flex_debug;
+}
+
+void igraph_ncol_yyset_debug (int  bdebug , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * igraph_ncol_yyget_lval  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylval;
+}
+
+void igraph_ncol_yyset_lval (YYSTYPE *  yylval_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylval = yylval_param;
+}
+
+YYLTYPE *igraph_ncol_yyget_lloc  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylloc;
+}
+    
+void igraph_ncol_yyset_lloc (YYLTYPE *  yylloc_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylloc = yylloc_param;
+}
+    
+/* User-visible API */
+
+/* igraph_ncol_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int igraph_ncol_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) igraph_ncol_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* igraph_ncol_yylex_init_extra has the same functionality as igraph_ncol_yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to igraph_ncol_yyalloc in
+ * the yyextra field.
+ */
+
+int igraph_ncol_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+    struct yyguts_t dummy_yyguts;
+
+    igraph_ncol_yyset_extra (yy_user_defined, &dummy_yyguts);
+
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+	
+    *ptr_yy_globals = (yyscan_t) igraph_ncol_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+	
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+    
+    /* By setting to 0xAA, we expose bugs in
+    yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+    
+    igraph_ncol_yyset_extra (yy_user_defined, *ptr_yy_globals);
+    
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from igraph_ncol_yylex_destroy(), so don't allocate here.
+     */
+
+    yyg->yy_buffer_stack = 0;
+    yyg->yy_buffer_stack_top = 0;
+    yyg->yy_buffer_stack_max = 0;
+    yyg->yy_c_buf_p = (char *) 0;
+    yyg->yy_init = 0;
+    yyg->yy_start = 0;
+
+    yyg->yy_start_stack_ptr = 0;
+    yyg->yy_start_stack_depth = 0;
+    yyg->yy_start_stack =  NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * igraph_ncol_yylex_init()
+     */
+    return 0;
+}
+
+/* igraph_ncol_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int igraph_ncol_yylex_destroy  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		igraph_ncol_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		igraph_ncol_yypop_buffer_state(yyscanner);
+	}
+
+	/* Destroy the stack itself. */
+	igraph_ncol_yyfree(yyg->yy_buffer_stack ,yyscanner);
+	yyg->yy_buffer_stack = NULL;
+
+    /* Destroy the start condition stack. */
+        igraph_ncol_yyfree(yyg->yy_start_stack ,yyscanner );
+        yyg->yy_start_stack = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * igraph_ncol_yylex() is called, initialization will occur. */
+    yy_init_globals( yyscanner);
+
+    /* Destroy the main struct (reentrant only). */
+    igraph_ncol_yyfree ( yyscanner , yyscanner );
+    yyscanner = NULL;
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *igraph_ncol_yyalloc (yy_size_t  size , yyscan_t yyscanner)
+{
+	return (void *) malloc( size );
+}
+
+void *igraph_ncol_yyrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void igraph_ncol_yyfree (void * ptr , yyscan_t yyscanner)
+{
+	free( (char *) ptr );	/* see igraph_ncol_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 99 "../../src/foreign-ncol-lexer.l"
+
+
+
+
diff --git a/igraph/src/foreign-ncol-parser.c b/igraph/src/foreign-ncol-parser.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-ncol-parser.c
@@ -0,0 +1,1685 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names.  */
+#define yyparse igraph_ncol_yyparse
+#define yylex   igraph_ncol_yylex
+#define yyerror igraph_ncol_yyerror
+#define yylval  igraph_ncol_yylval
+#define yychar  igraph_ncol_yychar
+#define yydebug igraph_ncol_yydebug
+#define yynerrs igraph_ncol_yynerrs
+#define yylloc igraph_ncol_yylloc
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     ALNUM = 258,
+     NEWLINE = 259,
+     ERROR = 260
+   };
+#endif
+/* Tokens.  */
+#define ALNUM 258
+#define NEWLINE 259
+#define ERROR 260
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 23 "../../src/foreign-ncol-parser.y"
+
+
+/* 
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "igraph_hacks_internal.h"
+#include "igraph_types.h" 
+#include "igraph_types_internal.h"
+#include "igraph_math.h"
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "config.h"
+#include "foreign-ncol-header.h"
+#include "foreign-ncol-parser.h"
+
+#define yyscan_t void*
+
+int igraph_ncol_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, 
+		      void* scanner);
+int igraph_ncol_yyerror(YYLTYPE* locp, 
+			igraph_i_ncol_parsedata_t *context, 
+			const char *s);
+char *igraph_ncol_yyget_text (yyscan_t yyscanner );
+int igraph_ncol_yyget_leng (yyscan_t yyscanner );
+igraph_real_t igraph_ncol_get_number(const char *str, long int len);
+
+#define scanner context->scanner
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 82 "../../src/foreign-ncol-parser.y"
+{
+  long int edgenum;
+  double weightnum;
+}
+/* Line 193 of yacc.c.  */
+#line 169 "foreign-ncol-parser.c"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 216 of yacc.c.  */
+#line 194 "foreign-ncol-parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+    int i;
+#endif
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+	     && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+    YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+      + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  2
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   10
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  6
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  5
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  8
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  12
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   260
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint8 yyprhs[] =
+{
+       0,     0,     3,     4,     7,    10,    14,    19,    21
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+       7,     0,    -1,    -1,     7,     4,    -1,     7,     8,    -1,
+       9,     9,     4,    -1,     9,     9,    10,     4,    -1,     3,
+      -1,     3,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,    96,    96,    97,    98,   101,   106,   114,   119
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "ALNUM", "NEWLINE", "ERROR", "$accept",
+  "input", "edge", "edgeid", "weight", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,     6,     7,     7,     7,     8,     8,     9,    10
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     0,     2,     2,     3,     4,     1,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       2,     0,     1,     7,     3,     4,     0,     0,     8,     5,
+       0,     6
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     1,     5,     6,    10
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -3
+static const yytype_int8 yypact[] =
+{
+      -3,     0,    -3,    -3,    -3,    -3,     2,    -2,    -3,    -3,
+       3,    -3
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+      -3,    -3,    -3,     4,    -3
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+       2,     8,     9,     3,     4,     3,     0,    11,     0,     0,
+       7
+};
+
+static const yytype_int8 yycheck[] =
+{
+       0,     3,     4,     3,     4,     3,    -1,     4,    -1,    -1,
+       6
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     7,     0,     3,     4,     8,     9,     9,     3,     4,
+      10,     4
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+	      (Loc).first_line, (Loc).first_column,	\
+	      (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc, scanner)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value, Location, context); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_ncol_parsedata_t* context;
+#endif
+{
+  if (!yyvaluep)
+    return;
+  YYUSE (yylocationp);
+  YYUSE (context);
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_ncol_parsedata_t* context;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  YY_LOCATION_PRINT (yyoutput, *yylocationp);
+  YYFPRINTF (yyoutput, ": ");
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    yytype_int16 *bottom;
+    yytype_int16 *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_ncol_parsedata_t* context)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule, context)
+    YYSTYPE *yyvsp;
+    YYLTYPE *yylsp;
+    int yyrule;
+    igraph_i_ncol_parsedata_t* context;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       , &(yylsp[(yyi + 1) - (yynrhs)])		       , context);
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, yylsp, Rule, context); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+	 constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+		    + sizeof yyexpecting - 1
+		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+		       * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+	 YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	  {
+	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+	      {
+		yycount = 1;
+		yysize = yysize0;
+		yyformat[sizeof yyunexpected - 1] = '\0';
+		break;
+	      }
+	    yyarg[yycount++] = yytname[yyx];
+	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+	    yysize_overflow |= (yysize1 < yysize);
+	    yysize = yysize1;
+	    yyfmt = yystpcpy (yyfmt, yyprefix);
+	    yyprefix = yyor;
+	  }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+	return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+	{
+	  /* Avoid sprintf, as that infringes on the user's name space.
+	     Don't have undefined behavior even if the translation
+	     produced a string with the wrong number of "%s"s.  */
+	  char *yyp = yyresult;
+	  int yyi = 0;
+	  while ((*yyp = *yyf) != '\0')
+	    {
+	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+		{
+		  yyp += yytnamerr (yyp, yyarg[yyi++]);
+		  yyf += 2;
+		}
+	      else
+		{
+		  yyp++;
+		  yyf++;
+		}
+	    }
+	}
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_ncol_parsedata_t* context)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp, context)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+    YYLTYPE *yylocationp;
+    igraph_i_ncol_parsedata_t* context;
+#endif
+{
+  YYUSE (yyvaluep);
+  YYUSE (yylocationp);
+  YYUSE (context);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (igraph_i_ncol_parsedata_t* context);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (igraph_i_ncol_parsedata_t* context)
+#else
+int
+yyparse (context)
+    igraph_i_ncol_parsedata_t* context;
+#endif
+#endif
+{
+  /* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+/* Location data for the look-ahead symbol.  */
+YYLTYPE yylloc;
+
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+  /* The location stack.  */
+  YYLTYPE yylsa[YYINITDEPTH];
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+  /* The locations where the error started and ended.  */
+  YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+  YYLTYPE yyloc;
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+  yylsp = yyls;
+#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+  /* Initialize the default location before parsing starts.  */
+  yylloc.first_line   = yylloc.last_line   = 1;
+  yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+	YYLTYPE *yyls1 = yyls;
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yyls1, yysize * sizeof (*yylsp),
+		    &yystacksize);
+	yyls = yyls1;
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+	YYSTACK_RELOCATE (yyls);
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+      yylsp = yyls + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+  *++yylsp = yylloc;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+  /* Default location.  */
+  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 5:
+#line 101 "../../src/foreign-ncol-parser.y"
+    { 
+           igraph_vector_push_back(context->vector, (yyvsp[(1) - (3)].edgenum));
+           igraph_vector_push_back(context->vector, (yyvsp[(2) - (3)].edgenum));
+           igraph_vector_push_back(context->weights, 0);
+       }
+    break;
+
+  case 6:
+#line 106 "../../src/foreign-ncol-parser.y"
+    { 
+           igraph_vector_push_back(context->vector, (yyvsp[(1) - (4)].edgenum));
+           igraph_vector_push_back(context->vector, (yyvsp[(2) - (4)].edgenum));
+           igraph_vector_push_back(context->weights, (yyvsp[(3) - (4)].weightnum));
+	   context->has_weights = 1;
+       }
+    break;
+
+  case 7:
+#line 114 "../../src/foreign-ncol-parser.y"
+    { igraph_trie_get2(context->trie, 
+				   igraph_ncol_yyget_text(scanner),
+				   igraph_ncol_yyget_leng(scanner), 
+				   &(yyval.edgenum)); }
+    break;
+
+  case 8:
+#line 119 "../../src/foreign-ncol-parser.y"
+    { (yyval.weightnum)=igraph_ncol_get_number(igraph_ncol_yyget_text(scanner), 
+					    igraph_ncol_yyget_leng(scanner)); }
+    break;
+
+
+/* Line 1267 of yacc.c.  */
+#line 1444 "foreign-ncol-parser.c"
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+  *++yylsp = yyloc;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (&yylloc, context, YY_("syntax error"));
+#else
+      {
+	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+	  {
+	    YYSIZE_T yyalloc = 2 * yysize;
+	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
+	    if (yymsg != yymsgbuf)
+	      YYSTACK_FREE (yymsg);
+	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+	    if (yymsg)
+	      yymsg_alloc = yyalloc;
+	    else
+	      {
+		yymsg = yymsgbuf;
+		yymsg_alloc = sizeof yymsgbuf;
+	      }
+	  }
+
+	if (0 < yysize && yysize <= yymsg_alloc)
+	  {
+	    (void) yysyntax_error (yymsg, yystate, yychar);
+	    yyerror (&yylloc, context, yymsg);
+	  }
+	else
+	  {
+	    yyerror (&yylloc, context, YY_("syntax error"));
+	    if (yysize != 0)
+	      goto yyexhaustedlab;
+	  }
+      }
+#endif
+    }
+
+  yyerror_range[0] = yylloc;
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval, &yylloc, context);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  yyerror_range[0] = yylsp[1-yylen];
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      yyerror_range[0] = *yylsp;
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+  yyerror_range[1] = yylloc;
+  /* Using YYLLOC is tempting, but would change the location of
+     the look-ahead.  YYLOC is available though.  */
+  YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+  *++yylsp = yyloc;
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (&yylloc, context, YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+		 yytoken, &yylval, &yylloc, context);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+#line 122 "../../src/foreign-ncol-parser.y"
+
+
+int igraph_ncol_yyerror(YYLTYPE* locp, 
+			igraph_i_ncol_parsedata_t *context, 
+			const char *s) {
+  snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, 
+	   "Parse error in NCOL file, line %i (%s)", 
+	   locp->first_line, s);
+  return 0;
+}
+
+igraph_real_t igraph_ncol_get_number(const char *str, long int length) {
+  igraph_real_t num;
+  char *tmp=igraph_Calloc(length+1, char);
+  
+  strncpy(tmp, str, length);
+  tmp[length]='\0';
+  sscanf(tmp, "%lf", &num);
+  igraph_Free(tmp);
+  return num;
+} 
+
diff --git a/igraph/src/foreign-pajek-lexer.c b/igraph/src/foreign-pajek-lexer.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-pajek-lexer.c
@@ -0,0 +1,2470 @@
+#line 2 "foreign-pajek-lexer.c"
+
+#line 4 "foreign-pajek-lexer.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE igraph_pajek_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = yyg->yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	yy_size_t yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via igraph_pajek_yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+                          ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void igraph_pajek_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void igraph_pajek_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_pajek_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void igraph_pajek_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_pajek_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void igraph_pajek_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void igraph_pajek_yypop_buffer_state (yyscan_t yyscanner );
+
+static void igraph_pajek_yyensure_buffer_stack (yyscan_t yyscanner );
+static void igraph_pajek_yy_load_buffer_state (yyscan_t yyscanner );
+static void igraph_pajek_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER igraph_pajek_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE igraph_pajek_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_pajek_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE igraph_pajek_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *igraph_pajek_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *igraph_pajek_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void igraph_pajek_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer igraph_pajek_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        igraph_pajek_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_pajek_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        igraph_pajek_yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            igraph_pajek_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define igraph_pajek_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yyg->yytext_ptr = yy_bp; \
+	yyleng = (yy_size_t) (yy_cp - yy_bp); \
+	yyg->yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 48
+#define YY_END_OF_BUFFER 49
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[160] =
+    {   0,
+        1,    1,   49,   46,    1,   12,   12,   46,   46,   46,
+       46,   46,   15,   46,   46,   46,   46,   46,   46,   46,
+       46,   46,   46,   46,   46,   46,   46,   46,   46,   46,
+        1,   12,   46,    0,   13,   46,    0,    2,    3,   46,
+        0,   14,   46,   46,   46,   46,   46,   15,   46,   46,
+       29,   46,   46,   46,   46,   46,   26,   46,   46,   46,
+       46,   46,   46,   38,   46,   46,   46,   46,   27,   46,
+       23,   22,   28,   46,   46,   30,   46,   46,   13,    2,
+        2,   14,   46,   46,   46,   46,   46,   15,   46,   15,
+       33,   34,   37,   19,   20,   46,   46,   31,   32,   18,
+
+       35,   36,   43,   41,   39,   46,   42,   46,   46,   46,
+       46,   46,    3,   46,   46,   46,    4,   46,   46,   45,
+       46,   21,   46,   25,   46,   46,    7,   46,   46,   46,
+       46,   24,   40,   44,   46,   46,   46,    8,   46,   46,
+       46,   46,   46,   46,   46,   11,   46,   46,   16,   17,
+       46,   46,    5,   46,    9,   46,    6,   10,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    4,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    5,    1,    1,    6,    1,    1,    7,
+        8,    9,   10,    1,   11,   12,    1,   13,   14,   15,
+       13,   13,   13,   13,   13,   13,   13,    1,    1,    1,
+        1,    1,    1,    1,   16,   17,   18,   19,   20,   21,
+       22,   23,   24,    1,   25,   26,   27,   28,   29,   30,
+       31,   32,   33,   34,   35,   36,   37,   38,   39,   40,
+        1,    1,    1,    1,   41,    1,   16,   17,   18,   19,
+
+       20,   21,   22,   23,   24,    1,   25,   26,   27,   28,
+       29,   30,   31,   32,   33,   34,   35,   36,   37,   38,
+       39,   40,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[42] =
+    {   0,
+        1,    2,    2,    2,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1
+    } ;
+
+static yyconst flex_int16_t yy_base[167] =
+    {   0,
+        0,    0,  288,    0,  285,  282,  282,   40,   44,   47,
+       36,   44,   53,   67,   42,   72,  255,   39,  265,   47,
+       96,   81,   84,   87,   91,  250,   99,  240,  239,    0,
+      277,  289,  103,  273,    0,  107,   74,  273,  113,  116,
+      268,    0,  243,  255,  257,  252,  251,  117,  108,  125,
+      289,  139,  142,  145,  148,  151,  289,  128,  155,  160,
+      163,  166,  169,  289,  172,  175,  178,  181,  289,  246,
+      289,  289,  289,  229,  242,  289,  246,  245,  289,  261,
+      130,  289,  246,  241,  228,  227,  228,  173,  176,  181,
+      289,  289,  289,  289,  289,  225,  195,  289,  289,  289,
+
+      289,  289,  289,  289,  289,  234,  289,  200,  237,  203,
+      240,  239,  251,  220,  232,  219,  213,  215,  206,  289,
+      209,  289,  212,  289,  230,  229,  220,  212,  220,  214,
+      218,  289,  289,  289,  207,  206,  215,  212,  199,  204,
+      217,  215,  218,  167,  168,    0,  135,  107,  289,  289,
+       91,   80,    0,   63,    0,   58,    0,    0,  289,   79,
+      222,  224,  226,  228,  230,  232
+    } ;
+
+static yyconst flex_int16_t yy_def[167] =
+    {   0,
+      159,    1,  159,  160,  159,  159,  159,  161,  162,  163,
+      160,  160,  160,  160,  160,  160,  160,  160,  160,  160,
+      160,  160,  160,  160,  160,  160,  160,  160,  160,  160,
+      159,  159,  161,  164,  160,  162,  165,  159,  165,  163,
+      166,  160,  160,  160,  160,  160,  160,  160,  160,  160,
+      159,  160,  160,  160,  160,  160,  159,  160,  160,  160,
+      160,  160,  160,  159,  160,  160,  160,  160,  159,  160,
+      159,  159,  159,  160,  160,  159,  160,  160,  159,  159,
+      159,  159,  160,  160,  160,  160,  160,  160,  160,  160,
+      159,  159,  159,  159,  159,  160,  160,  159,  159,  159,
+
+      159,  159,  159,  159,  159,  160,  159,  160,  160,  160,
+      160,  160,  159,  160,  160,  160,  160,  160,  160,  159,
+      160,  159,  160,  159,  160,  160,  160,  160,  160,  160,
+      160,  159,  159,  159,  160,  160,  160,  160,  160,  160,
+      160,  160,  160,  160,  160,  160,  160,  160,  159,  159,
+      160,  160,  160,  160,  160,  160,  160,  160,    0,  159,
+      159,  159,  159,  159,  159,  159
+    } ;
+
+static yyconst flex_int16_t yy_nxt[331] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,    4,   11,    4,
+       12,    4,   13,   13,   13,   14,   15,   16,    4,    4,
+       17,    4,   18,   19,   20,   21,    4,    4,    4,   22,
+       23,   24,   25,    4,   26,    4,   27,   28,   29,    4,
+        4,   34,   34,   34,   35,   37,   38,   39,   41,   41,
+       41,   43,   59,   60,   42,   44,   48,   48,   48,   55,
+       62,   63,   45,   46,   49,   48,   48,   48,   51,   51,
+       51,   47,   50,   57,   57,   57,   38,   39,   56,   30,
+       52,   53,   69,   69,   69,   71,   71,   71,   72,   72,
+       72,  158,   73,   73,   73,  157,   54,   64,   64,   64,
+
+       76,   76,   76,   70,   34,   34,   34,   35,   37,   38,
+       39,   65,  156,   66,   74,   81,   39,   41,   41,   41,
+       88,   88,   88,   42,  155,   67,  154,   68,   49,   48,
+       48,   48,  113,   80,   89,   89,   50,   90,   90,   90,
+       91,   91,   91,   92,   92,   92,   93,   93,   93,   94,
+       94,   94,   95,   95,   95,   96,   98,   98,   98,  153,
+       97,   99,   99,   99,  100,  100,  100,  101,  101,  101,
+      102,  102,  102,  103,  103,  103,  104,  104,  104,  105,
+      105,  105,  107,  107,  107,   88,   88,   88,   90,   90,
+       90,  152,   50,   90,   90,   90,  120,  120,  120,  151,
+
+      106,  122,  122,  122,  124,  124,  124,  132,  132,  132,
+      133,  133,  133,  134,  134,  134,  149,  149,  149,  150,
+      150,  150,   33,   33,   36,   36,   40,   40,   34,   34,
+       37,   37,   41,   41,  148,  147,  146,  145,  144,  143,
+      142,  141,  140,  139,  138,  137,  136,  135,  131,  130,
+      129,  128,  127,  113,  126,  125,  123,  121,  119,  118,
+      117,  116,  115,  114,   80,  112,  111,  110,  109,  108,
+       87,   86,   85,   84,   83,   82,   80,   79,   31,   78,
+       77,   75,   61,   58,   32,   32,   31,  159,    3,  159,
+      159,  159,  159,  159,  159,  159,  159,  159,  159,  159,
+
+      159,  159,  159,  159,  159,  159,  159,  159,  159,  159,
+      159,  159,  159,  159,  159,  159,  159,  159,  159,  159,
+      159,  159,  159,  159,  159,  159,  159,  159,  159,  159
+    } ;
+
+static yyconst flex_int16_t yy_chk[331] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    8,    8,    8,    8,    9,    9,    9,   10,   10,
+       10,   11,   18,   18,   10,   11,   12,   12,   12,   15,
+       20,   20,   11,   11,   13,   13,   13,   13,   14,   14,
+       14,   11,   13,   16,   16,   16,   37,   37,   15,  160,
+       14,   14,   22,   22,   22,   23,   23,   23,   24,   24,
+       24,  156,   25,   25,   25,  154,   14,   21,   21,   21,
+
+       27,   27,   27,   22,   33,   33,   33,   33,   36,   36,
+       36,   21,  152,   21,   25,   39,   39,   40,   40,   40,
+       49,   49,   49,   40,  151,   21,  148,   21,   48,   48,
+       48,   48,   81,   81,   50,   50,   48,   50,   50,   50,
+       52,   52,   52,   53,   53,   53,   54,   54,   54,   55,
+       55,   55,   56,   56,   56,   58,   59,   59,   59,  147,
+       58,   60,   60,   60,   61,   61,   61,   62,   62,   62,
+       63,   63,   63,   65,   65,   65,   66,   66,   66,   67,
+       67,   67,   68,   68,   68,   88,   88,   88,   89,   89,
+       89,  145,   88,   90,   90,   90,   97,   97,   97,  144,
+
+       67,  108,  108,  108,  110,  110,  110,  119,  119,  119,
+      121,  121,  121,  123,  123,  123,  142,  142,  142,  143,
+      143,  143,  161,  161,  162,  162,  163,  163,  164,  164,
+      165,  165,  166,  166,  141,  140,  139,  138,  137,  136,
+      135,  131,  130,  129,  128,  127,  126,  125,  118,  117,
+      116,  115,  114,  113,  112,  111,  109,  106,   96,   87,
+       86,   85,   84,   83,   80,   78,   77,   75,   74,   70,
+       47,   46,   45,   44,   43,   41,   38,   34,   31,   29,
+       28,   26,   19,   17,    7,    6,    5,    3,  159,  159,
+      159,  159,  159,  159,  159,  159,  159,  159,  159,  159,
+
+      159,  159,  159,  159,  159,  159,  159,  159,  159,  159,
+      159,  159,  159,  159,  159,  159,  159,  159,  159,  159,
+      159,  159,  159,  159,  159,  159,  159,  159,  159,  159
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "../../src/foreign-pajek-lexer.l"
+/* 
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+#line 24 "../../src/foreign-pajek-lexer.l"
+
+/* 
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include <stdlib.h>
+#include "foreign-pajek-header.h"
+#include "foreign-pajek-parser.h"
+#define YY_EXTRA_TYPE igraph_i_pajek_parsedata_t*
+#define YY_USER_ACTION yylloc->first_line = yylineno;
+/* We assume that 'file' is 'stderr' here. */
+#ifdef USING_R
+#define fprintf(file, msg, ...) (1)
+#endif
+#ifdef stdout 
+#  undef stdout
+#endif
+#define stdout 0
+#define exit(code) igraph_error("Fatal error in DL parser", __FILE__, \
+				__LINE__, IGRAPH_PARSEERROR);
+#define YY_NO_INPUT 1
+#line 619 "foreign-pajek-lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+    {
+
+    /* User-defined. Not touched by flex. */
+    YY_EXTRA_TYPE yyextra_r;
+
+    /* The rest are the same as the globals declared in the non-reentrant scanner. */
+    FILE *yyin_r, *yyout_r;
+    size_t yy_buffer_stack_top; /**< index of top of stack. */
+    size_t yy_buffer_stack_max; /**< capacity of stack. */
+    YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+    char yy_hold_char;
+    yy_size_t yy_n_chars;
+    yy_size_t yyleng_r;
+    char *yy_c_buf_p;
+    int yy_init;
+    int yy_start;
+    int yy_did_buffer_switch_on_eof;
+    int yy_start_stack_ptr;
+    int yy_start_stack_depth;
+    int *yy_start_stack;
+    yy_state_type yy_last_accepting_state;
+    char* yy_last_accepting_cpos;
+
+    int yylineno_r;
+    int yy_flex_debug_r;
+
+    char *yytext_r;
+    int yy_more_flag;
+    int yy_more_len;
+
+    YYSTYPE * yylval_r;
+
+    YYLTYPE * yylloc_r;
+
+    }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+    /* This must go here because YYSTYPE and YYLTYPE are included
+     * from bison output in section 1.*/
+    #    define yylval yyg->yylval_r
+    
+    #    define yylloc yyg->yylloc_r
+    
+int igraph_pajek_yylex_init (yyscan_t* scanner);
+
+int igraph_pajek_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int igraph_pajek_yylex_destroy (yyscan_t yyscanner );
+
+int igraph_pajek_yyget_debug (yyscan_t yyscanner );
+
+void igraph_pajek_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE igraph_pajek_yyget_extra (yyscan_t yyscanner );
+
+void igraph_pajek_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *igraph_pajek_yyget_in (yyscan_t yyscanner );
+
+void igraph_pajek_yyset_in  (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *igraph_pajek_yyget_out (yyscan_t yyscanner );
+
+void igraph_pajek_yyset_out  (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t igraph_pajek_yyget_leng (yyscan_t yyscanner );
+
+char *igraph_pajek_yyget_text (yyscan_t yyscanner );
+
+int igraph_pajek_yyget_lineno (yyscan_t yyscanner );
+
+void igraph_pajek_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * igraph_pajek_yyget_lval (yyscan_t yyscanner );
+
+void igraph_pajek_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+       YYLTYPE *igraph_pajek_yyget_lloc (yyscan_t yyscanner );
+    
+        void igraph_pajek_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+    
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int igraph_pajek_yywrap (yyscan_t yyscanner );
+#else
+extern int igraph_pajek_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		yy_size_t n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int igraph_pajek_yylex \
+               (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int igraph_pajek_yylex \
+               (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 78 "../../src/foreign-pajek-lexer.l"
+
+
+#line 861 "foreign-pajek-lexer.c"
+
+    yylval = yylval_param;
+
+    yylloc = yylloc_param;
+
+	if ( !yyg->yy_init )
+		{
+		yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yyg->yy_start )
+			yyg->yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			igraph_pajek_yyensure_buffer_stack (yyscanner);
+			YY_CURRENT_BUFFER_LVALUE =
+				igraph_pajek_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+		}
+
+		igraph_pajek_yy_load_buffer_state(yyscanner );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yyg->yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yyg->yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yyg->yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yyg->yy_last_accepting_state = yy_current_state;
+				yyg->yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 160 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 289 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yyg->yy_hold_char;
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 80 "../../src/foreign-pajek-lexer.l"
+{ }
+	YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 81 "../../src/foreign-pajek-lexer.l"
+{ }
+	YY_BREAK
+case 3:
+/* rule 3 can match eol */
+YY_RULE_SETUP
+#line 82 "../../src/foreign-pajek-lexer.l"
+{ }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 83 "../../src/foreign-pajek-lexer.l"
+{ return NETWORKLINE; }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 84 "../../src/foreign-pajek-lexer.l"
+{ return NETWORKLINE; }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 85 "../../src/foreign-pajek-lexer.l"
+{ return VERTICESLINE; }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 86 "../../src/foreign-pajek-lexer.l"
+{ return ARCSLINE; }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 87 "../../src/foreign-pajek-lexer.l"
+{ return EDGESLINE; }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 88 "../../src/foreign-pajek-lexer.l"
+{ return ARCSLISTLINE; }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 89 "../../src/foreign-pajek-lexer.l"
+{ return EDGESLISTLINE; }
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 90 "../../src/foreign-pajek-lexer.l"
+{ return MATRIXLINE; }
+	YY_BREAK
+case 12:
+/* rule 12 can match eol */
+YY_RULE_SETUP
+#line 91 "../../src/foreign-pajek-lexer.l"
+{ yyextra->mode=0; return NEWLINE; }
+	YY_BREAK
+case 13:
+/* rule 13 can match eol */
+YY_RULE_SETUP
+#line 92 "../../src/foreign-pajek-lexer.l"
+{ return QSTR; }
+	YY_BREAK
+case 14:
+/* rule 14 can match eol */
+YY_RULE_SETUP
+#line 93 "../../src/foreign-pajek-lexer.l"
+{ return PSTR; }
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 94 "../../src/foreign-pajek-lexer.l"
+{ 
+                    return NUM; }
+	YY_BREAK
+case 16:
+/* rule 16 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 6;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 97 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_X_FACT; } else { return ALNUM; } }
+	YY_BREAK
+case 17:
+/* rule 17 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 6;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 98 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_Y_FACT; } else { return ALNUM; } }
+	YY_BREAK
+case 18:
+/* rule 18 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 99 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_IC; } else { return ALNUM; } }
+	YY_BREAK
+case 19:
+/* rule 19 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 100 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_BC; } else { return ALNUM; } }
+	YY_BREAK
+case 20:
+/* rule 20 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 101 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_BW; } else { return ALNUM; } }
+	YY_BREAK
+case 21:
+/* rule 21 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 3;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 102 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_PHI; } else { return ALNUM; } }
+	YY_BREAK
+case 22:
+/* rule 22 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 103 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_R; } else { return ALNUM; } }
+	YY_BREAK
+case 23:
+/* rule 23 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 104 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_Q; } else { return ALNUM; } }
+	YY_BREAK
+case 24:
+/* rule 24 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 4;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 105 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_FONT; } else { return ALNUM; } }
+	YY_BREAK
+case 25:
+/* rule 25 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 3;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 106 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_URL; } else { return ALNUM; } }
+	YY_BREAK
+case 26:
+/* rule 26 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 108 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_C; } else { return ALNUM; } }
+	YY_BREAK
+case 27:
+/* rule 27 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 109 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_P; } else { return ALNUM; } }
+	YY_BREAK
+case 28:
+/* rule 28 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 110 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_S; } else { return ALNUM; } }
+	YY_BREAK
+case 29:
+/* rule 29 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 111 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_A; } else { return ALNUM; } }
+	YY_BREAK
+case 30:
+/* rule 30 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 112 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_W; } else { return ALNUM; } }
+	YY_BREAK
+case 31:
+/* rule 31 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 113 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_H1; } else { return ALNUM; } }
+	YY_BREAK
+case 32:
+/* rule 32 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 114 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_H2; } else { return ALNUM; } }
+	YY_BREAK
+case 33:
+/* rule 33 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 115 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_A1; } else { return ALNUM; } }
+	YY_BREAK
+case 34:
+/* rule 34 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 116 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_A2; } else { return ALNUM; } }
+	YY_BREAK
+case 35:
+/* rule 35 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 117 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_K1; } else { return ALNUM; } }
+	YY_BREAK
+case 36:
+/* rule 36 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 118 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_K2; } else { return ALNUM; } }
+	YY_BREAK
+case 37:
+/* rule 37 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 119 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_AP; } else { return ALNUM; } }
+	YY_BREAK
+case 38:
+/* rule 38 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 120 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_L; } else { return ALNUM; } }
+	YY_BREAK
+case 39:
+/* rule 39 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 121 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==2) { return EP_LP; } else { return ALNUM; } }
+	YY_BREAK
+case 40:
+/* rule 40 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 4;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 123 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_LPHI; } else
+                             if (yyextra->mode==2) { return EP_LPHI; } else { return ALNUM; } }
+	YY_BREAK
+case 41:
+/* rule 41 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 125 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_LC; } else
+                             if (yyextra->mode==2) { return EP_LC; } else { return ALNUM; } }
+	YY_BREAK
+case 42:
+/* rule 42 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 127 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_LR; } else
+                             if (yyextra->mode==2) { return EP_LR; } else { return ALNUM; } }
+	YY_BREAK
+case 43:
+/* rule 43 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 2;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 129 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_LA; } else
+                             if (yyextra->mode==2) { return EP_LA; } else { return ALNUM; } }
+	YY_BREAK
+case 44:
+/* rule 44 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 4;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 131 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_SIZE; } else 
+                             if (yyextra->mode==2) { return EP_SIZE; } else { return ALNUM; } }
+	YY_BREAK
+case 45:
+/* rule 45 can match eol */
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */
+yyg->yy_c_buf_p = yy_cp = yy_bp + 3;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 133 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->mode==1) { return VP_FOS; } else 
+                             if (yyextra->mode==2) { return EP_FOS; } else { return ALNUM; } }
+	YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 136 "../../src/foreign-pajek-lexer.l"
+{ return ALNUM; }
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+#line 138 "../../src/foreign-pajek-lexer.l"
+{ if (yyextra->eof) {
+                       yyterminate();
+                    } else {
+                       yyextra->eof=1;
+                       return NEWLINE; 
+                    }
+                  }
+	YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 146 "../../src/foreign-pajek-lexer.l"
+{ return ERROR; }
+	YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 148 "../../src/foreign-pajek-lexer.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+#line 1330 "foreign-pajek-lexer.c"
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yyg->yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * igraph_pajek_yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state( yyscanner );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+			yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yyg->yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yyg->yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer( yyscanner ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yyg->yy_did_buffer_switch_on_eof = 0;
+
+				if ( igraph_pajek_yywrap(yyscanner ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yyg->yy_c_buf_p =
+					yyg->yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yyg->yy_c_buf_p =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of igraph_pajek_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = yyg->yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+	else
+		{
+			yy_size_t num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				yy_size_t new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					igraph_pajek_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			yyg->yy_n_chars, num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	if ( yyg->yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			igraph_pajek_yyrestart(yyin  ,yyscanner);
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) igraph_pajek_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	yyg->yy_n_chars += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_current_state = yyg->yy_start;
+
+	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yyg->yy_last_accepting_state = yy_current_state;
+			yyg->yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 160 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+	register int yy_is_jam;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+	register char *yy_cp = yyg->yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yyg->yy_last_accepting_state = yy_current_state;
+		yyg->yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 160 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 159);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (yyscan_t yyscanner)
+#else
+    static int input  (yyscan_t yyscanner)
+#endif
+
+{
+	int c;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	*yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+	if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			/* This was really a NUL. */
+			*yyg->yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			++yyg->yy_c_buf_p;
+
+			switch ( yy_get_next_buffer( yyscanner ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					igraph_pajek_yyrestart(yyin ,yyscanner);
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( igraph_pajek_yywrap(yyscanner ) )
+						return 0;
+
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput(yyscanner);
+#else
+					return input(yyscanner);
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yyg->yy_c_buf_p;	/* cast for 8-bit char's */
+	*yyg->yy_c_buf_p = '\0';	/* preserve yytext */
+	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void igraph_pajek_yyrestart  (FILE * input_file , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! YY_CURRENT_BUFFER ){
+        igraph_pajek_yyensure_buffer_stack (yyscanner);
+		YY_CURRENT_BUFFER_LVALUE =
+            igraph_pajek_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+	}
+
+	igraph_pajek_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+	igraph_pajek_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_pajek_yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		igraph_pajek_yypop_buffer_state();
+	 *		igraph_pajek_yypush_buffer_state(new_buffer);
+     */
+	igraph_pajek_yyensure_buffer_stack (yyscanner);
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	igraph_pajek_yy_load_buffer_state(yyscanner );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (igraph_pajek_yywrap()) processing, but the only time this flag
+	 * is looked at is after igraph_pajek_yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void igraph_pajek_yy_load_buffer_state  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE igraph_pajek_yy_create_buffer  (FILE * file, int  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) igraph_pajek_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) igraph_pajek_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	igraph_pajek_yy_init_buffer(b,file ,yyscanner);
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with igraph_pajek_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+    void igraph_pajek_yy_delete_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		igraph_pajek_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+	igraph_pajek_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a igraph_pajek_yyrestart() or at EOF.
+ */
+    static void igraph_pajek_yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file , yyscan_t yyscanner)
+
+{
+	int oerrno = errno;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	igraph_pajek_yy_flush_buffer(b ,yyscanner);
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then igraph_pajek_yy_init_buffer was _probably_
+     * called from igraph_pajek_yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+    void igraph_pajek_yy_flush_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		igraph_pajek_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_pajek_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (new_buffer == NULL)
+		return;
+
+	igraph_pajek_yyensure_buffer_stack(yyscanner);
+
+	/* This block is copied from igraph_pajek_yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		yyg->yy_buffer_stack_top++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from igraph_pajek_yy_switch_to_buffer. */
+	igraph_pajek_yy_load_buffer_state(yyscanner );
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  @param yyscanner The scanner object.
+ */
+void igraph_pajek_yypop_buffer_state (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (!YY_CURRENT_BUFFER)
+		return;
+
+	igraph_pajek_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if (yyg->yy_buffer_stack_top > 0)
+		--yyg->yy_buffer_stack_top;
+
+	if (YY_CURRENT_BUFFER) {
+		igraph_pajek_yy_load_buffer_state(yyscanner );
+		yyg->yy_did_buffer_switch_on_eof = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void igraph_pajek_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+	yy_size_t num_to_alloc;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if (!yyg->yy_buffer_stack) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_pajek_yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yyensure_buffer_stack()" );
+								  
+		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		yyg->yy_buffer_stack_max = num_to_alloc;
+		yyg->yy_buffer_stack_top = 0;
+		return;
+	}
+
+	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)igraph_pajek_yyrealloc
+								(yyg->yy_buffer_stack,
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+		yyg->yy_buffer_stack_max = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE igraph_pajek_yy_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) igraph_pajek_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	igraph_pajek_yy_switch_to_buffer(b ,yyscanner );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to igraph_pajek_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       igraph_pajek_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE igraph_pajek_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+    
+	return igraph_pajek_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to igraph_pajek_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE igraph_pajek_yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n, i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) igraph_pajek_yyalloc(n ,yyscanner );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in igraph_pajek_yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = igraph_pajek_yy_scan_buffer(buf,n ,yyscanner);
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in igraph_pajek_yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = yyg->yy_hold_char; \
+		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+		yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+		*yyg->yy_c_buf_p = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE igraph_pajek_yyget_extra  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_pajek_yyget_lineno  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int igraph_pajek_yyget_column  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_pajek_yyget_in  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *igraph_pajek_yyget_out  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+yy_size_t igraph_pajek_yyget_leng  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *igraph_pajek_yyget_text  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void igraph_pajek_yyset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_pajek_yyset_lineno (int  line_number , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* lineno is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_pajek_yyset_lineno called with no buffer" , yyscanner); 
+    
+    yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void igraph_pajek_yyset_column (int  column_no , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* column is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "igraph_pajek_yyset_column called with no buffer" , yyscanner); 
+    
+    yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see igraph_pajek_yy_switch_to_buffer
+ */
+void igraph_pajek_yyset_in (FILE *  in_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyin = in_str ;
+}
+
+void igraph_pajek_yyset_out (FILE *  out_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyout = out_str ;
+}
+
+int igraph_pajek_yyget_debug  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yy_flex_debug;
+}
+
+void igraph_pajek_yyset_debug (int  bdebug , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * igraph_pajek_yyget_lval  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylval;
+}
+
+void igraph_pajek_yyset_lval (YYSTYPE *  yylval_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylval = yylval_param;
+}
+
+YYLTYPE *igraph_pajek_yyget_lloc  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylloc;
+}
+    
+void igraph_pajek_yyset_lloc (YYLTYPE *  yylloc_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylloc = yylloc_param;
+}
+    
+/* User-visible API */
+
+/* igraph_pajek_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int igraph_pajek_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) igraph_pajek_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* igraph_pajek_yylex_init_extra has the same functionality as igraph_pajek_yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to igraph_pajek_yyalloc in
+ * the yyextra field.
+ */
+
+int igraph_pajek_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+    struct yyguts_t dummy_yyguts;
+
+    igraph_pajek_yyset_extra (yy_user_defined, &dummy_yyguts);
+
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+	
+    *ptr_yy_globals = (yyscan_t) igraph_pajek_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+	
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+    
+    /* By setting to 0xAA, we expose bugs in
+    yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+    
+    igraph_pajek_yyset_extra (yy_user_defined, *ptr_yy_globals);
+    
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from igraph_pajek_yylex_destroy(), so don't allocate here.
+     */
+
+    yyg->yy_buffer_stack = 0;
+    yyg->yy_buffer_stack_top = 0;
+    yyg->yy_buffer_stack_max = 0;
+    yyg->yy_c_buf_p = (char *) 0;
+    yyg->yy_init = 0;
+    yyg->yy_start = 0;
+
+    yyg->yy_start_stack_ptr = 0;
+    yyg->yy_start_stack_depth = 0;
+    yyg->yy_start_stack =  NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * igraph_pajek_yylex_init()
+     */
+    return 0;
+}
+
+/* igraph_pajek_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int igraph_pajek_yylex_destroy  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		igraph_pajek_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		igraph_pajek_yypop_buffer_state(yyscanner);
+	}
+
+	/* Destroy the stack itself. */
+	igraph_pajek_yyfree(yyg->yy_buffer_stack ,yyscanner);
+	yyg->yy_buffer_stack = NULL;
+
+    /* Destroy the start condition stack. */
+        igraph_pajek_yyfree(yyg->yy_start_stack ,yyscanner );
+        yyg->yy_start_stack = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * igraph_pajek_yylex() is called, initialization will occur. */
+    yy_init_globals( yyscanner);
+
+    /* Destroy the main struct (reentrant only). */
+    igraph_pajek_yyfree ( yyscanner , yyscanner );
+    yyscanner = NULL;
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *igraph_pajek_yyalloc (yy_size_t  size , yyscan_t yyscanner)
+{
+	return (void *) malloc( size );
+}
+
+void *igraph_pajek_yyrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void igraph_pajek_yyfree (void * ptr , yyscan_t yyscanner)
+{
+	free( (char *) ptr );	/* see igraph_pajek_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 148 "../../src/foreign-pajek-lexer.l"
+
+
+
diff --git a/igraph/src/foreign-pajek-parser.c b/igraph/src/foreign-pajek-parser.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign-pajek-parser.c
@@ -0,0 +1,2815 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names.  */
+#define yyparse igraph_pajek_yyparse
+#define yylex   igraph_pajek_yylex
+#define yyerror igraph_pajek_yyerror
+#define yylval  igraph_pajek_yylval
+#define yychar  igraph_pajek_yychar
+#define yydebug igraph_pajek_yydebug
+#define yynerrs igraph_pajek_yynerrs
+#define yylloc igraph_pajek_yylloc
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     NEWLINE = 258,
+     NUM = 259,
+     ALNUM = 260,
+     QSTR = 261,
+     PSTR = 262,
+     NETWORKLINE = 263,
+     VERTICESLINE = 264,
+     ARCSLINE = 265,
+     EDGESLINE = 266,
+     ARCSLISTLINE = 267,
+     EDGESLISTLINE = 268,
+     MATRIXLINE = 269,
+     ERROR = 270,
+     VP_X_FACT = 271,
+     VP_Y_FACT = 272,
+     VP_IC = 273,
+     VP_BC = 274,
+     VP_LC = 275,
+     VP_LR = 276,
+     VP_LPHI = 277,
+     VP_BW = 278,
+     VP_FOS = 279,
+     VP_PHI = 280,
+     VP_R = 281,
+     VP_Q = 282,
+     VP_LA = 283,
+     VP_FONT = 284,
+     VP_URL = 285,
+     VP_SIZE = 286,
+     EP_C = 287,
+     EP_S = 288,
+     EP_A = 289,
+     EP_W = 290,
+     EP_H1 = 291,
+     EP_H2 = 292,
+     EP_A1 = 293,
+     EP_A2 = 294,
+     EP_K1 = 295,
+     EP_K2 = 296,
+     EP_AP = 297,
+     EP_P = 298,
+     EP_L = 299,
+     EP_LP = 300,
+     EP_LR = 301,
+     EP_LPHI = 302,
+     EP_LC = 303,
+     EP_LA = 304,
+     EP_SIZE = 305,
+     EP_FOS = 306
+   };
+#endif
+/* Tokens.  */
+#define NEWLINE 258
+#define NUM 259
+#define ALNUM 260
+#define QSTR 261
+#define PSTR 262
+#define NETWORKLINE 263
+#define VERTICESLINE 264
+#define ARCSLINE 265
+#define EDGESLINE 266
+#define ARCSLISTLINE 267
+#define EDGESLISTLINE 268
+#define MATRIXLINE 269
+#define ERROR 270
+#define VP_X_FACT 271
+#define VP_Y_FACT 272
+#define VP_IC 273
+#define VP_BC 274
+#define VP_LC 275
+#define VP_LR 276
+#define VP_LPHI 277
+#define VP_BW 278
+#define VP_FOS 279
+#define VP_PHI 280
+#define VP_R 281
+#define VP_Q 282
+#define VP_LA 283
+#define VP_FONT 284
+#define VP_URL 285
+#define VP_SIZE 286
+#define EP_C 287
+#define EP_S 288
+#define EP_A 289
+#define EP_W 290
+#define EP_H1 291
+#define EP_H2 292
+#define EP_A1 293
+#define EP_A2 294
+#define EP_K1 295
+#define EP_K2 296
+#define EP_AP 297
+#define EP_P 298
+#define EP_L 299
+#define EP_LP 300
+#define EP_LR 301
+#define EP_LPHI 302
+#define EP_LC 303
+#define EP_LA 304
+#define EP_SIZE 305
+#define EP_FOS 306
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 23 "../../src/foreign-pajek-parser.y"
+
+
+/* 
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA 
+   02110-1301 USA
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "igraph_hacks_internal.h"
+#include "igraph_types.h"
+#include "igraph_types_internal.h"
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "igraph_attributes.h"
+#include "config.h"
+#include "igraph_math.h"
+#include <math.h>
+#include "foreign-pajek-header.h"
+#include "foreign-pajek-parser.h"
+
+#define yyscan_t void*
+
+int igraph_pajek_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, 
+		       void* scanner);
+int igraph_pajek_yyerror(YYLTYPE* locp, 
+			 igraph_i_pajek_parsedata_t *context, 
+			 const char *s);
+char *igraph_pajek_yyget_text (yyscan_t yyscanner );
+int igraph_pajek_yyget_leng (yyscan_t yyscanner );
+
+int igraph_i_pajek_add_string_vertex_attribute(const char *name, 
+					       const char *value,
+					       int len, 
+					       igraph_i_pajek_parsedata_t *context);
+int igraph_i_pajek_add_string_edge_attribute(const char *name, 
+					     const char *value,
+					     int len,
+					     igraph_i_pajek_parsedata_t *context);
+int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, 
+						igraph_real_t value,
+						igraph_i_pajek_parsedata_t *context);
+int igraph_i_pajek_add_numeric_edge_attribute(const char *name, 
+					      igraph_real_t value, 
+					      igraph_i_pajek_parsedata_t *context);
+int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names,
+					 igraph_vector_ptr_t *attrs,
+					 long int count,
+					 const char *attrname,
+					 igraph_integer_t vid,
+					 igraph_real_t number);
+int igraph_i_pajek_add_string_attribute(igraph_trie_t *names,
+					igraph_vector_ptr_t *attrs,
+					long int count,
+					const char *attrname,
+					igraph_integer_t vid,
+					const char *str);
+
+int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context);
+int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context);
+
+extern igraph_real_t igraph_pajek_get_number(const char *str, long int len);
+extern long int igraph_i_pajek_actvertex;
+extern long int igraph_i_pajek_actedge;
+
+#define scanner context->scanner
+
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 118 "../../src/foreign-pajek-parser.y"
+{
+  long int intnum;
+  double   realnum;  
+  struct {
+    char *str;
+    int len;
+  } string;  
+}
+/* Line 193 of yacc.c.  */
+#line 301 "foreign-pajek-parser.c"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 216 of yacc.c.  */
+#line 326 "foreign-pajek-parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+    int i;
+#endif
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+	     && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+    YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+      + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  5
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   250
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  52
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  66
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  137
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  207
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   306
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49,    50,    51
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint16 yyprhs[] =
+{
+       0,     0,     3,     7,     8,    12,    16,    19,    23,    24,
+      27,    29,    32,    33,    41,    43,    45,    46,    49,    53,
+      54,    56,    57,    60,    62,    65,    68,    73,    78,    83,
+      86,    89,    92,    95,    98,   101,   104,   107,   110,   111,
+     115,   116,   120,   121,   125,   126,   130,   131,   135,   137,
+     138,   141,   144,   147,   150,   153,   157,   162,   163,   166,
+     168,   169,   176,   178,   180,   184,   189,   190,   193,   195,
+     196,   203,   205,   207,   208,   210,   211,   214,   216,   221,
+     224,   227,   230,   233,   236,   239,   242,   245,   248,   251,
+     254,   257,   260,   263,   266,   267,   271,   272,   276,   277,
+     281,   282,   286,   287,   291,   293,   297,   298,   301,   303,
+     307,   308,   311,   313,   315,   319,   320,   323,   325,   329,
+     330,   333,   335,   337,   341,   343,   344,   347,   350,   351,
+     354,   356,   358,   360,   361,   364,   366,   368
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+      53,     0,    -1,    54,    55,    73,    -1,    -1,     8,   116,
+       3,    -1,    56,     3,    57,    -1,     9,   114,    -1,     9,
+     114,   114,    -1,    -1,    57,    58,    -1,     3,    -1,    60,
+       3,    -1,    -1,    60,    59,    61,    62,    63,    64,     3,
+      -1,   114,    -1,   117,    -1,    -1,   115,   115,    -1,   115,
+     115,   115,    -1,    -1,   117,    -1,    -1,    64,    65,    -1,
+      66,    -1,    16,   115,    -1,    17,   115,    -1,    18,   115,
+     115,   115,    -1,    19,   115,   115,   115,    -1,    20,   115,
+     115,   115,    -1,    21,   115,    -1,    22,   115,    -1,    23,
+     115,    -1,    24,   115,    -1,    25,   115,    -1,    26,   115,
+      -1,    27,   115,    -1,    28,   115,    -1,    31,   115,    -1,
+      -1,    29,    67,    72,    -1,    -1,    30,    68,    72,    -1,
+      -1,    18,    69,    72,    -1,    -1,    19,    70,    72,    -1,
+      -1,    20,    71,    72,    -1,   117,    -1,    -1,    73,    74,
+      -1,    73,    80,    -1,    73,    96,    -1,    73,   102,    -1,
+      73,   108,    -1,    10,     3,    75,    -1,    10,   115,     3,
+      75,    -1,    -1,    75,    76,    -1,     3,    -1,    -1,    78,
+      79,    77,    86,    87,     3,    -1,   114,    -1,   114,    -1,
+      11,     3,    81,    -1,    11,   115,     3,    81,    -1,    -1,
+      81,    82,    -1,     3,    -1,    -1,    84,    85,    83,    86,
+      87,     3,    -1,   114,    -1,   114,    -1,    -1,   115,    -1,
+      -1,    87,    88,    -1,    89,    -1,    32,   115,   115,   115,
+      -1,    33,   115,    -1,    35,   115,    -1,    36,   115,    -1,
+      37,   115,    -1,    38,   115,    -1,    39,   115,    -1,    40,
+     115,    -1,    41,   115,    -1,    42,   115,    -1,    45,   115,
+      -1,    46,   115,    -1,    47,   115,    -1,    49,   115,    -1,
+      50,   115,    -1,    51,   115,    -1,    -1,    34,    90,    95,
+      -1,    -1,    43,    91,    95,    -1,    -1,    44,    92,    95,
+      -1,    -1,    48,    93,    95,    -1,    -1,    32,    94,    95,
+      -1,   117,    -1,    12,     3,    97,    -1,    -1,    97,    98,
+      -1,     3,    -1,   100,    99,     3,    -1,    -1,    99,   101,
+      -1,   114,    -1,   114,    -1,    13,     3,   103,    -1,    -1,
+     103,   104,    -1,     3,    -1,   106,   105,     3,    -1,    -1,
+     105,   107,    -1,   114,    -1,   114,    -1,   109,     3,   110,
+      -1,    14,    -1,    -1,   110,   111,    -1,   112,     3,    -1,
+      -1,   113,   112,    -1,   115,    -1,     4,    -1,     4,    -1,
+      -1,   116,   117,    -1,     5,    -1,     4,    -1,     6,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   192,   192,   196,   196,   198,   200,   204,   210,   210,
+     212,   213,   214,   214,   217,   219,   224,   225,   229,   235,
+     235,   239,   239,   242,   243,   246,   249,   254,   259,   264,
+     267,   270,   273,   276,   279,   282,   285,   288,   293,   293,
+     297,   297,   301,   301,   305,   305,   310,   310,   317,   319,
+     319,   319,   319,   319,   319,   321,   322,   324,   324,   326,
+     327,   327,   333,   335,   337,   338,   340,   340,   342,   343,
+     343,   349,   351,   353,   353,   357,   357,   360,   361,   366,
+     369,   372,   375,   378,   381,   384,   387,   390,   393,   396,
+     399,   402,   405,   408,   413,   413,   417,   417,   421,   421,
+     425,   425,   429,   429,   435,   437,   439,   439,   441,   441,
+     443,   443,   445,   447,   452,   454,   454,   456,   456,   458,
+     458,   460,   462,   469,   471,   476,   476,   478,   480,   480,
+     482,   502,   505,   508,   508,   510,   512,   514
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "NEWLINE", "NUM", "ALNUM", "QSTR",
+  "PSTR", "NETWORKLINE", "VERTICESLINE", "ARCSLINE", "EDGESLINE",
+  "ARCSLISTLINE", "EDGESLISTLINE", "MATRIXLINE", "ERROR", "VP_X_FACT",
+  "VP_Y_FACT", "VP_IC", "VP_BC", "VP_LC", "VP_LR", "VP_LPHI", "VP_BW",
+  "VP_FOS", "VP_PHI", "VP_R", "VP_Q", "VP_LA", "VP_FONT", "VP_URL",
+  "VP_SIZE", "EP_C", "EP_S", "EP_A", "EP_W", "EP_H1", "EP_H2", "EP_A1",
+  "EP_A2", "EP_K1", "EP_K2", "EP_AP", "EP_P", "EP_L", "EP_LP", "EP_LR",
+  "EP_LPHI", "EP_LC", "EP_LA", "EP_SIZE", "EP_FOS", "$accept", "input",
+  "nethead", "vertices", "verticeshead", "vertdefs", "vertexline", "@1",
+  "vertex", "vertexid", "vertexcoords", "shape", "params", "param",
+  "vpword", "@2", "@3", "@4", "@5", "@6", "vpwordpar", "edgeblock", "arcs",
+  "arcsdefs", "arcsline", "@7", "arcfrom", "arcto", "edges", "edgesdefs",
+  "edgesline", "@8", "edgefrom", "edgeto", "weight", "edgeparams",
+  "edgeparam", "epword", "@9", "@10", "@11", "@12", "@13", "epwordpar",
+  "arcslist", "arcslistlines", "arclistline", "arctolist", "arclistfrom",
+  "arclistto", "edgeslist", "edgelistlines", "edgelistline", "edgetolist",
+  "edgelistfrom", "edgelistto", "adjmatrix", "matrixline",
+  "adjmatrixlines", "adjmatrixline", "adjmatrixnumbers", "adjmatrixentry",
+  "longint", "number", "words", "word", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
+     295,   296,   297,   298,   299,   300,   301,   302,   303,   304,
+     305,   306
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    52,    53,    54,    54,    55,    56,    56,    57,    57,
+      58,    58,    59,    58,    60,    61,    62,    62,    62,    63,
+      63,    64,    64,    65,    65,    65,    65,    65,    65,    65,
+      65,    65,    65,    65,    65,    65,    65,    65,    67,    66,
+      68,    66,    69,    66,    70,    66,    71,    66,    72,    73,
+      73,    73,    73,    73,    73,    74,    74,    75,    75,    76,
+      77,    76,    78,    79,    80,    80,    81,    81,    82,    83,
+      82,    84,    85,    86,    86,    87,    87,    88,    88,    88,
+      88,    88,    88,    88,    88,    88,    88,    88,    88,    88,
+      88,    88,    88,    88,    90,    89,    91,    89,    92,    89,
+      93,    89,    94,    89,    95,    96,    97,    97,    98,    98,
+      99,    99,   100,   101,   102,   103,   103,   104,   104,   105,
+     105,   106,   107,   108,   109,   110,   110,   111,   112,   112,
+     113,   114,   115,   116,   116,   117,   117,   117
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     3,     0,     3,     3,     2,     3,     0,     2,
+       1,     2,     0,     7,     1,     1,     0,     2,     3,     0,
+       1,     0,     2,     1,     2,     2,     4,     4,     4,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     0,     3,
+       0,     3,     0,     3,     0,     3,     0,     3,     1,     0,
+       2,     2,     2,     2,     2,     3,     4,     0,     2,     1,
+       0,     6,     1,     1,     3,     4,     0,     2,     1,     0,
+       6,     1,     1,     0,     1,     0,     2,     1,     4,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     0,     3,     0,     3,     0,     3,
+       0,     3,     0,     3,     1,     3,     0,     2,     1,     3,
+       0,     2,     1,     1,     3,     0,     2,     1,     3,     0,
+       2,     1,     1,     3,     1,     0,     2,     2,     0,     2,
+       1,     1,     1,     0,     2,     1,     1,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       3,   133,     0,     0,     0,     1,     0,    49,     0,     4,
+     136,   135,   137,   134,   131,     6,     2,     8,     7,     0,
+       0,     0,     0,   124,    50,    51,    52,    53,    54,     0,
+       5,    57,   132,     0,    66,     0,   106,   115,   125,    10,
+       9,    12,    14,    55,    57,    64,    66,   105,   114,   123,
+      11,     0,    59,    58,     0,    62,    56,    68,    67,     0,
+      71,    65,   108,   107,   110,   112,   117,   116,   119,   121,
+     126,     0,   128,   130,    16,    15,    60,    63,    69,    72,
+       0,     0,   127,   129,    19,     0,    73,    73,   109,   111,
+     113,   118,   120,   122,    21,    20,    17,    75,    74,    75,
+       0,    18,     0,     0,    13,     0,     0,    42,    44,    46,
+       0,     0,     0,     0,     0,     0,     0,     0,    38,    40,
+       0,    22,    23,    61,   102,     0,    94,     0,     0,     0,
+       0,     0,     0,     0,     0,    96,    98,     0,     0,     0,
+     100,     0,     0,     0,    76,    77,    70,    24,    25,     0,
+       0,     0,     0,     0,     0,    29,    30,    31,    32,    33,
+      34,    35,    36,     0,     0,    37,     0,     0,    79,     0,
+      80,    81,    82,    83,    84,    85,    86,    87,     0,     0,
+      88,    89,    90,     0,    91,    92,    93,    43,    48,     0,
+      45,     0,    47,     0,    39,    41,   103,   104,     0,    95,
+      97,    99,   101,    26,    27,    28,    78
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int16 yydefgoto[] =
+{
+      -1,     2,     3,     7,     8,    30,    40,    51,    41,    74,
+      84,    94,   100,   121,   122,   163,   164,   149,   151,   153,
+     187,    16,    24,    43,    53,    86,    54,    76,    25,    45,
+      58,    87,    59,    78,    97,   102,   144,   145,   169,   178,
+     179,   183,   166,   196,    26,    47,    63,    80,    64,    89,
+      27,    48,    67,    81,    68,    92,    28,    29,    49,    70,
+      71,    72,    55,    73,     4,   188
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -167
+static const yytype_int16 yypact[] =
+{
+      -4,  -167,     7,    36,    22,  -167,    44,  -167,    49,  -167,
+    -167,  -167,  -167,  -167,  -167,    44,    10,  -167,  -167,    12,
+      27,    51,    56,  -167,  -167,  -167,  -167,  -167,  -167,    58,
+      29,  -167,  -167,    59,  -167,    60,  -167,  -167,  -167,  -167,
+    -167,    61,  -167,    31,  -167,    33,  -167,    35,    37,    39,
+    -167,     5,  -167,  -167,    44,  -167,    31,  -167,  -167,    44,
+    -167,    33,  -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,
+    -167,    62,    65,  -167,    65,  -167,  -167,  -167,  -167,  -167,
+      47,    53,  -167,  -167,     5,    65,    65,    65,  -167,  -167,
+    -167,  -167,  -167,  -167,  -167,  -167,    65,  -167,  -167,  -167,
+     219,  -167,   150,   170,  -167,    65,    65,    65,    65,    65,
+      65,    65,    65,    65,    65,    65,    65,    65,  -167,  -167,
+      65,  -167,  -167,  -167,    65,    65,  -167,    65,    65,    65,
+      65,    65,    65,    65,    65,  -167,  -167,    65,    65,    65,
+    -167,    65,    65,    65,  -167,  -167,  -167,  -167,  -167,     5,
+      65,     5,    65,     5,    65,  -167,  -167,  -167,  -167,  -167,
+    -167,  -167,  -167,     5,     5,  -167,     5,    65,  -167,     5,
+    -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,     5,     5,
+    -167,  -167,  -167,     5,  -167,  -167,  -167,  -167,  -167,    65,
+    -167,    65,  -167,    65,  -167,  -167,  -167,  -167,    65,  -167,
+    -167,  -167,  -167,  -167,  -167,  -167,  -167
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int16 yypgoto[] =
+{
+    -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,
+    -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,
+    -145,  -167,  -167,    26,  -167,  -167,  -167,  -167,  -167,    25,
+    -167,  -167,  -167,  -167,   -15,   -26,  -167,  -167,  -167,  -167,
+    -167,  -167,  -167,  -166,  -167,  -167,  -167,  -167,  -167,  -167,
+    -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,  -167,
+       2,  -167,    -1,   -19,  -167,    -2
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -129
+static const yytype_int16 yytable[] =
+{
+      33,    35,    13,   199,     1,    15,   190,     5,   192,    10,
+      11,    12,   200,   201,    18,    31,    32,   202,   194,   195,
+      19,    20,    21,    22,    23,     9,    10,    11,    12,    42,
+      34,    32,    39,    14,    52,    14,    57,    14,    62,    14,
+      66,    14,  -128,    32,    60,     6,    65,    69,    14,    75,
+      88,    14,    17,    77,    36,    85,    91,    14,    79,    37,
+      60,    38,    44,    46,    50,    82,    96,    98,    98,    32,
+      56,    61,    99,   103,    83,     0,     0,   101,     0,    90,
+      93,     0,    95,     0,     0,     0,   147,   148,   150,   152,
+     154,   155,   156,   157,   158,   159,   160,   161,   162,     0,
+       0,   165,     0,     0,     0,   167,   168,     0,   170,   171,
+     172,   173,   174,   175,   176,   177,     0,     0,   180,   181,
+     182,     0,   184,   185,   186,     0,     0,     0,     0,     0,
+       0,   189,     0,   191,     0,   193,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,   198,     0,
+       0,     0,     0,   123,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,   197,     0,     0,   197,     0,     0,
+     203,     0,   204,   146,   205,     0,   197,   197,     0,   206,
+       0,   197,   124,   125,   126,   127,   128,   129,   130,   131,
+     132,   133,   134,   135,   136,   137,   138,   139,   140,   141,
+     142,   143,   124,   125,   126,   127,   128,   129,   130,   131,
+     132,   133,   134,   135,   136,   137,   138,   139,   140,   141,
+     142,   143,   104,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,   105,   106,   107,   108,   109,
+     110,   111,   112,   113,   114,   115,   116,   117,   118,   119,
+     120
+};
+
+static const yytype_int16 yycheck[] =
+{
+      19,    20,     4,   169,     8,     6,   151,     0,   153,     4,
+       5,     6,   178,   179,    15,     3,     4,   183,   163,   164,
+      10,    11,    12,    13,    14,     3,     4,     5,     6,    30,
+       3,     4,     3,     4,     3,     4,     3,     4,     3,     4,
+       3,     4,     3,     4,    45,     9,    47,    48,     4,    51,
+       3,     4,     3,    54,     3,    74,     3,     4,    59,     3,
+      61,     3,     3,     3,     3,     3,    85,    86,    87,     4,
+      44,    46,    87,    99,    72,    -1,    -1,    96,    -1,    80,
+      81,    -1,    84,    -1,    -1,    -1,   105,   106,   107,   108,
+     109,   110,   111,   112,   113,   114,   115,   116,   117,    -1,
+      -1,   120,    -1,    -1,    -1,   124,   125,    -1,   127,   128,
+     129,   130,   131,   132,   133,   134,    -1,    -1,   137,   138,
+     139,    -1,   141,   142,   143,    -1,    -1,    -1,    -1,    -1,
+      -1,   150,    -1,   152,    -1,   154,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   167,    -1,
+      -1,    -1,    -1,     3,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,   166,    -1,    -1,   169,    -1,    -1,
+     189,    -1,   191,     3,   193,    -1,   178,   179,    -1,   198,
+      -1,   183,    32,    33,    34,    35,    36,    37,    38,    39,
+      40,    41,    42,    43,    44,    45,    46,    47,    48,    49,
+      50,    51,    32,    33,    34,    35,    36,    37,    38,    39,
+      40,    41,    42,    43,    44,    45,    46,    47,    48,    49,
+      50,    51,     3,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    16,    17,    18,    19,    20,
+      21,    22,    23,    24,    25,    26,    27,    28,    29,    30,
+      31
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     8,    53,    54,   116,     0,     9,    55,    56,     3,
+       4,     5,     6,   117,     4,   114,    73,     3,   114,    10,
+      11,    12,    13,    14,    74,    80,    96,   102,   108,   109,
+      57,     3,     4,   115,     3,   115,     3,     3,     3,     3,
+      58,    60,   114,    75,     3,    81,     3,    97,   103,   110,
+       3,    59,     3,    76,    78,   114,    75,     3,    82,    84,
+     114,    81,     3,    98,   100,   114,     3,   104,   106,   114,
+     111,   112,   113,   115,    61,   117,    79,   114,    85,   114,
+      99,   105,     3,   112,    62,   115,    77,    83,     3,   101,
+     114,     3,   107,   114,    63,   117,   115,    86,   115,    86,
+      64,   115,    87,    87,     3,    16,    17,    18,    19,    20,
+      21,    22,    23,    24,    25,    26,    27,    28,    29,    30,
+      31,    65,    66,     3,    32,    33,    34,    35,    36,    37,
+      38,    39,    40,    41,    42,    43,    44,    45,    46,    47,
+      48,    49,    50,    51,    88,    89,     3,   115,   115,    69,
+     115,    70,   115,    71,   115,   115,   115,   115,   115,   115,
+     115,   115,   115,    67,    68,   115,    94,   115,   115,    90,
+     115,   115,   115,   115,   115,   115,   115,   115,    91,    92,
+     115,   115,   115,    93,   115,   115,   115,    72,   117,   115,
+      72,   115,    72,   115,    72,    72,    95,   117,   115,    95,
+      95,    95,    95,   115,   115,   115,   115
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+	      (Loc).first_line, (Loc).first_column,	\
+	      (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc, scanner)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value, Location, context); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_pajek_parsedata_t* context;
+#endif
+{
+  if (!yyvaluep)
+    return;
+  YYUSE (yylocationp);
+  YYUSE (context);
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    igraph_i_pajek_parsedata_t* context;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  YY_LOCATION_PRINT (yyoutput, *yylocationp);
+  YYFPRINTF (yyoutput, ": ");
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    yytype_int16 *bottom;
+    yytype_int16 *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_pajek_parsedata_t* context)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule, context)
+    YYSTYPE *yyvsp;
+    YYLTYPE *yylsp;
+    int yyrule;
+    igraph_i_pajek_parsedata_t* context;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       , &(yylsp[(yyi + 1) - (yynrhs)])		       , context);
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, yylsp, Rule, context); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+	 constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+		    + sizeof yyexpecting - 1
+		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+		       * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+	 YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	  {
+	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+	      {
+		yycount = 1;
+		yysize = yysize0;
+		yyformat[sizeof yyunexpected - 1] = '\0';
+		break;
+	      }
+	    yyarg[yycount++] = yytname[yyx];
+	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+	    yysize_overflow |= (yysize1 < yysize);
+	    yysize = yysize1;
+	    yyfmt = yystpcpy (yyfmt, yyprefix);
+	    yyprefix = yyor;
+	  }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+	return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+	{
+	  /* Avoid sprintf, as that infringes on the user's name space.
+	     Don't have undefined behavior even if the translation
+	     produced a string with the wrong number of "%s"s.  */
+	  char *yyp = yyresult;
+	  int yyi = 0;
+	  while ((*yyp = *yyf) != '\0')
+	    {
+	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+		{
+		  yyp += yytnamerr (yyp, yyarg[yyi++]);
+		  yyf += 2;
+		}
+	      else
+		{
+		  yyp++;
+		  yyf++;
+		}
+	    }
+	}
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_pajek_parsedata_t* context)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp, context)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+    YYLTYPE *yylocationp;
+    igraph_i_pajek_parsedata_t* context;
+#endif
+{
+  YYUSE (yyvaluep);
+  YYUSE (yylocationp);
+  YYUSE (context);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (igraph_i_pajek_parsedata_t* context);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (igraph_i_pajek_parsedata_t* context)
+#else
+int
+yyparse (context)
+    igraph_i_pajek_parsedata_t* context;
+#endif
+#endif
+{
+  /* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+/* Location data for the look-ahead symbol.  */
+YYLTYPE yylloc;
+
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+  /* The location stack.  */
+  YYLTYPE yylsa[YYINITDEPTH];
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+  /* The locations where the error started and ended.  */
+  YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+  YYLTYPE yyloc;
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+  yylsp = yyls;
+#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+  /* Initialize the default location before parsing starts.  */
+  yylloc.first_line   = yylloc.last_line   = 1;
+  yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+	YYLTYPE *yyls1 = yyls;
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yyls1, yysize * sizeof (*yylsp),
+		    &yystacksize);
+	yyls = yyls1;
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+	YYSTACK_RELOCATE (yyls);
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+      yylsp = yyls + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+  *++yylsp = yylloc;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+  /* Default location.  */
+  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 192 "../../src/foreign-pajek-parser.y"
+    {
+  if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); }
+ }
+    break;
+
+  case 6:
+#line 200 "../../src/foreign-pajek-parser.y"
+    { 
+  context->vcount=(yyvsp[(2) - (2)].intnum); 
+  context->vcount2=0;
+            }
+    break;
+
+  case 7:
+#line 204 "../../src/foreign-pajek-parser.y"
+    { 
+  context->vcount=(yyvsp[(2) - (3)].intnum);
+  context->vcount2=(yyvsp[(3) - (3)].intnum);
+  igraph_i_pajek_add_bipartite_type(context);
+}
+    break;
+
+  case 12:
+#line 214 "../../src/foreign-pajek-parser.y"
+    { context->actvertex=(yyvsp[(1) - (1)].intnum); }
+    break;
+
+  case 13:
+#line 214 "../../src/foreign-pajek-parser.y"
+    { }
+    break;
+
+  case 14:
+#line 217 "../../src/foreign-pajek-parser.y"
+    { (yyval.intnum)=(yyvsp[(1) - (1)].intnum); context->mode=1; }
+    break;
+
+  case 15:
+#line 219 "../../src/foreign-pajek-parser.y"
+    {
+  igraph_i_pajek_add_string_vertex_attribute("id", (yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, context);
+  igraph_i_pajek_add_string_vertex_attribute("name", (yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, context);
+}
+    break;
+
+  case 17:
+#line 225 "../../src/foreign-pajek-parser.y"
+    { 
+  igraph_i_pajek_add_numeric_vertex_attribute("x", (yyvsp[(1) - (2)].realnum), context);
+  igraph_i_pajek_add_numeric_vertex_attribute("y", (yyvsp[(2) - (2)].realnum), context);
+	    }
+    break;
+
+  case 18:
+#line 229 "../../src/foreign-pajek-parser.y"
+    { 
+  igraph_i_pajek_add_numeric_vertex_attribute("x", (yyvsp[(1) - (3)].realnum), context);
+  igraph_i_pajek_add_numeric_vertex_attribute("y", (yyvsp[(2) - (3)].realnum), context);
+  igraph_i_pajek_add_numeric_vertex_attribute("z", (yyvsp[(3) - (3)].realnum), context);
+	    }
+    break;
+
+  case 20:
+#line 235 "../../src/foreign-pajek-parser.y"
+    { 
+  igraph_i_pajek_add_string_vertex_attribute("shape", (yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, context);
+}
+    break;
+
+  case 24:
+#line 243 "../../src/foreign-pajek-parser.y"
+    {
+	 igraph_i_pajek_add_numeric_vertex_attribute("xfact", (yyvsp[(2) - (2)].realnum), context);
+       }
+    break;
+
+  case 25:
+#line 246 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("yfact", (yyvsp[(2) - (2)].realnum), context);
+       }
+    break;
+
+  case 26:
+#line 249 "../../src/foreign-pajek-parser.y"
+    { /* RGB color */
+         igraph_i_pajek_add_numeric_vertex_attribute("color-red", (yyvsp[(2) - (4)].realnum), context);
+	 igraph_i_pajek_add_numeric_vertex_attribute("color-green", (yyvsp[(3) - (4)].realnum), context);
+	 igraph_i_pajek_add_numeric_vertex_attribute("color-blue", (yyvsp[(4) - (4)].realnum), context);
+       }
+    break;
+
+  case 27:
+#line 254 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", (yyvsp[(2) - (4)].realnum), context);
+	 igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", (yyvsp[(3) - (4)].realnum), context);
+	 igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", (yyvsp[(4) - (4)].realnum), context);
+       }
+    break;
+
+  case 28:
+#line 259 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", (yyvsp[(2) - (4)].realnum), context);
+	 igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", (yyvsp[(3) - (4)].realnum), context);
+	 igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", (yyvsp[(4) - (4)].realnum), context);
+       }
+    break;
+
+  case 29:
+#line 264 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("labeldist", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 30:
+#line 267 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 31:
+#line 270 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("framewidth", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 32:
+#line 273 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("fontsize", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 33:
+#line 276 "../../src/foreign-pajek-parser.y"
+    {       
+         igraph_i_pajek_add_numeric_vertex_attribute("rotation", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 34:
+#line 279 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("radius", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 35:
+#line 282 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 36:
+#line 285 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 37:
+#line 288 "../../src/foreign-pajek-parser.y"
+    {
+         igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", (yyvsp[(2) - (2)].realnum), context);
+     }
+    break;
+
+  case 38:
+#line 293 "../../src/foreign-pajek-parser.y"
+    { context->mode=3; }
+    break;
+
+  case 39:
+#line 293 "../../src/foreign-pajek-parser.y"
+    { 
+         context->mode=1;
+	 igraph_i_pajek_add_string_vertex_attribute("font", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+     }
+    break;
+
+  case 40:
+#line 297 "../../src/foreign-pajek-parser.y"
+    { context->mode=3; }
+    break;
+
+  case 41:
+#line 297 "../../src/foreign-pajek-parser.y"
+    {
+         context->mode=1;
+	 igraph_i_pajek_add_string_vertex_attribute("url", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+     }
+    break;
+
+  case 42:
+#line 301 "../../src/foreign-pajek-parser.y"
+    { context->mode=3; }
+    break;
+
+  case 43:
+#line 301 "../../src/foreign-pajek-parser.y"
+    {
+         context->mode=1;
+	 igraph_i_pajek_add_string_vertex_attribute("color", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+     }
+    break;
+
+  case 44:
+#line 305 "../../src/foreign-pajek-parser.y"
+    { context->mode=3; }
+    break;
+
+  case 45:
+#line 305 "../../src/foreign-pajek-parser.y"
+    {
+         context->mode=1;
+	 igraph_i_pajek_add_string_vertex_attribute("framecolor", 
+						    (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+     }
+    break;
+
+  case 46:
+#line 310 "../../src/foreign-pajek-parser.y"
+    { context->mode=3; }
+    break;
+
+  case 47:
+#line 310 "../../src/foreign-pajek-parser.y"
+    {
+         context->mode=1;
+	 igraph_i_pajek_add_string_vertex_attribute("labelcolor", 
+						    (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+     }
+    break;
+
+  case 48:
+#line 317 "../../src/foreign-pajek-parser.y"
+    { (yyval.string)=(yyvsp[(1) - (1)].string); }
+    break;
+
+  case 55:
+#line 321 "../../src/foreign-pajek-parser.y"
+    { context->directed=1; }
+    break;
+
+  case 56:
+#line 322 "../../src/foreign-pajek-parser.y"
+    { context->directed=1; }
+    break;
+
+  case 60:
+#line 327 "../../src/foreign-pajek-parser.y"
+    { context->actedge++;
+	                  context->mode=2; }
+    break;
+
+  case 61:
+#line 328 "../../src/foreign-pajek-parser.y"
+    { 
+  igraph_vector_push_back(context->vector, (yyvsp[(1) - (6)].intnum)-1);
+  igraph_vector_push_back(context->vector, (yyvsp[(2) - (6)].intnum)-1); }
+    break;
+
+  case 64:
+#line 337 "../../src/foreign-pajek-parser.y"
+    { context->directed=0; }
+    break;
+
+  case 65:
+#line 338 "../../src/foreign-pajek-parser.y"
+    { context->directed=0; }
+    break;
+
+  case 69:
+#line 343 "../../src/foreign-pajek-parser.y"
+    { context->actedge++; 
+	                    context->mode=2; }
+    break;
+
+  case 70:
+#line 344 "../../src/foreign-pajek-parser.y"
+    { 
+  igraph_vector_push_back(context->vector, (yyvsp[(1) - (6)].intnum)-1);
+  igraph_vector_push_back(context->vector, (yyvsp[(2) - (6)].intnum)-1); }
+    break;
+
+  case 74:
+#line 353 "../../src/foreign-pajek-parser.y"
+    {
+  igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context);
+}
+    break;
+
+  case 78:
+#line 361 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("color-red", (yyvsp[(2) - (4)].realnum), context);
+       igraph_i_pajek_add_numeric_edge_attribute("color-green", (yyvsp[(3) - (4)].realnum), context);
+       igraph_i_pajek_add_numeric_edge_attribute("color-blue", (yyvsp[(4) - (4)].realnum), context);
+   }
+    break;
+
+  case 79:
+#line 366 "../../src/foreign-pajek-parser.y"
+    { 
+       igraph_i_pajek_add_numeric_edge_attribute("arrowsize", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 80:
+#line 369 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("edgewidth", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 81:
+#line 372 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("hook1", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 82:
+#line 375 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("hook2", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 83:
+#line 378 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("angle1", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 84:
+#line 381 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("angle2", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 85:
+#line 384 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("velocity1", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 86:
+#line 387 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("velocity2", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 87:
+#line 390 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("arrowpos", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 88:
+#line 393 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("labelpos", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 89:
+#line 396 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("labelangle", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 90:
+#line 399 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("labelangle2", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 91:
+#line 402 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("labeldegree", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 92:
+#line 405 "../../src/foreign-pajek-parser.y"
+    {		/* what is this??? */
+       igraph_i_pajek_add_numeric_edge_attribute("arrowsize", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 93:
+#line 408 "../../src/foreign-pajek-parser.y"
+    {
+       igraph_i_pajek_add_numeric_edge_attribute("fontsize", (yyvsp[(2) - (2)].realnum), context);
+   }
+    break;
+
+  case 94:
+#line 413 "../../src/foreign-pajek-parser.y"
+    { context->mode=4; }
+    break;
+
+  case 95:
+#line 413 "../../src/foreign-pajek-parser.y"
+    {
+      context->mode=2;
+      igraph_i_pajek_add_string_edge_attribute("arrowtype", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+    }
+    break;
+
+  case 96:
+#line 417 "../../src/foreign-pajek-parser.y"
+    { context->mode=4; }
+    break;
+
+  case 97:
+#line 417 "../../src/foreign-pajek-parser.y"
+    {
+      context->mode=2;
+      igraph_i_pajek_add_string_edge_attribute("linepattern", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+    }
+    break;
+
+  case 98:
+#line 421 "../../src/foreign-pajek-parser.y"
+    { context->mode=4; }
+    break;
+
+  case 99:
+#line 421 "../../src/foreign-pajek-parser.y"
+    {
+      context->mode=2;
+      igraph_i_pajek_add_string_edge_attribute("label", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+    }
+    break;
+
+  case 100:
+#line 425 "../../src/foreign-pajek-parser.y"
+    { context->mode=4; }
+    break;
+
+  case 101:
+#line 425 "../../src/foreign-pajek-parser.y"
+    {
+      context->mode=2;
+      igraph_i_pajek_add_string_edge_attribute("labelcolor", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+    }
+    break;
+
+  case 102:
+#line 429 "../../src/foreign-pajek-parser.y"
+    { context->mode=4; }
+    break;
+
+  case 103:
+#line 429 "../../src/foreign-pajek-parser.y"
+    {
+      context->mode=2;
+      igraph_i_pajek_add_string_edge_attribute("color", (yyvsp[(3) - (3)].string).str, (yyvsp[(3) - (3)].string).len, context);
+    }
+    break;
+
+  case 104:
+#line 435 "../../src/foreign-pajek-parser.y"
+    { context->mode=2; (yyval.string)=(yyvsp[(1) - (1)].string); }
+    break;
+
+  case 105:
+#line 437 "../../src/foreign-pajek-parser.y"
+    { context->directed=1; }
+    break;
+
+  case 112:
+#line 445 "../../src/foreign-pajek-parser.y"
+    { context->mode=0; context->actfrom=labs((yyvsp[(1) - (1)].intnum))-1; }
+    break;
+
+  case 113:
+#line 447 "../../src/foreign-pajek-parser.y"
+    { 
+  igraph_vector_push_back(context->vector, context->actfrom); 
+  igraph_vector_push_back(context->vector, labs((yyvsp[(1) - (1)].intnum))-1); 
+}
+    break;
+
+  case 114:
+#line 452 "../../src/foreign-pajek-parser.y"
+    { context->directed=0; }
+    break;
+
+  case 121:
+#line 460 "../../src/foreign-pajek-parser.y"
+    { context->mode=0; context->actfrom=labs((yyvsp[(1) - (1)].intnum))-1; }
+    break;
+
+  case 122:
+#line 462 "../../src/foreign-pajek-parser.y"
+    { 
+  igraph_vector_push_back(context->vector, context->actfrom); 
+  igraph_vector_push_back(context->vector, labs((yyvsp[(1) - (1)].intnum))-1); 
+}
+    break;
+
+  case 124:
+#line 471 "../../src/foreign-pajek-parser.y"
+    { context->actfrom=0; 
+                         context->actto=0; 
+                         context->directed=(context->vcount2==0);
+                       }
+    break;
+
+  case 127:
+#line 478 "../../src/foreign-pajek-parser.y"
+    { context->actfrom++; context->actto=0; }
+    break;
+
+  case 130:
+#line 482 "../../src/foreign-pajek-parser.y"
+    {
+  if ((yyvsp[(1) - (1)].realnum) != 0) {
+    if (context->vcount2==0) {
+      context->actedge++;
+      igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context);    
+      igraph_vector_push_back(context->vector, context->actfrom);
+      igraph_vector_push_back(context->vector, context->actto);
+    } else if (context->vcount2 + context->actto < context->vcount) {
+      context->actedge++;
+      igraph_i_pajek_add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context);    
+      igraph_vector_push_back(context->vector, context->actfrom);
+      igraph_vector_push_back(context->vector, 
+			      context->vcount2+context->actto);
+    }
+  }
+  context->actto++;
+}
+    break;
+
+  case 131:
+#line 502 "../../src/foreign-pajek-parser.y"
+    { (yyval.intnum)=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner),
+					  igraph_pajek_yyget_leng(scanner)); }
+    break;
+
+  case 132:
+#line 505 "../../src/foreign-pajek-parser.y"
+    { (yyval.realnum)=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner),
+					  igraph_pajek_yyget_leng(scanner)); }
+    break;
+
+  case 135:
+#line 510 "../../src/foreign-pajek-parser.y"
+    { (yyval.string).str=igraph_pajek_yyget_text(scanner); 
+              (yyval.string).len=igraph_pajek_yyget_leng(scanner); }
+    break;
+
+  case 136:
+#line 512 "../../src/foreign-pajek-parser.y"
+    { (yyval.string).str=igraph_pajek_yyget_text(scanner); 
+              (yyval.string).len=igraph_pajek_yyget_leng(scanner); }
+    break;
+
+  case 137:
+#line 514 "../../src/foreign-pajek-parser.y"
+    { (yyval.string).str=igraph_pajek_yyget_text(scanner)+1; 
+               (yyval.string).len=igraph_pajek_yyget_leng(scanner)-2; }
+    break;
+
+
+/* Line 1267 of yacc.c.  */
+#line 2356 "foreign-pajek-parser.c"
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+  *++yylsp = yyloc;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (&yylloc, context, YY_("syntax error"));
+#else
+      {
+	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+	  {
+	    YYSIZE_T yyalloc = 2 * yysize;
+	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
+	    if (yymsg != yymsgbuf)
+	      YYSTACK_FREE (yymsg);
+	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+	    if (yymsg)
+	      yymsg_alloc = yyalloc;
+	    else
+	      {
+		yymsg = yymsgbuf;
+		yymsg_alloc = sizeof yymsgbuf;
+	      }
+	  }
+
+	if (0 < yysize && yysize <= yymsg_alloc)
+	  {
+	    (void) yysyntax_error (yymsg, yystate, yychar);
+	    yyerror (&yylloc, context, yymsg);
+	  }
+	else
+	  {
+	    yyerror (&yylloc, context, YY_("syntax error"));
+	    if (yysize != 0)
+	      goto yyexhaustedlab;
+	  }
+      }
+#endif
+    }
+
+  yyerror_range[0] = yylloc;
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval, &yylloc, context);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  yyerror_range[0] = yylsp[1-yylen];
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      yyerror_range[0] = *yylsp;
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+  yyerror_range[1] = yylloc;
+  /* Using YYLLOC is tempting, but would change the location of
+     the look-ahead.  YYLOC is available though.  */
+  YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+  *++yylsp = yyloc;
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (&yylloc, context, YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+		 yytoken, &yylval, &yylloc, context);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp, yylsp, context);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+#line 517 "../../src/foreign-pajek-parser.y"
+
+
+int igraph_pajek_yyerror(YYLTYPE* locp, 
+			 igraph_i_pajek_parsedata_t *context, 
+			 const char *s) {
+  snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, 
+	   "Parse error in Pajek file, line %i (%s)", 
+	   locp->first_line, s);
+  return 0;
+}
+
+igraph_real_t igraph_pajek_get_number(const char *str, long int length) {
+  igraph_real_t num;
+  char *tmp=igraph_Calloc(length+1, char);
+  
+  strncpy(tmp, str, length);
+  tmp[length]='\0';
+  sscanf(tmp, "%lf", &num);
+  igraph_Free(tmp);
+  return num;
+} 
+
+/* TODO: NA's */
+
+int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names,
+					 igraph_vector_ptr_t *attrs,
+					 long int count,
+					 const char *attrname,
+					 igraph_integer_t vid,
+					 igraph_real_t number) {
+  long int attrsize=igraph_trie_size(names);
+  long int id;
+  igraph_vector_t *na;
+  igraph_attribute_record_t *rec;
+
+  igraph_trie_get(names, attrname, &id);
+  if (id == attrsize) {
+    /* add a new attribute */
+    rec=igraph_Calloc(1, igraph_attribute_record_t);
+    na=igraph_Calloc(1, igraph_vector_t);
+    igraph_vector_init(na, count);
+    rec->name=strdup(attrname);
+    rec->type=IGRAPH_ATTRIBUTE_NUMERIC;
+    rec->value=na;
+    igraph_vector_ptr_push_back(attrs, rec);
+  }
+  rec=VECTOR(*attrs)[id];
+  na=(igraph_vector_t*)rec->value;
+  if (igraph_vector_size(na) == vid) {
+    IGRAPH_CHECK(igraph_vector_push_back(na, number));
+  } else if (igraph_vector_size(na) < vid) {
+    long int origsize=igraph_vector_size(na);
+    IGRAPH_CHECK(igraph_vector_resize(na, (long int)vid+1));
+    for (;origsize<count; origsize++) {
+      VECTOR(*na)[origsize] = IGRAPH_NAN;
+    }
+    VECTOR(*na)[(long int) vid] = number;
+  } else { 
+    VECTOR(*na)[(long int) vid] = number;
+  }    
+
+  return 0;
+}
+
+/* TODO: NA's */
+
+int igraph_i_pajek_add_string_attribute(igraph_trie_t *names,
+					igraph_vector_ptr_t *attrs,
+					long int count,
+					const char *attrname,
+					igraph_integer_t vid,
+					const char *str) {
+  long int attrsize=igraph_trie_size(names);
+  long int id;
+  igraph_strvector_t *na;
+  igraph_attribute_record_t *rec;
+  long int i;
+
+  igraph_trie_get(names, attrname, &id);
+  if (id == attrsize) {
+    /* add a new attribute */
+    rec=igraph_Calloc(1, igraph_attribute_record_t);
+    na=igraph_Calloc(1, igraph_strvector_t);
+    igraph_strvector_init(na, count);
+    for (i=0; i<count; i++) {
+      igraph_strvector_set(na, i, "");
+    }
+    rec->name=strdup(attrname);
+    rec->type=IGRAPH_ATTRIBUTE_STRING;
+    rec->value=na;
+    igraph_vector_ptr_push_back(attrs, rec);
+  }
+  rec=VECTOR(*attrs)[id];
+  na=(igraph_strvector_t*)rec->value;
+  if (igraph_strvector_size(na) <= vid) { 
+    long int origsize=igraph_strvector_size(na);
+    IGRAPH_CHECK(igraph_strvector_resize(na, vid+1));
+    for (;origsize<count; origsize++) {
+      igraph_strvector_set(na, origsize, "");
+    }
+  }
+  igraph_strvector_set(na, vid, str);
+
+  return 0;
+}
+
+int igraph_i_pajek_add_string_vertex_attribute(const char *name, 
+					       const char *value,
+					       int len,
+					       igraph_i_pajek_parsedata_t *context) {
+  char *tmp;
+  int ret;
+
+  tmp=igraph_Calloc(len+1, char);
+  if (tmp==0) {
+    IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM);
+  }
+  IGRAPH_FINALLY(free, tmp);
+  strncpy(tmp, value, len);
+  tmp[len]='\0';
+
+  ret=igraph_i_pajek_add_string_attribute(context->vertex_attribute_names,
+					  context->vertex_attributes,
+					  context->vcount,
+					  name, context->actvertex-1,
+					  tmp);
+  
+  igraph_Free(tmp);
+  IGRAPH_FINALLY_CLEAN(1);
+  
+  return ret;
+}
+
+int igraph_i_pajek_add_string_edge_attribute(const char *name, 
+					     const char *value,
+					     int len, 
+					     igraph_i_pajek_parsedata_t *context) {
+  char *tmp;
+  int ret;
+
+  tmp=igraph_Calloc(len+1, char);
+  if (tmp==0) {
+    IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM);
+  }
+  IGRAPH_FINALLY(free, tmp);
+  strncpy(tmp, value, len);
+  tmp[len]='\0';
+  
+  ret=igraph_i_pajek_add_string_attribute(context->edge_attribute_names,
+					  context->edge_attributes,
+					  context->actedge,
+					  name, context->actedge-1,
+					  tmp);
+
+  igraph_Free(tmp);
+  IGRAPH_FINALLY_CLEAN(1);
+  
+  return ret;
+}
+
+int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, 
+						igraph_real_t value, 
+						igraph_i_pajek_parsedata_t *context) {
+  
+  return
+    igraph_i_pajek_add_numeric_attribute(context->vertex_attribute_names,
+					 context->vertex_attributes,
+					 context->vcount,
+					 name, context->actvertex-1,
+					 value);
+}
+
+int igraph_i_pajek_add_numeric_edge_attribute(const char *name, 
+					      igraph_real_t value, 
+					      igraph_i_pajek_parsedata_t *context) {
+
+  return
+    igraph_i_pajek_add_numeric_attribute(context->edge_attribute_names,
+					 context->edge_attributes,
+					 context->actedge,
+					 name, context->actedge-1,
+					 value);
+}
+
+int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context) {
+  
+  const char *attrname="type";
+  igraph_trie_t *names=context->vertex_attribute_names;
+  igraph_vector_ptr_t *attrs=context->vertex_attributes;
+  int i, n=context->vcount, n1=context->vcount2;
+  long int attrid, attrsize=igraph_trie_size(names);
+  igraph_attribute_record_t *rec;  
+  igraph_vector_t *na;
+
+  if (n1 > n) { 
+    IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file", 
+		 IGRAPH_PARSEERROR);
+  }
+
+  igraph_trie_get(names, attrname, &attrid);
+  if (attrid != attrsize) { 
+    IGRAPH_ERROR("Duplicate 'type' attribute in Pajek file, "
+		 "this should not happen", IGRAPH_EINTERNAL);
+  }
+  
+  /* add a new attribute */
+  rec=igraph_Calloc(1, igraph_attribute_record_t);
+  na=igraph_Calloc(1, igraph_vector_t);
+  igraph_vector_init(na, n);
+  rec->name=strdup(attrname);
+  rec->type=IGRAPH_ATTRIBUTE_NUMERIC;
+  rec->value=na;
+  igraph_vector_ptr_push_back(attrs, rec);
+
+  for (i=0; i<n1; i++) { 
+    VECTOR(*na)[i] = 0;
+  }
+  for (i=n1; i<n; i++) { 
+    VECTOR(*na)[i] = 1;
+  }
+
+  return 0;
+}
+
+int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context) {
+  const igraph_vector_t *edges=context->vector;
+  int i, n1=context->vcount2;
+  int ne=igraph_vector_size(edges);
+  
+  for (i=0; i<ne; i+=2) {
+    int v1=VECTOR(*edges)[i];
+    int v2=VECTOR(*edges)[i+1];
+    if ( (v1 < n1 && v2 < n1) || (v1 > n1 && v2 > n1) ) {
+      IGRAPH_WARNING("Invalid edge in bipartite graph");
+    }
+  }
+  
+  return 0;
+}
+
diff --git a/igraph/src/foreign.c b/igraph/src/foreign.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/foreign.c
@@ -0,0 +1,3390 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph R package.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_foreign.h"
+#include "config.h"
+#include "igraph_math.h"
+#include "igraph_gml_tree.h"
+#include "igraph_memory.h"
+#include "igraph_attributes.h"
+#include "igraph_interface.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_constructors.h"
+#include "igraph_types_internal.h"
+
+#include <ctype.h>      /* isspace */
+#include <string.h>
+#include <time.h>
+
+/**
+ * \section about_loadsave
+ *
+ * <para>These functions can write a graph to a file, or read a graph
+ * from a file.</para>
+ *
+ * <para>Note that as \a igraph uses the traditional C streams, it is
+ * possible to read/write files from/to memory, at least on GNU
+ * operating systems supporting \quote non-standard\endquote streams.</para>
+ */
+
+/**
+ * \ingroup loadsave
+ * \function igraph_read_graph_edgelist
+ * \brief Reads an edge list from a file and creates a graph.
+ *
+ * </para><para>
+ * This format is simply a series of even number integers separated by
+ * whitespace. The one edge (ie. two integers) per line format is thus
+ * not required (but recommended for readability). Edges of directed
+ * graphs are assumed to be in from, to order.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param instream Pointer to a stream, it should be readable.
+ * \param n The number of vertices in the graph. If smaller than the
+ *        largest integer in the file it will be ignored. It is thus
+ *        safe to supply zero here.
+ * \param directed Logical, if true the graph is directed, if false it
+ *        will be undirected.
+ * \return Error code:
+ *         \c IGRAPH_PARSEERROR: if there is a
+ *         problem reading the file, or the file is syntactically
+ *         incorrect.
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices plus the number of edges. It is assumed that
+ * reading an integer requires O(1)
+ * time.
+ */
+
+int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream,
+                               igraph_integer_t n, igraph_bool_t directed) {
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int from, to;
+    int c;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, 100));
+
+    /* skip all whitespace */
+    do {
+        c = getc (instream);
+    } while (isspace (c));
+    ungetc (c, instream);
+
+    while (!feof(instream)) {
+        int read;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        read = fscanf(instream, "%li", &from);
+        if (read != 1) {
+            IGRAPH_ERROR("parsing edgelist file failed", IGRAPH_PARSEERROR);
+        }
+        read = fscanf(instream, "%li", &to);
+        if (read != 1) {
+            IGRAPH_ERROR("parsing edgelist file failed", IGRAPH_PARSEERROR);
+        }
+        IGRAPH_CHECK(igraph_vector_push_back(&edges, from));
+        IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+
+        /* skip all whitespace */
+        do {
+            c = getc (instream);
+        } while (isspace (c));
+        ungetc (c, instream);
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+#include "foreign-ncol-header.h"
+
+int igraph_ncol_yylex_init_extra (igraph_i_ncol_parsedata_t* user_defined,
+                                  void* scanner);
+int igraph_ncol_yylex_destroy (void *scanner );
+int igraph_ncol_yyparse (igraph_i_ncol_parsedata_t* context);
+void igraph_ncol_yyset_in  (FILE * in_str, void* yyscanner );
+
+/**
+ * \ingroup loadsave
+ * \function igraph_read_graph_ncol
+ * \brief Reads a <code>.ncol</code> file used by LGL.
+ *
+ * Also useful for creating graphs from \quote named\endquote (and
+ * optionally weighted) edge lists.
+ *
+ * </para><para>
+ * This format is used by the Large Graph Layout program
+ * (http://lgl.sourceforge.net), and it is simply a
+ * symbolic weighted edge list. It is a simple text file with one edge
+ * per line. An edge is defined by two symbolic vertex names separated
+ * by whitespace. (The symbolic vertex names themselves cannot contain
+ * whitespace. They might follow by an optional number, this will be
+ * the weight of the edge; the number can be negative and can be in
+ * scientific notation. If there is no weight specified to an edge it
+ * is assumed to be zero.
+ *
+ * </para><para>
+ * The resulting graph is always undirected.
+ * LGL cannot deal with files which contain multiple or loop edges,
+ * this is however not checked here, as \a igraph is happy with
+ * these.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param instream Pointer to a stream, it should be readable.
+ * \param predefnames Pointer to the symbolic names of the vertices in
+ *        the file. If \c NULL is given here then vertex ids will be
+ *        assigned to vertex names in the order of their appearance in
+ *        the \c .ncol file. If it is not \c NULL and some unknown
+ *        vertex names are found in the \c .ncol file then new vertex
+ *        ids will be assigned to them.
+ * \param names Logical value, if TRUE the symbolic names of the
+ *        vertices will be added to the graph as a vertex attribute
+ *        called \quote name\endquote.
+ * \param weights Whether to add the weights of the edges to the
+ *        graph as an edge attribute called \quote weight\endquote.
+ *        \c IGRAPH_ADD_WEIGHTS_YES adds the weights (even if they
+ *        are not present in the file, in this case they are assumed
+ *        to be zero). \c IGRAPH_ADD_WEIGHTS_NO does not add any
+ *        edge attribute. \c IGRAPH_ADD_WEIGHTS_IF_PRESENT adds the
+ *        attribute if and only if there is at least one explicit
+ *        edge weight in the input file.
+ * \param directed Whether to create a directed graph. As this format
+ *        was originally used only for undirected graphs there is no
+ *        information in the file about the directedness of the graph.
+ *        Set this parameter to \c IGRAPH_DIRECTED or \c
+ *        IGRAPH_UNDIRECTED to create a directed or undirected graph.
+ * \return Error code:
+ *         \c IGRAPH_PARSEERROR: if there is a
+ *          problem reading
+ *         the file, or the file is syntactically incorrect.
+ *
+ * Time complexity:
+ * O(|V|+|E|log(|V|)) if we neglect
+ * the time required by the parsing. As usual
+ * |V| is the number of vertices,
+ * while |E| is the number of edges.
+ *
+ * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol()
+ */
+
+int igraph_read_graph_ncol(igraph_t *graph, FILE *instream,
+                           igraph_strvector_t *predefnames,
+                           igraph_bool_t names,
+                           igraph_add_weights_t weights,
+                           igraph_bool_t directed) {
+
+    igraph_vector_t edges, ws;
+    igraph_trie_t trie = IGRAPH_TRIE_NULL;
+    igraph_integer_t no_of_nodes;
+    long int no_predefined = 0;
+    igraph_vector_ptr_t name, weight;
+    igraph_vector_ptr_t *pname = 0, *pweight = 0;
+    igraph_attribute_record_t namerec, weightrec;
+    const char *namestr = "name", *weightstr = "weight";
+    igraph_i_ncol_parsedata_t context;
+
+    IGRAPH_CHECK(igraph_empty(graph, 0, directed));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    IGRAPH_TRIE_INIT_FINALLY(&trie, names);
+    IGRAPH_VECTOR_INIT_FINALLY(&ws, 0);
+
+    /* Add the predefined names, if any */
+    if (predefnames != 0) {
+        long int i, id, n;
+        char *key;
+        n = no_predefined = igraph_strvector_size(predefnames);
+        for (i = 0; i < n; i++) {
+            igraph_strvector_get(predefnames, i, &key);
+            igraph_trie_get(&trie, key, &id);
+            if (id != i) {
+                IGRAPH_WARNING("reading NCOL file, duplicate entry in predefnames");
+                no_predefined--;
+            }
+        }
+    }
+
+    context.has_weights = 0;
+    context.vector = &edges;
+    context.weights = &ws;
+    context.trie = &trie;
+    context.eof = 0;
+
+    igraph_ncol_yylex_init_extra(&context, &context.scanner);
+    IGRAPH_FINALLY(igraph_ncol_yylex_destroy, context.scanner);
+
+    igraph_ncol_yyset_in(instream, context.scanner);
+
+    if (igraph_ncol_yyparse(&context)) {
+        if (context.errmsg[0] != 0) {
+            IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR);
+        } else {
+            IGRAPH_ERROR("Cannot read NCOL file", IGRAPH_PARSEERROR);
+        }
+    }
+
+    if (predefnames != 0 &&
+        igraph_trie_size(&trie) != no_predefined) {
+        IGRAPH_WARNING("unknown vertex/vertices found, predefnames extended");
+    }
+
+    if (names) {
+        const igraph_strvector_t *namevec;
+        IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1));
+        pname = &name;
+        igraph_trie_getkeys(&trie, &namevec); /* dirty */
+        namerec.name = namestr;
+        namerec.type = IGRAPH_ATTRIBUTE_STRING;
+        namerec.value = namevec;
+        VECTOR(name)[0] = &namerec;
+    }
+
+    if (weights == IGRAPH_ADD_WEIGHTS_YES ||
+        (weights == IGRAPH_ADD_WEIGHTS_IF_PRESENT && context.has_weights)) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1));
+        pweight = &weight;
+        weightrec.name = weightstr;
+        weightrec.type = IGRAPH_ATTRIBUTE_NUMERIC;
+        weightrec.value = &ws;
+        VECTOR(weight)[0] = &weightrec;
+    }
+
+    if (igraph_vector_empty(&edges)) {
+        no_of_nodes = 0;
+    } else {
+        no_of_nodes = igraph_vector_max(&edges) + 1;
+    }
+
+    IGRAPH_CHECK(igraph_add_vertices(graph, no_of_nodes, pname));
+    IGRAPH_CHECK(igraph_add_edges(graph, &edges, pweight));
+
+    if (pname) {
+        igraph_vector_ptr_destroy(pname);
+    }
+    if (pweight) {
+        igraph_vector_ptr_destroy(pweight);
+    }
+    igraph_vector_destroy(&ws);
+    igraph_trie_destroy(&trie);
+    igraph_vector_destroy(&edges);
+    igraph_ncol_yylex_destroy(context.scanner);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+#include "foreign-lgl-header.h"
+
+int igraph_lgl_yylex_init_extra (igraph_i_lgl_parsedata_t* user_defined,
+                                 void* scanner);
+int igraph_lgl_yylex_destroy (void *scanner );
+int igraph_lgl_yyparse (igraph_i_lgl_parsedata_t* context);
+void igraph_lgl_yyset_in  (FILE * in_str, void* yyscanner );
+
+/**
+ * \ingroup loadsave
+ * \function igraph_read_graph_lgl
+ * \brief Reads a graph from an <code>.lgl</code> file
+ *
+ * </para><para>
+ * The <code>.lgl</code> format is used by the Large Graph
+ * Layout visualization software
+ * (http://lgl.sourceforge.net), it can
+ * describe undirected optionally weighted graphs. From the LGL
+ * manual:
+ *
+ * \blockquote <para>The second format is the LGL file format
+ * (<code>.lgl</code> file
+ * suffix). This is yet another graph file format that tries to be as
+ * stingy as possible with space, yet keeping the edge file in a human
+ * readable (not binary) format. The format itself is like the
+ * following:
+ * \verbatim # vertex1name
+vertex2name [optionalWeight]
+vertex3name [optionalWeight] \endverbatim
+ * Here, the first vertex of an edge is preceded with a pound sign
+ * '#'.  Then each vertex that shares an edge with that vertex is
+ * listed one per line on subsequent lines.</para> \endblockquote
+ *
+ * </para><para>
+ * LGL cannot handle loop and multiple edges or directed graphs, but
+ * in \a igraph it is not an error to have multiple and loop edges.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param instream A stream, it should be readable.
+ * \param names Logical value, if TRUE the symbolic names of the
+ *        vertices will be added to the graph as a vertex attribute
+ *        called \quote name\endquote.
+ * \param weights Whether to add the weights of the edges to the
+ *        graph as an edge attribute called \quote weight\endquote.
+ *        \c IGRAPH_ADD_WEIGHTS_YES adds the weights (even if they
+ *        are not present in the file, in this case they are assumed
+ *        to be zero). \c IGRAPH_ADD_WEIGHTS_NO does not add any
+ *        edge attribute. \c IGRAPH_ADD_WEIGHTS_IF_PRESENT adds the
+ *        attribute if and only if there is at least one explicit
+ *        edge weight in the input file.
+ * \param directed Whether to create a directed graph. As this format
+ *        was originally used only for undirected graphs there is no
+ *        information in the file about the directedness of the graph.
+ *        Set this parameter to \c IGRAPH_DIRECTED or \c
+ *        IGRAPH_UNDIRECTED to create a directed or undirected graph.
+ * \return Error code:
+ *         \c IGRAPH_PARSEERROR: if there is a
+ *         problem reading the file, or the file is syntactically
+ *         incorrect.
+ *
+ * Time complexity:
+ * O(|V|+|E|log(|V|)) if we neglect
+ * the time required by the parsing. As usual
+ * |V| is the number of vertices,
+ * while |E| is the number of edges.
+ *
+ * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl()
+ *
+ * \example examples/simple/igraph_read_graph_lgl.c
+ */
+
+int igraph_read_graph_lgl(igraph_t *graph, FILE *instream,
+                          igraph_bool_t names,
+                          igraph_add_weights_t weights,
+                          igraph_bool_t directed) {
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL, ws = IGRAPH_VECTOR_NULL;
+    igraph_trie_t trie = IGRAPH_TRIE_NULL;
+    igraph_vector_ptr_t name, weight;
+    igraph_vector_ptr_t *pname = 0, *pweight = 0;
+    igraph_attribute_record_t namerec, weightrec;
+    const char *namestr = "name", *weightstr = "weight";
+    igraph_i_lgl_parsedata_t context;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&ws, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_TRIE_INIT_FINALLY(&trie, names);
+
+    context.has_weights = 0;
+    context.vector = &edges;
+    context.weights = &ws;
+    context.trie = &trie;
+    context.eof = 0;
+
+    igraph_lgl_yylex_init_extra(&context, &context.scanner);
+    IGRAPH_FINALLY(igraph_lgl_yylex_destroy, context.scanner);
+
+    igraph_lgl_yyset_in(instream, context.scanner);
+
+    if (igraph_lgl_yyparse(&context)) {
+        if (context.errmsg[0] != 0) {
+            IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR);
+        } else {
+            IGRAPH_ERROR("Cannot read LGL file", IGRAPH_PARSEERROR);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_empty(graph, 0, directed));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+
+    if (names) {
+        const igraph_strvector_t *namevec;
+        IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &name);
+        pname = &name;
+        igraph_trie_getkeys(&trie, &namevec); /* dirty */
+        namerec.name = namestr;
+        namerec.type = IGRAPH_ATTRIBUTE_STRING;
+        namerec.value = namevec;
+        VECTOR(name)[0] = &namerec;
+    }
+
+    if (weights == IGRAPH_ADD_WEIGHTS_YES ||
+        (weights == IGRAPH_ADD_WEIGHTS_IF_PRESENT && context.has_weights)) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &weight);
+        pweight = &weight;
+        weightrec.name = weightstr;
+        weightrec.type = IGRAPH_ATTRIBUTE_NUMERIC;
+        weightrec.value = &ws;
+        VECTOR(weight)[0] = &weightrec;
+    }
+
+    IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t)
+                                     igraph_trie_size(&trie), pname));
+    IGRAPH_CHECK(igraph_add_edges(graph, &edges, pweight));
+
+    if (pweight) {
+        igraph_vector_ptr_destroy(pweight);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (pname) {
+        igraph_vector_ptr_destroy(pname);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    igraph_trie_destroy(&trie);
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&ws);
+    igraph_lgl_yylex_destroy(context.scanner);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+#include "foreign-pajek-header.h"
+
+int igraph_pajek_yylex_init_extra(igraph_i_pajek_parsedata_t* user_defined,
+                                  void* scanner);
+int igraph_pajek_yylex_destroy (void *scanner );
+int igraph_pajek_yyparse (igraph_i_pajek_parsedata_t* context);
+void igraph_pajek_yyset_in  (FILE * in_str, void* yyscanner );
+
+/**
+ * \function igraph_read_graph_pajek
+ * \brief Reads a file in Pajek format
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param file An already opened file handler.
+ * \return Error code.
+ *
+ * </para><para>
+ * Only a subset of the Pajek format is implemented. This is partially
+ * because this format is not very well documented, but also because
+ * <command>igraph</command> does not support some Pajek features, like
+ * multigraphs.
+ *
+ * </para><para>
+ * Starting from version 0.6.1 igraph reads bipartite (two-mode)
+ * graphs from Pajek files and add the \c type vertex attribute for them.
+ * Warnings are given for invalid edges, i.e. edges connecting
+ * vertices of the same type.
+ *
+ * </para><para>
+ * The list of the current limitations:
+ * \olist
+ * \oli Only <filename>.net</filename> files are supported, Pajek
+ * project files (<filename>.paj</filename>) are not. These might be
+ * supported in the future if there is need for it.
+ * \oli Time events networks are not supported.
+ * \oli Hypergraphs (ie. graphs with non-binary edges) are not
+ * supported.
+ * \oli Graphs with both directed and non-directed edges are not
+ * supported, are they cannot be represented in
+ * <command>igraph</command>.
+ * \oli Only Pajek networks are supported, permutations, hierarchies,
+ * clusters and vectors are not.
+ * \oli Graphs with multiple edge sets are not supported.
+ * \endolist
+ *
+ * </para><para>
+ * If there are attribute handlers installed,
+ * <command>igraph</command> also reads the vertex and edge attributes
+ * from the file. Most attributes are renamed to be more informative:
+ * `\c color' instead of `\c c', `\c xfact' instead of `\c x_fact',
+ * `\c yfact' instead of `y_fact', `\c labeldist' instead of `\c lr',
+ * `\c labeldegree2' instead of `\c lphi', `\c framewidth' instead of `\c bw',
+ * `\c fontsize'
+ * instead of `\c fos', `\c rotation' instead of `\c phi', `\c radius' instead
+ * of `\c r',
+ * `\c diamondratio' instead of `\c q', `\c labeldegree' instead of `\c la',
+ * `\c vertexsize'
+ * instead of `\c size', `\c color' instead of `\c ic', `\c framecolor' instead of
+ * `\c bc', `\c labelcolor' instead of `\c lc', these belong to vertices.
+ *
+ * </para><para>
+ * Edge attributes are also renamed, `\c s' to `\c arrowsize', `\c w'
+ * to `\c edgewidth', `\c h1' to `\c hook1', `\c h2' to `\c hook2',
+ * `\c a1' to `\c angle1', `\c a2' to `\c angle2', `\c k1' to
+ * `\c velocity1', `\c k2' to `\c velocity2', `\c ap' to `\c
+ * arrowpos', `\c lp' to `\c labelpos', `\c lr' to
+ * `\c labelangle', `\c lphi' to `\c labelangle2', `\c la' to `\c
+ * labeldegree', `\c fos' to
+ * `\c fontsize', `\c a' to `\c arrowtype', `\c p' to `\c
+ * linepattern', `\c l' to `\c label', `\c lc' to
+ * `\c labelcolor', `\c c' to `\c color'.
+ *
+ * </para><para>
+ * In addition the following vertex attributes might be added: `\c id'
+ * if there are vertex ids in the file, `\c x' and `\c y' or `\c x'
+ * and `\c y' and `\c z' if there are vertex coordinates in the file.
+ *
+ * </para><para>The `\c weight' edge attribute might be
+ * added if there are edge weights present.
+ *
+ * </para><para>
+ * See the pajek homepage:
+ * http://vlado.fmf.uni-lj.si/pub/networks/pajek/ for more info on
+ * Pajek and the Pajek manual:
+ * http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/pajekman.pdf for
+ * information on the Pajek file format.
+ *
+ * </para><para>
+ * Time complexity: O(|V|+|E|+|A|), |V| is the number of vertices, |E|
+ * the number of edges, |A| the number of attributes (vertex + edge)
+ * in the graph if there are attribute handlers installed.
+ *
+ * \sa \ref igraph_write_graph_pajek() for writing Pajek files, \ref
+ * igraph_read_graph_graphml() for reading GraphML files.
+ *
+ * \example examples/simple/foreign.c
+ */
+
+int igraph_read_graph_pajek(igraph_t *graph, FILE *instream) {
+
+    igraph_vector_t edges;
+    igraph_trie_t vattrnames;
+    igraph_vector_ptr_t vattrs;
+    igraph_trie_t eattrnames;
+    igraph_vector_ptr_t eattrs;
+    long int i, j;
+    igraph_i_pajek_parsedata_t context;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 1);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&vattrs, 0);
+    IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 1);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&eattrs, 0);
+
+    context.vector = &edges;
+    context.mode = 0;
+    context.vcount = -1;
+    context.vertexid = 0;
+    context.vertex_attribute_names = &vattrnames;
+    context.vertex_attributes = &vattrs;
+    context.edge_attribute_names = &eattrnames;
+    context.edge_attributes = &eattrs;
+    context.actedge = 0;
+    context.eof = 0;
+
+    igraph_pajek_yylex_init_extra(&context, &context.scanner);
+    IGRAPH_FINALLY(igraph_pajek_yylex_destroy, context.scanner);
+
+    igraph_pajek_yyset_in(instream, context.scanner);
+
+    if (igraph_pajek_yyparse(&context)) {
+        if (context.errmsg[0] != 0) {
+            IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR);
+        } else {
+            IGRAPH_ERROR("Cannot read Pajek file", IGRAPH_PARSEERROR);
+        }
+    }
+
+    if (context.vcount < 0) {
+        IGRAPH_ERROR("invalid vertex count in Pajek file", IGRAPH_EINVAL);
+    }
+    if (context.vcount2 < 0) {
+        IGRAPH_ERROR("invalid 2-mode vertex count in Pajek file", IGRAPH_EINVAL);
+    }
+
+    for (i = 0; i < igraph_vector_ptr_size(&eattrs); i++) {
+        igraph_attribute_record_t *rec = VECTOR(eattrs)[i];
+        if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+            igraph_vector_t *vec = (igraph_vector_t*)rec->value;
+            long int origsize = igraph_vector_size(vec);
+            igraph_vector_resize(vec, context.actedge);
+            for (j = origsize; j < context.actedge; j++) {
+                VECTOR(*vec)[j] = IGRAPH_NAN;
+            }
+        } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+            igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value;
+            long int origsize = igraph_strvector_size(strvec);
+            igraph_strvector_resize(strvec, context.actedge);
+            for (j = origsize; j < context.actedge; j++) {
+                igraph_strvector_set(strvec, j, "");
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_empty(graph, 0, context.directed));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+    IGRAPH_CHECK(igraph_add_vertices(graph, context.vcount, &vattrs));
+    IGRAPH_CHECK(igraph_add_edges(graph, &edges, &eattrs));
+
+    for (i = 0; i < igraph_vector_ptr_size(&vattrs); i++) {
+        igraph_attribute_record_t *rec = VECTOR(vattrs)[i];
+        if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+            igraph_vector_t *vec = (igraph_vector_t*) rec->value;
+            igraph_vector_destroy(vec);
+            igraph_Free(vec);
+        } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+            igraph_strvector_t *strvec = (igraph_strvector_t *)rec->value;
+            igraph_strvector_destroy(strvec);
+            igraph_Free(strvec);
+        }
+        igraph_free( (char*)(rec->name));
+        igraph_Free(rec);
+    }
+
+    for (i = 0; i < igraph_vector_ptr_size(&eattrs); i++) {
+        igraph_attribute_record_t *rec = VECTOR(eattrs)[i];
+        if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+            igraph_vector_t *vec = (igraph_vector_t*) rec->value;
+            igraph_vector_destroy(vec);
+            igraph_Free(vec);
+        } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+            igraph_strvector_t *strvec = (igraph_strvector_t *)rec->value;
+            igraph_strvector_destroy(strvec);
+            igraph_Free(strvec);
+        }
+        igraph_free( (char*)(rec->name));
+        igraph_Free(rec);
+    }
+
+    igraph_vector_destroy(&edges);
+    igraph_vector_ptr_destroy(&eattrs);
+    igraph_trie_destroy(&eattrnames);
+    igraph_vector_ptr_destroy(&vattrs);
+    igraph_trie_destroy(&vattrnames);
+    igraph_pajek_yylex_destroy(context.scanner);
+
+    IGRAPH_FINALLY_CLEAN(7);
+    return 0;
+}
+
+/**
+ * \function igraph_read_graph_dimacs
+ * \brief Read a graph in DIMACS format.
+ *
+ * This function reads the DIMACS file format, more specifically the
+ * version for network flow problems, see the files at
+ * ftp://dimacs.rutgers.edu/pub/netflow/general-info/
+ *
+ * </para><para>
+ * This is a line-oriented text file (ASCII) format. The first
+ * character of each line defines the type of the line. If the first
+ * character is <code>c</code> the line is a comment line and it is
+ * ignored. There is one problem line (<code>p</code> in the file, it
+ * must appear before any node and arc descriptor lines. The problem
+ * line has three fields separated by spaces: the problem type
+ * (<code>min</code>, <code>max</code> or <code>asn</code>), the
+ * number of vertices and number of edges in the graph.
+ * Exactly two node identification lines are expected
+ * (<code>n</code>), one for the source, one for the target vertex.
+ * These have two fields: the id of the vertex and the type of the
+ * vertex, either <code>s</code> (=source) or <code>t</code>
+ * (=target). Arc lines start with <code>a</code> and have three
+ * fields: the source vertex, the target vertex and the edge capacity.
+ *
+ * </para><para>
+ * Vertex ids are numbered from 1.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param instream The file to read from.
+ * \param source Pointer to an integer, the id of the source node will
+ *    be stored here. (The igraph vertex id, which is one less than
+ *    the actual number in the file.) It is ignored if
+ *    <code>NULL</code>.
+ * \param target Pointer to an integer, the (igraph) id of the target
+ *    node will be stored here. It is ignored if <code>NULL</code>.
+ * \param capacity Pointer to an initialized vector, the capacity of
+ *    the edges will be stored here if not <code>NULL</code>.
+ * \param directed Boolean, whether to create a directed graph.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|+c), the number of vertices plus the
+ * number of edges, plus the size of the file in characters.
+ *
+ * \sa \ref igraph_write_graph_dimacs()
+ */
+
+int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream,
+                             igraph_strvector_t *problem,
+                             igraph_vector_t *label,
+                             igraph_integer_t *source,
+                             igraph_integer_t *target,
+                             igraph_vector_t *capacity,
+                             igraph_bool_t directed) {
+
+    igraph_vector_t edges;
+    long int no_of_nodes = -1;
+    long int no_of_edges = -1;
+    long int tsource = -1;
+    long int ttarget = -1;
+    char prob[21];
+    char c;
+    int problem_type = 0;
+
+#define PROBLEM_EDGE  1
+#define PROBLEM_MAX   2
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    if (capacity) {
+        igraph_vector_clear(capacity);
+    }
+
+    while (!feof(instream)) {
+        int read;
+        char str[3];
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        read = fscanf(instream, "%2c", str);
+        if (feof(instream)) {
+            break;
+        }
+        if (read != 1) {
+            IGRAPH_ERROR("parsing dimacs file failed", IGRAPH_PARSEERROR);
+        }
+        switch (str[0]) {
+            long int tmp, tmp2;
+            long int from, to;
+            igraph_real_t cap;
+
+        case 'c':
+            /* comment */
+            break;
+
+        case 'p':
+            if (no_of_nodes != -1) {
+                IGRAPH_ERROR("reading dimacs file failed, double 'p' line",
+                             IGRAPH_PARSEERROR);
+            }
+            read = fscanf(instream, "%20s %li %li", prob,
+                          &no_of_nodes, &no_of_edges);
+            if (read != 3) {
+                IGRAPH_ERROR("reading dimacs file failed", IGRAPH_PARSEERROR);
+            }
+            if (!strcmp(prob, "edge")) {
+                /* edge list */
+                problem_type = PROBLEM_EDGE;
+                if (label) {
+                    long int i;
+                    IGRAPH_CHECK(igraph_vector_resize(label, no_of_nodes));
+                    for (i = 0; i < no_of_nodes; i++) {
+                        VECTOR(*label)[i] = i + 1;
+                    }
+                }
+            } else if (!strcmp(prob, "max")) {
+                /* maximum flow problem */
+                problem_type = PROBLEM_MAX;
+                if (capacity) {
+                    IGRAPH_CHECK(igraph_vector_reserve(capacity, no_of_edges));
+                }
+            } else {
+                IGRAPH_ERROR("Unknown problem type, should be 'edge' or 'max'",
+                             IGRAPH_PARSEERROR);
+            }
+            if (problem) {
+                igraph_strvector_clear(problem);
+                IGRAPH_CHECK(igraph_strvector_add(problem, prob));
+            }
+            IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+            break;
+
+        case 'n':
+            /* for MAX this is either the source or target vertex,
+            for EDGE this is a vertex label */
+            if (problem_type == PROBLEM_MAX) {
+                str[0] = 'x';
+                read = fscanf(instream, "%li %1s", &tmp, str);
+                if (str[0] == 's') {
+                    if (tsource != -1) {
+                        IGRAPH_ERROR("reading dimacsfile: multiple source vertex line",
+                                     IGRAPH_PARSEERROR);
+                    } else {
+                        tsource = tmp;
+                    }
+                } else if (str[0] == 't') {
+                    if (ttarget != -1) {
+                        IGRAPH_ERROR("reading dimacsfile: multiple target vertex line",
+                                     IGRAPH_PARSEERROR);
+                    } else {
+                        ttarget = tmp;
+                    }
+                } else {
+                    IGRAPH_ERROR("invalid node descriptor line in dimacs file",
+                                 IGRAPH_PARSEERROR);
+                }
+            } else {
+                read = fscanf(instream, "%li %li", &tmp, &tmp2);
+                if (label) {
+                    VECTOR(*label)[tmp] = tmp2;
+                }
+            }
+
+            break;
+
+        case 'a':
+            /* This is valid only for MAX, a weighted edge */
+            if (problem_type != PROBLEM_MAX) {
+                IGRAPH_ERROR("'a' lines are allowed only in MAX problem files",
+                             IGRAPH_PARSEERROR);
+            }
+            read = fscanf(instream, "%li %li %lf", &from, &to, &cap);
+            if (read != 3) {
+                IGRAPH_ERROR("reading dimacs file", IGRAPH_PARSEERROR);
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, from - 1));
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, to - 1));
+            if (capacity) {
+                IGRAPH_CHECK(igraph_vector_push_back(capacity, cap));
+            }
+            break;
+
+        case 'e':
+            /* Edge line, only in EDGE */
+            if (problem_type != PROBLEM_EDGE) {
+                IGRAPH_ERROR("'e' lines are allowed only in EDGE problem files",
+                             IGRAPH_PARSEERROR);
+            }
+            read = fscanf(instream, "%li %li", &from, &to);
+            if (read != 2) {
+                IGRAPH_ERROR("reading dimacs file", IGRAPH_PARSEERROR);
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, from - 1));
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, to - 1));
+            break;
+
+        default:
+            IGRAPH_ERROR("unknown line type in dimacs file", IGRAPH_PARSEERROR);
+        }
+
+        /* Go to next line */
+        while (!feof(instream) && (c = (char) getc(instream)) != '\n') ;
+    }
+
+    if (source) {
+        *source = (igraph_integer_t) tsource - 1;
+    }
+    if (target) {
+        *target = (igraph_integer_t) ttarget - 1;
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_read_graph_graphdb_getword(FILE *instream) {
+    int b1, b2;
+    unsigned char c1, c2;
+    b1 = fgetc(instream);
+    b2 = fgetc(instream);
+    if (b1 != EOF) {
+        c1 = (unsigned char) b1; c2 = (unsigned char) b2;
+        return c1 | (c2 << 8);
+    } else {
+        return -1;
+    }
+}
+
+/**
+ * \function igraph_read_graph_graphdb
+ * \brief Read a graph in the binary graph database format.
+ *
+ * This is a binary format, used in the graph database
+ * for isomorphism testing. From the (now defunct) graph database
+ * homepage:
+ * </para>
+ *
+ * \blockquote <para>
+ * The graphs are stored in a compact binary format, one graph per
+ * file. The file is composed of 16 bit words, which are represented
+ * using the so-called little-endian convention, i.e. the least
+ * significant byte of the word is stored first.</para>
+ *
+ * <para>
+ * Then, for each node, the file contains the list of edges coming
+ * out of the node itself. The list is represented by a word encoding
+ * its length, followed by a word for each edge, representing the
+ * destination node of the edge. Node numeration is 0-based, so the
+ * first node of the graph has index 0.</para> \endblockquote
+ *
+ * <para>
+ * Only unlabelled graphs are implemented.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param instream The stream to read from.
+ * \param directed Logical scalar, whether to create a directed graph.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the
+ * number of edges.
+ *
+ * \example examples/simple/igraph_read_graph_graphdb.c
+ */
+
+int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream,
+                              igraph_bool_t directed) {
+
+    igraph_vector_t edges;
+    long int nodes;
+    long int i, j;
+    igraph_bool_t end = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    nodes = igraph_i_read_graph_graphdb_getword(instream);
+    if (nodes < 0) {
+        IGRAPH_ERROR("Can't read from file", IGRAPH_EFILE);
+    }
+    for (i = 0; !end && i < nodes; i++) {
+        long int len = igraph_i_read_graph_graphdb_getword(instream);
+        if (len < 0) {
+            end = 1;
+            break;
+        }
+        for (j = 0; ! end && j < len; j++) {
+            long int to = igraph_i_read_graph_graphdb_getword(instream);
+            if (to < 0) {
+                end = 1;
+                break;
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, to));
+        }
+    }
+
+    if (end) {
+        IGRAPH_ERROR("Truncated graphdb file", IGRAPH_EFILE);
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+#include "foreign-gml-header.h"
+
+int igraph_gml_yylex_init_extra (igraph_i_gml_parsedata_t* user_defined,
+                                 void* scanner);
+int igraph_gml_yylex_destroy (void *scanner );
+int igraph_gml_yyparse (igraph_i_gml_parsedata_t* context);
+void igraph_gml_yyset_in  (FILE * in_str, void* yyscanner );
+
+void igraph_i_gml_destroy_attrs(igraph_vector_ptr_t **ptr) {
+    long int i;
+    igraph_vector_ptr_t *vec;
+    for (i = 0; i < 3; i++) {
+        long int j;
+        vec = ptr[i];
+        for (j = 0; j < igraph_vector_ptr_size(vec); j++) {
+            igraph_attribute_record_t *atrec = VECTOR(*vec)[j];
+            if (atrec->type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_vector_t *value = (igraph_vector_t*)atrec->value;
+                if (value != 0) {
+                    igraph_vector_destroy(value);
+                    igraph_Free(value);
+                }
+            } else {
+                igraph_strvector_t *value = (igraph_strvector_t*)atrec->value;
+                if (value != 0) {
+                    igraph_strvector_destroy(value);
+                    igraph_Free(value);
+                }
+            }
+            igraph_Free(atrec->name);
+            igraph_Free(atrec);
+        }
+        igraph_vector_ptr_destroy(vec);
+    }
+}
+
+igraph_real_t igraph_i_gml_toreal(igraph_gml_tree_t *node, long int pos) {
+
+    igraph_real_t value = 0.0;
+    int type = igraph_gml_tree_type(node, pos);
+
+    switch (type) {
+    case IGRAPH_I_GML_TREE_INTEGER:
+        value = igraph_gml_tree_get_integer(node, pos);
+        break;
+    case IGRAPH_I_GML_TREE_REAL:
+        value = igraph_gml_tree_get_real(node, pos);
+        break;
+    default:
+        IGRAPH_ERROR("Internal error while parsing GML file", IGRAPH_FAILURE);
+        break;
+    }
+
+    return value;
+}
+
+const char *igraph_i_gml_tostring(igraph_gml_tree_t *node, long int pos) {
+
+    int type = igraph_gml_tree_type(node, pos);
+    char tmp[256];
+    const char *p = tmp;
+    long int i;
+    igraph_real_t d;
+
+    switch (type) {
+    case IGRAPH_I_GML_TREE_INTEGER:
+        i = igraph_gml_tree_get_integer(node, pos);
+        snprintf(tmp, sizeof(tmp) / sizeof(char), "%li", i);
+        break;
+    case IGRAPH_I_GML_TREE_REAL:
+        d = igraph_gml_tree_get_real(node, pos);
+        igraph_real_snprintf_precise(tmp, sizeof(tmp) / sizeof(char), d);
+        break;
+    case IGRAPH_I_GML_TREE_STRING:
+        p = igraph_gml_tree_get_string(node, pos);
+        break;
+    default:
+        break;
+    }
+
+    return p;
+}
+
+/**
+ * \function igraph_read_graph_gml
+ * \brief Read a graph in GML format.
+ *
+ * GML is a simple textual format, see
+ * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html for details.
+ *
+ * </para><para>
+ * Although all syntactically correct GML can be parsed,
+ * we implement only a subset of this format, some attributes might be
+ * ignored. Here is a list of all the differences:
+ * \olist
+ * \oli Only <code>node</code> and <code>edge</code> attributes are
+ *      used, and only if they have a simple type: integer, real or
+ *      string. So if an attribute is an array or a record, then it is
+ *      ignored. This is also true if only some values of the
+ *      attribute are complex.
+ * \oli Top level attributes except for <code>Version</code> and the
+ *      first <code>graph</code> attribute are completely ignored.
+ * \oli Graph attributes except for <code>node</code> and
+ *      <code>edge</code> are completely ignored.
+ * \oli There is no maximum line length.
+ * \oli There is no maximum keyword length.
+ * \oli Character entities in strings are not interpreted.
+ * \oli We allow <code>inf</code> (infinity) and <code>nan</code>
+ *      (not a number) as a real number. This is case insensitive, so
+ *      <code>nan</code>, <code>NaN</code> and <code>NAN</code> are equal.
+ * \endolist
+ *
+ * </para><para> Please contact us if you cannot live with these
+ * limitations of the GML parser.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param instream The stream to read the GML file from.
+ * \return Error code.
+ *
+ * Time complexity: should be proportional to the length of the file.
+ *
+ * \sa \ref igraph_read_graph_graphml() for a more modern format,
+ * \ref igraph_write_graph_gml() for writing GML files.
+ *
+ * \example examples/simple/gml.c
+ */
+
+int igraph_read_graph_gml(igraph_t *graph, FILE *instream) {
+
+    long int i, p;
+    long int no_of_nodes = 0, no_of_edges = 0;
+    igraph_trie_t trie;
+    igraph_vector_t edges;
+    igraph_bool_t directed = IGRAPH_UNDIRECTED;
+    igraph_gml_tree_t *gtree;
+    long int gidx;
+    igraph_trie_t vattrnames;
+    igraph_trie_t eattrnames;
+    igraph_trie_t gattrnames;
+    igraph_vector_ptr_t gattrs = IGRAPH_VECTOR_PTR_NULL,
+                        vattrs = IGRAPH_VECTOR_PTR_NULL, eattrs = IGRAPH_VECTOR_PTR_NULL;
+    igraph_vector_ptr_t *attrs[3];
+    long int edgeptr = 0;
+    igraph_i_gml_parsedata_t context;
+
+    attrs[0] = &gattrs; attrs[1] = &vattrs; attrs[2] = &eattrs;
+
+    context.eof = 0;
+    context.tree = 0;
+
+    igraph_gml_yylex_init_extra(&context, &context.scanner);
+    IGRAPH_FINALLY(igraph_gml_yylex_destroy, context.scanner);
+
+    igraph_gml_yyset_in(instream, context.scanner);
+
+    i = igraph_gml_yyparse(&context);
+    if (i != 0) {
+        if (context.errmsg[0] != 0) {
+            IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR);
+        } else {
+            IGRAPH_ERROR("Cannot read GML file", IGRAPH_PARSEERROR);
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    /* Check version, if present, integer and not '1' then ignored */
+    i = igraph_gml_tree_find(context.tree, "Version", 0);
+    if (i >= 0 &&
+        igraph_gml_tree_type(context.tree, i) == IGRAPH_I_GML_TREE_INTEGER &&
+        igraph_gml_tree_get_integer(context.tree, i) != 1) {
+        igraph_gml_tree_destroy(context.tree);
+        IGRAPH_ERROR("Unknown GML version", IGRAPH_UNIMPLEMENTED);
+        /* RETURN HERE!!!! */
+    }
+
+    /* get the graph */
+    gidx = igraph_gml_tree_find(context.tree, "graph", 0);
+    if (gidx == -1) {
+        IGRAPH_ERROR("No 'graph' object in GML file", IGRAPH_PARSEERROR);
+    }
+    if (igraph_gml_tree_type(context.tree, gidx) !=
+        IGRAPH_I_GML_TREE_TREE) {
+        IGRAPH_ERROR("Invalid type for 'graph' object in GML file", IGRAPH_PARSEERROR);
+    }
+    gtree = igraph_gml_tree_get_tree(context.tree, gidx);
+
+    IGRAPH_FINALLY(igraph_i_gml_destroy_attrs, &attrs);
+    igraph_vector_ptr_init(&gattrs, 0);
+    igraph_vector_ptr_init(&vattrs, 0);
+    igraph_vector_ptr_init(&eattrs, 0);
+
+    IGRAPH_TRIE_INIT_FINALLY(&trie, 0);
+    IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 0);
+    IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 0);
+    IGRAPH_TRIE_INIT_FINALLY(&gattrnames, 0);
+
+    /* Is is directed? */
+    i = igraph_gml_tree_find(gtree, "directed", 0);
+    if (i >= 0 && igraph_gml_tree_type(gtree, i) == IGRAPH_I_GML_TREE_INTEGER) {
+        if (igraph_gml_tree_get_integer(gtree, i) == 1) {
+            directed = IGRAPH_DIRECTED;
+        }
+    }
+
+    /* Now we go over all objects in the graph and collect the attribute names and
+       types. Plus we collect node ids. We also do some checks. */
+    for (i = 0; i < igraph_gml_tree_length(gtree); i++) {
+        long int j;
+        char cname[100];
+        const char *name = igraph_gml_tree_name(gtree, i);
+        if (!strcmp(name, "node")) {
+            igraph_gml_tree_t *node;
+            igraph_bool_t hasid;
+            no_of_nodes++;
+            if (igraph_gml_tree_type(gtree, i) != IGRAPH_I_GML_TREE_TREE) {
+                IGRAPH_ERROR("'node' is not a list", IGRAPH_PARSEERROR);
+            }
+            node = igraph_gml_tree_get_tree(gtree, i);
+            hasid = 0;
+            for (j = 0; j < igraph_gml_tree_length(node); j++) {
+                const char *name = igraph_gml_tree_name(node, j);
+                long int trieid, triesize = igraph_trie_size(&vattrnames);
+                IGRAPH_CHECK(igraph_trie_get(&vattrnames, name, &trieid));
+                if (trieid == triesize) {
+                    /* new attribute */
+                    igraph_attribute_record_t *atrec = igraph_Calloc(1, igraph_attribute_record_t);
+                    int type = igraph_gml_tree_type(node, j);
+                    if (!atrec) {
+                        IGRAPH_ERROR("Cannot read GML file", IGRAPH_ENOMEM);
+                    }
+                    IGRAPH_CHECK(igraph_vector_ptr_push_back(&vattrs, atrec));
+                    atrec->name = strdup(name);
+                    if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) {
+                        atrec->type = IGRAPH_ATTRIBUTE_NUMERIC;
+                    } else {
+                        atrec->type = IGRAPH_ATTRIBUTE_STRING;
+                    }
+                } else {
+                    /* already seen, should we update type? */
+                    igraph_attribute_record_t *atrec = VECTOR(vattrs)[trieid];
+                    int type1 = atrec->type;
+                    int type2 = igraph_gml_tree_type(node, j);
+                    if (type1 == IGRAPH_ATTRIBUTE_NUMERIC && type2 == IGRAPH_I_GML_TREE_STRING) {
+                        atrec->type = IGRAPH_ATTRIBUTE_STRING;
+                    }
+                }
+                /* check id */
+                if (!hasid && !strcmp(name, "id")) {
+                    long int id;
+                    if (igraph_gml_tree_type(node, j) != IGRAPH_I_GML_TREE_INTEGER) {
+                        IGRAPH_ERROR("Non-integer node id in GML file", IGRAPH_PARSEERROR);
+                    }
+                    id = igraph_gml_tree_get_integer(node, j);
+                    snprintf(cname, sizeof(cname) / sizeof(char) -1, "%li", id);
+                    IGRAPH_CHECK(igraph_trie_get(&trie, cname, &id));
+                    hasid = 1;
+                }
+            }
+            if (!hasid) {
+                IGRAPH_ERROR("Node without 'id' while parsing GML file", IGRAPH_PARSEERROR);
+            }
+        } else if (!strcmp(name, "edge")) {
+            igraph_gml_tree_t *edge;
+            igraph_bool_t has_source = 0, has_target = 0;
+            no_of_edges++;
+            if (igraph_gml_tree_type(gtree, i) != IGRAPH_I_GML_TREE_TREE) {
+                IGRAPH_ERROR("'edge' is not a list", IGRAPH_PARSEERROR);
+            }
+            edge = igraph_gml_tree_get_tree(gtree, i);
+            has_source = has_target = 0;
+            for (j = 0; j < igraph_gml_tree_length(edge); j++) {
+                const char *name = igraph_gml_tree_name(edge, j);
+                if (!strcmp(name, "source")) {
+                    has_source = 1;
+                    if (igraph_gml_tree_type(edge, j) != IGRAPH_I_GML_TREE_INTEGER) {
+                        IGRAPH_ERROR("Non-integer 'source' for an edge in GML file",
+                                     IGRAPH_PARSEERROR);
+                    }
+                } else if (!strcmp(name, "target")) {
+                    has_target = 1;
+                    if (igraph_gml_tree_type(edge, j) != IGRAPH_I_GML_TREE_INTEGER) {
+                        IGRAPH_ERROR("Non-integer 'source' for an edge in GML file",
+                                     IGRAPH_PARSEERROR);
+                    }
+                } else {
+                    long int trieid, triesize = igraph_trie_size(&eattrnames);
+                    IGRAPH_CHECK(igraph_trie_get(&eattrnames, name, &trieid));
+                    if (trieid == triesize) {
+                        /* new attribute */
+                        igraph_attribute_record_t *atrec = igraph_Calloc(1, igraph_attribute_record_t);
+                        int type = igraph_gml_tree_type(edge, j);
+                        if (!atrec) {
+                            IGRAPH_ERROR("Cannot read GML file", IGRAPH_ENOMEM);
+                        }
+                        IGRAPH_CHECK(igraph_vector_ptr_push_back(&eattrs, atrec));
+                        atrec->name = strdup(name);
+                        if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) {
+                            atrec->type = IGRAPH_ATTRIBUTE_NUMERIC;
+                        } else {
+                            atrec->type = IGRAPH_ATTRIBUTE_STRING;
+                        }
+                    } else {
+                        /* already seen, should we update type? */
+                        igraph_attribute_record_t *atrec = VECTOR(eattrs)[trieid];
+                        int type1 = atrec->type;
+                        int type2 = igraph_gml_tree_type(edge, j);
+                        if (type1 == IGRAPH_ATTRIBUTE_NUMERIC && type2 == IGRAPH_I_GML_TREE_STRING) {
+                            atrec->type = IGRAPH_ATTRIBUTE_STRING;
+                        }
+                    }
+                }
+            } /* for */
+            if (!has_source) {
+                IGRAPH_ERROR("No 'source' for edge in GML file", IGRAPH_PARSEERROR);
+            }
+            if (!has_target) {
+                IGRAPH_ERROR("No 'target' for edge in GML file", IGRAPH_PARSEERROR);
+            }
+        } else {
+            /* anything to do? Maybe add as graph attribute.... */
+        }
+    }
+
+    /* check vertex id uniqueness */
+    if (igraph_trie_size(&trie) != no_of_nodes) {
+        IGRAPH_ERROR("Node 'id' not unique", IGRAPH_PARSEERROR);
+    }
+
+    /* now we allocate the vectors and strvectors for the attributes */
+    for (i = 0; i < igraph_vector_ptr_size(&vattrs); i++) {
+        igraph_attribute_record_t *atrec = VECTOR(vattrs)[i];
+        int type = atrec->type;
+        if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+            igraph_vector_t *p = igraph_Calloc(1, igraph_vector_t);
+            atrec->value = p;
+            IGRAPH_CHECK(igraph_vector_init(p, no_of_nodes));
+        } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+            igraph_strvector_t *p = igraph_Calloc(1, igraph_strvector_t);
+            atrec->value = p;
+            IGRAPH_CHECK(igraph_strvector_init(p, no_of_nodes));
+        } else {
+            IGRAPH_WARNING("A composite attribute ignored");
+        }
+    }
+
+    for (i = 0; i < igraph_vector_ptr_size(&eattrs); i++) {
+        igraph_attribute_record_t *atrec = VECTOR(eattrs)[i];
+        int type = atrec->type;
+        if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+            igraph_vector_t *p = igraph_Calloc(1, igraph_vector_t);
+            atrec->value = p;
+            IGRAPH_CHECK(igraph_vector_init(p, no_of_edges));
+        } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+            igraph_strvector_t *p = igraph_Calloc(1, igraph_strvector_t);
+            atrec->value = p;
+            IGRAPH_CHECK(igraph_strvector_init(p, no_of_edges));
+        } else {
+            IGRAPH_WARNING("A composite attribute ignored");
+        }
+    }
+
+    /* Ok, now the edges, attributes too */
+    IGRAPH_CHECK(igraph_vector_resize(&edges, no_of_edges * 2));
+    p = -1;
+    while ( (p = igraph_gml_tree_find(gtree, "edge", p + 1)) != -1) {
+        igraph_gml_tree_t *edge;
+        long int from, to, fromidx = 0, toidx = 0;
+        char name[100];
+        long int j;
+        edge = igraph_gml_tree_get_tree(gtree, p);
+        for (j = 0; j < igraph_gml_tree_length(edge); j++) {
+            const char *n = igraph_gml_tree_name(edge, j);
+            if (!strcmp(n, "source")) {
+                fromidx = igraph_gml_tree_find(edge, "source", 0);
+            } else if (!strcmp(n, "target")) {
+                toidx = igraph_gml_tree_find(edge, "target", 0);
+            } else {
+                long int edgeid = edgeptr / 2;
+                long int trieidx;
+                igraph_attribute_record_t *atrec;
+                int type;
+                igraph_trie_get(&eattrnames, n, &trieidx);
+                atrec = VECTOR(eattrs)[trieidx];
+                type = atrec->type;
+                if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                    igraph_vector_t *v = (igraph_vector_t *)atrec->value;
+                    VECTOR(*v)[edgeid] = igraph_i_gml_toreal(edge, j);
+                } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+                    igraph_strvector_t *v = (igraph_strvector_t *)atrec->value;
+                    const char *value = igraph_i_gml_tostring(edge, j);
+                    IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value));
+                }
+            }
+        }
+        from = igraph_gml_tree_get_integer(edge, fromidx);
+        to = igraph_gml_tree_get_integer(edge, toidx);
+        snprintf(name, sizeof(name) / sizeof(char) -1, "%li", from);
+        IGRAPH_CHECK(igraph_trie_get(&trie, name, &from));
+        snprintf(name, sizeof(name) / sizeof(char) -1, "%li", to);
+        IGRAPH_CHECK(igraph_trie_get(&trie, name, &to));
+        if (igraph_trie_size(&trie) != no_of_nodes) {
+            IGRAPH_ERROR("Unknown node id found at an edge", IGRAPH_PARSEERROR);
+        }
+        VECTOR(edges)[edgeptr++] = from;
+        VECTOR(edges)[edgeptr++] = to;
+    }
+
+    /* and add vertex attributes */
+    for (i = 0; i < igraph_gml_tree_length(gtree); i++) {
+        const char *n;
+        char name[100];
+        long int j, k;
+        n = igraph_gml_tree_name(gtree, i);
+        if (!strcmp(n, "node")) {
+            igraph_gml_tree_t *node = igraph_gml_tree_get_tree(gtree, i);
+            long int iidx = igraph_gml_tree_find(node, "id", 0);
+            long int id = igraph_gml_tree_get_integer(node, iidx);
+            snprintf(name, sizeof(name) / sizeof(char) -1, "%li", id);
+            igraph_trie_get(&trie, name, &id);
+            for (j = 0; j < igraph_gml_tree_length(node); j++) {
+                const char *aname = igraph_gml_tree_name(node, j);
+                igraph_attribute_record_t *atrec;
+                int type;
+                igraph_trie_get(&vattrnames, aname, &k);
+                atrec = VECTOR(vattrs)[k];
+                type = atrec->type;
+                if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                    igraph_vector_t *v = (igraph_vector_t *)atrec->value;
+                    VECTOR(*v)[id] = igraph_i_gml_toreal(node, j);
+                } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+                    igraph_strvector_t *v = (igraph_strvector_t *)atrec->value;
+                    const char *value = igraph_i_gml_tostring(node, j);
+                    IGRAPH_CHECK(igraph_strvector_set(v, id, value));
+                }
+            }
+        }
+    }
+
+    igraph_gml_tree_destroy(context.tree);
+
+    igraph_trie_destroy(&trie);
+    igraph_trie_destroy(&gattrnames);
+    igraph_trie_destroy(&vattrnames);
+    igraph_trie_destroy(&eattrnames);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    IGRAPH_CHECK(igraph_empty_attrs(graph, 0, directed, 0)); /* TODO */
+    IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) no_of_nodes,
+                                     &vattrs));
+    IGRAPH_CHECK(igraph_add_edges(graph, &edges, &eattrs));
+
+    igraph_i_gml_destroy_attrs(attrs);
+    igraph_vector_destroy(&edges);
+    igraph_gml_yylex_destroy(context.scanner);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \ingroup loadsave
+ * \function igraph_write_graph_edgelist
+ * \brief Writes the edge list of a graph to a file.
+ *
+ * </para><para>
+ * One edge is written per line, separated by a single space.
+ * For directed graphs edges are written in from, to order.
+ * \param graph The graph object to write.
+ * \param outstream Pointer to a stream, it should be writable.
+ * \return Error code:
+ *         \c IGRAPH_EFILE if there is an error writing the
+ *         file.
+ *
+ * Time complexity: O(|E|), the
+ * number of edges in the  graph. It is assumed that writing an
+ * integer to the file requires O(1)
+ * time.
+ */
+
+int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) {
+
+    igraph_eit_t it;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM),
+                                   &it));
+    IGRAPH_FINALLY(igraph_eit_destroy, &it);
+
+    while (!IGRAPH_EIT_END(it)) {
+        igraph_integer_t from, to;
+        int ret;
+        igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to);
+        ret = fprintf(outstream, "%li %li\n",
+                      (long int) from,
+                      (long int) to);
+        if (ret < 0) {
+            IGRAPH_ERROR("Write error", IGRAPH_EFILE);
+        }
+        IGRAPH_EIT_NEXT(it);
+    }
+
+    igraph_eit_destroy(&it);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \ingroup loadsave
+ * \function igraph_write_graph_ncol
+ * \brief Writes the graph to a file in <code>.ncol</code> format
+ *
+ * </para><para>
+ * <code>.ncol</code> is a format used by LGL, see \ref
+ * igraph_read_graph_ncol() for details.
+ *
+ * </para><para>
+ * Note that having multiple or loop edges in an
+ * <code>.ncol</code> file breaks the  LGL software but
+ * \a igraph does not check for this condition.
+ * \param graph The graph to write.
+ * \param outstream The stream object to write to, it should be
+ *        writable.
+ * \param names The name of the vertex attribute, if symbolic names
+ *        are written to the file. If not, supply 0 here.
+ * \param weights The name of the edge attribute, if they are also
+ *        written to the file. If you don't want weights, supply 0
+ *        here.
+ * \return Error code:
+ *         \c IGRAPH_EFILE if there is an error writing the
+ *         file.
+ *
+ * Time complexity: O(|E|), the
+ * number of edges. All file operations are expected to have time
+ * complexity O(1).
+ *
+ * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl()
+ */
+
+int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream,
+                            const char *names, const char *weights) {
+    igraph_eit_t it;
+    igraph_attribute_type_t nametype, weighttype;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM),
+                                   &it));
+    IGRAPH_FINALLY(igraph_eit_destroy, &it);
+
+    /* Check if we have the names attribute */
+    if (names && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX,
+            names)) {
+        names = 0;
+        IGRAPH_WARNING("names attribute does not exists");
+    }
+    if (names) {
+        IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype,
+                                                IGRAPH_ATTRIBUTE_VERTEX, names));
+    }
+    if (names && nametype != IGRAPH_ATTRIBUTE_NUMERIC &&
+        nametype != IGRAPH_ATTRIBUTE_STRING) {
+        IGRAPH_WARNING("ignoring names attribute, unknown attribute type");
+        names = 0;
+    }
+
+    /* Check the weights as well */
+    if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE,
+            weights)) {
+        weights = 0;
+        IGRAPH_WARNING("weights attribute does not exists");
+    }
+    if (weights) {
+        IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype,
+                                                IGRAPH_ATTRIBUTE_EDGE, weights));
+    }
+    if (weights && weighttype != IGRAPH_ATTRIBUTE_NUMERIC) {
+        IGRAPH_WARNING("ignoring weights attribute, unknown attribute type");
+        weights = 0;
+    }
+
+    if (names == 0 && weights == 0) {
+        /* No names, no weights */
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_integer_t from, to;
+            int ret;
+            igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to);
+            ret = fprintf(outstream, "%li %li\n",
+                          (long int) from,
+                          (long int) to);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            IGRAPH_EIT_NEXT(it);
+        }
+    } else if (weights == 0) {
+        /* No weights, but use names */
+        igraph_strvector_t nvec;
+        IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph)));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &nvec);
+        IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names,
+                     igraph_vss_all(),
+                     &nvec));
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_integer_t edge = IGRAPH_EIT_GET(it);
+            igraph_integer_t from, to;
+            int ret = 0;
+            char *str1, *str2;
+            igraph_edge(graph, edge, &from, &to);
+            igraph_strvector_get(&nvec, from, &str1);
+            igraph_strvector_get(&nvec, to, &str2);
+            ret = fprintf(outstream, "%s %s\n", str1, str2);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            IGRAPH_EIT_NEXT(it);
+        }
+        igraph_strvector_destroy(&nvec);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else if (names == 0) {
+        /* No names but weights */
+        igraph_vector_t wvec;
+        IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph));
+        IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, weights,
+                     igraph_ess_all(IGRAPH_EDGEORDER_ID),
+                     &wvec));
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_integer_t edge = IGRAPH_EIT_GET(it);
+            igraph_integer_t from, to;
+            int ret1, ret2, ret3;
+            igraph_edge(graph, edge, &from, &to);
+            ret1 = fprintf(outstream, "%li %li ",
+                           (long int)from, (long int)to);
+            ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]);
+            ret3 = fputc('\n', outstream);
+            if (ret1 < 0 || ret2 < 0 || ret3 == EOF) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            IGRAPH_EIT_NEXT(it);
+        }
+        igraph_vector_destroy(&wvec);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        /* Both names and weights */
+        igraph_strvector_t nvec;
+        igraph_vector_t wvec;
+        IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph));
+        IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph)));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &nvec);
+        IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, weights,
+                     igraph_ess_all(IGRAPH_EDGEORDER_ID),
+                     &wvec));
+        IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names,
+                     igraph_vss_all(),
+                     &nvec));
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_integer_t edge = IGRAPH_EIT_GET(it);
+            igraph_integer_t from, to;
+            int ret = 0, ret2 = 0;
+            char *str1, *str2;
+            igraph_edge(graph, edge, &from, &to);
+            igraph_strvector_get(&nvec, from, &str1);
+            igraph_strvector_get(&nvec, to, &str2);
+            ret = fprintf(outstream, "%s %s ", str1, str2);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]);
+            ret2 = fputc('\n', outstream);
+            if (ret < 0 || ret2 == EOF) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            IGRAPH_EIT_NEXT(it);
+        }
+        igraph_strvector_destroy(&nvec);
+        igraph_vector_destroy(&wvec);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_eit_destroy(&it);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \ingroup loadsave
+ * \function igraph_write_graph_lgl
+ * \brief Writes the graph to a file in <code>.lgl</code> format
+ *
+ * </para><para>
+ * <code>.lgl</code> is a format used by LGL, see \ref
+ * igraph_read_graph_lgl() for details.
+ *
+ * </para><para>
+ * Note that having multiple or loop edges in an
+ * <code>.lgl</code> file breaks the  LGL software but \a igraph
+ * does not check for this condition.
+ * \param graph The graph to write.
+ * \param outstream The stream object to write to, it should be
+ *        writable.
+ * \param names The name of the vertex attribute, if symbolic names
+ *        are written to the file. If not supply 0 here.
+ * \param weights The name of the edge attribute, if they are also
+ *        written to the file. If you don't want weights supply 0
+ *        here.
+ * \param isolates Logical, if TRUE isolated vertices are also written
+ *        to the file. If FALSE they will be omitted.
+ * \return Error code:
+ *         \c IGRAPH_EFILE if there is an error
+ *         writing the file.
+ *
+ * Time complexity: O(|E|), the
+ * number of edges if \p isolates is
+ * FALSE, O(|V|+|E|) otherwise. All
+ * file operations are expected to have time complexity
+ * O(1).
+ *
+ * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol()
+ *
+ * \example examples/simple/igraph_write_graph_lgl.c
+ */
+
+int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream,
+                           const char *names, const char *weights,
+                           igraph_bool_t isolates) {
+    igraph_eit_t it;
+    long int actvertex = -1;
+    igraph_attribute_type_t nametype, weighttype;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM),
+                                   &it));
+    IGRAPH_FINALLY(igraph_eit_destroy, &it);
+
+    /* Check if we have the names attribute */
+    if (names && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX,
+            names)) {
+        names = 0;
+        IGRAPH_WARNING("names attribute does not exists");
+    }
+    if (names) {
+        IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype,
+                                                IGRAPH_ATTRIBUTE_VERTEX, names));
+    }
+    if (names && nametype != IGRAPH_ATTRIBUTE_NUMERIC &&
+        nametype != IGRAPH_ATTRIBUTE_STRING) {
+        IGRAPH_WARNING("ignoring names attribute, unknown attribute type");
+        names = 0;
+    }
+
+    /* Check the weights as well */
+    if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE,
+            weights)) {
+        weights = 0;
+        IGRAPH_WARNING("weights attribute does not exists");
+    }
+    if (weights) {
+        IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype,
+                                                IGRAPH_ATTRIBUTE_EDGE, weights));
+    }
+    if (weights && weighttype != IGRAPH_ATTRIBUTE_NUMERIC &&
+        weighttype != IGRAPH_ATTRIBUTE_STRING) {
+        IGRAPH_WARNING("ignoring weights attribute, unknown attribute type");
+        weights = 0;
+    }
+
+    if (names == 0 && weights == 0) {
+        /* No names, no weights */
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_integer_t from, to;
+            int ret;
+            igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to);
+            if (from == actvertex) {
+                ret = fprintf(outstream, "%li\n", (long int)to);
+            } else {
+                actvertex = from;
+                ret = fprintf(outstream, "# %li\n%li\n", (long int)from, (long int)to);
+            }
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            IGRAPH_EIT_NEXT(it);
+        }
+    } else if (weights == 0) {
+        /* No weights but use names */
+        igraph_strvector_t nvec;
+        IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph)));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &nvec);
+        IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names,
+                     igraph_vss_all(),
+                     &nvec));
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_integer_t edge = IGRAPH_EIT_GET(it);
+            igraph_integer_t from, to;
+            int ret = 0;
+            char *str1, *str2;
+            igraph_edge(graph, edge, &from, &to);
+            igraph_strvector_get(&nvec, to, &str2);
+
+            if (from == actvertex) {
+                ret = fprintf(outstream, "%s\n", str2);
+            } else {
+                actvertex = from;
+                igraph_strvector_get(&nvec, from, &str1);
+                ret = fprintf(outstream, "# %s\n%s\n", str1, str2);
+            }
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            IGRAPH_EIT_NEXT(it);
+        }
+        IGRAPH_FINALLY_CLEAN(1);
+    } else if (names == 0) {
+        igraph_strvector_t wvec;
+        IGRAPH_CHECK(igraph_strvector_init(&wvec, igraph_ecount(graph)));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &wvec);
+        IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, weights,
+                     igraph_ess_all(IGRAPH_EDGEORDER_ID),
+                     &wvec));
+        /* No names but weights */
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_integer_t edge = IGRAPH_EIT_GET(it);
+            igraph_integer_t from, to;
+            int ret = 0;
+            char *str1;
+            igraph_edge(graph, edge, &from, &to);
+            igraph_strvector_get(&wvec, edge, &str1);
+            if (from == actvertex) {
+                ret = fprintf(outstream, "%li %s\n", (long)to, str1);
+            } else {
+                actvertex = from;
+                ret = fprintf(outstream, "# %li\n%li %s\n", (long)from, (long)to, str1);
+            }
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            IGRAPH_EIT_NEXT(it);
+        }
+        igraph_strvector_destroy(&wvec);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        /* Both names and weights */
+        igraph_strvector_t nvec, wvec;
+        IGRAPH_CHECK(igraph_strvector_init(&wvec, igraph_ecount(graph)));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &wvec);
+        IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph)));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &nvec);
+        IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, weights,
+                     igraph_ess_all(IGRAPH_EDGEORDER_ID),
+                     &wvec));
+        IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names,
+                     igraph_vss_all(),
+                     &nvec));
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_integer_t edge = IGRAPH_EIT_GET(it);
+            igraph_integer_t from, to;
+            int ret = 0;
+            char *str1, *str2, *str3;
+            igraph_edge(graph, edge, &from, &to);
+            igraph_strvector_get(&nvec, to, &str2);
+            igraph_strvector_get(&wvec, edge, &str3);
+            if (from == actvertex) {
+                ret = fprintf(outstream, "%s ", str2);
+            } else {
+                actvertex = from;
+                igraph_strvector_get(&nvec, from, &str1);
+                ret = fprintf(outstream, "# %s\n%s ", str1, str2);
+            }
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            ret = fprintf(outstream, "%s\n", str3);
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+            IGRAPH_EIT_NEXT(it);
+        }
+        igraph_strvector_destroy(&nvec);
+        igraph_strvector_destroy(&wvec);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    if (isolates) {
+        long int nov = igraph_vcount(graph);
+        long int i;
+        int ret = 0;
+        igraph_vector_t deg;
+        igraph_strvector_t nvec;
+        char *str;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&deg, 1);
+        IGRAPH_CHECK(igraph_strvector_init(&nvec, 1));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &nvec);
+        for (i = 0; i < nov; i++) {
+            igraph_degree(graph, &deg, igraph_vss_1((igraph_integer_t) i),
+                          IGRAPH_ALL, IGRAPH_LOOPS);
+            if (VECTOR(deg)[0] == 0) {
+                if (names == 0) {
+                    ret = fprintf(outstream, "# %li\n", i);
+                } else {
+                    IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names,
+                                 igraph_vss_1((igraph_integer_t) i), &nvec));
+                    igraph_strvector_get(&nvec, 0, &str);
+                    ret = fprintf(outstream, "# %s\n", str);
+                }
+            }
+            if (ret < 0) {
+                IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
+            }
+        }
+        igraph_strvector_destroy(&nvec);
+        igraph_vector_destroy(&deg);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_eit_destroy(&it);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/* Order matters here! */
+#define V_ID                0
+#define V_X                 1
+#define V_Y                 2
+#define V_Z                 3
+#define V_SHAPE             4
+#define V_XFACT             5
+#define V_YFACT             6
+#define V_COLOR_RED         7
+#define V_COLOR_GREEN       8
+#define V_COLOR_BLUE        9
+#define V_FRAMECOLOR_RED   10
+#define V_FRAMECOLOR_GREEN 11
+#define V_FRAMECOLOR_BLUE  12
+#define V_LABELCOLOR_RED   13
+#define V_LABELCOLOR_GREEN 14
+#define V_LABELCOLOR_BLUE  15
+#define V_LABELDIST        16
+#define V_LABELDEGREE2     17
+#define V_FRAMEWIDTH       18
+#define V_FONTSIZE         19
+#define V_ROTATION         20
+#define V_RADIUS           21
+#define V_DIAMONDRATIO     22
+#define V_LABELDEGREE      23
+#define V_VERTEXSIZE       24
+#define V_FONT             25
+#define V_URL              26
+#define V_COLOR            27
+#define V_FRAMECOLOR       28
+#define V_LABELCOLOR       29
+#define V_LAST             30
+
+#define E_WEIGHT            0
+#define E_COLOR_RED         1
+#define E_COLOR_GREEN       2
+#define E_COLOR_BLUE        3
+#define E_ARROWSIZE         4
+#define E_EDGEWIDTH         5
+#define E_HOOK1             6
+#define E_HOOK2             7
+#define E_ANGLE1            8
+#define E_ANGLE2            9
+#define E_VELOCITY1        10
+#define E_VELOCITY2        11
+#define E_ARROWPOS         12
+#define E_LABELPOS         13
+#define E_LABELANGLE       14
+#define E_LABELANGLE2      15
+#define E_LABELDEGREE      16
+#define E_FONTSIZE         17
+#define E_ARROWTYPE        18
+#define E_LINEPATTERN      19
+#define E_LABEL            20
+#define E_LABELCOLOR       21
+#define E_COLOR            22
+#define E_LAST             23
+
+int igraph_i_pajek_escape(char* src, char** dest) {
+    long int destlen = 0;
+    igraph_bool_t need_escape = 0;
+
+    /* Determine whether the string contains characters to be escaped */
+    char *s, *d;
+    for (s = src; *s; s++, destlen++) {
+        if (*s == '\\') {
+            need_escape = 1;
+            destlen++;
+        } else if (*s == '"') {
+            need_escape = 1;
+            destlen++;
+        } else if (!isalnum(*s)) {
+            need_escape = 1;
+        }
+    }
+
+    if (!need_escape) {
+        /* At this point, we know that the string does not contain any chars
+         * that would warrant escaping. Therefore, we simply quote it and
+         * return the quoted string. This is necessary because Pajek uses some
+         * reserved words in its format (like 'c' standing for color) and they
+         * have to be quoted as well.
+         */
+        *dest = igraph_Calloc(destlen + 3, char);
+        if (!*dest) {
+            IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM);
+        }
+
+        d = *dest;
+        strcpy(d + 1, src);
+        d[0] = d[destlen + 1] = '"';
+        d[destlen + 2] = 0;
+        return IGRAPH_SUCCESS;
+    }
+
+    *dest = igraph_Calloc(destlen + 3, char);
+    if (!*dest) {
+        IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM);
+    }
+
+    d = *dest;
+    *d = '"'; d++;
+
+    for (s = src; *s; s++, d++) {
+        switch (*s) {
+        case '\\':
+        case '"':
+            *d = '\\'; d++;
+        default:
+            *d = *s;
+        }
+    }
+    *d = '"'; d++; *d = 0;
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_write_graph_pajek
+ * \brief Writes a graph to a file in Pajek format.
+ *
+ * </para><para>
+ * The Pajek vertex and edge parameters (like color) are determined by
+ * the attributes of the vertices and edges, of course this requires
+ * an attribute handler to be installed. The names of the
+ * corresponding vertex and edge attributes are listed at \ref
+ * igraph_read_graph_pajek(), eg. the `\c color' vertex attributes
+ * determines the color (`\c c' in Pajek) parameter.
+ *
+ * </para><para>
+ * As of version 0.6.1 igraph writes bipartite graphs into Pajek files
+ * correctly, i.e. they will be also bipartite when read into Pajek.
+ * As Pajek is less flexible for bipartite graphs (the numeric ids of
+ * the vertices must be sorted according to vertex type), igraph might
+ * need to reorder the vertices when writing a bipartite Pajek file.
+ * This effectively means that numeric vertex ids usually change when
+ * a bipartite graph is written to a Pajek file, and then read back
+ * into igraph.
+ * \param graph The graph object to write.
+ * \param outstream The file to write to. It should be opened and
+ * writable. Make sure that you open the file in binary format if you use MS Windows,
+ * otherwise end of line characters will be messed up. (igraph will be able
+ * to read back these messed up files, but Pajek won't.)
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|+|A|), |V| is the number of vertices, |E|
+ * is the number of edges, |A| the number of attributes (vertex +
+ * edge) in the graph if there are attribute handlers installed.
+ *
+ * \sa \ref igraph_read_graph_pajek() for reading Pajek graphs, \ref
+ * igraph_write_graph_graphml() for writing a graph in GraphML format,
+ * this suites <command>igraph</command> graphs better.
+ *
+ * \example examples/simple/igraph_write_graph_pajek.c
+ */
+
+int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, j;
+
+    igraph_attribute_type_t vtypes[V_LAST], etypes[E_LAST];
+    igraph_bool_t write_vertex_attrs = 0;
+
+    /* Same order as the #define's */
+    const char *vnames[] = { "id", "x", "y", "z", "shape", "xfact", "yfact",
+                             "", "", "", "", "", "", "", "", "",
+                             "labeldist", "labeldegree2", "framewidth",
+                             "fontsize", "rotation", "radius",
+                             "diamondratio", "labeldegree", "vertexsize",
+                             "font", "url", "color", "framecolor",
+                             "labelcolor"
+                           };
+
+    const char *vnumnames[] = { "xfact", "yfact", "labeldist",
+                                "labeldegree2", "framewidth", "fontsize",
+                                "rotation", "radius", "diamondratio",
+                                "labeldegree", "vertexsize"
+                              };
+    const char *vnumnames2[] = { "x_fact", "y_fact", "lr", "lphi", "bw",
+                                 "fos", "phi", "r", "q", "la", "size"
+                               };
+    const char *vstrnames[] = { "font", "url", "color", "framecolor",
+                                "labelcolor"
+                              };
+    const char *vstrnames2[] = { "font", "url", "ic", "bc", "lc" };
+
+    const char *enames[] = { "weight", "", "", "",
+                             "arrowsize", "edgewidth", "hook1", "hook2",
+                             "angle1", "angle2", "velocity1", "velocity2",
+                             "arrowpos", "labelpos", "labelangle",
+                             "labelangle2", "labeldegree", "fontsize",
+                             "arrowtype", "linepattern", "label", "labelcolor",
+                             "color"
+                           };
+    const char *enumnames[] = { "arrowsize", "edgewidth", "hook1", "hook2",
+                                "angle1", "angle2", "velocity1", "velocity2",
+                                "arrowpos", "labelpos", "labelangle",
+                                "labelangle2", "labeldegree", "fontsize"
+                              };
+    const char *enumnames2[] = { "s", "w", "h1", "h2", "a1", "a2", "k1", "k2",
+                                 "ap", "lp", "lr", "lphi", "la", "fos"
+                               };
+    const char *estrnames[] = { "arrowtype", "linepattern", "label",
+                                "labelcolor", "color"
+                              };
+    const char *estrnames2[] = { "a", "p", "l", "lc", "c" };
+
+    const char *newline = "\x0d\x0a";
+
+    igraph_es_t es;
+    igraph_eit_t eit;
+
+    igraph_vector_t numv;
+    igraph_strvector_t strv;
+
+    igraph_vector_t ex_numa;
+    igraph_vector_t ex_stra;
+    igraph_vector_t vx_numa;
+    igraph_vector_t vx_stra;
+
+    char *s, *escaped;
+
+    igraph_bool_t bipartite = 0;
+    igraph_vector_int_t bip_index, bip_index2;
+    igraph_vector_bool_t bvec;
+    long int notop = 0, nobottom = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&numv, 1);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&ex_numa, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&ex_stra, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&vx_numa, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&vx_stra, 0);
+
+    /* Check if graph is bipartite */
+    if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, "type")) {
+        igraph_attribute_type_t type_type;
+        igraph_i_attribute_gettype(graph, &type_type, IGRAPH_ATTRIBUTE_VERTEX,
+                                   "type");
+        if (type_type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            int bptr = 0, tptr = 0;
+            bipartite = 1; write_vertex_attrs = 1;
+            /* Count top and bottom vertices, we go over them twice,
+            because we want to keep their original order */
+            IGRAPH_CHECK(igraph_vector_int_init(&bip_index, no_of_nodes));
+            IGRAPH_FINALLY(igraph_vector_int_destroy, &bip_index);
+            IGRAPH_CHECK(igraph_vector_int_init(&bip_index2, no_of_nodes));
+            IGRAPH_FINALLY(igraph_vector_int_destroy, &bip_index2);
+            IGRAPH_CHECK(igraph_vector_bool_init(&bvec, 1));
+            IGRAPH_FINALLY(igraph_vector_bool_destroy, &bvec);
+            for (i = 0; i < no_of_nodes; i++) {
+                IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph,
+                             "type", igraph_vss_1((igraph_integer_t) i), &bvec));
+                if (VECTOR(bvec)[0]) {
+                    notop++;
+                } else {
+                    nobottom++;
+                }
+            }
+            for (i = 0, bptr = 0, tptr = (int) nobottom; i < no_of_nodes; i++) {
+                IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph,
+                             "type", igraph_vss_1((igraph_integer_t) i), &bvec));
+                if (VECTOR(bvec)[0]) {
+                    VECTOR(bip_index)[tptr] = (int) i;
+                    VECTOR(bip_index2)[i] = tptr;
+                    tptr++;
+                } else {
+                    VECTOR(bip_index)[bptr] = (int) i;
+                    VECTOR(bip_index2)[i] = bptr;
+                    bptr++;
+                }
+            }
+            igraph_vector_bool_destroy(&bvec);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    /* Write header */
+    if (bipartite) {
+        if (fprintf(outstream, "*Vertices %li %li%s", no_of_nodes, nobottom,
+                    newline) < 0) {
+            IGRAPH_ERROR("Cannot write pajek file", IGRAPH_EFILE);
+        }
+    } else {
+        if (fprintf(outstream, "*Vertices %li%s", no_of_nodes, newline) < 0) {
+            IGRAPH_ERROR("Cannot write pajek file", IGRAPH_EFILE);
+        }
+    }
+
+    /* Check the vertex attributes */
+    memset(vtypes, 0, sizeof(vtypes[0])*V_LAST);
+    for (i = 0; i < V_LAST; i++) {
+        if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX,
+                                        vnames[i])) {
+            igraph_i_attribute_gettype(graph, &vtypes[i], IGRAPH_ATTRIBUTE_VERTEX,
+                                       vnames[i]);
+            write_vertex_attrs = 1;
+        } else {
+            vtypes[i] = (igraph_attribute_type_t) -1;
+        }
+    }
+    for (i = 0; i < (long int) (sizeof(vnumnames) / sizeof(const char*)); i++) {
+        igraph_attribute_type_t type;
+        if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX,
+                                        vnumnames[i])) {
+            igraph_i_attribute_gettype(graph, &type, IGRAPH_ATTRIBUTE_VERTEX,
+                                       vnumnames[i]);
+            if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                IGRAPH_CHECK(igraph_vector_push_back(&vx_numa, i));
+            }
+        }
+    }
+    for (i = 0; i < (long int) (sizeof(vstrnames) / sizeof(const char*)); i++) {
+        igraph_attribute_type_t type;
+        if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX,
+                                        vstrnames[i])) {
+            igraph_i_attribute_gettype(graph, &type, IGRAPH_ATTRIBUTE_VERTEX,
+                                       vstrnames[i]);
+            if (type == IGRAPH_ATTRIBUTE_STRING) {
+                IGRAPH_CHECK(igraph_vector_push_back(&vx_stra, i));
+            }
+        }
+    }
+
+    /* Write vertices */
+    if (write_vertex_attrs) {
+        for (i = 0; i < no_of_nodes; i++) {
+            long int id = bipartite ? VECTOR(bip_index)[i] : i;
+
+            /* vertex id */
+            fprintf(outstream, "%li", i + 1);
+            if (vtypes[V_ID] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_ID],
+                        igraph_vss_1((igraph_integer_t) id), &numv);
+                fputs(" \"", outstream);
+                igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+                fputc('"', outstream);
+            } else if (vtypes[V_ID] == IGRAPH_ATTRIBUTE_STRING) {
+                igraph_i_attribute_get_string_vertex_attr(graph, vnames[V_ID],
+                        igraph_vss_1((igraph_integer_t) id), &strv);
+                igraph_strvector_get(&strv, 0, &s);
+                IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped));
+                fprintf(outstream, " %s", escaped);
+                igraph_Free(escaped);
+            } else {
+                fprintf(outstream, " \"%li\"", id + 1);
+            }
+
+            /* coordinates */
+            if (vtypes[V_X] == IGRAPH_ATTRIBUTE_NUMERIC &&
+                vtypes[V_Y] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_X],
+                        igraph_vss_1((igraph_integer_t) id), &numv);
+                fputc(' ', outstream);
+                igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+                igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_Y],
+                        igraph_vss_1((igraph_integer_t) id), &numv);
+                fputc(' ', outstream);
+                igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+                if (vtypes[V_Z] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                    igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_Z],
+                            igraph_vss_1((igraph_integer_t) id), &numv);
+                    fputc(' ', outstream);
+                    igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+                }
+            }
+
+            /* shape */
+            if (vtypes[V_SHAPE] == IGRAPH_ATTRIBUTE_STRING) {
+                igraph_i_attribute_get_string_vertex_attr(graph, vnames[V_SHAPE],
+                        igraph_vss_1((igraph_integer_t) id), &strv);
+                igraph_strvector_get(&strv, 0, &s);
+                IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped));
+                fprintf(outstream, " %s", escaped);
+                igraph_Free(escaped);
+            }
+
+            /* numeric parameters */
+            for (j = 0; j < igraph_vector_size(&vx_numa); j++) {
+                int idx = (int) VECTOR(vx_numa)[j];
+                igraph_i_attribute_get_numeric_vertex_attr(graph, vnumnames[idx],
+                        igraph_vss_1((igraph_integer_t) id), &numv);
+                fprintf(outstream, " %s ", vnumnames2[idx]);
+                igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+            }
+
+            /* string parameters */
+            for (j = 0; j < igraph_vector_size(&vx_stra); j++) {
+                int idx = (int) VECTOR(vx_stra)[j];
+                igraph_i_attribute_get_string_vertex_attr(graph, vstrnames[idx],
+                        igraph_vss_1((igraph_integer_t) id), &strv);
+                igraph_strvector_get(&strv, 0, &s);
+                IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped));
+                fprintf(outstream, " %s %s", vstrnames2[idx], escaped);
+                igraph_Free(escaped);
+            }
+
+            /* trailing newline */
+            fprintf(outstream, "%s", newline);
+        }
+    }
+
+    /* edges header */
+    if (igraph_is_directed(graph)) {
+        fprintf(outstream, "*Arcs%s", newline);
+    } else {
+        fprintf(outstream, "*Edges%s", newline);
+    }
+
+    IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID));
+    IGRAPH_FINALLY(igraph_es_destroy, &es);
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+    /* Check edge attributes */
+    for (i = 0; i < E_LAST; i++) {
+        if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE,
+                                        enames[i])) {
+            igraph_i_attribute_gettype(graph, &etypes[i], IGRAPH_ATTRIBUTE_EDGE,
+                                       enames[i]);
+        } else {
+            etypes[i] = (igraph_attribute_type_t) -1;
+        }
+    }
+    for (i = 0; i < (long int) (sizeof(enumnames) / sizeof(const char*)); i++) {
+        igraph_attribute_type_t type;
+        if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE,
+                                        enumnames[i])) {
+            igraph_i_attribute_gettype(graph, &type, IGRAPH_ATTRIBUTE_EDGE,
+                                       enumnames[i]);
+            if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                IGRAPH_CHECK(igraph_vector_push_back(&ex_numa, i));
+            }
+        }
+    }
+    for (i = 0; i < (long int) (sizeof(estrnames) / sizeof(const char*)); i++) {
+        igraph_attribute_type_t type;
+        if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE,
+                                        estrnames[i])) {
+            igraph_i_attribute_gettype(graph, &type, IGRAPH_ATTRIBUTE_EDGE,
+                                       estrnames[i]);
+            if (type == IGRAPH_ATTRIBUTE_STRING) {
+                IGRAPH_CHECK(igraph_vector_push_back(&ex_stra, i));
+            }
+        }
+    }
+
+    for (i = 0; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit), i++) {
+        long int edge = IGRAPH_EIT_GET(eit);
+        igraph_integer_t from, to;
+        igraph_edge(graph, (igraph_integer_t) edge, &from,  &to);
+        if (bipartite) {
+            from = VECTOR(bip_index2)[from];
+            to  = VECTOR(bip_index2)[to];
+        }
+        fprintf(outstream, "%li %li", (long int) from + 1, (long int) to + 1);
+
+        /* Weights */
+        if (etypes[E_WEIGHT] == IGRAPH_ATTRIBUTE_NUMERIC) {
+            igraph_i_attribute_get_numeric_edge_attr(graph, enames[E_WEIGHT],
+                    igraph_ess_1((igraph_integer_t) edge), &numv);
+            fputc(' ', outstream);
+            igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+        }
+
+        /* numeric parameters */
+        for (j = 0; j < igraph_vector_size(&ex_numa); j++) {
+            int idx = (int) VECTOR(ex_numa)[j];
+            igraph_i_attribute_get_numeric_edge_attr(graph, enumnames[idx],
+                    igraph_ess_1((igraph_integer_t) edge), &numv);
+            fprintf(outstream, " %s ", enumnames2[idx]);
+            igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
+        }
+
+        /* string parameters */
+        for (j = 0; j < igraph_vector_size(&ex_stra); j++) {
+            int idx = (int) VECTOR(ex_stra)[j];
+            igraph_i_attribute_get_string_edge_attr(graph, estrnames[idx],
+                                                    igraph_ess_1((igraph_integer_t) edge), &strv);
+            igraph_strvector_get(&strv, 0, &s);
+            IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped));
+            fprintf(outstream, " %s %s", estrnames2[idx], escaped);
+            igraph_Free(escaped);
+        }
+
+        /* trailing newline */
+        fprintf(outstream, "%s", newline);
+    }
+
+    igraph_eit_destroy(&eit);
+    igraph_es_destroy(&es);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    if (bipartite) {
+        igraph_vector_int_destroy(&bip_index2);
+        igraph_vector_int_destroy(&bip_index);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_vector_destroy(&ex_numa);
+    igraph_vector_destroy(&ex_stra);
+    igraph_vector_destroy(&vx_numa);
+    igraph_vector_destroy(&vx_stra);
+    igraph_strvector_destroy(&strv);
+    igraph_vector_destroy(&numv);
+    IGRAPH_FINALLY_CLEAN(6);
+    return 0;
+}
+
+/**
+ * \function igraph_write_graph_dimacs
+ * \brief Write a graph in DIMACS format.
+ *
+ * This function writes a graph to an output stream in DIMACS format,
+ * describing a maximum flow problem.
+ * See ftp://dimacs.rutgers.edu/pub/netflow/general-info/
+ *
+ * </para><para>
+ * This file format is discussed in the documentation of \ref
+ * igraph_read_graph_dimacs(), see that for more information.
+ *
+ * \param graph The graph to write to the stream.
+ * \param outstream The stream.
+ * \param source Integer, the id of the source vertex for the maximum
+ *     flow.
+ * \param target Integer, the id of the target vertex.
+ * \param capacity Pointer to an initialized vector containing the
+ *     edge capacity values.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|), the number of edges in the graph.
+ *
+ * \sa igraph_read_graph_dimacs()
+ */
+
+int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream,
+                              long int source, long int target,
+                              const igraph_vector_t *capacity) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_eit_t it;
+    long int i = 0;
+    int ret, ret1, ret2, ret3;
+
+    if (igraph_vector_size(capacity) != no_of_edges) {
+        IGRAPH_ERROR("invalid capacity vector length", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID),
+                                   &it));
+    IGRAPH_FINALLY(igraph_eit_destroy, &it);
+
+    ret = fprintf(outstream,
+                  "c created by igraph\np max %li %li\nn %li s\nn %li t\n",
+                  no_of_nodes, no_of_edges, source + 1, target + 1);
+    if (ret < 0) {
+        IGRAPH_ERROR("Write error", IGRAPH_EFILE);
+    }
+
+
+    while (!IGRAPH_EIT_END(it)) {
+        igraph_integer_t from, to;
+        igraph_real_t cap;
+        igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to);
+        cap = VECTOR(*capacity)[i++];
+        ret1 = fprintf(outstream, "a %li %li ",
+                       (long int) from + 1, (long int) to + 1);
+        ret2 = igraph_real_fprintf_precise(outstream, cap);
+        ret3 = fputc('\n', outstream);
+        if (ret1 < 0 || ret2 < 0 || ret3 == EOF) {
+            IGRAPH_ERROR("Write error", IGRAPH_EFILE);
+        }
+        IGRAPH_EIT_NEXT(it);
+    }
+
+    igraph_eit_destroy(&it);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_i_gml_convert_to_key(const char *orig, char **key) {
+    int no = 1;
+    char strno[50];
+    size_t i, len = strlen(orig), newlen = 0, plen = 0;
+
+    /* do we need a prefix? */
+    if (len == 0 || !isalpha(orig[0])) {
+        no++;
+        snprintf(strno, sizeof(strno) - 1, "igraph");
+        plen = newlen = strlen(strno);
+    }
+    for (i = 0; i < len; i++) {
+        if (isalnum(orig[i])) {
+            newlen++;
+        }
+    }
+    *key = igraph_Calloc(newlen + 1, char);
+    if (! *key) {
+        IGRAPH_ERROR("Writing GML file failed", IGRAPH_ENOMEM);
+    }
+    memcpy(*key, strno, plen * sizeof(char));
+    for (i = 0; i < len; i++) {
+        if (isalnum(orig[i])) {
+            (*key)[plen++] = orig[i];
+        }
+    }
+    (*key)[newlen] = '\0';
+
+    return 0;
+}
+
+#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } while (0)
+
+/**
+ * \function igraph_write_graph_gml
+ * \brief Write the graph to a stream in GML format
+ *
+ * GML is a quite general textual format, see
+ * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html for details.
+ *
+ * </para><para> The graph, vertex and edges attributes are written to the
+ * file as well, if they are numeric or string.
+ *
+ * </para><para> As igraph is more forgiving about attribute names, it might
+ * be necessary to simplify the them before writing to the GML file.
+ * This way we'll have a syntactically correct GML file. The following
+ * simple procedure is performed on each attribute name: first the alphanumeric
+ * characters are extracted, the others are ignored. Then if the first character
+ * is not a letter then the attribute name is prefixed with <quote>igraph</quote>.
+ * Note that this might result identical names for two attributes, igraph
+ * does not check this.
+ *
+ * </para><para> The <quote>id</quote> vertex attribute is treated specially.
+ * If the <parameter>id</parameter> argument is not 0 then it should be a numeric
+ * vector with the vertex ids and the <quote>id</quote> vertex attribute is
+ * ignored (if there is one). If <parameter>id</parameter> is 0 and there is a
+ * numeric <quote>id</quote> vertex attribute that is used instead. If ids
+ * are not specified in either way then the regular igraph vertex ids are used.
+ *
+ * </para><para> Note that whichever way vertex ids are specified, their
+ * uniqueness is not checked.
+ *
+ * </para><para> If the graph has edge attributes named <quote>source</quote>
+ * or <quote>target</quote> they're silently ignored. GML uses these attributes
+ * to specify the edges, so we cannot write them to the file. Rename them
+ * before calling this function if you want to preserve them.
+ * \param graph The graph to write to the stream.
+ * \param outstream The stream to write the file to.
+ * \param id Either <code>NULL</code> or a numeric vector with the vertex ids.
+ *        See details above.
+ * \param creator An optional string to write to the stream in the creator line.
+ *        If this is 0 then the current date and time is added.
+ * \return Error code.
+ *
+ * Time complexity: should be proportional to the number of characters written
+ * to the file.
+ *
+ * \sa \ref igraph_read_graph_gml() for reading GML files,
+ * \ref igraph_read_graph_graphml() for a more modern format.
+ *
+ * \example examples/simple/gml.c
+ */
+
+int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream,
+                           const igraph_vector_t *id, const char *creator) {
+    int ret;
+    igraph_strvector_t gnames, vnames, enames;
+    igraph_vector_t gtypes, vtypes, etypes;
+    igraph_vector_t numv;
+    igraph_strvector_t strv;
+    igraph_vector_bool_t boolv;
+    long int i;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+
+    igraph_vector_t v_myid;
+    const igraph_vector_t *myid = id;
+
+    time_t curtime = time(0);
+    char *timestr = ctime(&curtime);
+    timestr[strlen(timestr) - 1] = '\0'; /* nicely remove \n */
+
+    CHECK(fprintf(outstream,
+                  "Creator \"igraph version %s %s\"\nVersion 1\ngraph\n[\n",
+                  PACKAGE_VERSION, creator ? creator : timestr));
+
+    IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&gtypes, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0);
+    IGRAPH_CHECK(igraph_i_attribute_get_info(graph,
+                 &gnames, &gtypes,
+                 &vnames, &vtypes,
+                 &enames, &etypes));
+
+    IGRAPH_VECTOR_INIT_FINALLY(&numv, 1);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1);
+    IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1);
+
+    /* Check whether there is an 'id' node attribute if the supplied is 0 */
+    if (!id) {
+        igraph_bool_t found = 0;
+        for (i = 0; i < igraph_vector_size(&vtypes); i++) {
+            char *n;
+            igraph_strvector_get(&vnames, i, &n);
+            if (!strcmp(n, "id") && VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                found = 1; break;
+            }
+        }
+        if (found) {
+            IGRAPH_VECTOR_INIT_FINALLY(&v_myid, no_of_nodes);
+            IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, "id",
+                         igraph_vss_all(),
+                         &v_myid));
+            myid = &v_myid;
+        }
+    }
+
+    /* directedness */
+    CHECK(fprintf(outstream, "  directed %i\n", igraph_is_directed(graph) ? 1 : 0));
+
+    /* Graph attributes first */
+    for (i = 0; i < igraph_vector_size(&gtypes); i++) {
+        char *name, *newname;
+        igraph_strvector_get(&gnames, i, &name);
+        IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname));
+        if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+            IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv));
+            CHECK(fprintf(outstream, "  %s ", newname));
+            CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]));
+            CHECK(fputc('\n', outstream));
+        } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
+            char *s;
+            IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv));
+            igraph_strvector_get(&strv, 0, &s);
+            CHECK(fprintf(outstream, "  %s \"%s\"\n", newname, s));
+        } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+            IGRAPH_CHECK(igraph_i_attribute_get_bool_graph_attr(graph, name, &boolv));
+            CHECK(fprintf(outstream, "  %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0));
+            IGRAPH_WARNING("A boolean graph attribute was converted to numeric");
+        } else {
+            IGRAPH_WARNING("A non-numeric, non-string, non-boolean graph attribute ignored");
+        }
+        igraph_Free(newname);
+    }
+
+    /* Now come the vertices */
+    for (i = 0; i < no_of_nodes; i++) {
+        long int j;
+        CHECK(fprintf(outstream, "  node\n  [\n"));
+        /* id */
+        CHECK(fprintf(outstream, "    id %li\n", myid ? (long int)VECTOR(*myid)[i] : i));
+        /* other attributes */
+        for (j = 0; j < igraph_vector_size(&vtypes); j++) {
+            int type = (int) VECTOR(vtypes)[j];
+            char *name, *newname;
+            igraph_strvector_get(&vnames, j, &name);
+            if (!strcmp(name, "id")) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname));
+            if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name,
+                             igraph_vss_1((igraph_integer_t) i), &numv));
+                CHECK(fprintf(outstream, "    %s ", newname));
+                CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]));
+                CHECK(fputc('\n', outstream));
+            } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+                char *s;
+                IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name,
+                             igraph_vss_1((igraph_integer_t) i), &strv));
+                igraph_strvector_get(&strv, 0, &s);
+                CHECK(fprintf(outstream, "    %s \"%s\"\n", newname, s));
+            } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name,
+                             igraph_vss_1((igraph_integer_t) i), &boolv));
+                CHECK(fprintf(outstream, "    %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0));
+                IGRAPH_WARNING("A boolean vertex attribute was converted to numeric");
+            } else {
+                IGRAPH_WARNING("A non-numeric, non-string, non-boolean edge attribute was ignored");
+            }
+            igraph_Free(newname);
+        }
+        CHECK(fprintf(outstream, "  ]\n"));
+    }
+
+    /* The edges too */
+    for (i = 0; i < no_of_edges; i++) {
+        long int from = IGRAPH_FROM(graph, i);
+        long int to = IGRAPH_TO(graph, i);
+        long int j;
+        CHECK(fprintf(outstream, "  edge\n  [\n"));
+        /* source and target */
+        CHECK(fprintf(outstream, "    source %li\n",
+                      myid ? (long int)VECTOR(*myid)[from] : from));
+        CHECK(fprintf(outstream, "    target %li\n",
+                      myid ? (long int)VECTOR(*myid)[to] : to));
+
+        /* other attributes */
+        for (j = 0; j < igraph_vector_size(&etypes); j++) {
+            int type = (int) VECTOR(etypes)[j];
+            char *name, *newname;
+            igraph_strvector_get(&enames, j, &name);
+            if (!strcmp(name, "source") || !strcmp(name, "target")) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname));
+            if (type == IGRAPH_ATTRIBUTE_NUMERIC) {
+                IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name,
+                             igraph_ess_1((igraph_integer_t) i), &numv));
+                CHECK(fprintf(outstream, "    %s ", newname));
+                CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]));
+                CHECK(fputc('\n', outstream));
+            } else if (type == IGRAPH_ATTRIBUTE_STRING) {
+                char *s;
+                IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name,
+                             igraph_ess_1((igraph_integer_t) i), &strv));
+                igraph_strvector_get(&strv, 0, &s);
+                CHECK(fprintf(outstream, "    %s \"%s\"\n", newname, s));
+            } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, name,
+                             igraph_ess_1((igraph_integer_t) i), &boolv));
+                CHECK(fprintf(outstream, "    %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0));
+                IGRAPH_WARNING("A boolean edge attribute was converted to numeric");
+            } else {
+                IGRAPH_WARNING("A non-numeric, non-string, non-boolean edge attribute was ignored");
+            }
+            igraph_Free(newname);
+        }
+        CHECK(fprintf(outstream, "  ]\n"));
+    }
+
+    CHECK(fprintf(outstream, "]\n"));
+
+    if (&v_myid == myid) {
+        igraph_vector_destroy(&v_myid);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_bool_destroy(&boolv);
+    igraph_strvector_destroy(&strv);
+    igraph_vector_destroy(&numv);
+    igraph_vector_destroy(&etypes);
+    igraph_vector_destroy(&vtypes);
+    igraph_vector_destroy(&gtypes);
+    igraph_strvector_destroy(&enames);
+    igraph_strvector_destroy(&vnames);
+    igraph_strvector_destroy(&gnames);
+    IGRAPH_FINALLY_CLEAN(9);
+
+    return 0;
+}
+
+int igraph_i_dot_escape(const char *orig, char **result) {
+    /* do we have to escape the string at all? */
+    long int i, j, len = (long int) strlen(orig), newlen = 0;
+    igraph_bool_t need_quote = 0, is_number = 1;
+
+    /* first, check whether the string is equal to some reserved word */
+    if (!strcasecmp(orig, "graph") || !strcasecmp(orig, "digraph") ||
+        !strcasecmp(orig, "node") || !strcasecmp(orig, "edge") ||
+        !strcasecmp(orig, "strict") || !strcasecmp(orig, "subgraph")) {
+        need_quote = 1;
+        is_number = 0;
+    }
+
+    /* next, check whether we need to escape the string for any other reason.
+     * Also update is_number and newlen */
+    for (i = 0; i < len; i++) {
+        if (isdigit(orig[i])) {
+            newlen++;
+        } else if (orig[i] == '-' && i == 0) {
+            newlen++;
+        } else if (orig[i] == '.') {
+            if (is_number) {
+                newlen++;
+            } else {
+                need_quote = 1;
+                newlen++;
+            }
+        } else if (orig[i] == '_') {
+            is_number = 0; newlen++;
+        } else if (orig[i] == '\\' || orig[i] == '"' || orig[i] == '\n') {
+            need_quote = 1; is_number = 0; newlen += 2; /* will be escaped */
+        } else if (isalpha(orig[i])) {
+            is_number = 0; newlen++;
+        } else {
+            is_number = 0; need_quote = 1; newlen++;
+        }
+    }
+    if (is_number && orig[len - 1] == '.') {
+        is_number = 0;
+    }
+    if (!is_number && isdigit(orig[0])) {
+        need_quote = 1;
+    }
+
+    if (is_number || !need_quote) {
+        *result = strdup(orig);
+        if (!*result) {
+            IGRAPH_ERROR("Writing DOT file failed", IGRAPH_ENOMEM);
+        }
+    } else {
+        *result = igraph_Calloc(newlen + 3, char);
+        (*result)[0] = '"';
+        (*result)[newlen + 1] = '"';
+        (*result)[newlen + 2] = '\0';
+        for (i = 0, j = 1; i < len; i++) {
+            if (orig[i] == '\n') {
+                (*result)[j++] = '\\';
+                (*result)[j++] = 'n';
+                continue;
+            }
+            if (orig[i] == '\\' || orig[i] == '"') {
+                (*result)[j++] = '\\';
+            }
+            (*result)[j++] = orig[i];
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_write_graph_dot
+ * \brief Write the graph to a stream in DOT format
+ *
+ * DOT is the format used by the widely known GraphViz software, see
+ * http://www.graphviz.org for details. The grammar of the DOT format
+ * can be found here: http://www.graphviz.org/doc/info/lang.html
+ *
+ * </para><para>This is only a preliminary implementation, only the vertices
+ * and the edges are written but not the attributes or any visualization
+ * information.
+ *
+ * \param graph The graph to write to the stream.
+ * \param outstream The stream to write the file to.
+ *
+ * Time complexity: should be proportional to the number of characters written
+ * to the file.
+ *
+ * \sa \ref igraph_write_graph_graphml() for a more modern format.
+ *
+ * \example examples/simple/dot.c
+ */
+int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) {
+    int ret;
+    long int i, j;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    char edgeop[3];
+    igraph_strvector_t gnames, vnames, enames;
+    igraph_vector_t gtypes, vtypes, etypes;
+    igraph_vector_t numv;
+    igraph_strvector_t strv;
+    igraph_vector_bool_t boolv;
+
+    IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&gtypes, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&etypes, 0);
+    IGRAPH_CHECK(igraph_i_attribute_get_info(graph,
+                 &gnames, &gtypes,
+                 &vnames, &vtypes,
+                 &enames, &etypes));
+
+    IGRAPH_VECTOR_INIT_FINALLY(&numv, 1);
+    IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1);
+    IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1);
+
+    CHECK(fprintf(outstream, "/* Created by igraph %s */\n",
+                  PACKAGE_VERSION));
+
+    if (igraph_is_directed(graph)) {
+        CHECK(fprintf(outstream, "digraph {\n"));
+        strcpy(edgeop, "->");
+    } else {
+        CHECK(fprintf(outstream, "graph {\n"));
+        strcpy(edgeop, "--");
+    }
+
+    /* Write the graph attributes */
+    if (igraph_vector_size(&gtypes) > 0) {
+        CHECK(fprintf(outstream, "  graph [\n"));
+        for (i = 0; i < igraph_vector_size(&gtypes); i++) {
+            char *name, *newname;
+            igraph_strvector_get(&gnames, i, &name);
+            IGRAPH_CHECK(igraph_i_dot_escape(name, &newname));
+            if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv));
+                if (VECTOR(numv)[0] == (long)VECTOR(numv)[0]) {
+                    CHECK(fprintf(outstream, "    %s=%ld\n", newname, (long)VECTOR(numv)[0]));
+                } else {
+                    CHECK(fprintf(outstream, "    %s=", newname));
+                    CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]));
+                    CHECK(fputc('\n', outstream));
+                }
+            } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
+                char *s, *news;
+                IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv));
+                igraph_strvector_get(&strv, 0, &s);
+                IGRAPH_CHECK(igraph_i_dot_escape(s, &news));
+                CHECK(fprintf(outstream, "    %s=%s\n", newname, news));
+                igraph_Free(news);
+            } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                IGRAPH_CHECK(igraph_i_attribute_get_bool_graph_attr(graph, name, &boolv));
+                CHECK(fprintf(outstream, "    %s=%d\n", newname, VECTOR(boolv)[0] ? 1 : 0));
+                IGRAPH_WARNING("A boolean graph attribute was converted to numeric");
+            } else {
+                IGRAPH_WARNING("A non-numeric, non-string, non-boolean graph attribute ignored");
+            }
+            igraph_Free(newname);
+        }
+        CHECK(fprintf(outstream, "  ];\n"));
+    }
+
+    /* Write the vertices */
+    if (igraph_vector_size(&vtypes) > 0) {
+        for (i = 0; i < no_of_nodes; i++) {
+            CHECK(fprintf(outstream, "  %ld [\n", i));
+            for (j = 0; j < igraph_vector_size(&vtypes); j++) {
+                char *name, *newname;
+                igraph_strvector_get(&vnames, j, &name);
+                IGRAPH_CHECK(igraph_i_dot_escape(name, &newname));
+                if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                    IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, igraph_vss_1((igraph_integer_t) i), &numv));
+                    if (VECTOR(numv)[0] == (long)VECTOR(numv)[0]) {
+                        CHECK(fprintf(outstream, "    %s=%ld\n", newname, (long)VECTOR(numv)[0]));
+                    } else {
+                        CHECK(fprintf(outstream, "    %s=", newname));
+                        CHECK(igraph_real_fprintf_precise(outstream,
+                                                          VECTOR(numv)[0]));
+                        CHECK(fputc('\n', outstream));
+                    }
+                } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_STRING) {
+                    char *s, *news;
+                    IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, igraph_vss_1((igraph_integer_t) i), &strv));
+                    igraph_strvector_get(&strv, 0, &s);
+                    IGRAPH_CHECK(igraph_i_dot_escape(s, &news));
+                    CHECK(fprintf(outstream, "    %s=%s\n", newname, news));
+                    igraph_Free(news);
+                } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                    IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name, igraph_vss_1((igraph_integer_t) i), &boolv));
+                    CHECK(fprintf(outstream, "    %s=%d\n", newname, VECTOR(boolv)[0] ? 1 : 0));
+                    IGRAPH_WARNING("A boolean vertex attribute was converted to numeric");
+                } else {
+                    IGRAPH_WARNING("A non-numeric, non-string, non-boolean vertex attribute was ignored");
+                }
+                igraph_Free(newname);
+            }
+            CHECK(fprintf(outstream, "  ];\n"));
+        }
+    } else {
+        for (i = 0; i < no_of_nodes; i++) {
+            CHECK(fprintf(outstream, "  %ld;\n", i));
+        }
+    }
+    CHECK(fprintf(outstream, "\n"));
+
+    /* Write the edges */
+    if (igraph_vector_size(&etypes) > 0) {
+        for (i = 0; i < no_of_edges; i++) {
+            long int from = IGRAPH_FROM(graph, i);
+            long int to = IGRAPH_TO(graph, i);
+            CHECK(fprintf(outstream, "  %ld %s %ld [\n", from, edgeop, to));
+            for (j = 0; j < igraph_vector_size(&etypes); j++) {
+                char *name, *newname;
+                igraph_strvector_get(&enames, j, &name);
+                IGRAPH_CHECK(igraph_i_dot_escape(name, &newname));
+                if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
+                    IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph,
+                                 name, igraph_ess_1((igraph_integer_t) i), &numv));
+                    if (VECTOR(numv)[0] == (long)VECTOR(numv)[0]) {
+                        CHECK(fprintf(outstream, "    %s=%ld\n", newname, (long)VECTOR(numv)[0]));
+                    } else {
+                        CHECK(fprintf(outstream, "    %s=", newname));
+                        CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]));
+                        CHECK(fputc('\n', outstream));
+                    }
+                    igraph_Free(newname);
+                } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_STRING) {
+                    char *s, *news;
+                    IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph,
+                                 name, igraph_ess_1((igraph_integer_t) i), &strv));
+                    igraph_strvector_get(&strv, 0, &s);
+                    IGRAPH_CHECK(igraph_i_dot_escape(s, &news));
+                    CHECK(fprintf(outstream, "    %s=%s\n", newname, news));
+                    igraph_Free(newname);
+                    igraph_Free(news);
+                } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_BOOLEAN) {
+                    IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph,
+                                 name, igraph_ess_1((igraph_integer_t) i), &boolv));
+                    CHECK(fprintf(outstream, "    %s=%d\n", newname, VECTOR(boolv)[0] ? 1 : 0));
+                    IGRAPH_WARNING("A boolean edge attribute was converted to numeric");
+                } else {
+                    IGRAPH_WARNING("A non-numeric, non-string graph attribute ignored");
+                }
+            }
+            CHECK(fprintf(outstream, "  ];\n"));
+        }
+    } else {
+        for (i = 0; i < no_of_edges; i++) {
+            long int from = IGRAPH_FROM(graph, i);
+            long int to = IGRAPH_TO(graph, i);
+            CHECK(fprintf(outstream, "  %ld %s %ld;\n", from, edgeop, to));
+        }
+    }
+    CHECK(fprintf(outstream, "}\n"));
+
+    igraph_vector_bool_destroy(&boolv);
+    igraph_strvector_destroy(&strv);
+    igraph_vector_destroy(&numv);
+    igraph_vector_destroy(&etypes);
+    igraph_vector_destroy(&vtypes);
+    igraph_vector_destroy(&gtypes);
+    igraph_strvector_destroy(&enames);
+    igraph_strvector_destroy(&vnames);
+    igraph_strvector_destroy(&gnames);
+    IGRAPH_FINALLY_CLEAN(9);
+
+    return 0;
+}
+
+#include "foreign-dl-header.h"
+
+int igraph_dl_yylex_init_extra (igraph_i_dl_parsedata_t* user_defined,
+                                void* scanner);
+int igraph_dl_yylex_destroy (void *scanner );
+int igraph_dl_yyparse (igraph_i_dl_parsedata_t* context);
+void igraph_dl_yyset_in  (FILE * in_str, void* yyscanner );
+
+/**
+ * \function igraph_read_graph_dl
+ * \brief Read a file in the DL format of UCINET
+ *
+ * This is a simple textual file format used by UCINET. See
+ * http://www.analytictech.com/networks/dataentry.htm for
+ * examples. All the forms described here are supported by
+ * igraph. Vertex names and edge weights are also supported and they
+ * are added as attributes. (If an attribute handler is attached.)
+ *
+ * </para><para> Note the specification does not mention whether the
+ * format is case sensitive or not. For igraph DL files are case
+ * sensitive, i.e. \c Larry and \c larry are not the same.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param instream The stream to read the DL file from.
+ * \param directed Logical scalar, whether to create a directed file.
+ * \return Error code.
+ *
+ * Time complexity: linear in terms of the number of edges and
+ * vertices, except for the matrix format, which is quadratic in the
+ * number of vertices.
+ *
+ * \example examples/simple/igraph_read_graph_dl.c
+ */
+
+int igraph_read_graph_dl(igraph_t *graph, FILE *instream,
+                         igraph_bool_t directed) {
+
+    int i;
+    long int n, n2;
+    const igraph_strvector_t *namevec = 0;
+    igraph_vector_ptr_t name, weight;
+    igraph_vector_ptr_t *pname = 0, *pweight = 0;
+    igraph_attribute_record_t namerec, weightrec;
+    const char *namestr = "name", *weightstr = "weight";
+    igraph_i_dl_parsedata_t context;
+
+    context.eof = 0;
+    context.mode = 0;
+    context.n = -1;
+    context.from = 0;
+    context.to = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&context.edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&context.weights, 0);
+    IGRAPH_CHECK(igraph_strvector_init(&context.labels, 0));
+    IGRAPH_FINALLY(igraph_strvector_destroy, &context.labels);
+    IGRAPH_TRIE_INIT_FINALLY(&context.trie, /*names=*/ 1);
+
+    igraph_dl_yylex_init_extra(&context, &context.scanner);
+    IGRAPH_FINALLY(igraph_dl_yylex_destroy, context.scanner);
+
+    igraph_dl_yyset_in(instream, context.scanner);
+
+    i = igraph_dl_yyparse(&context);
+    if (i != 0) {
+        if (context.errmsg[0] != 0) {
+            IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR);
+        } else {
+            IGRAPH_ERROR("Cannot read DL file", IGRAPH_PARSEERROR);
+        }
+    }
+
+    /* Extend the weight vector, if needed */
+    n = igraph_vector_size(&context.weights);
+    n2 = igraph_vector_size(&context.edges) / 2;
+    if (n != 0) {
+        igraph_vector_resize(&context.weights, n2);
+        for (; n < n2; n++) {
+            VECTOR(context.weights)[n] = IGRAPH_NAN;
+        }
+    }
+
+    /* Check number of vertices */
+    if (n2 > 0) {
+        n = (long int) igraph_vector_max(&context.edges);
+    } else {
+        n = 0;
+    }
+    if (n >= context.n) {
+        IGRAPH_WARNING("More vertices than specified in `DL' file");
+        context.n = n;
+    }
+
+    /* OK, everything is ready, create the graph */
+    IGRAPH_CHECK(igraph_empty(graph, 0, directed));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+
+    /* Labels */
+    if (igraph_strvector_size(&context.labels) != 0) {
+        namevec = (const igraph_strvector_t*) &context.labels;
+    } else if (igraph_trie_size(&context.trie) != 0) {
+        igraph_trie_getkeys(&context.trie, &namevec);
+    }
+    if (namevec) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &name);
+        pname = &name;
+        namerec.name = namestr;
+        namerec.type = IGRAPH_ATTRIBUTE_STRING;
+        namerec.value = namevec;
+        VECTOR(name)[0] = &namerec;
+    }
+
+    /* Weights */
+    if (igraph_vector_size(&context.weights) != 0) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &weight);
+        pweight = &weight;
+        weightrec.name = weightstr;
+        weightrec.type = IGRAPH_ATTRIBUTE_NUMERIC;
+        weightrec.value = &context.weights;
+        VECTOR(weight)[0] = &weightrec;
+    }
+
+    IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) context.n, pname));
+    IGRAPH_CHECK(igraph_add_edges(graph, &context.edges, pweight));
+
+    if (pweight) {
+        igraph_vector_ptr_destroy(pweight);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (pname) {
+        igraph_vector_ptr_destroy(pname);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* don't destroy the graph itself but pop it from the finally stack */
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_trie_destroy(&context.trie);
+    igraph_strvector_destroy(&context.labels);
+    igraph_vector_destroy(&context.edges);
+    igraph_vector_destroy(&context.weights);
+    igraph_dl_yylex_destroy(context.scanner);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \function igraph_write_graph_leda
+ * \brief Write a graph in LEDA native graph format.
+ *
+ * This function writes a graph to an output stream in LEDA format.
+ * See http://www.algorithmic-solutions.info/leda_guide/graphs/leda_native_graph_fileformat.html
+ *
+ * </para><para>
+ * The support for the LEDA format is very basic at the moment; igraph
+ * writes only the LEDA graph section which supports one selected vertex
+ * and edge attribute and no layout information or visual attributes.
+ *
+ * \param graph The graph to write to the stream.
+ * \param outstream The stream.
+ * \param vertex_attr_name The name of the vertex attribute whose values
+ *                         are to be stored in the output or \c NULL if no
+ *                         vertex attribute has to be stored.
+ * \param edge_attr_name   The name of the edge attribute whose values
+ *                         are to be stored in the output or \c NULL if no
+ *                         edge attribute has to be stored.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices and edges in the
+ * graph.
+ *
+ * \example examples/simple/igraph_write_graph_leda.c
+ */
+
+int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream,
+                            const char* vertex_attr_name,
+                            const char* edge_attr_name) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_eit_t it;
+    long int i = 0;
+    int ret;
+    igraph_attribute_type_t vertex_attr_type = IGRAPH_ATTRIBUTE_DEFAULT;
+    igraph_attribute_type_t edge_attr_type = IGRAPH_ATTRIBUTE_DEFAULT;
+    igraph_integer_t from, to, rev;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM),
+                                   &it));
+    IGRAPH_FINALLY(igraph_eit_destroy, &it);
+
+    /* Check if we have the vertex attribute */
+    if (vertex_attr_name &&
+        !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vertex_attr_name)) {
+        vertex_attr_name = 0;
+        IGRAPH_WARNING("specified vertex attribute does not exist");
+    }
+    if (vertex_attr_name) {
+        IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &vertex_attr_type,
+                                                IGRAPH_ATTRIBUTE_VERTEX, vertex_attr_name));
+        if (vertex_attr_type != IGRAPH_ATTRIBUTE_NUMERIC &&
+            vertex_attr_type != IGRAPH_ATTRIBUTE_STRING) {
+            vertex_attr_name = 0; vertex_attr_type = IGRAPH_ATTRIBUTE_DEFAULT;
+            IGRAPH_WARNING("specified vertex attribute must be numeric or string");
+        }
+    }
+
+    /* Check if we have the edge attribute */
+    if (edge_attr_name &&
+        !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, edge_attr_name)) {
+        edge_attr_name = 0;
+        IGRAPH_WARNING("specified edge attribute does not exist");
+    }
+    if (edge_attr_name) {
+        IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &edge_attr_type,
+                                                IGRAPH_ATTRIBUTE_EDGE, edge_attr_name));
+        if (edge_attr_type != IGRAPH_ATTRIBUTE_NUMERIC &&
+            edge_attr_type != IGRAPH_ATTRIBUTE_STRING) {
+            edge_attr_name = 0; edge_attr_type = IGRAPH_ATTRIBUTE_DEFAULT;
+            IGRAPH_WARNING("specified edge attribute must be numeric or string");
+        }
+    }
+
+    /* Start writing header */
+    CHECK(fprintf(outstream, "LEDA.GRAPH\n"));
+
+    switch (vertex_attr_type) {
+    case IGRAPH_ATTRIBUTE_NUMERIC:
+        CHECK(fprintf(outstream, "float\n"));
+        break;
+    case IGRAPH_ATTRIBUTE_STRING:
+        CHECK(fprintf(outstream, "string\n"));
+        break;
+    default:
+        CHECK(fprintf(outstream, "void\n"));
+    }
+
+    switch (edge_attr_type) {
+    case IGRAPH_ATTRIBUTE_NUMERIC:
+        CHECK(fprintf(outstream, "float\n"));
+        break;
+    case IGRAPH_ATTRIBUTE_STRING:
+        CHECK(fprintf(outstream, "string\n"));
+        break;
+    default:
+        CHECK(fprintf(outstream, "void\n"));
+    }
+
+    CHECK(fprintf(outstream, "%d\n", (igraph_is_directed(graph) ? -1 : -2)));
+
+    /* Start writing vertices */
+    CHECK(fprintf(outstream, "# Vertices\n"));
+    CHECK(fprintf(outstream, "%ld\n", no_of_nodes));
+
+    if (vertex_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) {
+        /* Vertices with numeric attributes */
+        igraph_vector_t values;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes);
+        IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(
+                         graph, vertex_attr_name, igraph_vss_all(), &values));
+
+        for (i = 0; i < no_of_nodes; i++) {
+            CHECK(fprintf(outstream, "|{"));
+            CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[i]));
+            CHECK(fprintf(outstream, "}|\n"));
+        }
+
+        igraph_vector_destroy(&values);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_STRING) {
+        /* Vertices with string attributes */
+        igraph_strvector_t values;
+
+        IGRAPH_CHECK(igraph_strvector_init(&values, no_of_nodes));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &values);
+
+        IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(
+                         graph, vertex_attr_name, igraph_vss_all(), &values));
+
+        for (i = 0; i < no_of_nodes; i++) {
+            const char* str = STR(values, i);
+            if (strchr(str, '\n') != 0) {
+                IGRAPH_ERROR("edge attribute values cannot contain newline characters",
+                             IGRAPH_EINVAL);
+            }
+            CHECK(fprintf(outstream, "|{%s}|\n", str));
+        }
+
+        igraph_strvector_destroy(&values);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        /* Vertices with no attributes */
+        for (i = 0; i < no_of_nodes; i++) {
+            CHECK(fprintf(outstream, "|{}|\n"));
+        }
+    }
+
+    CHECK(fprintf(outstream, "# Edges\n"));
+    CHECK(fprintf(outstream, "%ld\n", no_of_edges));
+
+    if (edge_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) {
+        /* Edges with numeric attributes */
+        igraph_vector_t values;
+        IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes);
+        IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(
+                         graph, edge_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values));
+        while (!IGRAPH_EIT_END(it)) {
+            long int eid = IGRAPH_EIT_GET(it);
+            igraph_edge(graph, (igraph_integer_t) eid, &from, &to);
+            igraph_get_eid(graph, &rev, to, from, 1, 0);
+            if (rev == IGRAPH_EIT_GET(it)) {
+                rev = -1;
+            }
+            CHECK(fprintf(outstream, "%ld %ld %ld |{",
+                          (long int) from + 1, (long int) to + 1,
+                          (long int) rev + 1));
+            CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[eid]));
+            CHECK(fprintf(outstream, "}|\n"));
+            IGRAPH_EIT_NEXT(it);
+        }
+        igraph_vector_destroy(&values);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else if (edge_attr_type == IGRAPH_ATTRIBUTE_STRING) {
+        /* Edges with string attributes */
+        igraph_strvector_t values;
+        IGRAPH_CHECK(igraph_strvector_init(&values, no_of_nodes));
+        IGRAPH_FINALLY(igraph_strvector_destroy, &values);
+        IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(
+                         graph, edge_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values));
+        while (!IGRAPH_EIT_END(it)) {
+            long int eid = IGRAPH_EIT_GET(it);
+            const char* str = STR(values, eid);
+            igraph_edge(graph, (igraph_integer_t) eid, &from, &to);
+            igraph_get_eid(graph, &rev, to, from, 1, 0);
+            if (rev == IGRAPH_EIT_GET(it)) {
+                rev = -1;
+            }
+            if (strchr(str, '\n') != 0) {
+                IGRAPH_ERROR("edge attribute values cannot contain newline characters",
+                             IGRAPH_EINVAL);
+            }
+            CHECK(fprintf(outstream, "%ld %ld %ld |{%s}|\n",
+                          (long int) from + 1, (long int) to + 1,
+                          (long int) rev + 1, str));
+            IGRAPH_EIT_NEXT(it);
+        }
+        igraph_strvector_destroy(&values);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        /* Edges with no attributes */
+        while (!IGRAPH_EIT_END(it)) {
+            igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to);
+            igraph_get_eid(graph, &rev, to, from, 1, 0);
+            if (rev == IGRAPH_EIT_GET(it)) {
+                rev = -1;
+            }
+            CHECK(fprintf(outstream, "%ld %ld %ld |{}|\n",
+                          (long int) from + 1, (long int) to + 1,
+                          (long int) rev + 1));
+            IGRAPH_EIT_NEXT(it);
+        }
+    }
+
+    igraph_eit_destroy(&it);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+#undef CHECK
+
+
diff --git a/igraph/src/forestfire.c b/igraph/src/forestfire.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/forestfire.c
@@ -0,0 +1,264 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_games.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_progress.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_interface.h"
+#include "igraph_constructors.h"
+#include "igraph_dqueue.h"
+#include "config.h"
+
+typedef struct igraph_i_forest_fire_data_t {
+    igraph_vector_t *inneis;
+    igraph_vector_t *outneis;
+    long int no_of_nodes;
+} igraph_i_forest_fire_data_t;
+
+
+void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) {
+    long int i;
+    for (i = 0; i < data->no_of_nodes; i++) {
+        igraph_vector_destroy(data->inneis + i);
+        igraph_vector_destroy(data->outneis + i);
+    }
+}
+
+/**
+ * \function igraph_forest_fire_game
+ * \brief Generates a network according to the \quote forest fire game \endquote
+ *
+ * The forest fire model intends to reproduce the following network
+ * characteristics, observed in real networks:
+ * \ilist
+ * \ili Heavy-tailed in-degree distribution.
+ * \ili Heavy-tailed out-degree distribution.
+ * \ili Communities.
+ * \ili Densification power-law. The network is densifying in time,
+ *      according to a power-law rule.
+ * \ili Shrinking diameter. The diameter of the network decreases in
+ *      time.
+ * \endilist
+ *
+ * </para><para>
+ * The network is generated in the following way. One vertex is added at
+ * a time. This vertex connects to (cites) <code>ambs</code> vertices already
+ * present in the network, chosen uniformly random. Now, for each cited
+ * vertex <code>v</code> we do the following procedure:
+ * \olist
+ * \oli We generate two random number, <code>x</code> and <code>y</code>, that are
+ *   geometrically distributed with means <code>p/(1-p)</code> and
+ *   <code>rp(1-rp)</code>. (<code>p</code> is <code>fw_prob</code>, <code>r</code> is
+ *   <code>bw_factor</code>.) The new vertex cites <code>x</code> outgoing neighbors
+ *   and <code>y</code> incoming neighbors of <code>v</code>, from those which are
+ *   not yet cited by the new vertex. If there are less than <code>x</code> or
+ *   <code>y</code> such vertices available then we cite all of them.
+ * \oli The same procedure is applied to all the newly cited
+ *   vertices.
+ * \endolist
+ * </para><para>
+ * See also:
+ * Jure Leskovec, Jon Kleinberg and Christos Faloutsos. Graphs over time:
+ * densification laws, shrinking diameters and possible explanations.
+ * \emb KDD '05: Proceeding of the eleventh ACM SIGKDD international
+ * conference on Knowledge discovery in data mining \eme, 177--187, 2005.
+ * </para><para>
+ * Note however, that the version of the model in the published paper is incorrect
+ * in the sense that it cannot generate the kind of graphs the authors
+ * claim. A corrected version is available from
+ * http://cs.stanford.edu/people/jure/pubs/powergrowth-tkdd.pdf , our
+ * implementation is based on this.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param nodes The number of vertices in the graph.
+ * \param fw_prob The forward burning probability.
+ * \param bw_factor The backward burning ratio. The backward burning
+      probability is calculated as <code>bw.factor*fw.prob</code>.
+ * \param pambs The number of ambassador vertices.
+ * \param directed Whether to create a directed graph.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes,
+                            igraph_real_t fw_prob, igraph_real_t bw_factor,
+                            igraph_integer_t pambs, igraph_bool_t directed) {
+
+    igraph_vector_long_t visited;
+    long int no_of_nodes = nodes, actnode, i;
+    igraph_vector_t edges;
+    igraph_vector_t *inneis, *outneis;
+    igraph_i_forest_fire_data_t data;
+    igraph_dqueue_t neiq;
+    long int ambs = pambs;
+    igraph_real_t param_geom_out = 1 - fw_prob;
+    igraph_real_t param_geom_in = 1 - fw_prob * bw_factor;
+
+    if (fw_prob < 0) {
+        IGRAPH_ERROR("Forest fire model: 'fw_prob' should be between non-negative",
+                     IGRAPH_EINVAL);
+    }
+    if (bw_factor < 0) {
+        IGRAPH_ERROR("Forest fire model: 'bw_factor' should be non-negative",
+                     IGRAPH_EINVAL);
+    }
+    if (ambs < 0) {
+        IGRAPH_ERROR("Number of ambassadors ('ambs') should be non-negative",
+                     IGRAPH_EINVAL);
+    }
+
+    if (fw_prob == 0 || ambs == 0) {
+        IGRAPH_WARNING("'fw_prob or ambs is zero, creating empty graph");
+        IGRAPH_CHECK(igraph_empty(graph, nodes, directed));
+        return 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    inneis = igraph_Calloc(no_of_nodes, igraph_vector_t);
+    if (!inneis) {
+        IGRAPH_ERROR("Cannot run forest fire model", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, inneis);
+    outneis = igraph_Calloc(no_of_nodes, igraph_vector_t);
+    if (!outneis) {
+        IGRAPH_ERROR("Cannot run forest fire model", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, outneis);
+    data.inneis = inneis;
+    data.outneis = outneis;
+    data.no_of_nodes = no_of_nodes;
+    IGRAPH_FINALLY(igraph_i_forest_fire_free, &data);
+    for (i = 0; i < no_of_nodes; i++) {
+        IGRAPH_CHECK(igraph_vector_init(inneis + i, 0));
+        IGRAPH_CHECK(igraph_vector_init(outneis + i, 0));
+    }
+
+    IGRAPH_CHECK(igraph_vector_long_init(&visited, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &visited);
+    IGRAPH_DQUEUE_INIT_FINALLY(&neiq, 10);
+
+    RNG_BEGIN();
+
+#define ADD_EDGE_TO(nei) \
+    if (VECTOR(visited)[(nei)] != actnode+1) {                     \
+        VECTOR(visited)[(nei)] = actnode+1;                          \
+        IGRAPH_CHECK(igraph_dqueue_push(&neiq, nei));                \
+        IGRAPH_CHECK(igraph_vector_push_back(&edges, actnode));      \
+        IGRAPH_CHECK(igraph_vector_push_back(&edges, nei));          \
+        IGRAPH_CHECK(igraph_vector_push_back(outneis+actnode, nei)); \
+        IGRAPH_CHECK(igraph_vector_push_back(inneis+nei, actnode));  \
+    }
+
+    IGRAPH_PROGRESS("Forest fire: ", 0.0, NULL);
+
+    for (actnode = 1; actnode < no_of_nodes; actnode++) {
+
+        IGRAPH_PROGRESS("Forest fire: ", 100.0 * actnode / no_of_nodes, NULL);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /* We don't want to visit the current vertex */
+        VECTOR(visited)[actnode] = actnode + 1;
+
+        /* Choose ambassador(s) */
+        for (i = 0; i < ambs; i++) {
+            long int a = RNG_INTEGER(0, actnode - 1);
+            ADD_EDGE_TO(a);
+        }
+
+        while (!igraph_dqueue_empty(&neiq)) {
+            long int actamb = (long int) igraph_dqueue_pop(&neiq);
+            igraph_vector_t *outv = outneis + actamb;
+            igraph_vector_t *inv = inneis + actamb;
+            long int no_in = igraph_vector_size(inv);
+            long int no_out = igraph_vector_size(outv);
+            long int neis_out = (long int) RNG_GEOM(param_geom_out);
+            long int neis_in = (long int) RNG_GEOM(param_geom_in);
+            /* outgoing neighbors */
+            if (neis_out >= no_out) {
+                for (i = 0; i < no_out; i++) {
+                    long int nei = (long int) VECTOR(*outv)[i];
+                    ADD_EDGE_TO(nei);
+                }
+            } else {
+                long int oleft = no_out;
+                for (i = 0; i < neis_out && oleft > 0; ) {
+                    long int which = RNG_INTEGER(0, oleft - 1);
+                    long int nei = (long int) VECTOR(*outv)[which];
+                    VECTOR(*outv)[which] = VECTOR(*outv)[oleft - 1];
+                    VECTOR(*outv)[oleft - 1] = nei;
+                    if (VECTOR(visited)[nei] != actnode + 1) {
+                        ADD_EDGE_TO(nei);
+                        i++;
+                    }
+                    oleft--;
+                }
+            }
+            /* incoming neighbors */
+            if (neis_in >= no_in) {
+                for (i = 0; i < no_in; i++) {
+                    long int nei = (long int) VECTOR(*inv)[i];
+                    ADD_EDGE_TO(nei);
+                }
+            } else {
+                long int ileft = no_in;
+                for (i = 0; i < neis_in && ileft > 0; ) {
+                    long int which = RNG_INTEGER(0, ileft - 1);
+                    long int nei = (long int) VECTOR(*inv)[which];
+                    VECTOR(*inv)[which] = VECTOR(*inv)[ileft - 1];
+                    VECTOR(*inv)[ileft - 1] = nei;
+                    if (VECTOR(visited)[nei] != actnode + 1) {
+                        ADD_EDGE_TO(nei);
+                        i++;
+                    }
+                    ileft--;
+                }
+            }
+
+        } /* while neiq not empty */
+
+    } /* actnode < no_of_nodes */
+
+#undef ADD_EDGE_TO
+
+    RNG_END();
+
+    IGRAPH_PROGRESS("Forest fire: ", 100.0, NULL);
+
+    igraph_dqueue_destroy(&neiq);
+    igraph_vector_long_destroy(&visited);
+    igraph_i_forest_fire_free(&data);
+    igraph_free(outneis);
+    igraph_free(inneis);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
diff --git a/igraph/src/fortran_intrinsics.c b/igraph/src/fortran_intrinsics.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/fortran_intrinsics.c
@@ -0,0 +1,53 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-12  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge MA, 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include <float.h>
+
+double digitsdbl_(double x) {
+    return (double) DBL_MANT_DIG;
+}
+
+double epsilondbl_(double x) {
+    return DBL_EPSILON;
+}
+
+double hugedbl_(double x) {
+    return DBL_MAX;
+}
+
+double tinydbl_(double x) {
+    return DBL_MIN;
+}
+
+int maxexponentdbl_(double x) {
+    return DBL_MAX_EXP;
+}
+
+int minexponentdbl_(double x) {
+    return DBL_MIN_EXP;
+}
+
+double radixdbl_(double x) {
+    return (double) FLT_RADIX;
+}
+
diff --git a/igraph/src/ftell_.c b/igraph/src/ftell_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ftell_.c
@@ -0,0 +1,52 @@
+#include "f2c.h"
+#include "fio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ static FILE *
+#ifdef KR_headers
+unit_chk(Unit, who) integer Unit; char *who;
+#else
+unit_chk(integer Unit, const char *who)
+#endif
+{
+	if (Unit >= MXUNIT || Unit < 0)
+		f__fatal(101, who);
+	return f__units[Unit].ufd;
+	}
+
+ integer
+#ifdef KR_headers
+ftell_(Unit) integer *Unit;
+#else
+ftell_(integer *Unit)
+#endif
+{
+	FILE *f;
+	return (f = unit_chk(*Unit, "ftell")) ? ftell(f) : -1L;
+	}
+
+ int
+#ifdef KR_headers
+fseek_(Unit, offset, whence) integer *Unit, *offset, *whence;
+#else
+fseek_(integer *Unit, integer *offset, integer *whence)
+#endif
+{
+	FILE *f;
+	int w = (int)*whence;
+#ifdef SEEK_SET
+	static int wohin[3] = { SEEK_SET, SEEK_CUR, SEEK_END };
+#endif
+	if (w < 0 || w > 2)
+		w = 0;
+#ifdef SEEK_SET
+	w = wohin[w];
+#endif
+	return	!(f = unit_chk(*Unit, "fseek"))
+		|| fseek(f, *offset, w) ? 1 : 0;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/games.c b/igraph/src/games.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/games.c
@@ -0,0 +1,4784 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph R library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_interface.h"
+#include "igraph_games.h"
+#include "igraph_random.h"
+#include "igraph_memory.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_attributes.h"
+#include "igraph_constructors.h"
+#include "igraph_nongraph.h"
+#include "igraph_conversion.h"
+#include "igraph_psumtree.h"
+#include "igraph_dqueue.h"
+#include "igraph_adjlist.h"
+#include "igraph_iterators.h"
+#include "igraph_progress.h"
+#include "igraph_topology.h"
+#include "igraph_types_internal.h"
+#include "config.h"
+
+#include <math.h>
+
+typedef struct {
+    long int no;
+    igraph_psumtree_t *sumtrees;
+} igraph_i_citing_cited_type_game_struct_t;
+
+void igraph_i_citing_cited_type_game_free (
+    igraph_i_citing_cited_type_game_struct_t *s);
+/**
+ * \section about_games
+ *
+ * <para>Games are randomized graph generators. Randomization means that
+ * they generate a different graph every time you call them. </para>
+ */
+
+int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n,
+                               igraph_integer_t m,
+                               const igraph_vector_t *outseq,
+                               igraph_bool_t outpref,
+                               igraph_bool_t directed,
+                               const igraph_t *start_from);
+
+int igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph,
+        igraph_integer_t n,
+        igraph_real_t power,
+        igraph_integer_t m,
+        const igraph_vector_t *outseq,
+        igraph_bool_t outpref,
+        igraph_real_t A,
+        igraph_bool_t directed,
+        const igraph_t *start_from);
+
+int igraph_i_barabasi_game_psumtree(igraph_t *graph,
+                                    igraph_integer_t n,
+                                    igraph_real_t power,
+                                    igraph_integer_t m,
+                                    const igraph_vector_t *outseq,
+                                    igraph_bool_t outpref,
+                                    igraph_real_t A,
+                                    igraph_bool_t directed,
+                                    const igraph_t *start_from);
+
+int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n,
+                               igraph_integer_t m,
+                               const igraph_vector_t *outseq,
+                               igraph_bool_t outpref,
+                               igraph_bool_t directed,
+                               const igraph_t *start_from) {
+
+    long int no_of_nodes = n;
+    long int no_of_neighbors = m;
+    long int *bag;
+    long int bagp = 0;
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int resp;
+    long int i, j, k;
+    long int bagsize, start_nodes, start_edges, new_edges, no_of_edges;
+
+    if (!directed) {
+        outpref = 1;
+    }
+
+    start_nodes = start_from ? igraph_vcount(start_from) : 1;
+    start_edges = start_from ? igraph_ecount(start_from) : 0;
+    if (outseq) {
+        if (igraph_vector_size(outseq) > 1) {
+            new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]);
+        } else {
+            new_edges = 0;
+        }
+    } else {
+        new_edges = (no_of_nodes - start_nodes) * no_of_neighbors;
+    }
+    no_of_edges = start_edges + new_edges;
+    resp = start_edges * 2;
+    bagsize = no_of_nodes + no_of_edges + (outpref ? no_of_edges : 0);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+
+    bag = igraph_Calloc(bagsize, long int);
+    if (bag == 0) {
+        IGRAPH_ERROR("barabasi_game failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, bag);    /* TODO: hack */
+
+    /* The first node(s) in the bag */
+    if (start_from) {
+        igraph_vector_t deg;
+        long int ii, jj, sn = igraph_vcount(start_from);
+        igraph_neimode_t mm = outpref ? IGRAPH_ALL : IGRAPH_IN;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&deg, sn);
+        IGRAPH_CHECK(igraph_degree(start_from, &deg, igraph_vss_all(), mm,
+                                   IGRAPH_LOOPS));
+        for (ii = 0; ii < sn; ii++) {
+            long int d = (long int) VECTOR(deg)[ii];
+            for (jj = 0; jj <= d; jj++) {
+                bag[bagp++] = ii;
+            }
+        }
+
+        igraph_vector_destroy(&deg);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        bag[bagp++] = 0;
+    }
+
+    /* Initialize the edges vector */
+    if (start_from) {
+        IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0));
+        igraph_vector_resize(&edges, no_of_edges * 2);
+    }
+
+    RNG_BEGIN();
+
+    /* and the others */
+
+    for (i = (start_from ? start_nodes : 1), k = (start_from ? 0 : 1);
+         i < no_of_nodes; i++, k++) {
+        /* draw edges */
+        if (outseq) {
+            no_of_neighbors = (long int) VECTOR(*outseq)[k];
+        }
+        for (j = 0; j < no_of_neighbors; j++) {
+            long int to = bag[RNG_INTEGER(0, bagp - 1)];
+            VECTOR(edges)[resp++] = i;
+            VECTOR(edges)[resp++] = to;
+        }
+        /* update bag */
+        bag[bagp++] = i;
+        for (j = 0; j < no_of_neighbors; j++) {
+            bag[bagp++] = (long int) VECTOR(edges)[resp - 2 * j - 1];
+            if (outpref) {
+                bag[bagp++] = i;
+            }
+        }
+    }
+
+    RNG_END();
+
+    igraph_Free(bag);
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph,
+        igraph_integer_t n,
+        igraph_real_t power,
+        igraph_integer_t m,
+        const igraph_vector_t *outseq,
+        igraph_bool_t outpref,
+        igraph_real_t A,
+        igraph_bool_t directed,
+        const igraph_t *start_from) {
+
+    long int no_of_nodes = n;
+    long int no_of_neighbors = m;
+    igraph_vector_t edges;
+    long int i, j, k;
+    igraph_psumtree_t sumtree;
+    long int edgeptr = 0;
+    igraph_vector_t degree;
+    long int start_nodes, start_edges, new_edges, no_of_edges;
+
+    if (!directed) {
+        outpref = 1;
+    }
+
+    start_nodes = start_from ? igraph_vcount(start_from) : 1;
+    start_edges = start_from ? igraph_ecount(start_from) : 0;
+    if (outseq) {
+        if (igraph_vector_size(outseq) > 1) {
+            new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]);
+        } else {
+            new_edges = 0;
+        }
+    } else {
+        new_edges = (no_of_nodes - start_nodes) * no_of_neighbors;
+    }
+    no_of_edges = start_edges + new_edges;
+    edgeptr = start_edges * 2;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+    IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes));
+    IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    /* first node(s) */
+    if (start_from) {
+        long int ii, sn = igraph_vcount(start_from);
+        igraph_neimode_t mm = outpref ? IGRAPH_ALL : IGRAPH_IN;
+        IGRAPH_CHECK(igraph_degree(start_from, &degree, igraph_vss_all(), mm,
+                                   IGRAPH_LOOPS));
+        IGRAPH_CHECK(igraph_vector_resize(&degree,  no_of_nodes));
+        for (ii = 0; ii < sn; ii++) {
+            igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A);
+        }
+    } else {
+        igraph_psumtree_update(&sumtree, 0, A);
+    }
+
+    /* Initialize the edges vector */
+    if (start_from) {
+        IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0));
+        igraph_vector_resize(&edges, no_of_edges * 2);
+    }
+
+    RNG_BEGIN();
+
+    /* and the rest */
+    for (i = (start_from ? start_nodes : 1), k = (start_from ? 0 : 1);
+         i < no_of_nodes; i++, k++) {
+        igraph_real_t sum = igraph_psumtree_sum(&sumtree);
+        long int to;
+        if (outseq) {
+            no_of_neighbors = (long int) VECTOR(*outseq)[k];
+        }
+        for (j = 0; j < no_of_neighbors; j++) {
+            igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum));
+            VECTOR(degree)[to]++;
+            VECTOR(edges)[edgeptr++] = i;
+            VECTOR(edges)[edgeptr++] = to;
+        }
+        /* update probabilities */
+        for (j = 0; j < no_of_neighbors; j++) {
+            long int nn = (long int) VECTOR(edges)[edgeptr - 2 * j - 1];
+            igraph_psumtree_update(&sumtree, nn,
+                                   pow(VECTOR(degree)[nn], power) + A);
+        }
+        if (outpref) {
+            VECTOR(degree)[i] += no_of_neighbors;
+            igraph_psumtree_update(&sumtree, i,
+                                   pow(VECTOR(degree)[i], power) + A);
+        } else {
+            igraph_psumtree_update(&sumtree, i, A);
+        }
+    }
+
+    RNG_END();
+
+    igraph_psumtree_destroy(&sumtree);
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_barabasi_game_psumtree(igraph_t *graph,
+                                    igraph_integer_t n,
+                                    igraph_real_t power,
+                                    igraph_integer_t m,
+                                    const igraph_vector_t *outseq,
+                                    igraph_bool_t outpref,
+                                    igraph_real_t A,
+                                    igraph_bool_t directed,
+                                    const igraph_t *start_from) {
+
+    long int no_of_nodes = n;
+    long int no_of_neighbors = m;
+    igraph_vector_t edges;
+    long int i, j, k;
+    igraph_psumtree_t sumtree;
+    long int edgeptr = 0;
+    igraph_vector_t degree;
+    long int start_nodes, start_edges, new_edges, no_of_edges;
+
+    if (!directed) {
+        outpref = 1;
+    }
+
+    start_nodes = start_from ? igraph_vcount(start_from) : 1;
+    start_edges = start_from ? igraph_ecount(start_from) : 0;
+    if (outseq) {
+        if (igraph_vector_size(outseq) > 1) {
+            new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]);
+        } else {
+            new_edges = 0;
+        }
+    } else {
+        new_edges = (no_of_nodes - start_nodes) * no_of_neighbors;
+    }
+    no_of_edges = start_edges + new_edges;
+    edgeptr = start_edges * 2;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+    IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes));
+    IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    RNG_BEGIN();
+
+    /* first node(s) */
+    if (start_from) {
+        long int ii, sn = igraph_vcount(start_from);
+        igraph_neimode_t mm = outpref ? IGRAPH_ALL : IGRAPH_IN;
+        IGRAPH_CHECK(igraph_degree(start_from, &degree, igraph_vss_all(), mm,
+                                   IGRAPH_LOOPS));
+        IGRAPH_CHECK(igraph_vector_resize(&degree,  no_of_nodes));
+        for (ii = 0; ii < sn; ii++) {
+            igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A);
+        }
+    } else {
+        igraph_psumtree_update(&sumtree, 0, A);
+    }
+
+    /* Initialize the edges vector */
+    if (start_from) {
+        IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0));
+    }
+
+    /* and the rest */
+    for (i = (start_from ? start_nodes : 1), k = (start_from ? 0 : 1);
+         i < no_of_nodes; i++, k++) {
+        igraph_real_t sum;
+        long int to;
+        if (outseq) {
+            no_of_neighbors = (long int) VECTOR(*outseq)[k];
+        }
+        if (no_of_neighbors >= i) {
+            /* All existing vertices are cited */
+            for (to = 0; to < i; to++) {
+                VECTOR(degree)[to]++;
+                igraph_vector_push_back(&edges, i);
+                igraph_vector_push_back(&edges, to);
+                edgeptr += 2;
+                igraph_psumtree_update(&sumtree, to, pow(VECTOR(degree)[to], power) + A);
+            }
+        } else {
+            for (j = 0; j < no_of_neighbors; j++) {
+                sum = igraph_psumtree_sum(&sumtree);
+                igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum));
+                VECTOR(degree)[to]++;
+                igraph_vector_push_back(&edges, i);
+                igraph_vector_push_back(&edges, to);
+                edgeptr += 2;
+                igraph_psumtree_update(&sumtree, to, 0.0);
+            }
+            /* update probabilities */
+            for (j = 0; j < no_of_neighbors; j++) {
+                long int nn = (long int) VECTOR(edges)[edgeptr - 2 * j - 1];
+                igraph_psumtree_update(&sumtree, nn,
+                                       pow(VECTOR(degree)[nn], power) + A);
+            }
+        }
+        if (outpref) {
+            VECTOR(degree)[i] += no_of_neighbors > i ? i : no_of_neighbors;
+            igraph_psumtree_update(&sumtree, i,
+                                   pow(VECTOR(degree)[i], power) + A);
+        } else {
+            igraph_psumtree_update(&sumtree, i, A);
+        }
+    }
+
+    RNG_END();
+
+    igraph_psumtree_destroy(&sumtree);
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_barabasi_game
+ * \brief Generates a graph based on the Barab&aacute;si-Albert model.
+ *
+ * \param graph An uninitialized graph object.
+ * \param n The number of vertices in the graph.
+ * \param power Power of the preferential attachment. The probability
+ *        that a vertex is cited is proportional to d^power+A, where
+ *        d is its degree (see also the \p outpref argument), power
+ *        and A are given by arguments. In the classic preferential
+ *        attachment model power=1.
+ * \param m The number of outgoing edges generated for each
+ *        vertex. (Only if \p outseq is \c NULL.)
+ * \param outseq Gives the (out-)degrees of the vertices. If this is
+ *        constant, this can be a NULL pointer or an empty (but
+ *        initialized!) vector, in this case \p m contains
+ *        the constant out-degree. The very first vertex has by definition
+ *        no outgoing edges, so the first number in this vector is
+ *        ignored.
+ * \param outpref Boolean, if true not only the in- but also the out-degree
+ *        of a vertex increases its citation probability. Ie. the
+ *        citation probability is determined by the total degree of
+ *        the vertices. Ignored and assumed to be true if the graph
+ *        being generated is undirected.
+ * \param A The probability that a vertex is cited is proportional to
+ *        d^power+A, where d is its degree (see also the \p outpref
+ *        argument), power and A are given by arguments. In the
+ *        previous versions of the function this parameter was
+ *        implicitly set to one.
+ * \param directed Boolean, whether to generate a directed graph.
+ * \param algo The algorithm to use to generate the network. Possible
+ *        values:
+ *        \clist
+ *        \cli IGRAPH_BARABASI_BAG
+ *          This is the algorithm that was previously (before version
+ *          0.6) solely implemented in igraph. It works by putting the
+ *          ids of the vertices into a bag (multiset, really), exactly
+ *          as many times as their (in-)degree, plus once more. Then
+ *          the required number of cited vertices are drawn from the
+ *          bag, with replacement. This method might generate multiple
+ *          edges. It only works if power=1 and A=1.
+ *        \cli IGRAPH_BARABASI_PSUMTREE
+ *          This algorithm uses a partial prefix-sum tree to generate
+ *          the graph. It does not generate multiple edges and
+ *          works for any power and A values.
+ *        \cli IGRAPH_BARABASI_PSUMTREE_MULTIPLE
+ *          This algorithm also uses a partial prefix-sum tree to
+ *          generate the graph. The difference is, that now multiple
+ *          edges are allowed. This method was implemented under the
+ *          name \c igraph_nonlinear_barabasi_game before version 0.6.
+ *        \endclist
+ * \param start_from Either a null pointer, or a graph. In the former
+ *        case, the starting configuration is a clique of size \p m.
+ *        In the latter case, the graph is a starting configuration.
+ *        The graph must be non-empty, i.e. it must have at least one
+ *        vertex. If a graph is supplied here and the \p outseq
+ *        argument is also given, then \p outseq should only contain
+ *        information on the vertices that are not in the \p
+ *        start_from graph.
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid \p n,
+ *         \p m or \p outseq parameter.
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices plus the number of edges.
+ *
+ * \example examples/simple/igraph_barabasi_game.c
+ * \example examples/simple/igraph_barabasi_game2.c
+ */
+
+int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n,
+                         igraph_real_t power,
+                         igraph_integer_t m,
+                         const igraph_vector_t *outseq,
+                         igraph_bool_t outpref,
+                         igraph_real_t A,
+                         igraph_bool_t directed,
+                         igraph_barabasi_algorithm_t algo,
+                         const igraph_t *start_from) {
+
+    long int start_nodes = start_from ? igraph_vcount(start_from) : 0;
+    long int newn = start_from ? n - start_nodes : n;
+
+    /* Fix obscure parameterizations */
+    if (outseq && igraph_vector_size(outseq) == 0) {
+        outseq = 0;
+    }
+    if (!directed) {
+        outpref = 1;
+    }
+
+    /* Check arguments */
+
+    if (algo != IGRAPH_BARABASI_BAG &&
+        algo != IGRAPH_BARABASI_PSUMTREE &&
+        algo != IGRAPH_BARABASI_PSUMTREE_MULTIPLE) {
+        IGRAPH_ERROR("Invalid algorithm", IGRAPH_EINVAL);
+    }
+    if (n < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    } else if (newn < 0) {
+        IGRAPH_ERROR("Starting graph has too many vertices", IGRAPH_EINVAL);
+    }
+    if (start_from && start_nodes == 0) {
+        IGRAPH_ERROR("Cannot start from an empty graph", IGRAPH_EINVAL);
+    }
+    if (outseq != 0 && igraph_vector_size(outseq) != 0 &&
+        igraph_vector_size(outseq) != newn) {
+        IGRAPH_ERROR("Invalid out degree sequence length", IGRAPH_EINVAL);
+    }
+    if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) {
+        IGRAPH_ERROR("Invalid out degree", IGRAPH_EINVAL);
+    }
+    if (outseq && igraph_vector_min(outseq) < 0) {
+        IGRAPH_ERROR("Negative out degree in sequence", IGRAPH_EINVAL);
+    }
+    if (!outpref && A <= 0) {
+        IGRAPH_ERROR("Constant attractiveness (A) must be positive",
+                     IGRAPH_EINVAL);
+    }
+    if (outpref && A < 0) {
+        IGRAPH_ERROR("Constant attractiveness (A) must be non-negative",
+                     IGRAPH_EINVAL);
+    }
+    if (algo == IGRAPH_BARABASI_BAG) {
+        if (power != 1) {
+            IGRAPH_ERROR("Power must be one for 'bag' algorithm", IGRAPH_EINVAL);
+        }
+        if (A != 1) {
+            IGRAPH_ERROR("Constant attractiveness (A) must be one for bag algorithm",
+                         IGRAPH_EINVAL);
+        }
+    }
+    if (start_from && directed != igraph_is_directed(start_from)) {
+        IGRAPH_WARNING("Directedness of the start graph and the output graph"
+                       " mismatch");
+    }
+    if (start_from && !igraph_is_directed(start_from) && !outpref) {
+        IGRAPH_ERROR("`outpref' must be true if starting from an undirected "
+                     "graph", IGRAPH_EINVAL);
+    }
+
+    if (n == 0) {
+        return igraph_empty(graph, 0, directed);
+    }
+
+    if (algo == IGRAPH_BARABASI_BAG) {
+        return igraph_i_barabasi_game_bag(graph, n, m, outseq, outpref, directed,
+                                          start_from);
+    } else if (algo == IGRAPH_BARABASI_PSUMTREE) {
+        return igraph_i_barabasi_game_psumtree(graph, n, power, m, outseq,
+                                               outpref, A, directed, start_from);
+    } else if (algo == IGRAPH_BARABASI_PSUMTREE_MULTIPLE) {
+        return igraph_i_barabasi_game_psumtree_multiple(graph, n, power, m,
+                outseq, outpref, A,
+                directed, start_from);
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup internal
+ */
+
+int igraph_erdos_renyi_game_gnp(igraph_t *graph, igraph_integer_t n, igraph_real_t p,
+                                igraph_bool_t directed, igraph_bool_t loops) {
+
+    long int no_of_nodes = n;
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    igraph_vector_t s = IGRAPH_VECTOR_NULL;
+    int retval = 0;
+
+    if (n < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    }
+    if (p < 0.0 || p > 1.0) {
+        IGRAPH_ERROR("Invalid probability given", IGRAPH_EINVAL);
+    }
+
+    if (p == 0.0 || no_of_nodes <= 1) {
+        IGRAPH_CHECK(retval = igraph_empty(graph, n, directed));
+    } else if (p == 1.0) {
+        IGRAPH_CHECK(retval = igraph_full(graph, n, directed, loops));
+    } else {
+
+        long int i;
+        double maxedges = n, last;
+        if (directed && loops) {
+            maxedges *= n;
+        } else if (directed && !loops) {
+            maxedges *= (n - 1);
+        } else if (!directed && loops) {
+            maxedges *= (n + 1) / 2.0;
+        } else {
+            maxedges *= (n - 1) / 2.0;
+        }
+
+        IGRAPH_VECTOR_INIT_FINALLY(&s, 0);
+        IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1)));
+
+        RNG_BEGIN();
+
+        last = RNG_GEOM(p);
+        while (last < maxedges) {
+            IGRAPH_CHECK(igraph_vector_push_back(&s, last));
+            last += RNG_GEOM(p);
+            last += 1;
+        }
+
+        RNG_END();
+
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2));
+
+        if (directed && loops) {
+            for (i = 0; i < igraph_vector_size(&s); i++) {
+                long int to = (long int) floor(VECTOR(s)[i] / no_of_nodes);
+                long int from = (long int) (VECTOR(s)[i] - ((igraph_real_t)to) * no_of_nodes);
+                igraph_vector_push_back(&edges, from);
+                igraph_vector_push_back(&edges, to);
+            }
+        } else if (directed && !loops) {
+            for (i = 0; i < igraph_vector_size(&s); i++) {
+                long int to = (long int) floor(VECTOR(s)[i] / no_of_nodes);
+                long int from = (long int) (VECTOR(s)[i] - ((igraph_real_t)to) * no_of_nodes);
+                if (from == to) {
+                    to = no_of_nodes - 1;
+                }
+                igraph_vector_push_back(&edges, from);
+                igraph_vector_push_back(&edges, to);
+            }
+        } else if (!directed && loops) {
+            for (i = 0; i < igraph_vector_size(&s); i++) {
+                long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2);
+                long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2);
+                igraph_vector_push_back(&edges, from);
+                igraph_vector_push_back(&edges, to);
+            }
+        } else { /* !directed && !loops */
+            for (i = 0; i < igraph_vector_size(&s); i++) {
+                long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2);
+                long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2);
+                igraph_vector_push_back(&edges, from);
+                igraph_vector_push_back(&edges, to);
+            }
+        }
+
+        igraph_vector_destroy(&s);
+        IGRAPH_FINALLY_CLEAN(1);
+        IGRAPH_CHECK(retval = igraph_create(graph, &edges, n, directed));
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return retval;
+}
+
+int igraph_erdos_renyi_game_gnm(igraph_t *graph, igraph_integer_t n, igraph_real_t m,
+                                igraph_bool_t directed, igraph_bool_t loops) {
+
+    igraph_integer_t no_of_nodes = n;
+    igraph_integer_t no_of_edges = (igraph_integer_t) m;
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    igraph_vector_t s = IGRAPH_VECTOR_NULL;
+    int retval = 0;
+
+    if (n < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    }
+    if (m < 0) {
+        IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL);
+    }
+
+    if (m == 0.0 || no_of_nodes <= 1) {
+        IGRAPH_CHECK(retval = igraph_empty(graph, n, directed));
+    } else {
+
+        long int i;
+        double maxedges = n;
+        if (directed && loops) {
+            maxedges *= n;
+        } else if (directed && !loops) {
+            maxedges *= (n - 1);
+        } else if (!directed && loops) {
+            maxedges *= (n + 1) / 2.0;
+        } else {
+            maxedges *= (n - 1) / 2.0;
+        }
+
+        if (no_of_edges > maxedges) {
+            IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL);
+        }
+
+        if (maxedges == no_of_edges) {
+            retval = igraph_full(graph, n, directed, loops);
+        } else {
+
+            long int slen;
+
+            IGRAPH_VECTOR_INIT_FINALLY(&s, 0);
+            IGRAPH_CHECK(igraph_random_sample(&s, 0, maxedges - 1,
+                                              (igraph_integer_t) no_of_edges));
+
+            IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+            IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2));
+
+            slen = igraph_vector_size(&s);
+            if (directed && loops) {
+                for (i = 0; i < slen; i++) {
+                    long int to = (long int) floor(VECTOR(s)[i] / no_of_nodes);
+                    long int from = (long int) (VECTOR(s)[i] - ((igraph_real_t)to) * no_of_nodes);
+                    igraph_vector_push_back(&edges, from);
+                    igraph_vector_push_back(&edges, to);
+                }
+            } else if (directed && !loops) {
+                for (i = 0; i < slen; i++) {
+                    long int from = (long int) floor(VECTOR(s)[i] / (no_of_nodes - 1));
+                    long int to = (long int) (VECTOR(s)[i] - ((igraph_real_t)from) * (no_of_nodes - 1));
+                    if (from == to) {
+                        to = no_of_nodes - 1;
+                    }
+                    igraph_vector_push_back(&edges, from);
+                    igraph_vector_push_back(&edges, to);
+                }
+            } else if (!directed && loops) {
+                for (i = 0; i < slen; i++) {
+                    long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2);
+                    long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2);
+                    igraph_vector_push_back(&edges, from);
+                    igraph_vector_push_back(&edges, to);
+                }
+            } else { /* !directed && !loops */
+                for (i = 0; i < slen; i++) {
+                    long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2);
+                    long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2);
+                    igraph_vector_push_back(&edges, from);
+                    igraph_vector_push_back(&edges, to);
+                }
+            }
+
+            igraph_vector_destroy(&s);
+            IGRAPH_FINALLY_CLEAN(1);
+            retval = igraph_create(graph, &edges, n, directed);
+            igraph_vector_destroy(&edges);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    return retval;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_erdos_renyi_game
+ * \brief Generates a random (Erdos-Renyi) graph.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param type The type of the random graph, possible values:
+ *        \clist
+ *        \cli IGRAPH_ERDOS_RENYI_GNM
+ *          G(n,m) graph,
+ *          m edges are
+ *          selected uniformly randomly in a graph with
+ *          n vertices.
+ *        \cli IGRAPH_ERDOS_RENYI_GNP
+ *          G(n,p) graph,
+ *          every possible edge is included in the graph with
+ *          probability p.
+ *        \endclist
+ * \param n The number of vertices in the graph.
+ * \param p_or_m This is the p parameter for
+ *        G(n,p) graphs and the
+ *        m
+ *        parameter for G(n,m) graphs.
+ * \param directed Logical, whether to generate a directed graph.
+ * \param loops Logical, whether to generate loops (self) edges.
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid
+ *         \p type, \p n,
+ *         \p p or \p m
+ *          parameter.
+ *         \c IGRAPH_ENOMEM: there is not enough
+ *         memory for the operation.
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices plus the number of edges in the graph.
+ *
+ * \sa \ref igraph_barabasi_game(), \ref igraph_growing_random_game()
+ *
+ * \example examples/simple/igraph_erdos_renyi_game.c
+ */
+
+int igraph_erdos_renyi_game(igraph_t *graph, igraph_erdos_renyi_t type,
+                            igraph_integer_t n, igraph_real_t p_or_m,
+                            igraph_bool_t directed, igraph_bool_t loops) {
+    int retval = 0;
+    if (type == IGRAPH_ERDOS_RENYI_GNP) {
+        retval = igraph_erdos_renyi_game_gnp(graph, n, p_or_m, directed, loops);
+    } else if (type == IGRAPH_ERDOS_RENYI_GNM) {
+        retval = igraph_erdos_renyi_game_gnm(graph, n, p_or_m, directed, loops);
+    } else {
+        IGRAPH_ERROR("Invalid type", IGRAPH_EINVAL);
+    }
+
+    return retval;
+}
+
+int igraph_degree_sequence_game_simple(igraph_t *graph,
+                                       const igraph_vector_t *out_seq,
+                                       const igraph_vector_t *in_seq);
+
+int igraph_degree_sequence_game_simple(igraph_t *graph,
+                                       const igraph_vector_t *out_seq,
+                                       const igraph_vector_t *in_seq) {
+
+    long int outsum = 0, insum = 0;
+    igraph_bool_t directed = (in_seq != 0 && igraph_vector_size(in_seq) != 0);
+    igraph_bool_t degseq_ok;
+    long int no_of_nodes, no_of_edges;
+    long int *bag1 = 0, *bag2 = 0;
+    long int bagp1 = 0, bagp2 = 0;
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int i, j;
+
+    IGRAPH_CHECK(igraph_is_degree_sequence(out_seq, in_seq, &degseq_ok));
+    if (!degseq_ok) {
+        IGRAPH_ERROR(in_seq ? "No directed graph can realize the given degree sequences" :
+                     "No undirected graph can realize the given degree sequence", IGRAPH_EINVAL);
+    }
+
+    outsum = (long int) igraph_vector_sum(out_seq);
+    if (directed) {
+        insum = (long int) igraph_vector_sum(in_seq);
+    }
+
+    no_of_nodes = igraph_vector_size(out_seq);
+    no_of_edges = directed ? outsum : outsum / 2;
+
+    bag1 = igraph_Calloc(outsum, long int);
+    if (bag1 == 0) {
+        IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, bag1);   /* TODO: hack */
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j < VECTOR(*out_seq)[i]; j++) {
+            bag1[bagp1++] = i;
+        }
+    }
+    if (directed) {
+        bag2 = igraph_Calloc(insum, long int);
+        if (bag2 == 0) {
+            IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(free, bag2);
+        for (i = 0; i < no_of_nodes; i++) {
+            for (j = 0; j < VECTOR(*in_seq)[i]; j++) {
+                bag2[bagp2++] = i;
+            }
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+
+    RNG_BEGIN();
+
+    if (directed) {
+        for (i = 0; i < no_of_edges; i++) {
+            long int from = RNG_INTEGER(0, bagp1 - 1);
+            long int to = RNG_INTEGER(0, bagp2 - 1);
+            igraph_vector_push_back(&edges, bag1[from]); /* safe, already reserved */
+            igraph_vector_push_back(&edges, bag2[to]);   /* ditto */
+            bag1[from] = bag1[bagp1 - 1];
+            bag2[to] = bag2[bagp2 - 1];
+            bagp1--; bagp2--;
+        }
+    } else {
+        for (i = 0; i < no_of_edges; i++) {
+            long int from = RNG_INTEGER(0, bagp1 - 1);
+            long int to;
+            igraph_vector_push_back(&edges, bag1[from]); /* safe, already reserved */
+            bag1[from] = bag1[bagp1 - 1];
+            bagp1--;
+            to = RNG_INTEGER(0, bagp1 - 1);
+            igraph_vector_push_back(&edges, bag1[to]);   /* ditto */
+            bag1[to] = bag1[bagp1 - 1];
+            bagp1--;
+        }
+    }
+
+    RNG_END();
+
+    igraph_Free(bag1);
+    IGRAPH_FINALLY_CLEAN(1);
+    if (directed) {
+        igraph_Free(bag2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_degree_sequence_game_no_multiple_undirected(
+    igraph_t *graph, const igraph_vector_t *seq) {
+
+    igraph_vector_t stubs = IGRAPH_VECTOR_NULL;
+    igraph_vector_int_t *neis;
+    igraph_vector_t residual_degrees = IGRAPH_VECTOR_NULL;
+    igraph_set_t incomplete_vertices;
+    igraph_adjlist_t al;
+    igraph_bool_t finished, failed;
+    igraph_integer_t from, to, dummy;
+    long int i, j, k;
+    long int no_of_nodes, outsum = 0;
+    igraph_bool_t degseq_ok;
+
+    IGRAPH_CHECK(igraph_is_graphical_degree_sequence(seq, 0, &degseq_ok));
+    if (!degseq_ok) {
+        IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence",
+                     IGRAPH_EINVAL);
+    }
+
+    outsum = (long int) igraph_vector_sum(seq);
+    no_of_nodes = igraph_vector_size(seq);
+
+    /* Allocate required data structures */
+    IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
+    IGRAPH_VECTOR_INIT_FINALLY(&stubs, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&stubs, outsum));
+    IGRAPH_VECTOR_INIT_FINALLY(&residual_degrees, no_of_nodes);
+    IGRAPH_CHECK(igraph_set_init(&incomplete_vertices, 0));
+    IGRAPH_FINALLY(igraph_set_destroy, &incomplete_vertices);
+
+    /* Start the RNG */
+    RNG_BEGIN();
+
+    /* Outer loop; this will try to construct a graph several times from scratch
+     * until it finally succeeds. */
+    finished = 0;
+    while (!finished) {
+        /* Be optimistic :) */
+        failed = 0;
+
+        /* Clear the adjacency list to get rid of the previous attempt (if any) */
+        igraph_adjlist_clear(&al);
+
+        /* Initialize the residual degrees from the degree sequence */
+        IGRAPH_CHECK(igraph_vector_update(&residual_degrees, seq));
+
+        /* While there are some unconnected stubs left... */
+        while (!finished && !failed) {
+            /* Construct the initial stub vector */
+            igraph_vector_clear(&stubs);
+            for (i = 0; i < no_of_nodes; i++) {
+                for (j = 0; j < VECTOR(residual_degrees)[i]; j++) {
+                    igraph_vector_push_back(&stubs, i);
+                }
+            }
+
+            /* Clear the skipped stub counters and the set of incomplete vertices */
+            igraph_vector_null(&residual_degrees);
+            igraph_set_clear(&incomplete_vertices);
+
+            /* Shuffle the stubs in-place */
+            igraph_vector_shuffle(&stubs);
+
+            /* Connect the stubs where possible */
+            k = igraph_vector_size(&stubs);
+            for (i = 0; i < k; ) {
+                from = (igraph_integer_t) VECTOR(stubs)[i++];
+                to = (igraph_integer_t) VECTOR(stubs)[i++];
+
+                if (from > to) {
+                    dummy = from; from = to; to = dummy;
+                }
+
+                neis = igraph_adjlist_get(&al, from);
+                if (from == to || igraph_vector_int_binsearch(neis, to, &j)) {
+                    /* Edge exists already */
+                    VECTOR(residual_degrees)[from]++;
+                    VECTOR(residual_degrees)[to]++;
+                    IGRAPH_CHECK(igraph_set_add(&incomplete_vertices, from));
+                    IGRAPH_CHECK(igraph_set_add(&incomplete_vertices, to));
+                } else {
+                    /* Insert the edge */
+                    IGRAPH_CHECK(igraph_vector_int_insert(neis, j, to));
+                }
+            }
+
+            finished = igraph_set_empty(&incomplete_vertices);
+
+            if (!finished) {
+                /* We are not done yet; check if the remaining stubs are feasible. This
+                 * is done by enumerating all possible pairs and checking whether at
+                 * least one feasible pair is found. */
+                i = 0;
+                failed = 1;
+                while (failed && igraph_set_iterate(&incomplete_vertices, &i, &from)) {
+                    j = 0;
+                    while (igraph_set_iterate(&incomplete_vertices, &j, &to)) {
+                        if (from == to) {
+                            /* This is used to ensure that each pair is checked once only */
+                            break;
+                        }
+                        if (from > to) {
+                            dummy = from; from = to; to = dummy;
+                        }
+                        neis = igraph_adjlist_get(&al, from);
+                        if (!igraph_vector_int_binsearch(neis, to, 0)) {
+                            /* Found a suitable pair, so we can continue */
+                            failed = 0;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* Finish the RNG */
+    RNG_END();
+
+    /* Clean up */
+    igraph_set_destroy(&incomplete_vertices);
+    igraph_vector_destroy(&residual_degrees);
+    igraph_vector_destroy(&stubs);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    /* Create the graph. We cannot use IGRAPH_ALL here for undirected graphs
+     * because we did not add edges in both directions in the adjacency list.
+     * We will use igraph_to_undirected in an extra step. */
+    IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1));
+    IGRAPH_CHECK(igraph_to_undirected(graph, IGRAPH_TO_UNDIRECTED_EACH, 0));
+
+    /* Clear the adjacency list */
+    igraph_adjlist_destroy(&al);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_degree_sequence_game_no_multiple_directed(igraph_t *graph,
+        const igraph_vector_t *out_seq, const igraph_vector_t *in_seq) {
+    igraph_adjlist_t al;
+    igraph_bool_t deg_seq_ok, failed, finished;
+    igraph_vector_t in_stubs = IGRAPH_VECTOR_NULL;
+    igraph_vector_t out_stubs = IGRAPH_VECTOR_NULL;
+    igraph_vector_int_t *neis;
+    igraph_vector_t residual_in_degrees = IGRAPH_VECTOR_NULL;
+    igraph_vector_t residual_out_degrees = IGRAPH_VECTOR_NULL;
+    igraph_set_t incomplete_in_vertices;
+    igraph_set_t incomplete_out_vertices;
+    igraph_integer_t from, to;
+    long int i, j, k;
+    long int no_of_nodes, outsum;
+
+    IGRAPH_CHECK(igraph_is_graphical_degree_sequence(out_seq, in_seq, &deg_seq_ok));
+    if (!deg_seq_ok) {
+        IGRAPH_ERROR("No simple directed graph can realize the given degree sequence",
+                     IGRAPH_EINVAL);
+    }
+
+    outsum = (long int) igraph_vector_sum(out_seq);
+    no_of_nodes = igraph_vector_size(out_seq);
+
+    /* Allocate required data structures */
+    IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
+    IGRAPH_VECTOR_INIT_FINALLY(&out_stubs, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&out_stubs, outsum));
+    IGRAPH_VECTOR_INIT_FINALLY(&in_stubs, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&in_stubs, outsum));
+    IGRAPH_VECTOR_INIT_FINALLY(&residual_out_degrees, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&residual_in_degrees, no_of_nodes);
+    IGRAPH_CHECK(igraph_set_init(&incomplete_out_vertices, 0));
+    IGRAPH_FINALLY(igraph_set_destroy, &incomplete_out_vertices);
+    IGRAPH_CHECK(igraph_set_init(&incomplete_in_vertices, 0));
+    IGRAPH_FINALLY(igraph_set_destroy, &incomplete_in_vertices);
+
+    /* Start the RNG */
+    RNG_BEGIN();
+
+    /* Outer loop; this will try to construct a graph several times from scratch
+     * until it finally succeeds. */
+    finished = 0;
+    while (!finished) {
+        /* Be optimistic :) */
+        failed = 0;
+
+        /* Clear the adjacency list to get rid of the previous attempt (if any) */
+        igraph_adjlist_clear(&al);
+
+        /* Initialize the residual degrees from the degree sequences */
+        IGRAPH_CHECK(igraph_vector_update(&residual_out_degrees, out_seq));
+        IGRAPH_CHECK(igraph_vector_update(&residual_in_degrees, in_seq));
+
+        /* While there are some unconnected stubs left... */
+        while (!finished && !failed) {
+            /* Construct the initial stub vectors */
+            igraph_vector_clear(&out_stubs);
+            igraph_vector_clear(&in_stubs);
+            for (i = 0; i < no_of_nodes; i++) {
+                for (j = 0; j < VECTOR(residual_out_degrees)[i]; j++) {
+                    igraph_vector_push_back(&out_stubs, i);
+                }
+                for (j = 0; j < VECTOR(residual_in_degrees)[i]; j++) {
+                    igraph_vector_push_back(&in_stubs, i);
+                }
+            }
+
+            /* Clear the skipped stub counters and the set of incomplete vertices */
+            igraph_vector_null(&residual_out_degrees);
+            igraph_vector_null(&residual_in_degrees);
+            igraph_set_clear(&incomplete_out_vertices);
+            igraph_set_clear(&incomplete_in_vertices);
+            outsum = 0;
+
+            /* Shuffle the out-stubs in-place */
+            igraph_vector_shuffle(&out_stubs);
+
+            /* Connect the stubs where possible */
+            k = igraph_vector_size(&out_stubs);
+            for (i = 0; i < k; i++) {
+                from = (igraph_integer_t) VECTOR(out_stubs)[i];
+                to = (igraph_integer_t) VECTOR(in_stubs)[i];
+
+                neis = igraph_adjlist_get(&al, from);
+                if (from == to || igraph_vector_int_binsearch(neis, to, &j)) {
+                    /* Edge exists already */
+                    VECTOR(residual_out_degrees)[from]++;
+                    VECTOR(residual_in_degrees)[to]++;
+                    IGRAPH_CHECK(igraph_set_add(&incomplete_out_vertices, from));
+                    IGRAPH_CHECK(igraph_set_add(&incomplete_in_vertices, to));
+                } else {
+                    /* Insert the edge */
+                    IGRAPH_CHECK(igraph_vector_int_insert(neis, j, to));
+                }
+            }
+
+            /* Are we finished? */
+            finished = igraph_set_empty(&incomplete_out_vertices);
+
+            if (!finished) {
+                /* We are not done yet; check if the remaining stubs are feasible. This
+                 * is done by enumerating all possible pairs and checking whether at
+                 * least one feasible pair is found. */
+                i = 0;
+                failed = 1;
+                while (failed && igraph_set_iterate(&incomplete_out_vertices, &i, &from)) {
+                    j = 0;
+                    while (igraph_set_iterate(&incomplete_in_vertices, &j, &to)) {
+                        neis = igraph_adjlist_get(&al, from);
+                        if (from != to && !igraph_vector_int_binsearch(neis, to, 0)) {
+                            /* Found a suitable pair, so we can continue */
+                            failed = 0;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* Finish the RNG */
+    RNG_END();
+
+    /* Clean up */
+    igraph_set_destroy(&incomplete_in_vertices);
+    igraph_set_destroy(&incomplete_out_vertices);
+    igraph_vector_destroy(&residual_in_degrees);
+    igraph_vector_destroy(&residual_out_degrees);
+    igraph_vector_destroy(&in_stubs);
+    igraph_vector_destroy(&out_stubs);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    /* Create the graph */
+    IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1));
+
+    /* Clear the adjacency list */
+    igraph_adjlist_destroy(&al);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_degree_sequence_game_no_multiple_undirected_uniform(igraph_t *graph, const igraph_vector_t *degseq) {
+    igraph_vector_int_t stubs;
+    igraph_vector_t edges;
+    igraph_bool_t degseq_ok;
+    igraph_vector_ptr_t adjlist;
+    long i, j, k;
+    long vcount, ecount, stub_count;
+
+    IGRAPH_CHECK(igraph_is_graphical_degree_sequence(degseq, 0, &degseq_ok));
+    if (!degseq_ok) {
+        IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence", IGRAPH_EINVAL);
+    }
+
+    stub_count = (long) igraph_vector_sum(degseq);
+    ecount = stub_count / 2;
+    vcount = igraph_vector_size(degseq);
+
+    IGRAPH_VECTOR_INT_INIT_FINALLY(&stubs, stub_count);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, stub_count);
+
+    k = 0;
+    for (i = 0; i < vcount; ++i) {
+        long deg = (long) VECTOR(*degseq)[i];
+        for (j = 0; j < deg; ++j) {
+            VECTOR(stubs)[k++] = i;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&adjlist, vcount));
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&adjlist, igraph_set_destroy);
+    for (i = 0; i < vcount; ++i) {
+        igraph_set_t *set = igraph_malloc(sizeof(igraph_set_t));
+        if (! set) {
+            IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_set_init(set, 0));
+        VECTOR(adjlist)[i] = set;
+        IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*degseq)[i]));
+    }
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist);
+
+    RNG_BEGIN();
+
+    for (;;) {
+        igraph_bool_t success = 1;
+        IGRAPH_CHECK(igraph_vector_int_shuffle(&stubs));
+
+        for (i = 0; i < ecount; ++i) {
+            igraph_integer_t from = VECTOR(stubs)[2 * i];
+            igraph_integer_t to = VECTOR(stubs)[2 * i + 1];
+
+            /* loop edge, fail */
+            if (to == from) {
+                success = 0;
+                break;
+            }
+
+            /* multi-edge, fail */
+            if (igraph_set_contains((igraph_set_t *) VECTOR(adjlist)[to], from)) {
+                success = 0;
+                break;
+            }
+
+            /* sets are already reserved */
+            igraph_set_add((igraph_set_t *) VECTOR(adjlist)[to], from);
+            igraph_set_add((igraph_set_t *) VECTOR(adjlist)[from], to);
+
+            /* register edge */
+            VECTOR(edges)[2 * i]   = from;
+            VECTOR(edges)[2 * i + 1] = to;
+        }
+
+        if (success) {
+            break;
+        }
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        for (j = 0; j < vcount; ++j) {
+            igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]);
+        }
+    }
+
+    RNG_END();
+
+    igraph_vector_ptr_destroy_all(&adjlist);
+    igraph_vector_int_destroy(&stubs);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 0));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_degree_sequence_game_no_multiple_directed_uniform(
+    igraph_t *graph, const igraph_vector_t *out_deg, const igraph_vector_t *in_deg) {
+    igraph_vector_int_t out_stubs, in_stubs;
+    igraph_vector_t edges;
+    igraph_bool_t degseq_ok;
+    igraph_vector_ptr_t adjlist;
+    long i, j, k, l;
+    long vcount, ecount;
+
+    IGRAPH_CHECK(igraph_is_graphical_degree_sequence(out_deg, in_deg, &degseq_ok));
+    if (!degseq_ok) {
+        IGRAPH_ERROR("No simple directed graph can realize the given degree sequence", IGRAPH_EINVAL);
+    }
+
+    ecount = (long) igraph_vector_sum(out_deg);
+    vcount = igraph_vector_size(out_deg);
+
+    IGRAPH_VECTOR_INT_INIT_FINALLY(&out_stubs, ecount);
+    IGRAPH_VECTOR_INT_INIT_FINALLY(&in_stubs, ecount);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * ecount);
+
+    k = 0; l = 0;
+    for (i = 0; i < vcount; ++i) {
+        long dout, din;
+
+        dout = (long) VECTOR(*out_deg)[i];
+        for (j = 0; j < dout; ++j) {
+            VECTOR(out_stubs)[k++] = i;
+        }
+
+        din  = (long) VECTOR(*in_deg)[i];
+        for (j = 0; j < din; ++j) {
+            VECTOR(in_stubs)[l++] = i;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&adjlist, vcount));
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&adjlist, igraph_set_destroy);
+    for (i = 0; i < vcount; ++i) {
+        igraph_set_t *set = igraph_malloc(sizeof(igraph_set_t));
+        if (! set) {
+            IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_set_init(set, 0));
+        VECTOR(adjlist)[i] = set;
+        IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*out_deg)[i]));
+    }
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist);
+
+    RNG_BEGIN();
+
+    for (;;) {
+        igraph_bool_t success = 1;
+        IGRAPH_CHECK(igraph_vector_int_shuffle(&out_stubs));
+
+        for (i = 0; i < ecount; ++i) {
+            igraph_integer_t from = VECTOR(out_stubs)[i];
+            igraph_integer_t to = VECTOR(in_stubs)[i];
+            igraph_set_t *set;
+
+            /* loop edge, fail */
+            if (to == from) {
+                success = 0;
+                break;
+            }
+
+            /* multi-edge, fail */
+            set = (igraph_set_t *) VECTOR(adjlist)[from];
+            if (igraph_set_contains(set, to)) {
+                success = 0;
+                break;
+            }
+
+            /* sets are already reserved */
+            igraph_set_add(set, to);
+
+            /* register edge */
+            VECTOR(edges)[2 * i]   = from;
+            VECTOR(edges)[2 * i + 1] = to;
+        }
+
+        if (success) {
+            break;
+        }
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        for (j = 0; j < vcount; ++j) {
+            igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]);
+        }
+    }
+
+    RNG_END();
+
+    igraph_vector_ptr_destroy_all(&adjlist);
+    igraph_vector_int_destroy(&out_stubs);
+    igraph_vector_int_destroy(&in_stubs);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 1));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/* This is in gengraph_mr-connected.cpp */
+
+int igraph_degree_sequence_game_vl(igraph_t *graph,
+                                   const igraph_vector_t *out_seq,
+                                   const igraph_vector_t *in_seq);
+/**
+ * \ingroup generators
+ * \function igraph_degree_sequence_game
+ * \brief Generates a random graph with a given degree sequence
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param out_deg The degree sequence for an undirected graph (if
+ *        \p in_seq is of length zero), or the out-degree
+ *        sequence of a directed graph (if \p in_deq is not
+ *        of length zero.
+ * \param in_deg It is either a zero-length vector or
+ *        \c NULL (if an undirected
+ *        graph is generated), or the in-degree sequence.
+ * \param method The method to generate the graph. Possible values:
+ *        \clist
+ *          \cli IGRAPH_DEGSEQ_SIMPLE
+ *          This method implements the configuration model.
+ *          For undirected graphs, it puts all vertex IDs in a bag
+ *          such that the multiplicity of a vertex in the bag is the same as
+ *          its degree. Then it draws pairs from the bag until the bag becomes
+ *          empty. This method can generate both loop (self) edges and multiple
+ *          edges. For directed graphs, the algorithm is basically the same,
+ *          but two separate bags are used for the in- and out-degrees.
+ *          Undirected graphs are generated with probability proportional to
+ *          <code>(\prod_{i&lt;j} A_{ij} ! \prod_i A_{ii} !!)^{-1}</code>,
+ *          where \c A denotes the adjacency matrix and <code>!!</code> denotes
+ *          the double factorial.
+ *          The corresponding  expression for directed ones is
+ *          <code>(\prod_{i,j} A_{ij}!)^{-1}</code>.
+ *          Thus the probability of all simple graphs (which only have 0s and 1s
+ *          in the adjacency matrix) is the same, while that of
+ *          non-simple ones depends on their structure.
+ *          \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE
+ *          This method is similar to \c IGRAPH_DEGSEQ_SIMPLE
+ *          but tries to avoid multiple and loop edges and restarts the
+ *          generation from scratch if it gets stuck. It is not guaranteed
+ *          to sample uniformly from the space of all possible graphs with
+ *          the given sequence, but it is relatively fast and it will
+ *          eventually succeed if the provided degree sequence is graphical,
+ *          but there is no upper bound on the number of iterations.
+ *          \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM
+ *          This method is identical to \c IGRAPH_DEGSEQ_SIMPLE, but if the
+ *          generated graph is not simple, it rejects it and re-starts the
+ *          generation. It samples all simple graphs with the same probability.
+ *          \cli IGRAPH_DEGSEQ_VL
+ *          This method is a much more sophisticated generator than the
+ *          previous ones. It can sample undirected, connected simple graphs
+ *          uniformly and uses Monte-Carlo methods to randomize the graphs.
+ *          This generator should be favoured if undirected and connected
+ *          graphs are to be generated and execution time is not a concern.
+ *          igraph uses the original implementation of Fabien Viger; for the algorithm,
+ *          see https://www-complexnetworks.lip6.fr/~latapy/FV/generation.html
+ *          and the paper https://arxiv.org/abs/cs/0502085
+ *        \endclist
+ * \return Error code:
+ *          \c IGRAPH_ENOMEM: there is not enough
+ *           memory to perform the operation.
+ *          \c IGRAPH_EINVAL: invalid method parameter, or
+ *           invalid in- and/or out-degree vectors. The degree vectors
+ *           should be non-negative, \p out_deg should sum
+ *           up to an even integer for undirected graphs; the length
+ *           and sum of \p out_deg and
+ *           \p in_deg
+ *           should match for directed graphs.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges
+ *                  for \c IGRAPH_DEGSEQ_SIMPLE. The time complexity of the
+ *                  other modes is not known.
+ *
+ * \sa \ref igraph_barabasi_game(), \ref igraph_erdos_renyi_game(),
+ *     \ref igraph_is_degree_sequence(),
+ *     \ref igraph_is_graphical_degree_sequence()
+ *
+ * \example examples/simple/igraph_degree_sequence_game.c
+ */
+
+int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg,
+                                const igraph_vector_t *in_deg,
+                                igraph_degseq_t method) {
+    if (in_deg && igraph_vector_empty(in_deg) && !igraph_vector_empty(out_deg)) {
+        in_deg = 0;
+    }
+
+    switch (method) {
+    case IGRAPH_DEGSEQ_SIMPLE:
+        return igraph_degree_sequence_game_simple(graph, out_deg, in_deg);
+
+    case IGRAPH_DEGSEQ_VL:
+        return igraph_degree_sequence_game_vl(graph, out_deg, in_deg);
+
+    case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE:
+        if (in_deg == 0) {
+            return igraph_degree_sequence_game_no_multiple_undirected(graph, out_deg);
+        } else {
+            return igraph_degree_sequence_game_no_multiple_directed(graph, out_deg, in_deg);
+        }
+
+    case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM:
+        if (in_deg == 0) {
+            return igraph_degree_sequence_game_no_multiple_undirected_uniform(graph, out_deg);
+        } else {
+            return igraph_degree_sequence_game_no_multiple_directed_uniform(graph, out_deg, in_deg);
+        }
+
+    default:
+        IGRAPH_ERROR("Invalid degree sequence game method", IGRAPH_EINVAL);
+    }
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_growing_random_game
+ * \brief Generates a growing random graph.
+ *
+ * </para><para>
+ * This function simulates a growing random graph. In each discrete
+ * time step a new vertex is added and a number of new edges are also
+ * added. These graphs are known to be different from standard (not
+ * growing) random graphs.
+ * \param graph Uninitialized graph object.
+ * \param n The number of vertices in the graph.
+ * \param m The number of edges to add in a time step (ie. after
+ *        adding a vertex).
+ * \param directed Boolean, whether to generate a directed graph.
+ * \param citation Boolean, if \c TRUE, the edges always
+ *        originate from the most recently added vertex.
+ * \return Error code:
+ *          \c IGRAPH_EINVAL: invalid
+ *          \p n or \p m
+ *          parameter.
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices plus the number of edges.
+ *
+ * \example examples/simple/igraph_growing_random_game.c
+ */
+int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n,
+                               igraph_integer_t m, igraph_bool_t directed,
+                               igraph_bool_t citation) {
+
+    long int no_of_nodes = n;
+    long int no_of_neighbors = m;
+    long int no_of_edges;
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+
+    long int resp = 0;
+
+    long int i, j;
+
+    if (n < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    }
+    if (m < 0) {
+        IGRAPH_ERROR("Invalid number of edges per step (m)", IGRAPH_EINVAL);
+    }
+
+    no_of_edges = (no_of_nodes - 1) * no_of_neighbors;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+
+    RNG_BEGIN();
+
+    for (i = 1; i < no_of_nodes; i++) {
+        for (j = 0; j < no_of_neighbors; j++) {
+            if (citation) {
+                long int to = RNG_INTEGER(0, i - 1);
+                VECTOR(edges)[resp++] = i;
+                VECTOR(edges)[resp++] = to;
+            } else {
+                long int from = RNG_INTEGER(0, i);
+                long int to = RNG_INTEGER(1, i);
+                VECTOR(edges)[resp++] = from;
+                VECTOR(edges)[resp++] = to;
+            }
+        }
+    }
+
+    RNG_END();
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_callaway_traits_game
+ * \brief Simulate a growing network with vertex types.
+ *
+ * </para><para>
+ * The different types of vertices prefer to connect other types of
+ * vertices with a given probability.</para><para>
+ *
+ * </para><para>
+ * The simulation goes like this: in each discrete time step a new
+ * vertex is added to the graph. The type of this vertex is generated
+ * based on \p type_dist. Then two vertices are selected uniformly
+ * randomly from the graph. The probability that they will be
+ * connected depends on the types of these vertices and is taken from
+ * \p pref_matrix. Then another two vertices are selected and this is
+ * repeated \p edges_per_step times in each time step.
+ * \param graph Pointer to an uninitialized graph.
+ * \param nodes The number of nodes in the graph.
+ * \param types Number of node types.
+ * \param edges_per_step The number of edges to be add per time step.
+ * \param type_dist Vector giving the distribution of the vertex
+ * types.
+ * \param pref_matrix Matrix giving the connection probabilities for
+ * the vertex types.
+ * \param directed Logical, whether to generate a directed graph.
+ * \return Error code.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(|V|e*log(|V|)), |V| is the number of vertices, e
+ * is \p edges_per_step.
+ */
+
+int igraph_callaway_traits_game (igraph_t *graph, igraph_integer_t nodes,
+                                 igraph_integer_t types, igraph_integer_t edges_per_step,
+                                 igraph_vector_t *type_dist,
+                                 igraph_matrix_t *pref_matrix,
+                                 igraph_bool_t directed) {
+    long int i, j;
+    igraph_vector_t edges;
+    igraph_vector_t cumdist;
+    igraph_real_t maxcum;
+    igraph_vector_t nodetypes;
+
+    /* TODO: parameter checks */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&nodetypes, nodes);
+
+    VECTOR(cumdist)[0] = 0;
+    for (i = 0; i < types; i++) {
+        VECTOR(cumdist)[i + 1] = VECTOR(cumdist)[i] + VECTOR(*type_dist)[i];
+    }
+    maxcum = igraph_vector_tail(&cumdist);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < nodes; i++) {
+        igraph_real_t uni = RNG_UNIF(0, maxcum);
+        long int type;
+        igraph_vector_binsearch(&cumdist, uni, &type);
+        VECTOR(nodetypes)[i] = type - 1;
+    }
+
+    for (i = 1; i < nodes; i++) {
+        for (j = 0; j < edges_per_step; j++) {
+            long int node1 = RNG_INTEGER(0, i);
+            long int node2 = RNG_INTEGER(0, i);
+            long int type1 = (long int) VECTOR(nodetypes)[node1];
+            long int type2 = (long int) VECTOR(nodetypes)[node2];
+            /*    printf("unif: %f, %f, types: %li, %li\n", uni1, uni2, type1, type2); */
+            if (RNG_UNIF01() < MATRIX(*pref_matrix, type1, type2)) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, node1));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, node2));
+            }
+        }
+    }
+
+    RNG_END();
+
+    igraph_vector_destroy(&nodetypes);
+    igraph_vector_destroy(&cumdist);
+    IGRAPH_FINALLY_CLEAN(2);
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_establishment_game
+ * \brief Generates a graph with a simple growing model with vertex types.
+ *
+ * </para><para>
+ * The simulation goes like this: a single vertex is added at each
+ * time step. This new vertex tries to connect to \p k vertices in the
+ * graph. The probability that such a connection is realized depends
+ * on the types of the vertices involved.
+ *
+ * \param graph Pointer to an uninitialized graph.
+ * \param nodes The number of vertices in the graph.
+ * \param types The number of vertex types.
+ * \param k The number of connections tried in each time step.
+ * \param type_dist Vector giving the distribution of vertex types.
+ * \param pref_matrix Matrix giving the connection probabilities for
+ * different vertex types.
+ * \param directed Logical, whether to generate a directed graph.
+ * \return Error code.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(|V|*k*log(|V|)), |V| is the number of vertices
+ * and k is the \p k parameter.
+ */
+
+int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes,
+                              igraph_integer_t types, igraph_integer_t k,
+                              igraph_vector_t *type_dist,
+                              igraph_matrix_t *pref_matrix,
+                              igraph_bool_t directed) {
+
+    long int i, j;
+    igraph_vector_t edges;
+    igraph_vector_t cumdist;
+    igraph_vector_t potneis;
+    igraph_real_t maxcum;
+    igraph_vector_t nodetypes;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&potneis, k);
+    IGRAPH_VECTOR_INIT_FINALLY(&nodetypes, nodes);
+
+    VECTOR(cumdist)[0] = 0;
+    for (i = 0; i < types; i++) {
+        VECTOR(cumdist)[i + 1] = VECTOR(cumdist)[i] + VECTOR(*type_dist)[i];
+    }
+    maxcum = igraph_vector_tail(&cumdist);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < nodes; i++) {
+        igraph_real_t uni = RNG_UNIF(0, maxcum);
+        long int type;
+        igraph_vector_binsearch(&cumdist, uni, &type);
+        VECTOR(nodetypes)[i] = type - 1;
+    }
+
+    for (i = k; i < nodes; i++) {
+        long int type1 = (long int) VECTOR(nodetypes)[i];
+        igraph_random_sample(&potneis, 0, i - 1, k);
+        for (j = 0; j < k; j++) {
+            long int type2 = (long int) VECTOR(nodetypes)[(long int)VECTOR(potneis)[j]];
+            if (RNG_UNIF01() < MATRIX(*pref_matrix, type1, type2)) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, VECTOR(potneis)[j]));
+            }
+        }
+    }
+
+    RNG_END();
+
+    igraph_vector_destroy(&nodetypes);
+    igraph_vector_destroy(&potneis);
+    igraph_vector_destroy(&cumdist);
+    IGRAPH_FINALLY_CLEAN(3);
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_recent_degree_game
+ * \brief Stochastic graph generator based on the number of incident edges a node has gained recently
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param n The number of vertices in the graph, this is the same as
+ *        the number of time steps.
+ * \param power The exponent, the probability that a node gains a
+ *        new edge is proportional to the number of edges it has
+ *        gained recently (in the last \p window time steps) to \p
+ *        power.
+ * \param window Integer constant, the size of the time window to use
+ *        to count the number of recent edges.
+ * \param m Integer constant, the number of edges to add per time
+ *        step if the \p outseq parameter is a null pointer or a
+ *        zero-length vector.
+ * \param outseq The number of edges to add in each time step. This
+ *        argument is ignored if it is a null pointer or a zero length
+ *        vector, is this case the constant \p m parameter is used.
+ * \param outpref Logical constant, if true the edges originated by a
+ *        vertex also count as recent incident edges. It is false in
+ *        most cases.
+ * \param zero_appeal Constant giving the attractiveness of the
+ *        vertices which haven't gained any edge recently.
+ * \param directed Logical constant, whether to generate a directed
+ *        graph.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|*log(|V|)+|E|), |V| is the number of
+ * vertices, |E| is the number of edges in the graph.
+ *
+ */
+
+int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t n,
+                              igraph_real_t power,
+                              igraph_integer_t window,
+                              igraph_integer_t m,
+                              const igraph_vector_t *outseq,
+                              igraph_bool_t outpref,
+                              igraph_real_t zero_appeal,
+                              igraph_bool_t directed) {
+
+    long int no_of_nodes = n;
+    long int no_of_neighbors = m;
+    long int no_of_edges;
+    igraph_vector_t edges;
+    long int i, j;
+    igraph_psumtree_t sumtree;
+    long int edgeptr = 0;
+    igraph_vector_t degree;
+    long int time_window = window;
+    igraph_dqueue_t history;
+
+    if (n < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    }
+    if (outseq != 0 && igraph_vector_size(outseq) != 0 && igraph_vector_size(outseq) != n) {
+        IGRAPH_ERROR("Invalid out degree sequence length", IGRAPH_EINVAL);
+    }
+    if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) {
+        IGRAPH_ERROR("Invalid out degree", IGRAPH_EINVAL);
+    }
+
+    if (outseq == 0 || igraph_vector_size(outseq) == 0) {
+        no_of_neighbors = m;
+        no_of_edges = (no_of_nodes - 1) * no_of_neighbors;
+    } else {
+        no_of_edges = 0;
+        for (i = 1; i < igraph_vector_size(outseq); i++) {
+            no_of_edges += VECTOR(*outseq)[i];
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+    IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes));
+    IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+    IGRAPH_CHECK(igraph_dqueue_init(&history,
+                                    time_window * (no_of_neighbors + 1) + 10));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &history);
+
+    RNG_BEGIN();
+
+    /* first node */
+    igraph_psumtree_update(&sumtree, 0, zero_appeal);
+    igraph_dqueue_push(&history, -1);
+
+    /* and the rest */
+    for (i = 1; i < no_of_nodes; i++) {
+        igraph_real_t sum;
+        long int to;
+        if (outseq != 0 && igraph_vector_size(outseq) != 0) {
+            no_of_neighbors = (long int) VECTOR(*outseq)[i];
+        }
+
+        if (i >= time_window) {
+            while ((j = (long int) igraph_dqueue_pop(&history)) != -1) {
+                VECTOR(degree)[j] -= 1;
+                igraph_psumtree_update(&sumtree, j,
+                                       pow(VECTOR(degree)[j], power) + zero_appeal);
+            }
+        }
+
+        sum = igraph_psumtree_sum(&sumtree);
+        for (j = 0; j < no_of_neighbors; j++) {
+            igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum));
+            VECTOR(degree)[to]++;
+            VECTOR(edges)[edgeptr++] = i;
+            VECTOR(edges)[edgeptr++] = to;
+            igraph_dqueue_push(&history, to);
+        }
+        igraph_dqueue_push(&history, -1);
+
+        /* update probabilities */
+        for (j = 0; j < no_of_neighbors; j++) {
+            long int nn = (long int) VECTOR(edges)[edgeptr - 2 * j - 1];
+            igraph_psumtree_update(&sumtree, nn,
+                                   pow(VECTOR(degree)[nn], power) + zero_appeal);
+        }
+        if (outpref) {
+            VECTOR(degree)[i] += no_of_neighbors;
+            igraph_psumtree_update(&sumtree, i,
+                                   pow(VECTOR(degree)[i], power) + zero_appeal);
+        } else {
+            igraph_psumtree_update(&sumtree, i, zero_appeal);
+        }
+    }
+
+    RNG_END();
+
+    igraph_dqueue_destroy(&history);
+    igraph_psumtree_destroy(&sumtree);
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_barabasi_aging_game
+ * \brief Preferential attachment with aging of vertices
+ *
+ * </para><para>
+ * In this game, the probability that a node gains a new edge is
+ * given by its (in-)degree (k) and age (l). This probability has a
+ * degree dependent component multiplied by an age dependent
+ * component. The degree dependent part is: \p deg_coef times k to the
+ * power of \p pa_exp plus \p zero_deg_appeal; and the age dependent
+ * part is \p age_coef times l to the power of \p aging_exp plus \p
+ * zero_age_appeal.
+ *
+ * </para><para>
+ * The age is based on the number of vertices in the
+ * network and the \p aging_bin argument: vertices grew one unit older
+ * after each \p aging_bin vertices added to the network.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param nodes The number of vertices in the graph.
+ * \param m The number of edges to add in each time step. If the \p
+ *        outseq argument is not a null vector and not a zero-length
+ *        vector.
+ * \param outseq The number of edges to add in each time step. If it
+ *        is a null pointer or a zero-length vector then it is ignored
+ *        and the \p m argument is used instead.
+ * \param outpref Logical constant, whether the edges
+ *        initiated by a vertex contribute to the probability to gain
+ *        a new edge.
+ * \param pa_exp The exponent of the preferential attachment, a small
+ *        positive number usually, the value 1 yields the classic
+ *        linear preferential attachment.
+ * \param aging_exp The exponent of the aging, this is a negative
+ *        number usually.
+ * \param aging_bin Integer constant, the number of vertices to add
+ *        before vertices in the network grew one unit older.
+ * \param zero_deg_appeal The degree dependent part of the
+ *        attractiveness of the zero degree vertices.
+ * \param zero_age_appeal The age dependent part of the attractiveness
+ *        of the vertices of age zero. This parameter is usually zero.
+ * \param deg_coef The coefficient for the degree.
+ * \param age_coef The coefficient for the age.
+ * \param directed Logical constant, whether to generate a directed
+ *        graph.
+ * \return Error code.
+ *
+ * Time complexity: O((|V|+|V|/aging_bin)*log(|V|)+|E|). |V| is the number
+ * of vertices, |E| the number of edges.
+ */
+
+int igraph_barabasi_aging_game(igraph_t *graph,
+                               igraph_integer_t nodes,
+                               igraph_integer_t m,
+                               const igraph_vector_t *outseq,
+                               igraph_bool_t outpref,
+                               igraph_real_t pa_exp,
+                               igraph_real_t aging_exp,
+                               igraph_integer_t aging_bin,
+                               igraph_real_t zero_deg_appeal,
+                               igraph_real_t zero_age_appeal,
+                               igraph_real_t deg_coef,
+                               igraph_real_t age_coef,
+                               igraph_bool_t directed) {
+    long int no_of_nodes = nodes;
+    long int no_of_neighbors = m;
+    long int binwidth = nodes / aging_bin + 1;
+    long int no_of_edges;
+    igraph_vector_t edges;
+    long int i, j, k;
+    igraph_psumtree_t sumtree;
+    long int edgeptr = 0;
+    igraph_vector_t degree;
+
+    if (no_of_nodes < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    }
+    if (outseq != 0 && igraph_vector_size(outseq) != 0 && igraph_vector_size(outseq) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid out degree sequence length", IGRAPH_EINVAL);
+    }
+    if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) {
+        IGRAPH_ERROR("Invalid out degree", IGRAPH_EINVAL);
+    }
+    if (aging_bin <= 0) {
+        IGRAPH_ERROR("Invalid aging bin", IGRAPH_EINVAL);
+    }
+
+    if (outseq == 0 || igraph_vector_size(outseq) == 0) {
+        no_of_neighbors = m;
+        no_of_edges = (no_of_nodes - 1) * no_of_neighbors;
+    } else {
+        no_of_edges = 0;
+        for (i = 1; i < igraph_vector_size(outseq); i++) {
+            no_of_edges += VECTOR(*outseq)[i];
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+    IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes));
+    IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    RNG_BEGIN();
+
+    /* first node */
+    igraph_psumtree_update(&sumtree, 0, zero_deg_appeal * (1 + zero_age_appeal));
+
+    /* and the rest */
+    for (i = 1; i < no_of_nodes; i++) {
+        igraph_real_t sum;
+        long int to;
+        if (outseq != 0 && igraph_vector_size(outseq) != 0) {
+            no_of_neighbors = (long int) VECTOR(*outseq)[i];
+        }
+        sum = igraph_psumtree_sum(&sumtree);
+        for (j = 0; j < no_of_neighbors; j++) {
+            igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum));
+            VECTOR(degree)[to]++;
+            VECTOR(edges)[edgeptr++] = i;
+            VECTOR(edges)[edgeptr++] = to;
+        }
+        /* update probabilities */
+        for (j = 0; j < no_of_neighbors; j++) {
+            long int n = (long int) VECTOR(edges)[edgeptr - 2 * j - 1];
+            long int age = (i - n) / binwidth;
+            igraph_psumtree_update(&sumtree, n,
+                                   (deg_coef * pow(VECTOR(degree)[n], pa_exp)
+                                    + zero_deg_appeal)*
+                                   (age_coef * pow(age + 1, aging_exp) + zero_age_appeal));
+        }
+        if (outpref) {
+            VECTOR(degree)[i] += no_of_neighbors;
+            igraph_psumtree_update(&sumtree, i, (zero_age_appeal + 1)*
+                                   (deg_coef * pow(VECTOR(degree)[i], pa_exp)
+                                    + zero_deg_appeal));
+        } else {
+            igraph_psumtree_update(&sumtree, i, (1 + zero_age_appeal)*zero_deg_appeal);
+        }
+
+        /* aging */
+        for (k = 1; i - binwidth * k + 1 >= 1; k++) {
+            long int shnode = i - binwidth * k;
+            long int deg = (long int) VECTOR(degree)[shnode];
+            long int age = (i - shnode) / binwidth;
+            /* igraph_real_t old=igraph_psumtree_get(&sumtree, shnode); */
+            igraph_psumtree_update(&sumtree, shnode,
+                                   (deg_coef * pow(deg, pa_exp) + zero_deg_appeal) *
+                                   (age_coef * pow(age + 2, aging_exp) + zero_age_appeal));
+        }
+    }
+
+    RNG_END();
+
+    igraph_vector_destroy(&degree);
+    igraph_psumtree_destroy(&sumtree);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_recent_degree_aging_game
+ * \brief Preferential attachment based on the number of edges gained recently, with aging of vertices
+ *
+ * </para><para>
+ * This game is very similar to \ref igraph_barabasi_aging_game(),
+ * except that instead of the total number of incident edges the
+ * number of edges gained in the last \p time_window time steps are
+ * counted.
+ *
+ * </para><para>The degree dependent part of the attractiveness is
+ * given by k to the power of \p pa_exp plus \p zero_appeal; the age
+ * dependent part is l to the power to \p aging_exp.
+ * k is the number of edges gained in the last \p time_window time
+ * steps, l is the age of the vertex.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param nodes The number of vertices in the graph.
+ * \param m The number of edges to add in each time step. If the \p
+ *        outseq argument is not a null vector or a zero-length vector
+ *        then it is ignored.
+ * \param outseq Vector giving the number of edges to add in each time
+ *        step. If it is a null pointer or a zero-length vector then
+ *        it is ignored and the \p m argument is used.
+ * \param outpref Logical constant, if true the edges initiated by a
+ *        vertex are also counted. Normally it is false.
+ * \param pa_exp The exponent for the preferential attachment.
+ * \param aging_exp The exponent for the aging, normally it is
+ *        negative: old vertices gain edges with less probability.
+ * \param aging_bin Integer constant, gives the scale of the aging.
+ *        The age of the vertices is incremented by one after every \p
+ *        aging_bin vertex added.
+ * \param time_window The time window to use to count the number of
+ *        incident edges for the vertices.
+ * \param zero_appeal The degree dependent part of the attractiveness
+ *        for zero degree vertices.
+ * \param directed Logical constant, whether to create a directed
+ *        graph.
+ * \return Error code.
+ *
+ * Time complexity: O((|V|+|V|/aging_bin)*log(|V|)+|E|). |V| is the number
+ * of vertices, |E| the number of edges.
+ */
+
+int igraph_recent_degree_aging_game(igraph_t *graph,
+                                    igraph_integer_t nodes,
+                                    igraph_integer_t m,
+                                    const igraph_vector_t *outseq,
+                                    igraph_bool_t outpref,
+                                    igraph_real_t pa_exp,
+                                    igraph_real_t aging_exp,
+                                    igraph_integer_t aging_bin,
+                                    igraph_integer_t time_window,
+                                    igraph_real_t zero_appeal,
+                                    igraph_bool_t directed) {
+
+    long int no_of_nodes = nodes;
+    long int no_of_neighbors = m;
+    long int binwidth = nodes / aging_bin + 1;
+    long int no_of_edges;
+    igraph_vector_t edges;
+    long int i, j, k;
+    igraph_psumtree_t sumtree;
+    long int edgeptr = 0;
+    igraph_vector_t degree;
+    igraph_dqueue_t history;
+
+    if (no_of_nodes < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL);
+    }
+    if (outseq != 0 && igraph_vector_size(outseq) != 0 && igraph_vector_size(outseq) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid out degree sequence length", IGRAPH_EINVAL);
+    }
+    if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) {
+        IGRAPH_ERROR("Invalid out degree", IGRAPH_EINVAL);
+    }
+    if (aging_bin <= 0) {
+        IGRAPH_ERROR("Invalid aging bin", IGRAPH_EINVAL);
+    }
+
+    if (outseq == 0 || igraph_vector_size(outseq) == 0) {
+        no_of_neighbors = m;
+        no_of_edges = (no_of_nodes - 1) * no_of_neighbors;
+    } else {
+        no_of_edges = 0;
+        for (i = 1; i < igraph_vector_size(outseq); i++) {
+            no_of_edges += VECTOR(*outseq)[i];
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+    IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes));
+    IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+    IGRAPH_CHECK(igraph_dqueue_init(&history,
+                                    time_window * (no_of_neighbors + 1) + 10));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &history);
+
+    RNG_BEGIN();
+
+    /* first node */
+    igraph_psumtree_update(&sumtree, 0, zero_appeal);
+    igraph_dqueue_push(&history, -1);
+
+    /* and the rest */
+    for (i = 1; i < no_of_nodes; i++) {
+        igraph_real_t sum;
+        long int to;
+        if (outseq != 0 && igraph_vector_size(outseq) != 0) {
+            no_of_neighbors = (long int) VECTOR(*outseq)[i];
+        }
+
+        if (i >= time_window) {
+            while ((j = (long int) igraph_dqueue_pop(&history)) != -1) {
+                long int age = (i - j) / binwidth;
+                VECTOR(degree)[j] -= 1;
+                igraph_psumtree_update(&sumtree, j,
+                                       (pow(VECTOR(degree)[j], pa_exp) + zero_appeal)*
+                                       pow(age + 1, aging_exp));
+            }
+        }
+
+        sum = igraph_psumtree_sum(&sumtree);
+        for (j = 0; j < no_of_neighbors; j++) {
+            igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum));
+            VECTOR(degree)[to]++;
+            VECTOR(edges)[edgeptr++] = i;
+            VECTOR(edges)[edgeptr++] = to;
+            igraph_dqueue_push(&history, to);
+        }
+        igraph_dqueue_push(&history, -1);
+
+        /* update probabilities */
+        for (j = 0; j < no_of_neighbors; j++) {
+            long int n = (long int) VECTOR(edges)[edgeptr - 2 * j - 1];
+            long int age = (i - n) / binwidth;
+            igraph_psumtree_update(&sumtree, n,
+                                   (pow(VECTOR(degree)[n], pa_exp) + zero_appeal)*
+                                   pow(age + 1, aging_exp));
+        }
+        if (outpref) {
+            VECTOR(degree)[i] += no_of_neighbors;
+            igraph_psumtree_update(&sumtree, i,
+                                   pow(VECTOR(degree)[i], pa_exp) + zero_appeal);
+        } else {
+            igraph_psumtree_update(&sumtree, i, zero_appeal);
+        }
+
+        /* aging */
+        for (k = 1; i - binwidth * k + 1 >= 1; k++) {
+            long int shnode = i - binwidth * k;
+            long int deg = (long int) VECTOR(degree)[shnode];
+            long int age = (i - shnode) / binwidth;
+            igraph_psumtree_update(&sumtree, shnode,
+                                   (pow(deg, pa_exp) + zero_appeal) *
+                                   pow(age + 2, aging_exp));
+        }
+    }
+
+    RNG_END();
+
+    igraph_dqueue_destroy(&history);
+    igraph_vector_destroy(&degree);
+    igraph_psumtree_destroy(&sumtree);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_grg_game
+ * \brief Generating geometric random graphs.
+ *
+ * A geometric random graph is created by dropping points (=vertices)
+ * randomly to the unit square and then connecting all those pairs
+ * which are less than \c radius apart in Euclidean norm.
+ *
+ * </para><para>
+ * Original code contributed by Keith Briggs, thanks Keith.
+ * \param graph Pointer to an uninitialized graph object,
+ * \param nodes The number of vertices in the graph.
+ * \param radius The radius within which the vertices will be connected.
+ * \param torus Logical constant, if true periodic boundary conditions
+ *        will be used, ie. the vertices are assumed to be on a torus
+ *        instead of a square.
+ * \return Error code.
+ *
+ * Time complexity: TODO, less than O(|V|^2+|E|).
+ *
+ * \example examples/simple/igraph_grg_game.c
+ */
+
+int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes,
+                    igraph_real_t radius, igraph_bool_t torus,
+                    igraph_vector_t *x, igraph_vector_t *y) {
+
+    long int i;
+    igraph_vector_t myx, myy, *xx = &myx, *yy = &myy, edges;
+    igraph_real_t r2 = radius * radius;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes));
+
+    if (x) {
+        xx = x;
+        IGRAPH_CHECK(igraph_vector_resize(xx, nodes));
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(xx, nodes);
+    }
+    if (y) {
+        yy = y;
+        IGRAPH_CHECK(igraph_vector_resize(yy, nodes));
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(yy, nodes);
+    }
+
+    RNG_BEGIN();
+
+    for (i = 0; i < nodes; i++) {
+        VECTOR(*xx)[i] = RNG_UNIF01();
+        VECTOR(*yy)[i] = RNG_UNIF01();
+    }
+
+    RNG_END();
+
+    igraph_vector_sort(xx);
+
+    if (!torus) {
+        for (i = 0; i < nodes; i++) {
+            igraph_real_t xx1 = VECTOR(*xx)[i];
+            igraph_real_t yy1 = VECTOR(*yy)[i];
+            long int j = i + 1;
+            igraph_real_t dx, dy;
+            while ( j < nodes && (dx = VECTOR(*xx)[j] - xx1) < radius) {
+                dy = VECTOR(*yy)[j] - yy1;
+                if (dx * dx + dy * dy < r2) {
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, j));
+                }
+                j++;
+            }
+        }
+    } else {
+        for (i = 0; i < nodes; i++) {
+            igraph_real_t xx1 = VECTOR(*xx)[i];
+            igraph_real_t yy1 = VECTOR(*yy)[i];
+            long int j = i + 1;
+            igraph_real_t dx, dy;
+            while ( j < nodes && (dx = VECTOR(*xx)[j] - xx1) < radius) {
+                dy = fabs(VECTOR(*yy)[j] - yy1);
+                if (dx > 0.5) {
+                    dx = 1 - dx;
+                }
+                if (dy > 0.5) {
+                    dy = 1 - dy;
+                }
+                if (dx * dx + dy * dy < r2) {
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, j));
+                }
+                j++;
+            }
+            if (j == nodes) {
+                j = 0;
+                while (j < i && (dx = 1 - xx1 + VECTOR(*xx)[j]) < radius &&
+                       xx1 - VECTOR(*xx)[j] >= radius) {
+                    dy = fabs(VECTOR(*yy)[j] - yy1);
+                    if (dy > 0.5) {
+                        dy = 1 - dy;
+                    }
+                    if (dx * dx + dy * dy < r2) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, j));
+                    }
+                    j++;
+                }
+            }
+        }
+    }
+
+    if (!y) {
+        igraph_vector_destroy(yy);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (!x) {
+        igraph_vector_destroy(xx);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, IGRAPH_UNDIRECTED));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+
+void igraph_i_preference_game_free_vids_by_type(igraph_vector_ptr_t *vecs);
+
+void igraph_i_preference_game_free_vids_by_type(igraph_vector_ptr_t *vecs) {
+    int i = 0, n;
+    igraph_vector_t *v;
+
+    n = (int) igraph_vector_ptr_size(vecs);
+    for (i = 0; i < n; i++) {
+        v = (igraph_vector_t*)VECTOR(*vecs)[i];
+        if (v) {
+            igraph_vector_destroy(v);
+        }
+    }
+    igraph_vector_ptr_destroy_all(vecs);
+}
+
+/**
+ * \function igraph_preference_game
+ * \brief Generates a graph with vertex types and connection preferences
+ *
+ * </para><para>
+ * This is practically the nongrowing variant of \ref
+ * igraph_establishment_game. A given number of vertices are
+ * generated. Every vertex is assigned to a vertex type according to
+ * the given type probabilities. Finally, every
+ * vertex pair is evaluated and an edge is created between them with a
+ * probability depending on the types of the vertices involved.
+ *
+ * </para><para>
+ * In other words, this function generates a graph according to a
+ * block-model. Vertices are divided into groups (or blocks), and
+ * the probability the two vertices are connected depends on their
+ * groups only.
+ *
+ * \param graph Pointer to an uninitialized graph.
+ * \param nodes The number of vertices in the graph.
+ * \param types The number of vertex types.
+ * \param type_dist Vector giving the distribution of vertex types. If
+ *   \c NULL, all vertex types will have equal probability. See also the
+ *   \c fixed_sizes argument.
+ * \param fixed_sizes Boolean. If true, then the number of vertices with a
+ *   given vertex type is fixed and the \c type_dist argument gives these
+ *   numbers for each vertex type. If true, and \c type_dist is \c NULL,
+ *   then the function tries to make vertex groups of the same size. If this
+ *   is not possible, then some groups will have an extra vertex.
+ * \param pref_matrix Matrix giving the connection probabilities for
+ *   different vertex types. This should be symmetric if the requested
+ *   graph is undirected.
+ * \param node_type_vec A vector where the individual generated vertex types
+ *   will be stored. If \c NULL , the vertex types won't be saved.
+ * \param directed Logical, whether to generate a directed graph. If undirected
+ *   graphs are requested, only the lower left triangle of the preference
+ *   matrix is considered.
+ * \param loops Logical, whether loop edges are allowed.
+ * \return Error code.
+ *
+ * Added in version 0.3.</para><para>
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices plus the number of edges in the graph.
+ *
+ * \sa igraph_establishment_game()
+ *
+ * \example examples/simple/igraph_preference_game.c
+ */
+
+int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes,
+                           igraph_integer_t types,
+                           const igraph_vector_t *type_dist,
+                           igraph_bool_t fixed_sizes,
+                           const igraph_matrix_t *pref_matrix,
+                           igraph_vector_t *node_type_vec,
+                           igraph_bool_t directed,
+                           igraph_bool_t loops) {
+
+    long int i, j;
+    igraph_vector_t edges, s;
+    igraph_vector_t* nodetypes;
+    igraph_vector_ptr_t vids_by_type;
+    igraph_real_t maxcum, maxedges;
+
+    if (types < 1) {
+        IGRAPH_ERROR("types must be >= 1", IGRAPH_EINVAL);
+    }
+    if (nodes < 0) {
+        IGRAPH_ERROR("nodes must be >= 0", IGRAPH_EINVAL);
+    }
+    if (type_dist && igraph_vector_size(type_dist) != types) {
+        if (igraph_vector_size(type_dist) > types) {
+            IGRAPH_WARNING("length of type_dist > types, type_dist will be trimmed");
+        } else {
+            IGRAPH_ERROR("type_dist vector too short", IGRAPH_EINVAL);
+        }
+    }
+    if (igraph_matrix_nrow(pref_matrix) < types ||
+        igraph_matrix_ncol(pref_matrix) < types) {
+        IGRAPH_ERROR("pref_matrix too small", IGRAPH_EINVAL);
+    }
+
+    if (fixed_sizes && type_dist) {
+        if (igraph_vector_sum(type_dist) != nodes) {
+            IGRAPH_ERROR("Invalid group sizes, their sum must match the number"
+                         " of vertices", IGRAPH_EINVAL);
+        }
+    }
+
+    if (node_type_vec) {
+        IGRAPH_CHECK(igraph_vector_resize(node_type_vec, nodes));
+        nodetypes = node_type_vec;
+    } else {
+        nodetypes = igraph_Calloc(1, igraph_vector_t);
+        if (nodetypes == 0) {
+            IGRAPH_ERROR("preference_game failed", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, nodetypes);
+        IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes);
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_type, types));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_type);
+    for (i = 0; i < types; i++) {
+        VECTOR(vids_by_type)[i] = igraph_Calloc(1, igraph_vector_t);
+        if (VECTOR(vids_by_type)[i] == 0) {
+            IGRAPH_ERROR("preference_game failed", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_init(VECTOR(vids_by_type)[i], 0));
+    }
+    IGRAPH_FINALLY_CLEAN(1);   /* removing igraph_vector_ptr_destroy_all */
+    IGRAPH_FINALLY(igraph_i_preference_game_free_vids_by_type, &vids_by_type);
+
+    RNG_BEGIN();
+
+    if (!fixed_sizes) {
+
+        igraph_vector_t cumdist;
+        IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1);
+
+        VECTOR(cumdist)[0] = 0;
+        if (type_dist) {
+            for (i = 0; i < types; i++) {
+                VECTOR(cumdist)[i + 1] = VECTOR(cumdist)[i] + VECTOR(*type_dist)[i];
+            }
+        } else {
+            for (i = 0; i < types; i++) {
+                VECTOR(cumdist)[i + 1] = i + 1;
+            }
+        }
+        maxcum = igraph_vector_tail(&cumdist);
+
+        for (i = 0; i < nodes; i++) {
+            long int type1;
+            igraph_real_t uni1 = RNG_UNIF(0, maxcum);
+            igraph_vector_binsearch(&cumdist, uni1, &type1);
+            VECTOR(*nodetypes)[i] = type1 - 1;
+            IGRAPH_CHECK(igraph_vector_push_back(
+                             (igraph_vector_t*)VECTOR(vids_by_type)[type1 - 1], i));
+        }
+
+        igraph_vector_destroy(&cumdist);
+        IGRAPH_FINALLY_CLEAN(1);
+
+    } else {
+
+        int an = 0;
+        if (type_dist) {
+            for (i = 0; i < types; i++) {
+                int no = (int) VECTOR(*type_dist)[i];
+                igraph_vector_t *v = VECTOR(vids_by_type)[i];
+                for (j = 0; j < no && an < nodes; j++) {
+                    VECTOR(*nodetypes)[an] = i;
+                    IGRAPH_CHECK(igraph_vector_push_back(v, an));
+                    an++;
+                }
+            }
+        } else {
+            int fixno = (int) ceil( (double)nodes / types);
+            for (i = 0; i < types; i++) {
+                igraph_vector_t *v = VECTOR(vids_by_type)[i];
+                for (j = 0; j < fixno && an < nodes; j++) {
+                    VECTOR(*nodetypes)[an++] = i;
+                    IGRAPH_CHECK(igraph_vector_push_back(v, an));
+                    an++;
+                }
+            }
+        }
+
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&s, 0);
+
+    for (i = 0; i < types; i++) {
+        for (j = 0; j < types; j++) {
+            /* Generating the random subgraph between vertices of type i and j */
+            long int k, l;
+            igraph_real_t p, last;
+            igraph_vector_t *v1, *v2;
+            long int v1_size, v2_size;
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            v1 = (igraph_vector_t*)VECTOR(vids_by_type)[i];
+            v2 = (igraph_vector_t*)VECTOR(vids_by_type)[j];
+            v1_size = igraph_vector_size(v1);
+            v2_size = igraph_vector_size(v2);
+
+            p = MATRIX(*pref_matrix, i, j);
+            igraph_vector_clear(&s);
+            if (i != j) {
+                /* The two vertex sets are disjoint, this is the easier case */
+                if (i > j && !directed) {
+                    continue;
+                }
+                maxedges = v1_size * v2_size;
+            } else {
+                if (directed && loops) {
+                    maxedges = v1_size * v1_size;
+                } else if (directed && !loops) {
+                    maxedges = v1_size * (v1_size - 1);
+                } else if (!directed && loops) {
+                    maxedges = v1_size * (v1_size + 1) / 2;
+                } else {
+                    maxedges = v1_size * (v1_size - 1) / 2;
+                }
+            }
+
+            IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1)));
+
+            last = RNG_GEOM(p);
+            while (last < maxedges) {
+                IGRAPH_CHECK(igraph_vector_push_back(&s, last));
+                last += RNG_GEOM(p);
+                last += 1;
+            }
+            l = igraph_vector_size(&s);
+
+            IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&edges) + l * 2));
+
+            if (i != j) {
+                /* Generating the subgraph between vertices of type i and j */
+                for (k = 0; k < l; k++) {
+                    long int to = (long int) floor(VECTOR(s)[k] / v1_size);
+                    long int from = (long int) (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size);
+                    igraph_vector_push_back(&edges, VECTOR(*v1)[from]);
+                    igraph_vector_push_back(&edges, VECTOR(*v2)[to]);
+                }
+            } else {
+                /* Generating the subgraph among vertices of type i */
+                if (directed && loops) {
+                    for (k = 0; k < l; k++) {
+                        long int to = (long int) floor(VECTOR(s)[k] / v1_size);
+                        long int from = (long int) (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size);
+                        igraph_vector_push_back(&edges, VECTOR(*v1)[from]);
+                        igraph_vector_push_back(&edges, VECTOR(*v1)[to]);
+                    }
+                } else if (directed && !loops) {
+                    for (k = 0; k < l; k++) {
+                        long int to = (long int) floor(VECTOR(s)[k] / v1_size);
+                        long int from = (long int) (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size);
+                        if (from == to) {
+                            to = v1_size - 1;
+                        }
+                        igraph_vector_push_back(&edges, VECTOR(*v1)[from]);
+                        igraph_vector_push_back(&edges, VECTOR(*v1)[to]);
+                    }
+                } else if (!directed && loops) {
+                    for (k = 0; k < l; k++) {
+                        long int to = (long int) floor((sqrt(8 * VECTOR(s)[k] + 1) - 1) / 2);
+                        long int from = (long int) (VECTOR(s)[k] - (((igraph_real_t)to) * (to + 1)) / 2);
+                        igraph_vector_push_back(&edges, VECTOR(*v1)[from]);
+                        igraph_vector_push_back(&edges, VECTOR(*v1)[to]);
+                    }
+                } else {
+                    for (k = 0; k < l; k++) {
+                        long int to = (long int) floor((sqrt(8 * VECTOR(s)[k] + 1) + 1) / 2);
+                        long int from = (long int) (VECTOR(s)[k] - (((igraph_real_t)to) * (to - 1)) / 2);
+                        igraph_vector_push_back(&edges, VECTOR(*v1)[from]);
+                        igraph_vector_push_back(&edges, VECTOR(*v1)[to]);
+                    }
+                }
+            }
+        }
+    }
+
+    RNG_END();
+
+    igraph_vector_destroy(&s);
+    igraph_i_preference_game_free_vids_by_type(&vids_by_type);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    if (node_type_vec == 0) {
+        igraph_vector_destroy(nodetypes);
+        igraph_Free(nodetypes);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_asymmetric_preference_game
+ * \brief Generates a graph with asymmetric vertex types and connection preferences
+ *
+ * </para><para>
+ * This is the asymmetric variant of \ref igraph_preference_game() .
+ * A given number of vertices are generated. Every vertex is assigned to an
+ * "incoming" and an "outgoing" vertex type according to the given joint
+ * type probabilities. Finally, every vertex pair is evaluated and a
+ * directed edge is created between them with a probability depending on the
+ * "outgoing" type of the source vertex and the "incoming" type of the target
+ * vertex.
+ *
+ * \param graph Pointer to an uninitialized graph.
+ * \param nodes The number of vertices in the graph.
+ * \param types The number of vertex types.
+ * \param type_dist_matrix Matrix giving the joint distribution of vertex types.
+ *   If null, incoming and outgoing vertex types are independent and uniformly
+ *   distributed.
+ * \param pref_matrix Matrix giving the connection probabilities for
+ *   different vertex types.
+ * \param node_type_in_vec A vector where the individual generated "incoming"
+ *   vertex types will be stored. If NULL, the vertex types won't be saved.
+ * \param node_type_out_vec A vector where the individual generated "outgoing"
+ *   vertex types will be stored. If NULL, the vertex types won't be saved.
+ * \param loops Logical, whether loop edges are allowed.
+ * \return Error code.
+ *
+ * Added in version 0.3.</para><para>
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices plus the number of edges in the graph.
+ *
+ * \sa \ref igraph_preference_game()
+ */
+
+int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes,
+                                      igraph_integer_t types,
+                                      igraph_matrix_t *type_dist_matrix,
+                                      igraph_matrix_t *pref_matrix,
+                                      igraph_vector_t *node_type_in_vec,
+                                      igraph_vector_t *node_type_out_vec,
+                                      igraph_bool_t loops) {
+
+    long int i, j, k;
+    igraph_vector_t edges, cumdist, s, intersect;
+    igraph_vector_t *nodetypes_in;
+    igraph_vector_t *nodetypes_out;
+    igraph_vector_ptr_t vids_by_intype, vids_by_outtype;
+    igraph_real_t maxcum, maxedges;
+
+    if (types < 1) {
+        IGRAPH_ERROR("types must be >= 1", IGRAPH_EINVAL);
+    }
+    if (nodes < 0) {
+        IGRAPH_ERROR("nodes must be >= 0", IGRAPH_EINVAL);
+    }
+    if (type_dist_matrix) {
+        if (igraph_matrix_nrow(type_dist_matrix) < types ||
+            igraph_matrix_ncol(type_dist_matrix) < types) {
+            IGRAPH_ERROR("type_dist_matrix too small", IGRAPH_EINVAL);
+        } else if (igraph_matrix_nrow(type_dist_matrix) > types ||
+                   igraph_matrix_ncol(type_dist_matrix) > types) {
+            IGRAPH_WARNING("type_dist_matrix will be trimmed");
+        }
+    }
+    if (igraph_matrix_nrow(pref_matrix) < types ||
+        igraph_matrix_ncol(pref_matrix) < types) {
+        IGRAPH_ERROR("pref_matrix too small", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types * types + 1);
+
+    if (node_type_in_vec) {
+        nodetypes_in = node_type_in_vec;
+        IGRAPH_CHECK(igraph_vector_resize(nodetypes_in, nodes));
+    } else {
+        nodetypes_in = igraph_Calloc(1, igraph_vector_t);
+        if (nodetypes_in == 0) {
+            IGRAPH_ERROR("asymmetric_preference_game failed", IGRAPH_ENOMEM);
+        }
+        IGRAPH_VECTOR_INIT_FINALLY(nodetypes_in, nodes);
+    }
+
+    if (node_type_out_vec) {
+        nodetypes_out = node_type_out_vec;
+        IGRAPH_CHECK(igraph_vector_resize(nodetypes_out, nodes));
+    } else {
+        nodetypes_out = igraph_Calloc(1, igraph_vector_t);
+        if (nodetypes_out == 0) {
+            IGRAPH_ERROR("asymmetric_preference_game failed", IGRAPH_ENOMEM);
+        }
+        IGRAPH_VECTOR_INIT_FINALLY(nodetypes_out, nodes);
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_intype, types));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_intype);
+    IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_outtype, types));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_outtype);
+    for (i = 0; i < types; i++) {
+        VECTOR(vids_by_intype)[i] = igraph_Calloc(1, igraph_vector_t);
+        VECTOR(vids_by_outtype)[i] = igraph_Calloc(1, igraph_vector_t);
+        if (VECTOR(vids_by_intype)[i] == 0 || VECTOR(vids_by_outtype)[i] == 0) {
+            IGRAPH_ERROR("asymmetric_preference_game failed", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_init(VECTOR(vids_by_intype)[i], 0));
+        IGRAPH_CHECK(igraph_vector_init(VECTOR(vids_by_outtype)[i], 0));
+    }
+    IGRAPH_FINALLY_CLEAN(2);   /* removing igraph_vector_ptr_destroy_all */
+    IGRAPH_FINALLY(igraph_i_preference_game_free_vids_by_type, &vids_by_intype);
+    IGRAPH_FINALLY(igraph_i_preference_game_free_vids_by_type, &vids_by_outtype);
+
+    VECTOR(cumdist)[0] = 0;
+    if (type_dist_matrix) {
+        for (i = 0, k = 0; i < types; i++) {
+            for (j = 0; j < types; j++, k++) {
+                VECTOR(cumdist)[k + 1] = VECTOR(cumdist)[k] + MATRIX(*type_dist_matrix, i, j);
+            }
+        }
+    } else {
+        for (i = 0; i < types * types; i++) {
+            VECTOR(cumdist)[i + 1] = i + 1;
+        }
+    }
+    maxcum = igraph_vector_tail(&cumdist);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < nodes; i++) {
+        long int type1, type2;
+        igraph_real_t uni1 = RNG_UNIF(0, maxcum);
+        igraph_vector_binsearch(&cumdist, uni1, &type1);
+        type2 = (type1 - 1) % (int)types;
+        type1 = (type1 - 1) / (int)types;
+        VECTOR(*nodetypes_in)[i] = type1;
+        VECTOR(*nodetypes_out)[i] = type2;
+        IGRAPH_CHECK(igraph_vector_push_back(
+                         (igraph_vector_t*)VECTOR(vids_by_intype)[type1], i));
+        IGRAPH_CHECK(igraph_vector_push_back(
+                         (igraph_vector_t*)VECTOR(vids_by_outtype)[type2], i));
+    }
+
+    igraph_vector_destroy(&cumdist);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&s, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&intersect, 0);
+    for (i = 0; i < types; i++) {
+        for (j = 0; j < types; j++) {
+            long int kk, l, c;
+            igraph_real_t p, last;
+            igraph_vector_t *v1, *v2;
+            long int v1_size, v2_size;
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            v1 = (igraph_vector_t*)VECTOR(vids_by_outtype)[i];
+            v2 = (igraph_vector_t*)VECTOR(vids_by_intype)[j];
+            v1_size = igraph_vector_size(v1);
+            v2_size = igraph_vector_size(v2);
+
+            maxedges = v1_size * v2_size;
+            if (!loops) {
+                IGRAPH_CHECK(igraph_vector_intersect_sorted(v1, v2, &intersect));
+                c = igraph_vector_size(&intersect);
+                maxedges -= c;
+            }
+
+            p = MATRIX(*pref_matrix, i, j);
+            igraph_vector_clear(&s);
+            IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1)));
+
+            last = RNG_GEOM(p);
+            while (last < maxedges) {
+                IGRAPH_CHECK(igraph_vector_push_back(&s, last));
+                last += RNG_GEOM(p);
+                last += 1;
+            }
+            l = igraph_vector_size(&s);
+
+            IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&edges) + l * 2));
+
+            if (!loops && c > 0) {
+                for (kk = 0; kk < l; kk++) {
+                    long int to = (long int) floor(VECTOR(s)[kk] / v1_size);
+                    long int from = (long int) (VECTOR(s)[kk] - ((igraph_real_t)to) * v1_size);
+                    if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) {
+                        /* remap loop edges */
+                        to = v2_size - 1;
+                        igraph_vector_binsearch(&intersect, VECTOR(*v1)[from], &c);
+                        from = v1_size - 1;
+                        if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) {
+                            from--;
+                        }
+                        while (c > 0) {
+                            c--; from--;
+                            if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) {
+                                from--;
+                            }
+                        }
+                    }
+                    igraph_vector_push_back(&edges, VECTOR(*v1)[from]);
+                    igraph_vector_push_back(&edges, VECTOR(*v2)[to]);
+                }
+            } else {
+                for (kk = 0; kk < l; kk++) {
+                    long int to = (long int) floor(VECTOR(s)[kk] / v1_size);
+                    long int from = (long int) (VECTOR(s)[kk] - ((igraph_real_t)to) * v1_size);
+                    igraph_vector_push_back(&edges, VECTOR(*v1)[from]);
+                    igraph_vector_push_back(&edges, VECTOR(*v2)[to]);
+                }
+            }
+        }
+    }
+
+    RNG_END();
+
+    igraph_vector_destroy(&s);
+    igraph_vector_destroy(&intersect);
+    igraph_i_preference_game_free_vids_by_type(&vids_by_intype);
+    igraph_i_preference_game_free_vids_by_type(&vids_by_outtype);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    if (node_type_out_vec == 0) {
+        igraph_vector_destroy(nodetypes_out);
+        igraph_Free(nodetypes_out);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (node_type_in_vec == 0) {
+        igraph_vector_destroy(nodetypes_in);
+        igraph_Free(nodetypes_in);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, 1));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob,
+                                      igraph_bool_t loops,
+                                      igraph_vector_t *edges);
+
+int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob,
+                                      igraph_bool_t loops,
+                                      igraph_vector_t *edges) {
+
+    int no_verts = igraph_vcount(graph);
+    int no_edges = igraph_ecount(graph);
+    igraph_vector_t eorder, tmp;
+    igraph_vector_int_t first, next, prev, marked;
+    int i, to_rewire, last_other = -1;
+
+    /* Create our special graph representation */
+
+# define ADD_STUB(vertex, stub) do {                \
+        if (VECTOR(first)[(vertex)]) {              \
+            VECTOR(prev)[(int) VECTOR(first)[(vertex)]-1]=(stub)+1;   \
+        }                               \
+        VECTOR(next)[(stub)]=VECTOR(first)[(vertex)];       \
+        VECTOR(prev)[(stub)]=0;                 \
+        VECTOR(first)[(vertex)]=(stub)+1;               \
+    } while (0)
+
+# define DEL_STUB(vertex, stub) do {                    \
+        if (VECTOR(next)[(stub)]) {                     \
+            VECTOR(prev)[VECTOR(next)[(stub)]-1]=VECTOR(prev)[(stub)];    \
+        }                                   \
+        if (VECTOR(prev)[(stub)]) {                     \
+            VECTOR(next)[VECTOR(prev)[(stub)]-1]=VECTOR(next)[(stub)];    \
+        } else {                                \
+            VECTOR(first)[(vertex)]=VECTOR(next)[(stub)];         \
+        }                                   \
+    } while (0)
+
+# define MARK_NEIGHBORS(vertex) do {                \
+        int xxx_ =VECTOR(first)[(vertex)];              \
+        while (xxx_) {                      \
+            int o= (int) VECTOR(*edges)[xxx_ % 2 ? xxx_ : xxx_-2];    \
+            VECTOR(marked)[o]=other+1;                \
+            xxx_=VECTOR(next)[xxx_-1];                \
+        }                               \
+    } while (0)
+
+    IGRAPH_CHECK(igraph_vector_int_init(&first, no_verts));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &first);
+    IGRAPH_CHECK(igraph_vector_int_init(&next, no_edges * 2));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &next);
+    IGRAPH_CHECK(igraph_vector_int_init(&prev, no_edges * 2));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &prev);
+    IGRAPH_CHECK(igraph_get_edgelist(graph, edges, /*bycol=*/ 0));
+    IGRAPH_VECTOR_INIT_FINALLY(&eorder, no_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_edges);
+    for (i = 0; i < no_edges; i++) {
+        int idx1 = 2 * i, idx2 = idx1 + 1,
+            from = (int) VECTOR(*edges)[idx1], to = (int) VECTOR(*edges)[idx2];
+        VECTOR(tmp)[i] = from;
+        ADD_STUB(from, idx1);
+        ADD_STUB(to, idx2);
+    }
+    IGRAPH_CHECK(igraph_vector_order1(&tmp, &eorder, no_verts));
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&marked, no_verts));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &marked);
+
+    /* Rewire the stubs, part I */
+
+    to_rewire = (int) RNG_GEOM(prob);
+    while (to_rewire < no_edges) {
+        int stub = (int) (2 * VECTOR(eorder)[to_rewire] + 1);
+        int v = (int) VECTOR(*edges)[stub];
+        int ostub = stub - 1;
+        int other = (int) VECTOR(*edges)[ostub];
+        int pot;
+        if (last_other != other) {
+            MARK_NEIGHBORS(other);
+        }
+        /* Do the rewiring */
+        do {
+            if (loops) {
+                pot = (int) RNG_INTEGER(0, no_verts - 1);
+            } else {
+                pot = (int) RNG_INTEGER(0, no_verts - 2);
+                pot = pot != other ? pot : no_verts - 1;
+            }
+        } while (VECTOR(marked)[pot] == other + 1 && pot != v);
+
+        if (pot != v) {
+            DEL_STUB(v, stub);
+            ADD_STUB(pot, stub);
+            VECTOR(marked)[v] = 0;
+            VECTOR(marked)[pot] = other + 1;
+            VECTOR(*edges)[stub] = pot;
+        }
+
+        to_rewire += RNG_GEOM(prob) + 1;
+        last_other = other;
+    }
+
+    /* Create the new index, from the potentially rewired stubs */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_edges);
+    for (i = 0; i < no_edges; i++) {
+        VECTOR(tmp)[i] = VECTOR(*edges)[2 * i + 1];
+    }
+    IGRAPH_CHECK(igraph_vector_order1(&tmp, &eorder, no_verts));
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Rewire the stubs, part II */
+
+    igraph_vector_int_null(&marked);
+    last_other = -1;
+
+    to_rewire = (int) RNG_GEOM(prob);
+    while (to_rewire < no_edges) {
+        int stub = (int) (2 * VECTOR(eorder)[to_rewire]);
+        int v = (int) VECTOR(*edges)[stub];
+        int ostub = stub + 1;
+        int other = (int) VECTOR(*edges)[ostub];
+        int pot;
+        if (last_other != other) {
+            MARK_NEIGHBORS(other);
+        }
+        /* Do the rewiring */
+        do {
+            if (loops) {
+                pot = (int) RNG_INTEGER(0, no_verts - 1);
+            } else {
+                pot = (int) RNG_INTEGER(0, no_verts - 2);
+                pot = pot != other ? pot : no_verts - 1;
+            }
+        } while (VECTOR(marked)[pot] == other + 1 && pot != v);
+        if (pot != v) {
+            DEL_STUB(v, stub);
+            ADD_STUB(pot, stub);
+            VECTOR(marked)[v] = 0;
+            VECTOR(marked)[pot] = other + 1;
+            VECTOR(*edges)[stub] = pot;
+        }
+
+        to_rewire += RNG_GEOM(prob) + 1;
+        last_other = other;
+    }
+
+    igraph_vector_int_destroy(&marked);
+    igraph_vector_int_destroy(&prev);
+    igraph_vector_int_destroy(&next);
+    igraph_vector_int_destroy(&first);
+    igraph_vector_destroy(&eorder);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+#undef ADD_STUB
+#undef DEL_STUB
+#undef MARK_NEIGHBORS
+
+/**
+ * \function igraph_rewire_edges
+ * \brief Rewire the edges of a graph with constant probability
+ *
+ * This function rewires the edges of a graph with a constant
+ * probability. More precisely each end point of each edge is rewired
+ * to a uniformly randomly chosen vertex with constant probability \p
+ * prob.
+ *
+ * </para><para> Note that this function modifies the input \p graph,
+ * call \ref igraph_copy() if you want to keep it.
+ *
+ * \param graph The input graph, this will be rewired, it can be
+ *    directed or undirected.
+ * \param prob The rewiring probability a constant between zero and
+ *    one (inclusive).
+ * \param loops Boolean, whether loop edges are allowed in the new
+ *    graph, or not.
+ * \param multiple Boolean, whether multiple edges are allowed in the
+ *    new graph.
+ * \return Error code.
+ *
+ * \sa \ref igraph_watts_strogatz_game() uses this function for the
+ * rewiring.
+ *
+ * Time complexity: O(|V|+|E|).
+ */
+
+int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob,
+                        igraph_bool_t loops, igraph_bool_t multiple) {
+
+    igraph_t newgraph;
+    long int no_of_edges = igraph_ecount(graph);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int endpoints = no_of_edges * 2;
+    long int to_rewire;
+    igraph_vector_t edges;
+
+    if (prob < 0 || prob > 1) {
+        IGRAPH_ERROR("Rewiring probability should be between zero and one",
+                     IGRAPH_EINVAL);
+    }
+
+    if (prob == 0) {
+        /* This is easy, just leave things as they are */
+        return IGRAPH_SUCCESS;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, endpoints);
+
+    RNG_BEGIN();
+
+    if (prob != 0 && no_of_edges > 0) {
+        if (multiple) {
+            /* If multiple edges are allowed, then there is an easy and fast
+            method. Each endpoint of an edge is rewired with probability p,
+             so the "skips" between the really rewired endpoints follow a
+             geometric distribution. */
+            IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
+            to_rewire = (long int) RNG_GEOM(prob);
+            while (to_rewire < endpoints) {
+                if (loops) {
+                    VECTOR(edges)[to_rewire] = RNG_INTEGER(0, no_of_nodes - 1);
+                } else {
+                    long int opos = to_rewire % 2 ? to_rewire - 1 : to_rewire + 1;
+                    long int nei = (long int) VECTOR(edges)[opos];
+                    long int r = RNG_INTEGER(0, no_of_nodes - 2);
+                    VECTOR(edges)[ to_rewire ] = (r != nei ? r : no_of_nodes - 1);
+                }
+                to_rewire += RNG_GEOM(prob) + 1;
+            }
+
+        } else {
+            IGRAPH_CHECK(igraph_i_rewire_edges_no_multiple(graph, prob, loops,
+                         &edges));
+        }
+    }
+
+    RNG_END();
+
+    IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes,
+                               igraph_is_directed(graph)));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_FINALLY(igraph_destroy, &newgraph);
+    IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
+    IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1);
+    IGRAPH_FINALLY_CLEAN(1);
+    igraph_destroy(graph);
+    *graph = newgraph;
+
+    return 0;
+}
+
+/**
+ * \function igraph_rewire_directed_edges
+ * \brief Rewire the chosen endpoint of directed edges
+ *
+ * This function rewires either the start or end of directed edges in a graph
+ * with a constant probability. Correspondingly, either the in-degree sequence
+ * or the out-degree sequence of the graph will be preserved.
+ *
+ * </para><para> Note that this function modifies the input \p graph,
+ * call \ref igraph_copy() if you want to keep it.
+ *
+ * \param graph The input graph, this will be rewired, it can be
+ *    directed or undirected. If it is directed, \ref igraph_rewire_edges()
+ *    will be called.
+ * \param prob The rewiring probability, a constant between zero and
+ *    one (inclusive).
+ * \param loops Boolean, whether loop edges are allowed in the new
+ *    graph, or not.
+ * \param mode The endpoints of directed edges to rewire. It is ignored for
+ *    undirected graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          rewire the end of each directed edge
+ *        \cli IGRAPH_IN
+ *          rewire the start of each directed edge
+ *        \cli IGRAPH_ALL
+ *          rewire both endpoints of each edge
+ *        \endclist
+ * \return Error code.
+ *
+ * \sa \ref igraph_rewire_edges(), \ref igraph_rewire()
+ *
+ * Time complexity: O(|E|).
+ */
+
+int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob,
+                                 igraph_bool_t loops, igraph_neimode_t mode) {
+
+    if (prob < 0 || prob > 1) {
+        IGRAPH_ERROR("Rewiring probability should be between zero and one",
+                     IGRAPH_EINVAL);
+    }
+
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    if (prob == 0) {
+        return IGRAPH_SUCCESS;
+    }
+
+    if (igraph_is_directed(graph) && mode != IGRAPH_ALL) {
+        igraph_t newgraph;
+        long int no_of_edges = igraph_ecount(graph);
+        long int no_of_nodes = igraph_vcount(graph);
+        long int to_rewire;
+        long int offset;
+        igraph_vector_t edges;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges);
+
+        switch (mode) {
+        case IGRAPH_IN:
+            offset = 0;
+            break;
+        case IGRAPH_OUT:
+            offset = 1;
+            break;
+        case IGRAPH_ALL:
+            break; /* suppress compiler warning */
+        }
+
+        IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
+
+        RNG_BEGIN();
+
+        to_rewire = RNG_GEOM(prob);
+        while (to_rewire < no_of_edges) {
+            if (loops) {
+                VECTOR(edges)[2 * to_rewire + offset] = RNG_INTEGER(0, no_of_nodes - 1);
+            } else {
+                long int nei = (long int) VECTOR(edges)[2 * to_rewire + (1 - offset)];
+                long int r = RNG_INTEGER(0, no_of_nodes - 2);
+                VECTOR(edges)[2 * to_rewire + offset] = (r != nei ? r : no_of_nodes - 1);
+            }
+            to_rewire += RNG_GEOM(prob) + 1;
+        }
+
+        RNG_END();
+
+        IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes,
+                                   igraph_is_directed(graph)));
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        IGRAPH_FINALLY(igraph_destroy, &newgraph);
+        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
+        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1);
+        IGRAPH_FINALLY_CLEAN(1);
+        igraph_destroy(graph);
+        *graph = newgraph;
+
+    } else {
+        IGRAPH_CHECK(igraph_rewire_edges(graph, prob, loops, /* multiple = */ 0));
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_watts_strogatz_game
+ * \brief The Watts-Strogatz small-world model
+ *
+ * This function generates a graph according to the Watts-Strogatz
+ * model of small-world networks. The graph is obtained by creating a
+ * circular undirected lattice and then rewire the edges randomly with
+ * a constant probability.
+ *
+ * </para><para>See also: Duncan J Watts and Steven H Strogatz:
+ * Collective dynamics of <quote>small world</quote> networks, Nature
+ * 393, 440-442, 1998.
+ * \param graph The graph to initialize.
+ * \param dim The dimension of the lattice.
+ * \param size The size of the lattice along each dimension.
+ * \param nei The size of the neighborhood for each vertex. This is
+ *    the same as the \p nei argument of \ref
+ *    igraph_connect_neighborhood().
+ * \param p The rewiring probability. A real number between zero and
+ *   one (inclusive).
+ * \param loops Logical, whether to generate loop edges.
+ * \param multiple Logical, whether to allow multiple edges in the
+ *   generated graph.
+ * \return Error code.
+ *
+ * \sa \ref igraph_lattice(), \ref igraph_connect_neighborhood() and
+ * \ref igraph_rewire_edges() can be used if more flexibility is
+ * needed, eg. a different type of lattice.
+ *
+ * Time complexity: O(|V|*d^o+|E|), |V| and |E| are the number of
+ * vertices and edges, d is the average degree, o is the \p nei
+ * argument.
+ */
+
+int igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim,
+                               igraph_integer_t size, igraph_integer_t nei,
+                               igraph_real_t p, igraph_bool_t loops,
+                               igraph_bool_t multiple) {
+
+    igraph_vector_t dimvector;
+    long int i;
+
+    if (dim < 1) {
+        IGRAPH_ERROR("WS game: dimension should be at least one", IGRAPH_EINVAL);
+    }
+    if (size < 1) {
+        IGRAPH_ERROR("WS game: lattice size should be at least one",
+                     IGRAPH_EINVAL);
+    }
+    if (p < 0 || p > 1) {
+        IGRAPH_ERROR("WS game: rewiring probability should be between 0 and 1",
+                     IGRAPH_EINVAL);
+    }
+
+    /* Create the lattice first */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&dimvector, dim);
+    for (i = 0; i < dim; i++) {
+        VECTOR(dimvector)[i] = size;
+    }
+
+    IGRAPH_CHECK(igraph_lattice(graph, &dimvector, nei, IGRAPH_UNDIRECTED,
+                                0 /* mutual */, 1 /* circular */));
+    igraph_vector_destroy(&dimvector);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_destroy, graph);
+
+    /* Rewire the edges then */
+
+    IGRAPH_CHECK(igraph_rewire_edges(graph, p, loops, multiple));
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_lastcit_game
+ * \brief Simulate citation network, based on time passed since the last citation.
+ *
+ * This is a quite special stochastic graph generator, it models an
+ * evolving graph. In each time step a single vertex is added to the
+ * network and it cites a number of other vertices (as specified by
+ * the \p edges_per_step argument). The cited vertices are selected
+ * based on the last time they were cited. Time is measured by the
+ * addition of vertices and it is binned into \p pagebins bins.
+ * So if the current time step is \c t and the last citation to a
+ * given \c i vertex was made in time step \c t0, then \c
+ * (t-t0)/binwidth is calculated where binwidth is \c nodes/pagebins+1,
+ * in the last expression '/' denotes integer division, so the
+ * fraction part is omitted.
+ *
+ * </para><para>
+ * The \p preference argument specifies the preferences for the
+ * citation lags, ie. its first elements contains the attractivity
+ * of the very recently cited vertices, etc. The last element is
+ * special, it contains the attractivity of the vertices which were
+ * never cited. This element should be bigger than zero.
+ *
+ * </para><para>
+ * Note that this function generates networks with multiple edges if
+ * \p edges_per_step is bigger than one, call \ref igraph_simplify()
+ * on the result to get rid of these edges.
+ * \param graph Pointer to an uninitialized graph object, the result
+ *     will be stored here.
+ * \param node The number of vertices in the network.
+ * \param edges_per_node The number of edges to add in each time
+ *     step.
+ * \param pagebins The number of age bins to use.
+ * \param preference Pointer to an initialized vector of length
+ *     \c pagebins+1. This contains the `attractivity' of the various
+ *     age bins, the last element is the attractivity of the vertices
+ *     which were never cited, and it should be greater than zero.
+ *     It is a good idea to have all positive values in this vector.
+ * \param directed Logical constant, whether to create directed
+ *      networks.
+ * \return Error code.
+ *
+ * \sa \ref igraph_barabasi_aging_game().
+ *
+ * Time complexity: O(|V|*a+|E|*log|V|), |V| is the number of vertices,
+ * |E| is the total number of edges, a is the \p pagebins parameter.
+ */
+
+int igraph_lastcit_game(igraph_t *graph,
+                        igraph_integer_t nodes, igraph_integer_t edges_per_node,
+                        igraph_integer_t pagebins,
+                        const igraph_vector_t *preference,
+                        igraph_bool_t directed) {
+
+    long int no_of_nodes = nodes;
+    igraph_psumtree_t sumtree;
+    igraph_vector_t edges;
+    long int i, j, k;
+    long int *lastcit;
+    long int *index;
+    long int agebins = pagebins;
+    long int binwidth = no_of_nodes / agebins + 1;
+
+    if (agebins != igraph_vector_size(preference) - 1) {
+        IGRAPH_ERROR("`preference' vector should be of length `agebins' plus one",
+                     IGRAPH_EINVAL);
+    }
+    if (agebins <= 1 ) {
+        IGRAPH_ERROR("at least two age bins are need for lastcit game",
+                     IGRAPH_EINVAL);
+    }
+    if (VECTOR(*preference)[agebins] <= 0) {
+        IGRAPH_ERROR("the last element of the `preference' vector needs to be positive",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    lastcit = igraph_Calloc(no_of_nodes, long int);
+    if (!lastcit) {
+        IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, lastcit);
+
+    index = igraph_Calloc(no_of_nodes + 1, long int);
+    if (!index) {
+        IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, index);
+
+    IGRAPH_CHECK(igraph_psumtree_init(&sumtree, nodes));
+    IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_node));
+
+    /* The first node */
+    igraph_psumtree_update(&sumtree, 0, VECTOR(*preference)[agebins]);
+    index[0] = 0;
+    index[1] = 0;
+
+    RNG_BEGIN();
+
+    for (i = 1; i < no_of_nodes; i++) {
+
+        /* Add new edges */
+        for (j = 0; j < edges_per_node; j++) {
+            long int to;
+            igraph_real_t sum = igraph_psumtree_sum(&sumtree);
+            igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum));
+            igraph_vector_push_back(&edges, i);
+            igraph_vector_push_back(&edges, to);
+            lastcit[to] = i + 1;
+            igraph_psumtree_update(&sumtree, to, VECTOR(*preference)[0]);
+        }
+
+        /* Add the node itself */
+        igraph_psumtree_update(&sumtree, i, VECTOR(*preference)[agebins]);
+        index[i + 1] = index[i] + edges_per_node;
+
+        /* Update the preference of some vertices if they got to another bin.
+           We need to know the citations of some older vertices, this is in the index. */
+        for (k = 1; i - binwidth * k >= 1; k++) {
+            long int shnode = i - binwidth * k;
+            long int m = index[shnode], n = index[shnode + 1];
+            for (j = 2 * m; j < 2 * n; j += 2) {
+                long int cnode = (long int) VECTOR(edges)[j + 1];
+                if (lastcit[cnode] == shnode + 1) {
+                    igraph_psumtree_update(&sumtree, cnode, VECTOR(*preference)[k]);
+                }
+            }
+        }
+
+    }
+
+    RNG_END();
+
+    igraph_psumtree_destroy(&sumtree);
+    igraph_free(index);
+    igraph_free(lastcit);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_cited_type_game
+ * \brief Simulate a citation based on vertex types.
+ *
+ * Function to create a network based on some vertex categories. This
+ * function creates a citation network, in each step a single vertex
+ * and \p edges_per_step citating edges are added, nodes with
+ * different categories (may) have different probabilities to get
+ * cited, as given by the \p pref vector.
+ *
+ * </para><para>
+ * Note that this function might generate networks with multiple edges
+ * if \p edges_per_step is greater than one. You might want to call
+ * \ref igraph_simplify() on the result to remove multiple edges.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param nodes The number of vertices in the network.
+ * \param types Numeric vector giving the categories of the vertices,
+ *     so it should contain \p nodes non-negative integer
+ *     numbers. Types are numbered from zero.
+ * \param pref The attractivity of the different vertex categories in
+ *     a vector. Its length should be the maximum element in \p types
+ *     plus one (types are numbered from zero).
+ * \param edges_per_step Integer constant, the number of edges to add
+ *     in each time step.
+ * \param directed Logical constant, whether to create a directed
+ *     network.
+ * \return Error code.
+ *
+ * \sa \ref igraph_citing_cited_type_game() for a bit more general
+ * game.
+ *
+ * Time complexity: O((|V|+|E|)log|V|), |V| and |E| are number of
+ * vertices and edges, respectively.
+ */
+
+int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes,
+                           const igraph_vector_t *types,
+                           const igraph_vector_t *pref,
+                           igraph_integer_t edges_per_step,
+                           igraph_bool_t directed) {
+
+    igraph_vector_t edges;
+    igraph_vector_t cumsum;
+    igraph_real_t sum;
+    long int i, j, nnval, type;
+
+    if (igraph_vector_size(types) != nodes) {
+        IGRAPH_ERROR("Invalid size of types", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    /* return an empty graph is nodes is zero */
+    if (nodes == 0) {
+        igraph_create(graph, &edges, nodes, directed);
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(1);
+        return 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&cumsum, 2);
+    IGRAPH_CHECK(igraph_vector_reserve(&cumsum, nodes + 1));
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_step));
+
+    /* first node */
+    VECTOR(cumsum)[0] = 0;
+    type = (long int) VECTOR(*types)[0];
+    if (type >= igraph_vector_size(pref)) {
+        IGRAPH_ERROR("pref is too short for the given types", IGRAPH_EINVAL);
+    }
+    nnval = VECTOR(*pref)[type];
+    if (nnval < 0) {
+        IGRAPH_ERROR("pref contains negative entries", IGRAPH_EINVAL);
+    }
+    sum = VECTOR(cumsum)[1] = nnval;
+
+    RNG_BEGIN();
+
+    for (i = 1; i < nodes; i++) {
+        for (j = 0; j < edges_per_step; j++) {
+            long int to;
+            if (sum > 0) {
+                igraph_vector_binsearch(&cumsum, RNG_UNIF(0, sum), &to);
+            } else {
+                to = i + 1;
+            }
+            igraph_vector_push_back(&edges, i);
+            igraph_vector_push_back(&edges, to - 1);
+        }
+        type = (long int) VECTOR(*types)[i];
+        if (type >= igraph_vector_size(pref)) {
+            IGRAPH_ERROR("pref is too short for the given types", IGRAPH_EINVAL);
+        }
+        nnval = VECTOR(*pref)[type];
+        if (nnval < 0) {
+            IGRAPH_ERROR("pref contains negative entries", IGRAPH_EINVAL);
+        }
+        sum += nnval;
+        igraph_vector_push_back(&cumsum, sum);
+    }
+
+    RNG_END();
+
+    igraph_vector_destroy(&cumsum);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+void igraph_i_citing_cited_type_game_free(igraph_i_citing_cited_type_game_struct_t *s) {
+    long int i;
+    if (!s->sumtrees) {
+        return;
+    }
+    for (i = 0; i < s->no; i++) {
+        igraph_psumtree_destroy(&s->sumtrees[i]);
+    }
+}
+
+/**
+ * \function igraph_citing_cited_type_game
+ * \brief Simulate a citation network based on vertex types.
+ *
+ * This game is similar to \ref igraph_cited_type_game() but here the
+ * category of the citing vertex is also considered.
+ *
+ * </para><para>
+ * An evolving citation network is modeled here, a single vertex and
+ * its \p edges_per_step citation are added in each time step. The
+ * odds the a given vertex is cited by the new vertex depends on the
+ * category of both the citing and the cited vertex and is given in
+ * the \p pref matrix. The categories of the citing vertex correspond
+ * to the rows, the categories of the cited vertex to the columns of
+ * this matrix. Ie. the element in row \c i and column \c j gives the
+ * probability that a \c j vertex is cited, if the category of the
+ * citing vertex is \c i.
+ *
+ * </para><para>
+ * Note that this function might generate networks with multiple edges
+ * if \p edges_per_step is greater than one. You might want to call
+ * \ref igraph_simplify() on the result to remove multiple edges.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param nodes The number of vertices in the network.
+ * \param types A numeric matrix of length \p nodes, containing the
+ *    categories of the vertices. The categories are numbered from
+ *    zero.
+ * \param pref The preference matrix, a square matrix is required,
+ *     both the number of rows and columns should be the maximum
+ *     element in \p types plus one (types are numbered from zero).
+ * \param directed Logical constant, whether to create a directed
+ *     network.
+ * \return Error code.
+ *
+ * Time complexity: O((|V|+|E|)log|V|), |V| and |E| are number of
+ * vertices and edges, respectively.
+ */
+
+int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes,
+                                  const igraph_vector_t *types,
+                                  const igraph_matrix_t *pref,
+                                  igraph_integer_t edges_per_step,
+                                  igraph_bool_t directed) {
+
+    igraph_vector_t edges;
+    igraph_i_citing_cited_type_game_struct_t str = { 0, 0 };
+    igraph_psumtree_t *sumtrees;
+    igraph_vector_t sums;
+    long int nocats;
+    long int i, j;
+
+    if (igraph_vector_size(types) != nodes) {
+        IGRAPH_ERROR("Invalid size of types", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    /* return an empty graph is nodes is zero */
+    if (nodes == 0) {
+        igraph_create(graph, &edges, nodes, directed);
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(2); /* str and edges */
+        return 0;
+    }
+
+    nocats = igraph_matrix_ncol(pref);
+    str.sumtrees = sumtrees = igraph_Calloc(nocats, igraph_psumtree_t);
+    if (!sumtrees) {
+        IGRAPH_ERROR("Citing-cited type game failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_i_citing_cited_type_game_free, &str);
+
+    for (i = 0; i < nocats; i++) {
+        IGRAPH_CHECK(igraph_psumtree_init(&sumtrees[i], nodes));
+        str.no++;
+    }
+    IGRAPH_VECTOR_INIT_FINALLY(&sums, nocats);
+
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_step));
+
+    /* First node */
+    for (i = 0; i < nocats; i++) {
+        long int type = (long int) VECTOR(*types)[0];
+        if ( MATRIX(*pref, i, type) < 0) {
+            IGRAPH_ERROR("pref contains negative entries", IGRAPH_EINVAL);
+        }
+        igraph_psumtree_update(&sumtrees[i], 0, MATRIX(*pref, i, type));
+        VECTOR(sums)[i] = MATRIX(*pref, i, type);
+    }
+
+    RNG_BEGIN();
+
+    for (i = 1; i < nodes; i++) {
+        long int type = (long int) VECTOR(*types)[i];
+        igraph_real_t sum = VECTOR(sums)[type];
+        for (j = 0; j < edges_per_step; j++) {
+            long int to;
+            igraph_psumtree_search(&sumtrees[type], &to, RNG_UNIF(0, sum));
+            igraph_vector_push_back(&edges, i);
+            igraph_vector_push_back(&edges, to);
+        }
+
+        /* add i */
+        for (j = 0; j < nocats; j++) {
+            if ( MATRIX(*pref, j, type) < 0) {
+                IGRAPH_ERROR("pref contains negative entries", IGRAPH_EINVAL);
+            }
+            igraph_psumtree_update(&sumtrees[j], i, MATRIX(*pref, j,  type));
+            VECTOR(sums)[j] += MATRIX(*pref, j, type);
+        }
+    }
+
+    RNG_END();
+
+    igraph_i_citing_cited_type_game_free(&str);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_create(graph, &edges, nodes, directed);
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+
+
+/**
+ * \ingroup generators
+ * \function igraph_simple_interconnected_islands_game
+ * \brief Generates a random graph made of several interconnected islands, each island being a random graph.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param islands_n The number of islands in the graph.
+ * \param islands_size The size of islands in the graph.
+ * \param islands_pin The probability to create each possible edge into each island .
+ * \param n_inter The number of edges to create between two islands .
+
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid parameter
+ *         \c IGRAPH_ENOMEM: there is not enough
+ *         memory for the operation.
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices plus the number of edges in the graph.
+ *
+ */
+int igraph_simple_interconnected_islands_game(
+    igraph_t        *graph,
+    igraph_integer_t    islands_n,
+    igraph_integer_t    islands_size,
+    igraph_real_t       islands_pin,
+    igraph_integer_t    n_inter) {
+
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    igraph_vector_t s = IGRAPH_VECTOR_NULL;
+    int retval = 0;
+    int nbNodes;
+    double maxpossibleedgesPerIsland;
+    double maxedgesPerIsland;
+    int nbEdgesInterIslands;
+    double maxedges;
+    int startIsland = 0;
+    int endIsland = 0;
+    int i, j, is;
+    double myrand, last;
+
+    if (islands_n < 0) {
+        IGRAPH_ERROR("Invalid number of islands", IGRAPH_EINVAL);
+    }
+    if (islands_size < 0) {
+        IGRAPH_ERROR("Invalid size for islands", IGRAPH_EINVAL);
+    }
+    if (islands_pin < 0 || islands_pin > 1) {
+        IGRAPH_ERROR("Invalid probability for islands", IGRAPH_EINVAL);
+    }
+    if ( (n_inter < 0) || (n_inter > islands_size) ) {
+        IGRAPH_ERROR("Invalid number of inter-islands links", IGRAPH_EINVAL);
+    }
+
+    // how much memory ?
+    nbNodes = islands_n * islands_size;
+    maxpossibleedgesPerIsland = ((double)islands_size * ((double)islands_size - (double)1)) / (double)2;
+    maxedgesPerIsland = islands_pin * maxpossibleedgesPerIsland;
+    nbEdgesInterIslands = n_inter * (islands_n * (islands_n - 1)) / 2;
+    maxedges = maxedgesPerIsland * islands_n + nbEdgesInterIslands;
+
+    // debug&tests : printf("total nodes %d, maxedgesperisland %f, maxedgesinterislands %d, maxedges %f\n", nbNodes, maxedgesPerIsland, nbEdgesInterIslands, maxedges);
+
+    // reserve enough place for all the edges, thanks !
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, (long int) maxedges));
+
+    RNG_BEGIN();
+
+    // first create all the islands
+    for (is = 1; is <= islands_n; is++) { // for each island
+
+        // index for start and end of nodes in this island
+        startIsland = islands_size * (is - 1);
+        endIsland = startIsland + islands_size - 1;
+
+
+        // debug&tests : printf("start %d,end %d\n", startIsland, endIsland);
+
+        // create the random numbers to be used (into s)
+        IGRAPH_VECTOR_INIT_FINALLY(&s, 0);
+        IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) maxedgesPerIsland));
+
+        last = RNG_GEOM(islands_pin);
+        // debug&tests : printf("last=%f \n", last);
+        while (last < maxpossibleedgesPerIsland) { // maxedgesPerIsland
+            IGRAPH_CHECK(igraph_vector_push_back(&s, last));
+            myrand = RNG_GEOM(islands_pin);
+            last += myrand; //RNG_GEOM(islands_pin);
+            //printf("myrand=%f , last=%f \n", myrand, last);
+            last += 1;
+        }
+
+
+
+        // change this to edges !
+        for (i = 0; i < igraph_vector_size(&s); i++) {
+
+            long int to = (long int) floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2);
+            long int from = (long int) (VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2);
+            to += startIsland;
+            from += startIsland;
+            // debug&tests : printf("from %d to %d\n", from, to);
+            igraph_vector_push_back(&edges, from);
+            igraph_vector_push_back(&edges, to);
+        }
+
+        // clear the memory used for random number for this island
+        igraph_vector_destroy(&s);
+        IGRAPH_FINALLY_CLEAN(1);
+
+
+        // create the links with other islands
+        for (i = is + 1; i <= islands_n; i++) { // for each other island (not the previous ones)
+
+            // debug&tests : printf("link islands %d and %d\n", is, i);
+            for (j = 0; j < n_inter; j++) { // for each link between islands
+
+                long int from = (long int) RNG_UNIF(startIsland, endIsland);
+                long int to = (long int) RNG_UNIF((i - 1) * islands_size, i * islands_size);
+                //printf("from %d to %d\n", from, to);
+                igraph_vector_push_back(&edges, from);
+                igraph_vector_push_back(&edges, to);
+            }
+
+        }
+    }
+
+    RNG_END();
+
+    // actually fill the graph object
+    IGRAPH_CHECK(retval = igraph_create(graph, &edges, nbNodes, 0));
+
+    // an clear remaining things
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return retval;
+}
+
+
+/**
+ * \ingroup generators
+ * \function igraph_static_fitness_game
+ * \brief Generates a non-growing random graph with edge probabilities
+ *        proportional to node fitness scores.
+ *
+ * This game generates a directed or undirected random graph where the
+ * probability of an edge between vertices i and j depends on the fitness
+ * scores of the two vertices involved. For undirected graphs, each vertex
+ * has a single fitness score. For directed graphs, each vertex has an out-
+ * and an in-fitness, and the probability of an edge from i to j depends on
+ * the out-fitness of vertex i and the in-fitness of vertex j.
+ *
+ * </para><para>
+ * The generation process goes as follows. We start from N disconnected nodes
+ * (where N is given by the length of the fitness vector). Then we randomly
+ * select two vertices i and j, with probabilities proportional to their
+ * fitnesses. (When the generated graph is directed, i is selected according to
+ * the out-fitnesses and j is selected according to the in-fitnesses). If the
+ * vertices are not connected yet (or if multiple edges are allowed), we
+ * connect them; otherwise we select a new pair. This is repeated until the
+ * desired number of links are created.
+ *
+ * </para><para>
+ * It can be shown that the \em expected degree of each vertex will be
+ * proportional to its fitness, although the actual, observed degree will not
+ * be. If you need to generate a graph with an exact degree sequence, consider
+ * \ref igraph_degree_sequence_game instead.
+ *
+ * </para><para>
+ * This model is commonly used to generate static scale-free networks. To
+ * achieve this, you have to draw the fitness scores from the desired power-law
+ * distribution. Alternatively, you may use \ref igraph_static_power_law_game
+ * which generates the fitnesses for you with a given exponent.
+ *
+ * </para><para>
+ * Reference: Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution
+ * in scale-free networks. Phys Rev Lett 87(27):278701, 2001.
+ *
+ * \param graph        Pointer to an uninitialized graph object.
+ * \param fitness_out  A numeric vector containing the fitness of each vertex.
+ *                     For directed graphs, this specifies the out-fitness
+ *                     of each vertex.
+ * \param fitness_in   If \c NULL, the generated graph will be undirected.
+ *                     If not \c NULL, this argument specifies the in-fitness
+ *                     of each vertex.
+ * \param no_of_edges  The number of edges in the generated graph.
+ * \param loops        Whether to allow loop edges in the generated graph.
+ * \param multiple     Whether to allow multiple edges in the generated graph.
+ *
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid parameter
+ *         \c IGRAPH_ENOMEM: there is not enough
+ *         memory for the operation.
+ *
+ * Time complexity: O(|V| + |E| log |E|).
+ */
+int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges,
+                               igraph_vector_t* fitness_out, igraph_vector_t* fitness_in,
+                               igraph_bool_t loops, igraph_bool_t multiple) {
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    igraph_integer_t no_of_nodes;
+    igraph_integer_t outnodes, innodes, nodes;
+    igraph_vector_t cum_fitness_in, cum_fitness_out;
+    igraph_vector_t *p_cum_fitness_in, *p_cum_fitness_out;
+    igraph_real_t x, max_in, max_out;
+    igraph_real_t max_no_of_edges;
+    igraph_bool_t is_directed = (fitness_in != 0);
+    float num_steps;
+    igraph_integer_t step_counter = 0;
+    long int i, from, to, pos;
+
+    if (fitness_out == 0) {
+        IGRAPH_ERROR("fitness_out must not be null", IGRAPH_EINVAL);
+    }
+
+    if (no_of_edges < 0) {
+        IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL);
+    }
+
+    no_of_nodes = (int) igraph_vector_size(fitness_out);
+    if (no_of_nodes == 0) {
+        IGRAPH_CHECK(igraph_empty(graph, 0, is_directed));
+        return IGRAPH_SUCCESS;
+    }
+
+    if (is_directed && igraph_vector_size(fitness_in) != no_of_nodes) {
+        IGRAPH_ERROR("fitness_in must have the same size as fitness_out", IGRAPH_EINVAL);
+    }
+
+    /* Sanity checks for the fitnesses */
+    if (igraph_vector_min(fitness_out) < 0) {
+        IGRAPH_ERROR("Fitness scores must be non-negative", IGRAPH_EINVAL);
+    }
+    if (fitness_in != 0 && igraph_vector_min(fitness_in) < 0) {
+        IGRAPH_ERROR("Fitness scores must be non-negative", IGRAPH_EINVAL);
+    }
+
+    /* Avoid getting into an infinite loop when too many edges are requested */
+    if (!multiple) {
+        if (is_directed) {
+            outnodes = innodes = nodes = 0;
+            for (i = 0; i < no_of_nodes; i++) {
+                if (VECTOR(*fitness_out)[i] != 0) {
+                    outnodes++;
+                }
+                if (VECTOR(*fitness_in)[i] != 0) {
+                    innodes++;
+                }
+                if (VECTOR(*fitness_out)[i] != 0 && VECTOR(*fitness_in)[i] != 0) {
+                    nodes++;
+                }
+            }
+            max_no_of_edges = ((igraph_real_t) outnodes) * innodes - (loops ? 0 : nodes);
+        } else {
+            nodes = 0;
+            for (i = 0; i < no_of_nodes; i++) {
+                if (VECTOR(*fitness_out)[i] != 0) {
+                    nodes++;
+                }
+            }
+            max_no_of_edges = loops
+                              ? nodes * ((igraph_real_t)nodes + 1) / 2
+                              : nodes * ((igraph_real_t)nodes - 1) / 2;
+        }
+        if (no_of_edges > max_no_of_edges) {
+            IGRAPH_ERROR("Too many edges requested", IGRAPH_EINVAL);
+        }
+    }
+
+    /* Calculate the cumulative fitness scores */
+    IGRAPH_VECTOR_INIT_FINALLY(&cum_fitness_out, no_of_nodes);
+    IGRAPH_CHECK(igraph_vector_cumsum(&cum_fitness_out, fitness_out));
+    max_out = igraph_vector_tail(&cum_fitness_out);
+    p_cum_fitness_out = &cum_fitness_out;
+    if (is_directed) {
+        IGRAPH_VECTOR_INIT_FINALLY(&cum_fitness_in, no_of_nodes);
+        IGRAPH_CHECK(igraph_vector_cumsum(&cum_fitness_in, fitness_in));
+        max_in = igraph_vector_tail(&cum_fitness_in);
+        p_cum_fitness_in = &cum_fitness_in;
+    } else {
+        max_in = max_out;
+        p_cum_fitness_in = &cum_fitness_out;
+    }
+
+    RNG_BEGIN();
+    num_steps = no_of_edges;
+    if (multiple) {
+        /* Generating when multiple edges are allowed */
+
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * no_of_edges));
+
+        while (no_of_edges > 0) {
+            /* Report progress after every 10000 edges */
+            if ((step_counter++) % 10000 == 0) {
+                IGRAPH_PROGRESS("Static fitness game", 100.0 * (1 - no_of_edges / num_steps), NULL);
+                IGRAPH_ALLOW_INTERRUPTION();
+            }
+
+            x = RNG_UNIF(0, max_out);
+            igraph_vector_binsearch(p_cum_fitness_out, x, &from);
+            x = RNG_UNIF(0, max_in);
+            igraph_vector_binsearch(p_cum_fitness_in, x, &to);
+
+            /* Skip if loop edge and loops = false */
+            if (!loops && from == to) {
+                continue;
+            }
+
+            igraph_vector_push_back(&edges, from);
+            igraph_vector_push_back(&edges, to);
+
+            no_of_edges--;
+        }
+
+        /* Create the graph */
+        IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, is_directed));
+
+        /* Clear the edge list */
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        /* Multiple edges are disallowed */
+        igraph_adjlist_t al;
+        igraph_vector_int_t* neis;
+
+        IGRAPH_CHECK(igraph_adjlist_init_empty(&al, no_of_nodes));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
+        while (no_of_edges > 0) {
+            /* Report progress after every 10000 edges */
+            if ((step_counter++) % 10000 == 0) {
+                IGRAPH_PROGRESS("Static fitness game", 100.0 * (1 - no_of_edges / num_steps), NULL);
+                IGRAPH_ALLOW_INTERRUPTION();
+            }
+
+            x = RNG_UNIF(0, max_out);
+            igraph_vector_binsearch(p_cum_fitness_out, x, &from);
+            x = RNG_UNIF(0, max_in);
+            igraph_vector_binsearch(p_cum_fitness_in, x, &to);
+
+            /* Skip if loop edge and loops = false */
+            if (!loops && from == to) {
+                continue;
+            }
+
+            /* For undirected graphs, ensure that from < to */
+            if (!is_directed && from > to) {
+                pos = from; from = to; to = pos;
+            }
+
+            /* Is there already an edge? If so, try again */
+            neis = igraph_adjlist_get(&al, from);
+            if (igraph_vector_int_binsearch(neis, to, &pos)) {
+                continue;
+            }
+
+            /* Insert the edge */
+            IGRAPH_CHECK(igraph_vector_int_insert(neis, pos, to));
+
+            no_of_edges--;
+        }
+
+        /* Create the graph. We cannot use IGRAPH_ALL here for undirected graphs
+         * because we did not add edges in both directions in the adjacency list.
+         * We will use igraph_to_undirected in an extra step. */
+        IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1));
+        if (!is_directed) {
+            IGRAPH_CHECK(igraph_to_undirected(graph, IGRAPH_TO_UNDIRECTED_EACH, 0));
+        }
+
+        /* Clear the adjacency list */
+        igraph_adjlist_destroy(&al);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    RNG_END();
+
+    IGRAPH_PROGRESS("Static fitness game", 100.0, NULL);
+
+    /* Cleanup before we create the graph */
+    if (is_directed) {
+        igraph_vector_destroy(&cum_fitness_in);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    igraph_vector_destroy(&cum_fitness_out);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/**
+ * \ingroup generators
+ * \function igraph_static_power_law_game
+ * \brief Generates a non-growing random graph with expected power-law degree distributions.
+ *
+ * This game generates a directed or undirected random graph where the
+ * degrees of vertices follow power-law distributions with prescribed
+ * exponents. For directed graphs, the exponents of the in- and out-degree
+ * distributions may be specified separately.
+ *
+ * </para><para>
+ * The game simply uses \ref igraph_static_fitness_game with appropriately
+ * constructed fitness vectors. In particular, the fitness of vertex i
+ * is i<superscript>-alpha</superscript>, where alpha = 1/(gamma-1)
+ * and gamma is the exponent given in the arguments.
+ *
+ * </para><para>
+ * To remove correlations between in- and out-degrees in case of directed
+ * graphs, the in-fitness vector will be shuffled after it has been set up
+ * and before \ref igraph_static_fitness_game is called.
+ *
+ * </para><para>
+ * Note that significant finite size effects may be observed for exponents
+ * smaller than 3 in the original formulation of the game. This function
+ * provides an argument that lets you remove the finite size effects by
+ * assuming that the fitness of vertex i is
+ * (i+i0-1)<superscript>-alpha</superscript>,
+ * where i0 is a constant chosen appropriately to ensure that the maximum
+ * degree is less than the square root of the number of edges times the
+ * average degree; see the paper of Chung and Lu, and Cho et al for more
+ * details.
+ *
+ * </para><para>
+ * References:
+ *
+ * </para><para>
+ * Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution
+ * in scale-free networks. Phys Rev Lett 87(27):278701, 2001.
+ *
+ * </para><para>
+ * Chung F and Lu L: Connected components in a random graph with given
+ * degree sequences. Annals of Combinatorics 6, 125-145, 2002.
+ *
+ * </para><para>
+ * Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in
+ * scale-free networks under the Achlioptas process. Phys Rev Lett
+ * 103:135702, 2009.
+ *
+ * \param graph        Pointer to an uninitialized graph object.
+ * \param no_of_nodes  The number of nodes in the generated graph.
+ * \param no_of_edges  The number of edges in the generated graph.
+ * \param exponent_out The power law exponent of the degree distribution.
+ *                     For directed graphs, this specifies the exponent of the
+ *                     out-degree distribution. It must be greater than or
+ *                     equal to 2. If you pass \c IGRAPH_INFINITY here, you
+ *                     will get back an Erdos-Renyi random network.
+ * \param exponent_in  If negative, the generated graph will be undirected.
+ *                     If greater than or equal to 2, this argument specifies
+ *                     the exponent of the in-degree distribution. If
+ *                     non-negative but less than 2, an error will be
+ *                     generated.
+ * \param loops        Whether to allow loop edges in the generated graph.
+ * \param multiple     Whether to allow multiple edges in the generated graph.
+ * \param finite_size_correction  Whether to use the proposed finite size
+ *                     correction of Cho et al.
+ *
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid parameter
+ *         \c IGRAPH_ENOMEM: there is not enough
+ *         memory for the operation.
+ *
+ * Time complexity: O(|V| + |E| log |E|).
+ */
+int igraph_static_power_law_game(igraph_t *graph,
+                                 igraph_integer_t no_of_nodes, igraph_integer_t no_of_edges,
+                                 igraph_real_t exponent_out, igraph_real_t exponent_in,
+                                 igraph_bool_t loops, igraph_bool_t multiple,
+                                 igraph_bool_t finite_size_correction) {
+
+    igraph_vector_t fitness_out, fitness_in;
+    igraph_real_t alpha_out = 0.0, alpha_in = 0.0;
+    long int i;
+    igraph_real_t j;
+
+    if (no_of_nodes < 0) {
+        IGRAPH_ERROR("Invalid number of nodes", IGRAPH_EINVAL);
+    }
+
+    /* Calculate alpha_out */
+    if (exponent_out < 2) {
+        IGRAPH_ERROR("out-degree exponent must be >= 2", IGRAPH_EINVAL);
+    } else if (igraph_finite(exponent_out)) {
+        alpha_out = -1.0 / (exponent_out - 1);
+    } else {
+        alpha_out = 0.0;
+    }
+
+    /* Construct the out-fitnesses */
+    IGRAPH_VECTOR_INIT_FINALLY(&fitness_out, no_of_nodes);
+    j = no_of_nodes;
+    if (finite_size_correction && alpha_out < -0.5) {
+        /* See the Cho et al paper, first page first column + footnote 7 */
+        j += pow(no_of_nodes, 1 + 0.5 / alpha_out) *
+             pow(10 * sqrt(2) * (1 + alpha_out), -1.0 / alpha_out) - 1;
+    }
+    if (j < no_of_nodes) {
+        j = no_of_nodes;
+    }
+    for (i = 0; i < no_of_nodes; i++, j--) {
+        VECTOR(fitness_out)[i] = pow(j, alpha_out);
+    }
+
+    if (exponent_in >= 0) {
+        if (exponent_in < 2) {
+            IGRAPH_ERROR("in-degree exponent must be >= 2; use negative numbers "
+                         "for undirected graphs", IGRAPH_EINVAL);
+        } else if (igraph_finite(exponent_in)) {
+            alpha_in = -1.0 / (exponent_in - 1);
+        } else {
+            alpha_in = 0.0;
+        }
+
+        IGRAPH_VECTOR_INIT_FINALLY(&fitness_in, no_of_nodes);
+        j = no_of_nodes;
+        if (finite_size_correction && alpha_in < -0.5) {
+            /* See the Cho et al paper, first page first column + footnote 7 */
+            j += pow(no_of_nodes, 1 + 0.5 / alpha_in) *
+                 pow(10 * sqrt(2) * (1 + alpha_in), -1.0 / alpha_in) - 1;
+        }
+        if (j < no_of_nodes) {
+            j = no_of_nodes;
+        }
+        for (i = 0; i < no_of_nodes; i++, j--) {
+            VECTOR(fitness_in)[i] = pow(j, alpha_in);
+        }
+        IGRAPH_CHECK(igraph_vector_shuffle(&fitness_in));
+
+        IGRAPH_CHECK(igraph_static_fitness_game(graph, no_of_edges,
+                                                &fitness_out, &fitness_in, loops, multiple));
+
+        igraph_vector_destroy(&fitness_in);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        IGRAPH_CHECK(igraph_static_fitness_game(graph, no_of_edges,
+                                                &fitness_out, 0, loops, multiple));
+    }
+
+    igraph_vector_destroy(&fitness_out);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/**
+ * \ingroup generators
+ * \function igraph_k_regular_game
+ * \brief Generates a random graph where each vertex has the same degree.
+ *
+ * This game generates a directed or undirected random graph where the
+ * degrees of vertices are equal to a predefined constant k. For undirected
+ * graphs, at least one of k and the number of vertices must be even.
+ *
+ * </para><para>
+ * The game simply uses \ref igraph_degree_sequence_game with appropriately
+ * constructed degree sequences.
+ *
+ * \param graph        Pointer to an uninitialized graph object.
+ * \param no_of_nodes  The number of nodes in the generated graph.
+ * \param k            The degree of each vertex in an undirected graph, or
+ *                     the out-degree and in-degree of each vertex in a
+ *                     directed graph.
+ * \param directed     Whether the generated graph will be directed.
+ * \param multiple     Whether to allow multiple edges in the generated graph.
+ *
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid parameter; e.g., negative number of nodes,
+ *                           or odd number of nodes and odd k for undirected
+ *                           graphs.
+ *         \c IGRAPH_ENOMEM: there is not enough memory for the operation.
+ *
+ * Time complexity: O(|V|+|E|) if \c multiple is true, otherwise not known.
+ */
+int igraph_k_regular_game(igraph_t *graph,
+                          igraph_integer_t no_of_nodes, igraph_integer_t k,
+                          igraph_bool_t directed, igraph_bool_t multiple) {
+    igraph_vector_t degseq;
+    igraph_degseq_t mode = multiple ? IGRAPH_DEGSEQ_SIMPLE : IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE;
+
+    /* Note to self: we are not using IGRAPH_DEGSEQ_VL when multiple = false
+     * because the VL method is not really good at generating k-regular graphs.
+     * Actually, that's why we have added SIMPLE_NO_MULTIPLE. */
+
+    if (no_of_nodes < 0) {
+        IGRAPH_ERROR("number of nodes must be non-negative", IGRAPH_EINVAL);
+    }
+    if (k < 0) {
+        IGRAPH_ERROR("degree must be non-negative", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&degseq, no_of_nodes);
+    igraph_vector_fill(&degseq, k);
+    IGRAPH_CHECK(igraph_degree_sequence_game(graph, &degseq, directed ? &degseq : 0, mode));
+
+    igraph_vector_destroy(&degseq);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_correlated_game
+ * Generate pairs of correlated random graphs
+ *
+ * Sample a new graph by perturbing the adjacency matrix of a
+ * given graph and shuffling its vertices.
+ *
+ * \param old_graph The original graph.
+ * \param new_graph The new graph will be stored here.
+ * \param corr A scalar in the unit interval, the target Pearson
+ *        correlation between the adjacency matrices of the original the
+ *        generated graph (the adjacency matrix being used as a vector).
+ * \param p A numeric scalar, the probability of an edge between two
+ *        vertices, it must in the open (0,1) interval.
+ * \param permutation A permutation to apply to the vertices of the
+ *        generated graph. It can also be a null pointer, in which case
+ *        the vertices will not be permuted.
+ * \return Error code
+ *
+ * \sa \ref igraph_correlated_pair_game() for generating a pair
+ * of correlated random graphs in one go.
+ */
+
+int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph,
+                           igraph_real_t corr, igraph_real_t p,
+                           const igraph_vector_t *permutation) {
+
+    int no_of_nodes = igraph_vcount(old_graph);
+    int no_of_edges = igraph_ecount(old_graph);
+    igraph_bool_t directed = igraph_is_directed(old_graph);
+    igraph_real_t no_of_all = directed ? no_of_nodes * (no_of_nodes - 1) :
+                              no_of_nodes * (no_of_nodes - 1) / 2;
+    igraph_real_t no_of_missing = no_of_all - no_of_edges;
+    igraph_real_t q = p + corr * (1 - p);
+    igraph_real_t p_del = 1 - q;
+    igraph_real_t p_add = ((1 - q) * (p / (1 - p)));
+    igraph_vector_t add, delete, edges, newedges;
+    igraph_real_t last;
+    int p_e = 0, p_a = 0, p_d = 0, no_add, no_del;
+    igraph_real_t inf = IGRAPH_INFINITY;
+    igraph_real_t next_e, next_a, next_d;
+    int i;
+
+    if (corr < -1 || corr > 1) {
+        IGRAPH_ERROR("Correlation must be in [-1,1] in correlated "
+                     "Erdos-Renyi game", IGRAPH_EINVAL);
+    }
+    if (p <= 0 || p >= 1) {
+        IGRAPH_ERROR("Edge probability must be in (0,1) in correlated "
+                     "Erdos-Renyi game", IGRAPH_EINVAL);
+    }
+    if (permutation) {
+        if (igraph_vector_size(permutation) != no_of_nodes) {
+            IGRAPH_ERROR("Invalid permutation length in correlated Erdos-Renyi game",
+                         IGRAPH_EINVAL);
+        }
+    }
+
+    /* Special cases */
+
+    if (corr == 0) {
+        return igraph_erdos_renyi_game(new_graph, IGRAPH_ERDOS_RENYI_GNP,
+                                       no_of_nodes, p, directed,
+                                       IGRAPH_NO_LOOPS);
+    }
+    if (corr == 1) {
+        /* We don't copy, because we don't need the attributes.... */
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+        IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0));
+        if (permutation) {
+            int newec = igraph_vector_size(&edges);
+            for (i = 0; i < newec; i++) {
+                int tmp = VECTOR(edges)[i];
+                VECTOR(edges)[i] = VECTOR(*permutation)[tmp];
+            }
+        }
+        IGRAPH_CHECK(igraph_create(new_graph, &edges, no_of_nodes, directed));
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(1);
+        return 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&add, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&delete, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+
+    IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0));
+
+    RNG_BEGIN();
+
+    if (p_del > 0) {
+        last = RNG_GEOM(p_del);
+        while (last < no_of_edges) {
+            IGRAPH_CHECK(igraph_vector_push_back(&delete, last));
+            last += RNG_GEOM(p_del);
+            last += 1;
+        }
+    }
+    no_del = igraph_vector_size(&delete);
+
+    if (p_add > 0) {
+        last = RNG_GEOM(p_add);
+        while (last < no_of_missing) {
+            IGRAPH_CHECK(igraph_vector_push_back(&add, last));
+            last += RNG_GEOM(p_add);
+            last += 1;
+        }
+    }
+    no_add = igraph_vector_size(&add);
+
+    RNG_END();
+
+    IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0));
+
+    /* Now we are merging the original edges, the edges that are removed,
+       and the new edges. We have the following pointers:
+       - p_a: the next edge to add
+       - p_d: the next edge to delete
+       - p_e: the next original edge
+       - next_e: the code of the next edge in 'edges'
+       - next_a: the code of the next edge to add
+       - next_d: the code of the next edge to delete */
+
+#define D_CODE(f,t) (((t)==no_of_nodes-1 ? f : t) * no_of_nodes + (f))
+#define U_CODE(f,t) ((t) * ((t)-1) / 2 + (f))
+#define CODE(f,t) (directed ? D_CODE(f,t) : U_CODE(f,t))
+#define CODEE() (CODE(VECTOR(edges)[2*p_e], VECTOR(edges)[2*p_e+1]))
+
+    /* First we (re)code the edges to delete */
+
+    for (i = 0; i < no_del; i++) {
+        int td = VECTOR(delete)[i];
+        int from = VECTOR(edges)[2 * td];
+        int to = VECTOR(edges)[2 * td + 1];
+        VECTOR(delete)[i] = CODE(from, to);
+    }
+
+    IGRAPH_CHECK(igraph_vector_reserve(&newedges,
+                                       (no_of_edges - no_del + no_add) * 2));
+
+    /* Now we can do the merge. Additional edges are tricky, because
+       the code must be shifted by the edges in the original graph. */
+
+#define UPD_E()                             \
+    { if (p_e < no_of_edges) { next_e=CODEE(); } else { next_e = inf; } }
+#define UPD_A()                             \
+{ if (p_a < no_add) { \
+            next_a = VECTOR(add)[p_a] + p_e; } else { next_a = inf; } }
+#define UPD_D()                             \
+{ if (p_d < no_del) { \
+            next_d = VECTOR(delete)[p_d]; } else { next_d = inf; } }
+
+    UPD_E(); UPD_A(); UPD_D();
+
+    while (next_e != inf || next_a != inf || next_d != inf) {
+        if (next_e <= next_a && next_e < next_d) {
+
+            /* keep an edge */
+            IGRAPH_CHECK(igraph_vector_push_back(&newedges, VECTOR(edges)[2 * p_e]));
+            IGRAPH_CHECK(igraph_vector_push_back(&newedges, VECTOR(edges)[2 * p_e + 1]));
+            p_e ++; UPD_E(); UPD_A()
+
+        } else if (next_e <= next_a && next_e == next_d) {
+
+            /* delete an edge */
+            p_e ++; UPD_E(); UPD_A();
+            p_d++; UPD_D();
+
+        } else {
+
+            /* add an edge */
+            int to, from;
+            if (directed) {
+                to = (int) floor(next_a / no_of_nodes);
+                from = (int) (next_a - ((igraph_real_t)to) * no_of_nodes);
+                if (from == to) {
+                    to = no_of_nodes - 1;
+                }
+            } else {
+                to = (int) floor((sqrt(8 * next_a + 1) + 1) / 2);
+                from = (int) (next_a - (((igraph_real_t)to) * (to - 1)) / 2);
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(&newedges, from));
+            IGRAPH_CHECK(igraph_vector_push_back(&newedges, to));
+            p_a++; UPD_A();
+
+        }
+    }
+
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&add);
+    igraph_vector_destroy(&delete);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (permutation) {
+        int newec = igraph_vector_size(&newedges);
+        for (i = 0; i < newec; i++) {
+            int tmp = VECTOR(newedges)[i];
+            VECTOR(newedges)[i] = VECTOR(*permutation)[tmp];
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(new_graph, &newedges, no_of_nodes, directed));
+
+    igraph_vector_destroy(&newedges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+#undef D_CODE
+#undef U_CODE
+#undef CODE
+#undef CODEE
+#undef UPD_E
+#undef UPD_A
+#undef UPD_D
+
+/**
+ * \function igraph_correlated_pair_game
+ * Generate pairs of correlated random graphs
+ *
+ * Sample two random graphs, with given correlation.
+ *
+ * \param graph1 The first graph will be stored here.
+ * \param graph2 The second graph will be stored here.
+ * \param n The number of vertices in both graphs.
+ * \param corr A scalar in the unit interval, the target Pearson
+ *        correlation between the adjacency matrices of the original the
+ *        generated graph (the adjacency matrix being used as a vector).
+ * \param p A numeric scalar, the probability of an edge between two
+ *        vertices, it must in the open (0,1) interval.
+ * \param directed Whether to generate directed graphs.
+ * \param permutation A permutation to apply to the vertices of the
+ *        second graph. It can also be a null pointer, in which case
+ *        the vertices will not be permuted.
+ * \return Error code
+ *
+ * \sa \ref igraph_correlated_game() for generating a correlated pair
+ * to a given graph.
+ */
+
+int igraph_correlated_pair_game(igraph_t *graph1, igraph_t *graph2,
+                                int n, igraph_real_t corr, igraph_real_t p,
+                                igraph_bool_t directed,
+                                const igraph_vector_t *permutation) {
+
+    IGRAPH_CHECK(igraph_erdos_renyi_game(graph1, IGRAPH_ERDOS_RENYI_GNP, n, p,
+                                         directed, IGRAPH_NO_LOOPS));
+    IGRAPH_CHECK(igraph_correlated_game(graph1, graph2, corr, p, permutation));
+    return 0;
+}
+
+
+/* Uniform sampling of labelled trees (igraph_tree_game) */
+
+/* The following implementation uniformly samples Prufer trees and converts
+ * them to trees.
+ */
+
+static int igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) {
+    igraph_vector_int_t prufer;
+    long i;
+
+    if (directed) {
+        IGRAPH_ERROR("The Prufer method for random tree generation does not support directed trees", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_init(&prufer, n - 2));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &prufer);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < n - 2; ++i) {
+        VECTOR(prufer)[i] = RNG_INTEGER(0, n - 1);
+    }
+
+    RNG_END();
+
+    IGRAPH_CHECK(igraph_from_prufer(graph, &prufer));
+
+    igraph_vector_int_destroy(&prufer);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/* The following implementation is based on loop-erased random walks and Wilson's algorithm
+ * for uniformly sampling spanning trees. We effectively sample spanning trees of the complete
+ * graph.
+ */
+
+/* swap two elements of a vector_int */
+#define SWAP_INT_ELEM(vec, i, j) \
+    { \
+        igraph_integer_t temp; \
+        temp = VECTOR(vec)[i]; \
+        VECTOR(vec)[i] = VECTOR(vec)[j]; \
+        VECTOR(vec)[j] = temp; \
+    }
+
+static int igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) {
+    igraph_vector_t edges;
+    igraph_vector_int_t vertices;
+    igraph_vector_bool_t visited;
+    long i, j, k;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (n - 1));
+
+    IGRAPH_CHECK(igraph_vector_bool_init(&visited, n));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited);
+
+    /* The vertices vector contains visited vertices between 0..k-1, unvisited ones between k..n-1. */
+    IGRAPH_CHECK(igraph_vector_int_init_seq(&vertices, 0, n - 1));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &vertices);
+
+    RNG_BEGIN();
+
+    /* A simple implementation could be as below. This is for illustration only.
+     * The actually implemented algorithm avoids unnecessary walking on the already visited
+     * portion of the vertex set.
+     */
+    /*
+    // pick starting point for the walk
+    i = RNG_INTEGER(0, n-1);
+    VECTOR(visited)[i] = 1;
+
+    k=1;
+    while (k < n) {
+        // pick next vertex in the walk
+        j = RNG_INTEGER(0, n-1);
+        // if it has not been visited before, connect to the previous vertex in the sequence
+        if (! VECTOR(visited)[j]) {
+            VECTOR(edges)[2*k - 2] = i;
+            VECTOR(edges)[2*k - 1] = j;
+            VECTOR(visited)[j] = 1;
+            k++;
+        }
+        i=j;
+    }
+    */
+
+    i = RNG_INTEGER(0, n - 1);
+    VECTOR(visited)[i] = 1;
+    SWAP_INT_ELEM(vertices, 0, i);
+
+    for (k = 1; k < n; ++k) {
+        j = RNG_INTEGER(0, n - 1);
+        if (VECTOR(visited)[VECTOR(vertices)[j]]) {
+            i = VECTOR(vertices)[j];
+            j = RNG_INTEGER(k, n - 1);
+        }
+        VECTOR(visited)[VECTOR(vertices)[j]] = 1;
+        SWAP_INT_ELEM(vertices, k, j);
+        VECTOR(edges)[2 * k - 2] = i;
+        i = VECTOR(vertices)[k];
+        VECTOR(edges)[2 * k - 1] = i;
+    }
+
+    RNG_END();
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+
+    igraph_vector_int_destroy(&vertices);
+    igraph_vector_bool_destroy(&visited);
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+#undef SWAP_INT_ELEM
+
+/**
+ * \ingroup generators
+ * \function igraph_tree_game
+ * \brief Generates a random tree with the given number of nodes
+ *
+ * This function samples uniformly from the set of labelled trees,
+ * i.e. it can generate each labelled tree with the same probability.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param n The number of nodes in the tree.
+ * \param directed Whether to create a directed tree. The edges are oriented away from the root.
+ * \param method The algorithm to use to generate the tree. Possible values:
+ *        \clist
+ *        \cli IGRAPH_RANDOM_TREE_PRUFER
+ *          This algorithm samples Pr&uuml;fer sequences unformly, then converts them to trees.
+ *          Directed trees are not currently supported.
+ *        \cli IGRAPH_RANDOM_LERW
+ *          This algorithm effectively performs a loop-erased random walk on the complete graph
+ *          to uniformly sample its spanning trees (Wilson's algorithm).
+ *        \endclist
+ * \return Error code:
+ *          \c IGRAPH_ENOMEM: there is not enough
+ *           memory to perform the operation.
+ *          \c IGRAPH_EINVAL: invalid tree size
+ *
+ * \sa \ref igraph_from_prufer()
+ *
+ */
+
+int igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_random_tree_t method) {
+    if (n < 2) {
+        IGRAPH_CHECK(igraph_empty(graph, n, directed));
+        return IGRAPH_SUCCESS;
+    }
+
+    switch (method) {
+    case IGRAPH_RANDOM_TREE_PRUFER:
+        return igraph_i_tree_game_prufer(graph, n, directed);
+    case IGRAPH_RANDOM_TREE_LERW:
+        return igraph_i_tree_game_loop_erased_random_walk(graph, n, directed);
+    default:
+        IGRAPH_ERROR("Invalid method for random tree construction", IGRAPH_EINVAL);
+    }
+}
diff --git a/igraph/src/gengraph_box_list.cpp b/igraph/src/gengraph_box_list.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/gengraph_box_list.cpp
@@ -0,0 +1,108 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "gengraph_box_list.h"
+#include <cassert>
+
+namespace gengraph {
+
+void box_list::insert(int v) {
+    register int d = deg[v];
+    if (d < 1) {
+        return;
+    }
+    if (d > dmax) {
+        dmax = d;
+    }
+    int yo = list[d - 1];
+    list[d - 1] = v;
+    prev[v] = -1;
+    next[v] = yo;
+    if (yo >= 0) {
+        prev[yo] = v;
+    }
+}
+
+void box_list::pop(int v) {
+    register int p = prev[v];
+    register int n = next[v];
+    if (p < 0) {
+        register int d = deg[v];
+        assert(list[d - 1] == v);
+        list[d - 1] = n;
+        if (d == dmax && n < 0) do {
+                dmax--;
+            } while (dmax > 0 && list[dmax - 1] < 0);
+    } else {
+        next[p] = n;
+    }
+    if (n >= 0) {
+        prev[n] = p;
+    }
+}
+
+box_list::box_list(int n0, int *deg0) : n(n0), deg(deg0) {
+    next = new int[n];
+    prev = new int[n];
+    dmax = -1;
+    int i;
+    for (i = 0; i < n; i++) if (deg[i] > dmax) {
+            dmax = deg[i];
+        }
+    list = new int[dmax];
+    for (i = 0; i < dmax; i++) {
+        list[i] = -1;
+    }
+    for (i = 0; i < n; i++) {
+        insert(i);
+    }
+}
+
+box_list::~box_list() {
+    delete[] prev;
+    delete[] next;
+    delete[] list;
+}
+
+void box_list::pop_vertex(int v, int **neigh) {
+    int k = deg[v];
+    if (k < 1) {
+        return;
+    }
+    pop(v);
+    int *w = neigh[v];
+    while (k--) {
+        int v2 = *(w++);
+        register int *w2 = neigh[v2];
+        while (*w2 != v) {
+            w2++;
+        }
+        register int *w3 = neigh[v2] + (deg[v2] - 1);
+        assert(w2 <= w3);
+        register int tmp = *w3;
+        *w3 = *w2;
+        *w2 = tmp;
+        pop(v2);
+        deg[v2]--;
+        insert(v2);
+    }
+}
+
+} // namespace gengraph
diff --git a/igraph/src/gengraph_degree_sequence.cpp b/igraph/src/gengraph_degree_sequence.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/gengraph_degree_sequence.cpp
@@ -0,0 +1,420 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "gengraph_definitions.h"
+#include "gengraph_random.h"
+#include "gengraph_powerlaw.h"
+#include "gengraph_degree_sequence.h"
+#include "gengraph_hash.h"
+
+#include "igraph_statusbar.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cmath>
+#include <cassert>
+#include <vector>
+
+// using namespace __gnu_cxx;
+using namespace std;
+
+namespace gengraph {
+
+// shuffle an int[] randomly
+void random_permute(int *a, int n);
+
+// sort an array of positive integers in time & place O(n + max)
+void cumul_sort(int *q, int n);
+
+
+void degree_sequence::detach() {
+    deg = NULL;
+}
+
+degree_sequence::~degree_sequence() {
+    if (deg != NULL) {
+        delete[] deg;
+    }
+    deg = NULL;
+}
+
+void degree_sequence::make_even(int mini, int maxi) {
+    if (total % 2 == 0) {
+        return;
+    }
+    if (maxi < 0) {
+        maxi = 0x7FFFFFFF;
+    }
+    int i;
+    for (i = 0; i < n; i++) {
+        if (deg[i] > mini) {
+            deg[i]--;
+            total--;
+            break;
+        } else if (deg[i] < maxi) {
+            deg[i]++;
+            total++;
+            break;
+        }
+    }
+    if (i == n) {
+        IGRAPH_WARNING("Warning: degree_sequence::make_even() forced one "
+                       "degree to go over degmax");
+        deg[0]++;
+        total++;
+    }
+}
+
+void degree_sequence::shuffle() {
+    random_permute(deg, n);
+}
+
+void degree_sequence::sort() {
+    cumul_sort(deg, n);
+}
+
+void degree_sequence::compute_total() {
+    total = 0;
+    for (int i = 0; i < n; i++) {
+        total += deg[i];
+    }
+}
+
+degree_sequence::
+degree_sequence(int n0, int *degs) {
+    deg = degs;
+    n = n0;
+    compute_total();
+}
+
+degree_sequence::
+degree_sequence(const igraph_vector_t *out_seq) {
+    n = igraph_vector_size(out_seq);
+    deg = new int[n];
+    for (long int i = 0; i < n; i++) {
+        deg[i] = VECTOR(*out_seq)[i];
+    }
+    compute_total();
+}
+
+#ifndef FBUFF_SIZE
+    #define FBUFF_SIZE 999
+#endif //FBUFF_SIZE
+
+// degree_sequence::degree_sequence(FILE *f, bool DISTRIB) {
+//   n = 0;
+//   total = 0;
+//   char *buff = new char[FBUFF_SIZE];
+//   char *c;
+//   vector<int> degree;
+//   if(!DISTRIB) {
+//     // Input is a 'raw' degree sequence d0 d1 d2 d3 ...
+//     while(fgets(buff, FBUFF_SIZE, f)) {
+//       int d = strtol(buff, &c, 10);
+//       if(c == buff) continue;
+//       degree.push_back(d);
+//       total += d;
+//     }
+//     n = int(degree.size());
+//     deg = new int[n];
+//     int *yo = deg;
+//     vector<int>::iterator end = degree.end();
+//     for(vector<int>::iterator it=degree.begin(); it!=end; *(yo++) = *(it++));
+//   }
+//   else {
+//     // Input is a degree distribution : d0 #(degree=d0), d1 #(degree=d1), ...
+//     vector<int> n_with_degree;
+//     int line = 0;
+//     int syntax  = 0;
+//     int ignored = 0;
+//     int first_syntax  = 0;
+//     int first_ignored = 0;
+//     while(fgets(buff, FBUFF_SIZE, f)) {
+//       line++;
+//       int d = strtol(buff, &c, 10);
+//       if(c == buff) { ignored++; first_ignored = line; continue; }
+//       char *cc;
+//       int i = strtol(c, &cc, 10);
+//       if(cc == c) { syntax++; first_syntax = line; continue; }
+//       n += i;
+//       total += i*d;
+//       degree.push_back(d);
+//       n_with_degree.push_back(i);
+//       if( cc != c) {  syntax++; first_syntax = line; }
+//     }
+//     if(VERBOSE()) {
+//       if(ignored > 0) fprintf(stderr,"Ignored %d lines (first was line #%d)\n", ignored, first_ignored);
+//       if(syntax > 0) fprintf(stderr,"Found %d probable syntax errors (first was line #%d)\n", syntax, first_syntax);
+//     }
+//     deg = new int[n];
+//     int *yo = deg;
+//     vector<int>::iterator it_n = n_with_degree.begin();
+//     for(vector<int>::iterator it = degree.begin(); it != degree.end(); it++)
+//       for(int k = *(it_n++); k--; *yo++ = *it);
+//   }
+//   if(VERBOSE()) {
+//     if(total % 2 != 0) fprintf(stderr,"Warning: degree sequence is odd\n");
+//     fprintf(stderr,"Degree sequence created. N=%d, 2M=%d\n", n, total);
+//   }
+// }
+
+// n vertices, exponent, min degree, max degree, average degree (optional, default is -1)
+degree_sequence::
+degree_sequence(int _n, double exp, int degmin, int degmax, double z) {
+
+    n = _n;
+    if (exp == 0.0) {
+        // Binomial distribution
+        if (z < 0) {
+            igraph_error("Fatal error in degree_sequence Ctor: "
+                         "positive average degree must be specified", __FILE__,
+                         __LINE__, IGRAPH_EINVAL);
+        }
+        if (degmax < 0) {
+            degmax = n - 1;
+        }
+        total = int(floor(double(n) * z + 0.5));
+        deg = new int[n];
+        KW_RNG::RNG myrand;
+        double p = (z - double(degmin)) / double(n);
+        total = 0;
+        for (int i = 0; i < n; i++) {
+            do {
+                deg[i] = 1 + myrand.binomial(p, n);
+            } while (deg[i] > degmax);
+            total += deg[i];
+        }
+    } else {
+        // Power-law distribution
+        igraph_status("Creating powerlaw sampler...", 0);
+        powerlaw pw(exp, degmin, degmax);
+        if (z == -1.0) {
+            pw.init();
+            igraph_statusf("done. Mean=%f\n", 0, pw.mean());
+        } else {
+            double offset = pw.init_to_mean(z);
+            igraph_statusf("done. Offset=%f, Mean=%f\n", 0, offset, pw.mean());
+        }
+
+        deg = new int[n];
+        total = 0;
+        int i;
+
+        igraph_statusf("Sampling %d random numbers...", 0, n);
+        for (i = 0; i < n; i++) {
+            deg[i] = pw.sample();
+            total += deg[i];
+        }
+
+        igraph_status("done\nSimple statistics on degrees...", 0);
+        int wanted_total = int(floor(z * n + 0.5));
+        sort();
+        igraph_statusf("done : Max=%d, Total=%d.\n", 0, deg[0], total);
+        if (z != -1.0)  {
+            igraph_statusf("Adjusting total to %d...", 0, wanted_total);
+            int iterations = 0;
+
+            while (total != wanted_total) {
+                sort();
+                for (i = 0; i < n && total > wanted_total; i++) {
+                    total -= deg[i];
+                    if (total + degmin <= wanted_total) {
+                        deg[i] = wanted_total - total;
+                    } else {
+                        deg[i] = pw.sample();
+                    }
+                    total += deg[i];
+                }
+                iterations += i;
+                for (i = n - 1; i > 0 && total < wanted_total; i--) {
+                    total -= deg[i];
+                    if (total + (deg[0] >> 1) >= wanted_total) {
+                        deg[i] = wanted_total - total;
+                    } else {
+                        deg[i] = pw.sample();
+                    }
+                    total += deg[i];
+                }
+                iterations += n - 1 - i;
+            }
+            igraph_statusf("done(%d iterations).", 0, iterations);
+            igraph_statusf("  Now, degmax = %d\n", 0, dmax());
+        }
+
+        shuffle();
+    }
+}
+
+// void degree_sequence::print() {
+//   for(int i=0; i<n; i++) printf("%d\n",deg[i]);
+// }
+
+// void degree_sequence::print_cumul() {
+//   if(n==0) return;
+//   int dmax = deg[0];
+//   int dmin = deg[0];
+//   int i;
+//   for(i=1; i<n; i++) if(dmax<deg[i]) dmax=deg[i];
+//   for(i=1; i<n; i++) if(dmin>deg[i]) dmin=deg[i];
+//   int *dd = new int[dmax-dmin+1];
+//   for(i=dmin; i<=dmax; i++) dd[i-dmin]=0;
+//   if(VERBOSE()) fprintf(stderr,"Computing cumulative distribution...");
+//   for(i=0; i<n; i++) dd[deg[i]-dmin]++;
+//   if(VERBOSE()) fprintf(stderr,"done\n");
+//   for(i=dmin; i<=dmax; i++) if(dd[i-dmin]>0) printf("%d %d\n",i,dd[i-dmin]);
+//   delete[] dd;
+// }
+
+bool degree_sequence::havelhakimi() {
+
+    int i;
+    int dm = dmax() + 1;
+    // Sort vertices using basket-sort, in descending degrees
+    int *nb = new int[dm];
+    int *sorted = new int[n];
+    // init basket
+    for (i = 0; i < dm; i++) {
+        nb[i] = 0;
+    }
+    // count basket
+    for (i = 0; i < n; i++) {
+        nb[deg[i]]++;
+    }
+    // cumul
+    int c = 0;
+    for (i = dm - 1; i >= 0; i--) {
+        int t = nb[i];
+        nb[i] = c;
+        c += t;
+    }
+    // sort
+    for (i = 0; i < n; i++) {
+        sorted[nb[deg[i]]++] = i;
+    }
+
+// Binding process starts
+    int first = 0;  // vertex with biggest residual degree
+    int d = dm - 1; // maximum residual degree available
+
+    for (c = total / 2; c > 0; ) {
+        // We design by 'v' the vertex of highest degree (indexed by first)
+        // look for current degree of v
+        while (nb[d] <= first) {
+            d--;
+        }
+        // store it in dv
+        int dv = d;
+        // bind it !
+        c -= dv;
+        int dc = d;         // residual degree of vertices we bind to
+        int fc = ++first;   // position of the first vertex with degree dc
+
+        while (dv > 0 && dc > 0) {
+            int lc = nb[dc];
+            if (lc != fc) {
+                while (dv > 0 && lc > fc) {
+                    // binds v with sorted[--lc]
+                    dv--;
+                    lc--;
+                }
+                fc = nb[dc];
+                nb[dc] = lc;
+            }
+            dc--;
+        }
+        if (dv != 0) { // We couldn't bind entirely v
+            delete[] nb;
+            delete[] sorted;
+            return false;
+        }
+    }
+    delete[] nb;
+    delete[] sorted;
+    return true;
+}
+
+//*************************
+// Subroutines definitions
+//*************************
+
+inline int int_adjust(double x) {
+    return (int(floor(x + random_float())));
+}
+
+void random_permute(int *a, int n) {
+    int j, tmp;
+    for (int i = 0; i < n - 1; i++) {
+        j = i + my_random() % (n - i);
+        tmp = a[i];
+        a[i] = a[j];
+        a[j] = tmp;
+    }
+}
+
+void cumul_sort(int *q, int n) {
+    // looks for the maximum q[i] and minimum
+    if (n == 0) {
+        return;
+    }
+    int qmax = q[0];
+    int qmin = q[0];
+    int i;
+    for (i = 0; i < n; i++) if (q[i] > qmax) {
+            qmax = q[i];
+        }
+    for (i = 0; i < n; i++) if (q[i] < qmin) {
+            qmin = q[i];
+        }
+
+    // counts #q[i] with given q
+    int *nb = new int[qmax - qmin + 1];
+    for (int *onk = nb + (qmax - qmin + 1); onk != nb; * (--onk) = 0) { }
+    for (i = 0; i < n; i++) {
+        nb[q[i] - qmin]++;
+    }
+
+    // counts cumulative distribution
+    for (i = qmax - qmin; i > 0; i--) {
+        nb[i - 1] += nb[i];
+    }
+
+    // sort by q[i]
+    int last_q;
+    int tmp;
+    int modifier = qmax - qmin + 1;
+    for (int current = 0; current < n; current++) {
+        tmp = q[current];
+        if (tmp >= qmin && tmp <= qmax) {
+            last_q = qmin;
+            do {
+                q[current] = last_q + modifier;
+                last_q = tmp;
+                current = --nb[last_q - qmin];
+            } while ((tmp = q[current]) >= qmin && tmp <= qmax);
+            q[current] = last_q + modifier;
+        }
+    }
+    delete[] nb;
+    for (i = 0; i < n; i++) {
+        q[i] = q[i] - modifier;
+    }
+}
+
+} // namespace gengraph
diff --git a/igraph/src/gengraph_graph_molloy_hash.cpp b/igraph/src/gengraph_graph_molloy_hash.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/gengraph_graph_molloy_hash.cpp
@@ -0,0 +1,1173 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "gengraph_definitions.h"
+#include <cassert>
+#include <cstdlib>
+#include <cstdio>
+#include <cmath>
+
+#include "gengraph_qsort.h"
+#include "gengraph_hash.h"
+#include "gengraph_degree_sequence.h"
+#include "gengraph_graph_molloy_hash.h"
+
+#include "config.h"
+#include "igraph_math.h"
+#include "igraph_constructors.h"
+#include "igraph_error.h"
+#include "igraph_statusbar.h"
+#include "igraph_progress.h"
+
+namespace gengraph {
+
+//_________________________________________________________________________
+void graph_molloy_hash::compute_neigh() {
+    int *p = links;
+    for (int i = 0; i < n; i++) {
+        neigh[i] = p;
+        p += HASH_SIZE(deg[i]);
+    }
+}
+
+//_________________________________________________________________________
+void graph_molloy_hash::compute_size() {
+    size = 0;
+    for (int i = 0; i < n; i++) {
+        size += HASH_SIZE(deg[i]);
+    }
+}
+
+//_________________________________________________________________________
+void graph_molloy_hash::init() {
+    for (int i = 0; i < size; i++) {
+        links[i] = HASH_NONE;
+    }
+}
+
+//_________________________________________________________________________
+graph_molloy_hash::graph_molloy_hash(degree_sequence &degs) {
+    igraph_status("Allocating memory for graph...", 0);
+    int s = alloc(degs);
+    igraph_statusf("%d bytes allocated successfully\n", 0, s);
+}
+
+//_________________________________________________________________________
+int graph_molloy_hash::alloc(degree_sequence &degs) {
+    n = degs.size();
+    a = degs.sum();
+    assert(a % 2 == 0);
+
+    deg = degs.seq();
+    compute_size();
+    deg = new int[n + size];
+    if (deg == NULL) {
+        return 0;
+    }
+    int i;
+    for (i = 0; i < n; i++) {
+        deg[i] = degs[i];
+    }
+    links = deg + n;
+    init();
+    neigh = new int*[n];
+    if (neigh == NULL) {
+        return 0;
+    }
+    compute_neigh();
+    return sizeof(int *)*n + sizeof(int) * (n + size);
+}
+
+//_________________________________________________________________________
+graph_molloy_hash::~graph_molloy_hash() {
+    if (deg != NULL) {
+        delete[] deg;
+    }
+    if (neigh != NULL) {
+        delete[] neigh;
+    }
+    deg = NULL;
+    neigh = NULL;
+}
+
+//_________________________________________________________________________
+graph_molloy_hash::graph_molloy_hash(int *svg) {
+    // Read n
+    n = *(svg++);
+    // Read a
+    a = *(svg++);
+    assert(a % 2 == 0);
+    // Read degree sequence
+    degree_sequence dd(n, svg);
+    // Build neigh[] and alloc links[]
+    alloc(dd);
+    dd.detach();
+    // Read links[]
+    restore(svg + n);
+}
+
+//_________________________________________________________________________
+int *graph_molloy_hash::hard_copy() {
+    int *hc = new int[2 + n + a / 2]; // to store n,a,deg[] and links[]
+    hc[0] = n;
+    hc[1] = a;
+    memcpy(hc + 2, deg, sizeof(int)*n);
+    int *p = hc + 2 + n;
+    int *l = links;
+    for (int i = 0; i < n; i++) for (int j = HASH_SIZE(deg[i]); j--; l++) {
+            register int d;
+            if ((d = *l) != HASH_NONE && d >= i) {
+                *(p++) = d;
+            }
+        }
+    assert(p == hc + 2 + n + a / 2);
+    return hc;
+}
+
+//_________________________________________________________________________
+bool graph_molloy_hash::is_connected() {
+    bool *visited = new bool[n];
+    int *buff = new int[n];
+    int comp_size = depth_search(visited, buff);
+    delete[] visited;
+    delete[] buff;
+    return (comp_size == n);
+}
+
+//_________________________________________________________________________
+int* graph_molloy_hash::backup() {
+    int *b = new int[a / 2];
+    int *c = b;
+    int *p = links;
+    for (int i = 0; i < n; i++)
+        for (int d = HASH_SIZE(deg[i]); d--; p++) if (*p != HASH_NONE && *p > i) {
+                *(c++) = *p;
+            }
+    assert(c == b + (a / 2));
+    return b;
+}
+
+//_________________________________________________________________________
+void graph_molloy_hash::restore(int* b) {
+    init();
+    int i;
+    int *dd = new int[n];
+    memcpy(dd, deg, sizeof(int)*n);
+    for (i = 0; i < n; i++) {
+        deg[i] = 0;
+    }
+    for (i = 0; i < n - 1; i++) {
+        while (deg[i] < dd[i]) {
+            add_edge(i, *b, dd);
+            b++;
+        }
+    }
+    delete[] dd;
+}
+
+//_________________________________________________________________________
+bool graph_molloy_hash::isolated(int v, int K, int *Kbuff, bool *visited) {
+    if (K < 2) {
+        return false;
+    }
+#ifdef OPT_ISOLATED
+    if (K <= deg[v] + 1) {
+        return false;
+    }
+#endif //OPT_ISOLATED
+    int *seen  = Kbuff;
+    int *known = Kbuff;
+    int *max   = Kbuff + K;
+    *(known++) = v;
+    visited[v] = true;
+    bool is_isolated = true;
+
+    while (known != seen) {
+        v = *(seen++);
+        int *ww = neigh[v];
+        int w;
+        for (int d = HASH_SIZE(deg[v]); d--; ww++) if ((w = *ww) != HASH_NONE && !visited[w]) {
+#ifdef OPT_ISOLATED
+                if (K <= deg[w] + 1 || known == max) {
+#else //OPT_ISOLATED
+                if (known == max) {
+#endif //OPT_ISOLATED
+                    is_isolated = false;
+                    goto end_isolated;
+                }
+                visited[w] = true;
+                *(known++) = w;
+            }
+    }
+end_isolated:
+    // Undo the changes to visited[]...
+    while (known != Kbuff) {
+        visited[*(--known)] = false;
+    }
+    return is_isolated;
+}
+
+//_________________________________________________________________________
+int graph_molloy_hash::random_edge_swap(int K, int *Kbuff, bool *visited) {
+    // Pick two random vertices a and c
+    int f1 = pick_random_vertex();
+    int f2 = pick_random_vertex();
+    // Check that f1 != f2
+    if (f1 == f2) {
+        return 0;
+    }
+    // Get two random edges (f1,*f1t1) and (f2,*f2t2)
+    int *f1t1 = random_neighbour(f1);
+    int t1 = *f1t1;
+    int *f2t2 = random_neighbour(f2);
+    int t2 = *f2t2;
+    // Check simplicity
+    if (t1 == t2 || f1 == t2 || f2 == t1) {
+        return 0;
+    }
+    if (is_edge(f1, t2) || is_edge(f2, t1)) {
+        return 0;
+    }
+    // Swap
+    int *f1t2 = H_rpl(neigh[f1], deg[f1], f1t1, t2);
+    int *f2t1 = H_rpl(neigh[f2], deg[f2], f2t2, t1);
+    int *t1f2 = H_rpl(neigh[t1], deg[t1], f1, f2);
+    int *t2f1 = H_rpl(neigh[t2], deg[t2], f2, f1);
+    // isolation test
+    if (K <= 2) {
+        return 1;
+    }
+    if ( !isolated(f1, K, Kbuff, visited) && !isolated(f2, K, Kbuff, visited) ) {
+        return 1;
+    }
+    // undo swap
+    H_rpl(neigh[f1], deg[f1], f1t2, t1);
+    H_rpl(neigh[f2], deg[f2], f2t1, t2);
+    H_rpl(neigh[t1], deg[t1], t1f2, f1);
+    H_rpl(neigh[t2], deg[t2], t2f1, f2);
+    return 0;
+}
+
+//_________________________________________________________________________
+unsigned long graph_molloy_hash::shuffle(unsigned long times,
+        unsigned long maxtimes, int type) {
+    igraph_progress("Shuffle", 0, 0);
+    // assert(verify());
+    // counters
+    unsigned long nb_swaps = 0;
+    unsigned long all_swaps = 0;
+    unsigned long cost = 0;
+    // window
+    double T = double(min((unsigned long)(a), times) / 10);
+    if (type == OPTIMAL_HEURISTICS) {
+        T = double(optimal_window());
+    }
+    if (type == BRUTE_FORCE_HEURISTICS) {
+        T = double(times * 2);
+    }
+    // isolation test parameter, and buffers
+    double K = 2.4;
+    int *Kbuff = new int[int(K) + 1];
+    bool *visited = new bool[n];
+    for (int i = 0; i < n; i++) {
+        visited[i] = false;
+    }
+    // Used for monitoring , active only if VERBOSE()
+    int failures = 0;
+    int successes = 0;
+    double avg_K = 0;
+    double avg_T = 0;
+    unsigned long next = times;
+    next = 0;
+
+    // Shuffle: while #edge swap attempts validated by connectivity < times ...
+    while (times > nb_swaps && maxtimes > all_swaps) {
+        // Backup graph
+        int *save = backup();
+        // Prepare counters, K, T
+        unsigned long swaps = 0;
+        int K_int = 0;
+        if (type == FINAL_HEURISTICS || type == BRUTE_FORCE_HEURISTICS) {
+            K_int = int(K);
+        }
+        unsigned long T_int = (unsigned long)(floor(T));
+        if (T_int < 1) {
+            T_int = 1;
+        }
+        // compute cost
+        cost += T_int;
+        if (K_int > 2) {
+            cost += (unsigned long)(K_int) * (unsigned long)(T_int);
+        }
+        // Perform T edge swap attempts
+        for (int i = T_int; i > 0; i--) {
+            // try one swap
+            swaps += (unsigned long)(random_edge_swap(K_int, Kbuff, visited));
+            all_swaps++;
+            // Verbose
+            if (nb_swaps + swaps > next) {
+                next = (nb_swaps + swaps) + max((unsigned long)(100), (unsigned long)(times / 1000));
+                int progress = int(double(nb_swaps + swaps) / double(times));
+                igraph_progress("Shuffle",  progress, 0);
+            }
+        }
+        // test connectivity
+        cost += (unsigned long)(a / 2);
+        bool ok = is_connected();
+        // performance monitor
+        {
+            avg_T += double(T_int); avg_K += double(K_int);
+            if (ok) {
+                successes++;
+            } else {
+                failures++;
+            }
+        }
+        // restore graph if needed, and count validated swaps
+        if (ok) {
+            nb_swaps += swaps;
+        } else {
+            restore(save);
+            next = nb_swaps;
+        }
+        delete[] save;
+        // Adjust K and T following the heuristics.
+        switch (type) {
+            int steps;
+        case GKAN_HEURISTICS:
+            if (ok) {
+                T += 1.0;
+            } else {
+                T *= 0.5;
+            }
+            break;
+        case FAB_HEURISTICS:
+            steps = 50 / (8 + failures + successes);
+            if (steps < 1) {
+                steps = 1;
+            }
+            while (steps--) if (ok) {
+                    T *= 1.17182818;
+                } else {
+                    T *= 0.9;
+                }
+            if (T > double(5 * a)) {
+                T = double(5 * a);
+            }
+            break;
+        case FINAL_HEURISTICS:
+            if (ok) {
+                if ((K + 10.0)*T > 5.0 * double(a)) {
+                    K /= 1.03;
+                } else {
+                    T *= 2;
+                }
+            } else {
+                K *= 1.35;
+                delete[] Kbuff;
+                Kbuff = new int[int(K) + 1];
+            }
+            break;
+        case OPTIMAL_HEURISTICS:
+            if (ok) {
+                T = double(optimal_window());
+            }
+            break;
+        case BRUTE_FORCE_HEURISTICS:
+            K *= 2; delete[] Kbuff; Kbuff = new int[int(K) + 1];
+            break;
+        default:
+            IGRAPH_ERROR("Error in graph_molloy_hash::shuffle(): "
+                         "Unknown heuristics type", IGRAPH_EINVAL);
+            return 0;
+        }
+    }
+
+    delete[] Kbuff;
+    delete[] visited;
+
+    if (maxtimes <= all_swaps) {
+        IGRAPH_WARNING("Cannot shuffle graph, maybe there is only a single one?");
+    }
+
+    // Status report
+    {
+        igraph_status("*** Shuffle Monitor ***\n", 0);
+        igraph_statusf(" - Average cost : %f / validated edge swap\n", 0,
+                       double(cost) / double(nb_swaps));
+        igraph_statusf(" - Connectivity tests : %d (%d successes, %d failures)\n",
+                       0, successes + failures, successes, failures);
+        igraph_statusf(" - Average window : %d\n", 0,
+                       int(avg_T / double(successes + failures)));
+        if (type == FINAL_HEURISTICS || type == BRUTE_FORCE_HEURISTICS)
+            igraph_statusf(" - Average isolation test width : %f\n", 0,
+                           avg_K / double(successes + failures));
+    }
+    return nb_swaps;
+}
+
+//_________________________________________________________________________
+void graph_molloy_hash::print(FILE *f) {
+    int i, j;
+    for (i = 0; i < n; i++) {
+        fprintf(f, "%d", i);
+        for (j = 0; j < HASH_SIZE(deg[i]); j++) if (neigh[i][j] != HASH_NONE) {
+                fprintf(f, " %d", neigh[i][j]);
+            }
+        fprintf(f, "\n");
+    }
+}
+
+int graph_molloy_hash::print(igraph_t *graph) {
+    int i, j;
+    long int ptr = 0;
+    igraph_vector_t edges;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, a); // every edge is counted twice....
+
+    for (i = 0; i < n; i++) {
+        for (j = 0; j < HASH_SIZE(deg[i]); j++) {
+            if (neigh[i][j] != HASH_NONE) {
+                if (neigh[i][j] > i) {
+                    VECTOR(edges)[ptr++] = i;
+                    VECTOR(edges)[ptr++] = neigh[i][j];
+                }
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, /*undirected=*/ 0));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+//_________________________________________________________________________
+bool graph_molloy_hash::try_shuffle(int T, int K, int *backup_graph) {
+    // init all
+    int *Kbuff = NULL;
+    bool *visited = NULL;
+    if (K > 2) {
+        Kbuff = new int[K];
+        visited = new bool[n];
+        for (int i = 0; i < n; i++) {
+            visited[i] = false;
+        }
+    }
+    int *back = backup_graph;
+    if (back == NULL) {
+        back = backup();
+    }
+    // perform T edge swap attempts
+    while (T--) {
+        random_edge_swap(K, Kbuff, visited);
+    }
+    // clean
+    if (visited != NULL) {
+        delete[] visited;
+    }
+    if (Kbuff   != NULL) {
+        delete[] Kbuff;
+    }
+    // check & restore
+    bool yo = is_connected();
+    restore(back);
+    if (backup_graph == NULL) {
+        delete[] back;
+    }
+    return yo;
+}
+
+//_________________________________________________________________________
+#define _TRUST_BERNOULLI_LOWER 0.01
+
+bool bernoulli_param_is_lower(int success, int trials, double param) {
+    if (double(success) >= double(trials)*param) {
+        return false;
+    }
+    double comb = 1.0;
+    double fact = 1.0;
+    for (int i = 0; i < success; i++) {
+        comb *= double(trials - i);
+        fact *= double(i + 1);
+    }
+    comb /= fact;
+    comb *= pow(param, double(success)) * exp(double(trials - success) * log1p(-param));
+    double sum = comb;
+    while (success && sum < _TRUST_BERNOULLI_LOWER) {
+        comb *= double(success) * (1.0 - param) / (double(trials - success) * param);
+        sum += comb;
+        success--;
+    }
+    // fprintf(stderr,"bernoulli test : %d/%d success against p=%f -> %s\n",success, trials, param, (sum < _TRUST_BERNOULLI_LOWER) ? "lower" : "can't say");
+    return (sum < _TRUST_BERNOULLI_LOWER);
+}
+
+//_________________________________________________________________________
+#define _MIN_SUCCESS_FOR_BERNOULLI_TRUST 100
+double graph_molloy_hash::average_cost(int T, int *backup, double min_cost) {
+    if (T < 1) {
+        return 1e+99;
+    }
+    int successes = 0;
+    int trials = 0;
+    while (successes < _MIN_SUCCESS_FOR_BERNOULLI_TRUST &&
+           !bernoulli_param_is_lower(successes, trials, 1.0 / min_cost)) {
+        if (try_shuffle(T, 0, backup)) {
+            successes++;
+        }
+        trials++;
+    }
+    if (successes >= _MIN_SUCCESS_FOR_BERNOULLI_TRUST) {
+        return double(trials) / double(successes) * (1.0 + double(a / 2) / double(T));
+    } else {
+        return 2.0 * min_cost;
+    }
+}
+
+//_________________________________________________________________________
+int graph_molloy_hash::optimal_window() {
+    int Tmax;
+    int optimal_T = 1;
+    double min_cost = 1e+99;
+    int *back = backup();
+    // on cherche une borne sup pour Tmax
+    int been_greater = 0;
+    for (Tmax = 1; Tmax <= 5 * a ; Tmax *= 2) {
+        double c = average_cost(Tmax, back, min_cost);
+        if (c > 1.5 * min_cost) {
+            break;
+        }
+        if (c > 1.2 * min_cost && ++been_greater >= 3) {
+            break;
+        }
+        if (c < min_cost) {
+            min_cost = c;
+            optimal_T = Tmax;
+        }
+        igraph_statusf("Tmax = %d [%f]", 0, Tmax, min_cost);
+    }
+    // on cree Tmin
+    int Tmin = int(0.5 * double(a) / (min_cost - 1.0));
+    igraph_statusf("Optimal T is in [%d, %d]\n", 0, Tmin, Tmax);
+    // on cherche autour
+    double span = 2.0;
+    int try_again = 4;
+    while (span > 1.05 && optimal_T <= 5 * a) {
+        igraph_statusf("Best T [cost]: %d [%f]", 0, optimal_T, min_cost);
+        int T_low  = int(double(optimal_T) / span);
+        int T_high = int(double(optimal_T) * span);
+        double c_low  = average_cost(T_low, back, min_cost);
+        double c_high = average_cost(T_high, back, min_cost);
+        if (c_low < min_cost && c_high < min_cost) {
+            if (try_again--) {
+                continue;
+            }
+            {
+                igraph_status("Warning: when looking for optimal T,\n", 0);
+                igraph_statusf("Low: %d [%f]  Middle: %d [%f]  High: %d [%f]\n", 0,
+                               T_low, c_low, optimal_T, min_cost, T_high, c_high);
+            }
+            delete[] back;
+            return optimal_T;
+        }
+        if (c_low < min_cost) {
+            optimal_T = T_low;
+            min_cost = c_low;
+        } else if (c_high < min_cost) {
+            optimal_T = T_high;
+            min_cost = c_high;
+        };
+        span = pow(span, 0.618);
+    }
+    delete[] back;
+    return optimal_T;
+}
+
+//_________________________________________________________________________
+double graph_molloy_hash::eval_K(int quality) {
+    double K = 5.0;
+    double avg_K = 1.0;
+    for (int i = quality; i--; ) {
+        int int_K = int(floor(K + 0.5));
+        if (try_shuffle(a / (int_K + 1), int_K)) {
+            K *= 0.8; /*fprintf(stderr,"+");*/
+        } else {
+            K *= 1.25; /*fprintf(stderr,"-");*/
+        }
+        if (i < quality / 2) {
+            avg_K *= K;
+        }
+    }
+    return pow(avg_K, 1.0 / double(quality / 2));
+}
+
+//_________________________________________________________________________
+double graph_molloy_hash::effective_K(int K, int quality) {
+    if (K < 3) {
+        return 0.0;
+    }
+    long sum_K = 0;
+    int *Kbuff = new int[K];
+    bool *visited = new bool[n];
+    int i;
+    for (i = 0; i < n; i++) {
+        visited[i] = false;
+    }
+    for (int i = 0; i < quality; i++) {
+        // assert(verify());
+        int f1, f2, t1, t2;
+        int *f1t1, *f2t2;
+        do {
+            // Pick two random vertices
+            do {
+                f1 = pick_random_vertex();
+                f2 = pick_random_vertex();
+            } while (f1 == f2);
+            // Pick two random neighbours
+            f1t1 = random_neighbour(f1);
+            t1 = *f1t1;
+            f2t2 = random_neighbour(f2);
+            t2 = *f2t2;
+            // test simplicity
+        } while (t1 == t2 || f1 == t2 || f2 == t1 || is_edge(f1, t2) || is_edge(f2, t1));
+        // swap
+        swap_edges(f1, t2, f2, t1);
+        // assert(verify());
+        sum_K += effective_isolated(deg[f1] > deg[t2] ? f1 : t2, K, Kbuff, visited);
+        // assert(verify());
+        sum_K += effective_isolated(deg[f2] > deg[t1] ? f2 : t1, K, Kbuff, visited);
+        // assert(verify());
+        // undo swap
+        swap_edges(f1, t2, f2, t1);
+        // assert(verify());
+    }
+    delete[] Kbuff;
+    delete[] visited;
+    return double(sum_K) / double(2 * quality);
+}
+
+//_________________________________________________________________________
+long graph_molloy_hash::effective_isolated(int v, int K, int *Kbuff, bool *visited) {
+    int i;
+    for (i = 0; i < K; i++) {
+        Kbuff[i] = -1;
+    }
+    long count = 0;
+    int left = K;
+    int *KB = Kbuff;
+    //yapido = (my_random()%1000 == 0);
+    depth_isolated(v, count, left, K, KB, visited);
+    while (KB-- != Kbuff) {
+        visited[*KB] = false;
+    }
+    //if(yapido) fprintf(stderr,"\n");
+    return count;
+}
+
+//_________________________________________________________________________
+void graph_molloy_hash::depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited) {
+    if (left_to_explore == 0) {
+        return;
+    }
+//  if(yapido) fprintf(stderr,"%d ",deg[v]);
+    if (--left_to_explore == 0) {
+        return;
+    }
+    if (deg[v] + 1 >= dmax) {
+        left_to_explore = 0;
+        return;
+    }
+    *(Kbuff++) = v;
+    visited[v] = true;
+//  print();
+//  fflush(stdout);
+    calls++;
+    int *copy = NULL;
+    int *w = neigh[v];
+    if (IS_HASH(deg[v])) {
+        copy = new int[deg[v]];
+        H_copy(copy, w, deg[v]);
+        w = copy;
+    }
+    qsort(deg, w, deg[v]);
+    w += deg[v];
+    for (int i = deg[v]; i--; ) {
+        if (visited[*--w]) {
+            calls++;
+        } else {
+            depth_isolated(*w, calls, left_to_explore, dmax, Kbuff, visited);
+        }
+        if (left_to_explore == 0) {
+            break;
+        }
+    }
+    if (copy != NULL) {
+        delete[] copy;
+    }
+}
+
+//_________________________________________________________________________
+int graph_molloy_hash::depth_search(bool *visited, int *buff, int v0) {
+    for (int i = 0; i < n; i++) {
+        visited[i] = false;
+    }
+    int *to_visit = buff;
+    int nb_visited = 1;
+    visited[v0] = true;
+    *(to_visit++) = v0;
+    while (to_visit != buff && nb_visited < n) {
+        int v = *(--to_visit);
+        int *ww = neigh[v];
+        int w;
+        for (int k = HASH_SIZE(deg[v]); k--; ww++) {
+            if (HASH_NONE != (w = *ww) && !visited[w]) {
+                visited[w] = true;
+                nb_visited++;
+                *(to_visit++) = w;
+            }
+        }
+    }
+    return nb_visited;
+}
+
+//_________________________________________________________________________
+// bool graph_molloy_hash::verify() {
+//   fprintf(stderr,"Warning: graph_molloy_hash::verify() called..\n");
+//   fprintf(stderr,"   try to convert graph into graph_molloy_opt() instead\n");
+//   return true;
+// }
+
+
+/*____________________________________________________________________________
+  Not to use anymore : use graph_molloy_opt class instead
+
+bool graph_molloy_hash::verify() {
+int i;
+  assert(neigh[0]==links);
+  // verify edges count
+  int sum = 0;
+  for(i=0; i<n; i++) sum+=deg[i];
+  assert(sum==a);
+  // verify neigh[] and deg[] compatibility
+  for(i=0; i<n-1; i++) assert(neigh[i]+HASH_SIZE(deg[i])==neigh[i+1]);
+  // verify hash tables : do we see everyone ?
+  for(i=0; i<n; i++) for(int j=HASH_SIZE(deg[i]); j--; )
+    if(neigh[i][j]!=HASH_NONE) assert(H_is(neigh[i],deg[i],neigh[i][j]));
+  degree_sequence dd(n,deg);
+  graph_molloy_opt g(dd);
+  dd.detach();
+  int *bb = backup();
+  g.restore(bb);
+  delete[] bb;
+  return g.verify();
+}
+
+graph_molloy_hash::graph_molloy_hash(FILE *f) {
+  char *buff = new char[FBUFF_SIZE];
+  // How many vertices ?
+  if(VERBOSE()) fprintf(stderr,"Read file: #vertices=");
+  int i;
+  int n=0;
+  while(fgets(buff,FBUFF_SIZE,f)) if(sscanf(buff,"%d",&i)==1 && i>n) n=i;
+  n++;
+  // degrees ?
+  if(VERBOSE()) fprintf(stderr,"%d, #edges=",n);
+  int *degs = new int[n];
+  rewind(f);
+  while(fgets(buff,FBUFF_SIZE,f)) {
+    int d = 0;
+    if(sscanf(buff,"%d",&i)==1) {
+      char *b = buff;
+      while(skip_int(b)) d++;
+      degs[i]=d;
+    }
+  }
+  // allocate memory
+  degree_sequence dd(n,degs);
+  if(VERBOSE()) fprintf(stderr,"%d\nAllocating memory...",dd.sum());
+  alloc(dd);
+  // add edges
+  if(VERBOSE()) fprintf(stderr,"done\nCreating edges...");
+  rewind(f);
+  for(i=0; i<n; i++) deg[i]=0;
+  int line=0;
+  int j;
+  while(fgets(buff,FBUFF_SIZE,f)) {
+    line++;
+    if(sscanf(buff,"%d",&i)==1) {
+      char *b = buff;
+      while(skip_int(b)) {
+        if(sscanf(b,"%d",&j)!=1) {
+          fprintf(stderr,"\nParse error at line %d, col %d : integer expected\n",line,int(b-buff));
+          exit(6);
+        }
+        if(i<j) add_edge(i,j,dd.seq());
+      }
+    }
+  }
+  if(VERBOSE()) fprintf(stderr,"done\n");
+  delete[] buff;
+}
+
+
+int graph_molloy_hash::max_degree() {
+  int m=0;
+  for(int k=0; k<n; k++) if(deg[k]>m) m=deg[k];
+  return m;
+}
+
+
+bool graph_molloy_hash::havelhakimi() {
+
+  int i;
+  int dmax = max_degree()+1;
+  // Sort vertices using basket-sort, in descending degrees
+  int *nb = new int[dmax];
+  int *sorted = new int[n];
+  // init basket
+  for(i=0; i<dmax; i++) nb[i]=0;
+  // count basket
+  for(i=0; i<n; i++) nb[deg[i]]++;
+  // cumul
+  int c = 0;
+  for(i=dmax-1; i>=0; i--) {
+    int t=nb[i];
+    nb[i]=c;
+    c+=t;
+  }
+  // sort
+  for(i=0; i<n; i++) sorted[nb[deg[i]]++]=i;
+  // Init edge count
+  for(i=0; i<n; i++) deg[i] = 0;
+
+// Binding process starts
+  int first = 0;  // vertex with biggest residual degree
+  int d = dmax-1; // maximum residual degree available
+
+  for(c=a/2; c>0; ) {
+    // pick a vertex. we could pick any, but here we pick the one with biggest degree
+    int v = sorted[first];
+    // look for current degree of v
+    while(nb[d]<=first) d--;
+    // store it in dv
+    int dv = d;
+    // bind it !
+    c -= dv;
+    int dc = d;         // residual degree of vertices we bind to
+    int fc = ++first;   // position of the first vertex with degree dc
+
+    while(dv>0 && dc>0) {
+      int lc = nb[dc];
+      if(lc!=fc) {
+        while(dv>0 && lc>fc) {
+          // binds v with sorted[--lc]
+          dv--;
+          int w = sorted[--lc];
+          add_edge(v,w);
+        }
+        fc = nb[dc];
+        nb[dc] = lc;
+      }
+      dc--;
+    }
+    if(dv != 0) { // We couldn't bind entirely v
+      if(VERBOSE()) {
+        fprintf(stderr,"Error in graph_molloy_hash::havelhakimi() :\n");
+        fprintf(stderr,"Couldn't bind vertex %d entirely (%d edges remaining)\n",v,dv);
+      }
+      delete[] nb;
+      delete[] sorted;
+      return false;
+    }
+  }
+  assert(c==0);
+  delete[] nb;
+  delete[] sorted;
+  return true;
+}
+
+
+bool graph_molloy_hash::make_connected() {
+  assert(verify());
+  if(a/2 < n-1) {
+    // fprintf(stderr,"\ngraph::make_connected() failed : #edges < #vertices-1\n");
+    return false;
+  }
+  int i;
+
+// Data struct for the visit :
+// - buff[] contains vertices to visit
+// - dist[V] is V's distance modulo 4 to the root of its comp, or -1 if it hasn't been visited yet
+#define MC_BUFF_SIZE (n+2)
+  int *buff = new int[MC_BUFF_SIZE];
+  unsigned char * dist  = new unsigned char[n];
+#define NOT_VISITED 255
+#define FORBIDDEN   254
+  for(i=n; i>0; dist[--i]=NOT_VISITED);
+
+// Data struct to store components : either surplus trees or surplus edges are stored at buff[]'s end
+// - A Tree is coded by one of its vertices
+// - An edge (a,b) is coded by the TWO ints a and b
+  int *ffub = buff+MC_BUFF_SIZE;
+  edge *edges = (edge *) ffub;
+  int *trees = ffub;
+  int *min_ffub = buff+1+(MC_BUFF_SIZE%2 ? 0 : 1);
+
+// There will be only one "fatty" component, and trees.
+  edge fatty_edge;
+  fatty_edge.from = -1;
+  bool enough_edges = false;
+
+  // start main loop
+  for(int v0=0; v0<n; v0++) if(dist[v0]==NOT_VISITED) {
+    // is v0 an isolated vertex?
+    if(deg[v0]==0) {
+#ifdef VERBOSE
+      fprintf(stderr,"graph_molloy_opt::make_connected() returned FALSE : vertex %d has degree 0\n",v0);
+#endif //VERBOSE
+      delete[] dist;
+      delete[] buff;
+      return false;
+    }
+    dist[v0] = 0; // root
+    int *to_visit = buff;
+    int *current  = buff;
+    *(to_visit++) = v0;
+
+    // explore component connected to v0
+    bool is_a_tree = true;
+    while(current != to_visit) {
+      int v = *(current++);
+      unsigned char current_dist = dist[v];
+      unsigned char next_dist = (current_dist+1) & 0x03;
+      //unsigned char prev_dist = (current_dist-1) & 0x03;
+      int* ww = neigh[v];
+      int w;
+      for(int k=HASH_SIZE(deg[v]); k--; ww++) if((w=*ww)!=HASH_NONE) {
+        if(dist[w]==NOT_VISITED) {
+          // we didn't visit w yet
+          dist[w] = next_dist;
+          *(to_visit++) = w;
+          if(to_visit>min_ffub) min_ffub+=2; // update limit of ffub's storage
+          //assert(verify());
+        }
+        else if(dist[w]==next_dist || (w!=HASH_NONE && w>v && dist[w]==current_dist)) {
+          // we found a removable edge
+          if(is_a_tree) {
+            // we must first merge with the fatty component
+            is_a_tree = false;
+            if(fatty_edge.from < 0) {
+              // we ARE the first component! fatty is us
+              fatty_edge.from = v;
+              fatty_edge.to   = w;
+            }
+            else {
+              // we connect to fatty
+              swap_edges(fatty_edge.from, fatty_edge.to, v, w);
+              //assert(verify());
+            }
+          }
+          else {
+            // we have removable edges to give!
+            if(trees!=ffub) {
+              // some trees still.. Let's merge with them!
+              assert(trees>=min_ffub);
+              assert(edges==(edge *)ffub);
+              swap_edges(v,w,*trees,neigh[*trees][0]);
+              trees++;
+              //assert(verify());
+            }
+            else if(!enough_edges) {
+              // Store the removable edge for future use
+              if(edges<=(edge *)min_ffub+1)
+                enough_edges = true;
+              else {
+                edges--;
+                edges->from = v;
+                edges->to   = w;
+              }
+            }
+          }
+        }
+      }
+    }
+    // Mark component
+    while(to_visit!=buff) dist[*(--to_visit)] = FORBIDDEN;
+    // Check if it is a tree
+    if(is_a_tree ) {
+      assert(deg[v0]!=0);
+      if(edges!=(edge *)ffub) {
+        // let's bind the tree we found with a removable edge in stock
+        assert(trees == ffub);
+        if(edges<(edge *)min_ffub) edges=(edge *)min_ffub;
+        swap_edges(v0,neigh[v0][0],edges->from,edges->to);
+        edges++;
+        assert(verify());
+    }
+      else {
+        // add the tree to the list of trees
+        assert(trees>min_ffub);
+        *(--trees) = v0;
+        assert(verify());
+      }
+    }
+  }
+  delete[] buff;
+  delete[] dist;
+  return(trees == ffub);
+}
+
+int64_t graph_molloy_hash::slow_connected_shuffle(int64_t times) {
+  assert(verify());
+  int64_t nb_swaps = 0;
+  int T = 1;
+
+  while(times>nb_swaps) {
+    // Backup graph
+    int *save = backup();
+    // Swaps
+    int swaps = 0;
+    for(int i=T; i>0; i--) {
+      // Pick two random vertices a and c
+      int f1 = pick_random_vertex();
+      int f2 = pick_random_vertex();
+      // Check that f1 != f2
+      if(f1==f2) continue;
+      // Get two random edges (f1,*f1t1) and (f2,*f2t2)
+      int *f1t1 = random_neighbour(f1);
+      int t1 = *f1t1;
+      int *f2t2 = random_neighbour(f2);
+      int t2 = *f2t2;
+      // Check simplicity
+      if(t1==t2 || f1==t2 || f2==t1) continue;
+      if(is_edge(f1,t2) || is_edge(f2,t1)) continue;
+      // Swap
+      H_rpl(neigh[f1],deg[f1],f1t1,t2);
+      H_rpl(neigh[f2],deg[f2],f2t2,t1);
+      H_rpl(neigh[t1],deg[t1],f1,f2);
+      H_rpl(neigh[t2],deg[t2],f2,f1);
+      swaps++;
+    }
+    // test connectivity
+    bool ok = is_connected();
+    if(ok) {
+      nb_swaps += swaps;
+    }
+    else {
+      restore(save);
+    }
+    delete[] save;
+  }
+  return nb_swaps;
+}
+
+
+int graph_molloy_hash::width_search(unsigned char *dist, int *buff, int v0) {
+  for(int i=0; i<n; i++) dist[i] = 0;
+  int *to_visit = buff;
+  int *to_add = buff;
+  int nb_visited = 1;
+  dist[v0]=1;
+  *(to_add++)=v0;
+  while(to_visit != to_add && nb_visited<n) {
+    int v = *(to_visit++);
+    int *ww = neigh[v];
+    int w;
+    unsigned char d = next_dist(dist[v]);
+    for(int k=HASH_SIZE(deg[v]); k--; ww++) {
+      if(HASH_NONE!=(w=*ww) && dist[w]==0) {
+        dist[w]=d;
+        nb_visited++;
+        *(to_add++)=w;
+      }
+    }
+  }
+  return nb_visited;
+}
+
+
+
+int *graph_molloy_hash::vertex_betweenness_rsp(bool trivial_paths) {
+  int i;
+  unsigned char *dist = new unsigned char[n];
+  int *buff = new int[n];
+  int *b = new int[n];
+  int *bb = new int[n];
+  for(i=0; i<n; i++) b[i]=0;
+  for(int v0 = 0; v0<n; v0++) {
+    for(i=0; i<n; i++) bb[i]=0;
+    int nb_vertices = width_search(dist, buff, v0);
+    while(--nb_vertices) {
+      int v=buff[nb_vertices];
+      int d = prev_dist(dist[v]);
+      int *adj = neigh[v];
+      int adj_size = deg[v];
+      int *ww;
+      do ww=H_random(adj,adj_size); while(dist[*ww]!=d);
+      if(trivial_paths || *ww!=v0) bb[*ww] += bb[v]+1;
+      if(trivial_paths) bb[v]++;
+    }
+    for(i=0; i<n; i++) b[i]+=bb[i];
+  }
+  delete[] dist;
+  delete[] buff;
+  delete[] bb;
+  return b;
+}
+
+double *graph_molloy_hash::vertex_betweenness_asp(bool trivial_paths) {
+  int i;
+  unsigned char *dist = new unsigned char[n];
+  int *buff = new int[n];
+  double *b = new double[n];
+  double *bb = new double[n];
+  for(i=0; i<n; i++) b[i]=0.0;
+  for(int v0 = 0; v0<n; v0++) {
+    for(i=0; i<n; i++) bb[i]=0.0;
+    int nb_vertices = width_search(dist, buff, v0);
+    if(!trivial_paths) dist[v0]=2;
+    while(--nb_vertices) {
+      int v=buff[nb_vertices];
+      int d = prev_dist(dist[v]);
+      int nb_father = 0;
+      int *ww = neigh[v];
+      int k;
+      for(k=HASH_SIZE(deg[v]); k--; ww++) if(*ww != HASH_NONE && dist[*ww]==d) nb_father++;
+      if(nb_father!=0) {
+        double badd = (bb[v]+1.0)/double(nb_father);
+        ww = neigh[v];
+        for(k=HASH_SIZE(deg[v]); k--; ww++) if(*ww != HASH_NONE && dist[*ww]==d) bb[*ww]+=badd;
+      }
+      if(trivial_paths) bb[v]+=1.0;
+    }
+    for(i=0; i<n; i++) b[i]+=bb[i];
+  }
+  delete[] dist;
+  delete[] buff;
+  delete[] bb;
+  return b;
+}
+
+//___________________________________________________________________________________
+//*/
+
+} // namespace gengraph
diff --git a/igraph/src/gengraph_graph_molloy_optimized.cpp b/igraph/src/gengraph_graph_molloy_optimized.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/gengraph_graph_molloy_optimized.cpp
@@ -0,0 +1,2221 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "gengraph_definitions.h"
+#include <cassert>
+#include <cstdio>
+#include <cmath>
+#include <limits>
+
+#include "gengraph_qsort.h"
+#include "gengraph_box_list.h"
+#include "gengraph_vertex_cover.h"
+#include "gengraph_degree_sequence.h"
+#include "gengraph_graph_molloy_optimized.h"
+
+#include "igraph_error.h"
+#include "igraph_statusbar.h"
+#include "igraph_progress.h"
+
+#ifndef register
+    #define register
+#endif
+
+using namespace std;
+
+namespace gengraph {
+
+void graph_molloy_opt::breadth_search(int *dist, int v0, int *buff) {
+    bool tmpbuff = (buff == NULL);
+    if (tmpbuff) {
+        buff = new int[n];
+    }
+    for (int i = 0; i < n; i++) {
+        dist[i] = -1;
+    }
+    dist[v0] = 0;
+    int *visited = buff;
+    int *to_visit = buff;
+    *to_visit++ = v0;
+    while (visited != to_visit) {
+        int v = *visited++;
+        int *w = neigh[v];
+        int dd = dist[v] + 1;
+        for (int d = deg[v]; d--; w++) if (dist[*w] < 0) {
+                dist[*w] = dd;
+                *to_visit++ = *w;
+            }
+    }
+    if (tmpbuff) {
+        delete[] buff;
+    }
+}
+
+
+int graph_molloy_opt::max_degree() {
+    int m = 0;
+    for (int k = 0; k < n; k++) if (deg[k] > m) {
+            m = deg[k];
+        }
+    return m;
+}
+
+void graph_molloy_opt::compute_neigh() {
+    int *p = links;
+    for (int i = 0; i < n; i++) {
+        neigh[i] = p;
+        p += deg[i];
+    }
+}
+
+void graph_molloy_opt::alloc(degree_sequence &degs) {
+    n = degs.size();
+    a = degs.sum();
+    assert(a % 2 == 0);
+    deg = new int[n + a];
+    for (int i = 0; i < n; i++) {
+        deg[i] = degs[i];
+    }
+    links = deg + n;
+    neigh = new int*[n];
+    compute_neigh();
+}
+
+graph_molloy_opt::graph_molloy_opt(degree_sequence &degs) {
+    alloc(degs);
+}
+
+// graph_molloy_opt::graph_molloy_opt(FILE *f) {
+//   char *buff = new char[FBUFF_SIZE];
+//   // How many vertices ?
+//   if(VERBOSE()) fprintf(stderr,"Read file: #vertices=");
+//   int i;
+//   int n=0;
+//   while(fgets(buff,FBUFF_SIZE,f)) if(sscanf(buff,"%d",&i)==1 && i>n) n=i;
+//   n++;
+//   // degrees ?
+//   if(VERBOSE()) fprintf(stderr,"%d, #edges=",n);
+//   int *degs = new int[n];
+//   for(i=0; i<n; i++) degs[i]=0;
+//   rewind(f);
+//   while(fgets(buff,FBUFF_SIZE,f)) {
+//     int d = 0;
+//     if(sscanf(buff,"%d",&i)==1) {
+//       char *b = buff;
+//       while(skip_int(b)) d++;
+//       degs[i]=d;
+//     }
+//   }
+//   // allocate memory
+//   degree_sequence dd(n,degs);
+//   a = dd.sum();
+//   if(VERBOSE()) fprintf(stderr,"%d\nAllocating memory...",a);
+//   alloc(dd);
+//   // add edges
+//   if(VERBOSE()) fprintf(stderr,"done\nCreating edges...");
+//   rewind(f);
+//   int line=0;
+//   int j;
+//   while(fgets(buff,FBUFF_SIZE,f)) {
+//     line++;
+//     if(sscanf(buff,"%d",&i)==1) {
+//       char *b = buff;
+//       while(skip_int(b)) {
+//         if(sscanf(b,"%d",&j)!=1) {
+//           fprintf(stderr,"\nParse error at line %d, col %d : integer expected\n",line,int(b-buff));
+//           exit(6);
+//         }
+//         *(neigh[i]++) = j;
+//       }
+//     }
+//   }
+//   delete[] buff;
+//   compute_neigh();
+//   if(VERBOSE()) fprintf(stderr,"done\n");
+// }
+
+graph_molloy_opt::graph_molloy_opt(int *svg) {
+    // Read n
+    n = *(svg++);
+    // Read a
+    a = *(svg++);
+    assert(a % 2 == 0);
+    // Read degree sequence
+    degree_sequence dd(n, svg);
+    // Build neigh[] and alloc links[]
+    alloc(dd);
+    dd.detach();
+    // Read links[]
+    restore(svg + n);
+}
+
+void graph_molloy_opt::detach() {
+    deg = NULL;
+    neigh = NULL;
+}
+
+graph_molloy_opt::~graph_molloy_opt() {
+    if (deg != NULL) {
+        delete[] deg;
+    }
+    if (neigh != NULL) {
+        delete[] neigh;
+    }
+    detach();
+}
+
+int* graph_molloy_opt::backup(int *b) {
+    if (b == NULL) {
+        b = new int[a / 2];
+    }
+    int *c = b;
+    for (int i = 0; i < n; i++) {
+        int *p = neigh[i];
+        for (int d = deg[i]; d--; p++) {
+            assert(*p != i);
+            if (*p >= i) {
+                *(c++) = *p;
+            }
+        }
+    }
+    assert(c == b + (a / 2));
+    return b;
+}
+
+int *graph_molloy_opt::hard_copy() {
+    int *hc = new int[2 + n + a / 2]; // to store n,a,deg[] and links[]
+    hc[0] = n;
+    hc[1] = a;
+    memcpy(hc + 2, deg, sizeof(int)*n);
+    int *c = hc + 2 + n;
+    for (int i = 0; i < n; i++) {
+        int *p = neigh[i];
+        for (int d = deg[i]; d--; p++) {
+            assert(*p != i);
+            if (*p >= i) {
+                *(c++) = *p;
+            }
+        }
+    }
+    assert(c == hc + 2 + n + a / 2);
+    return hc;
+}
+
+void graph_molloy_opt::restore(int* b) {
+    int i;
+    for (i = 0; i < n; i++) {
+        deg[i] = 0;
+    }
+    int *p = links;
+    for (i = 0; i < n - 1; i++) {
+        p += deg[i];
+        deg[i] = int(neigh[i + 1] - neigh[i]);
+        assert((neigh[i] + deg[i]) == neigh[i + 1]);
+        while (p != neigh[i + 1]) {
+            // b points to the current 'j'
+            neigh[*b][deg[*b]++] = i;
+            *(p++) = *(b++);
+        }
+    }
+}
+
+int* graph_molloy_opt::backup_degs(int *b) {
+    if (b == NULL) {
+        b = new int[n];
+    }
+    memcpy(b, deg, sizeof(int)*n);
+    return b;
+}
+
+void graph_molloy_opt::restore_degs_only(int *b) {
+    memcpy(deg, b, sizeof(int)*n);
+    refresh_nbarcs();
+}
+
+void graph_molloy_opt::restore_degs_and_neigh(int *b) {
+    restore_degs_only(b);
+    compute_neigh();
+}
+
+void graph_molloy_opt::restore_degs(int last_degree) {
+    a = last_degree;
+    deg[n - 1] = last_degree;
+    for (int i = n - 2; i >= 0; i--) {
+        a += (deg[i] = int(neigh[i + 1] - neigh[i]));
+    }
+    refresh_nbarcs();
+}
+
+void graph_molloy_opt::clean() {
+    int *b = hard_copy();
+    replace(b);
+    delete[] b;
+}
+
+void graph_molloy_opt::replace(int *_hardcopy) {
+    delete[] deg;
+    n = *(_hardcopy++);
+    a = *(_hardcopy++);
+    deg = new int[a + n];
+    memcpy(deg, _hardcopy, sizeof(int)*n);
+    links = deg + n;
+    compute_neigh();
+    restore(_hardcopy + n);
+}
+
+int* graph_molloy_opt::components(int *comp) {
+    int i;
+    // breadth-first search buffer
+    int *buff = new int[n];
+    // comp[i] will contain the index of the component that contains vertex i
+    if (comp == NULL) {
+        comp = new int[n];
+    }
+    memset(comp, 0, sizeof(int)*n);
+    // current component index
+    int curr_comp = 0;
+    // loop over all non-visited vertices...
+    for (int v0 = 0; v0 < n; v0++) if (comp[v0] == 0) {
+            curr_comp++;
+            // initiate breadth-first search
+            int *to_visit = buff;
+            int *visited = buff;
+            *(to_visit++) = v0;
+            comp[v0] = curr_comp;
+            // breadth-first search
+            while (visited != to_visit) {
+                int v = *(visited++);
+                int d = deg[v];
+                for (int *w = neigh[v]; d--; w++) if (comp[*w] == 0) {
+                        comp[*w] = curr_comp;
+                        *(to_visit++) = *w;
+                    }
+            }
+        }
+    // compute component sizes and store them in buff[]
+    int nb_comp = 0;
+    memset(buff, 0, sizeof(int)*n);
+    for (i = 0; i < n; i++)
+        if (buff[comp[i] - 1]++ == 0 && comp[i] > nb_comp) {
+            nb_comp = comp[i];
+        }
+    // box-sort sizes
+    int offset = 0;
+    int *box = pre_boxsort(buff, nb_comp, offset);
+    for (i = nb_comp - 1; i >= 0; i--) {
+        buff[i] = --box[buff[i] - offset];
+    }
+    delete[] box;
+    // reassign component indexes
+    for (int *c = comp + n; comp != c--; *c = buff[*c - 1]) { }
+    // clean.. at last!
+    delete[] buff;
+    return comp;
+}
+
+void graph_molloy_opt::giant_comp() {
+    int *comp = components();
+    // Clear edges of all vertices that do not belong to comp 0
+    for (int i = 0; i < n; i++) if (comp[i] != 0) {
+            deg[i] = 0;
+        }
+    // Clean comp[]
+    delete[] comp;
+}
+
+int graph_molloy_opt::nbvertices_comp() {
+    int *comp = components();
+    // Count all vertices that belong to comp 0
+    int nb = 0;
+    for (int i = 0; i < n; i++) if (comp[i] == 0) {
+            nb++;
+        }
+    // Clean comp[]
+    delete[] comp;
+    return nb;
+}
+
+int graph_molloy_opt::nbarcs_comp() {
+    int *comp = components();
+    // Count all vertices that belong to comp 0
+    int nb = 0;
+    for (int i = 0; i < n; i++) if (comp[i] == 0) {
+            nb += deg[i];
+        }
+    // Clean comp[]
+    delete[] comp;
+    return nb;
+}
+
+bool graph_molloy_opt::havelhakimi() {
+
+    int i;
+    int dmax = max_degree() + 1;
+    // Sort vertices using basket-sort, in descending degrees
+    int *nb = new int[dmax];
+    int *sorted = new int[n];
+    // init basket
+    for (i = 0; i < dmax; i++) {
+        nb[i] = 0;
+    }
+    // count basket
+    for (i = 0; i < n; i++) {
+        nb[deg[i]]++;
+    }
+    // cumul
+    int c = 0;
+    for (i = dmax - 1; i >= 0; i--) {
+        c += nb[i];
+        nb[i] = -nb[i] + c;
+    }
+    // sort
+    for (i = 0; i < n; i++) {
+        sorted[nb[deg[i]]++] = i;
+    }
+
+// Binding process starts
+    int first = 0;  // vertex with biggest residual degree
+    int d = dmax - 1; // maximum residual degree available
+
+    for (c = a / 2; c > 0; ) {
+        // pick a vertex. we could pick any, but here we pick the one with biggest degree
+        int v = sorted[first];
+        // look for current degree of v
+        while (nb[d] <= first) {
+            d--;
+        }
+        // store it in dv
+        int dv = d;
+        // bind it !
+        c -= dv;
+        int dc = d;         // residual degree of vertices we bind to
+        int fc = ++first;   // position of the first vertex with degree dc
+
+        while (dv > 0 && dc > 0) {
+            int lc = nb[dc];
+            if (lc != fc) {
+                while (dv > 0 && lc > fc) {
+                    // binds v with sorted[--lc]
+                    dv--;
+                    int w = sorted[--lc];
+                    *(neigh[v]++) = w;
+                    *(neigh[w]++) = v;
+                }
+                fc = nb[dc];
+                nb[dc] = lc;
+            }
+            dc--;
+        }
+        if (dv != 0) { // We couldn't bind entirely v
+            delete[] nb;
+            delete[] sorted;
+            compute_neigh();
+            igraph_errorf("Error in graph_molloy_opt::havelhakimi():"
+                          " Couldn't bind vertex %d entirely "
+                          "(%d edges remaining)", __FILE__, __LINE__,
+                          IGRAPH_EINTERNAL, v, dv);
+            return false;
+        }
+    }
+    assert(c == 0);
+    compute_neigh();
+    delete[] nb;
+    delete[] sorted;
+    return true;
+}
+
+bool graph_molloy_opt::is_connected() {
+    bool *visited = new bool[n];
+    for (int i = n; i > 0; visited[--i] = false) { }
+    int *to_visit = new int[n];
+    int *stop = to_visit;
+    int left = n - 1;
+    *(to_visit++) = 0;
+    visited[0] = true;
+    while (left > 0 && to_visit != stop) {
+        int v = *(--to_visit);
+        int *w = neigh[v];
+        for (int k = deg[v]; k--; w++) if (!visited[*w]) {
+                visited[*w] = true;
+                left--;
+                *(to_visit++) = *w;
+            }
+    }
+    delete[] visited;
+    delete[] stop;
+    assert(left >= 0);
+    return (left == 0);
+}
+
+
+bool graph_molloy_opt::make_connected() {
+    //assert(verify());
+    if (a / 2 < n - 1) {
+        // fprintf(stderr,"\ngraph::make_connected() failed : #edges < #vertices-1\n");
+        return false;
+    }
+    int i;
+
+// Data struct for the visit :
+// - buff[] contains vertices to visit
+// - dist[V] is V's distance modulo 4 to the root of its comp, or -1 if it hasn't been visited yet
+#define MC_BUFF_SIZE (n+2)
+    int *buff = new int[MC_BUFF_SIZE];
+    unsigned char * dist  = new unsigned char[n];
+#define NOT_VISITED 255
+#define FORBIDDEN   254
+    for (i = n; i > 0; dist[--i] = NOT_VISITED) { }
+
+// Data struct to store components : either surplus trees or surplus edges are stored at buff[]'s end
+// - A Tree is coded by one of its vertices
+// - An edge (a,b) is coded by the TWO ints a and b
+    int *ffub = buff + MC_BUFF_SIZE;
+    edge *edges = (edge *) ffub;
+    int *trees = ffub;
+    int *min_ffub = buff + 1 + (MC_BUFF_SIZE % 2 ? 0 : 1);
+
+// There will be only one "fatty" component, and trees.
+    edge fatty_edge = { -1, -1 };
+    bool enough_edges = false;
+
+    // start main loop
+    for (int v0 = 0; v0 < n; v0++) if (dist[v0] == NOT_VISITED) {
+            // is v0 an isolated vertex?
+            if (deg[v0] == 0) {
+                delete[] dist;
+                delete[] buff;
+                igraph_errorf("graph_molloy_opt::make_connected() returned FALSE : "
+                              "vertex %d has degree 0", __FILE__, __LINE__,
+                              IGRAPH_EINTERNAL, v0);
+                return false;
+            }
+            dist[v0] = 0; // root
+            int *to_visit = buff;
+            int *current  = buff;
+            *(to_visit++) = v0;
+
+            // explore component connected to v0
+            bool is_a_tree = true;
+            while (current != to_visit) {
+                int v = *(current++);
+                unsigned char current_dist = dist[v];
+                unsigned char next_dist = (current_dist + 1) & 0x03;
+                //unsigned char prev_dist = (current_dist-1) & 0x03;
+                int* ww = neigh[v];
+                int w;
+                for (int k = deg[v]; k--; ww++) {
+                    if (dist[w = *ww] == NOT_VISITED) {
+                        // we didn't visit *w yet
+                        dist[w] = next_dist;
+                        *(to_visit++) = w;
+                        if (to_visit > min_ffub) {
+                            min_ffub += 2;    // update limit of ffub's storage
+                        }
+                        //assert(verify());
+                    } else if (dist[w] == next_dist || (w >= v && dist[w] == current_dist)) {
+                        // we found a removable edge
+                        if (trees != ffub) {
+                            // some trees still.. Let's merge with them!
+                            assert(trees >= min_ffub);
+                            assert(edges == (edge *)ffub);
+                            swap_edges(v, w, *trees, neigh[*trees][0]);
+                            trees++;
+                            //assert(verify());
+                        } else if (is_a_tree) {
+                            // we must merge with the fatty component
+                            is_a_tree = false;
+                            if (fatty_edge.from < 0) {
+                                // we ARE the first component! fatty is us
+                                fatty_edge.from = v;
+                                fatty_edge.to   = w;
+                            } else {
+                                // we connect to fatty
+                                swap_edges(fatty_edge.from, fatty_edge.to, v, w);
+                                fatty_edge.to = w;
+                                //assert(verify());
+                            }
+                        } else if (!enough_edges) {
+                            // Store the removable edge for future use
+                            if (edges <= (edge *)min_ffub + 1) {
+                                enough_edges = true;
+                            } else {
+                                edges--;
+                                edges->from = v;
+                                edges->to   = w;
+                            }
+                        }
+                    }
+                }
+            }
+            // Mark component
+            while (to_visit != buff) {
+                dist[*(--to_visit)] = FORBIDDEN;
+            }
+            // Check if it is a tree
+            if (is_a_tree ) {
+                assert(deg[v0] != 0);
+                if (edges != (edge *)ffub) {
+                    // let's bind the tree we found with a removable edge in stock
+                    assert(trees == ffub);
+                    if (edges < (edge *)min_ffub) {
+                        edges = (edge *)min_ffub;
+                    }
+                    swap_edges(v0, neigh[v0][0], edges->from, edges->to);
+                    edges++;
+                    assert(verify());
+                } else if (fatty_edge.from >= 0) {
+                    // if there is a fatty component, let's merge with it ! and discard fatty :-/
+                    assert(trees == ffub);
+                    swap_edges(v0, neigh[v0][0], fatty_edge.from, fatty_edge.to);
+                    fatty_edge.from = -1;
+                    fatty_edge.to = -1;
+                    assert(verify());
+                } else {
+                    // add the tree to the list of trees
+                    assert(trees > min_ffub);
+                    *(--trees) = v0;
+                    assert(verify());
+                }
+            }
+        }
+    delete[] buff;
+    delete[] dist;
+    // Should ALWAYS return true : either we have no tree left, or we are a unique, big tree
+    return (trees == ffub || ((trees + 1) == ffub && fatty_edge.from < 0));
+}
+
+bool graph_molloy_opt::swap_edges_simple(int from1, int to1, int from2, int to2) {
+    if (from1 == to1 || from1 == from2 || from1 == to2 || to1 == from2 || to1 == to2 || from2 == to2) {
+        return false;
+    }
+    if (is_edge(from1, to2) || is_edge(from2, to1)) {
+        return false;
+    }
+    swap_edges(from1, to1, from2, to2);
+    return true;
+}
+
+long graph_molloy_opt::fab_connected_shuffle(long times) {
+    //assert(verify());
+    long nb_swaps = 0;
+    double T = double(min(a, times)) / 10.0;
+    double q1 = 1.131;
+    double q2 = 0.9237;
+
+    while (times > 0) {
+        long iperiod = max(1, long(T));
+        // Backup graph
+        int *save = backup();
+        //assert(verify());
+        // Swaps
+        long swaps = 0;
+        for (long i = iperiod; i > 0; i--) {
+            // Pick two random vertices
+            int f1 = links[my_random() % a];
+            int f2 = links[my_random() % a];
+            if (f1 == f2) {
+                continue;
+            }
+            // Pick two random neighbours
+            int *f1t1 = neigh[f1] + my_random() % deg[f1];
+            int *f2t2 = neigh[f2] + my_random() % deg[f2];
+            int t1 = *f1t1;
+            int t2 = *f2t2;
+            // test simplicity
+            if (t1 != t2 && f1 != t2 && f2 != t1 && is_edge(f1, t2) && !is_edge(f2, t1)) {
+                // swap
+                *f1t1 = t2;
+                *f2t2 = t1;
+                fast_rpl(neigh[t1], f1, f2);
+                fast_rpl(neigh[t2], f2, f1);
+                swaps++;
+            }
+        }
+        //assert(verify());
+        // test connectivity
+        if (is_connected()) {
+            nb_swaps += swaps;
+            times -= iperiod;
+            // adjust T
+            T *= q1;
+        } else {
+            restore(save);
+            //assert(verify());
+            T *= q2;
+        }
+        delete[] save;
+    }
+    return nb_swaps;
+}
+
+long graph_molloy_opt::opt_fab_connected_shuffle(long times) {
+    //assert(verify());
+    long nb_swaps = 0;
+    double T = double(min(a, times)) / 10.0;
+    double q1 = 1.131;
+    double q2 = 0.9237;
+
+    while (times > 0) {
+        long iperiod = max(1, long(T));
+        // Backup graph
+        int *save = backup();
+        //assert(verify());
+        // Swaps
+        long swaps = 0;
+        for (long i = iperiod; i > 0; i--) {
+            // Pick two random vertices
+            int f1 = links[my_random() % a];
+            int f2 = links[my_random() % a];
+            if (f1 == f2) {
+                continue;
+            }
+            // Pick two random neighbours
+            int *f1t1 = neigh[f1] + my_random() % deg[f1];
+            int *f2t2 = neigh[f2] + my_random() % deg[f2];
+            int t1 = *f1t1;
+            int t2 = *f2t2;
+            if (
+                // test simplicity
+                t1 != t2 && f1 != t2 && f2 != t1 && is_edge(f1, t2) && !is_edge(f2, t1) &&
+                // test isolated pair
+                (deg[f1] > 1 || deg[t2] > 1) && (deg[f2] > 1 || deg[t1] > 1)
+            ) {
+                // swap
+                *f1t1 = t2;
+                *f2t2 = t1;
+                fast_rpl(neigh[t1], f1, f2);
+                fast_rpl(neigh[t2], f2, f1);
+                swaps++;
+            }
+        }
+        //assert(verify());
+        // test connectivity
+        if (is_connected()) {
+            nb_swaps += swaps;
+            times -= iperiod;
+            // adjust T
+            T *= q1;
+        } else {
+            restore(save);
+            //assert(verify());
+            T *= q2;
+        }
+        delete[] save;
+    }
+    return nb_swaps;
+}
+
+long graph_molloy_opt::gkantsidis_connected_shuffle(long times) {
+    //assert(verify());
+    long nb_swaps = 0;
+    long T = min(a, times) / 10;
+
+    while (times > 0) {
+        // Backup graph
+        int *save = backup();
+        //assert(verify());
+        // Swaps
+        long swaps = 0;
+        for (int i = T; i > 0; i--) {
+            // Pick two random vertices
+            int f1 = links[my_random() % a];
+            int f2 = links[my_random() % a];
+            if (f1 == f2) {
+                continue;
+            }
+            // Pick two random neighbours
+            int *f1t1 = neigh[f1] + my_random() % deg[f1];
+            int *f2t2 = neigh[f2] + my_random() % deg[f2];
+            int t1 = *f1t1;
+            int t2 = *f2t2;
+            // test simplicity
+            if (t1 != t2 && f1 != t2 && f2 != t1 && is_edge(f1, t2) && !is_edge(f2, t1)) {
+                // swap
+                *f1t1 = t2;
+                *f2t2 = t1;
+                fast_rpl(neigh[t1], f1, f2);
+                fast_rpl(neigh[t2], f2, f1);
+                swaps++;
+            }
+        }
+        //assert(verify());
+        // test connectivity
+        if (is_connected()) {
+            nb_swaps += swaps;
+            times -= T;
+            // adjust T
+            T++;
+        } else {
+            restore(save);
+            //assert(verify());
+            T /= 2; if (T == 0) T = 1;
+        }
+        delete[] save;
+    }
+    return nb_swaps;
+}
+
+long graph_molloy_opt::slow_connected_shuffle(long times) {
+    //assert(verify());
+    long nb_swaps = 0;
+
+    while (times--) {
+        // Pick two random vertices
+        int f1 = links[my_random() % a];
+        int f2 = links[my_random() % a];
+        if (f1 == f2) {
+            continue;
+        }
+        // Pick two random neighbours
+        int *f1t1 = neigh[f1] + my_random() % deg[f1];
+        int *f2t2 = neigh[f2] + my_random() % deg[f2];
+        int t1 = *f1t1;
+        int t2 = *f2t2;
+        // test simplicity
+        if (t1 != t2 && f1 != t2 && f2 != t1 && is_edge(f1, t2) && !is_edge(f2, t1)) {
+            // swap
+            *f1t1 = t2;
+            *f2t2 = t1;
+            int *t1f1 = fast_rpl(neigh[t1], f1, f2);
+            int *t2f2 = fast_rpl(neigh[t2], f2, f1);
+            // test connectivity
+            if (is_connected()) {
+                nb_swaps++;
+            } else {
+                // undo swap
+                *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2;
+            }
+        }
+    }
+    return nb_swaps;
+}
+
+void graph_molloy_opt::print(FILE *f, bool NOZERO) {
+    int i, j;
+    for (i = 0; i < n; i++) {
+        if (!NOZERO || deg[i] > 0) {
+            fprintf(f, "%d", i);
+            for (j = 0; j < deg[i]; j++) {
+                fprintf(f, " %d", neigh[i][j]);
+            }
+            fprintf(f, "\n");
+        }
+    }
+}
+
+long graph_molloy_opt::effective_isolated(int v, int K, int *Kbuff, bool *visited) {
+    int i;
+    for (i = 0; i < K; i++) {
+        Kbuff[i] = -1;
+    }
+    long count = 0;
+    int left = K;
+    int *KB = Kbuff;
+    //yapido = (my_random()%1000 == 0);
+    depth_isolated(v, count, left, K, KB, visited);
+    while (KB-- != Kbuff) {
+        visited[*KB] = false;
+    }
+    //if(yapido) fprintf(stderr,"\n");
+    return count;
+}
+
+void graph_molloy_opt::depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited) {
+    if (left_to_explore == 0) {
+        return;
+    }
+//  if(yapido) fprintf(stderr,"%d ",deg[v]);
+    if (--left_to_explore == 0) {
+        return;
+    }
+    if (deg[v] + 1 >= dmax) {
+        left_to_explore = 0;
+        return;
+    }
+    *(Kbuff++) = v;
+    visited[v] = true;
+    calls++;
+    int *w = neigh[v];
+    qsort(deg, w, deg[v]);
+    w += deg[v];
+    for (int i = deg[v]; i--; ) {
+        if (visited[*--w]) {
+            calls++;
+        } else {
+            depth_isolated(*w, calls, left_to_explore, dmax, Kbuff, visited);
+        }
+        if (left_to_explore == 0) {
+            break;
+        }
+    }
+}
+
+int graph_molloy_opt::depth_search(bool *visited, int *buff, int v0) {
+    for (int i = 0; i < n; i++) {
+        visited[i] = false;
+    }
+    int *to_visit = buff;
+    int nb_visited = 1;
+    visited[v0] = true;
+    *(to_visit++) = v0;
+    while (to_visit != buff && nb_visited < n) {
+        int v = *(--to_visit);
+        int *ww = neigh[v];
+        int w;
+        for (int k = deg[v]; k--; ww++) if (!visited[w = *ww]) {
+                visited[w] = true;
+                nb_visited++;
+                *(to_visit++) = w;
+            }
+    }
+    return nb_visited;
+}
+
+int graph_molloy_opt::width_search(unsigned char *dist, int *buff, int v0, int toclear) {
+    if (toclear >= 0) for (int i = 0; i < toclear; i++) {
+            dist[buff[i]] = 0;
+        } else for (int i = 0; i < n; i++) {
+            dist[i] = 0;
+        }
+    int *to_visit = buff;
+    int *to_add = buff;
+    int nb_visited = 1;
+    dist[v0] = 1;
+    *(to_add++) = v0;
+    while (to_visit != to_add && nb_visited < n) {
+        int v = *(to_visit++);
+        int *ww = neigh[v];
+        int w;
+        unsigned char d = next_dist(dist[v]);
+        for (int k = deg[v]; k--; ww++) if (dist[w = *ww] == 0) {
+                dist[w] = d;
+                nb_visited++;
+                *(to_add++) = w;
+            }
+    }
+    return nb_visited;
+}
+
+double graph_molloy_opt::avg_dist(unsigned char *dist, int *buff, int v0, int &nb_visited, int toclear) {
+    nb_visited = width_search(dist, buff, v0, toclear);
+    unsigned char curr_dist = 1;
+    assert(curr_dist == dist[v0]);
+    double total_dist = 0.0;
+    int current_dist = 0;
+    for (int p = 0; p < nb_visited; p++) {
+        v0 = buff[p];
+        if (dist[v0] != curr_dist) {
+            current_dist++;
+            curr_dist = dist[v0];
+        }
+        total_dist += double(current_dist);
+    }
+    nb_visited--;
+    return total_dist / double(nb_visited);
+}
+
+
+void graph_molloy_opt::add_traceroute_edge(int v, int k, int *newdeg, double **edge_redudancy, double red) {
+    int *ww = neigh[v] + k;
+    int w = *ww;
+    int k2 = 0;
+    // Is neigh[v][k] a new edge ?
+    if (k >= newdeg[v]) {
+        int *p = neigh[v] + (newdeg[v]++);
+        *ww = *p;
+        *p = w;
+        // Now, add the dual edge
+        ww = neigh[w];
+        p = ww + (newdeg[w]);
+        while (ww != p && *ww != v) {
+            ww++;
+            k2++;
+        }
+        if (ww == p) {
+            // dual edge was not discovered.. search it and add it.
+            while (*ww != v) {
+                ww++;
+                k2++;
+            }
+            *ww = *p;
+            *p = v;
+            newdeg[w]++;
+        }
+    }
+    // if edge redudancy is asked, look for dual edge
+    else if (edge_redudancy != NULL)
+        for (int *ww = neigh[w]; * (ww++) != v; k2++) { }
+    // add edge redudancy
+    if (edge_redudancy != NULL) {
+        edge_redudancy[v][k]  += red;
+        edge_redudancy[w][k2] += red;
+    }
+    assert(newdeg[v] <= deg[v]);
+}
+
+// dist[] MUST be full of zeros !!!!
+int graph_molloy_opt::breadth_path_search(int src, int *buff, double *paths, unsigned char *dist) {
+    unsigned char last_dist = 0;
+    unsigned char curr_dist = 1;
+    int *to_visit = buff;
+    int *visited  = buff;
+    *(to_visit++) = src;
+    paths[src] = 1.0;
+    dist[src]  = curr_dist;
+    int nb_visited = 1;
+    while (visited != to_visit) {
+        int v = *(visited++);
+        if (last_dist == (curr_dist = dist[v])) {
+            break;
+        }
+        unsigned char nd = next_dist(curr_dist);
+        int *ww = neigh[v];
+        double p = paths[v];
+        for (int k = deg[v]; k--;) {
+            int w = *(ww++);
+            unsigned char d = dist[w];
+            if (d == 0) {
+                // not visited yet !
+                *(to_visit++) = w;
+                dist[w] = nd;
+                paths[w] = p;
+                // is it the last one ?
+                if (++nb_visited == n) {
+                    last_dist = nd;
+                }
+            } else if (d == nd) if ((paths[w] += p) == numeric_limits<double>::infinity()) {
+                    IGRAPH_ERROR("Fatal error : too many (>MAX_DOUBLE) possible"
+                                 " paths in graph", IGRAPH_EOVERFLOW);
+                }
+        }
+    }
+    assert(to_visit == buff + nb_visited);
+    return nb_visited;
+}
+
+// dist[] MUST be full of zeros !!!!
+void graph_molloy_opt::explore_usp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg, double **edge_redudancy) {
+
+    while (--nb_vertices) {
+        int v = buff[nb_vertices];
+        if (target[v] > 0.0) {
+            unsigned char pd = prev_dist(dist[v]);
+            int *ww = neigh[v];
+            int k = 0;
+            // pick ONE father at random
+            double father_index = my_random01() * paths[v];
+            double f = 0.0;
+            int father = -1;
+            while (f < father_index) {
+                while (dist[father = ww[k++]] != pd) { }
+                f += paths[father];
+            }
+            // increase target[] of father
+            target[father] += target[v];
+            // add edge, if necessary
+            if (newdeg != NULL) {
+                add_traceroute_edge(v, k - 1, newdeg, edge_redudancy, target[v]);
+            }
+        }
+        // clear dist[]
+        dist[v] = 0;
+    }
+    dist[buff[0]] = 0;
+}
+
+// dist[] MUST be full of zeros !!!!
+void graph_molloy_opt::explore_asp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg, double **edge_redudancy) {
+
+    while (--nb_vertices) {
+        int v = buff[nb_vertices];
+        if (target[v] > 0.0) {
+            unsigned char pd = prev_dist(dist[v]);
+            int *ww = neigh[v];
+            int dv = deg[v];
+            double f = target[v] / paths[v];
+            // pick ALL fathers
+            register int father;
+            for (int k = 0; k < dv; k++) if (dist[father = ww[k]] == pd) {
+                    // increase target[] of father
+                    target[father] += paths[father] * f;
+                    // add edge, if necessary
+                    if (newdeg != NULL) {
+                        add_traceroute_edge(v, k, newdeg, edge_redudancy, target[v]);
+                    }
+                }
+        }
+        // clear dist[]
+        dist[v] = 0;
+    }
+    dist[buff[0]] = 0;
+}
+
+// dist[] MUST be full of zeros !!!!
+void graph_molloy_opt::explore_rsp(double *target, int nb_vertices, int *buff, double *paths, unsigned char *dist, int *newdeg, double** edge_redudancy) {
+
+    while (--nb_vertices) {
+        int v = buff[nb_vertices];
+        if (target[v] > 0.0) {
+            unsigned char pd = prev_dist(dist[v]);
+            int *ww = neigh[v];
+            // for all fathers : do we take it ?
+            int paths_left = int(target[v]);
+            double father_index = paths[v];
+            int father;
+            for (int k = 0; k < deg[v]; k++) if (dist[father = ww[k]] == pd) {
+                    double pf = paths[father];
+                    int to_add_to_father = my_binomial(pf / father_index, paths_left);
+                    father_index -= pf;
+                    if (to_add_to_father > 0) {
+                        paths_left -= to_add_to_father;
+                        // increase target[] of father
+                        target[father] += to_add_to_father;
+                        // add edge, if necessary
+                        if (newdeg != NULL) {
+                            add_traceroute_edge(v, k, newdeg, edge_redudancy, target[v]);
+                        }
+                    }
+                }
+        }
+        // clear dist[]
+        dist[v] = 0;
+    }
+    dist[buff[0]] = 0;
+}
+
+double *graph_molloy_opt::vertex_betweenness(int mode, bool trivial_paths) {
+    char MODES[3] = {'U', 'A', 'R'};
+    igraph_statusf("Computing vertex betweenness %cSP...", 0, MODES[mode]);
+
+    // breadth-first search vertex fifo
+    int *buff = new int[n];
+    // breadth-first search path count
+    double *paths = new double[n];
+    // breadth-first search distance vector
+    unsigned char *dist = new unsigned char[n];
+    // global betweenness
+    double *b = new double[n];
+    // local betweenness (for one source)
+    double *target = new double[n];
+    // init all
+    int progress = 0;
+    memset(dist, 0, sizeof(unsigned char)*n);
+    for (double *yo = target + n; (yo--) != target; *yo = 1.0) { }
+    for (double *yo = b + n; (yo--) != b; *yo = 0.0) { }
+
+    int progress_steps = max(1000, n / 10);
+    // Main loop
+    for (int v0 = 0; v0 < n; v0++) {
+        // Verbose
+        if (v0 > (progress * n) / progress_steps) {
+            progress++;
+            igraph_progressf("Computing vertex betweenness %cSP",
+                             100.0 * double(progress) / double(progress_steps), 0,
+                             MODES[mode]);
+        }
+        // Breadth-first search
+        int nb_vertices = breadth_path_search(v0, buff, paths, dist);
+        // initialize target[vertices in component] to 1
+        //for(int *yo = buff+nb_vertices; (yo--)!=buff; target[*yo]=1.0);
+        // backwards-cumulative exploration
+        switch (mode) {
+        case MODE_USP:
+            explore_usp(target, nb_vertices, buff, paths, dist); break;
+        case MODE_ASP:
+            explore_asp(target, nb_vertices, buff, paths, dist); break;
+        case MODE_RSP:
+            explore_rsp(target, nb_vertices, buff, paths, dist); break;
+        default:
+            IGRAPH_WARNING("graph_molloy_opt::vertex_betweenness() "
+                           "called with Invalid Mode");
+        }
+        // add targets[vertices in component] to global betweenness and reset targets[]
+        if (nb_vertices == n) {
+            // cache optimization if all vertices are in component
+            double *bb = b;
+            double *tt_end = target + n;
+            if (trivial_paths) for (double *yo = target; yo != tt_end; * (bb++) += *(yo++)) {}
+            else {
+                for (double *yo = target; yo != tt_end; * (bb++) += (*(yo++) - 1.0)) { }
+                b[*buff] -= (target[*buff] - 1.0);
+            }
+            for (double *yo = target; yo != tt_end; * (yo++) = 1.0) { }
+        } else {
+            if (trivial_paths)
+                for (int *yo = buff + nb_vertices; (yo--) != buff; b[*yo] += target[*yo]) { }
+            else
+                for (int *yo = buff + nb_vertices; (--yo) != buff; b[*yo] += (target[*yo] - 1.0)) { }
+            for (int *yo = buff + nb_vertices; (yo--) != buff; target[*yo] = 1.0) { }
+        }
+    }
+    // Clean all & return
+    delete[] target;
+    delete[] dist;
+    delete[] buff;
+    delete[] paths;
+    igraph_status("Done\n", 0);
+    return b;
+}
+
+double graph_molloy_opt::traceroute_sample(int mode, int nb_src, int *src, int nb_dst, int* dst, double *redudancy, double **edge_redudancy) {
+    // verify & verbose
+    assert(verify());
+    char MODES[3] = {'U', 'A', 'R'};
+    igraph_statusf("traceroute %cSP on G(N=%d,M=%d) with %d src and %d dst...",
+                   0, MODES[mode], nbvertices_real(), nbarcs(), nb_src, nb_dst);
+
+    // create dst[] buffer if necessary
+    bool newdist = dst == NULL;
+    if (newdist) {
+        dst = new int[n];
+    }
+    // breadth-first search vertex fifo
+    int *buff = new int[n];
+    // breadth-first search path count
+    double *paths = new double[n];
+    // breadth-first search distance vector
+    unsigned char *dist = new unsigned char[n];
+    // newdeg[] allows to tag discovered edges
+    int *newdeg = new int[n];
+    // target[v] is > 0 if v is a destination
+    double *target = new double[n];
+
+    // init all
+    int i;
+    memset(dist, 0, sizeof(unsigned char)*n);
+    memset(newdeg, 0, sizeof(int)*n);
+    for (double *yo = target + n; (yo--) != target; *yo = 0.0) { }
+    if (redudancy != NULL)
+        for (double *yo = redudancy + n; (yo--) != redudancy; *yo = 0.0) { }
+
+    // src_0 counts the number of sources having degree 0
+    int src_0 = 0;
+    // nopath counts the number of pairs (src,dst) having no possible path
+    int nopath = 0;
+    // nb_paths & total_dist are for the average distance estimator
+    int nb_paths = 0;
+    double total_dist = 0;
+    // s will be the current source
+    int s;
+
+    while (nb_src--) if (deg[s = *(src++)] == 0) {
+            src_0++;
+        } else {
+            // breadth-first search
+            int nb_vertices = breadth_path_search(s, buff, paths, dist);
+            // do we have to pick new destinations ?
+            if (newdist) {
+                pick_random_dst(double(nb_dst), NULL, dst);
+            }
+            // mark reachable destinations as "targets"
+            for (i = 0; i < nb_dst; i++) {
+                if (dist[dst[i]] != 0) {
+                    target[dst[i]] = 1.0;
+                } else {
+                    nopath++;
+                }
+            }
+            // compute avg_dist estimator
+            int current_dist = 0;
+            unsigned char curr_dist = 1;
+            for (int p = 1; p < nb_vertices; p++) {
+                int v = buff[p];
+                if (dist[v] != curr_dist) {
+                    curr_dist = dist[v];
+                    current_dist++;
+                }
+                if (target[v] > 0.0) {
+                    total_dist += double(current_dist);
+                    nb_paths++;
+                }
+            }
+            // substract target[] to redudancy if needed
+            if (redudancy != NULL) for (i = 1; i < nb_vertices; i++) {
+                    redudancy[buff[i]] -= (target[buff[i]]);
+                }
+            // traceroute exploration
+            switch (mode) {
+            case MODE_USP:
+                explore_usp(target, nb_vertices, buff, paths, dist, newdeg, edge_redudancy); break;
+            case MODE_ASP:
+                explore_asp(target, nb_vertices, buff, paths, dist, newdeg, edge_redudancy); break;
+            case MODE_RSP:
+                explore_rsp(target, nb_vertices, buff, paths, dist, newdeg, edge_redudancy); break;
+            default:
+                IGRAPH_WARNING("graph_molloy_opt::traceroute_sample() called "
+                               "with Invalid Mode");
+            }
+            // add target[] to redudancy[] if needed
+            if (redudancy != NULL) for (i = 1; i < nb_vertices; i++) {
+                    redudancy[buff[i]] += (target[buff[i]]);
+                }
+            // clear target[]
+            for (int *yo = buff + nb_vertices; yo-- != buff; target[*yo] = 0.0) { }
+        }
+    // update degrees
+    for (i = 0; i < n; i++) {
+        deg[i] = newdeg[i];
+    }
+    refresh_nbarcs();
+    // clean all
+    delete[] buff;
+    delete[] paths;
+    delete[] dist;
+    delete[] newdeg;
+    delete[] target;
+    if (newdist) {
+        delete[] dst;
+    }
+    {
+        igraph_statusf("discovered %d vertices and %d edges\n", 0,
+                       nbvertices_real(), nbarcs());
+        if (src_0)  igraph_warningf("%d sources had degree 0\n", __FILE__,
+                                        __LINE__, -1, src_0);
+        if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path\n",
+                                        __FILE__, __LINE__, -1, nopath);
+    }
+    return total_dist / double(nb_paths);
+}
+
+int graph_molloy_opt::disconnecting_edges() {
+    int removed = 0;
+    while (is_connected()) {
+        // replace random edge by loops
+        int i;
+        do {
+            i = pick_random_vertex();
+        } while (i < 0 || deg[i] < 1);
+        int *p = neigh[i] + (my_random() % deg[i]);
+        int j = *p; *p = i;
+        fast_rpl(neigh[j], i, j);
+        removed++;
+    }
+    return removed;
+}
+
+void graph_molloy_opt::vertex_covering() {
+    vertex_cover(n, links, deg, neigh);
+}
+
+
+// optimisations a faire :
+// 1/ arreter le breadth-first search qd on a vu toutes les dst
+// 2/ faire une seule redescente pour toutes les dst.
+
+double graph_molloy_opt::path_sampling(int *nb_dst, int *dst, double* redudancies, double **edge_redudancies) {
+    assert(verify());
+    // do we have to store the destinations (for one src) in a temp buffer?
+    bool NOMEM = (dst == NULL);
+    if (NOMEM) {
+        dst = new int[n];
+    }
+    int i;
+    int next_step = n + 1;
+    {
+        igraph_status("Sampling paths", 0);
+        next_step = 0;
+    }
+    // breadth-first search buffers buff[] and dist[]
+    int *buff = new int[n];
+    unsigned char *dist = new unsigned char[n];
+    for (i = 0; i < n; i++) {
+        dist[i] = 0;
+    }
+    // nb_pos[] counts the number of possible paths to get to a vertex
+    int *nb_pos = new int[n];
+    for (i = 0; i < n; i++) {
+        nb_pos[i] = 0;
+    }
+    // newdeg[i] is the number of edges of vertex i "seen" by traceroute
+    int *newdeg = new int[n];
+    for (i = 0; i < n; i++) {
+        newdeg[i] = 0;
+    }
+
+    // src_0 counts the number of sources having degree 0
+    int src_0 = 0;
+    // nopath counts the number of pairs (src,dst) having no possible path
+    int nopath = 0;
+    // nb_paths & total_dist are for the average distance estimator
+    int nb_paths = 0;
+    unsigned int total_dist = 0;
+    unsigned int total_dist64 = 0;
+
+    // s is the source of the breadth-first search
+    for (int s = 0; s < n; s++) if (nb_dst[s] > 0) {
+            if (deg[s] == 0) {
+                src_0++;
+            } else {
+                if (s > next_step) {
+                    next_step = s + (n / 1000) + 1;
+                    igraph_progress("Sampling paths", double(s) / double(n), 0);
+                }
+                int v;
+                // breadth-first search
+                int *to_visit = buff;
+                int *visited = buff;
+                *(to_visit++) = s;
+                dist[s] = 1;
+                nb_pos[s] = 1;
+                while (visited != to_visit) {
+                    v = *(visited++);
+                    unsigned char n_dist = next_dist(dist[v]);
+                    int *w0 = neigh[v];
+                    for (int *w = w0 + deg[v]; w-- != w0; ) {
+                        unsigned char d2 = dist[*w];
+                        if (d2 == 0) {
+                            dist[*w] = d2 = n_dist;
+                            *(to_visit++) = *w;
+                        }
+                        if (d2 == n_dist) {
+                            nb_pos[*w] += nb_pos[v];
+                        }
+                    }
+                }
+
+                // for every target, pick a random path.
+                int t_index = nb_dst[s];
+                // create dst[] if necessary
+                if (NOMEM) {
+                    pick_random_src(double(t_index), NULL, dst);
+                }
+                while (t_index--) if (dist[v = *(dst++)] == 0) {
+                        nopath++;
+                    } else {
+#ifdef _DEBUG
+                        igraph_statusf("Sampling path %d -> %d\n", 0, s, v);
+#endif //_DEBUG
+                        nb_paths++;
+                        // while we haven't reached the source..
+                        while (v != s) {
+                            // pick a random father
+                            int index = my_random() % nb_pos[v];
+                            unsigned char p_dist = prev_dist(dist[v]);
+                            int *w = neigh[v];
+                            int k = 0;
+                            int new_father;
+                            while (dist[new_father = w[k]] != p_dist || (index -= nb_pos[new_father]) >= 0) {
+                                k++;
+                            }
+                            // add edge
+                            add_traceroute_edge(v, k, newdeg, edge_redudancies, 1.0);
+                            if (redudancies != NULL && new_father != s) {
+                                redudancies[new_father] += 1.0;
+                            }
+                            // step down to father
+                            v = new_father;
+                            // increase total distance
+                            total_dist++;
+                            if (total_dist == 0) {
+                                total_dist64++;
+                            }
+                        }
+                    }
+                // reset (int *)dst if necessary
+                if (NOMEM) {
+                    dst -= nb_dst[s];
+                }
+
+                // clear breadth-first search buffers
+                while (visited != buff) {
+                    v = *(--visited);
+                    dist[v] = 0;
+                    nb_pos[v] = 0;
+                }
+            }
+        }
+    // update degrees
+    for (i = 0; i < n; i++) {
+        deg[i] = newdeg[i];
+    }
+    refresh_nbarcs();
+    // clean
+    delete[] newdeg;
+    delete[] buff;
+    delete[] dist;
+    delete[] nb_pos;
+    if (NOMEM) {
+        delete[] dst;
+    }
+    if (VERBOSE()) {
+        igraph_status("Sampling paths :  Done   \n", 0);
+        if (src_0)  igraph_warningf("%d sources had degree 0", __FILE__,
+                                        __LINE__, -1, src_0);
+        if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path",
+                                        __FILE__, __LINE__, -1, nopath);
+    }
+    double tdist = double(total_dist64);
+    if (total_dist64 > 0) {
+        tdist *= 4294967296.0;
+    }
+    tdist += double(total_dist);
+    return tdist / double(nb_paths);
+}
+
+int *graph_molloy_opt::vertices_real(int &nb_v) {
+    int *yo;
+    if (nb_v < 0) {
+        nb_v = 0;
+        for (yo = deg; yo != deg + n; ) if (*(yo++) > 0) {
+                nb_v++;
+            }
+    }
+    if (nb_v == 0) {
+        IGRAPH_WARNING("graph is empty");
+        return NULL;
+    }
+    int *buff = new int[nb_v];
+    yo = buff;
+    for (int i = 0; i < n; i++) if (deg[i] > 0) {
+            *(yo++) = i;
+        }
+    if (yo != buff + nb_v) {
+        igraph_warningf("wrong #vertices in graph_molloy_opt::vertices_real(%d)",
+                        __FILE__, __LINE__, -1, nb_v);
+        delete[] buff;
+        return NULL;
+    } else {
+        return buff;
+    }
+}
+
+int *graph_molloy_opt::pick_random_vertices(int &k, int *output, int nb_v, int *among) {
+    int i;
+    bool CREATED_AMONG = false;
+    if (among == NULL && k > 0) {
+        among = vertices_real(nb_v);
+        CREATED_AMONG = true;
+    }
+    if (k > nb_v) {
+        igraph_warningf("Warning : tried to pick %d among %d vertices. "
+                        "Picked only %d", __FILE__, __LINE__, -1, k, nb_v, nb_v);
+        k = nb_v;
+    }
+    if (k > 0) {
+        if (output == NULL) {
+            output = new int[k];
+        }
+        for (i = 0; i < k; i++) {
+            int tmp = i + my_random() % (nb_v - i);
+            output[i] = among[tmp];
+            among[tmp] = among[i];
+            among[i] = output[i];
+        }
+    }
+    if (CREATED_AMONG) {
+        delete[] among;
+    }
+    return output;
+}
+
+int *graph_molloy_opt::pick_random_src(double k, int *nb, int* buff, int nb_v, int* among) {
+    bool AMONG_CREATED = false;
+    if (among == NULL || nb_v < 0) {
+        AMONG_CREATED = true;
+        among = vertices_real(nb_v);
+    }
+    int kk = int(floor(0.5 + (k >= 1.0 ? k : k * double(nb_v))));
+    if (kk == 0) {
+        kk = 1;
+    }
+    int *yo = pick_random_vertices(kk, buff, nb_v, among);
+    if (nb != NULL) {
+        *nb = kk;
+    }
+    if (AMONG_CREATED) {
+        delete[] among;
+    }
+    return yo;
+}
+
+int *graph_molloy_opt::pick_random_dst(double k, int *nb, int* buff, int nb_v, int* among) {
+    bool AMONG_CREATED = false;
+    if (among == NULL || nb_v < 0) {
+        AMONG_CREATED = true;
+        among = vertices_real(nb_v);
+    }
+    int kk = int(floor(0.5 + (k > 1.0 ? k : k * double(nb_v))));
+    if (kk == 0) {
+        kk = 1;
+    }
+    int *yo = pick_random_vertices(kk, buff, nb_v, among);
+    if (nb != NULL) {
+        *nb = kk;
+    }
+    if (AMONG_CREATED) {
+        delete[] among;
+    }
+    return yo;
+}
+
+int graph_molloy_opt::core() {
+    box_list b(n, deg);
+    int v;
+    int removed = 0;
+    while ((v = b.get_one()) >= 0) {
+        b.pop_vertex(v, neigh);
+        deg[v] = 0;
+        removed++;
+    }
+    refresh_nbarcs();
+    return removed;
+}
+
+int graph_molloy_opt::try_disconnect(int K, int max_tries) {
+    bool *visited = new bool[n];
+    for (bool *p = visited + n; p != visited; * (--p) = false) { }
+    int *Kbuff = new int[K];
+    int tries = 0;
+    int next_step = -1;
+    if (VERBOSE()) {
+        next_step = 0;
+    }
+    bool yo = true;
+    while (yo && tries < max_tries) {
+        if (tries == next_step) {
+            igraph_statusf("Trying to disconnect the graph... "
+                           "%d edges swaps done so far", 0, tries);
+            next_step += 100;
+        }
+        int v1 = pick_random_vertex();
+        int v2 = pick_random_vertex();
+        int w1 = *(random_neighbour(v1));
+        int w2 = *(random_neighbour(v2));
+        if (swap_edges_simple(v1, w1, v2, w2)) {
+            tries++;
+            yo = (!isolated(v1, K, Kbuff, visited) && !isolated(v2, K, Kbuff, visited) && !is_connected());
+            swap_edges(v1, w2, v2, w1);
+        }
+    }
+    delete[] visited;
+    delete[] Kbuff;
+    return tries;
+}
+
+bool graph_molloy_opt::isolated(int v, int K, int *Kbuff, bool *visited) {
+    if (K < 2) {
+        return false;
+    }
+#ifdef OPT_ISOLATED
+    if (K <= deg[v] + 1) {
+        return false;
+    }
+#endif //OPT_ISOLATED
+    int *seen  = Kbuff;
+    int *known = Kbuff;
+    int *max   = Kbuff + (K - 1);
+    *(known++) = v;
+    visited[v] = true;
+    bool is_isolated = true;
+
+    while (known != seen) {
+        v = *(seen++);
+        int *w = neigh[v];
+        for (int d = deg[v]; d--; w++) if (!visited[*w]) {
+#ifdef OPT_ISOLATED
+                if (K <= deg[*w] + 1 || known == max) {
+#else //OPT_ISOLATED
+                if (known == max) {
+#endif //OPT_ISOLATED
+                    is_isolated = false;
+                    goto end_isolated;
+                }
+                visited[*w] = true;
+                *(known++) = *w;
+            }
+    }
+end_isolated:
+    // Undo the changes to visited[]...
+    while (known != Kbuff) {
+        visited[*(--known)] = false;
+    }
+    return is_isolated;
+}
+
+double graph_molloy_opt::rho(int mode, int nb_src, int *src, int nb_dst, int *dst) {
+    assert(verify());
+
+    // create dst[] buffer if necessary
+    bool newdist = dst == NULL;
+    if (newdist) {
+        dst = new int[n];
+    }
+    // breadth-first search vertex fifo
+    int *buff = new int[n];
+    // breadth-first search path count
+    double *paths = new double[n];
+    // breadth-first search distance vector
+    unsigned char *dist = new unsigned char[n];
+    // target[v] is > 0 if v is a destination
+    double *target = new double[n];
+    // times_seen count the times we saw each vertex
+    int *times_seen = new int[n];
+
+    // init all
+    int i;
+    memset(dist, 0, sizeof(unsigned char)*n);
+    memset(times_seen, 0, sizeof(int)*n);
+    for (double *yo = target + n; (yo--) != target; *yo = 0.0) { }
+
+    // src_0 counts the number of sources having degree 0
+    int src_0 = 0;
+    // nopath counts the number of pairs (src,dst) having no possible path
+    int nopath = 0;
+    // s will be the current source
+    int s;
+
+    for (int nsrc = 0; nsrc < nb_src; nsrc++) if (deg[s = *(src++)] == 0) {
+            src_0++;
+        } else {
+            // breadth-first search
+            int nb_vertices = breadth_path_search(s, buff, paths, dist);
+            // do we have to pick new destinations ?
+            if (newdist) {
+                pick_random_dst(double(nb_dst), NULL, dst);
+            }
+            // mark reachable destinations as "targets" and substract one time_seen
+            for (i = 0; i < nb_dst; i++) {
+                if (dist[dst[i]] != 0) {
+                    target[dst[i]] = 1.0;
+                } else {
+                    nopath++;
+                }
+            }
+            // traceroute exploration
+            switch (mode) {
+            case MODE_USP:
+                explore_usp(target, nb_vertices, buff, paths, dist); break;
+            case MODE_ASP:
+                explore_asp(target, nb_vertices, buff, paths, dist); break;
+            case MODE_RSP:
+                explore_rsp(target, nb_vertices, buff, paths, dist); break;
+            default:
+                IGRAPH_WARNING("graph_molloy_opt::rho() called with Invalid Mode");
+            }
+            // remove destinations that weren't discovered by a path coming through
+            for (i = 0; i < nb_dst; i++) {
+                int yo = dst[i];
+                if (target[yo] == 1.0) {
+                    target[yo] = 0.0;
+                }
+            }
+            // add target[] to times_seen[]
+            for (i = 1; i < nb_vertices; i++) {
+                int yo = buff[i];
+                if (target[yo] != 0.0) {
+                    target[yo] = 0.0;
+                    times_seen[yo]++;
+                }
+            }
+            // also clear  the source
+            target[buff[0]] = 0.0;
+        }
+    // clean all
+    delete[] buff;
+    delete[] paths;
+    delete[] dist;
+    delete[] target;
+    if (newdist) {
+        delete[] dst;
+    }
+    // compute rho
+    double sum_nij = 0.0;
+    double sum_ni = 0.0;
+    for (i = 0; i < n; i++) {
+        double d = double(times_seen[i]);
+        sum_ni += d;
+        sum_nij += d * d;
+    }
+    delete[] times_seen;
+    {
+        igraph_status("done\n", 0);
+        if (src_0)  igraph_warningf("%d sources had degree 0", __FILE__, __LINE__,
+                                        -1, src_0);
+        if (nopath) igraph_warningf("%d (src,dst) pairs had no possible path",
+                                        __FILE__, __LINE__, -1, nopath);
+    }
+    return (sum_nij - sum_ni) * double(n) * double(nb_src) / (sum_ni * sum_ni * double(nb_src - 1));
+}
+
+void graph_molloy_opt::sort() {
+    for (int v = 0; v < n; v++) {
+        qsort(neigh[v], deg[v]);
+    }
+}
+
+int* graph_molloy_opt::sort_vertices(int *buff) {
+    // pre-sort vertices by degrees
+    buff = boxsort(deg, n, buff);
+    // sort vertices having the same degrees
+    int i = 0;
+    while (i < n) {
+        int d = deg[buff[i]];
+        int j = i + 1;
+        while (j < n && deg[buff[j]] == d) {
+            j++;
+        }
+        lex_qsort(neigh, buff + i, j - i, d);
+        i = j;
+    }
+    return buff;
+}
+
+int graph_molloy_opt::cycles(int v) {
+    return v;
+}
+
+// void graph_molloy_opt::remove_vertex(int v) {
+//   fprintf(stderr,"Warning : graph_molloy_opt::remove_vertex(%d) called",v);
+// }
+
+bool graph_molloy_opt::verify(int mode) {
+    int i, j, k;
+    assert(neigh[0] == links);
+    // verify edges count
+    if ((mode & VERIFY_NOARCS) == 0) {
+        int sum = 0;
+        for (i = 0; i < n; i++) {
+            sum += deg[i];
+        }
+        assert(sum == a);
+    }
+    // verify neigh[] and deg[] compatibility
+    if ((mode & VERIFY_NONEIGH) == 0)
+        for (i = 0; i < n - 1; i++) {
+            assert(neigh[i] + deg[i] == neigh[i + 1]);
+        }
+    // verify vertex range
+    for (i = 0; i < a; i++) {
+        assert(links[i] >= 0 && links[i] < n);
+    }
+    // verify simplicity
+//  for(i=0; i<n; i++) for(j=0; j<deg[i]; j++) for(k=j+1; k<deg[i]; k++)
+//    assert(neigh[i][j]!=neigh[i][k]);
+    // verify symmetry
+    for (i = 0; i < n; i++) for (j = 0; j < deg[i]; j++) {
+            int v = neigh[i][j];
+            int nb = 0;
+            for (k = 0; k < deg[v]; k++) if (neigh[v][k] == i) {
+                    nb++;
+                }
+            assert(nb > 0);
+        }
+    return true;
+}
+
+/*___________________________________________________________________________________
+  Not to use anymore : use graph_molloy_hash class instead
+
+void graph_molloy_opt::shuffle(long times) {
+  while(times) {
+    int f1 = links[my_random()%a];
+    int f2 = links[my_random()%a];
+    int t1 = neigh[f1][my_random()%deg[f1]];
+    int t2 = neigh[f2][my_random()%deg[f2]];
+    if(swap_edges_simple(f1,t1,f2,t2)) times--;
+  }
+}
+
+
+long graph_molloy_opt::connected_shuffle(long times) {
+  //assert(verify());
+#ifdef PERFORMANCE_MONITOR
+  long failures = 0;
+  long successes = 0;
+  double avg_K = 0.0;
+  long avg_T = 0;
+#endif //PERFORMANCE_MONITOR
+
+  long nb_swaps = 0;
+  long T = min(a,times)/10;
+  double double_K = 1.0;
+  int K = int(double_K);
+  double Q1 = 1.35;
+  double Q2 = 1.01;
+  int *Kbuff = new int[K];
+  bool *visited = new bool[n];
+  for(int i=0; i<n; i++) visited[i] = false;
+
+  while(times>nb_swaps) {
+    // Backup graph
+#ifdef PERFORMANCE_MONITOR
+    avg_K+=double_K;
+    avg_T+=T;
+#endif //PERFORMANCE_MONITOR
+    int *save = backup();
+    //assert(verify());
+    // Swaps
+    long swaps = 0;
+    for(int i=T; i>0; i--) {
+      // Pick two random vertices
+      int f1 = pick_random_vertex();
+      int f2 = pick_random_vertex();
+      if(f1==f2) continue;
+      // Pick two random neighbours
+      int *f1t1 = random_neighbour(f1);
+      int t1 = *f1t1;
+      int *f2t2 = random_neighbour(f2);
+      int t2 = *f2t2;
+      // test simplicity
+      if(t1!=t2 && f1!=t2 && f2!=t1 && !is_edge(f1,t2) && !is_edge(f2,t1)) {
+        // swap
+        *f1t1 = t2;
+        *f2t2 = t1;
+        int *t1f1 = fast_rpl(neigh[t1],f1,f2);
+        int *t2f2 = fast_rpl(neigh[t2],f2,f1);
+        // isolation test
+        if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) {
+          // undo swap
+          *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2;
+        }
+        else swaps++;
+      }
+    }
+    //assert(verify());
+    // test connectivity
+    bool ok = is_connected();
+#ifdef PERFORMANCE_MONITOR
+    if(ok) successes++; else failures++;
+#endif //PERFORMANCE_MONITOR
+    if(ok) {
+      nb_swaps += swaps;
+      // adjust K and T
+      if((K+10)*T>5*a) {
+        double_K/=Q2;
+        K = int(double_K);
+      }
+      else T*=2;
+    }
+    else {
+      restore(save);
+      //assert(verify());
+      double_K*=Q1;
+      K = int(double_K);
+      delete[] Kbuff;
+      Kbuff = new int[K];
+    }
+    delete[] save;
+  }
+#ifdef PERFORMANCE_MONITOR
+    fprintf(stderr,"\n*** Performance Monitor ***\n");
+    fprintf(stderr," - Connectivity test successes : %ld\n",successes);
+    fprintf(stderr," - Connectivity test failures  : %ld\n",failures);
+    fprintf(stderr," - Average window : %ld\n",avg_T/long(successes+failures));
+    fprintf(stderr," - Average isolation test width : %f\n",avg_K/double(successes+failures));
+#endif //PERFORMANCE_MONITOR
+  return nb_swaps;
+}
+
+bool graph_molloy_opt::try_shuffle(int T, int K) {
+    int i;
+    int *Kbuff = NULL;
+    if(K>0) Kbuff = new int[K];
+    bool *visited = new bool[n];
+    for(i=0; i<n; i++) visited[i]=false;
+    int *back=backup();
+    for(i=T; i>0; i--) {
+      // Pick two random vertices
+      int f1 = pick_random_vertex();
+      int f2 = pick_random_vertex();
+      if(f1==f2) continue;
+      // Pick two random neighbours
+      int *f1t1 = random_neighbour(f1);
+      int t1 = *f1t1;
+      int *f2t2 = random_neighbour(f2);
+      int t2 = *f2t2;
+      // test simplicity
+      if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) {
+        // swap
+        *f1t1 = t2;
+        *f2t2 = t1;
+        int *t1f1 = fast_rpl(neigh[t1],f1,f2);
+        int *t2f2 = fast_rpl(neigh[t2],f2,f1);
+        // isolation test
+        if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) {
+          // undo swap
+          *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2;
+        }
+      }
+    }
+    delete[] visited;
+    if(Kbuff != NULL) delete[] Kbuff;
+    bool yo = is_connected();
+    restore(back);
+    delete[] back;
+    return yo;
+}
+
+double graph_molloy_opt::window(int K, double ratio) {
+  int steps = 100;
+  double T = double(a*10);
+  double q2 = 0.1;
+  double q1 = pow(q2,(ratio-1.0)/ratio);
+
+  int failures = 0;
+  int successes = 0;
+  int *Kbuff = new int[K];
+  bool *visited = new bool[n];
+
+  while(successes<10*steps) {
+    int *back=backup();
+    for(int i=int(T); i>0; i--) {
+      // Pick two random vertices
+      int f1 = links[my_random()%a];
+      int f2 = links[my_random()%a];
+      if(f1==f2) continue;
+      // Pick two random neighbours
+      int *f1t1 = neigh[f1]+my_random()%deg[f1];
+      int *f2t2 = neigh[f2]+my_random()%deg[f2];
+      int t1 = *f1t1;
+      int t2 = *f2t2;
+      // test simplicity
+      if(t1!=t2 && f1!=t2 && f2!=t1 && is_edge(f1,t2) && !is_edge(f2,t1)) {
+        // swap
+        *f1t1 = t2;
+        *f2t2 = t1;
+        int *t1f1 = fast_rpl(neigh[t1],f1,f2);
+        int *t2f2 = fast_rpl(neigh[t2],f2,f1);
+        // isolation test
+        if(isolated(f1, K, Kbuff, visited) || isolated(f2, K, Kbuff, visited)) {
+          // undo swap
+          *t1f1 = f1; *t2f2 = f2; *f1t1 = t1; *f2t2 = t2;
+        }
+      }
+    }
+    if(is_connected()) {
+      T *= q1;
+      if(T>double(5*a)) T=double(5*a);
+      successes++;
+      if((successes%steps)==0) {
+        q2 = sqrt(q2);
+        q1 = sqrt(q1);
+      }
+    }
+    else {
+      T*=q2;
+      failures++;
+    }
+    if(VERBOSE()) fprintf(stderr,".");
+    restore(back);
+    delete[] back;
+  }
+  delete[] Kbuff;
+  delete[] visited;
+  if(VERBOSE()) fprintf(stderr,"Failures:%d   Successes:%d\n",failures, successes);
+  return T;
+}
+
+
+double graph_molloy_opt::eval_K(int quality) {
+  double K = 5.0;
+  double avg_K = 1.0;
+  for(int i=quality; i--; ) {
+    int int_K = int(floor(K+0.5));
+    if(try_shuffle(a/(int_K+1),int_K)) {
+      K*=0.8; fprintf(stderr,"+"); }
+    else {
+      K*=1.25; fprintf(stderr,"-"); }
+    if(i<quality/2) avg_K *= K;
+  }
+  return pow(avg_K,1.0/double(quality/2));
+}
+
+
+double graph_molloy_opt::effective_K(int K, int quality) {
+  if(K<3) return 0.0;
+  long sum_K = 0;
+  int *Kbuff = new int[K];
+  bool *visited = new bool[n];
+  int i;
+  for(i=0; i<n; i++) visited[i] = false;
+  for(int i=0; i<quality; i++) {
+//    assert(verify());
+    int f1,f2,t1,t2;
+    int *f1t1, *f2t2;
+    do {
+      // Pick two random vertices
+      do {
+        f1 = pick_random_vertex();
+        f2 = pick_random_vertex();
+      } while(f1==f2);
+      // Pick two random neighbours
+      f1t1 = random_neighbour(f1);
+      t1 = *f1t1;
+      f2t2 = random_neighbour(f2);
+      t2 = *f2t2;
+      // test simplicity
+    }
+    while (t1==t2 || f1==t2 || f2==t1 || is_edge(f1,t2) || is_edge(f2,t1));
+    // swap
+    *f1t1 = t2;
+    *f2t2 = t1;
+    fast_rpl(neigh[t1],f1,f2);
+    fast_rpl(neigh[t2],f2,f1);
+    sum_K += effective_isolated(deg[f1]>deg[t2] ? f1 : t2, K, Kbuff, visited);
+    sum_K += effective_isolated(deg[f2]>deg[t1] ? f2 : t1, K, Kbuff, visited);
+    // undo swap
+    swap_edges(f1,t2,f2,t1);
+//    assert(verify());
+  }
+  delete[] Kbuff;
+  delete[] visited;
+  return double(sum_K)/double(2*quality);
+}
+
+
+//___________________________________________________________________________________
+//*/
+
+
+
+/***** NOT USED ANYMORE (Modif 22/04/2005) ******
+
+int64_t *graph_molloy_opt::vertex_betweenness_usp(bool trivial_paths) {
+  if(VERBOSE()) fprintf(stderr,"Computing vertex betweenness USP...");
+  int i;
+  unsigned char *dist = new unsigned char[n];
+  int *buff = new int[n];
+  int64_t *b = new int64_t[n];
+  int *bb = new int[n];
+  int *dd = new int[max_degree()];
+  for(i=0; i<n; i++) b[i]=0;
+  int progress = 0;
+  for(int v0 = 0; v0<n; v0++) {
+    if(VERBOSE()==VERBOSE_LOTS && v0>(progress*n)/1000) {
+      progress++;
+      fprintf(stderr,"\rComputing vertex betweenness USP : %d.%d%% ",progress/10,progress%10);
+    }
+    int nb_vertices = width_search(dist, buff, v0);
+    int nv = nb_vertices;
+    for(i=0; i<nv; i++) bb[buff[i]]=0;
+    while(--nv) {
+      int v = buff[nv];
+      unsigned char d = prev_dist(dist[v]);
+      int n_father = 0;
+      int *ww = neigh[v];
+      for(int k=deg[v]; k--; ww++) if(dist[*ww]==d) dd[n_father++]=*ww;
+      int w = dd[my_random()%n_father];
+      if(trivial_paths || w!=v0) bb[w] += bb[v]+1;
+      if(trivial_paths) bb[v]++;
+    }
+    for(i=0; i<nb_vertices; i++) b[buff[i]]+=(int64_t)(bb[buff[i]]);
+  }
+  delete[] dist;
+  delete[] buff;
+  delete[] bb;
+  delete[] dd;
+  return b;
+}
+
+int64_t *graph_molloy_opt::vertex_betweenness_rsp(bool trivial_paths) {
+  if(VERBOSE()) fprintf(stderr,"Computing vertex betweenness RSP...");
+  int i;
+  unsigned char *dist = new unsigned char[n];
+  int *buff = new int[n];
+  int64_t *b = new int64_t[n];
+  int *bb = new int[n];
+  int *dd = new int[max_degree()];
+  for(i=0; i<n; i++) b[i]=0;
+  int progress = 0;
+  for(int v0 = 0; v0<n; v0++) {
+    if(VERBOSE()==VERBOSE_LOTS && v0>(progress*n)/1000) {
+      progress++;
+      fprintf(stderr,"\rComputing vertex betweenness RSP : %d.%d%% ",progress/10,progress%10);
+    }
+    int nb_vertices = width_search(dist, buff, v0);
+    int nv = nb_vertices;
+    for(i=0; i<nv; i++) bb[buff[i]]=0;
+    while(--nv) {
+      int v = buff[nv];
+      unsigned char d = prev_dist(dist[v]);
+      int n_father = 0;
+      int *ww = neigh[v];
+      for(int k=deg[v]; k--; ww++) if(dist[*ww]==d) dd[n_father++]=*ww;
+      int to_give = bb[v]+1;
+      if(dd[0]==v0) {
+        if(trivial_paths) bb[v0]+= to_give;
+      }
+      else  {
+        while(n_father>1 && to_give>2*n_father) {
+          int o = rng.binomial(1.0/n_father,to_give);
+          to_give -= o;
+          bb[dd[--n_father]]+=o;
+        }
+        if(n_father==1) bb[dd[0]]+=to_give;
+        else {
+          while(to_give--) bb[dd[my_random()%n_father]]++;
+        }
+      }
+      if(trivial_paths) bb[v]++;
+    }
+    for(i=0; i<nb_vertices; i++) b[buff[i]]+=(int64_t)(bb[buff[i]]);
+  }
+  delete[] dist;
+  delete[] buff;
+  delete[] bb;
+  delete[] dd;
+  return b;
+}
+
+double *graph_molloy_opt::vertex_betweenness_asp(bool trivial_paths) {
+  if(VERBOSE()) fprintf(stderr,"Computing vertex betweenness ASP...");
+  int i;
+  unsigned char *dist = new unsigned char[n];
+  int *buff = new int[n];
+  double *b = new double[n];
+  double *bb = new double[n];
+  int *dd = new int[max_degree()];
+  for(i=0; i<n; i++) b[i]=0.0;
+  int progress = 0;
+  for(int v0 = 0; v0<n; v0++) if(deg[v0]>0) {
+    if(VERBOSE()==VERBOSE_LOTS && v0>(progress*n)/1000) {
+      progress++;
+      fprintf(stderr,"\rComputing vertex betweenness ASP : %d.%d%% ",progress/10,progress%10);
+    }
+    int nb_vertices = width_search(dist, buff, v0);
+    if(!trivial_paths) dist[v0]=2;
+    int nv = nb_vertices;
+    for(i=0; i<nv; i++) bb[buff[i]]=0.0;
+    while(--nv) {
+      int v = buff[nv];
+      unsigned char d = prev_dist(dist[v]);
+      int n_father = 0;
+      int *ww = neigh[v];
+      for(int k=deg[v]; k--; ww++) if(dist[*ww]==d) dd[n_father++]=*ww;
+      if(n_father!=0) {
+        double badd = (bb[v]+1.0)/double(n_father);
+        int *d2 = dd;
+        while(n_father--) bb[*(d2++)]+=badd;
+      }
+      if(trivial_paths) bb[v]+=1.0;
+    }
+    for(i=0; i<nb_vertices; i++) b[buff[i]]+=bb[buff[i]];
+  }
+  delete[] dist;
+  delete[] buff;
+  delete[] bb;
+  delete[] dd;
+  if(VERBOSE()) fprintf(stderr,"done\n");
+  return b;
+}
+
+//*/
+
+} // namespace gengraph
diff --git a/igraph/src/gengraph_mr-connected.cpp b/igraph/src/gengraph_mr-connected.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/gengraph_mr-connected.cpp
@@ -0,0 +1,186 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "gengraph_header.h"
+#include "gengraph_graph_molloy_optimized.h"
+#include "gengraph_graph_molloy_hash.h"
+#include "gengraph_degree_sequence.h"
+#include "gengraph_random.h"
+
+#include "igraph_datatype.h"
+#include "igraph_types.h"
+#include "igraph_error.h"
+
+namespace gengraph {
+
+// return negative number if program should exit
+int parse_options(int &argc, char** &argv);
+
+// options
+static const bool MONITOR_TIME = false;
+static const int  SHUFFLE_TYPE = FINAL_HEURISTICS;
+static const bool RAW_DEGREES  = false;
+static const FILE *Fdeg = stdin;
+
+//_________________________________________________________________________
+// int main(int argc, char** argv) {
+
+//   // options
+//   SET_VERBOSE(VERBOSE_NONE);
+//   if(parse_options(argc, argv) < 0) return -1;
+
+//   //Read degree distribution
+//   degree_sequence dd(Fdeg, !RAW_DEGREES);
+
+//   //Allocate memory
+//   if(VERBOSE()) fprintf(stderr,"Allocate memory for graph...");
+//   graph_molloy_opt g(dd);
+//   dd.~degree_sequence();
+//   //Realize degree sequence
+//   if(VERBOSE()) fprintf(stderr,"done\nRealize degree sequence...");
+//   bool FAILED = !g.havelhakimi();
+//   if(VERBOSE()) fprintf(stderr," %s\n", FAILED ? "Failed" : "Success");
+//   if(FAILED) return 2;
+//   //Merge connected components together
+//   if(VERBOSE()) fprintf(stderr,"Connecting...");
+//   FAILED = !g.make_connected();
+//   if(VERBOSE()) fprintf(stderr," %s\n", FAILED ? "Failed" : "Success");
+//   if(FAILED) return 3;
+//   //Convert graph_molloy_opt to graph_molloy_hash
+//   if(VERBOSE()) fprintf(stderr,"Convert adjacency lists into hash tables...");
+//   int *hc = g.hard_copy();
+//   g.~graph_molloy_opt();
+//   graph_molloy_hash gh(hc);
+//   delete[] hc;
+//   if(VERBOSE()) fprintf(stderr,"Done\n");
+//   //Shuffle
+//   gh.shuffle(5*gh.nbarcs(), SHUFFLE_TYPE);
+//   //Output
+//   gh.print();
+//   if(MONITOR_TIME) {
+//     double t = double(clock()) / double(CLOCKS_PER_SEC);
+//     fprintf(stderr,"Time used: %f\n", t);
+//   }
+//   return 0;
+// }
+
+//_________________________________________________________________________
+// int parse_options(int &argc, char** &argv) {
+// bool HELP = false;
+// int argc0 = argc;
+// argc = 1;
+// for(int a=1; a<argc0; a++) {
+//   if(strcmp(argv[a],"-v")==0) SET_VERBOSE(VERBOSE_SOME);
+//   else if(strcmp(argv[a],"-vv")==0) SET_VERBOSE(VERBOSE_LOTS);
+//   else if(strcmp(argv[a],"-s")==0) my_srandom(0);
+//   else if(strcmp(argv[a],"-?")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"/?")==0) HELP = true;
+//   else if(strcmp(argv[a],"-t")==0) MONITOR_TIME = true;
+//   else if(strcmp(argv[a],"-g")==0) SHUFFLE_TYPE = GKAN_HEURISTICS;
+//   else if(strcmp(argv[a],"-b")==0) SHUFFLE_TYPE = BRUTE_FORCE_HEURISTICS;
+//   else if(strcmp(argv[a],"-f")==0) SHUFFLE_TYPE = FAB_HEURISTICS;
+//   else if(strcmp(argv[a],"-o")==0) SHUFFLE_TYPE = OPTIMAL_HEURISTICS;
+//   else if(strcmp(argv[a],"-raw")==0) RAW_DEGREES=true;
+//   else // No option present
+//     argv[argc++] = argv[a];
+// }
+// if(!HELP && argc==2) {
+//   Fdeg = fopen(argv[1],"r");
+//   if(Fdeg==NULL) {
+//     fprintf(stderr,"Error : couldn't open file \"%s\" for reading\n",argv[1]);
+//     return -1;
+//   }
+//   argv[1]=argv[0];
+//   argv++;
+//   argc--;
+// }
+// if(HELP || argc!=1) {
+//   fprintf(stderr,"Usage : %s [options] [file containing degree distribution]\n",argv[0]);
+//   fprintf(stderr," -> %s returns a graph in its standard output\n",argv[0]);
+//   fprintf(stderr,"    If no file is given, %s reads its standard input\n",argv[0]);
+//   fprintf(stderr,"    [-v] and [-vv] options causes extra verbose.\n");
+//   fprintf(stderr,"    [-g] option uses the Gkantsidis heuristics.\n");
+//   fprintf(stderr,"    [-b] option uses the Brute Force heuristics.\n");
+//   fprintf(stderr,"    [-f] option uses the Modified Gkantsidis heuristics.\n");
+//   fprintf(stderr,"    [-o] option uses the Optimal Gkantsidis heuristics.\n");
+//   fprintf(stderr,"    [-t] option monitors computation time\n");
+//   fprintf(stderr,"    [-s] does a srandom(0) to get a constant random graph\n");
+//   fprintf(stderr,"    [-raw] is to take raw degree sequences as input\n");
+//   return -1;
+// }
+//   return 0;
+// }
+
+
+} // namespace gengraph
+
+using namespace gengraph;
+
+extern "C" {
+
+    int igraph_degree_sequence_game_vl(igraph_t *graph,
+                                       const igraph_vector_t *out_seq,
+                                       const igraph_vector_t *in_seq) {
+        long int sum = igraph_vector_sum(out_seq);
+        if (sum % 2 != 0) {
+            IGRAPH_ERROR("Sum of degrees should be even", IGRAPH_EINVAL);
+        }
+
+        RNG_BEGIN();
+
+        if (in_seq && igraph_vector_size(in_seq) != 0) {
+            RNG_END();
+            IGRAPH_ERROR("This generator works with undirected graphs only", IGRAPH_EINVAL);
+        }
+
+        degree_sequence *dd = new degree_sequence(out_seq);
+
+        graph_molloy_opt *g = new graph_molloy_opt(*dd);
+        delete dd;
+
+        if (!g->havelhakimi()) {
+            delete g;
+            RNG_END();
+            IGRAPH_ERROR("Cannot realize the given degree sequence as an undirected, simple graph",
+                         IGRAPH_EINVAL);
+        }
+
+        if (!g->make_connected()) {
+            delete g;
+            RNG_END();
+            IGRAPH_ERROR("Cannot make a connected graph from the given degree sequence",
+                         IGRAPH_EINVAL);
+        }
+
+        int *hc = g->hard_copy();
+        delete g;
+        graph_molloy_hash *gh = new graph_molloy_hash(hc);
+        delete [] hc;
+
+        gh->shuffle(5 * gh->nbarcs(), 100 * gh->nbarcs(), SHUFFLE_TYPE);
+
+        IGRAPH_CHECK(gh->print(graph));
+        delete gh;
+
+        RNG_END();
+
+        return 0;
+    }
+
+}
diff --git a/igraph/src/gengraph_powerlaw.cpp b/igraph/src/gengraph_powerlaw.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/gengraph_powerlaw.cpp
@@ -0,0 +1,270 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+// Pascalou ...
+#ifdef pascalou
+    #define my_random() random()
+    #define MY_RAND_MAX 0x7FFFFFFF
+#else
+    #include "gengraph_definitions.h"
+#endif
+
+#include "gengraph_powerlaw.h"
+#include <cstdio>
+#include <cmath>
+#include <cassert>
+
+#include "igraph_error.h"
+
+namespace gengraph {
+
+// Destructor
+powerlaw::~powerlaw() {
+    delete[] table;
+    if (dt != NULL) {
+        delete[] dt;
+    }
+}
+
+// Constructor
+powerlaw::powerlaw(double _alpha, int _mini, int _maxi) {
+    alpha = _alpha;
+    mini = _mini;
+    maxi = _maxi;
+    if (alpha <= 2.0 && maxi < 0)
+        igraph_warningf("powerlaw exponent %f should be > 2 when no "
+                        "Maximum is specified", __FILE__, __LINE__, -1, alpha);
+    if (alpha <= 1.0 && maxi >= 0)
+        igraph_warningf("powerlaw exponent %f should be > 1", __FILE__, __LINE__,
+                        -1, alpha);
+    if (maxi >= 0 && mini > maxi)
+        igraph_warningf("powerlaw max %d should be greater than min %d",
+                        __FILE__, __LINE__, -1, maxi, mini);
+    table = new int[POWERLAW_TABLE];
+    tabulated = 0;
+    dt = NULL;
+}
+
+// Sample
+int powerlaw::sample() {
+    if (proba_big != 0 && test_proba(proba_big)) {
+        return int(floor(0.5 + big_sample(random_float())));
+    }
+    int r = my_random();
+    // table[] contains integer from MY_RAND_MAX downto 0, in blocks. Search block...
+    if (r > (MY_RAND_MAX >> max_dt)) {
+        return mini;
+    }
+    int k = 0;
+    while (k < max_dt) {
+        r <<= 1;
+        r += random_bit();
+        k++;
+    };
+    int a = 0;
+    int b;
+    while ((b = dt[k++]) < 0 || r < table[b]) {
+        if (b >= 0) {
+            a = b + 1;
+            if (a == tabulated - 1) {
+                break;
+            }
+            r <<= 1;
+            r += random_bit();
+        }
+    }
+
+    // Now that we found the good block, run a dichotomy on this block [a,b]
+    while (a < b) {
+        int c = (a + b) / 2;
+        if (r < table[c]) {
+            a = c + 1;
+        } else {
+            b = c;
+        }
+    }
+    return mini + a;
+}
+
+// Proba
+double powerlaw::proba(int k) {
+    if (k < mini || (maxi >= 0 && k > maxi)) {
+        return 0.0;
+    }
+    if (k >= mini + tabulated) {
+        return proba_big * (big_inv_sample(double(k) - 0.5) - big_inv_sample(double(k) + 0.5));
+    } else {
+        double div = table_mul;
+        int prev_pos_in_table = k - mini - 1;
+        if (prev_pos_in_table < 0) {
+            return (double(MY_RAND_MAX) + 1.0 - double(table[0] >> max_dt)) * div;
+        }
+        // what block are we in ?
+        int k = 0;
+        while (k < max_dt) {
+            div *= 0.5;
+            k++;
+        };
+        while (dt[k] < 0 || dt[k] < prev_pos_in_table) {
+            k++;
+            div *= 0.5;
+        };
+        double prob2 = double(table[prev_pos_in_table + 1]);
+        if (dt[k] == prev_pos_in_table) do {
+                prob2 *= 0.5;
+            } while (dt[++k] < 0);
+        return (double(table[prev_pos_in_table]) - prob2) * div;
+    }
+}
+
+// Relative Error
+double powerlaw::error() {
+    return 1.0 / (double(tabulated) * double(tabulated));
+}
+
+// Mean
+double powerlaw::mean() {
+    double sum = 0.0;
+    for (int i = mini + tabulated; --i >= mini; ) {
+        sum += double(i) * proba(i);
+    }
+    // add proba_big * integral(big_sample(t),t=0..1)
+    if (proba_big != 0) {
+        sum += proba_big * ((pow(_a + _b, _exp + 1.0) - pow(_b, _exp + 1.0)) / (_a * (_exp + 1.0)) + double(mini) - offset - sum);
+    }
+    return sum;
+}
+
+// Median. Returns integer Med such that P(X<=Med) >= 1/2
+int powerlaw::median() {
+    if (proba_big > 0.5) {
+        return int(floor(0.5 + big_sample(1.0 - 0.5 / proba_big)));
+    }
+    double sum = 0.0;
+    int i = mini;
+    while (sum < 0.5) {
+        sum += proba(i++);
+    }
+    return i - 1;
+}
+
+void powerlaw::init_to_offset(double _offset, int _tabulated) {
+    offset = _offset;
+    tabulated = _tabulated;
+    if (maxi >= 0 && tabulated > maxi - mini) {
+        tabulated = maxi - mini + 1;
+    }
+    double sum = 0.0;
+    double item = double(tabulated) + offset;
+    // Compute sum of tabulated probabilities
+    for (int i = tabulated; i--; ) {
+        sum += pow(item -= 1.0, -alpha);
+    }
+    // Compute others parameters : proba_big, table_mul, _a, _b, _exp
+    if (maxi > 0 && maxi <= mini + tabulated - 1) {
+        proba_big = 0;
+        table_mul = inv_RANDMAX;
+    } else {
+        if (maxi < 0) {
+            _b = 0.0;
+        } else {
+            _b = pow(double(maxi - mini) + 0.5 + offset, 1.0 - alpha);
+        }
+        _a = pow(double(tabulated) - 0.5 + offset, 1.0 - alpha) - _b;
+        _exp = 1.0 / (1.0 - alpha);
+        double sum_big = _a * (-_exp);
+        proba_big = sum_big / (sum + sum_big);
+        table_mul = inv_RANDMAX * sum / (sum + sum_big);
+    }
+    // How many delimiters will be necessary for the table ?
+    max_dt = max(0, int(floor(alpha * log(double(tabulated)) / log(2.0))) - 6);
+    if (dt != NULL) {
+        delete[] dt;
+    }
+    dt = new int[max_dt + 1];
+    // Create table as decreasing integers from MY_RAND_MAX+1 (in virtual position -1) down to 0
+    // Every time the index crosses a delimiter, numbers get doubled.
+    double ssum = 0;
+    double mul = (double(MY_RAND_MAX) + 1.0) * pow(2.0, max_dt) / sum;
+    item = double(tabulated) + offset;
+    int k = max_dt;
+    dt[k--] = tabulated - 1;
+    for (int i = tabulated; --i > 0; ) {
+        table[i] = int(floor(0.5 + ssum));
+        ssum += mul * pow(item -= 1.0, -alpha);
+        if (ssum > double(MY_RAND_MAX / 2) && k >= 0) {
+            while ((ssum *= 0.5) > double(MY_RAND_MAX / 2)) {
+                mul *= 0.5;
+                dt[k--] = -1;
+            };
+            mul *= 0.5; dt[k--] = i - 1;
+        }
+    }
+    table[0] = int(floor(0.5 + ssum));
+    max_dt = k + 1;
+}
+
+void powerlaw::adjust_offset_mean(double _mean, double err, double factor) {
+    // Set two bounds for offset
+    double ol = offset;
+    double oh = offset;
+    if (mean() < _mean) {
+        do {
+            ol = oh;
+            oh *= factor;
+            init_to_offset(oh, tabulated);
+        } while (mean() < _mean);
+    } else {
+        do {
+            oh = ol;
+            ol /= factor;
+            init_to_offset(ol, tabulated);
+        } while (mean() > _mean);
+    }
+    // Now, dichotomy
+    while (fabs(oh - ol) > err * ol) {
+        double oc = sqrt(oh * ol);
+        init_to_offset(oc, tabulated);
+        if (mean() < _mean) {
+            ol = oc;
+        } else {
+            oh = oc;
+        }
+    }
+    init_to_offset(sqrt(ol * oh), tabulated);
+}
+
+double powerlaw::init_to_mean(double _mean) {
+    if (maxi >= 0 && _mean >= 0.5 * double((mini + maxi))) {
+        igraph_errorf("Fatal error in powerlaw::init_to_mean(%f): "
+                      "Mean must be in ]min, (min+max)/2[ = ]%d, %d[",
+                      __FILE__, __LINE__, IGRAPH_EINVAL,
+                      _mean, mini, (mini + maxi) / 2);
+        return (-1.0);
+    }
+    init_to_offset(_mean - double(mini), 100);
+    adjust_offset_mean(_mean, 0.01, 2);
+    init_to_offset(offset, POWERLAW_TABLE);
+    double eps = 1.0 / (double(POWERLAW_TABLE));
+    adjust_offset_mean(_mean, eps * eps, 1.01);
+    return offset;
+}
+
+} // namespace gengraph
diff --git a/igraph/src/gengraph_random.cpp b/igraph/src/gengraph_random.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/gengraph_random.cpp
@@ -0,0 +1,278 @@
+/*
+ *
+ * gengraph - generation of random simple connected graphs with prescribed
+ *            degree sequence
+ *
+ * Copyright (C) 2006  Fabien Viger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#define RNG_C
+
+#ifdef RCSID
+    static const char rcsid[] = "$Id: random.cpp,v 1.15 2003/05/14 03:04:45 wilder Exp wilder $";
+#endif
+
+//________________________________________________________________________
+// See the header file random.h for a description of the contents of this
+// file as well as references and credits.
+
+#include <cmath>
+#include "gengraph_random.h"
+
+using namespace std;
+using namespace KW_RNG;
+
+//________________________________________________________________________
+// RNG::RNOR generates normal variates with rejection.
+// nfix() generates variates after rejection in RNOR.
+// Despite rejection, this method is much faster than Box-Muller.
+
+// double RNG::nfix(slong h, ulong i)
+// {
+//   const double r = 3.442620f;    // The starting of the right tail
+//   static double x, y;
+
+//   for(;;) {
+//     x = h * wn[i];
+
+//     // If i == 0, handle the base strip
+//     if (i==0){
+//       do {
+//  x = -log(rand_open01()) * 0.2904764;   // .2904764 is 1/r
+//  y = -log(rand_open01());
+//       } while (y + y < x * x);
+//       return ((h > 0) ? r + x : -r - x);
+//     }
+
+//     // If i > 0, handle the wedges of other strips
+//     if (fn[i] + rand_open01() * (fn[i - 1] - fn[i]) < exp(-.5 * x * x) )
+//       return x;
+
+//     // start all over
+//     h = rand_int32();
+//     i = h & 127;
+//     if ((ulong) abs((sint) h) < kn[i])
+//       return (h * wn[i]);
+//   }
+
+// } // RNG::nfix
+
+// // __________________________________________________________________________
+// // RNG::RNOR generates exponential variates with rejection.
+// // efix() generates variates after rejection in REXP.
+
+// double RNG::efix(ulong j, ulong i)
+// {
+//   double x;
+//   for (;;)
+//   {
+//     if (i == 0)
+//       return (7.69711 - log(rand_open01()));
+
+//     x = j * we[i];
+//     if (fe[i] + rand_open01() * (fe[i - 1] - fe[i]) < exp(-x))
+//       return (x);
+
+//     j = rand_int32();
+//     i = (j & 255);
+//     if (j < ke[i])
+//       return (j * we[i]);
+//   }
+
+// } // RNG::efix
+
+// // __________________________________________________________________________
+// // This procedure creates the tables used by RNOR and REXP
+
+// void RNG::zigset()
+// {
+//   const double m1 = 2147483648.0; // 2^31
+//   const double m2 = 4294967296.0; // 2^32
+
+//   const double vn = 9.91256303526217e-3;
+//   const double ve = 3.949659822581572e-3;
+
+//   double dn = 3.442619855899, tn = dn;
+//   double de = 7.697117470131487, te = de;
+
+//   int i;
+
+//   // Set up tables for RNOR
+//   double q = vn / exp(-.5 * dn * dn);
+//   kn[0] = (ulong) ((dn / q) * m1);
+//   kn[1] = 0;
+//   wn[0] = q / m1;
+//   wn[127] = dn / m1;
+//   fn[0]=1.;
+//   fn[127] = exp(-.5 * dn * dn);
+//   for(i = 126; i >= 1; i--)
+//   {
+//     dn = sqrt(-2 * log(vn / dn + exp(-.5 * dn * dn)));
+//     kn[i + 1] = (ulong) ((dn / tn) * m1);
+//     tn = dn;
+//     fn[i] = exp(-.5 * dn * dn);
+//     wn[i] = dn / m1;
+//   }
+
+//   // Set up tables for REXP
+//   q = ve / exp(-de);
+//   ke[0] = (ulong) ((de / q) * m2);
+//   ke[1] = 0;
+//   we[0] = q / m2;
+//   we[255] = de / m2;
+//   fe[0] = 1.;
+//   fe[255] = exp(-de);
+//   for (i = 254; i >= 1; i--)
+//   {
+//     de = -log(ve / de + exp(-de));
+//     ke[i+1] = (ulong) ((de / te) * m2);
+//     te = de;
+//     fe[i] = exp(-de);
+//     we[i] = de / m2;
+//   }
+
+// } // RNG::zigset
+
+// // __________________________________________________________________________
+// // Generate a gamma variate with parameters 'shape' and 'scale'
+
+// double RNG::gamma(double shape, double scale)
+// {
+//   if (shape < 1)
+//     return gamma(shape + 1, scale) * pow(rand_open01(), 1.0 / shape);
+
+//   const double d = shape - 1.0 / 3.0;
+//   const double c = 1.0 / sqrt(9.0 * d);
+//   double x, v, u;
+//   for (;;) {
+//     do {
+//       x = RNOR();
+//       v = 1.0 + c * x;
+//     } while (v <= 0.0);
+//     v = v * v * v;
+//     u = rand_open01();
+//     if (u < 1.0 - 0.0331 * x * x * x * x)
+//       return (d * v / scale);
+//     if (log(u) < 0.5 * x * x + d * (1.0 - v + log(v)))
+//       return (d * v / scale);
+//   }
+
+// } // RNG::gamma
+
+// // __________________________________________________________________________
+// // gammalog returns the logarithm of the gamma function.  From Numerical
+// // Recipes.
+
+// double gammalog(double xx)
+// {
+//   static double cof[6]={
+//     76.18009172947146, -86.50532032941677, 24.01409824083091,
+//     -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5};
+
+//   double x = xx;
+//   double y = xx;
+//   double tmp = x + 5.5;
+//   tmp -= (x + 0.5) * log(tmp);
+//   double ser=1.000000000190015;
+//   for (int j=0; j<=5; j++)
+//     ser += cof[j] / ++y;
+//   return -tmp + log(2.5066282746310005 * ser / x);
+// }
+
+// // __________________________________________________________________________
+// // Generate a Poisson variate
+// // This is essentially the algorithm from Numerical Recipes
+
+// double RNG::poisson(double lambda)
+// {
+//   static double sq, alxm, g, oldm = -1.0;
+//   double em, t, y;
+
+//   if (lambda < 12.0) {
+//     if (lambda != oldm) {
+//       oldm = lambda;
+//       g = exp(-lambda);
+//     }
+//     em = -1;
+//     t = 1.0;
+//     do {
+//       ++em;
+//       t *= rand_open01();
+//     } while (t > g);
+//   } else {
+//     if (lambda != oldm) {
+//       oldm = lambda;
+//       sq = sqrt(2.0 * lambda);
+//       alxm = log(lambda);
+//       g = lambda * alxm - gammalog(lambda + 1.0);
+//     }
+//     do {
+//       do {
+//  y = tan(PI * rand_open01());
+//  em = sq * y + lambda;
+//       } while (em < 0.0);
+//       em = floor(em);
+//       t = 0.9 * (1.0 + y * y) * exp(em * alxm - gammalog(em + 1.0)-g);
+//     } while (rand_open01() > t);
+//   }
+//   return em;
+
+// } // RNG::poisson
+
+// // __________________________________________________________________________
+// // Generate a binomial variate
+// // This is essentially the algorithm from Numerical Recipes
+
+// int RNG::binomial(double pp, int n)
+// {
+//   if(n==0) return 0;
+//   if(pp==0.0) return 0;
+//   if(pp==1.0) return n;
+//   double p = (pp<0.5 ? pp : 1.0-pp);
+//   double am = n*p;
+//   int bnl = 0;
+//   if(n<25) {
+//     for(int j=n; j--; ) if(rand_closed01()<p) ++bnl;
+//   }
+//   else if(am<1.0) {
+//     double g = exp(-am);
+//     double t = 1.0;
+//     for (; bnl<n; bnl++) if((t*=rand_closed01())<g) break;
+//   }
+//   else {
+//     double en = n;
+//     double oldg = gammalog(en + 1.0);
+//     double pc = 1.0 - p;
+//     double sq = sqrt(2.0 * am * pc);
+//     double y, em, t;
+//     do {
+//       do {
+//         double angle = PI * rand_halfclosed01();
+//          y = tan(angle);
+//         em = sq * y + am;
+//       } while (em < 0.0 || em >= en + 1.0);
+//       em = floor(em);
+//       t = 1.2 * sq * (1 + y * y) * exp(oldg - gammalog(em + 1.0) -
+//           gammalog(en - em + 1.0) + em * log(p) + (en - em) * log(pc));
+//     } while (rand_closed01() > t);
+//     bnl = int(em);
+//   }
+//   if (p!=pp) bnl=n-bnl;
+//   return bnl;
+// } // RNG::binomial
+
+// __________________________________________________________________________
+// rng.C
+
diff --git a/igraph/src/getenv_.c b/igraph/src/getenv_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/getenv_.c
@@ -0,0 +1,62 @@
+#include "f2c.h"
+#undef abs
+#ifdef KR_headers
+extern char *F77_aloc(), *getenv();
+#else
+#include <stdlib.h>
+#include <string.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern char *F77_aloc(ftnlen, const char*);
+#endif
+
+/*
+ * getenv - f77 subroutine to return environment variables
+ *
+ * called by:
+ *	call getenv (ENV_NAME, char_var)
+ * where:
+ *	ENV_NAME is the name of an environment variable
+ *	char_var is a character variable which will receive
+ *		the current value of ENV_NAME, or all blanks
+ *		if ENV_NAME is not defined
+ */
+
+#ifdef KR_headers
+ VOID
+getenv_(fname, value, flen, vlen) char *value, *fname; ftnlen vlen, flen;
+#else
+ void
+getenv_(char *fname, char *value, ftnlen flen, ftnlen vlen)
+#endif
+{
+	char buf[256], *ep, *fp;
+	integer i;
+
+	if (flen <= 0)
+		goto add_blanks;
+	for(i = 0; i < sizeof(buf); i++) {
+		if (i == flen || (buf[i] = fname[i]) == ' ') {
+			buf[i] = 0;
+			ep = getenv(buf);
+			goto have_ep;
+			}
+		}
+	while(i < flen && fname[i] != ' ')
+		i++;
+	strncpy(fp = F77_aloc(i+1, "getenv_"), fname, (int)i);
+	fp[i] = 0;
+	ep = getenv(fp);
+	free(fp);
+ have_ep:
+	if (ep)
+		while(*ep && vlen-- > 0)
+			*value++ = *ep++;
+ add_blanks:
+	while(vlen-- > 0)
+		*value++ = ' ';
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/glet.c b/igraph/src/glet.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/glet.c
@@ -0,0 +1,870 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_graphlets.h"
+#include "igraph_memory.h"
+#include "igraph_constructors.h"
+#include "igraph_cliques.h"
+#include "igraph_structural.h"
+#include "igraph_qsort.h"
+#include "igraph_conversion.h"
+
+/**
+ * \section graphlets_intro Introduction
+ *
+ * <para>
+ * Graphlet decomposition models a weighted undirected graph
+ * via the union of potentially overlapping dense social groups.
+ * This is done by a two-step algorithm. In the first step, a candidate
+ * set of groups (a candidate basis) is created by finding cliques
+ * in the thresholded input graph. In the second step,
+ * the graph is projected onto the candidate basis, resulting in a
+ * weight coefficient for each clique in the candidate basis.
+ * </para>
+ *
+ * <para>
+ * For more information on graphlet decomposition, see
+ * Hossein Azari Soufiani and Edoardo M Airoldi: "Graphlet decomposition of a weighted network",
+ * https://arxiv.org/abs/1203.2821 and http://proceedings.mlr.press/v22/azari12/azari12.pdf
+ * </para>
+ *
+ * <para>
+ * igraph contains three functions for performing the graphlet
+ * decomponsition of a graph. The first is \ref igraph_graphlets(), which
+ * performs both steps of the method and returns a list of subgraphs
+ * with their corresponding weights. The other two functions
+ * correspond to the first and second steps of the algorithm, and they are
+ * useful if the user wishes to perform them individually:
+ * \ref igraph_graphlets_candidate_basis() and
+ * \ref igraph_graphlets_project().
+ * </para>
+ *
+ * <para>
+ * <remark>
+ * Note: The term "graphlet" is used for several unrelated concepts
+ * in the literature. If you are looking to count induced subgraphs, see
+ * \ref igraph_motifs_randesu() and \ref igraph_subisomorphic_lad().
+ * </remark>
+ * </para>
+ */
+
+typedef struct {
+    igraph_vector_int_t *resultids;
+    igraph_t *result;
+    igraph_vector_t *resultweights;
+    int nc;
+} igraph_i_subclique_next_free_t;
+
+void igraph_i_subclique_next_free(void *ptr) {
+    igraph_i_subclique_next_free_t *data = ptr;
+    int i;
+    if (data->resultids) {
+        for (i = 0; i < data->nc; i++) {
+            if (data->resultids + i) {
+                igraph_vector_int_destroy(data->resultids + i);
+            }
+        }
+        igraph_Free(data->resultids);
+    }
+    if (data->result) {
+        for (i = 0; i < data->nc; i++) {
+            if (data->result + i) {
+                igraph_destroy(data->result + i);
+            }
+        }
+        igraph_Free(data->result);
+    }
+    if (data->resultweights) {
+        for (i = 0; i < data->nc; i++) {
+            if (data->resultweights + i) {
+                igraph_vector_destroy(data->resultweights + i);
+            }
+        }
+        igraph_Free(data->resultweights);
+    }
+}
+
+/**
+ * \function igraph_i_subclique_next
+ * Calculate subcliques of the cliques found at the previous level
+ *
+ * \param graph Input graph.
+ * \param weight Edge weights.
+ * \param ids The ids of the vertices in the input graph.
+ * \param cliques A list of vectors, vertex ids for cliques.
+ * \param result The result is stored here, a list of graphs is stored
+ *        here.
+ * \param resultids The ids of the vertices in the result graphs is
+ *        stored here.
+ * \param clique_thr The thresholds for the cliques are stored here,
+ *        if not a null pointer.
+ * \param next_thr The next thresholds for the cliques are stored
+ *        here, if not a null pointer.
+ *
+ */
+
+int igraph_i_subclique_next(const igraph_t *graph,
+                            const igraph_vector_t *weights,
+                            const igraph_vector_int_t *ids,
+                            const igraph_vector_ptr_t *cliques,
+                            igraph_t **result,
+                            igraph_vector_t **resultweights,
+                            igraph_vector_int_t **resultids,
+                            igraph_vector_t *clique_thr,
+                            igraph_vector_t *next_thr) {
+
+    /* The input is a set of cliques, that were found at a previous level.
+       For each clique, we calculate the next threshold, drop the isolate
+       vertices, and create a new graph from them. */
+
+    igraph_vector_int_t mark, map;
+    igraph_vector_int_t edges;
+    igraph_vector_t neis, newedges;
+    igraph_integer_t c, nc = igraph_vector_ptr_size(cliques);
+    igraph_integer_t no_of_nodes = igraph_vcount(graph);
+    igraph_integer_t no_of_edges = igraph_ecount(graph);
+    igraph_i_subclique_next_free_t freedata = { 0, 0, 0, nc };
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_int_size(ids) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid length of ID vector", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_FINALLY(igraph_i_subclique_next_free, &freedata);
+    *resultids = igraph_Calloc(nc, igraph_vector_int_t);
+    if (!*resultids) {
+        IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM);
+    }
+    freedata.resultids = *resultids;
+    *resultweights = igraph_Calloc(nc, igraph_vector_t);
+    if (!*resultweights) {
+        IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM);
+    }
+    freedata.resultweights = *resultweights;
+    *result = igraph_Calloc(nc, igraph_t);
+    if (!*result) {
+        IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM);
+    }
+    freedata.result = *result;
+
+    igraph_vector_init(&newedges, 100);
+    IGRAPH_FINALLY(igraph_vector_destroy, &newedges);
+    igraph_vector_int_init(&mark, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_destroy, &mark);
+    igraph_vector_int_init(&map, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_destroy, &map);
+    igraph_vector_int_init(&edges, 100);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &edges);
+    igraph_vector_init(&neis, 10);
+    IGRAPH_FINALLY(igraph_vector_destroy, &neis);
+
+    if (clique_thr) {
+        igraph_vector_resize(clique_thr, nc);
+    }
+    if (next_thr)   {
+        igraph_vector_resize(next_thr,   nc);
+    }
+
+    /* Iterate over all cliques. We will create graphs for all
+       subgraphs defined by the cliques. */
+
+    for (c = 0; c < nc; c++) {
+        igraph_vector_t *clique = VECTOR(*cliques)[c];
+        igraph_real_t minweight = IGRAPH_INFINITY, nextweight = IGRAPH_INFINITY;
+        igraph_integer_t e, v, clsize = igraph_vector_size(clique);
+        igraph_integer_t noe, nov = 0;
+        igraph_vector_int_t *newids = (*resultids) + c;
+        igraph_vector_t *neww = (*resultweights) + c;
+        igraph_t *newgraph = (*result) + c;
+        igraph_vector_int_clear(&edges);
+        igraph_vector_clear(&newedges);
+
+        /* --------------------------------------------------- */
+
+        /* Iterate over the vertices of a clique and find the
+           edges within the clique, put them in a list.
+           At the same time, search for the minimum edge weight within
+           the clique and the next edge weight if any. */
+
+        for (v = 0; v < clsize; v++) {
+            igraph_integer_t i, neilen, node = VECTOR(*clique)[v];
+            igraph_incident(graph, &neis, node, IGRAPH_ALL);
+            neilen = igraph_vector_size(&neis);
+            VECTOR(mark)[node] = c + 1;
+            for (i = 0; i < neilen; i++) {
+                igraph_integer_t edge = VECTOR(neis)[i];
+                igraph_integer_t nei = IGRAPH_OTHER(graph, edge, node);
+                if (VECTOR(mark)[nei] == c + 1) {
+                    igraph_real_t w = VECTOR(*weights)[edge];
+                    igraph_vector_int_push_back(&edges, edge);
+                    if (w < minweight) {
+                        nextweight = minweight;
+                        minweight = w;
+                    } else if (w > minweight && w < nextweight) {
+                        nextweight = w;
+                    }
+                }
+            }
+        } /* v < clsize */
+
+        /* --------------------------------------------------- */
+
+        /* OK, we have stored the edges and found the weight of
+           the clique and the next weight to consider */
+
+        if (clique_thr) {
+            VECTOR(*clique_thr)[c] = minweight;
+        }
+        if (next_thr)   {
+            VECTOR(*next_thr  )[c] = nextweight;
+        }
+
+        /* --------------------------------------------------- */
+
+        /* Now we create the subgraph from the edges above the next
+           threshold, and their incident vertices. */
+
+        igraph_vector_int_init(newids, 0);
+        igraph_vector_init(neww, 0);
+
+        /* We use mark[] to denote the vertices already mapped to
+           the new graph. If this is -(c+1), then the vertex was
+           mapped, otherwise it was not. The mapping itself is in
+           map[]. */
+
+        noe = igraph_vector_int_size(&edges);
+        for (e = 0; e < noe; e++) {
+            igraph_integer_t edge = VECTOR(edges)[e];
+            igraph_integer_t from, to;
+            igraph_real_t w = VECTOR(*weights)[edge];
+            igraph_edge(graph, edge, &from, &to);
+            if (w >= nextweight) {
+                if (VECTOR(mark)[from] == c + 1) {
+                    VECTOR(map)[from] = nov++;
+                    VECTOR(mark)[from] = -(c + 1);
+                    igraph_vector_int_push_back(newids, VECTOR(*ids)[from]);
+                }
+                if (VECTOR(mark)[to] == c + 1) {
+                    VECTOR(map)[to] = nov++;
+                    VECTOR(mark)[to] = -(c + 1);
+                    igraph_vector_int_push_back(newids, VECTOR(*ids)[to]);
+                }
+                igraph_vector_push_back(neww, w);
+                igraph_vector_push_back(&newedges, VECTOR(map)[from]);
+                igraph_vector_push_back(&newedges, VECTOR(map)[to]);
+            }
+        }
+
+        igraph_create(newgraph, &newedges, nov, IGRAPH_UNDIRECTED);
+
+        /* --------------------------------------------------- */
+
+    } /* c < nc */
+
+    igraph_vector_destroy(&neis);
+    igraph_vector_int_destroy(&edges);
+    igraph_vector_int_destroy(&mark);
+    igraph_vector_int_destroy(&map);
+    igraph_vector_destroy(&newedges);
+    IGRAPH_FINALLY_CLEAN(6);  /* + freedata */
+
+    return 0;
+}
+
+void igraph_i_graphlets_destroy_vectorlist(igraph_vector_ptr_t *vl) {
+    int i, n = igraph_vector_ptr_size(vl);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *v = (igraph_vector_t*) VECTOR(*vl)[i];
+        if (v) {
+            igraph_vector_destroy(v);
+        }
+    }
+    igraph_vector_ptr_destroy(vl);
+}
+
+int igraph_i_graphlets(const igraph_t *graph,
+                       const igraph_vector_t *weights,
+                       igraph_vector_ptr_t *cliques,
+                       igraph_vector_t *thresholds,
+                       const igraph_vector_int_t *ids,
+                       igraph_real_t startthr) {
+
+    /* This version is different from the main function, and is
+       appropriate to use in recursive calls, because it _adds_ the
+       results to 'cliques' and 'thresholds' and uses the supplied
+       'startthr' */
+
+    igraph_vector_ptr_t mycliques;
+    int no_of_edges = igraph_ecount(graph);
+    igraph_vector_t subv;
+    igraph_t subg;
+    int i, nographs, nocliques;
+    igraph_t *newgraphs = 0;
+    igraph_vector_t *newweights = 0;
+    igraph_vector_int_t *newids = 0;
+    igraph_vector_t clique_thr, next_thr;
+    igraph_i_subclique_next_free_t freedata = { 0, 0, 0, 0 };
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&mycliques, 0));
+    IGRAPH_FINALLY(igraph_i_graphlets_destroy_vectorlist, &mycliques);
+    IGRAPH_VECTOR_INIT_FINALLY(&subv, 0);
+
+    /* We start by finding cliques at the lowest threshold */
+    for (i = 0; i < no_of_edges; i++) {
+        if (VECTOR(*weights)[i] >= startthr) {
+            IGRAPH_CHECK(igraph_vector_push_back(&subv, i));
+        }
+    }
+    igraph_subgraph_edges(graph, &subg, igraph_ess_vector(&subv),
+                          /*delete_vertices=*/ 0);
+    IGRAPH_FINALLY(igraph_destroy, &subg);
+    igraph_maximal_cliques(&subg, &mycliques, /*min_size=*/ 0, /*max_size=*/ 0);
+    igraph_destroy(&subg);
+    IGRAPH_FINALLY_CLEAN(1);
+    nocliques = igraph_vector_ptr_size(&mycliques);
+
+    igraph_vector_destroy(&subv);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Get the next cliques and thresholds */
+    IGRAPH_VECTOR_INIT_FINALLY(&next_thr, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&clique_thr, 0);
+
+    igraph_i_subclique_next(graph, weights, ids, &mycliques,
+                            &newgraphs, &newweights, &newids,
+                            &clique_thr, &next_thr);
+
+    freedata.result = newgraphs;
+    freedata.resultids = newids;
+    freedata.resultweights = newweights;
+    freedata.nc = nocliques;
+    IGRAPH_FINALLY(igraph_i_subclique_next_free, &freedata);
+
+    /* Store cliques at the current level */
+    igraph_vector_append(thresholds, &clique_thr);
+    for (i = 0; i < nocliques; i++) {
+        igraph_vector_t *cl = (igraph_vector_t*) VECTOR(mycliques)[i];
+        int j, n = igraph_vector_size(cl);
+        for (j = 0; j < n; j++) {
+            int node = VECTOR(*cl)[j];
+            VECTOR(*cl)[j] = VECTOR(*ids)[node];
+        }
+        igraph_vector_sort(cl);
+    }
+    igraph_vector_ptr_append(cliques, &mycliques);
+
+    /* Recursive calls for cliques found */
+    nographs = igraph_vector_ptr_size(&mycliques);
+    for (i = 0; i < nographs; i++) {
+        igraph_t *g = newgraphs + i;
+        if (igraph_vcount(g) > 1) {
+            igraph_vector_t *w = newweights + i;
+            igraph_vector_int_t *ids = newids + i;
+            igraph_i_graphlets(g, w, cliques, thresholds, ids, VECTOR(next_thr)[i]);
+        }
+    }
+
+    igraph_vector_destroy(&clique_thr);
+    igraph_vector_destroy(&next_thr);
+    igraph_i_subclique_next_free(&freedata);
+    igraph_vector_ptr_destroy(&mycliques); /* contents was copied over */
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+typedef struct {
+    const igraph_vector_ptr_t *cliques;
+    const igraph_vector_t *thresholds;
+} igraph_i_graphlets_filter_t;
+
+int igraph_i_graphlets_filter_cmp(void *data, const void *a, const void *b) {
+    igraph_i_graphlets_filter_t *ddata = (igraph_i_graphlets_filter_t *) data;
+    int *aa = (int*) a;
+    int *bb = (int*) b;
+    igraph_real_t t_a = VECTOR(*ddata->thresholds)[*aa];
+    igraph_real_t t_b = VECTOR(*ddata->thresholds)[*bb];
+    igraph_vector_t *v_a, *v_b;
+    int s_a, s_b;
+
+    if (t_a < t_b) {
+        return -1;
+    } else if (t_a > t_b) {
+        return 1;
+    }
+
+    v_a = (igraph_vector_t*) VECTOR(*ddata->cliques)[*aa];
+    v_b = (igraph_vector_t*) VECTOR(*ddata->cliques)[*bb];
+    s_a = igraph_vector_size(v_a);
+    s_b = igraph_vector_size(v_b);
+
+    if (s_a < s_b) {
+        return -1;
+    } else if (s_a > s_b) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques,
+                              igraph_vector_t *thresholds) {
+
+    /* Filter out non-maximal cliques. Every non-maximal clique is
+       part of a maximal clique, at the same threshold.
+
+       First we order the cliques, according to their threshold, and
+       then according to their size. So when we look for a candidate
+       superset, we only need to check the cliques next in the list,
+       until their threshold is different. */
+
+    int i, iptr, nocliques = igraph_vector_ptr_size(cliques);
+    igraph_vector_int_t order;
+    igraph_i_graphlets_filter_t sortdata = { cliques, thresholds };
+
+    igraph_vector_int_init(&order, nocliques);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &order);
+    for (i = 0; i < nocliques; i++) {
+        VECTOR(order)[i] = i;
+    }
+
+    igraph_qsort_r(VECTOR(order), nocliques, sizeof(int), &sortdata,
+                   igraph_i_graphlets_filter_cmp);
+
+    for (i = 0; i < nocliques - 1; i++) {
+        int ri = VECTOR(order)[i];
+        igraph_vector_t *needle = VECTOR(*cliques)[ri];
+        igraph_real_t thr_i = VECTOR(*thresholds)[ri];
+        int n_i = igraph_vector_size(needle);
+        int j = i + 1;
+
+        for (j = i + 1; j < nocliques; j++) {
+            int rj = VECTOR(order)[j];
+            igraph_real_t thr_j = VECTOR(*thresholds)[rj];
+            igraph_vector_t *hay;
+            int n_j, pi = 0, pj = 0;
+
+            /* Done, not found */
+            if (thr_j != thr_i) {
+                break;
+            }
+
+            /* Check size of hay */
+            hay = VECTOR(*cliques)[rj];
+            n_j = igraph_vector_size(hay);
+            if (n_i > n_j) {
+                continue;
+            }
+
+            /* Check if hay is a superset */
+            while (pi < n_i && pj < n_j && n_i - pi <= n_j - pj) {
+                int ei = VECTOR(*needle)[pi];
+                int ej = VECTOR(*hay)[pj];
+                if (ei < ej) {
+                    break;
+                } else if (ei > ej) {
+                    pj++;
+                } else {
+                    pi++; pj++;
+                }
+            }
+            if (pi == n_i) {
+                /* Found, delete immediately */
+                igraph_vector_destroy(needle);
+                igraph_free(needle);
+                VECTOR(*cliques)[ri] = 0;
+                break;
+            }
+        }
+    }
+
+    /* Remove null pointers from the list of cliques */
+    for (i = 0, iptr = 0; i < nocliques; i++) {
+        igraph_vector_t *v = VECTOR(*cliques)[i];
+        if (v) {
+            VECTOR(*cliques)[iptr] = v;
+            VECTOR(*thresholds)[iptr] = VECTOR(*thresholds)[i];
+            iptr++;
+        }
+    }
+    igraph_vector_ptr_resize(cliques, iptr);
+    igraph_vector_resize(thresholds, iptr);
+
+    igraph_vector_int_destroy(&order);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_graphlets_candidate_basis
+ * Calculate a candidate graphlets basis
+ *
+ * \param graph The input graph, it must be a simple graph, edge directions are
+ *        ignored.
+ * \param weights Weights of the edges, a vector.
+ * \param cliques An initialized vector of pointers.
+ *        The graphlet basis is stored here. Each element of the pointer
+ *        vector will be a vector of vertex ids. Each elements must be
+ *        destroyed using \ref igraph_vector_destroy() and \ref igraph_free().
+ * \param thresholds An initialized vector, the (highest possible)
+ *        weight thresholds for finding the basis subgraphs are stored
+ *        here.
+ * \return Error code.
+ *
+ * See also: \ref igraph_graphlets() and \ref igraph_graphlets_project().
+ */
+
+int igraph_graphlets_candidate_basis(const igraph_t *graph,
+                                     const igraph_vector_t *weights,
+                                     igraph_vector_ptr_t *cliques,
+                                     igraph_vector_t *thresholds) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    int no_of_edges = igraph_ecount(graph);
+    igraph_real_t minthr;
+    igraph_vector_int_t ids;
+    igraph_bool_t simple;
+    int i;
+
+    /* Some checks */
+    if (weights == NULL) {
+        IGRAPH_ERROR("Graphlet functions require weighted graphs", IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    igraph_is_simple(graph, &simple);
+    if (!simple) {
+        IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL);
+    }
+
+    minthr = igraph_vector_min(weights);
+    igraph_vector_ptr_clear(cliques);
+    igraph_vector_clear(thresholds);
+    igraph_vector_int_init(&ids, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &ids);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(ids)[i] = i;
+    }
+
+    igraph_i_graphlets(graph, weights, cliques, thresholds, &ids, minthr);
+
+    igraph_vector_int_destroy(&ids);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_i_graphlets_filter(cliques, thresholds);
+
+    return 0;
+}
+
+int igraph_i_graphlets_project(const igraph_t *graph,
+                               const igraph_vector_t *weights,
+                               const igraph_vector_ptr_t *cliques,
+                               igraph_vector_t *Mu, igraph_bool_t startMu,
+                               int niter, int vid1) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    int no_of_edges = igraph_ecount(graph);
+    int no_cliques = igraph_vector_ptr_size(cliques);
+    igraph_vector_int_t vcl, vclidx, ecl, eclidx, cel, celidx;
+    igraph_vector_t edgelist, newweights, normfact;
+    int i, total_vertices, e, ptr, total_edges;
+    igraph_bool_t simple;
+
+    /* Check arguments */
+    if (weights == NULL) {
+        IGRAPH_ERROR("Graphlet functions require weighted graphs", IGRAPH_EINVAL);
+    }
+    if (no_of_edges != igraph_vector_size(weights)) {
+        IGRAPH_ERROR("Invalid weight vector size", IGRAPH_EINVAL);
+    }
+    if (startMu && igraph_vector_size(Mu) != no_cliques) {
+        IGRAPH_ERROR("Invalid start coefficient vector size", IGRAPH_EINVAL);
+    }
+    if (niter < 0) {
+        IGRAPH_ERROR("Number of iterations must be non-negative", IGRAPH_EINVAL);
+    }
+    igraph_is_simple(graph, &simple);
+    if (!simple) {
+        IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL);
+    }
+
+    if (!startMu) {
+        igraph_vector_resize(Mu, no_cliques);
+        igraph_vector_fill(Mu, 1);
+    }
+
+    /* Count # cliques per vertex. Also, create an index
+       for the edges per clique. */
+    IGRAPH_CHECK(igraph_vector_int_init(&vclidx, no_of_nodes + 2));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &vclidx);
+    IGRAPH_CHECK(igraph_vector_int_init(&celidx, no_cliques + 3));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &celidx);
+    for (i = 0, total_vertices = 0, total_edges = 0; i < no_cliques; i++) {
+        igraph_vector_t *v = VECTOR(*cliques)[i];
+        int j, n = igraph_vector_size(v);
+        total_vertices += n;
+        total_edges += n * (n - 1) / 2;
+        VECTOR(celidx)[i + 2] = total_edges;
+        for (j = 0; j < n; j++) {
+            int vv = VECTOR(*v)[j] - vid1;
+            VECTOR(vclidx)[vv + 2] += 1;
+        }
+    }
+    VECTOR(celidx)[i + 2] = total_edges;
+
+    /* Finalize index vector */
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(vclidx)[i + 2] += VECTOR(vclidx)[i + 1];
+    }
+
+    /* Create vertex-clique list, the cliques for each vertex. */
+    IGRAPH_CHECK(igraph_vector_int_init(&vcl, total_vertices));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &vcl);
+    for (i = 0; i < no_cliques; i++) {
+        igraph_vector_t *v = VECTOR(*cliques)[i];
+        int j, n = igraph_vector_size(v);
+        for (j = 0; j < n; j++) {
+            int vv = VECTOR(*v)[j] - vid1;
+            int p = VECTOR(vclidx)[vv + 1];
+            VECTOR(vcl)[p] = i;
+            VECTOR(vclidx)[vv + 1] += 1;
+        }
+    }
+
+    /* Create an edge-clique list, the cliques of each edge */
+    IGRAPH_CHECK(igraph_vector_int_init(&ecl, total_edges));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &ecl);
+    IGRAPH_CHECK(igraph_vector_int_init(&eclidx, no_of_edges + 1));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &eclidx);
+    IGRAPH_CHECK(igraph_vector_init(&edgelist, no_of_edges * 2));
+    IGRAPH_FINALLY(igraph_vector_destroy, &edgelist);
+    IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, /*by_col=*/ 0));
+    for (i = 0, e = 0, ptr = 0; e < no_of_edges; e++) {
+        int from = VECTOR(edgelist)[i++];
+        int to = VECTOR(edgelist)[i++];
+        int from_s = VECTOR(vclidx)[from];
+        int from_e = VECTOR(vclidx)[from + 1];
+        int to_s = VECTOR(vclidx)[to];
+        int to_e = VECTOR(vclidx)[to + 1];
+        VECTOR(eclidx)[e] = ptr;
+        while (from_s < from_e && to_s < to_e) {
+            int from_v = VECTOR(vcl)[from_s];
+            int to_v = VECTOR(vcl)[to_s];
+            if (from_v == to_v) {
+                VECTOR(ecl)[ptr++] = from_v;
+                from_s++; to_s++;
+            } else if (from_v < to_v) {
+                from_s++;
+            } else {
+                to_s++;
+            }
+        }
+    }
+    VECTOR(eclidx)[e] = ptr;
+
+    igraph_vector_destroy(&edgelist);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Convert the edge-clique list to a clique-edge list */
+    IGRAPH_CHECK(igraph_vector_int_init(&cel, total_edges));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &cel);
+    for (i = 0; i < no_of_edges; i++) {
+        int ecl_s = VECTOR(eclidx)[i], ecl_e = VECTOR(eclidx)[i + 1], j;
+        for (j = ecl_s; j < ecl_e; j++) {
+            int cl = VECTOR(ecl)[j];
+            int epos = VECTOR(celidx)[cl + 1];
+            VECTOR(cel)[epos] = i;
+            VECTOR(celidx)[cl + 1] += 1;
+        }
+    }
+
+    /* Normalizing factors for the iteration */
+    IGRAPH_CHECK(igraph_vector_init(&normfact, no_cliques));
+    IGRAPH_FINALLY(igraph_vector_destroy, &normfact);
+    for (i = 0; i < no_cliques; i++) {
+        igraph_vector_t *v = VECTOR(*cliques)[i];
+        int n = igraph_vector_size(v);
+        VECTOR(normfact)[i] = n * (n + 1) / 2;
+    }
+
+    /* We have the clique-edge list, so do the projection now */
+    IGRAPH_CHECK(igraph_vector_init(&newweights, no_of_edges));
+    IGRAPH_FINALLY(igraph_vector_destroy, &newweights);
+    for (i = 0; i < niter; i++) {
+        for (e = 0; e < no_of_edges; e++) {
+            int start = VECTOR(eclidx)[e];
+            int end = VECTOR(eclidx)[e + 1];
+            VECTOR(newweights)[e] = 0.0001;
+            while (start < end) {
+                int clique = VECTOR(ecl)[start++];
+                VECTOR(newweights)[e] += VECTOR(*Mu)[clique];
+            }
+        }
+        for (e = 0; e < no_cliques; e++) {
+            igraph_real_t sumratio = 0;
+            int start = VECTOR(celidx)[e];
+            int end = VECTOR(celidx)[e + 1];
+            while (start < end) {
+                int edge = VECTOR(cel)[start++];
+                sumratio += VECTOR(*weights)[edge] / VECTOR(newweights)[edge];
+            }
+            VECTOR(*Mu)[e] *= sumratio / VECTOR(normfact)[e];
+        }
+    }
+
+    igraph_vector_destroy(&newweights);
+    igraph_vector_destroy(&normfact);
+    igraph_vector_int_destroy(&cel);
+    igraph_vector_int_destroy(&eclidx);
+    igraph_vector_int_destroy(&ecl);
+    igraph_vector_int_destroy(&vcl);
+    igraph_vector_int_destroy(&celidx);
+    igraph_vector_int_destroy(&vclidx);
+    IGRAPH_FINALLY_CLEAN(8);
+
+    return 0;
+}
+
+/**
+ * \function igraph_graphlets_project
+ * Project a graph on a graphlets basis
+ *
+ * Note that the graph projected does not have to be the same that
+ * was used to calculate the graphlet basis, but it is assumed that
+ * it has the same number of vertices, and the vertex ids of the two
+ * graphs match.
+ * \param graph The input graph, it must be a simple graph, edge directions are
+ *        ignored.
+ * \param weights Weights of the edges in the input graph, a vector.
+ * \param cliques The graphlet basis, a pointer vector, in which each
+ *        element is a vector of vertex ids.
+ * \param Mu An initialized vector, the weights of the graphlets will
+ *        be stored here. This vector is also used to initialize the
+ *        the weight vector for the iterative algorithm, if the
+ *        \c startMu argument is true (non-zero).
+ * \param startMu If true (non-zero), then the supplied Mu vector is
+ *        used as the starting point of the iteration. Otherwise a
+ *        constant 1 vector is used.
+ * \param niter Integer scalar, the number of iterations to perform.
+ * \return Error code.
+ *
+ * See also: \ref igraph_graphlets() and
+ * \ref igraph_graphlets_candidate_basis().
+ */
+
+int igraph_graphlets_project(const igraph_t *graph,
+                             const igraph_vector_t *weights,
+                             const igraph_vector_ptr_t *cliques,
+                             igraph_vector_t *Mu, igraph_bool_t startMu,
+                             int niter) {
+
+    return igraph_i_graphlets_project(graph, weights, cliques, Mu, startMu,
+                                      niter, /*vid1=*/ 0);
+}
+
+typedef struct igraph_i_graphlets_order_t {
+    const igraph_vector_ptr_t *cliques;
+    const igraph_vector_t *Mu;
+} igraph_i_graphlets_order_t;
+
+int igraph_i_graphlets_order_cmp(void *data, const void *a, const void *b) {
+    igraph_i_graphlets_order_t *ddata = (igraph_i_graphlets_order_t*) data;
+    int *aa = (int*) a;
+    int *bb = (int*) b;
+    igraph_real_t Mu_a = VECTOR(*ddata->Mu)[*aa];
+    igraph_real_t Mu_b = VECTOR(*ddata->Mu)[*bb];
+
+    if (Mu_a < Mu_b) {
+        return 1;
+    } else if (Mu_a > Mu_b) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+/**
+ * \function igraph_graphlets
+ * Calculate graphlets basis and project the graph on it
+ *
+ * This function simply calls \ref igraph_graphlets_candidate_basis()
+ * and \ref igraph_graphlets_project(), and then orders the graphlets
+ * according to decreasing weights.
+ * \param graph The input graph, it must be a simple graph, edge directions are
+ *        ignored.
+ * \param weights Weights of the edges, a vector.
+ * \param cliques An initialized vector of pointers.
+ *        The graphlet basis is stored here. Each element of the pointer
+ *        vector will be a vector of vertex ids.
+ * \param Mu An initialized vector, the weights of the graphlets will
+ *        be stored here.
+ * \param niter Integer scalar, the number of iterations to perform
+ *        for the projection step.
+ * \return Error code.
+ *
+ * See also: \ref igraph_graphlets_candidate_basis() and
+ * \ref igraph_graphlets_project().
+ */
+
+int igraph_graphlets(const igraph_t *graph,
+                     const igraph_vector_t *weights,
+                     igraph_vector_ptr_t *cliques,
+                     igraph_vector_t *Mu, int niter) {
+
+    int i, nocliques;
+    igraph_vector_t thresholds;
+    igraph_vector_int_t order;
+    igraph_i_graphlets_order_t sortdata = { cliques, Mu };
+
+    igraph_vector_init(&thresholds, 0);
+    IGRAPH_FINALLY(igraph_vector_destroy, &thresholds);
+    igraph_graphlets_candidate_basis(graph, weights, cliques, &thresholds);
+    igraph_vector_destroy(&thresholds);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_graphlets_project(graph, weights, cliques, Mu, /*startMu=*/ 0, niter);
+
+    nocliques = igraph_vector_ptr_size(cliques);
+    igraph_vector_int_init(&order, nocliques);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &order);
+    for (i = 0; i < nocliques; i++) {
+        VECTOR(order)[i] = i;
+    }
+    igraph_qsort_r(VECTOR(order), nocliques, sizeof(int), &sortdata,
+                   igraph_i_graphlets_order_cmp);
+
+    igraph_vector_ptr_index_int(cliques, &order);
+    igraph_vector_index_int(Mu, &order);
+
+    igraph_vector_int_destroy(&order);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
diff --git a/igraph/src/glpk_support.c b/igraph/src/glpk_support.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/glpk_support.c
@@ -0,0 +1,101 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_GLPK
+
+#include "igraph_types.h"
+#include "igraph_error.h"
+#include "igraph_interrupt_internal.h"
+#include <glpk.h>
+#include <memory.h>
+#include <stdio.h>
+
+void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info) {
+    IGRAPH_UNUSED(info);
+
+    /* This is a special version of IGRAPH_ALLOW_INTERRUPTION().
+       Calling glp_ios_terminate() from glp_intopt()'s callback function
+       signals to GLPK that it should terminate the optimization and return
+       with the code GLP_ESTOP.
+    */
+    if (igraph_i_interruption_handler) {
+        if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) {
+            glp_ios_terminate(tree);
+        }
+    }
+}
+
+int igraph_i_glpk_check(int retval, const char* message) {
+    char* code = "none";
+    char message_and_code[4096];
+
+    if (retval == IGRAPH_SUCCESS) {
+        return IGRAPH_SUCCESS;
+    }
+
+    /* handle errors */
+#define HANDLE_CODE(c) case c: code = #c; retval = IGRAPH_##c; break;
+#define HANDLE_CODE2(c) case c: code = #c; retval = IGRAPH_FAILURE; break;
+#define HANDLE_CODE3(c) case c: code = #c; retval = IGRAPH_INTERRUPTED; break;
+    switch (retval) {
+        HANDLE_CODE(GLP_EBOUND);
+        HANDLE_CODE(GLP_EROOT);
+        HANDLE_CODE(GLP_ENOPFS);
+        HANDLE_CODE(GLP_ENODFS);
+        HANDLE_CODE(GLP_EFAIL);
+        HANDLE_CODE(GLP_EMIPGAP);
+        HANDLE_CODE(GLP_ETMLIM);
+
+        HANDLE_CODE3(GLP_ESTOP);
+
+        HANDLE_CODE2(GLP_EBADB);
+        HANDLE_CODE2(GLP_ESING);
+        HANDLE_CODE2(GLP_ECOND);
+        HANDLE_CODE2(GLP_EOBJLL);
+        HANDLE_CODE2(GLP_EOBJUL);
+        HANDLE_CODE2(GLP_EITLIM);
+
+    default:
+        IGRAPH_ERROR("unknown GLPK error", IGRAPH_FAILURE);
+    }
+#undef HANDLE_CODE
+#undef HANDLE_CODE2
+#undef HANDLE_CODE3
+
+    sprintf(message_and_code, "%s (%s)", message, code);
+    IGRAPH_ERROR(message_and_code, retval);
+}
+
+#endif
+
+#ifdef USING_R
+
+int igraph_glpk_dummy() {
+    return 'b' + 'a' + 's' + 's' + 'z' + 'a' + 't' + 'o' + 'k' +
+           'm' + 'e' + 'g';
+}
+
+#endif
diff --git a/igraph/src/gml_tree.c b/igraph/src/gml_tree.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/gml_tree.c
@@ -0,0 +1,261 @@
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_gml_tree.h"
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <string.h>
+#include <stdio.h>
+
+int igraph_gml_tree_init_integer(igraph_gml_tree_t *t,
+                                 const char *name, int namelen,
+                                 igraph_integer_t value) {
+
+    igraph_integer_t *p;
+
+    IGRAPH_UNUSED(namelen);
+
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1);
+    IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1);
+
+    /* names */
+    VECTOR(t->names)[0] = (void*)name;
+
+    /* types */
+    VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_INTEGER;
+
+    /* children */
+    p = igraph_Calloc(1, igraph_integer_t);
+    if (!p) {
+        IGRAPH_ERROR("Cannot create integer GML tree node", IGRAPH_ENOMEM);
+    }
+    *p = value;
+    VECTOR(t->children)[0] = p;
+
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+int igraph_gml_tree_init_real(igraph_gml_tree_t *t,
+                              const char *name, int namelen,
+                              igraph_real_t value) {
+
+    igraph_real_t *p;
+
+    IGRAPH_UNUSED(namelen);
+
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1);
+    IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1);
+
+    /* names */
+    VECTOR(t->names)[0] = (void*) name;
+
+    /* types */
+    VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_REAL;
+
+    /* children */
+    p = igraph_Calloc(1, igraph_real_t);
+    if (!p) {
+        IGRAPH_ERROR("Cannot create real GML tree node", IGRAPH_ENOMEM);
+    }
+    *p = value;
+    VECTOR(t->children)[0] = p;
+
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+int igraph_gml_tree_init_string(igraph_gml_tree_t *t,
+                                const char *name, int namelen,
+                                const char *value, int valuelen) {
+
+    IGRAPH_UNUSED(namelen);
+    IGRAPH_UNUSED(valuelen);
+
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1);
+    IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1);
+
+    /* names */
+    VECTOR(t->names)[0] = (void*) name;
+
+    /* types */
+    VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_STRING;
+
+    /* children */
+    VECTOR(t->children)[0] = (void*)value;
+
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+int igraph_gml_tree_init_tree(igraph_gml_tree_t *t,
+                              const char *name, int namelen,
+                              igraph_gml_tree_t *value) {
+
+    IGRAPH_UNUSED(namelen);
+
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1);
+    IGRAPH_CHECK(igraph_vector_char_init(&t->types, 1));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &t->types);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1);
+
+    /* names */
+    VECTOR(t->names)[0] = (void*)name;
+
+    /* types */
+    VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_TREE;
+
+    /* children */
+    VECTOR(t->children)[0] = value;
+
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+
+}
+
+/* merge is destructive, the _second_ tree is destroyed */
+int igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2) {
+    long int i, n = igraph_vector_ptr_size(&t2->children);
+    for (i = 0; i < n; i++) {
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(&t1->names, VECTOR(t2->names)[i]));
+        IGRAPH_CHECK(igraph_vector_char_push_back(&t1->types, VECTOR(t2->types)[i]));
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(&t1->children,
+                     VECTOR(t2->children)[i]));
+    }
+
+    igraph_vector_ptr_destroy(&t2->names);
+    igraph_vector_char_destroy(&t2->types);
+    igraph_vector_ptr_destroy(&t2->children);
+    return 0;
+}
+
+void igraph_gml_tree_destroy(igraph_gml_tree_t *t) {
+
+    long int i, n = igraph_vector_ptr_size(&t->children);
+    for (i = 0; i < n; i++) {
+        int type = VECTOR(t->types)[i];
+        switch (type) {
+        case IGRAPH_I_GML_TREE_TREE:
+            igraph_gml_tree_destroy(VECTOR(t->children)[i]);
+            igraph_Free(VECTOR(t->names)[i]);
+            break;
+        case IGRAPH_I_GML_TREE_INTEGER:
+            igraph_Free(VECTOR(t->children)[i]);
+            igraph_Free(VECTOR(t->names)[i]);
+            break;
+        case IGRAPH_I_GML_TREE_REAL:
+            igraph_Free(VECTOR(t->children)[i]);
+            igraph_Free(VECTOR(t->names)[i]);
+            break;
+        case IGRAPH_I_GML_TREE_STRING:
+            igraph_Free(VECTOR(t->children)[i]);
+            igraph_Free(VECTOR(t->names)[i]);
+            break;
+        case IGRAPH_I_GML_TREE_DELETED:
+            break;
+        }
+    }
+    igraph_vector_ptr_destroy(&t->names);
+    igraph_vector_char_destroy(&t->types);
+    igraph_vector_ptr_destroy(&t->children);
+    igraph_Free(t);
+}
+
+long int igraph_gml_tree_length(const igraph_gml_tree_t *t) {
+    return igraph_vector_ptr_size(&t->names);
+}
+
+long int igraph_gml_tree_find(const igraph_gml_tree_t *t,
+                              const char *name, long int from) {
+
+    long int size = igraph_vector_ptr_size(&t->names);
+    while ( from < size && (! VECTOR(t->names)[from] ||
+                            strcmp(VECTOR(t->names)[from], name)) ) {
+        from++;
+    }
+
+    if (from == size) {
+        from = -1;
+    }
+    return from;
+}
+
+long int igraph_gml_tree_findback(const igraph_gml_tree_t *t,
+                                  const char *name, long int from) {
+    while ( from >= 0 && (! VECTOR(t->names)[from] ||
+                          strcmp(VECTOR(t->names)[from], name)) ) {
+        from--;
+    }
+
+    return from;
+}
+
+int igraph_gml_tree_type(const igraph_gml_tree_t *t, long int pos) {
+    return VECTOR(t->types)[pos];
+}
+
+const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, long int pos) {
+    return VECTOR(t->names)[pos];
+}
+
+igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t,
+        long int pos) {
+    igraph_integer_t *i = VECTOR(t->children)[pos];
+    return *i;
+}
+
+igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t,
+                                       long int pos) {
+    igraph_real_t *d = VECTOR(t->children)[pos];
+    return *d;
+}
+
+const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t,
+                                       long int pos) {
+    const char *s = VECTOR(t->children)[pos];
+    return s;
+}
+
+igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t,
+        long int pos) {
+    igraph_gml_tree_t *tree = VECTOR(t->children)[pos];
+    return tree;
+}
+
+void igraph_gml_tree_delete(igraph_gml_tree_t *t, long int pos) {
+    if (VECTOR(t->types)[pos] == IGRAPH_I_GML_TREE_TREE) {
+        igraph_gml_tree_destroy(VECTOR(t->children)[pos]);
+    }
+    igraph_Free(VECTOR(t->names)[pos]);
+    igraph_Free(VECTOR(t->children)[pos]);
+    VECTOR(t->children)[pos] = 0;
+    VECTOR(t->names)[pos] = 0;
+    VECTOR(t->types)[pos] = IGRAPH_I_GML_TREE_DELETED;
+}
diff --git a/igraph/src/graph.cc b/igraph/src/graph.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/graph.cc
@@ -0,0 +1,5609 @@
+#include <cstdio>
+#include <cassert>
+#include <climits>
+#include <set>
+#include <list>
+#include <algorithm>
+
+#include "defs.hh"
+#include "graph.hh"
+#include "partition.hh"
+#include "utils.hh"
+
+/* use 'and' instead of '&&' */
+#if _MSC_VER
+#include <ciso646>
+#endif
+
+#ifdef USING_R
+#undef stdout
+#define stdout NULL
+#endif
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+namespace bliss {
+
+#define _INTERNAL_ERROR() fatal_error("%s:%d: internal error",__FILE__,__LINE__)
+#define _OUT_OF_MEMORY() fatal_error("%s:%d: out of memory",__FILE__,__LINE__)
+
+/*-------------------------------------------------------------------------
+ *
+ * Constructor and destructor routines for the abstract graph class
+ *
+ *-------------------------------------------------------------------------*/
+
+
+AbstractGraph::AbstractGraph()
+{
+  /* Initialize stuff */
+  first_path_labeling = 0;
+  first_path_labeling_inv = 0;
+  best_path_labeling = 0;
+  best_path_labeling_inv = 0;
+  first_path_automorphism = 0;
+  best_path_automorphism = 0;
+  in_search = false;
+
+  /* Default value for using "long prune" */
+  opt_use_long_prune = true;
+  /* Default value for using failure recording */
+  opt_use_failure_recording = true;
+  /* Default value for using component recursion */
+  opt_use_comprec = true;
+
+
+  verbose_level = 0;
+  verbstr = stdout;
+
+  report_hook = 0;
+  report_user_param = 0;
+}
+
+
+AbstractGraph::~AbstractGraph()
+{
+  if(first_path_labeling) {
+    free(first_path_labeling); first_path_labeling = 0; }
+  if(first_path_labeling_inv) {
+    free(first_path_labeling_inv); first_path_labeling_inv = 0; }
+  if(best_path_labeling) {
+    free(best_path_labeling); best_path_labeling = 0; }
+  if(best_path_labeling_inv) {
+    free(best_path_labeling_inv); best_path_labeling_inv = 0; }
+  if(first_path_automorphism) {
+    free(first_path_automorphism); first_path_automorphism = 0; }
+  if(best_path_automorphism) {
+    free(best_path_automorphism); best_path_automorphism = 0; }
+ 
+  report_hook = 0;
+  report_user_param = 0;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Verbose output management routines
+ *
+ *-------------------------------------------------------------------------*/
+
+void
+AbstractGraph::set_verbose_level(const unsigned int level)
+{
+  verbose_level = level;
+}
+
+void
+AbstractGraph::set_verbose_file(FILE* const fp)
+{
+  verbstr = fp;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Routines for refinement to equitable partition
+ *
+ *-------------------------------------------------------------------------*/
+
+void
+AbstractGraph::refine_to_equitable()
+{
+
+  /* Start refinement from all cells -> push 'em all in the splitting queue */
+  for(Partition::Cell* cell = p.first_cell; cell; cell = cell->next)
+    p.splitting_queue_add(cell);
+
+  do_refine_to_equitable();
+
+}
+
+void
+AbstractGraph::refine_to_equitable(Partition::Cell* const unit_cell)
+{
+
+  p.splitting_queue_add(unit_cell);
+
+  do_refine_to_equitable();
+}
+
+
+
+void
+AbstractGraph::refine_to_equitable(Partition::Cell* const unit_cell1,
+				   Partition::Cell* const unit_cell2)
+{
+
+  p.splitting_queue_add(unit_cell1);
+  p.splitting_queue_add(unit_cell2);
+
+  do_refine_to_equitable();
+}
+
+
+
+bool
+AbstractGraph::do_refine_to_equitable()
+{
+
+  eqref_hash.reset();
+
+  while(!p.splitting_queue_is_empty())
+    {
+      Partition::Cell* const cell = p.splitting_queue_pop();
+
+      if(cell->is_unit())
+	{
+	  if(in_search) {
+	    const unsigned int index = cell->first;
+	    if(first_path_automorphism)
+	      {
+		/* Build the (potential) automorphism on-the-fly */
+		first_path_automorphism[first_path_labeling_inv[index]] =
+		  p.elements[index];
+	      }
+	    if(best_path_automorphism)
+	      {
+		/* Build the (potential) automorphism on-the-fly */
+		best_path_automorphism[best_path_labeling_inv[index]] =
+		  p.elements[index];
+	      }
+	  }
+	  const bool worse = split_neighbourhood_of_unit_cell(cell);
+	  if(in_search and worse)
+	    goto worse_exit;
+	}
+      else
+	{
+	  const bool worse = split_neighbourhood_of_cell(cell);
+	  if(in_search and worse)
+	    goto worse_exit;
+	}
+    }
+
+  return true;
+
+ worse_exit:
+  /* Clear splitting_queue */
+  p.splitting_queue_clear();
+  return false;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Routines for handling the canonical labeling
+ *
+ *-------------------------------------------------------------------------*/
+
+/** \internal
+ * Assign the labeling induced by the current partition 'this.p' to
+ * \a labeling.
+ * That is, if the partition is [[2,0],[1]],
+ * then \a labeling will map 0 to 1, 1 to 2, and 2 to 0.
+ */
+void
+AbstractGraph::update_labeling(unsigned int* const labeling)
+{
+  const unsigned int N = get_nof_vertices();
+  unsigned int* ep = p.elements;
+  for(unsigned int i = 0; i < N; i++, ep++)
+    labeling[*ep] = i;
+}
+
+/** \internal
+ * The same as update_labeling() except that the inverse of the labeling
+ * is also produced and assigned to \a labeling_inv.
+ */
+void
+AbstractGraph::update_labeling_and_its_inverse(unsigned int* const labeling,
+					       unsigned int* const labeling_inv)
+{
+  const unsigned int N = get_nof_vertices();
+  unsigned int* ep = p.elements;
+  unsigned int* clip = labeling_inv;
+
+  for(unsigned int i = 0; i < N; ) {
+    labeling[*ep] = i;
+    i++;
+    *clip = *ep;
+    ep++;
+    clip++;
+  }
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Routines for handling automorphisms
+ *
+ *-------------------------------------------------------------------------*/
+
+
+/** \internal
+ * Reset the permutation \a perm to the identity permutation.
+ */
+void
+AbstractGraph::reset_permutation(unsigned int* perm)
+{
+  const unsigned int N = get_nof_vertices();
+  for(unsigned int i = 0; i < N; i++, perm++)
+    *perm = i;
+}
+
+bool
+AbstractGraph::is_automorphism(unsigned int* const perm)
+{
+  _INTERNAL_ERROR();
+  return false;
+}
+
+bool
+AbstractGraph::is_automorphism(const std::vector<unsigned int>& perm) const
+{
+  _INTERNAL_ERROR();
+  return false;
+}
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Certificate building
+ *
+ *-------------------------------------------------------------------------*/
+
+void
+AbstractGraph::cert_add(const unsigned int v1,
+			const unsigned int v2,
+			const unsigned int v3)
+{
+  if(refine_compare_certificate)
+    {
+      if(refine_equal_to_first)
+	{
+	  /* So far equivalent to the first path... */
+	  unsigned int index = certificate_current_path.size();
+	  if(index >= refine_first_path_subcertificate_end)
+	    {
+	      refine_equal_to_first = false;
+	    }
+	  else if(certificate_first_path[index] != v1)
+	    {
+	      refine_equal_to_first = false;
+	    }
+	  else if(certificate_first_path[++index] != v2)
+	    {
+	      refine_equal_to_first = false;
+	    }
+	  else if(certificate_first_path[++index] != v3)
+	    {
+	      refine_equal_to_first = false;
+	    }
+	  if(opt_use_failure_recording and !refine_equal_to_first)
+	    {
+	      /* We just became different from the first path,
+	       * remember the deviation point tree-specific invariant
+	       * for the use of failure recording */
+	      UintSeqHash h;
+	      h.update(v1);
+	      h.update(v2);
+	      h.update(v3);
+	      h.update(index);
+	      h.update(eqref_hash.get_value());
+	      failure_recording_fp_deviation = h.get_value();
+	    }
+	}
+      if(refine_cmp_to_best == 0)
+	{
+	  /* So far equivalent to the current best path... */
+	  unsigned int index = certificate_current_path.size();
+	  if(index >= refine_best_path_subcertificate_end)
+	    {
+	      refine_cmp_to_best = 1;
+	    }
+	  else if(v1 > certificate_best_path[index])
+	    {
+	      refine_cmp_to_best = 1;
+	    }
+	  else if(v1 < certificate_best_path[index])
+	    {
+	      refine_cmp_to_best = -1;
+	    }
+	  else if(v2 > certificate_best_path[++index])
+	    {
+	      refine_cmp_to_best = 1;
+	    }
+	  else if(v2 < certificate_best_path[index])
+	    {
+	      refine_cmp_to_best = -1;
+	    }
+	  else if(v3 > certificate_best_path[++index])
+	    {
+	      refine_cmp_to_best = 1;
+	    }
+	  else if(v3 < certificate_best_path[index])
+	    {
+	      refine_cmp_to_best = -1;
+	    }
+	}
+      if((refine_equal_to_first == false) and
+	 (refine_cmp_to_best < 0))
+	return;
+    }
+  /* Update the current path certificate */
+  certificate_current_path.push_back(v1);
+  certificate_current_path.push_back(v2);
+  certificate_current_path.push_back(v3);
+}
+
+
+void
+AbstractGraph::cert_add_redundant(const unsigned int v1,
+				  const unsigned int v2,
+				  const unsigned int v3)
+{
+  return cert_add(v1, v2, v3);
+}
+
+
+
+
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Long prune code
+ *
+ *-------------------------------------------------------------------------*/
+
+void
+AbstractGraph::long_prune_init()
+{
+  const unsigned int N = get_nof_vertices();
+  long_prune_temp.clear();
+  long_prune_temp.resize(N);
+  /* Of how many automorphisms we can store information in
+     the predefined, fixed amount of memory? */
+  const unsigned int nof_fitting_in_max_mem =
+    (long_prune_options_max_mem * 1024 * 1024) / (((N * 2) / 8)+1);
+  long_prune_max_stored_autss = long_prune_options_max_stored_auts;
+  /* Had some problems with g++ in using (a<b)?a:b when constants involved,
+     so had to make this in a stupid way... */
+  if(nof_fitting_in_max_mem < long_prune_options_max_stored_auts)
+    long_prune_max_stored_autss = nof_fitting_in_max_mem;
+
+  long_prune_deallocate();
+  long_prune_fixed.resize(N, 0);
+  long_prune_mcrs.resize(N, 0);
+  long_prune_begin = 0;
+  long_prune_end = 0;
+}
+
+void
+AbstractGraph::long_prune_deallocate()
+{
+  while(!long_prune_fixed.empty())
+    {
+      delete long_prune_fixed.back();
+      long_prune_fixed.pop_back();
+    }
+  while(!long_prune_mcrs.empty())
+    {
+      delete long_prune_mcrs.back();
+      long_prune_mcrs.pop_back();
+    }
+}
+
+void
+AbstractGraph::long_prune_swap(const unsigned int i, const unsigned int j)
+{
+  const unsigned int real_i = i % long_prune_max_stored_autss;
+  const unsigned int real_j = j % long_prune_max_stored_autss;
+  std::vector<bool>* tmp = long_prune_fixed[real_i];
+  long_prune_fixed[real_i] = long_prune_fixed[real_j];
+  long_prune_fixed[real_j] = tmp;
+  tmp = long_prune_mcrs[real_i];
+  long_prune_mcrs[real_i] = long_prune_mcrs[real_j];
+  long_prune_mcrs[real_j] = tmp;
+}
+
+std::vector<bool>&
+AbstractGraph::long_prune_allocget_fixed(const unsigned int index)
+{
+  const unsigned int i = index % long_prune_max_stored_autss;
+  if(!long_prune_fixed[i])
+    long_prune_fixed[i] = new std::vector<bool>(get_nof_vertices());
+  return *long_prune_fixed[i];
+}
+
+std::vector<bool>&
+AbstractGraph::long_prune_get_fixed(const unsigned int index)
+{
+  return *long_prune_fixed[index % long_prune_max_stored_autss];
+}
+
+std::vector<bool>&
+AbstractGraph::long_prune_allocget_mcrs(const unsigned int index)
+{
+  const unsigned int i = index % long_prune_max_stored_autss;
+  if(!long_prune_mcrs[i])
+    long_prune_mcrs[i] = new std::vector<bool>(get_nof_vertices());
+  return *long_prune_mcrs[i];
+}
+
+std::vector<bool>&
+AbstractGraph::long_prune_get_mcrs(const unsigned int index)
+{
+  return *long_prune_mcrs[index % long_prune_max_stored_autss];
+}
+
+void
+AbstractGraph::long_prune_add_automorphism(const unsigned int* aut)
+{
+  if(long_prune_max_stored_autss == 0)
+    return;
+
+  const unsigned int N = get_nof_vertices();
+
+
+  /* If the buffer of stored auts is full, remove the oldest aut */
+  if(long_prune_end - long_prune_begin == long_prune_max_stored_autss)
+    {
+      long_prune_begin++;
+    }
+  long_prune_end++;
+  std::vector<bool>& fixed = long_prune_allocget_fixed(long_prune_end-1);
+  std::vector<bool>& mcrs = long_prune_allocget_mcrs(long_prune_end-1);
+  /* Mark nodes that are (i) fixed or (ii) minimal orbit representatives
+   * under the automorphism 'aut' */
+  for(unsigned int i = 0; i < N; i++)
+    {
+      fixed[i] = (aut[i] == i);
+      if(long_prune_temp[i] == false)
+	{
+	  mcrs[i] = true;
+	  unsigned int j = aut[i];
+	  while(j != i)
+	    {
+	      long_prune_temp[j] = true;
+	      j = aut[j];
+	    }
+	}
+      else
+	{
+	  mcrs[i] = false;
+	}
+      /* Clear the temp array on-the-fly... */
+      long_prune_temp[i] = false;
+    }
+
+
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Routines for handling orbit information
+ *
+ *-------------------------------------------------------------------------*/
+
+void
+AbstractGraph::update_orbit_information(Orbit& o, const unsigned int* perm)
+{
+  const unsigned int N = get_nof_vertices();
+  for(unsigned int i = 0; i < N; i++)
+    if(perm[i] != i)
+      o.merge_orbits(i, perm[i]);
+}
+
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * The actual backtracking search
+ *
+ *-------------------------------------------------------------------------*/
+
+class TreeNode
+{
+  //friend class AbstractGraph;
+public:
+  unsigned int split_cell_first;
+
+  int split_element;
+  static const int SPLIT_START = -1;
+  static const int SPLIT_END   = -2;
+
+  Partition::BacktrackPoint partition_bt_point;
+
+  unsigned int certificate_index;
+
+  static const char NO = -1;
+  static const char MAYBE = 0;
+  static const char YES = 1;
+
+  /* First path stuff */
+  bool fp_on;
+  bool fp_cert_equal;
+  char fp_extendable;
+
+  /* Best path stuff */
+  bool in_best_path;
+  int cmp_to_best_path;
+
+  unsigned int failure_recording_ival;
+
+  /* Component recursion related data */
+  unsigned int cr_cep_stack_size;
+  unsigned int cr_cep_index;
+  unsigned int cr_level;
+
+  bool needs_long_prune;
+  unsigned int long_prune_begin;
+  std::set<unsigned int, std::less<unsigned int> > long_prune_redundant;
+  
+  UintSeqHash eqref_hash;
+  unsigned int subcertificate_length;
+};
+
+
+
+
+typedef struct {
+  unsigned int splitting_element;
+  unsigned int certificate_index;
+  unsigned int subcertificate_length;
+  UintSeqHash eqref_hash;
+} PathInfo;
+
+
+void
+AbstractGraph::search(const bool canonical, Stats& stats)
+{
+  const unsigned int N = get_nof_vertices();
+
+  unsigned int all_same_level = UINT_MAX;
+
+  p.graph = this;
+
+  /*
+   * Must be done!
+   */
+  remove_duplicate_edges();
+
+  /*
+   * Reset search statistics
+   */
+  stats.reset();
+  stats.nof_nodes = 1;
+  stats.nof_leaf_nodes = 1;
+
+  /* Free old first path data structures */
+  if(first_path_labeling) {
+    free(first_path_labeling); first_path_labeling = 0; }
+  if(first_path_labeling_inv) {
+    free(first_path_labeling_inv); first_path_labeling_inv = 0; }
+  if(first_path_automorphism) {
+    free(first_path_automorphism); first_path_automorphism = 0; }
+
+  /* Free old best path data structures */
+  if(best_path_labeling) {
+    free(best_path_labeling); best_path_labeling = 0; }
+  if(best_path_labeling_inv) {
+    free(best_path_labeling_inv); best_path_labeling_inv = 0; }
+  if(best_path_automorphism) {
+    free(best_path_automorphism); best_path_automorphism = 0; }
+
+  if(N == 0)
+    {
+      /* Nothing to do, return... */
+      return;
+    }
+
+  /* Initialize the partition ... */
+  p.init(N);
+  /* ... and the component recursion data structures in the partition */
+  if(opt_use_comprec)
+    p.cr_init();
+  
+  neighbour_heap.init(N);
+
+  in_search = false;
+  /* Do not compute certificate when building the initial partition */
+  refine_compare_certificate = false;
+  /* The 'eqref_hash' hash value is not computed when building
+   * the initial partition as it is not used for anything at the moment.
+   * This saves some cycles. */
+  compute_eqref_hash = false;
+
+  make_initial_equitable_partition();
+
+  /*
+   * Allocate space for the "first path" and "best path" labelings
+   */
+  if(first_path_labeling) free(first_path_labeling);
+  first_path_labeling = (unsigned int*)calloc(N, sizeof(unsigned int));
+  if(!first_path_labeling) _OUT_OF_MEMORY();
+  if(best_path_labeling) free(best_path_labeling);
+  best_path_labeling = (unsigned int*)calloc(N, sizeof(unsigned int));
+  if(!best_path_labeling) _OUT_OF_MEMORY();
+
+  /*
+   * Is the initial partition discrete?
+   */
+  if(p.is_discrete())
+    {
+      /* Make the best path labeling i.e. the canonical labeling */
+      update_labeling(best_path_labeling);
+      /* Update statistics */
+      stats.nof_leaf_nodes = 1;
+      /* Free component recursion data */
+      if(opt_use_comprec)
+        p.cr_free();
+      return;
+    }
+
+  /*
+   * Allocate the inverses of the "first path" and "best path" labelings
+   */
+  if(first_path_labeling_inv) free(first_path_labeling_inv);
+  first_path_labeling_inv = (unsigned int*)calloc(N, sizeof(unsigned int));
+  if(!first_path_labeling_inv) _OUT_OF_MEMORY();
+  if(best_path_labeling_inv) free(best_path_labeling_inv);
+  best_path_labeling_inv = (unsigned int*)calloc(N, sizeof(unsigned int));
+  if(!best_path_labeling_inv) _OUT_OF_MEMORY();
+
+  /*
+   * Allocate space for the automorphisms
+   */
+  if(first_path_automorphism) free(first_path_automorphism);
+  first_path_automorphism = (unsigned int*)malloc(N * sizeof(unsigned int));
+  if(!first_path_automorphism) _OUT_OF_MEMORY();
+  if(best_path_automorphism) free(best_path_automorphism);
+  best_path_automorphism = (unsigned int*)malloc(N * sizeof(unsigned int));
+  if(!best_path_automorphism) _OUT_OF_MEMORY();
+
+  /*
+   * Initialize orbit information so that all vertices are in their own orbits
+   */
+  first_path_orbits.init(N);
+  best_path_orbits.init(N);
+
+  /*
+   * Initialize certificate memory
+   */
+  initialize_certificate();
+
+  std::vector<TreeNode> search_stack;
+  std::vector<PathInfo> first_path_info;
+  std::vector<PathInfo> best_path_info;
+
+  search_stack.clear();
+
+  /* Initialize "long prune" data structures */
+  if(opt_use_long_prune)
+    long_prune_init();
+
+  /*
+   * Initialize failure recording data structures
+   */
+  typedef std::set<unsigned int, std::less<unsigned int> > FailureRecordingSet;
+  std::vector<FailureRecordingSet> failure_recording_hashes;
+
+  /*
+   * Initialize component recursion data structures
+   */
+  cr_cep_stack.clear();
+  unsigned int cr_cep_index = 0;
+  {
+    /* Inset a sentinel "component end point" */
+    CR_CEP sentinel;
+    sentinel.creation_level = 0;
+    sentinel.discrete_cell_limit = get_nof_vertices();
+    sentinel.next_cr_level = 0;
+    sentinel.next_cep_index = 0;
+    sentinel.first_checked = false;
+    sentinel.best_checked = false;
+    cr_cep_index = 0;
+    cr_cep_stack.push_back(sentinel);
+  }
+  cr_level = 0;
+  if(opt_use_comprec and
+     nucr_find_first_component(cr_level) == true and
+     p.nof_discrete_cells() + cr_component_elements < 
+     cr_cep_stack[cr_cep_index].discrete_cell_limit)
+    {
+      cr_level = p.cr_split_level(0, cr_component);
+      CR_CEP cep;
+      cep.creation_level = 0;
+      cep.discrete_cell_limit = p.nof_discrete_cells() + cr_component_elements;
+      cep.next_cr_level = 0;
+      cep.next_cep_index = cr_cep_index;
+      cep.first_checked = false;
+      cep.best_checked = false;
+      cr_cep_index = cr_cep_stack.size();
+      cr_cep_stack.push_back(cep);
+    }
+
+  /*
+   * Build the root node of the search tree
+   */
+  {
+    TreeNode root;
+    Partition::Cell* split_cell = find_next_cell_to_be_splitted(p.first_cell);
+    root.split_cell_first = split_cell->first;
+    root.split_element = TreeNode::SPLIT_START;
+    root.partition_bt_point = p.set_backtrack_point();
+    root.certificate_index = 0;
+    root.fp_on = true;
+    root.fp_cert_equal = true;
+    root.fp_extendable = TreeNode::MAYBE;
+    root.in_best_path = false;
+    root.cmp_to_best_path = 0;
+    root.long_prune_begin = 0;
+
+    root.failure_recording_ival = 0;
+
+    /* Save component recursion info for backtracking */
+    root.cr_level = cr_level;
+    root.cr_cep_stack_size = cr_cep_stack.size();
+    root.cr_cep_index = cr_cep_index;
+    search_stack.push_back(root);
+  }
+
+  /*
+   * Set status and global flags for search related procedures
+   */
+  in_search = true;
+  /* Do not compare certificates during refinement until the first path has been traversed to the leaf */
+  refine_compare_certificate = false;
+
+
+
+
+  /*
+   * The actual backtracking search
+   */
+  while(!search_stack.empty()) 
+    {
+      TreeNode&          current_node  = search_stack.back();
+      const unsigned int current_level = (unsigned int)search_stack.size()-1;
+
+
+      if(opt_use_comprec)
+	{
+	  CR_CEP& cep = cr_cep_stack[current_node.cr_cep_index];
+	  if(cep.first_checked == true and
+	     current_node.fp_extendable == TreeNode::MAYBE and
+	     !search_stack[cep.creation_level].fp_on)
+	    {
+	      current_node.fp_extendable = TreeNode::NO;
+	    }
+	}
+
+      if(current_node.fp_on)
+	{
+	  if(current_node.split_element == TreeNode::SPLIT_END)
+	    {
+	      search_stack.pop_back();
+	      continue;
+	    }
+	}
+      else
+	{
+	  if(current_node.fp_extendable == TreeNode::YES)
+	    {
+	      search_stack.pop_back();
+	      continue;	      
+	    }
+	  if(current_node.split_element == TreeNode::SPLIT_END)
+	    {
+	      if(opt_use_failure_recording)
+		{
+		  TreeNode& parent_node = search_stack[current_level-1];
+		  if(parent_node.fp_on)
+		    failure_recording_hashes[current_level-1].insert(current_node.failure_recording_ival);
+		}
+	      search_stack.pop_back();
+	      continue;
+	    }
+	  if(current_node.fp_extendable == TreeNode::NO and
+	     (!canonical or current_node.cmp_to_best_path < 0))
+	    {
+	      if(opt_use_failure_recording)
+		{
+		  TreeNode& parent_node = search_stack[current_level-1];
+		  if(parent_node.fp_on)
+		    failure_recording_hashes[current_level-1].insert(current_node.failure_recording_ival);
+		}
+	      search_stack.pop_back();
+	      continue;
+	    }
+	}
+
+      /* Restore partition ... */
+      p.goto_backtrack_point(current_node.partition_bt_point);
+      /* ... and re-remember backtracking point */
+      current_node.partition_bt_point = p.set_backtrack_point();
+
+      /* Restore current path certificate */
+      certificate_index = current_node.certificate_index;
+      refine_current_path_certificate_index = current_node.certificate_index;
+      certificate_current_path.resize(certificate_index);
+
+      /* Fetch split cell information */
+      Partition::Cell * const cell =
+	p.get_cell(p.elements[current_node.split_cell_first]);
+  
+      /* Restore component recursion information */
+      cr_level = current_node.cr_level;
+      cr_cep_stack.resize(current_node.cr_cep_stack_size);
+      cr_cep_index = current_node.cr_cep_index;
+
+
+      /*
+       * Update long prune redundancy sets
+       */
+      if(opt_use_long_prune and current_level >= 1 and !current_node.fp_on)
+	{
+	  unsigned int begin = (current_node.long_prune_begin>long_prune_begin)?current_node.long_prune_begin:long_prune_begin;
+	  for(unsigned int i = begin; i < long_prune_end; i++)
+	    {
+	      const std::vector<bool>& fixed = long_prune_get_fixed(i);
+#if defined(BLISS_CONSISTENCY_CHECKS)
+	      for(unsigned int l = 0; l < search_stack.size()-2; l++)
+		assert(fixed[search_stack[l].split_element]);
+#endif
+	      if(fixed[search_stack[search_stack.size()-1-1].split_element] ==
+		 false)
+		{
+		  long_prune_swap(begin, i);
+		  begin++;
+		  current_node.long_prune_begin = begin;
+		  continue;
+		}
+	    }
+
+	  if(current_node.split_element == TreeNode::SPLIT_START)
+	    {
+	      current_node.needs_long_prune = true;
+	    }
+	  else if(current_node.needs_long_prune)
+	    {
+	      current_node.needs_long_prune = false;
+	      unsigned int begin = (current_node.long_prune_begin>long_prune_begin)?current_node.long_prune_begin:long_prune_begin;
+	      for(unsigned int i = begin; i < long_prune_end; i++)
+		{
+		  const std::vector<bool>& fixed = long_prune_get_fixed(i);
+#if defined(BLISS_CONSISTENCY_CHECKS)
+		  for(unsigned int l = 0; l < search_stack.size()-2; l++)
+		    assert(fixed[search_stack[l].split_element]);
+#endif
+		  assert(fixed[search_stack[current_level-1].split_element] == true);
+		  if(fixed[search_stack[current_level-1].split_element] == false)
+		    {
+		      long_prune_swap(begin, i);
+		      begin++;
+		      current_node.long_prune_begin = begin;
+		      continue;
+		    }
+		  const std::vector<bool>& mcrs = long_prune_get_mcrs(i);
+		  unsigned int* ep = p.elements + cell->first;
+		  for(unsigned int j = cell->length; j > 0; j--, ep++) {
+		    if(mcrs[*ep] == false)
+		      current_node.long_prune_redundant.insert(*ep);
+		  }
+		}
+	    }
+	}
+
+
+      /*
+       * Find the next smallest, non-isomorphic element in the cell and
+       * store it in current_node.split_element
+       */
+      {
+	unsigned int  next_split_element = UINT_MAX;
+	//unsigned int* next_split_element_pos = 0;
+	unsigned int* ep = p.elements + cell->first;
+	if(current_node.fp_on)
+	  {
+	    /* Find the next larger splitting element that is
+	     * a minimal orbit representative w.r.t. first_path_orbits */
+	    for(unsigned int i = cell->length; i > 0; i--, ep++) {
+	      if((int)(*ep) > current_node.split_element and
+		 *ep < next_split_element and
+		 first_path_orbits.is_minimal_representative(*ep)) {
+		next_split_element = *ep;
+		//next_split_element_pos = ep;
+	      }
+	    }
+	  }
+	else if(current_node.in_best_path)
+	  {
+	    /* Find the next larger splitting element that is
+	     * a minimal orbit representative w.r.t. best_path_orbits */
+	    for(unsigned int i = cell->length; i > 0; i--, ep++) {
+	      if((int)(*ep) > current_node.split_element and
+		 *ep < next_split_element and
+		 best_path_orbits.is_minimal_representative(*ep) and
+		 (!opt_use_long_prune or
+		  current_node.long_prune_redundant.find(*ep) ==
+		  current_node.long_prune_redundant.end())) {
+		next_split_element = *ep;
+		//next_split_element_pos = ep;
+	      }
+	    }
+	  }
+	else
+	  {
+	    /* Find the next larger splitting element */
+	    for(unsigned int i = cell->length; i > 0; i--, ep++) {
+	      if((int)(*ep) > current_node.split_element and
+		 *ep < next_split_element and
+		 (!opt_use_long_prune or
+		  current_node.long_prune_redundant.find(*ep) ==
+		  current_node.long_prune_redundant.end())) {
+		next_split_element = *ep;
+		//next_split_element_pos = ep;
+	      }
+	    }
+	  }
+	if(next_split_element == UINT_MAX)
+	  {
+	    /* No more (unexplored children) in the cell */
+	    current_node.split_element = TreeNode::SPLIT_END;
+	    if(current_node.fp_on)
+	      {
+		/* Update group size */
+		const unsigned int index = first_path_orbits.orbit_size(first_path_info[search_stack.size()-1].splitting_element);
+		stats.group_size.multiply(index);
+		stats.group_size_approx *= (long double)index;
+		/*
+		 * Update all_same_level
+		 */
+		if(index == cell->length and all_same_level == current_level+1)
+		  all_same_level = current_level;
+		if(verbstr and verbose_level >= 2) {
+		  fprintf(verbstr,
+			  "Level %u: orbits=%u, index=%u/%u, all_same_level=%u\n",
+			  current_level,
+			  first_path_orbits.nof_orbits(),
+			  index, cell->length,
+			  all_same_level);
+		  fflush(verbstr);
+		}
+	      }
+	    continue;
+	  }
+	
+	/* Split on smallest */
+	current_node.split_element = next_split_element;
+      }
+
+      const unsigned int child_level = current_level+1;
+      /* Update some statistics */
+      stats.nof_nodes++;
+      if(search_stack.size() > stats.max_level)
+	stats.max_level = search_stack.size();
+
+
+
+      /* Set flags and indices for the refiner certificate builder */
+      refine_equal_to_first = current_node.fp_cert_equal;
+      refine_cmp_to_best = current_node.cmp_to_best_path;
+      if(!first_path_info.empty())
+	{
+	  if(refine_equal_to_first)
+	    refine_first_path_subcertificate_end =
+	      first_path_info[search_stack.size()-1].certificate_index +
+	      first_path_info[search_stack.size()-1].subcertificate_length;
+	  if(canonical)
+	    {
+	      if(refine_cmp_to_best == 0)
+		refine_best_path_subcertificate_end =
+		  best_path_info[search_stack.size()-1].certificate_index +
+		  best_path_info[search_stack.size()-1].subcertificate_length;
+	    }
+	  else
+	    refine_cmp_to_best = -1;
+	}
+
+      const bool was_fp_cert_equal = current_node.fp_cert_equal;
+
+      /* Individualize, i.e. split the cell in two, the latter new cell
+       * will be a unit one containing info.split_element */
+      Partition::Cell* const new_cell =
+	p.individualize(cell, current_node.split_element);
+
+      /*
+       * Refine the new partition to equitable
+       */
+      if(cell->is_unit())
+	refine_to_equitable(cell, new_cell);
+      else 
+	refine_to_equitable(new_cell);
+
+
+
+
+      /* Update statistics */
+      if(p.is_discrete())
+	stats.nof_leaf_nodes++;
+
+
+      if(!first_path_info.empty())
+	{
+	  /* We are no longer on the first path */
+	  const unsigned int subcertificate_length = 
+	    certificate_current_path.size() - certificate_index;
+	  if(refine_equal_to_first)
+	    {
+	      /* Was equal to the first path so far */
+	      PathInfo& first_pinfo = first_path_info[current_level];
+	      assert(first_pinfo.certificate_index == certificate_index);
+	      if(subcertificate_length != first_pinfo.subcertificate_length)
+		{
+		  refine_equal_to_first = false;
+		  if(opt_use_failure_recording)
+		    failure_recording_fp_deviation = subcertificate_length;
+		}
+	      else if(first_pinfo.eqref_hash.cmp(eqref_hash) != 0)
+		{
+		  refine_equal_to_first = false;
+		  if(opt_use_failure_recording)
+		    failure_recording_fp_deviation = eqref_hash.get_value();
+		}
+	    }
+	  if(canonical and (refine_cmp_to_best == 0))
+	    {
+	      /* Was equal to the best path so far */
+	      PathInfo& bestp_info = best_path_info[current_level];
+	      assert(bestp_info.certificate_index == certificate_index);
+	      if(subcertificate_length < bestp_info.subcertificate_length)
+		{
+		  refine_cmp_to_best = -1;
+		}
+	      else if(subcertificate_length > bestp_info.subcertificate_length)
+		{
+		  refine_cmp_to_best = 1;
+		}
+	      else if(bestp_info.eqref_hash.cmp(eqref_hash) > 0)
+		{
+		  refine_cmp_to_best = -1;
+		}
+	      else if(bestp_info.eqref_hash.cmp(eqref_hash) < 0)
+		{
+		  refine_cmp_to_best = 1;
+		}
+	    }
+
+	  if(opt_use_failure_recording and
+	     was_fp_cert_equal and
+	     !refine_equal_to_first)
+	    {
+	      UintSeqHash k;
+	      k.update(failure_recording_fp_deviation);
+	      k.update(eqref_hash.get_value());
+	      failure_recording_fp_deviation = k.get_value();
+
+	      if(current_node.fp_on)
+		failure_recording_hashes[current_level].insert(failure_recording_fp_deviation);
+	      else
+		{
+		  for(unsigned int i = current_level; i > 0; i--)
+		    {
+		      if(search_stack[i].fp_on)
+			break;
+		      const FailureRecordingSet& s = failure_recording_hashes[i];
+		      if(i == current_level and
+			 s.find(failure_recording_fp_deviation) != s.end())
+			break;
+		      if(s.find(0) != s.end())
+			break;
+		      search_stack[i].fp_extendable = TreeNode::NO;
+		    }
+		}
+	    }
+
+	  
+	  /* Check if no longer equal to the first path and,
+	   * if canonical labeling is desired, also worse than the
+	   * current best path */
+	  if(refine_equal_to_first == false and
+	     (!canonical or (refine_cmp_to_best < 0)))
+	    {
+	      /* Yes, backtrack */
+	      stats.nof_bad_nodes++;
+	      if(current_node.fp_cert_equal == true and
+		 current_level+1 > all_same_level)
+		{
+		  assert(all_same_level >= 1);
+		  for(unsigned int i = all_same_level;
+		      i < search_stack.size();
+		      i++)
+		    {
+		      search_stack[i].fp_extendable = TreeNode::NO;
+		    }
+		}
+
+	      continue;
+	    }
+	}
+
+#if defined(BLISS_VERIFY_EQUITABLEDNESS)
+      /* The new partition should be equitable */
+      if(!is_equitable())
+	fatal_error("consistency check failed - partition after refinement is not equitable");
+#endif
+
+      /*
+       * Next level search tree node info
+       */
+      TreeNode child_node;
+
+      /* No more in the first path */
+      child_node.fp_on = false;
+      /* No more in the best path */
+      child_node.in_best_path = false;
+
+      child_node.fp_cert_equal = refine_equal_to_first;
+      if(current_node.fp_extendable == TreeNode::NO or
+	 (current_node.fp_extendable == TreeNode::MAYBE and
+	  child_node.fp_cert_equal == false))
+	child_node.fp_extendable = TreeNode::NO;
+      else
+	child_node.fp_extendable = TreeNode::MAYBE;
+      child_node.cmp_to_best_path = refine_cmp_to_best;
+
+      child_node.failure_recording_ival = 0;
+      child_node.cr_cep_stack_size = current_node.cr_cep_stack_size;
+      child_node.cr_cep_index = current_node.cr_cep_index;
+      child_node.cr_level = current_node.cr_level;
+
+      certificate_index = certificate_current_path.size();
+
+      current_node.eqref_hash = eqref_hash;
+      current_node.subcertificate_length =
+	certificate_index - current_node.certificate_index;
+
+
+      /*
+       * The first encountered leaf node at the end of the "first path"?
+       */
+      if(p.is_discrete() and first_path_info.empty())
+	{
+	  //fprintf(stdout, "Level %u: FIRST\n", child_level); fflush(stdout);
+	  stats.nof_canupdates++;
+	  /*
+	   * Update labelings and their inverses
+	   */
+	  update_labeling_and_its_inverse(first_path_labeling,
+					  first_path_labeling_inv);
+	  update_labeling_and_its_inverse(best_path_labeling,
+					  best_path_labeling_inv);
+	  /*
+	   * Reset automorphism array
+	   */
+	  reset_permutation(first_path_automorphism);
+	  reset_permutation(best_path_automorphism);
+	  /*
+	   * Reset orbit information
+	   */
+	  first_path_orbits.reset();
+	  best_path_orbits.reset();
+	  /*
+	   * Reset group size
+	   */
+	  stats.group_size.assign(1);
+	  stats.group_size_approx = 1.0;
+	  /*
+	   * Reset all_same_level
+	   */
+	  all_same_level = child_level;
+	  /*
+	   * Mark the current path to be the first and best one and save it
+	   */
+	  const unsigned int base_size = search_stack.size();
+	  best_path_info.clear();
+	  //fprintf(stdout, " New base is: ");
+	  for(unsigned int i = 0; i < base_size; i++) {
+	    search_stack[i].fp_on = true;
+	    search_stack[i].fp_cert_equal = true;
+	    search_stack[i].fp_extendable = TreeNode::YES;
+	    search_stack[i].in_best_path = true;
+	    search_stack[i].cmp_to_best_path = 0;
+	    PathInfo path_info;
+	    path_info.splitting_element = search_stack[i].split_element;
+	    path_info.certificate_index = search_stack[i].certificate_index;
+	    path_info.eqref_hash = search_stack[i].eqref_hash;
+	    path_info.subcertificate_length = search_stack[i].subcertificate_length;
+	    first_path_info.push_back(path_info);
+	    best_path_info.push_back(path_info);
+	    //fprintf(stdout, "%u ", search_stack[i].split_element);
+	  }
+	  //fprintf(stdout, "\n"); fflush(stdout);
+	  /* Copy certificates */
+	  certificate_first_path = certificate_current_path;
+	  certificate_best_path = certificate_current_path;
+
+	  /* From now on, compare certificates when refining */
+	  refine_compare_certificate = true;
+
+	  if(opt_use_failure_recording)
+	    failure_recording_hashes.resize(base_size);
+	  
+	  /*
+	  for(unsigned int j = 0; j < search_stack.size(); j++)
+	    fprintf(stderr, "%u ", search_stack[j].split_element);
+	  fprintf(stderr, "\n");
+	  p.print(stderr); fprintf(stderr, "\n");
+	  */
+	  
+	  /*
+	   * Backtrack to the previous level
+	   */
+	  continue;
+	}
+
+
+      if(p.is_discrete() and child_node.fp_cert_equal)
+	{
+	  /*
+	   * A leaf node that is equal to the first one.
+	   * An automorphism found: aut[i] = elements[first_path_labeling[i]]
+	   */
+	  goto handle_first_path_automorphism;
+	}
+
+
+      if(!p.is_discrete())
+	{
+	  Partition::Cell* next_split_cell = 0;
+	  /*
+	   * An internal, non-leaf node
+	   */
+	  if(opt_use_comprec)
+	    {
+	      assert(p.nof_discrete_cells() <=
+		     cr_cep_stack[cr_cep_index].discrete_cell_limit);
+	      assert(cr_level == child_node.cr_level);
+
+
+	      if(p.nof_discrete_cells() ==
+		 cr_cep_stack[cr_cep_index].discrete_cell_limit)
+		{
+		  /* We have reached the end of a component */
+		  assert(cr_cep_index != 0);
+		  CR_CEP& cep = cr_cep_stack[cr_cep_index];
+
+		  /* First, compare with respect to the first path */
+		  if(first_path_info.empty() or child_node.fp_cert_equal) {
+		    if(cep.first_checked == false)
+		      {
+			/* First time, go to the next component */
+			cep.first_checked = true;
+		      }
+		    else
+		      {
+			assert(!first_path_info.empty());
+			assert(cep.creation_level < search_stack.size());
+			TreeNode& old_info = search_stack[cep.creation_level];
+			/* If the component was found when on the first path,
+			 * handle the found automorphism as the other
+			 * first path automorphisms */
+			if(old_info.fp_on)
+			  goto handle_first_path_automorphism;
+		      }
+		  }
+
+		  if(canonical and
+		     !first_path_info.empty() and
+		     child_node.cmp_to_best_path >= 0) {
+		    if(cep.best_checked == false)
+		      {
+			/* First time, go to the next component */
+			cep.best_checked = true;
+		      }
+		    else
+		      {
+			assert(cep.creation_level < search_stack.size());
+			TreeNode& old_info = search_stack[cep.creation_level];
+			if(child_node.cmp_to_best_path == 0) {
+			  /* If the component was found when on the best path,
+			   * handle the found automorphism as the other
+			   * best path automorphisms */
+			  if(old_info.in_best_path)
+			    goto handle_best_path_automorphism;
+			  /* Otherwise, we do not remember the automorhism as
+			   * we didn't memorize the path that was invariant
+			   * equal to the best one and passed through the
+			   * component.
+			   * Thus we can only backtrack to the previous level */
+			  child_node.cmp_to_best_path = -1;
+			  if(!child_node.fp_cert_equal)
+			    {
+			      continue;
+			    }
+			}
+			else {
+			  assert(child_node.cmp_to_best_path > 0);
+			  if(old_info.in_best_path)
+			    {
+			      stats.nof_canupdates++;
+			      /*
+			       * Update canonical labeling and its inverse
+			       */
+			      for(unsigned int i = 0; i < N; i++) {
+				if(p.get_cell(p.elements[i])->is_unit()) {
+				  best_path_labeling[p.elements[i]] = i;
+				  best_path_labeling_inv[i] = p.elements[i];
+				}
+			      }
+			      //update_labeling_and_its_inverse(best_path_labeling, best_path_labeling_inv);
+			      /* Reset best path automorphism */
+			      reset_permutation(best_path_automorphism);
+			      /* Reset best path orbit structure */
+			      best_path_orbits.reset();
+			      /* Mark to be the best one and save prefix */
+			      unsigned int postfix_start = cep.creation_level;
+			      assert(postfix_start < best_path_info.size());
+			      while(p.get_cell(best_path_info[postfix_start].splitting_element)->is_unit()) {
+				postfix_start++;
+				assert(postfix_start < best_path_info.size());
+			      }
+			      unsigned int postfix_start_cert = best_path_info[postfix_start].certificate_index;
+			      std::vector<PathInfo> best_path_temp = best_path_info;
+			      best_path_info.clear();
+			      for(unsigned int i = 0; i < search_stack.size(); i++) {
+				TreeNode& ss_info = search_stack[i];
+				PathInfo  bp_info;
+				ss_info.cmp_to_best_path = 0;
+				ss_info.in_best_path = true;
+				bp_info.splitting_element = ss_info.split_element;
+				bp_info.certificate_index = ss_info.certificate_index;
+				bp_info.subcertificate_length = ss_info.subcertificate_length;
+				bp_info.eqref_hash = ss_info.eqref_hash;
+				best_path_info.push_back(bp_info);
+			      }
+			      /* Copy the postfix of the previous best path */
+			      for(unsigned int i = postfix_start;
+				  i < best_path_temp.size();
+				  i++)
+				{
+				  best_path_info.push_back(best_path_temp[i]);
+				  best_path_info[best_path_info.size()-1].certificate_index =
+				    best_path_info[best_path_info.size()-2].certificate_index +
+				    best_path_info[best_path_info.size()-2].subcertificate_length;
+				}
+			      std::vector<unsigned int> certificate_best_path_old = certificate_best_path;
+			      certificate_best_path = certificate_current_path;
+			      for(unsigned int i = postfix_start_cert;  i < certificate_best_path_old.size(); i++)
+				certificate_best_path.push_back(certificate_best_path_old[i]);
+			      assert(certificate_best_path.size() == best_path_info.back().certificate_index + best_path_info.back().subcertificate_length);
+			      /* Backtrack to the previous level */
+			      continue;
+			    }
+			}
+		      }
+		  }
+
+		  /* No backtracking performed, go to next componenet */
+		  cr_level = cep.next_cr_level;
+		  cr_cep_index = cep.next_cep_index;
+		}
+
+	      /* Check if the current component has been split into
+	       * new non-uniformity subcomponents */
+	      //if(nucr_find_first_component(cr_level) == true and
+	      // p.nof_discrete_cells() + cr_component_elements <
+	      // cr_cep_stack[cr_cep_index].discrete_cell_limit)
+	      if(nucr_find_first_component(cr_level, cr_component,
+					   cr_component_elements,
+					   next_split_cell) == true and
+		 p.nof_discrete_cells() + cr_component_elements <
+		 cr_cep_stack[cr_cep_index].discrete_cell_limit)
+		{
+		  const unsigned int next_cr_level =
+		    p.cr_split_level(cr_level, cr_component);
+		  CR_CEP cep;
+		  cep.creation_level = search_stack.size();
+		  cep.discrete_cell_limit =
+		    p.nof_discrete_cells() + cr_component_elements;
+		  cep.next_cr_level = cr_level;
+		  cep.next_cep_index = cr_cep_index;
+		  cep.first_checked = false;
+		  cep.best_checked = false;
+		  cr_cep_index = cr_cep_stack.size();
+		  cr_cep_stack.push_back(cep);
+		  cr_level = next_cr_level;
+		}
+	    }
+
+
+	  /*
+	   * Build the next node info
+	   */
+	  /* Find the next cell to be splitted */
+	  if(!next_split_cell)
+	    next_split_cell = find_next_cell_to_be_splitted(p.get_cell(p.elements[current_node.split_cell_first]));
+	  //Partition::Cell * const next_split_cell = find_next_cell_to_be_splitted(p.get_cell(p.elements[current_node.split_cell_first]));
+	  child_node.split_cell_first = next_split_cell->first;
+	  child_node.split_element = TreeNode::SPLIT_START;
+	  child_node.certificate_index = certificate_index;
+	  child_node.partition_bt_point = p.set_backtrack_point();
+	  child_node.long_prune_redundant.clear();
+	  child_node.long_prune_begin = current_node.long_prune_begin;
+
+	  /* Save component recursion info for backtracking */
+	  child_node.cr_level = cr_level;
+	  child_node.cr_cep_stack_size = cr_cep_stack.size();
+	  child_node.cr_cep_index = cr_cep_index;
+
+	  search_stack.push_back(child_node);
+	  continue;
+	}
+
+      /*
+       * A leaf node not in the first path or equivalent to the first path
+       */
+
+
+
+      if(child_node.cmp_to_best_path > 0)
+	{
+	  /*
+	   * A new, better representative found
+	   */
+	  //fprintf(stdout, "Level %u: NEW BEST\n", child_level); fflush(stdout);
+	  stats.nof_canupdates++;
+	  /*
+	   * Update canonical labeling and its inverse
+	   */
+	  update_labeling_and_its_inverse(best_path_labeling,
+					  best_path_labeling_inv);
+	  /* Reset best path automorphism */
+	  reset_permutation(best_path_automorphism);
+	  /* Reset best path orbit structure */
+	  best_path_orbits.reset();
+	  /*
+	   * Mark the current path to be the best one and save it
+	   */
+	  const unsigned int base_size = search_stack.size();
+	  assert(current_level+1 == base_size);
+	  best_path_info.clear();
+	  for(unsigned int i = 0; i < base_size; i++) {
+	    search_stack[i].cmp_to_best_path = 0;
+	    search_stack[i].in_best_path = true;
+	    PathInfo path_info;
+	    path_info.splitting_element = search_stack[i].split_element;
+	    path_info.certificate_index = search_stack[i].certificate_index;
+	    path_info.subcertificate_length = search_stack[i].subcertificate_length;
+	    path_info.eqref_hash = search_stack[i].eqref_hash;
+	    best_path_info.push_back(path_info);
+	  }
+	  certificate_best_path = certificate_current_path;
+	  /*
+	   * Backtrack to the previous level
+	   */
+	  continue;
+	}
+
+      
+    handle_best_path_automorphism:
+      /*
+       *
+       * Best path automorphism handling
+       *
+       */
+      {
+
+	/*
+	 * Equal to the previous best path
+	 */
+	if(p.is_discrete())
+	  {
+#if defined(BLISS_CONSISTENCY_CHECKS)
+	    /* Verify that the automorphism is correctly built */
+	    for(unsigned int i = 0; i < N; i++)
+	      assert(best_path_automorphism[i] ==
+		     p.elements[best_path_labeling[i]]);
+#endif
+	  }
+	else
+	  {
+	    /* An automorphism that was found before the partition was discrete.
+	     * Set the image of all elements in non-disrete cells accordingly */
+	    for(Partition::Cell* c = p.first_nonsingleton_cell; c;
+		c = c->next_nonsingleton) {
+	      for(unsigned int i = c->first; i < c->first+c->length; i++)
+		if(p.get_cell(p.elements[best_path_labeling[p.elements[i]]])->is_unit())
+		  best_path_automorphism[p.elements[best_path_labeling[p.elements[i]]]] = p.elements[i];
+		else
+		  best_path_automorphism[p.elements[i]] = p.elements[i];
+	    }
+	  }
+	
+#if defined(BLISS_VERIFY_AUTOMORPHISMS)
+	/* Verify that it really is an automorphism */
+	if(!is_automorphism(best_path_automorphism))
+	  fatal_error("Best path automorhism validation check failed");
+#endif
+	
+	unsigned int gca_level_with_first = 0;
+	for(unsigned int i = search_stack.size(); i > 0; i--) {
+	  if((int)first_path_info[gca_level_with_first].splitting_element !=
+	     search_stack[gca_level_with_first].split_element)
+	    break;
+	  gca_level_with_first++;
+	}
+
+	unsigned int gca_level_with_best = 0;
+	for(unsigned int i = search_stack.size(); i > 0; i--) {
+	  if((int)best_path_info[gca_level_with_best].splitting_element !=
+	     search_stack[gca_level_with_best].split_element)
+	    break;
+	  gca_level_with_best++;
+	}
+
+	if(opt_use_long_prune)
+	  {
+	    /* Record automorphism */
+	    long_prune_add_automorphism(best_path_automorphism);
+	  }
+	    
+	/*
+	 * Update orbit information
+	 */
+	update_orbit_information(best_path_orbits, best_path_automorphism);
+
+	/*
+	 * Update orbit information
+	 */
+	const unsigned int nof_old_orbits = first_path_orbits.nof_orbits();
+	update_orbit_information(first_path_orbits, best_path_automorphism);
+	if(nof_old_orbits != first_path_orbits.nof_orbits())
+	  {
+	    /* Some orbits were merged */
+	    /* Report automorphism */
+	    if(report_hook)
+	      (*report_hook)(report_user_param,
+			     get_nof_vertices(),
+			     best_path_automorphism);
+	    /* Update statistics */
+	    stats.nof_generators++;
+	  }
+	  
+	/*
+	 * Compute backjumping level
+	 */
+	unsigned int backjumping_level = current_level+1-1;
+	if(!first_path_orbits.is_minimal_representative(search_stack[gca_level_with_first].split_element))
+	  {
+	    backjumping_level = gca_level_with_first;
+	  }
+	else
+	  {
+	    assert(!best_path_orbits.is_minimal_representative(search_stack[gca_level_with_best].split_element));
+	    backjumping_level = gca_level_with_best;
+	  }
+	/* Backtrack */
+	search_stack.resize(backjumping_level + 1);
+	continue;
+      }
+
+
+      _INTERNAL_ERROR();
+
+      
+    handle_first_path_automorphism:
+      /*
+       *
+       * A first-path automorphism: aut[i] = elements[first_path_labeling[i]]
+       *
+       */
+      
+
+      if(p.is_discrete())
+	{
+#if defined(BLISS_CONSISTENCY_CHECKS)
+	  /* Verify that the complete automorphism is correctly built */
+	  for(unsigned int i = 0; i < N; i++)
+	    assert(first_path_automorphism[i] ==
+		   p.elements[first_path_labeling[i]]);
+#endif
+	}
+      else
+	{
+	  /* An automorphism that was found before the partition was discrete.
+	   * Set the image of all elements in non-disrete cells accordingly */
+	  for(Partition::Cell* c = p.first_nonsingleton_cell; c;
+	      c = c->next_nonsingleton) {
+	    for(unsigned int i = c->first; i < c->first+c->length; i++)
+	      if(p.get_cell(p.elements[first_path_labeling[p.elements[i]]])->is_unit())
+		first_path_automorphism[p.elements[first_path_labeling[p.elements[i]]]] = p.elements[i];
+	      else
+		first_path_automorphism[p.elements[i]] = p.elements[i];
+	  }
+	}
+
+#if defined(BLISS_VERIFY_AUTOMORPHISMS)
+      /* Verify that it really is an automorphism */
+      if(!is_automorphism(first_path_automorphism))
+	fatal_error("First path automorphism validation check failed");
+#endif
+      
+      if(opt_use_long_prune)
+	{
+	  long_prune_add_automorphism(first_path_automorphism);
+	}
+      
+      /*
+       * Update orbit information
+       */
+      update_orbit_information(first_path_orbits, first_path_automorphism);
+      
+      /*
+       * Compute backjumping level
+       */
+      for(unsigned int i = 0; i < search_stack.size(); i++) {
+	TreeNode& n = search_stack[i];
+	if(n.fp_on) {
+	  ;
+	} else {
+	  n.fp_extendable = TreeNode::YES;
+	}
+      }
+
+      /* Report automorphism by calling the user defined hook function */
+      if(report_hook)
+	(*report_hook)(report_user_param,
+		       get_nof_vertices(),
+		       first_path_automorphism);
+
+      /* Update statistics */
+      stats.nof_generators++;
+      continue;
+
+    } /* while(!search_stack.empty()) */
+
+
+
+
+  /* Free "long prune" technique memory */
+  if(opt_use_long_prune)
+    long_prune_deallocate();
+
+  /* Release component recursion data in partition */
+  if(opt_use_comprec)
+    p.cr_free();
+}
+
+
+
+
+void
+AbstractGraph::find_automorphisms(Stats& stats,
+				  void (*hook)(void *user_param,
+					       unsigned int n,
+					       const unsigned int *aut),
+				  void *user_param)
+{
+  report_hook = hook;
+  report_user_param = user_param;
+
+  search(false, stats);
+
+  if(first_path_labeling)
+    {
+      free(first_path_labeling);
+      first_path_labeling = 0;
+    }
+  if(best_path_labeling)
+    {
+      free(best_path_labeling);
+      best_path_labeling = 0;
+    }
+}
+
+
+const unsigned int *
+AbstractGraph::canonical_form(Stats& stats,
+			      void (*hook)(void *user_param,
+					   unsigned int n,
+					   const unsigned int *aut),
+			      void *user_param)
+{
+
+  report_hook = hook;
+  report_user_param = user_param;
+
+  search(true, stats);
+
+  return best_path_labeling;
+}
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Routines for directed graphs
+ *
+ *-------------------------------------------------------------------------*/
+
+Digraph::Vertex::Vertex()
+{
+  color = 0;
+}
+
+
+Digraph::Vertex::~Vertex()
+{
+  ;
+}
+
+
+void
+Digraph::Vertex::add_edge_to(const unsigned int other_vertex)
+{
+  edges_out.push_back(other_vertex);
+}
+
+
+void
+Digraph::Vertex::add_edge_from(const unsigned int other_vertex)
+{
+  edges_in.push_back(other_vertex);
+}
+
+
+void
+Digraph::Vertex::remove_duplicate_edges(std::vector<bool>& tmp)
+{
+#if defined(BLISS_CONSISTENCY_CHECKS)
+  /* Pre-conditions  */
+  for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false);
+#endif
+  for(std::vector<unsigned int>::iterator iter = edges_out.begin();
+      iter != edges_out.end(); )
+    {
+      const unsigned int dest_vertex = *iter;
+      if(tmp[dest_vertex] == true)
+	{
+	  /* A duplicate edge found! */
+	  iter = edges_out.erase(iter);
+	}
+      else
+	{
+	  /* Not seen earlier, mark as seen */
+	  tmp[dest_vertex] = true;
+	  iter++;
+	}
+    }
+
+  /* Clear tmp */
+  for(std::vector<unsigned int>::iterator iter = edges_out.begin();
+      iter != edges_out.end();
+      iter++)
+    {
+      tmp[*iter] = false;
+    }
+
+  for(std::vector<unsigned int>::iterator iter = edges_in.begin();
+      iter != edges_in.end(); )
+    {
+      const unsigned int dest_vertex = *iter;
+      if(tmp[dest_vertex] == true)
+	{
+	  /* A duplicate edge found! */
+	  iter = edges_in.erase(iter);
+	}
+      else
+	{
+	  /* Not seen earlier, mark as seen */
+	  tmp[dest_vertex] = true;
+	  iter++;
+	}
+    }
+
+  /* Clear tmp */
+  for(std::vector<unsigned int>::iterator iter = edges_in.begin();
+      iter != edges_in.end();
+      iter++)
+    {
+      tmp[*iter] = false;
+    }
+#if defined(BLISS_CONSISTENCY_CHECKS)
+  /* Post-conditions  */
+  for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false);
+#endif
+}
+
+
+/**
+ * Sort the edges entering and leaving the vertex according to
+ * the vertex number of the other edge end.
+ * Time complexity: O(e log(e)), where e is the number of edges
+ * entering/leaving the vertex.
+ */
+void
+Digraph::Vertex::sort_edges()
+{
+  std::sort(edges_in.begin(), edges_in.end());
+  std::sort(edges_out.begin(), edges_out.end());
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Constructor and destructor for directed graphs
+ *
+ *-------------------------------------------------------------------------*/
+
+
+Digraph::Digraph(const unsigned int nof_vertices)
+{
+  vertices.resize(nof_vertices);
+  sh = shs_flm;
+}
+
+
+Digraph::~Digraph()
+{
+  ;
+}
+
+
+unsigned int
+Digraph::add_vertex(const unsigned int color)
+{
+  const unsigned int new_vertex_num = vertices.size();
+  vertices.resize(new_vertex_num + 1);
+  vertices.back().color = color;
+  return new_vertex_num;
+}
+
+
+void
+Digraph::add_edge(const unsigned int vertex1, const unsigned int vertex2)
+{
+  assert(vertex1 < get_nof_vertices());
+  assert(vertex2 < get_nof_vertices());
+  vertices[vertex1].add_edge_to(vertex2);
+  vertices[vertex2].add_edge_from(vertex1);
+}
+
+
+void
+Digraph::change_color(const unsigned int vertex, const unsigned int new_color)
+{
+  assert(vertex < get_nof_vertices());
+  vertices[vertex].color = new_color;
+}
+
+
+void
+Digraph::sort_edges()
+{
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    vertices[i].sort_edges();
+}
+
+
+int
+Digraph::cmp(Digraph& other)
+{
+  /* Compare the numbers of vertices */
+  if(get_nof_vertices() < other.get_nof_vertices())
+    return -1;
+  if(get_nof_vertices() > other.get_nof_vertices())
+    return 1;
+  /* Compare vertex colors */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      if(vertices[i].color < other.vertices[i].color)
+	return -1;
+      if(vertices[i].color > other.vertices[i].color)
+	return 1;
+    }
+  /* Compare vertex degrees */
+  remove_duplicate_edges();
+  other.remove_duplicate_edges();
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      if(vertices[i].nof_edges_in() < other.vertices[i].nof_edges_in())
+	return -1;
+      if(vertices[i].nof_edges_in() > other.vertices[i].nof_edges_in())
+	return 1;
+      if(vertices[i].nof_edges_out() < other.vertices[i].nof_edges_out())
+	return -1;
+      if(vertices[i].nof_edges_out() > other.vertices[i].nof_edges_out())
+	return 1;
+    }
+  /* Compare edges */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex& v1 = vertices[i];
+      Vertex& v2 = other.vertices[i];
+      v1.sort_edges();
+      v2.sort_edges();
+      std::vector<unsigned int>::const_iterator ei1 = v1.edges_in.begin();
+      std::vector<unsigned int>::const_iterator ei2 = v2.edges_in.begin();
+      while(ei1 != v1.edges_in.end())
+	{
+	  if(*ei1 < *ei2)
+	    return -1;
+	  if(*ei1 > *ei2)
+	    return 1;
+	  ei1++;
+	  ei2++;
+	}
+      ei1 = v1.edges_out.begin();
+      ei2 = v2.edges_out.begin();
+      while(ei1 != v1.edges_out.end())
+	{
+	  if(*ei1 < *ei2)
+	    return -1;
+	  if(*ei1 > *ei2)
+	    return 1;
+	  ei1++;
+	  ei2++;
+	}
+    }
+  return 0;
+}
+
+
+
+
+Digraph*
+Digraph::permute(const std::vector<unsigned int>& perm) const
+{
+  Digraph* const g = new Digraph(get_nof_vertices());
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      const Vertex& v = vertices[i];
+      g->change_color(perm[i], v.color);
+      for(std::vector<unsigned int>::const_iterator ei = v.edges_out.begin();
+	  ei != v.edges_out.end();
+	  ei++)
+	{
+	  g->add_edge(perm[i], perm[*ei]);
+	}
+    }
+  g->sort_edges();
+  return g;
+}
+
+
+Digraph*
+Digraph::permute(const unsigned int* const perm) const
+{
+  Digraph* const g = new Digraph(get_nof_vertices());
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      const Vertex &v = vertices[i];
+      g->change_color(perm[i], v.color);
+      for(std::vector<unsigned int>::const_iterator ei = v.edges_out.begin();
+	  ei != v.edges_out.end();
+	  ei++)
+	{
+	  g->add_edge(perm[i], perm[*ei]);
+	}
+    }
+  g->sort_edges();
+  return g;
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Print graph in graphviz format
+ *
+ *-------------------------------------------------------------------------*/
+
+
+void
+Digraph::write_dot(const char* const filename)
+{
+  FILE* const fp = fopen(filename, "w");
+  if(fp)
+    {
+      write_dot(fp);
+      fclose(fp);
+    }
+}
+
+
+void
+Digraph::write_dot(FILE* const fp)
+{
+  remove_duplicate_edges();
+
+  fprintf(fp, "digraph g {\n");
+
+  unsigned int vnum = 0;
+  for(std::vector<Vertex>::const_iterator vi = vertices.begin();
+      vi != vertices.end();
+      vi++, vnum++)
+    {
+      const Vertex& v = *vi;
+      fprintf(fp, "v%u [label=\"%u:%u\"];\n", vnum, vnum, v.color);
+      for(std::vector<unsigned int>::const_iterator ei = v.edges_out.begin();
+	  ei != v.edges_out.end();
+	  ei++)
+	{
+	  fprintf(fp, "v%u -> v%u\n", vnum, *ei);
+	}
+    }
+
+  fprintf(fp, "}\n");
+}
+
+
+void
+Digraph::remove_duplicate_edges()
+{
+  std::vector<bool> tmp(get_nof_vertices(), false);
+
+  for(std::vector<Vertex>::iterator vi = vertices.begin();
+      vi != vertices.end();
+      vi++)
+    {
+#if defined(BLISS_EXPENSIVE_CONSISTENCY_CHECKS)
+      for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false);
+#endif
+      (*vi).remove_duplicate_edges(tmp);
+    }
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Get a hash value for the graph.
+ *
+ *-------------------------------------------------------------------------*/
+
+unsigned int
+Digraph::get_hash()
+{
+  remove_duplicate_edges();
+  sort_edges();
+
+  UintSeqHash h;
+
+  h.update(get_nof_vertices());
+
+  /* Hash the color of each vertex */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      h.update(vertices[i].color);
+    }
+
+  /* Hash the edges */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex &v = vertices[i];
+      for(std::vector<unsigned int>::const_iterator ei = v.edges_out.begin();
+	  ei != v.edges_out.end();
+	  ei++)
+	{
+	  h.update(i);
+	  h.update(*ei);
+	}
+    }
+
+  return h.get_value();
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Read directed graph in the DIMACS format.
+ * Returns 0 if an error occurred.
+ *
+ *-------------------------------------------------------------------------*/
+
+Digraph*
+Digraph::read_dimacs(FILE* const fp, FILE* const errstr)
+{
+  Digraph* g = 0;
+  unsigned int nof_vertices;
+  unsigned int nof_edges;
+  unsigned int line_num = 1;
+
+  const bool verbose = false;
+  FILE* const verbstr = stdout;
+  
+  /* Read comments and the problem definition line */
+  while(1)
+    {
+      int c = getc(fp);
+      if(c == 'c')
+	{
+	  /* A comment, ignore the rest of the line */
+	  while((c = getc(fp)) != '\n')
+	    {
+	      if(c == EOF) {
+		if(errstr)
+		  fprintf(errstr, "error in line %u: not in DIMACS format\n",
+			  line_num);
+		goto error_exit;
+	      }
+	    }
+	  line_num++;
+	  continue;
+	}
+      if(c == 'p')
+	{
+	  /* The problem definition line */
+	  if(fscanf(fp, " edge %u %u\n", &nof_vertices, &nof_edges) != 2)
+	    {
+	      if(errstr)
+		fprintf(errstr, "error in line %u: not in DIMACS format\n",
+			line_num);
+	      goto error_exit;
+	    }
+	  line_num++;
+	  break;
+	}
+      if(errstr)
+	fprintf(errstr, "error in line %u: not in DIMACS format\n", line_num);
+      goto error_exit;
+    }
+  
+  if(nof_vertices <= 0)
+    {
+      if(errstr)
+	fprintf(errstr, "error: no vertices\n");
+      goto error_exit;
+    }
+  if(verbose)
+    {
+      fprintf(verbstr, "Instance has %d vertices and %d edges\n",
+	      nof_vertices, nof_edges);
+      fflush(verbstr);
+    }
+
+  g = new Digraph(nof_vertices);
+
+  //
+  // Read vertex colors
+  //
+  if(verbose)
+    {
+      fprintf(verbstr, "Reading vertex colors...\n");
+      fflush(verbstr);
+    }
+  while(1)
+    {
+      int c = getc(fp);
+      if(c != 'n')
+	{
+	  ungetc(c, fp);
+	  break;
+	}
+      ungetc(c, fp);
+      unsigned int vertex;
+      unsigned int color;
+      if(fscanf(fp, "n %u %u\n", &vertex, &color) != 2)
+	{
+	  if(errstr)
+	    fprintf(errstr, "error in line %u: not in DIMACS format\n",
+		    line_num);
+	  goto error_exit;
+	}
+      if(!((vertex >= 1) && (vertex <= nof_vertices)))
+	{
+	  if(errstr)
+	    fprintf(errstr,
+		    "error in line %u: vertex %u not in range [1,...%u]\n",
+		    line_num, vertex, nof_vertices);
+	  goto error_exit;
+	}
+      line_num++;
+      g->change_color(vertex - 1, color);
+    }
+  if(verbose)
+    {
+      fprintf(verbstr, "Done\n");
+      fflush(verbstr);
+    }
+
+  //
+  // Read edges
+  //
+  if(verbose)
+    {
+      fprintf(verbstr, "Reading edges...\n");
+      fflush(verbstr);
+    }
+  for(unsigned i = 0; i < nof_edges; i++)
+    {
+      unsigned int from, to;
+      if(fscanf(fp, "e %u %u\n", &from, &to) != 2)
+	{
+	  if(errstr)
+	    fprintf(errstr, "error in line %u: not in DIMACS format\n",
+		    line_num);
+	  goto error_exit;
+	}
+      if(not((1 <= from) and (from <= nof_vertices)))
+	{
+	  if(errstr)
+	    fprintf(errstr,
+		    "error in line %u: vertex %u not in range [1,...%u]\n",
+		    line_num, from, nof_vertices);
+	  goto error_exit;
+	}
+      if(not((1 <= to) and (to <= nof_vertices)))
+	{
+	  if(errstr)
+	    fprintf(errstr,
+		    "error in line %u: vertex %u not in range [1,...%u]\n",
+		    line_num, to, nof_vertices);
+	  goto error_exit;
+	}
+      line_num++;
+      g->add_edge(from-1, to-1);
+    }
+  if(verbose)
+    {
+      fprintf(verbstr, "Done\n");
+      fflush(verbstr);
+    }
+  
+  return g;
+
+ error_exit:
+  if(g)
+    delete g;
+  return 0;
+}
+
+
+
+
+
+void
+Digraph::write_dimacs(FILE* const fp)
+{
+  remove_duplicate_edges();
+  sort_edges();
+
+  /* First count the total number of edges */
+  unsigned int nof_edges = 0;
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      nof_edges += vertices[i].edges_out.size();
+    }
+
+  /* Output the "header" line */
+  fprintf(fp, "p edge %u %u\n", get_nof_vertices(), nof_edges);
+
+  /* Print the color of each vertex */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex& v = vertices[i];
+      fprintf(fp, "n %u %u\n", i+1, v.color);
+      /*
+      if(v.color != 0)
+	{
+	  fprintf(fp, "n %u %u\n", i+1, v.color);
+	}
+      */
+    }
+
+  /* Print the edges */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex& v = vertices[i];
+      for(std::vector<unsigned int>::const_iterator ei = v.edges_out.begin();
+	  ei != v.edges_out.end();
+	  ei++)
+	{
+	  fprintf(fp, "e %u %u\n", i+1, (*ei)+1);
+	}
+    }
+}
+
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Partition independent invariants
+ *
+ *-------------------------------------------------------------------------*/
+
+unsigned int
+Digraph::vertex_color_invariant(const Digraph* const g, const unsigned int vnum)
+{
+  return g->vertices[vnum].color;
+}
+
+unsigned int
+Digraph::indegree_invariant(const Digraph* const g, const unsigned int vnum)
+{
+  return g->vertices[vnum].nof_edges_in();
+}
+
+unsigned int
+Digraph::outdegree_invariant(const Digraph* const g, const unsigned int vnum)
+{
+  return g->vertices[vnum].nof_edges_out();
+}
+
+unsigned int
+Digraph::selfloop_invariant(const Digraph* const g, const unsigned int vnum)
+{
+  /* Quite inefficient but luckily not in the critical path */
+  const Vertex& v = g->vertices[vnum];
+  for(std::vector<unsigned int>::const_iterator ei = v.edges_out.begin();
+      ei != v.edges_out.end();
+      ei++)
+    {
+      if(*ei == vnum)
+	return 1;
+    }
+  return 0;
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Refine the partition p according to a partition independent invariant
+ *
+ *-------------------------------------------------------------------------*/
+
+bool
+Digraph::refine_according_to_invariant(unsigned int (*inv)(const Digraph* const g,
+							   const unsigned int v))
+{
+  bool refined = false;
+
+  for(Partition::Cell* cell = p.first_nonsingleton_cell; cell; )
+    {
+      
+      Partition::Cell* const next_cell = cell->next_nonsingleton;
+      const unsigned int* ep = p.elements + cell->first;
+      for(unsigned int i = cell->length; i > 0; i--, ep++)
+	{
+	  unsigned int ival = inv(this, *ep);
+	  p.invariant_values[*ep] = ival;
+	  if(ival > cell->max_ival) {
+	    cell->max_ival = ival;
+	    cell->max_ival_count = 1;
+	  }
+	  else if(ival == cell->max_ival) {
+	    cell->max_ival_count++;
+	  }
+	}
+      Partition::Cell* const last_new_cell = p.zplit_cell(cell, true);
+      refined |= (last_new_cell != cell);
+      cell = next_cell;
+    }
+
+  return refined;
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Split the neighbourhood of a cell according to the equitable invariant
+ *
+ *-------------------------------------------------------------------------*/
+
+bool
+Digraph::split_neighbourhood_of_cell(Partition::Cell* const cell)
+{
+  
+
+  const bool was_equal_to_first = refine_equal_to_first;
+
+  if(compute_eqref_hash)
+    {
+      eqref_hash.update(cell->first);
+      eqref_hash.update(cell->length);
+    }
+
+  const unsigned int* ep = p.elements + cell->first;
+  for(unsigned int i = cell->length; i > 0; i--)
+    {
+      const Vertex& v = vertices[*ep++];
+      
+      std::vector<unsigned int>::const_iterator ei = v.edges_out.begin();
+      for(unsigned int j = v.nof_edges_out(); j != 0; j--)
+	{
+	  const unsigned int dest_vertex = *ei++;
+	  Partition::Cell* const neighbour_cell = p.get_cell(dest_vertex);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  const unsigned int ival = ++p.invariant_values[dest_vertex];
+	  if(ival > neighbour_cell->max_ival) {
+	    neighbour_cell->max_ival = ival;
+	    neighbour_cell->max_ival_count = 1;
+	    if(ival == 1)
+	      neighbour_heap.insert(neighbour_cell->first);
+	  }
+	  else if(ival == neighbour_cell->max_ival) {
+	    neighbour_cell->max_ival_count++;
+	  }
+	}
+    }
+
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell* const neighbour_cell = p.get_cell(p.elements[start]);
+      
+      if(compute_eqref_hash)
+	{
+	  eqref_hash.update(neighbour_cell->first);
+	  eqref_hash.update(neighbour_cell->length);
+	  eqref_hash.update(neighbour_cell->max_ival);
+	  eqref_hash.update(neighbour_cell->max_ival_count);
+	}
+
+
+      Partition::Cell* const last_new_cell = p.zplit_cell(neighbour_cell, true);
+
+      /* Update certificate and hash if needed */
+      const Partition::Cell* c = neighbour_cell;
+      while(1)
+	{
+	  if(in_search)
+	    {
+	      /* Build certificate */
+	      cert_add_redundant(CERT_SPLIT, c->first, c->length);
+	      /* No need to continue? */
+	      if(refine_compare_certificate and
+		 (refine_equal_to_first == false) and
+		 (refine_cmp_to_best < 0))
+		goto worse_exit;
+	    }
+	  if(compute_eqref_hash)
+	    {
+	      eqref_hash.update(c->first);
+	      eqref_hash.update(c->length);
+	    }
+	  if(c == last_new_cell)
+	    break;
+	  c = c->next;
+	}
+    }
+
+  if(cell->is_in_splitting_queue())
+    {
+      return false;
+    }
+
+
+  ep = p.elements + cell->first;
+  for(unsigned int i = cell->length; i > 0; i--)
+    {
+      const Vertex& v = vertices[*ep++];
+
+      std::vector<unsigned int>::const_iterator ei = v.edges_in.begin();
+      for(unsigned int j = v.nof_edges_in(); j > 0; j--)
+	{
+	  const unsigned int dest_vertex = *ei++;
+	  Partition::Cell* const neighbour_cell = p.get_cell(dest_vertex);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  const unsigned int ival = ++p.invariant_values[dest_vertex];
+	  if(ival > neighbour_cell->max_ival)
+	    {
+	      neighbour_cell->max_ival = ival;
+	      neighbour_cell->max_ival_count = 1;
+	      if(ival == 1)
+		neighbour_heap.insert(neighbour_cell->first);
+	    }
+	  else if(ival == neighbour_cell->max_ival) {
+	    neighbour_cell->max_ival_count++;
+	  }
+	}
+    }
+
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell* const neighbour_cell = p.get_cell(p.elements[start]);
+
+      if(compute_eqref_hash)
+	{
+	  eqref_hash.update(neighbour_cell->first);
+	  eqref_hash.update(neighbour_cell->length);
+	  eqref_hash.update(neighbour_cell->max_ival);
+	  eqref_hash.update(neighbour_cell->max_ival_count);
+	}
+
+      Partition::Cell* const last_new_cell = p.zplit_cell(neighbour_cell, true);
+
+      /* Update certificate and hash if needed */
+      const Partition::Cell* c = neighbour_cell;
+      while(1)
+	{
+	  if(in_search)
+	    {
+	      /* Build certificate */
+	      cert_add_redundant(CERT_SPLIT, c->first, c->length);
+	      /* No need to continue? */
+	      if(refine_compare_certificate and
+		 (refine_equal_to_first == false) and
+		 (refine_cmp_to_best < 0))
+		goto worse_exit;
+	    }
+	  if(compute_eqref_hash)
+	    {
+	      eqref_hash.update(c->first);
+	      eqref_hash.update(c->length);
+	    }
+	  if(c == last_new_cell)
+	    break;
+	  c = c->next;
+	}
+    }
+
+
+  if(refine_compare_certificate and
+     (refine_equal_to_first == false) and
+     (refine_cmp_to_best < 0))
+    return true;
+
+  return false;
+  
+ worse_exit:
+  /* Clear neighbour heap */
+  UintSeqHash rest;
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell* const neighbour_cell = p.get_cell(p.elements[start]);
+      if(opt_use_failure_recording and was_equal_to_first)
+	{
+	  rest.update(neighbour_cell->first);
+	  rest.update(neighbour_cell->length);
+	  rest.update(neighbour_cell->max_ival);
+	  rest.update(neighbour_cell->max_ival_count);
+	}
+      neighbour_cell->max_ival = 0;
+      neighbour_cell->max_ival_count = 0;
+      p.clear_ivs(neighbour_cell);
+    }
+  if(opt_use_failure_recording and was_equal_to_first)
+    {
+      for(unsigned int i = p.splitting_queue.size(); i > 0; i--)
+	{
+	  Partition::Cell* const cell = p.splitting_queue.pop_front();
+	  rest.update(cell->first);
+	  rest.update(cell->length);
+	  p.splitting_queue.push_back(cell);
+	}
+      rest.update(failure_recording_fp_deviation);
+      failure_recording_fp_deviation = rest.get_value();
+    }
+
+   return true;
+}
+
+
+bool
+Digraph::split_neighbourhood_of_unit_cell(Partition::Cell* const unit_cell)
+{
+
+
+  const bool was_equal_to_first = refine_equal_to_first;
+
+  if(compute_eqref_hash)
+    {
+      eqref_hash.update(0x87654321);
+      eqref_hash.update(unit_cell->first);
+      eqref_hash.update(1);
+    }
+
+  const Vertex& v = vertices[p.elements[unit_cell->first]];
+
+  /*
+   * Phase 1
+   * Refine neighbours according to the edges that leave the vertex v
+   */
+  std::vector<unsigned int>::const_iterator ei = v.edges_out.begin();
+  for(unsigned int j = v.nof_edges_out(); j > 0; j--)
+    {
+      const unsigned int dest_vertex = *ei++;
+      Partition::Cell* const neighbour_cell = p.get_cell(dest_vertex);
+   
+      if(neighbour_cell->is_unit()) {
+	if(in_search) {
+	  /* Remember neighbour in order to generate certificate */
+	  neighbour_heap.insert(neighbour_cell->first);
+	}
+	continue;
+      }
+      if(neighbour_cell->max_ival_count == 0)
+	{
+	  neighbour_heap.insert(neighbour_cell->first);
+	}
+      neighbour_cell->max_ival_count++;
+      
+      unsigned int* const swap_position =
+	p.elements + neighbour_cell->first + neighbour_cell->length -
+	neighbour_cell->max_ival_count;
+      *p.in_pos[dest_vertex] = *swap_position;
+      p.in_pos[*swap_position] = p.in_pos[dest_vertex];
+      *swap_position = dest_vertex;
+      p.in_pos[dest_vertex] = swap_position;
+    }
+
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell* neighbour_cell =	p.get_cell(p.elements[start]);
+
+#if defined(BLISS_CONSISTENCY_CHECKS)
+      assert(neighbour_cell->first == start);
+      if(neighbour_cell->is_unit()) {
+	assert(neighbour_cell->max_ival_count == 0);
+      } else {
+	assert(neighbour_cell->max_ival_count > 0);
+	assert(neighbour_cell->max_ival_count <= neighbour_cell->length);
+      }
+#endif
+
+      if(compute_eqref_hash)
+	{
+	  eqref_hash.update(neighbour_cell->first);
+	  eqref_hash.update(neighbour_cell->length);
+	  eqref_hash.update(neighbour_cell->max_ival_count);
+	}
+
+      if(neighbour_cell->length > 1 and
+	 neighbour_cell->max_ival_count != neighbour_cell->length)
+	{
+	  
+	  Partition::Cell* const new_cell =
+	    p.aux_split_in_two(neighbour_cell,
+			       neighbour_cell->length -
+			       neighbour_cell->max_ival_count);
+	  unsigned int* ep = p.elements + new_cell->first;
+	  unsigned int* const lp = p.elements+new_cell->first+new_cell->length;
+	  while(ep < lp)
+	    {
+	      p.element_to_cell_map[*ep] = new_cell;
+	      ep++;
+	    }
+	  neighbour_cell->max_ival_count = 0;
+
+	  
+	  if(compute_eqref_hash)
+	    {
+	      /* Update hash */
+	      eqref_hash.update(neighbour_cell->first);
+	      eqref_hash.update(neighbour_cell->length);
+	      eqref_hash.update(0);
+	      eqref_hash.update(new_cell->first);
+	      eqref_hash.update(new_cell->length);
+	      eqref_hash.update(1);
+	    }
+	  
+	  /* Add cells in splitting_queue */
+	  if(neighbour_cell->is_in_splitting_queue()) {
+	    /* Both cells must be included in splitting_queue in order
+	       to have refinement to equitable partition */
+	    p.splitting_queue_add(new_cell);
+	  } else {
+	    Partition::Cell *min_cell, *max_cell;
+	  if(neighbour_cell->length <= new_cell->length) {
+	    min_cell = neighbour_cell;
+	    max_cell = new_cell;
+	  } else {
+	    min_cell = new_cell;
+	    max_cell = neighbour_cell;
+	  }
+	  /* Put the smaller cell in splitting_queue */
+	   p.splitting_queue_add(min_cell);
+	  if(max_cell->is_unit()) {
+	    /* Put the "larger" cell also in splitting_queue */
+	    p.splitting_queue_add(max_cell);
+	  }
+	}
+	/* Update pointer for certificate generation */
+	neighbour_cell = new_cell;
+      }
+      else
+	{
+	  neighbour_cell->max_ival_count = 0;
+	}
+      
+      /*
+       * Build certificate if required
+       */
+      if(in_search)
+	{
+	  for(unsigned int i = neighbour_cell->first,
+		j = neighbour_cell->length;
+	      j > 0;
+	      j--, i++)
+	    {
+	      /* Build certificate */
+	      cert_add(CERT_EDGE, unit_cell->first, i);
+	      /* No need to continue? */
+	      if(refine_compare_certificate and
+		 (refine_equal_to_first == false) and
+		 (refine_cmp_to_best < 0))
+		goto worse_exit;
+	    }
+	} /* if(in_search) */
+    } /* while(!neighbour_heap.is_empty()) */
+
+  /*
+   * Phase 2
+   * Refine neighbours according to the edges that enter the vertex v
+   */
+  ei = v.edges_in.begin();
+  for(unsigned int j = v.nof_edges_in(); j > 0; j--)
+    {
+      const unsigned int dest_vertex = *ei++;
+      Partition::Cell* const neighbour_cell = p.get_cell(dest_vertex);
+      
+      if(neighbour_cell->is_unit()) {
+	if(in_search) {
+	  neighbour_heap.insert(neighbour_cell->first);
+	}
+	continue;
+      }
+      if(neighbour_cell->max_ival_count == 0)
+	{
+	  neighbour_heap.insert(neighbour_cell->first);
+	}
+      neighbour_cell->max_ival_count++;
+
+      unsigned int* const swap_position =
+	p.elements + neighbour_cell->first + neighbour_cell->length -
+	neighbour_cell->max_ival_count;
+      *p.in_pos[dest_vertex] = *swap_position;
+      p.in_pos[*swap_position] = p.in_pos[dest_vertex];
+      *swap_position = dest_vertex;
+      p.in_pos[dest_vertex] = swap_position;
+    }
+
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell* neighbour_cell =	p.get_cell(p.elements[start]);
+
+#if defined(BLISS_CONSISTENCY_CHECKS)
+      assert(neighbour_cell->first == start);
+      if(neighbour_cell->is_unit()) {
+	assert(neighbour_cell->max_ival_count == 0);
+      } else {
+	assert(neighbour_cell->max_ival_count > 0);
+	assert(neighbour_cell->max_ival_count <= neighbour_cell->length);
+      }
+#endif
+
+      if(compute_eqref_hash)
+	{
+	  eqref_hash.update(neighbour_cell->first);
+	  eqref_hash.update(neighbour_cell->length);
+	  eqref_hash.update(neighbour_cell->max_ival_count);
+	}
+
+      if(neighbour_cell->length > 1 and
+	 neighbour_cell->max_ival_count != neighbour_cell->length)
+	{
+	  Partition::Cell* const new_cell =
+	    p.aux_split_in_two(neighbour_cell,
+			       neighbour_cell->length -
+			       neighbour_cell->max_ival_count);
+	  unsigned int* ep = p.elements + new_cell->first;
+	  unsigned int* const lp = p.elements+new_cell->first+new_cell->length;
+	  while(ep < lp) {
+	    p.element_to_cell_map[*ep] = new_cell;
+	    ep++;
+	  }
+	  neighbour_cell->max_ival_count = 0;
+	  
+	  
+	  if(compute_eqref_hash)
+	    {
+	      eqref_hash.update(neighbour_cell->first);
+	      eqref_hash.update(neighbour_cell->length);
+	      eqref_hash.update(0);
+	      eqref_hash.update(new_cell->first);
+	      eqref_hash.update(new_cell->length);
+	      eqref_hash.update(1);
+	    }
+
+	  /* Add cells in splitting_queue */
+	  if(neighbour_cell->is_in_splitting_queue()) {
+	    /* Both cells must be included in splitting_queue in order
+	       to have refinement to equitable partition */
+	    p.splitting_queue_add(new_cell);
+	  } else {
+	    Partition::Cell *min_cell, *max_cell;
+	    if(neighbour_cell->length <= new_cell->length) {
+	      min_cell = neighbour_cell;
+	      max_cell = new_cell;
+	    } else {
+	      min_cell = new_cell;
+	      max_cell = neighbour_cell;
+	    }
+	    /* Put the smaller cell in splitting_queue */
+	    p.splitting_queue_add(min_cell);
+	    if(max_cell->is_unit()) {
+	      /* Put the "larger" cell also in splitting_queue */
+	      p.splitting_queue_add(max_cell);
+	    }
+	  }
+	  /* Update pointer for certificate generation */
+	  neighbour_cell = new_cell;
+	}
+      else
+	{
+	  neighbour_cell->max_ival_count = 0;
+	}
+      
+      /*
+       * Build certificate if required
+       */
+      if(in_search)
+	{
+	  for(unsigned int i = neighbour_cell->first,
+		j = neighbour_cell->length;
+	      j > 0;
+	      j--, i++)
+	    {
+	      /* Build certificate */
+	      cert_add(CERT_EDGE, i, unit_cell->first);
+	      /* No need to continue? */
+	      if(refine_compare_certificate and
+		 (refine_equal_to_first == false) and
+		 (refine_cmp_to_best < 0))
+		goto worse_exit;
+	    }
+	} /* if(in_search) */
+    } /* while(!neighbour_heap.is_empty()) */
+
+  if(refine_compare_certificate and
+     (refine_equal_to_first == false) and
+     (refine_cmp_to_best < 0))
+    return true;
+
+  return false;
+
+ worse_exit:
+  /* Clear neighbour heap */
+  UintSeqHash rest;
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell* const neighbour_cell = p.get_cell(p.elements[start]);
+      if(opt_use_failure_recording and was_equal_to_first)
+	{
+	  rest.update(neighbour_cell->first);
+	  rest.update(neighbour_cell->length);
+	  rest.update(neighbour_cell->max_ival_count);
+	}
+      neighbour_cell->max_ival_count = 0;
+    }
+  if(opt_use_failure_recording and was_equal_to_first)
+    {
+      rest.update(failure_recording_fp_deviation);
+      failure_recording_fp_deviation = rest.get_value();
+    }
+  return true;
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Check whether the current partition p is equitable.
+ * Performance: very slow, use only for debugging purposes.
+ *
+ *-------------------------------------------------------------------------*/
+
+bool
+Digraph::is_equitable() const
+{
+  const unsigned int N = get_nof_vertices();
+  if(N == 0)
+    return true;
+
+  std::vector<unsigned int> first_count = std::vector<unsigned int>(N, 0);
+  std::vector<unsigned int> other_count = std::vector<unsigned int>(N, 0);
+
+  /*
+   * Check equitabledness w.r.t. outgoing edges
+   */
+  for(Partition::Cell* cell = p.first_cell; cell; cell = cell->next)
+    {
+      if(cell->is_unit())
+	continue;
+
+      unsigned int* ep = p.elements + cell->first;
+      const Vertex& first_vertex = vertices[*ep++];
+
+      /* Count outgoing edges of the first vertex for cells */
+      for(std::vector<unsigned int>::const_iterator ei =
+	    first_vertex.edges_out.begin();
+	  ei != first_vertex.edges_out.end();
+	  ei++)
+	{
+	  first_count[p.get_cell(*ei)->first]++;
+	}
+
+      /* Count and compare outgoing edges of the other vertices */
+      for(unsigned int i = cell->length; i > 1; i--)
+	{
+	  const Vertex &vertex = vertices[*ep++];
+	  for(std::vector<unsigned int>::const_iterator ei =
+		vertex.edges_out.begin();
+	      ei != vertex.edges_out.end();
+	      ei++)
+	    {
+	      other_count[p.get_cell(*ei)->first]++;
+	    }
+	  for(Partition::Cell *cell2 = p.first_cell;
+	      cell2;
+	      cell2 = cell2->next)
+	    {
+	      if(first_count[cell2->first] != other_count[cell2->first])
+		{
+		  /* Not equitable */
+		  return false;
+		}
+	      other_count[cell2->first] = 0;
+	    }
+	}
+      /* Reset first_count */
+      for(unsigned int i = 0; i < N; i++)
+	first_count[i] = 0;
+    }
+
+
+  /*
+   * Check equitabledness w.r.t. incoming edges
+   */
+  for(Partition::Cell* cell = p.first_cell; cell; cell = cell->next)
+    {
+      if(cell->is_unit())
+	continue;
+
+      unsigned int* ep = p.elements + cell->first;
+      const Vertex& first_vertex = vertices[*ep++];
+
+      /* Count incoming edges of the first vertex for cells */
+      for(std::vector<unsigned int>::const_iterator ei =
+	    first_vertex.edges_in.begin();
+	  ei != first_vertex.edges_in.end();
+	  ei++)
+	{
+	  first_count[p.get_cell(*ei)->first]++;
+	}
+
+      /* Count and compare incoming edges of the other vertices */
+      for(unsigned int i = cell->length; i > 1; i--)
+	{
+	  const Vertex &vertex = vertices[*ep++];
+	  for(std::vector<unsigned int>::const_iterator ei =
+		vertex.edges_in.begin();
+	      ei != vertex.edges_in.end();
+	      ei++)
+	    {
+	      other_count[p.get_cell(*ei)->first]++;
+	    }
+	  for(Partition::Cell *cell2 = p.first_cell;
+	      cell2;
+	      cell2 = cell2->next)
+	    {
+	      if(first_count[cell2->first] != other_count[cell2->first])
+		{
+		  /* Not equitable */
+		  return false;
+		}
+	      other_count[cell2->first] = 0;
+	    }
+	}
+      /* Reset first_count */
+      for(unsigned int i = 0; i < N; i++)
+	first_count[i] = 0;
+    }
+  return true;
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Build the initial equitable partition
+ *
+ *-------------------------------------------------------------------------*/
+
+void
+Digraph::make_initial_equitable_partition()
+{
+  refine_according_to_invariant(&vertex_color_invariant);
+  p.splitting_queue_clear();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+
+  refine_according_to_invariant(&selfloop_invariant);
+  p.splitting_queue_clear();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+
+  refine_according_to_invariant(&outdegree_invariant);
+  p.splitting_queue_clear();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+
+  refine_according_to_invariant(&indegree_invariant);
+  p.splitting_queue_clear();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+
+  refine_to_equitable();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Find the next cell to be splitted
+ *
+ *-------------------------------------------------------------------------*/
+
+Partition::Cell*
+Digraph::find_next_cell_to_be_splitted(Partition::Cell* cell)
+{
+  switch(sh) {
+  case shs_f:   return sh_first();
+  case shs_fs:  return sh_first_smallest();
+  case shs_fl:  return sh_first_largest();
+  case shs_fm:  return sh_first_max_neighbours();
+  case shs_fsm: return sh_first_smallest_max_neighbours();
+  case shs_flm: return sh_first_largest_max_neighbours();
+  default:
+    fatal_error("Internal error - unknown splitting heuristics");
+    return 0;
+  }
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first nonsingleton cell in the current partition.
+ * The argument \a cell is ignored.
+ */
+Partition::Cell*
+Digraph::sh_first()
+{
+  Partition::Cell* best_cell = 0;
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      best_cell = cell;
+      break;
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first smallest nonsingleton cell in the current partition.
+ * The argument \a cell is ignored.
+ */
+Partition::Cell*
+Digraph::sh_first_smallest()
+{
+  Partition::Cell* best_cell = 0;
+  unsigned int best_size = UINT_MAX;
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      if(cell->length < best_size)
+	{
+	  best_size = cell->length;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first largest nonsingleton cell in the current partition.
+ * The argument \a cell is ignored.
+ */
+Partition::Cell*
+Digraph::sh_first_largest()
+{
+  Partition::Cell* best_cell = 0;
+  unsigned int best_size = 0;
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      if(cell->length > best_size)
+	{
+	  best_size = cell->length;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first nonsingleton cell with max number of neighbouring
+ * nonsingleton cells.
+ * Assumes that the partition p is equitable.
+ * Assumes that the max_ival fields of the cells are all 0.
+ */
+Partition::Cell*
+Digraph::sh_first_max_neighbours()
+{
+  Partition::Cell* best_cell = 0;
+  int best_value = -1;
+  KStack<Partition::Cell*> neighbour_cells_visited;
+  neighbour_cells_visited.init(get_nof_vertices());
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      int value = 0;
+      const Vertex &v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei;
+      ei = v.edges_in.begin();
+      for(unsigned int j = v.nof_edges_in(); j > 0; j--)
+	{
+	  Partition::Cell * const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+
+      ei = v.edges_out.begin();
+      for(unsigned int j = v.nof_edges_out(); j > 0; j--)
+	{
+	  Partition::Cell * const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+      
+      if(value > best_value)
+	{
+	  best_value = value;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first smallest nonsingleton cell with max number of neighbouring
+ * nonsingleton cells.
+ * Assumes that the partition p is equitable.
+ * Assumes that the max_ival fields of the cells are all 0.
+ */
+Partition::Cell*
+Digraph::sh_first_smallest_max_neighbours()
+{
+  Partition::Cell* best_cell = 0;
+  int best_value = -1;
+  unsigned int best_size = UINT_MAX;
+  KStack<Partition::Cell*> neighbour_cells_visited;
+  neighbour_cells_visited.init(get_nof_vertices());
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+	
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+	
+      int value = 0;
+      const Vertex& v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei;
+
+      ei = v.edges_in.begin();
+      for(unsigned int j = v.nof_edges_in(); j > 0; j--)
+	{
+	  Partition::Cell * const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell * const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+
+      ei = v.edges_out.begin();
+      for(unsigned int j = v.nof_edges_out(); j > 0; j--)
+	{
+	  Partition::Cell * const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell * const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+
+      if((value > best_value) or
+	 (value == best_value and cell->length < best_size))
+	{
+	  best_value = value;
+	  best_size = cell->length;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first largest nonsingleton cell with max number of neighbouring
+ * nonsingleton cells.
+ * Assumes that the partition p is equitable.
+ * Assumes that the max_ival fields of the cells are all 0.
+ */
+Partition::Cell*
+Digraph::sh_first_largest_max_neighbours()
+{
+  Partition::Cell* best_cell = 0;
+  int best_value = -1;
+  unsigned int best_size = 0;
+  KStack<Partition::Cell*> neighbour_cells_visited;
+  neighbour_cells_visited.init(get_nof_vertices());
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+
+      int value = 0;
+      const Vertex &v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei;
+
+      ei = v.edges_in.begin();
+      for(unsigned int j = v.nof_edges_in(); j > 0; j--)
+	{
+	  Partition::Cell* const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+
+      ei = v.edges_out.begin();
+      for(unsigned int j = v.nof_edges_out(); j > 0; j--)
+	{
+	  Partition::Cell* const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+
+      if((value > best_value) ||
+	 (value == best_value && cell->length > best_size))
+	{
+	  best_value = value;
+	  best_size = cell->length;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+
+
+
+
+
+/*------------------------------------------------------------------------
+ *
+ * Initialize the certificate size and memory
+ *
+ *-------------------------------------------------------------------------*/
+
+void
+Digraph::initialize_certificate()
+{
+  certificate_index = 0;
+  certificate_current_path.clear();
+  certificate_first_path.clear();
+  certificate_best_path.clear();
+}
+
+
+
+/*
+ * Check whether perm is an automorphism.
+ * Slow, mainly for debugging and validation purposes.
+ */
+bool
+Digraph::is_automorphism(unsigned int* const perm)
+{
+  std::set<unsigned int, std::less<unsigned int> > edges1;
+  std::set<unsigned int, std::less<unsigned int> > edges2;
+
+#if defined(BLISS_CONSISTENCY_CHECKS)
+  if(!is_permutation(get_nof_vertices(), perm))
+    _INTERNAL_ERROR();
+#endif
+
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex& v1 = vertices[i];
+      Vertex& v2 = vertices[perm[i]];
+
+      edges1.clear();
+      for(std::vector<unsigned int>::iterator ei = v1.edges_in.begin();
+	  ei != v1.edges_in.end();
+	  ei++)
+	edges1.insert(perm[*ei]);
+      edges2.clear();
+      for(std::vector<unsigned int>::iterator ei = v2.edges_in.begin();
+	  ei != v2.edges_in.end();
+	  ei++)
+	edges2.insert(*ei);
+      if(!(edges1 == edges2))
+	return false;
+
+      edges1.clear();
+      for(std::vector<unsigned int>::iterator ei = v1.edges_out.begin();
+	  ei != v1.edges_out.end();
+	  ei++)
+	edges1.insert(perm[*ei]);
+      edges2.clear();
+      for(std::vector<unsigned int>::iterator ei = v2.edges_out.begin();
+	  ei != v2.edges_out.end();
+	  ei++)
+	edges2.insert(*ei);
+      if(!(edges1 == edges2))
+	return false;
+    }
+
+  return true;
+}
+
+bool
+Digraph::is_automorphism(const std::vector<unsigned int>& perm) const
+{
+
+  if(!(perm.size() == get_nof_vertices() and is_permutation(perm)))
+    return false;
+
+  std::set<unsigned int, std::less<unsigned int> > edges1;
+  std::set<unsigned int, std::less<unsigned int> > edges2;
+
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      const Vertex& v1 = vertices[i];
+      const Vertex& v2 = vertices[perm[i]];
+
+      edges1.clear();
+      for(std::vector<unsigned int>::const_iterator ei = v1.edges_in.begin();
+	  ei != v1.edges_in.end();
+	  ei++)
+	edges1.insert(perm[*ei]);
+      edges2.clear();
+      for(std::vector<unsigned int>::const_iterator ei = v2.edges_in.begin();
+	  ei != v2.edges_in.end();
+	  ei++)
+	edges2.insert(*ei);
+      if(!(edges1 == edges2))
+	return false;
+
+      edges1.clear();
+      for(std::vector<unsigned int>::const_iterator ei = v1.edges_out.begin();
+	  ei != v1.edges_out.end();
+	  ei++)
+	edges1.insert(perm[*ei]);
+      edges2.clear();
+      for(std::vector<unsigned int>::const_iterator ei = v2.edges_out.begin();
+	  ei != v2.edges_out.end();
+	  ei++)
+	edges2.insert(*ei);
+      if(!(edges1 == edges2))
+	return false;
+    }
+
+  return true;
+}
+
+
+
+
+bool
+Digraph::nucr_find_first_component(const unsigned int level)
+{
+
+  cr_component.clear();
+  cr_component_elements = 0;
+
+  /* Find first non-discrete cell in the component level */
+  Partition::Cell* first_cell = p.first_nonsingleton_cell;
+  while(first_cell)
+    {
+      if(p.cr_get_level(first_cell->first) == level)
+	break;
+      first_cell = first_cell->next_nonsingleton;
+    }
+
+  /* The component is discrete, return false */
+  if(!first_cell)
+    return false;
+	
+  std::vector<Partition::Cell*> component;
+  first_cell->max_ival = 1;
+  component.push_back(first_cell);
+
+  for(unsigned int i = 0; i < component.size(); i++)
+    {
+      Partition::Cell* const cell = component[i];
+	  
+      const Vertex& v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei;
+
+      ei = v.edges_out.begin();
+      for(unsigned int j = v.nof_edges_out(); j > 0; j--)
+	{
+	  const unsigned int neighbour = *ei++;
+	  Partition::Cell* const neighbour_cell = p.get_cell(neighbour);
+
+	  /* Skip unit neighbours */
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  /* Already marked to be in the same component? */
+	  if(neighbour_cell->max_ival == 1)
+	    continue;
+	  /* Is the neighbour at the same component recursion level? */
+	  if(p.cr_get_level(neighbour_cell->first) != level)
+	    continue;
+
+	  if(neighbour_cell->max_ival_count == 0)
+	    neighbour_heap.insert(neighbour_cell->first);
+	  neighbour_cell->max_ival_count++;
+	}
+      while(!neighbour_heap.is_empty())
+	{
+	  const unsigned int start = neighbour_heap.remove();
+	  Partition::Cell* const neighbour_cell =
+	    p.get_cell(p.elements[start]);
+	  
+	  /* Skip saturated neighbour cells */
+	  if(neighbour_cell->max_ival_count == neighbour_cell->length)
+	    {
+	      neighbour_cell->max_ival_count = 0;
+	      continue;
+	    } 
+	  neighbour_cell->max_ival_count = 0;
+	  neighbour_cell->max_ival = 1;
+	  component.push_back(neighbour_cell);
+	}
+
+      ei = v.edges_in.begin();
+      for(unsigned int j = v.nof_edges_in(); j > 0; j--)
+	{
+	  const unsigned int neighbour = *ei++;
+	  
+	  Partition::Cell* const neighbour_cell = p.get_cell(neighbour);
+
+	  /* Skip unit neighbours */
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  /* Already marked to be in the same component? */
+	  if(neighbour_cell->max_ival == 1)
+	    continue;
+	  /* Is the neighbour at the same component recursion level? */
+	  if(p.cr_get_level(neighbour_cell->first) != level)
+	    continue;
+
+	  if(neighbour_cell->max_ival_count == 0)
+	    neighbour_heap.insert(neighbour_cell->first);
+	  neighbour_cell->max_ival_count++;
+	}
+      while(!neighbour_heap.is_empty())
+	{
+	  const unsigned int start = neighbour_heap.remove();
+	  Partition::Cell* const neighbour_cell =
+	    p.get_cell(p.elements[start]);
+	  
+	  /* Skip saturated neighbour cells */
+	  if(neighbour_cell->max_ival_count == neighbour_cell->length)
+	    {
+	      neighbour_cell->max_ival_count = 0;
+	      continue;
+	    } 
+	  neighbour_cell->max_ival_count = 0;
+	  neighbour_cell->max_ival = 1;
+	  component.push_back(neighbour_cell);
+	}
+    }
+
+  for(unsigned int i = 0; i < component.size(); i++)
+    {
+      Partition::Cell* const cell = component[i];
+      cell->max_ival = 0;
+      cr_component.push_back(cell->first);
+      cr_component_elements += cell->length;
+    }
+
+  if(verbstr and verbose_level > 2) {
+    fprintf(verbstr, "NU-component with %lu cells and %u vertices\n",
+	    (long unsigned)cr_component.size(), cr_component_elements);
+    fflush(verbstr);
+  }
+
+  return true;
+}
+
+
+
+
+
+bool
+Digraph::nucr_find_first_component(const unsigned int level,
+				 std::vector<unsigned int>& component,
+				 unsigned int& component_elements,
+				 Partition::Cell*& sh_return)
+{
+
+  component.clear();
+  component_elements = 0;
+  sh_return = 0;
+  unsigned int sh_first  = 0;
+  unsigned int sh_size   = 0;
+  unsigned int sh_nuconn = 0;
+
+  /* Find first non-discrete cell in the component level */
+  Partition::Cell* first_cell = p.first_nonsingleton_cell;
+  while(first_cell)
+    {
+      if(p.cr_get_level(first_cell->first) == level)
+	break;
+      first_cell = first_cell->next_nonsingleton;
+    }
+
+  if(!first_cell)
+    {
+      /* The component is discrete, return false */
+      return false;
+    }
+	
+  std::vector<Partition::Cell*> comp;
+  KStack<Partition::Cell*> neighbours;
+  neighbours.init(get_nof_vertices());
+
+  first_cell->max_ival = 1;
+  comp.push_back(first_cell);
+
+  for(unsigned int i = 0; i < comp.size(); i++)
+    {
+      Partition::Cell* const cell = comp[i];
+
+      unsigned int nuconn = 1;
+
+      const Vertex& v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei;
+
+      /*| Phase 1: outgoing edges */
+      ei = v.edges_out.begin();
+      for(unsigned int j = v.nof_edges_out(); j > 0; j--)
+	{
+	  const unsigned int neighbour = *ei++;
+	  
+	  Partition::Cell* const neighbour_cell = p.get_cell(neighbour);
+
+	  /* Skip unit neighbours */
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  /* Is the neighbour at the same component recursion level? */
+	  //if(p.cr_get_level(neighbour_cell->first) != level)
+	  //  continue;
+	  if(neighbour_cell->max_ival_count == 0)
+	    neighbours.push(neighbour_cell);
+	  neighbour_cell->max_ival_count++;
+	}
+      while(!neighbours.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbours.pop();
+	  /* Skip saturated neighbour cells */
+	  if(neighbour_cell->max_ival_count == neighbour_cell->length)
+	    {
+	      neighbour_cell->max_ival_count = 0;
+	      continue;
+	    }
+	  nuconn++;
+	  neighbour_cell->max_ival_count = 0;
+	  if(neighbour_cell->max_ival == 0) {
+	    comp.push_back(neighbour_cell);
+	    neighbour_cell->max_ival = 1;
+	  }
+	}
+
+      /*| Phase 2: incoming edges */
+      ei = v.edges_in.begin();
+      for(unsigned int j = v.nof_edges_in(); j > 0; j--)
+	{
+	  const unsigned int neighbour = *ei++;
+	  Partition::Cell* const neighbour_cell = p.get_cell(neighbour);
+	  /*| Skip unit neighbours */
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  /* Is the neighbour at the same component recursion level? */
+	  //if(p.cr_get_level(neighbour_cell->first) != level)
+	  //  continue;
+	  if(neighbour_cell->max_ival_count == 0)
+	    neighbours.push(neighbour_cell);
+	  neighbour_cell->max_ival_count++;
+	}
+      while(!neighbours.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbours.pop();
+	  /* Skip saturated neighbour cells */
+	  if(neighbour_cell->max_ival_count == neighbour_cell->length)
+	    {
+	      neighbour_cell->max_ival_count = 0;
+	      continue;
+	    }
+	  nuconn++;
+	  neighbour_cell->max_ival_count = 0;
+	  if(neighbour_cell->max_ival == 0) {
+	    comp.push_back(neighbour_cell);
+	    neighbour_cell->max_ival = 1;
+	  }
+	}
+
+      /*| Phase 3: splitting heuristics */
+      switch(sh) {
+      case shs_f:
+	if(sh_return == 0 or
+	   cell->first <= sh_first) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	}
+	break;
+      case shs_fs:
+	if(sh_return == 0 or
+	   cell->length < sh_size or
+	   (cell->length == sh_size and cell->first <= sh_first)) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_size = cell->length;
+	}
+	break;
+      case shs_fl:
+	if(sh_return == 0 or
+	   cell->length > sh_size or
+	   (cell->length == sh_size and cell->first <= sh_first)) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_size = cell->length;
+	}
+	break;
+      case shs_fm:
+	if(sh_return == 0 or
+	   nuconn > sh_nuconn or
+	   (nuconn == sh_nuconn and cell->first <= sh_first)) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_nuconn = nuconn;
+	}
+	break;
+      case shs_fsm:
+	if(sh_return == 0 or
+	   nuconn > sh_nuconn or
+	   (nuconn == sh_nuconn and
+	    (cell->length < sh_size or
+	     (cell->length == sh_size and cell->first <= sh_first)))) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_size = cell->length;
+	  sh_nuconn = nuconn;
+	}
+	break;
+      case shs_flm:
+	if(sh_return == 0 or
+	   nuconn > sh_nuconn or
+	   (nuconn == sh_nuconn and
+	    (cell->length > sh_size or
+	     (cell->length == sh_size and cell->first <= sh_first)))) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_size = cell->length;
+	  sh_nuconn = nuconn;
+	}
+	break;
+      default:
+	fatal_error("Internal error - unknown splitting heuristics");
+	return 0;
+      }
+    }
+  assert(sh_return);
+
+  for(unsigned int i = 0; i < comp.size(); i++)
+    {
+      Partition::Cell* const cell = comp[i];
+      cell->max_ival = 0;
+      component.push_back(cell->first);
+      component_elements += cell->length;
+    }
+
+  if(verbstr and verbose_level > 2) {
+    fprintf(verbstr, "NU-component with %lu cells and %u vertices\n",
+	    (long unsigned)component.size(), component_elements);
+    fflush(verbstr);
+  }
+
+  return true;
+}
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Routines for undirected graphs
+ *
+ *-------------------------------------------------------------------------*/
+
+Graph::Vertex::Vertex()
+{
+  color = 0;
+}
+
+
+Graph::Vertex::~Vertex()
+{
+  ;
+}
+
+
+void
+Graph::Vertex::add_edge(const unsigned int other_vertex)
+{
+  edges.push_back(other_vertex);
+}
+
+
+void
+Graph::Vertex::remove_duplicate_edges(std::vector<bool>& tmp)
+{
+#if defined(BLISS_CONSISTENCY_CHECKS)
+  /* Pre-conditions  */
+  for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false);
+#endif
+  for(std::vector<unsigned int>::iterator iter = edges.begin();
+      iter != edges.end(); )
+    {
+      const unsigned int dest_vertex = *iter;
+      if(tmp[dest_vertex] == true)
+	{
+	  /* A duplicate edge found! */
+	  iter = edges.erase(iter);
+	}
+      else
+	{
+	  /* Not seen earlier, mark as seen */
+	  tmp[dest_vertex] = true;
+	  iter++;
+	}
+    }
+
+  /* Clear tmp */
+  for(std::vector<unsigned int>::iterator iter = edges.begin();
+      iter != edges.end();
+      iter++)
+    {
+      tmp[*iter] = false;
+    }
+#if defined(BLISS_CONSISTENCY_CHECKS)
+  /* Post-conditions  */
+  for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false);
+#endif
+}
+
+
+/**
+ * Sort the edges leaving the vertex according to
+ * the vertex number of the other edge end.
+ * Time complexity: O(e log(e)), where e is the number of edges
+ * leaving the vertex.
+ */
+void
+Graph::Vertex::sort_edges()
+{
+  std::sort(edges.begin(), edges.end());
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Constructor and destructor for undirected graphs
+ *
+ *-------------------------------------------------------------------------*/
+
+
+Graph::Graph(const unsigned int nof_vertices)
+{
+  vertices.resize(nof_vertices);
+  sh = shs_flm;
+}
+
+
+Graph::~Graph()
+{
+  ;
+}
+
+
+unsigned int
+Graph::add_vertex(const unsigned int color)
+{
+  const unsigned int vertex_num = vertices.size();
+  vertices.resize(vertex_num + 1);
+  vertices.back().color = color;
+  return vertex_num;
+}
+
+
+void
+Graph::add_edge(const unsigned int vertex1, const unsigned int vertex2)
+{
+  //fprintf(stderr, "(%u,%u) ", vertex1, vertex2);
+  vertices[vertex1].add_edge(vertex2);
+  vertices[vertex2].add_edge(vertex1);
+}
+
+
+void
+Graph::change_color(const unsigned int vertex, const unsigned int color)
+{
+  vertices[vertex].color = color;
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Read graph in the DIMACS format.
+ * Returns 0 if an error occurred.
+ *
+ *-------------------------------------------------------------------------*/
+
+Graph*
+Graph::read_dimacs(FILE* const fp, FILE* const errstr)
+{
+  Graph *g = 0;
+  unsigned int nof_vertices;
+  unsigned int nof_edges;
+  unsigned int line_num = 1;
+  int c;
+
+  const bool verbose = false;
+  FILE* const verbstr = stdout;
+  
+  /* Read comments and the problem definition line */
+  while(1)
+    {
+      c = getc(fp);
+      if(c == 'c')
+	{
+	  /* A comment, ignore the rest of the line */
+	  while((c = getc(fp)) != '\n')
+	    {
+	      if(c == EOF)
+		{
+		  if(errstr)
+		    fprintf(errstr,
+			    "error in line %u: not in DIMACS format\n",
+			    line_num);
+		  goto error_exit;
+		}
+	    }
+	  line_num++;
+	  continue;
+	}
+      if(c == 'p')
+	{
+	  /* The problem definition line */
+	  if(fscanf(fp, " edge %u %u\n", &nof_vertices, &nof_edges) != 2)
+	    {
+	      if(errstr)
+		fprintf(errstr, "error in line %u: not in DIMACS format\n",
+			line_num);
+	      goto error_exit;
+	    }
+	  line_num++;
+	  break;
+	}
+      if(errstr)
+	fprintf(errstr, "error in line %u: not in DIMACS format\n", line_num);
+      goto error_exit;
+    }
+  
+  if(nof_vertices <= 0)
+    {
+      if(errstr)
+	fprintf(errstr, "error: no vertices\n");
+      goto error_exit;
+    }
+  if(verbose)
+    {
+      fprintf(verbstr, "Instance has %d vertices and %d edges\n",
+	      nof_vertices, nof_edges);
+      fflush(verbstr);
+    }
+
+  g = new Graph(nof_vertices);
+
+  //
+  // Read vertex colors
+  //
+  if(verbose)
+    {
+      fprintf(verbstr, "Reading vertex colors...\n");
+      fflush(verbstr);
+    }
+  while(1)
+    {
+      c = getc(fp);
+      if(c != 'n')
+	{
+	  ungetc(c, fp);
+	  break;
+	}
+      ungetc(c, fp);
+      unsigned int vertex;
+      unsigned int color;
+      if(fscanf(fp, "n %u %u\n", &vertex, &color) != 2)
+	{
+	  if(errstr)
+	    fprintf(errstr, "error in line %u: not in DIMACS format\n",
+		    line_num);
+	  goto error_exit;
+	}
+      if(!((vertex >= 1) && (vertex <= nof_vertices)))
+	{
+	  if(errstr)
+	    fprintf(errstr,
+		    "error in line %u: vertex %u not in range [1,...,%u]\n",
+		    line_num, vertex, nof_vertices);
+	  goto error_exit;
+	}
+      line_num++;
+      g->change_color(vertex - 1, color);
+    }
+  if(verbose)
+    {
+      fprintf(verbstr, "Done\n");
+      fflush(verbstr);
+    }
+
+  //
+  // Read edges
+  //
+  if(verbose)
+    {
+      fprintf(verbstr, "Reading edges...\n");
+      fflush(verbstr);
+    }
+  for(unsigned i = 0; i < nof_edges; i++)
+    {
+      unsigned int from, to;
+      if(fscanf(fp, "e %u %u\n", &from, &to) != 2)
+	{
+	  if(errstr)
+	    fprintf(errstr, "error in line %u: not in DIMACS format\n",
+		    line_num);
+	  goto error_exit;
+	}
+      if(!((from >= 1) && (from <= nof_vertices)))
+	{
+	  if(errstr)
+	    fprintf(errstr,
+		    "error in line %u: vertex %u not in range [1,...,%u]\n",
+		    line_num, from, nof_vertices);
+	  goto error_exit;
+	}
+      if(!((to >= 1) && (to <= nof_vertices)))
+	{
+	  if(errstr)
+	    fprintf(errstr,
+		    "error in line %u: vertex %u not in range [1,...,%u]\n",
+		    line_num, to, nof_vertices);
+	  goto error_exit;
+	}
+      line_num++;
+      g->add_edge(from-1, to-1);
+    }
+  if(verbose)
+    {
+      fprintf(verbstr, "Done\n");
+      fflush(verbstr);
+    }
+
+  return g;
+
+ error_exit:
+  if(g)
+    delete g;
+  return 0;
+
+}
+
+
+void
+Graph::write_dimacs(FILE* const fp)
+{
+  remove_duplicate_edges();
+  sort_edges();
+
+  /* First count the total number of edges */
+  unsigned int nof_edges = 0;
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex &v = vertices[i];
+      for(std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+	  ei != v.edges.end();
+	  ei++)
+	{
+	  const unsigned int dest_i = *ei;
+	  if(dest_i < i)
+	    continue;
+	  nof_edges++;
+	}
+    }
+
+  /* Output the "header" line */
+  fprintf(fp, "p edge %u %u\n", get_nof_vertices(), nof_edges);
+
+  /* Print the color of each vertex */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex &v = vertices[i];
+      fprintf(fp, "n %u %u\n", i+1, v.color);
+      /*
+      if(v.color != 0)
+	{
+	  fprintf(fp, "n %u %u\n", i+1, v.color);
+	}
+      */
+    }
+
+  /* Print the edges */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex &v = vertices[i];
+      for(std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+	  ei != v.edges.end();
+	  ei++)
+	{
+	  const unsigned int dest_i = *ei;
+	  if(dest_i < i)
+	    continue;
+	  fprintf(fp, "e %u %u\n", i+1, dest_i+1);
+	}
+    }
+}
+
+
+
+void
+Graph::sort_edges()
+{
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    vertices[i].sort_edges();
+}
+
+
+int
+Graph::cmp(Graph& other)
+{
+  /* Compare the numbers of vertices */
+  if(get_nof_vertices() < other.get_nof_vertices())
+    return -1;
+  if(get_nof_vertices() > other.get_nof_vertices())
+    return 1;
+  /* Compare vertex colors */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      if(vertices[i].color < other.vertices[i].color)
+	return -1;
+      if(vertices[i].color > other.vertices[i].color)
+	return 1;
+    }
+  /* Compare vertex degrees */
+  remove_duplicate_edges();
+  other.remove_duplicate_edges();
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      if(vertices[i].nof_edges() < other.vertices[i].nof_edges())
+	return -1;
+      if(vertices[i].nof_edges() > other.vertices[i].nof_edges())
+	return 1;
+    }
+  /* Compare edges */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex &v1 = vertices[i];
+      Vertex &v2 = other.vertices[i];
+      v1.sort_edges();
+      v2.sort_edges();
+      std::vector<unsigned int>::const_iterator ei1 = v1.edges.begin();
+      std::vector<unsigned int>::const_iterator ei2 = v2.edges.begin();
+      while(ei1 != v1.edges.end())
+	{
+	  if(*ei1 < *ei2)
+	    return -1;
+	  if(*ei1 > *ei2)
+	    return 1;
+	  ei1++;
+	  ei2++;
+	}
+    }
+  return 0;
+}
+
+
+Graph*
+Graph::permute(const std::vector<unsigned int>& perm) const
+{
+#if defined(BLISS_CONSISTENCY_CHECKS)
+#endif
+
+  Graph* const g = new Graph(get_nof_vertices());
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      const Vertex& v = vertices[i];
+      Vertex& permuted_v = g->vertices[perm[i]];
+      permuted_v.color = v.color;
+      for(std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+	  ei != v.edges.end();
+	  ei++)
+	{
+	  const unsigned int dest_v = *ei;
+	  permuted_v.add_edge(perm[dest_v]);
+	}
+      permuted_v.sort_edges();
+    }
+  return g;
+}
+
+Graph*
+Graph::permute(const unsigned int* perm) const
+{
+#if defined(BLISS_CONSISTENCY_CHECKS)
+  if(!is_permutation(get_nof_vertices(), perm))
+    _INTERNAL_ERROR();
+#endif
+
+  Graph* const g = new Graph(get_nof_vertices());
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      const Vertex& v = vertices[i];
+      Vertex& permuted_v = g->vertices[perm[i]];
+      permuted_v.color = v.color;
+      for(std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+	  ei != v.edges.end();
+	  ei++)
+	{
+	  const unsigned int dest_v = *ei;
+	  permuted_v.add_edge(perm[dest_v]);
+	}
+      permuted_v.sort_edges();
+    }
+  return g;
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Print graph in graphviz format
+ *
+ *-------------------------------------------------------------------------*/
+
+
+void
+Graph::write_dot(const char* const filename)
+{
+  FILE *fp = fopen(filename, "w");
+  if(fp)
+    {
+      write_dot(fp);
+      fclose(fp);
+    }
+}
+
+void
+Graph::write_dot(FILE* const fp)
+{
+  remove_duplicate_edges();
+
+  fprintf(fp, "graph g {\n");
+
+  unsigned int vnum = 0;
+  for(std::vector<Vertex>::iterator vi = vertices.begin();
+      vi != vertices.end();
+      vi++, vnum++)
+    {
+      Vertex& v = *vi;
+      fprintf(fp, "v%u [label=\"%u:%u\"];\n", vnum, vnum, v.color);
+      for(std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+	  ei != v.edges.end();
+	  ei++)
+	{
+	  const unsigned int vnum2 = *ei;
+	  if(vnum2 > vnum)
+	    fprintf(fp, "v%u -- v%u\n", vnum, vnum2);
+	}
+    }
+
+  fprintf(fp, "}\n");
+}
+
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Get a hash value for the graph.
+ *
+ *-------------------------------------------------------------------------*/
+
+unsigned int
+Graph::get_hash()
+{
+  remove_duplicate_edges();
+  sort_edges();
+
+  UintSeqHash h;
+
+  h.update(get_nof_vertices());
+
+  /* Hash the color of each vertex */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      h.update(vertices[i].color);
+    }
+
+  /* Hash the edges */
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex &v = vertices[i];
+      for(std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+	  ei != v.edges.end();
+	  ei++)
+	{
+	  const unsigned int dest_i = *ei;
+	  if(dest_i < i)
+	    continue;
+	  h.update(i);
+	  h.update(dest_i);
+	}
+    }
+
+  return h.get_value();
+}
+
+
+
+
+
+void
+Graph::remove_duplicate_edges()
+{
+  std::vector<bool> tmp(vertices.size(), false);
+
+  for(std::vector<Vertex>::iterator vi = vertices.begin();
+      vi != vertices.end();
+      vi++)
+    {
+#if defined(BLISS_EXPENSIVE_CONSISTENCY_CHECKS)
+      for(unsigned int i = 0; i < tmp.size(); i++) assert(tmp[i] == false);
+#endif
+      (*vi).remove_duplicate_edges(tmp);
+    }
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Partition independent invariants
+ *
+ *-------------------------------------------------------------------------*/
+
+/*
+ * Return the color of the vertex.
+ * Time complexity: O(1)
+ */
+unsigned int
+Graph::vertex_color_invariant(const Graph* const g, const unsigned int v)
+{
+  return g->vertices[v].color;
+}
+
+/*
+ * Return the degree of the vertex.
+ * Time complexity: O(1)
+ */
+unsigned int
+Graph::degree_invariant(const Graph* const g, const unsigned int v)
+{
+  return g->vertices[v].nof_edges();
+}
+
+/*
+ * Return 1 if the vertex v has a self-loop, 0 otherwise
+ * Time complexity: O(E_v), where E_v is the number of edges leaving v
+ */
+unsigned int
+Graph::selfloop_invariant(const Graph* const g, const unsigned int v)
+{
+  const Vertex& vertex = g->vertices[v];
+  for(std::vector<unsigned int>::const_iterator ei = vertex.edges.begin();
+      ei != vertex.edges.end();
+      ei++)
+    {
+      if(*ei == v)
+	return 1;
+    }
+  return 0;
+}
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Refine the partition p according to a partition independent invariant
+ *
+ *-------------------------------------------------------------------------*/
+
+bool
+Graph::refine_according_to_invariant(unsigned int (*inv)(const Graph* const g,
+							 const unsigned int v))
+{
+  bool refined = false;
+
+  for(Partition::Cell* cell = p.first_nonsingleton_cell; cell; )
+    {
+
+      Partition::Cell* const next_cell = cell->next_nonsingleton;
+
+      const unsigned int* ep = p.elements + cell->first;
+      for(unsigned int i = cell->length; i > 0; i--, ep++)
+	{
+	  const unsigned int ival = inv(this, *ep);
+	  p.invariant_values[*ep] = ival;
+	  if(ival > cell->max_ival)
+	    {
+	      cell->max_ival = ival;
+	      cell->max_ival_count = 1;
+	    }
+	  else if(ival == cell->max_ival)
+	    {
+	      cell->max_ival_count++;
+	    }
+	}
+      Partition::Cell* const last_new_cell = p.zplit_cell(cell, true);
+      refined |= (last_new_cell != cell);
+      cell = next_cell;
+    }
+
+  return refined;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Split the neighbourhood of a cell according to the equitable invariant
+ *
+ *-------------------------------------------------------------------------*/
+
+bool
+Graph::split_neighbourhood_of_cell(Partition::Cell* const cell)
+{
+
+
+  const bool was_equal_to_first = refine_equal_to_first;
+
+  if(compute_eqref_hash)
+    {
+      eqref_hash.update(cell->first);
+      eqref_hash.update(cell->length);
+    }
+
+  const unsigned int* ep = p.elements + cell->first;
+  for(unsigned int i = cell->length; i > 0; i--)
+    {
+      const Vertex& v = vertices[*ep++];
+      
+      std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+      for(unsigned int j = v.nof_edges(); j != 0; j--)
+	{
+	  const unsigned int dest_vertex = *ei++;
+	  Partition::Cell * const neighbour_cell = p.get_cell(dest_vertex);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  const unsigned int ival = ++p.invariant_values[dest_vertex];
+	  if(ival > neighbour_cell->max_ival)
+	    {
+	      neighbour_cell->max_ival = ival;
+	      neighbour_cell->max_ival_count = 1;
+	      if(ival == 1) {
+		neighbour_heap.insert(neighbour_cell->first);
+	      }
+	    }
+	  else if(ival == neighbour_cell->max_ival) {
+	    neighbour_cell->max_ival_count++;
+	  }
+	}
+    }
+  
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell * const neighbour_cell = p.get_cell(p.elements[start]);
+      
+      if(compute_eqref_hash)
+	{
+	  eqref_hash.update(neighbour_cell->first);
+	  eqref_hash.update(neighbour_cell->length);
+	  eqref_hash.update(neighbour_cell->max_ival);
+	  eqref_hash.update(neighbour_cell->max_ival_count);
+	}
+
+
+      Partition::Cell* const last_new_cell = p.zplit_cell(neighbour_cell, true);
+
+      /* Update certificate and hash if needed */
+      const Partition::Cell* c = neighbour_cell;
+      while(1)
+	{
+	  if(in_search)
+	    {
+	      /* Build certificate */
+	      cert_add_redundant(CERT_SPLIT, c->first, c->length);
+	      /* No need to continue? */
+	      if(refine_compare_certificate and
+		 (refine_equal_to_first == false) and
+		 (refine_cmp_to_best < 0))
+		goto worse_exit;
+	    }
+	  if(compute_eqref_hash)
+	    {
+	      eqref_hash.update(c->first);
+	      eqref_hash.update(c->length);
+	    }
+	  if(c == last_new_cell)
+	    break;
+	  c = c->next;
+	}
+    }
+
+  if(refine_compare_certificate and
+     (refine_equal_to_first == false) and
+     (refine_cmp_to_best < 0))
+    return true;
+
+  return false;
+
+ worse_exit:
+  /* Clear neighbour heap */
+  UintSeqHash rest;
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell * const neighbour_cell = p.get_cell(p.elements[start]);
+      if(opt_use_failure_recording and was_equal_to_first)
+	{
+	  rest.update(neighbour_cell->first);
+	  rest.update(neighbour_cell->length);
+	  rest.update(neighbour_cell->max_ival);
+	  rest.update(neighbour_cell->max_ival_count);
+	}
+      neighbour_cell->max_ival = 0;
+      neighbour_cell->max_ival_count = 0;
+     p.clear_ivs(neighbour_cell);
+    }
+  if(opt_use_failure_recording and was_equal_to_first)
+    {
+      for(unsigned int i = p.splitting_queue.size(); i > 0; i--)
+	{
+	  Partition::Cell* const cell = p.splitting_queue.pop_front();
+	  rest.update(cell->first);
+	  rest.update(cell->length);
+	  p.splitting_queue.push_back(cell);
+	}
+      rest.update(failure_recording_fp_deviation);
+      failure_recording_fp_deviation = rest.get_value();
+    }
+ 
+  return true;
+}
+
+
+
+bool
+Graph::split_neighbourhood_of_unit_cell(Partition::Cell* const unit_cell)
+{
+
+
+  const bool was_equal_to_first = refine_equal_to_first;
+
+  if(compute_eqref_hash)
+    {
+      eqref_hash.update(0x87654321);
+      eqref_hash.update(unit_cell->first);
+      eqref_hash.update(1);
+    }
+
+  const Vertex& v = vertices[p.elements[unit_cell->first]];
+
+  std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+  for(unsigned int j = v.nof_edges(); j > 0; j--)
+    {
+      const unsigned int dest_vertex = *ei++;
+      Partition::Cell * const neighbour_cell = p.get_cell(dest_vertex);
+      
+      if(neighbour_cell->is_unit()) {
+	if(in_search) {
+	  /* Remember neighbour in order to generate certificate */
+	  neighbour_heap.insert(neighbour_cell->first);
+	}
+	continue;
+      }
+      if(neighbour_cell->max_ival_count == 0)
+	{
+	  neighbour_heap.insert(neighbour_cell->first);
+	}
+      neighbour_cell->max_ival_count++;
+
+      unsigned int * const swap_position =
+	p.elements + neighbour_cell->first + neighbour_cell->length -
+	neighbour_cell->max_ival_count;
+      *p.in_pos[dest_vertex] = *swap_position;
+      p.in_pos[*swap_position] = p.in_pos[dest_vertex];
+      *swap_position = dest_vertex;
+      p.in_pos[dest_vertex] = swap_position;
+    }
+  
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell* neighbour_cell =	p.get_cell(p.elements[start]);
+
+#if defined(BLISS_CONSISTENCY_CHECKS)
+      if(neighbour_cell->is_unit()) {
+      } else {
+      }
+#endif
+
+      if(compute_eqref_hash)
+	{
+	  eqref_hash.update(neighbour_cell->first);
+	  eqref_hash.update(neighbour_cell->length);
+	  eqref_hash.update(neighbour_cell->max_ival_count);
+	}
+
+      if(neighbour_cell->length > 1 and
+	 neighbour_cell->max_ival_count != neighbour_cell->length)
+	{
+	  Partition::Cell * const new_cell =
+	    p.aux_split_in_two(neighbour_cell,
+			       neighbour_cell->length -
+			       neighbour_cell->max_ival_count);
+	  unsigned int *ep = p.elements + new_cell->first;
+	  unsigned int * const lp = p.elements+new_cell->first+new_cell->length;
+	  while(ep < lp)
+	    {
+	      p.element_to_cell_map[*ep] = new_cell;
+	      ep++;
+	    }
+	  neighbour_cell->max_ival_count = 0;
+	  
+	  
+	  if(compute_eqref_hash)
+	    {
+	      /* Update hash */
+	      eqref_hash.update(neighbour_cell->first);
+	      eqref_hash.update(neighbour_cell->length);
+	      eqref_hash.update(0);
+	      eqref_hash.update(new_cell->first);
+	      eqref_hash.update(new_cell->length);
+	      eqref_hash.update(1);
+	    }
+	  
+	  /* Add cells in splitting_queue */
+	  if(neighbour_cell->is_in_splitting_queue()) {
+	    /* Both cells must be included in splitting_queue in order
+	       to ensure refinement into equitable partition */
+	    p.splitting_queue_add(new_cell);
+	  } else {
+	    Partition::Cell *min_cell, *max_cell;
+	    if(neighbour_cell->length <= new_cell->length) {
+	      min_cell = neighbour_cell;
+	      max_cell = new_cell;
+	    } else {
+	      min_cell = new_cell;
+	      max_cell = neighbour_cell;
+	    }
+	    /* Put the smaller cell in splitting_queue */
+	    p.splitting_queue_add(min_cell);
+	    if(max_cell->is_unit()) {
+	      /* Put the "larger" cell also in splitting_queue */
+	      p.splitting_queue_add(max_cell);
+	    }
+	  }
+	  /* Update pointer for certificate generation */
+	  neighbour_cell = new_cell;
+	}
+      else
+	{
+	  /* neighbour_cell->length == 1 ||
+	     neighbour_cell->max_ival_count == neighbour_cell->length */
+	  neighbour_cell->max_ival_count = 0;
+	}
+      
+      /*
+       * Build certificate if required
+       */
+      if(in_search)
+	{
+	  for(unsigned int i = neighbour_cell->first,
+		j = neighbour_cell->length;
+	      j > 0;
+	      j--, i++)
+	    {
+	      /* Build certificate */
+	      cert_add(CERT_EDGE, unit_cell->first, i);
+	      /* No need to continue? */
+	      if(refine_compare_certificate and
+		 (refine_equal_to_first == false) and
+		 (refine_cmp_to_best < 0))
+		goto worse_exit;
+	    }
+	} /* if(in_search) */
+    } /* while(!neighbour_heap.is_empty()) */
+  
+  if(refine_compare_certificate and
+     (refine_equal_to_first == false) and
+     (refine_cmp_to_best < 0))
+    return true;
+
+  return false;
+
+ worse_exit:
+  /* Clear neighbour heap */
+  UintSeqHash rest;
+  while(!neighbour_heap.is_empty())
+    {
+      const unsigned int start = neighbour_heap.remove();
+      Partition::Cell * const neighbour_cell = p.get_cell(p.elements[start]);
+      if(opt_use_failure_recording and was_equal_to_first)
+	{
+	  rest.update(neighbour_cell->first);
+	  rest.update(neighbour_cell->length);
+	  rest.update(neighbour_cell->max_ival_count);
+	}
+      neighbour_cell->max_ival_count = 0;
+    }
+  if(opt_use_failure_recording and was_equal_to_first)
+    {
+      rest.update(failure_recording_fp_deviation);
+      failure_recording_fp_deviation = rest.get_value();
+    }
+  return true;
+}
+
+
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Check whether the current partition p is equitable.
+ * Performance: very slow, use only for debugging purposes.
+ *
+ *-------------------------------------------------------------------------*/
+
+bool Graph::is_equitable() const
+{
+  const unsigned int N = get_nof_vertices();
+  if(N == 0)
+    return true;
+
+  std::vector<unsigned int> first_count = std::vector<unsigned int>(N, 0);
+  std::vector<unsigned int> other_count = std::vector<unsigned int>(N, 0);
+
+  for(Partition::Cell *cell = p.first_cell; cell; cell = cell->next)
+    {
+      if(cell->is_unit())
+	continue;
+      
+      unsigned int *ep = p.elements + cell->first;
+      const Vertex &first_vertex = vertices[*ep++];
+
+      /* Count how many edges lead from the first vertex to
+       * the neighbouring cells */
+      for(std::vector<unsigned int>::const_iterator ei =
+	    first_vertex.edges.begin();
+	  ei != first_vertex.edges.end();
+	  ei++)
+	{
+	  first_count[p.get_cell(*ei)->first]++;
+	}
+
+      /* Count and compare to the edges of the other vertices */
+      for(unsigned int i = cell->length; i > 1; i--)
+	{
+	  const Vertex &vertex = vertices[*ep++];
+	  for(std::vector<unsigned int>::const_iterator ei =
+		vertex.edges.begin();
+	      ei != vertex.edges.end();
+	      ei++)
+	    {
+	      other_count[p.get_cell(*ei)->first]++;
+	    }
+	  for(Partition::Cell *cell2 = p.first_cell;
+	      cell2;
+	      cell2 = cell2->next)
+	    {
+	      if(first_count[cell2->first] != other_count[cell2->first])
+		{
+		  /* Not equitable */
+		  return false;
+		}
+	      other_count[cell2->first] = 0;
+	    }
+	}
+      /* Reset first_count */
+      for(unsigned int i = 0; i < N; i++)
+	first_count[i] = 0;
+    }
+  return true;
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Build the initial equitable partition
+ *
+ *-------------------------------------------------------------------------*/
+
+void Graph::make_initial_equitable_partition()
+{
+  refine_according_to_invariant(&vertex_color_invariant);
+  p.splitting_queue_clear();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+
+  refine_according_to_invariant(&selfloop_invariant);
+  p.splitting_queue_clear();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+
+  refine_according_to_invariant(&degree_invariant);
+  p.splitting_queue_clear();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+
+  refine_to_equitable();
+  //p.print_signature(stderr); fprintf(stderr, "\n");
+
+}
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Find the next cell to be splitted
+ *
+ *-------------------------------------------------------------------------*/
+
+
+Partition::Cell*
+Graph::find_next_cell_to_be_splitted(Partition::Cell* cell)
+{
+  switch(sh) {
+  case shs_f:   return sh_first();
+  case shs_fs:  return sh_first_smallest();
+  case shs_fl:  return sh_first_largest();
+  case shs_fm:  return sh_first_max_neighbours();
+  case shs_fsm: return sh_first_smallest_max_neighbours();
+  case shs_flm: return sh_first_largest_max_neighbours();
+  default:
+    fatal_error("Internal error - unknown splitting heuristics");
+    return 0;
+  }
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first nonsingleton cell in the current partition.
+ */
+Partition::Cell*
+Graph::sh_first()
+{
+  Partition::Cell* best_cell = 0;
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      best_cell = cell;
+      break;
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first smallest nonsingleton cell in the current partition.
+ */
+Partition::Cell*
+Graph::sh_first_smallest()
+{
+  Partition::Cell* best_cell = 0;
+  unsigned int best_size = UINT_MAX;
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      if(cell->length < best_size)
+	{
+	  best_size = cell->length;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first largest nonsingleton cell in the current partition.
+ */
+Partition::Cell*
+Graph::sh_first_largest()
+{
+  Partition::Cell* best_cell = 0;
+  unsigned int best_size = 0;
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      if(cell->length > best_size)
+	{
+	  best_size = cell->length;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first nonsingleton cell with max number of neighbouring
+ *   nonsingleton cells.
+ * Assumes that the partition p is equitable.
+ * Assumes that the max_ival fields of the cells are all 0.
+ */
+Partition::Cell*
+Graph::sh_first_max_neighbours()
+{
+  Partition::Cell* best_cell = 0;
+  int best_value = -1;
+  KStack<Partition::Cell*> neighbour_cells_visited;
+  neighbour_cells_visited.init(get_nof_vertices());
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      const Vertex& v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+      for(unsigned int j = v.nof_edges(); j > 0; j--)
+	{
+	  Partition::Cell * const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      int value = 0;
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+      if(value > best_value)
+	{
+	  best_value = value;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first smallest nonsingleton cell with max number of neighbouring
+ * nonsingleton cells.
+ * Assumes that the partition p is equitable.
+ * Assumes that the max_ival fields of the cells are all 0.
+ */
+Partition::Cell*
+Graph::sh_first_smallest_max_neighbours()
+{
+  Partition::Cell* best_cell = 0;
+  int best_value = -1;
+  unsigned int best_size = UINT_MAX;
+  KStack<Partition::Cell*> neighbour_cells_visited;
+  neighbour_cells_visited.init(get_nof_vertices());
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+	
+      const Vertex& v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+      for(unsigned int j = v.nof_edges(); j > 0; j--)
+	{
+	  Partition::Cell* const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      int value = 0;
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+      if((value > best_value) or
+	 (value == best_value and cell->length < best_size))
+	{
+	  best_value = value;
+	  best_size = cell->length;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+/** \internal
+ * A splitting heuristic.
+ * Returns the first largest nonsingleton cell with max number of neighbouring
+ * nonsingleton cells.
+ * Assumes that the partition p is equitable.
+ * Assumes that the max_ival fields of the cells are all 0.
+ */
+Partition::Cell*
+Graph::sh_first_largest_max_neighbours()
+{
+  Partition::Cell* best_cell = 0;
+  int best_value = -1;
+  unsigned int best_size = 0;
+  KStack<Partition::Cell*> neighbour_cells_visited;
+  neighbour_cells_visited.init(get_nof_vertices());
+  for(Partition::Cell* cell = p.first_nonsingleton_cell;
+      cell;
+      cell = cell->next_nonsingleton)
+    {
+
+      if(opt_use_comprec and p.cr_get_level(cell->first) != cr_level)
+	continue;
+      const Vertex& v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+      for(unsigned int j = v.nof_edges(); j > 0; j--)
+	{
+	  Partition::Cell* const neighbour_cell = p.get_cell(*ei++);
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  neighbour_cell->max_ival++;
+	  if(neighbour_cell->max_ival == 1)
+	    neighbour_cells_visited.push(neighbour_cell);
+	}
+      int value = 0;
+      while(!neighbour_cells_visited.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbour_cells_visited.pop();
+	  if(neighbour_cell->max_ival != neighbour_cell->length)
+	    value++;
+	  neighbour_cell->max_ival = 0;
+	}
+      if((value > best_value) or
+	 (value == best_value and cell->length > best_size))
+	{
+	  best_value = value;
+	  best_size = cell->length;
+	  best_cell = cell;
+	}
+    }
+  return best_cell;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Initialize the certificate size and memory
+ *
+ *-------------------------------------------------------------------------*/
+
+void
+Graph::initialize_certificate()
+{
+  certificate_index = 0;
+  certificate_current_path.clear();
+  certificate_first_path.clear();
+  certificate_best_path.clear();
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Check whether perm is an automorphism.
+ * Slow, mainly for debugging and validation purposes.
+ *
+ *-------------------------------------------------------------------------*/
+
+bool
+Graph::is_automorphism(unsigned int* const perm)
+{
+  std::set<unsigned int, std::less<unsigned int> > edges1;
+  std::set<unsigned int, std::less<unsigned int> > edges2;
+
+#if defined(BLISS_CONSISTENCY_CHECKS)
+  if(!is_permutation(get_nof_vertices(), perm))
+    _INTERNAL_ERROR();
+#endif
+
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      Vertex& v1 = vertices[i];
+      edges1.clear();
+      for(std::vector<unsigned int>::iterator ei = v1.edges.begin();
+	  ei != v1.edges.end();
+	  ei++)
+	edges1.insert(perm[*ei]);
+      
+      Vertex& v2 = vertices[perm[i]];
+      edges2.clear();
+      for(std::vector<unsigned int>::iterator ei = v2.edges.begin();
+	  ei != v2.edges.end();
+	  ei++)
+	edges2.insert(*ei);
+
+      if(!(edges1 == edges2))
+	return false;
+    }
+
+  return true;
+}
+
+
+
+
+bool
+Graph::is_automorphism(const std::vector<unsigned int>& perm) const
+{
+
+  if(!(perm.size() == get_nof_vertices() and is_permutation(perm)))
+    return false;
+
+  std::set<unsigned int, std::less<unsigned int> > edges1;
+  std::set<unsigned int, std::less<unsigned int> > edges2;
+
+  for(unsigned int i = 0; i < get_nof_vertices(); i++)
+    {
+      const Vertex& v1 = vertices[i];
+      edges1.clear();
+      for(std::vector<unsigned int>::const_iterator ei = v1.edges.begin();
+	  ei != v1.edges.end();
+	  ei++)
+	edges1.insert(perm[*ei]);
+      
+      const Vertex& v2 = vertices[perm[i]];
+      edges2.clear();
+      for(std::vector<unsigned int>::const_iterator ei = v2.edges.begin();
+	  ei != v2.edges.end();
+	  ei++)
+	edges2.insert(*ei);
+
+      if(!(edges1 == edges2))
+	return false;
+    }
+
+  return true;
+}
+
+
+
+
+
+
+
+bool
+Graph::nucr_find_first_component(const unsigned int level)
+{
+
+  cr_component.clear();
+  cr_component_elements = 0;
+
+  /* Find first non-discrete cell in the component level */
+  Partition::Cell* first_cell = p.first_nonsingleton_cell;
+  while(first_cell)
+    {
+      if(p.cr_get_level(first_cell->first) == level)
+	break;
+      first_cell = first_cell->next_nonsingleton;
+    }
+
+  /* The component is discrete, return false */
+  if(!first_cell)
+    return false;
+	
+  std::vector<Partition::Cell*> component;
+  first_cell->max_ival = 1;
+  component.push_back(first_cell);
+
+  for(unsigned int i = 0; i < component.size(); i++)
+    {
+      Partition::Cell* const cell = component[i];
+	  
+      const Vertex& v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+      for(unsigned int j = v.nof_edges(); j > 0; j--)
+	{
+	  const unsigned int neighbour = *ei++;
+	  
+	  Partition::Cell* const neighbour_cell = p.get_cell(neighbour);
+
+	  /* Skip unit neighbours */
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  /* Already marked to be in the same component? */
+	  if(neighbour_cell->max_ival == 1)
+	    continue;
+	  /* Is the neighbour at the same component recursion level? */
+	  if(p.cr_get_level(neighbour_cell->first) != level)
+	    continue;
+
+	  if(neighbour_cell->max_ival_count == 0)
+	    neighbour_heap.insert(neighbour_cell->first);
+	  neighbour_cell->max_ival_count++;
+	}
+      while(!neighbour_heap.is_empty())
+	{
+	  const unsigned int start = neighbour_heap.remove();
+	  Partition::Cell* const neighbour_cell =
+	    p.get_cell(p.elements[start]);
+	  
+	  /* Skip saturated neighbour cells */
+	  if(neighbour_cell->max_ival_count == neighbour_cell->length)
+	    {
+	      neighbour_cell->max_ival_count = 0;
+	      continue;
+	    } 
+	  neighbour_cell->max_ival_count = 0;
+	  neighbour_cell->max_ival = 1;
+	  component.push_back(neighbour_cell);
+	}
+    }
+
+  for(unsigned int i = 0; i < component.size(); i++)
+    {
+      Partition::Cell* const cell = component[i];
+      cell->max_ival = 0;
+      cr_component.push_back(cell->first);
+      cr_component_elements += cell->length;
+    }
+
+  if(verbstr and verbose_level > 2) {
+    fprintf(verbstr, "NU-component with %lu cells and %u vertices\n",
+	    (long unsigned)cr_component.size(), cr_component_elements);
+    fflush(verbstr);
+  }
+
+  return true;
+}
+
+
+
+
+bool
+Graph::nucr_find_first_component(const unsigned int level,
+				 std::vector<unsigned int>& component,
+				 unsigned int& component_elements,
+				 Partition::Cell*& sh_return)
+{
+
+  component.clear();
+  component_elements = 0;
+  sh_return = 0;
+  unsigned int sh_first  = 0;
+  unsigned int sh_size   = 0;
+  unsigned int sh_nuconn = 0;
+
+  /* Find first non-discrete cell in the component level */
+  Partition::Cell* first_cell = p.first_nonsingleton_cell;
+  while(first_cell)
+    {
+      if(p.cr_get_level(first_cell->first) == level)
+	break;
+      first_cell = first_cell->next_nonsingleton;
+    }
+
+  if(!first_cell)
+    {
+      /* The component is discrete, return false */
+      return false;
+    }
+
+  std::vector<Partition::Cell*> comp;
+  KStack<Partition::Cell*> neighbours;
+  neighbours.init(get_nof_vertices());
+
+  first_cell->max_ival = 1;
+  comp.push_back(first_cell);
+
+  for(unsigned int i = 0; i < comp.size(); i++)
+    {
+      Partition::Cell* const cell = comp[i];
+
+      const Vertex& v = vertices[p.elements[cell->first]];
+      std::vector<unsigned int>::const_iterator ei = v.edges.begin();
+      for(unsigned int j = v.nof_edges(); j > 0; j--)
+	{
+	  const unsigned int neighbour = *ei++;
+	  
+	  Partition::Cell* const neighbour_cell = p.get_cell(neighbour);
+
+	  /* Skip unit neighbours */
+	  if(neighbour_cell->is_unit())
+	    continue;
+	  /* Is the neighbour at the same component recursion level? */
+	  //if(p.cr_get_level(neighbour_cell->first) != level)
+	  //  continue;
+	  if(neighbour_cell->max_ival_count == 0)
+	    neighbours.push(neighbour_cell);
+	  neighbour_cell->max_ival_count++;
+	}
+      unsigned int nuconn = 1;
+      while(!neighbours.is_empty())
+	{
+	  Partition::Cell* const neighbour_cell = neighbours.pop();
+	  //neighbours.pop_back();
+	  
+	  /* Skip saturated neighbour cells */
+	  if(neighbour_cell->max_ival_count == neighbour_cell->length)
+	    {
+	      neighbour_cell->max_ival_count = 0;
+	      continue;
+	    }
+	  nuconn++;
+	  neighbour_cell->max_ival_count = 0;
+	  if(neighbour_cell->max_ival == 0) {
+	    comp.push_back(neighbour_cell);
+	    neighbour_cell->max_ival = 1;
+	  }
+	}
+
+      switch(sh) {
+      case shs_f:
+	if(sh_return == 0 or
+	   cell->first <= sh_first) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	}
+	break;
+      case shs_fs:
+	if(sh_return == 0 or
+	   cell->length < sh_size or
+	   (cell->length == sh_size and cell->first <= sh_first)) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_size = cell->length;
+	}
+	break;
+      case shs_fl:
+	if(sh_return == 0 or
+	   cell->length > sh_size or
+	   (cell->length == sh_size and cell->first <= sh_first)) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_size = cell->length;
+	}
+	break;
+      case shs_fm:
+	if(sh_return == 0 or
+	   nuconn > sh_nuconn or
+	   (nuconn == sh_nuconn and cell->first <= sh_first)) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_nuconn = nuconn;
+	}
+	break;
+      case shs_fsm:
+	if(sh_return == 0 or
+	   nuconn > sh_nuconn or
+	   (nuconn == sh_nuconn and
+	    (cell->length < sh_size or
+	     (cell->length == sh_size and cell->first <= sh_first)))) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_size = cell->length;
+	  sh_nuconn = nuconn;
+	}
+	break;
+      case shs_flm:
+	if(sh_return == 0 or
+	   nuconn > sh_nuconn or
+	   (nuconn == sh_nuconn and
+	    (cell->length > sh_size or
+	     (cell->length == sh_size and cell->first <= sh_first)))) {
+	  sh_return = cell;
+	  sh_first = cell->first;
+	  sh_size = cell->length;
+	  sh_nuconn = nuconn;
+	}
+	break;
+      default:
+	fatal_error("Internal error - unknown splitting heuristics");
+	return 0;
+      }
+    }
+  assert(sh_return);
+
+  for(unsigned int i = 0; i < comp.size(); i++)
+    {
+      Partition::Cell* const cell = comp[i];
+      cell->max_ival = 0;
+      component.push_back(cell->first);
+      component_elements += cell->length;
+    }
+
+  if(verbstr and verbose_level > 2) {
+    fprintf(verbstr, "NU-component with %lu cells and %u vertices\n",
+	    (long unsigned)component.size(), component_elements);
+    fflush(verbstr);
+  }
+
+  return true;
+}
+
+
+
+
+}
diff --git a/igraph/src/gss.c b/igraph/src/gss.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/gss.c
@@ -0,0 +1,154 @@
+/* gss.c
+ *
+ * Copyright (C) 2012 Tamas Nepusz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <float.h>
+#include <math.h>
+#include <string.h>
+#include "error.h"
+#include "gss.h"
+#include "platform.h"
+
+/**
+ * \def PHI
+ *
+ * The golden ratio, i.e. 1+sqrt(5)/2
+ */
+#define PHI 1.618033988749895
+
+/**
+ * \def RESPHI
+ *
+ * Constant defined as 2 - \c PHI
+ */
+#define RESPHI 0.3819660112501051
+
+/**
+ * \const _defparam
+ *
+ * Default parameters for the GSS algorithm.
+ */
+static const gss_parameter_t _defparam = {
+    /* .epsilon = */  DBL_MIN,
+	/* .on_error = */ GSS_ERROR_STOP
+};
+
+/**
+ * Stores whether the last optimization run triggered a warning or not.
+ */
+static unsigned short int gss_i_warning_flag = 0;
+
+void gss_parameter_init(gss_parameter_t *param) {
+    memcpy(param, &_defparam, sizeof(*param));
+}
+
+unsigned short int gss_get_warning_flag() {
+	return gss_i_warning_flag;
+}
+
+#define TERMINATE {        \
+    if (_min) {            \
+        *(_min) = min;     \
+    }                      \
+    if (_fmin) {           \
+        *(_fmin) = fmin;   \
+    }                      \
+}
+
+#define EVALUATE(x, fx) { \
+    fx = proc_evaluate(instance, x); \
+    if (fmin > fx) { \
+        min = x;     \
+        fmin = fx;   \
+    } \
+    if (proc_progress) { \
+        retval = proc_progress(instance, x, fx, min, fmin, \
+                (a < b) ? a : b, (a < b) ? b : a, k); \
+        if (retval) { \
+			TERMINATE;            \
+            return PLFIT_SUCCESS; \
+        } \
+    } \
+}
+
+int gss(double a, double b, double *_min, double *_fmin,
+        gss_evaluate_t proc_evaluate, gss_progress_t proc_progress,
+        void* instance, const gss_parameter_t *_param) {
+    double c, d, min;
+    double fa, fb, fc, fd, fmin;
+    int k = 0;
+    int retval;
+    unsigned short int successful = 1;
+
+    gss_parameter_t param = _param ? (*_param) : _defparam;
+
+	gss_i_warning_flag = 0;
+
+    if (a > b) {
+        c = a; a = b; b = c;
+    }
+
+    min = a;
+    fmin = proc_evaluate(instance, a);
+
+    c = a + RESPHI*(b-a);
+
+    EVALUATE(a, fa);
+    EVALUATE(b, fb);
+    EVALUATE(c, fc);
+
+    if (fc >= fa || fc >= fb) {
+		if (param.on_error == GSS_ERROR_STOP) {
+			return PLFIT_FAILURE;
+		} else {
+			gss_i_warning_flag = 1;
+		}
+	}
+
+    while (fabs(a-b) > param.epsilon) {
+        k++;
+
+        d = c + RESPHI*(b-c);
+        EVALUATE(d, fd);
+
+        if (fd >= fa || fd >= fb) {
+			if (param.on_error == GSS_ERROR_STOP) {
+				successful = 0;
+				break;
+			} else {
+				gss_i_warning_flag = 1;
+			}
+        }
+
+        if (fc <= fd) {
+            b = a; a = d;
+        } else {
+            a = c; c = d; fc = fd;
+        }
+    }
+
+    if (successful) {
+        c = (a+b) / 2.0;
+        k++;
+        EVALUATE(c, fc);
+		TERMINATE;
+    }
+
+    return successful ? PLFIT_SUCCESS : PLFIT_FAILURE;
+}
+
diff --git a/igraph/src/h_abs.c b/igraph/src/h_abs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/h_abs.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+shortint h_abs(x) shortint *x;
+#else
+shortint h_abs(shortint *x)
+#endif
+{
+if(*x >= 0)
+	return(*x);
+return(- *x);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/h_dim.c b/igraph/src/h_dim.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/h_dim.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+shortint h_dim(a,b) shortint *a, *b;
+#else
+shortint h_dim(shortint *a, shortint *b)
+#endif
+{
+return( *a > *b ? *a - *b : 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/h_dnnt.c b/igraph/src/h_dnnt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/h_dnnt.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double floor();
+shortint h_dnnt(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+shortint h_dnnt(doublereal *x)
+#endif
+{
+return (shortint)(*x >= 0. ? floor(*x + .5) : -floor(.5 - *x));
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/h_indx.c b/igraph/src/h_indx.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/h_indx.c
@@ -0,0 +1,32 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+shortint h_indx(a, b, la, lb) char *a, *b; ftnlen la, lb;
+#else
+shortint h_indx(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+ftnlen i, n;
+char *s, *t, *bend;
+
+n = la - lb + 1;
+bend = b + lb;
+
+for(i = 0 ; i < n ; ++i)
+	{
+	s = a + i;
+	t = b;
+	while(t < bend)
+		if(*s++ != *t++)
+			goto no;
+	return((shortint)i+1);
+	no: ;
+	}
+return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/h_len.c b/igraph/src/h_len.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/h_len.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+shortint h_len(s, n) char *s; ftnlen n;
+#else
+shortint h_len(char *s, ftnlen n)
+#endif
+{
+return(n);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/h_mod.c b/igraph/src/h_mod.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/h_mod.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+shortint h_mod(a,b) short *a, *b;
+#else
+shortint h_mod(short *a, short *b)
+#endif
+{
+return( *a % *b);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/h_nint.c b/igraph/src/h_nint.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/h_nint.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double floor();
+shortint h_nint(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+shortint h_nint(real *x)
+#endif
+{
+return (shortint)(*x >= 0 ? floor(*x + .5) : -floor(.5 - *x));
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/h_sign.c b/igraph/src/h_sign.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/h_sign.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+shortint h_sign(a,b) shortint *a, *b;
+#else
+shortint h_sign(shortint *a, shortint *b)
+#endif
+{
+shortint x;
+x = (*a >= 0 ? *a : - *a);
+return( *b >= 0 ? x : -x);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/hacks.c b/igraph/src/hacks.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/hacks.c
@@ -0,0 +1,54 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include <memory.h>
+#include <string.h>
+#include <stdlib.h>
+#include "igraph_hacks_internal.h"
+
+/* These are implementations of common C functions that may be missing from some
+ * compilers; for instance, icc does not provide stpcpy so we implement it
+ * here. */
+
+/**
+ * Drop-in replacement for strdup.
+ * Used only in compilers that do not have strdup or _strdup
+ */
+char* igraph_i_strdup(const char *s) {
+    size_t n = strlen(s) + 1;
+    char* result = (char*)malloc(sizeof(char) * n);
+    if (result) {
+        memcpy(result, s, n);
+    }
+    return result;
+}
+
+/**
+ * Drop-in replacement for stpcpy.
+ * Used only in compilers that do not have stpcpy
+ */
+char* igraph_i_stpcpy(char* s1, const char* s2) {
+    char* result = strcpy(s1, s2);
+    return result + strlen(s1);
+}
+
diff --git a/igraph/src/heap.c b/igraph/src/heap.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/heap.c
@@ -0,0 +1,1083 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_types_internal.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_error.h"
+#include "config.h"
+#include "igraph_math.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+
+#define PARENT(x)     (((x)+1)/2-1)
+#define LEFTCHILD(x)  (((x)+1)*2-1)
+#define RIGHTCHILD(x) (((x)+1)*2)
+
+/**
+ * \ingroup indheap
+ * \brief Initializes an indexed heap (constructor).
+ *
+ * @return Error code:
+ *         - <b>IGRAPH_ENOMEM</b>: out of memory
+ */
+
+int igraph_indheap_init           (igraph_indheap_t* h, long int alloc_size) {
+    if (alloc_size <= 0 ) {
+        alloc_size = 1;
+    }
+    h->stor_begin = igraph_Calloc(alloc_size, igraph_real_t);
+    if (h->stor_begin == 0) {
+        h->index_begin = 0;
+        IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM);
+    }
+    h->index_begin = igraph_Calloc(alloc_size, long int);
+    if (h->index_begin == 0) {
+        igraph_Free(h->stor_begin);
+        h->stor_begin = 0;
+        IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM);
+    }
+
+    h->stor_end = h->stor_begin + alloc_size;
+    h->end = h->stor_begin;
+    h->destroy = 1;
+
+    return 0;
+}
+
+int igraph_indheap_clear(igraph_indheap_t *h) {
+    h->end = h->stor_begin;
+    return 0;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Initializes and build an indexed heap from a C array (constructor).
+ *
+ * @return Error code:
+ *         - <b>IGRAPH_ENOMEM</b>: out of memory
+ */
+
+int igraph_indheap_init_array     (igraph_indheap_t *h, igraph_real_t* data, long int len) {
+    long int i;
+
+    h->stor_begin = igraph_Calloc(len, igraph_real_t);
+    if (h->stor_begin == 0) {
+        h->index_begin = 0;
+        IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM);
+    }
+    h->index_begin = igraph_Calloc(len, long int);
+    if (h->index_begin == 0) {
+        igraph_Free(h->stor_begin);
+        h->stor_begin = 0;
+        IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM);
+    }
+    h->stor_end = h->stor_begin + len;
+    h->end = h->stor_end;
+    h->destroy = 1;
+
+    memcpy(h->stor_begin, data, (size_t) len * sizeof(igraph_real_t));
+    for (i = 0; i < len; i++) {
+        h->index_begin[i] = i + 1;
+    }
+
+    igraph_indheap_i_build (h, 0);
+
+    return 0;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Destroys an initialized indexed heap.
+ */
+
+void igraph_indheap_destroy        (igraph_indheap_t* h) {
+    assert(h != 0);
+    if (h->destroy) {
+        if (h->stor_begin != 0) {
+            igraph_Free(h->stor_begin);
+            h->stor_begin = 0;
+        }
+        if (h->index_begin != 0) {
+            igraph_Free(h->index_begin);
+            h->index_begin = 0;
+        }
+    }
+}
+
+/**
+ * \ingroup indheap
+ * \brief Checks whether a heap is empty.
+ */
+
+igraph_bool_t igraph_indheap_empty          (igraph_indheap_t* h) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+    return h->stor_begin == h->end;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Adds an element to an indexed heap.
+ */
+
+int igraph_indheap_push           (igraph_indheap_t* h, igraph_real_t elem) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+
+    /* full, allocate more storage */
+    if (h->stor_end == h->end) {
+        long int new_size = igraph_indheap_size(h) * 2;
+        if (new_size == 0) {
+            new_size = 1;
+        }
+        IGRAPH_CHECK(igraph_indheap_reserve(h, new_size));
+    }
+
+    *(h->end) = elem;
+    h->end += 1;
+    *(h->index_begin + igraph_indheap_size(h) - 1) = igraph_indheap_size(h) - 1;
+
+    /* maintain indheap */
+    igraph_indheap_i_shift_up(h, igraph_indheap_size(h) - 1);
+
+    return 0;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Adds an element to an indexed heap with a given index.
+ */
+
+int igraph_indheap_push_with_index(igraph_indheap_t* h, long int idx, igraph_real_t elem) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+
+    /* full, allocate more storage */
+    if (h->stor_end == h->end) {
+        long int new_size = igraph_indheap_size(h) * 2;
+        if (new_size == 0) {
+            new_size = 1;
+        }
+        IGRAPH_CHECK(igraph_indheap_reserve(h, new_size));
+    }
+
+    *(h->end) = elem;
+    h->end += 1;
+    *(h->index_begin + igraph_indheap_size(h) - 1) = idx;
+
+    /* maintain indheap */
+    igraph_indheap_i_shift_up(h, igraph_indheap_size(h) - 1);
+
+    return 0;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Modifies an element in an indexed heap.
+ */
+
+int igraph_indheap_modify(igraph_indheap_t* h, long int idx, igraph_real_t elem) {
+    long int i, n;
+
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+
+    n = igraph_indheap_size(h);
+    for (i = 0; i < n; i++)
+        if (h->index_begin[i] == idx) {
+            h->stor_begin[i] = elem;
+            break;
+        }
+
+    if (i == n) {
+        return 0;
+    }
+
+    /* maintain indheap */
+    igraph_indheap_i_build(h, 0);
+
+    return 0;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Returns the largest element in an indexed heap.
+ */
+
+igraph_real_t igraph_indheap_max       (igraph_indheap_t* h) {
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+    assert(h->stor_begin != h->end);
+
+    return h->stor_begin[0];
+}
+
+/**
+ * \ingroup indheap
+ * \brief Removes the largest element from an indexed heap.
+ */
+
+igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h) {
+    igraph_real_t tmp;
+
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+
+    tmp = h->stor_begin[0];
+    igraph_indheap_i_switch(h, 0, igraph_indheap_size(h) - 1);
+    h->end -= 1;
+    igraph_indheap_i_sink(h, 0);
+
+    return tmp;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Gives the number of elements in an indexed heap.
+ */
+
+long int igraph_indheap_size      (igraph_indheap_t* h) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+    return h->end - h->stor_begin;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Reserves more memory for an indexed heap.
+ *
+ * @return Error code:
+ *         - <b>IGRAPH_ENOMEM</b>: out of memory
+ */
+
+int igraph_indheap_reserve        (igraph_indheap_t* h, long int size) {
+    long int actual_size = igraph_indheap_size(h);
+    igraph_real_t *tmp1;
+    long int *tmp2;
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+
+    if (size <= actual_size) {
+        return 0;
+    }
+
+    tmp1 = igraph_Calloc(size, igraph_real_t);
+    if (tmp1 == 0) {
+        IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, tmp1);   /* TODO: hack */
+    tmp2 = igraph_Calloc(size, long int);
+    if (tmp2 == 0) {
+        IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, tmp2);
+    memcpy(tmp1, h->stor_begin, (size_t) actual_size * sizeof(igraph_real_t));
+    memcpy(tmp2, h->index_begin, (size_t) actual_size * sizeof(long int));
+    igraph_Free(h->stor_begin);
+    igraph_Free(h->index_begin);
+
+    h->stor_begin = tmp1;
+    h->index_begin = tmp2;
+    h->stor_end = h->stor_begin + size;
+    h->end = h->stor_begin + actual_size;
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \ingroup indheap
+ * \brief Returns the index of the largest element in an indexed heap.
+ */
+
+long int igraph_indheap_max_index(igraph_indheap_t *h) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+    return h->index_begin[0];
+}
+
+/**
+ * \ingroup indheap
+ * \brief Builds an indexed heap, this function should not be called
+ * directly.
+ */
+
+void igraph_indheap_i_build(igraph_indheap_t* h, long int head) {
+
+    long int size = igraph_indheap_size(h);
+    if (RIGHTCHILD(head) < size) {
+        /* both subtrees */
+        igraph_indheap_i_build(h, LEFTCHILD(head) );
+        igraph_indheap_i_build(h, RIGHTCHILD(head));
+        igraph_indheap_i_sink(h, head);
+    } else if (LEFTCHILD(head) < size) {
+        /* only left */
+        igraph_indheap_i_build(h, LEFTCHILD(head));
+        igraph_indheap_i_sink(h, head);
+    } else {
+        /* none */
+    }
+}
+
+/**
+ * \ingroup indheap
+ * \brief Moves an element up in the heap, don't call this function
+ * directly.
+ */
+
+void igraph_indheap_i_shift_up(igraph_indheap_t *h, long int elem) {
+
+    if (elem == 0 || h->stor_begin[elem] < h->stor_begin[PARENT(elem)]) {
+        /* at the top */
+    } else {
+        igraph_indheap_i_switch(h, elem, PARENT(elem));
+        igraph_indheap_i_shift_up(h, PARENT(elem));
+    }
+}
+
+/**
+ * \ingroup indheap
+ * \brief Moves an element down in the heap, don't call this function
+ * directly.
+ */
+
+void igraph_indheap_i_sink(igraph_indheap_t* h, long int head) {
+
+    long int size = igraph_indheap_size(h);
+    if (LEFTCHILD(head) >= size) {
+        /* no subtrees */
+    } else if (RIGHTCHILD(head) == size ||
+               h->stor_begin[LEFTCHILD(head)] >= h->stor_begin[RIGHTCHILD(head)]) {
+        /* sink to the left if needed */
+        if (h->stor_begin[head] < h->stor_begin[LEFTCHILD(head)]) {
+            igraph_indheap_i_switch(h, head, LEFTCHILD(head));
+            igraph_indheap_i_sink(h, LEFTCHILD(head));
+        }
+    } else {
+        /* sink to the right */
+        if (h->stor_begin[head] < h->stor_begin[RIGHTCHILD(head)]) {
+            igraph_indheap_i_switch(h, head, RIGHTCHILD(head));
+            igraph_indheap_i_sink(h, RIGHTCHILD(head));
+        }
+    }
+}
+
+/**
+ * \ingroup indheap
+ * \brief Switches two elements in a heap, don't call this function
+ * directly.
+ */
+
+void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e2) {
+    if (e1 != e2) {
+        igraph_real_t tmp = h->stor_begin[e1];
+        h->stor_begin[e1] = h->stor_begin[e2];
+        h->stor_begin[e2] = tmp;
+
+        tmp = h->index_begin[e1];
+        h->index_begin[e1] = h->index_begin[e2];
+        h->index_begin[e2] = (long int) tmp;
+    }
+}
+
+
+/**
+ * \ingroup doubleindheap
+ * \brief Initializes an empty doubly indexed heap object (constructor).
+ *
+ * @return Error code:
+ *         - <b>IGRAPH_ENOMEM</b>: out of memory
+ */
+
+int igraph_d_indheap_init           (igraph_d_indheap_t* h, long int alloc_size) {
+    if (alloc_size <= 0 ) {
+        alloc_size = 1;
+    }
+    h->stor_begin = igraph_Calloc(alloc_size, igraph_real_t);
+    if (h->stor_begin == 0) {
+        h->index_begin = 0;
+        h->index2_begin = 0;
+        IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM);
+    }
+    h->stor_end = h->stor_begin + alloc_size;
+    h->end = h->stor_begin;
+    h->destroy = 1;
+    h->index_begin = igraph_Calloc(alloc_size, long int);
+    if (h->index_begin == 0) {
+        igraph_Free(h->stor_begin);
+        h->stor_begin = 0;
+        h->index2_begin = 0;
+        IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM);
+    }
+    h->index2_begin = igraph_Calloc(alloc_size, long int);
+    if (h->index2_begin == 0) {
+        igraph_Free(h->stor_begin);
+        igraph_Free(h->index_begin);
+        h->stor_begin = 0;
+        h->index_begin = 0;
+        IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM);
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Destroys an initialized doubly indexed heap object.
+ */
+
+void igraph_d_indheap_destroy        (igraph_d_indheap_t* h) {
+    assert(h != 0);
+    if (h->destroy) {
+        if (h->stor_begin != 0) {
+            igraph_Free(h->stor_begin);
+            h->stor_begin = 0;
+        }
+        if (h->index_begin != 0) {
+            igraph_Free(h->index_begin);
+            h->index_begin = 0;
+        }
+        if (h->index2_begin != 0) {
+            igraph_Free(h->index2_begin);
+            h->index2_begin = 0;
+        }
+    }
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Decides whether a heap is empty.
+ */
+
+igraph_bool_t igraph_d_indheap_empty          (igraph_d_indheap_t* h) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+    return h->stor_begin == h->end;
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Adds an element to the heap.
+ */
+
+int igraph_d_indheap_push           (igraph_d_indheap_t* h, igraph_real_t elem,
+                                     long int idx, long int idx2) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+
+    /* full, allocate more storage */
+    if (h->stor_end == h->end) {
+        long int new_size = igraph_d_indheap_size(h) * 2;
+        if (new_size == 0) {
+            new_size = 1;
+        }
+        IGRAPH_CHECK(igraph_d_indheap_reserve(h, new_size));
+    }
+
+    *(h->end) = elem;
+    h->end += 1;
+    *(h->index_begin + igraph_d_indheap_size(h) - 1) = idx ;
+    *(h->index2_begin + igraph_d_indheap_size(h) - 1) = idx2 ;
+
+    /* maintain d_indheap */
+    igraph_d_indheap_i_shift_up(h, igraph_d_indheap_size(h) - 1);
+
+    return 0;
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Returns the largest element in the heap.
+ */
+
+igraph_real_t igraph_d_indheap_max       (igraph_d_indheap_t* h) {
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+    assert(h->stor_begin != h->end);
+
+    return h->stor_begin[0];
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Removes the largest element from the heap.
+ */
+
+igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t* h) {
+    igraph_real_t tmp;
+
+    assert(h != NULL);
+    assert(h->stor_begin != NULL);
+
+    tmp = h->stor_begin[0];
+    igraph_d_indheap_i_switch(h, 0, igraph_d_indheap_size(h) - 1);
+    h->end -= 1;
+    igraph_d_indheap_i_sink(h, 0);
+
+    return tmp;
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Gives the number of elements in the heap.
+ */
+
+long int igraph_d_indheap_size      (igraph_d_indheap_t* h) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+    return h->end - h->stor_begin;
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Allocates memory for a heap.
+ *
+ * @return Error code:
+ *         - <b>IGRAPH_ENOMEM</b>: out of memory
+ */
+
+int igraph_d_indheap_reserve        (igraph_d_indheap_t* h, long int size) {
+    long int actual_size = igraph_d_indheap_size(h);
+    igraph_real_t *tmp1;
+    long int *tmp2, *tmp3;
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+
+    if (size <= actual_size) {
+        return 0;
+    }
+
+    tmp1 = igraph_Calloc(size, igraph_real_t);
+    if (tmp1 == 0) {
+        IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, tmp1);   /* TODO: hack */
+    tmp2 = igraph_Calloc(size, long int);
+    if (tmp2 == 0) {
+        IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, tmp2);   /* TODO: hack */
+    tmp3 = igraph_Calloc(size, long int);
+    if (tmp3 == 0) {
+        IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, tmp3);   /* TODO: hack */
+
+    memcpy(tmp1, h->stor_begin, (size_t) actual_size * sizeof(igraph_real_t));
+    memcpy(tmp2, h->index_begin, (size_t) actual_size * sizeof(long int));
+    memcpy(tmp3, h->index2_begin, (size_t) actual_size * sizeof(long int));
+    igraph_Free(h->stor_begin);
+    igraph_Free(h->index_begin);
+    igraph_Free(h->index2_begin);
+
+    h->stor_begin = tmp1;
+    h->stor_end = h->stor_begin + size;
+    h->end = h->stor_begin + actual_size;
+    h->index_begin = tmp2;
+    h->index2_begin = tmp3;
+
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Gives the indices of the maximal element in the heap.
+ */
+
+void igraph_d_indheap_max_index(igraph_d_indheap_t *h, long int *idx, long int *idx2) {
+    assert(h != 0);
+    assert(h->stor_begin != 0);
+    (*idx) = h->index_begin[0];
+    (*idx2) = h->index2_begin[0];
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Builds the heap, don't call it directly.
+ */
+
+void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head) {
+
+    long int size = igraph_d_indheap_size(h);
+    if (RIGHTCHILD(head) < size) {
+        /* both subtrees */
+        igraph_d_indheap_i_build(h, LEFTCHILD(head) );
+        igraph_d_indheap_i_build(h, RIGHTCHILD(head));
+        igraph_d_indheap_i_sink(h, head);
+    } else if (LEFTCHILD(head) < size) {
+        /* only left */
+        igraph_d_indheap_i_build(h, LEFTCHILD(head));
+        igraph_d_indheap_i_sink(h, head);
+    } else {
+        /* none */
+    }
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Moves an element up in the heap, don't call it directly.
+ */
+
+void igraph_d_indheap_i_shift_up(igraph_d_indheap_t *h, long int elem) {
+
+    if (elem == 0 || h->stor_begin[elem] < h->stor_begin[PARENT(elem)]) {
+        /* at the top */
+    } else {
+        igraph_d_indheap_i_switch(h, elem, PARENT(elem));
+        igraph_d_indheap_i_shift_up(h, PARENT(elem));
+    }
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Moves an element down in the heap, don't call it directly.
+ */
+
+void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head) {
+
+    long int size = igraph_d_indheap_size(h);
+    if (LEFTCHILD(head) >= size) {
+        /* no subtrees */
+    } else if (RIGHTCHILD(head) == size ||
+               h->stor_begin[LEFTCHILD(head)] >= h->stor_begin[RIGHTCHILD(head)]) {
+        /* sink to the left if needed */
+        if (h->stor_begin[head] < h->stor_begin[LEFTCHILD(head)]) {
+            igraph_d_indheap_i_switch(h, head, LEFTCHILD(head));
+            igraph_d_indheap_i_sink(h, LEFTCHILD(head));
+        }
+    } else {
+        /* sink to the right */
+        if (h->stor_begin[head] < h->stor_begin[RIGHTCHILD(head)]) {
+            igraph_d_indheap_i_switch(h, head, RIGHTCHILD(head));
+            igraph_d_indheap_i_sink(h, RIGHTCHILD(head));
+        }
+    }
+}
+
+/**
+ * \ingroup doubleindheap
+ * \brief Switches two elements in the heap, don't call it directly.
+ */
+
+void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long int e2) {
+    if (e1 != e2) {
+        long int tmpi;
+        igraph_real_t tmp = h->stor_begin[e1];
+        h->stor_begin[e1] = h->stor_begin[e2];
+        h->stor_begin[e2] = tmp;
+
+        tmpi = h->index_begin[e1];
+        h->index_begin[e1] = h->index_begin[e2];
+        h->index_begin[e2] = tmpi;
+
+        tmpi = h->index2_begin[e1];
+        h->index2_begin[e1] = h->index2_begin[e2];
+        h->index2_begin[e2] = tmpi;
+    }
+}
+
+/*************************************************/
+
+#undef PARENT
+#undef LEFTCHILD
+#undef RIGHTCHILD
+#define PARENT(x)     ((x)/2)
+#define LEFTCHILD(x)  ((x)*2+1)
+#define RIGHTCHILD(x) ((x)*2)
+#define INACTIVE      IGRAPH_INFINITY
+#define UNDEFINED     0.0
+#define INDEXINC      1
+
+void igraph_i_cutheap_switch(igraph_i_cutheap_t *ch,
+                             long int hidx1, long int hidx2) {
+    if (hidx1 != hidx2) {
+        long int idx1 = (long int) VECTOR(ch->index)[hidx1];
+        long int idx2 = (long int) VECTOR(ch->index)[hidx2];
+
+        igraph_real_t tmp = VECTOR(ch->heap)[hidx1];
+        VECTOR(ch->heap)[hidx1] = VECTOR(ch->heap)[hidx2];
+        VECTOR(ch->heap)[hidx2] = tmp;
+
+        VECTOR(ch->index)[hidx1] = idx2;
+        VECTOR(ch->index)[hidx2] = idx1;
+
+        VECTOR(ch->hptr)[idx1] = hidx2 + INDEXINC;
+        VECTOR(ch->hptr)[idx2] = hidx1 + INDEXINC;
+    }
+}
+
+void igraph_i_cutheap_sink(igraph_i_cutheap_t *ch, long int hidx) {
+    long int size = igraph_vector_size(&ch->heap);
+    if (LEFTCHILD(hidx) >= size) {
+        /* leaf node */
+    } else if (RIGHTCHILD(hidx) == size ||
+               VECTOR(ch->heap)[LEFTCHILD(hidx)] >=
+               VECTOR(ch->heap)[RIGHTCHILD(hidx)]) {
+        /* sink to the left if needed */
+        if (VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[LEFTCHILD(hidx)]) {
+            igraph_i_cutheap_switch(ch, hidx, LEFTCHILD(hidx));
+            igraph_i_cutheap_sink(ch, LEFTCHILD(hidx));
+        }
+    } else {
+        /* sink to the right */
+        if (VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[RIGHTCHILD(hidx)]) {
+            igraph_i_cutheap_switch(ch, hidx, RIGHTCHILD(hidx));
+            igraph_i_cutheap_sink(ch, RIGHTCHILD(hidx));
+        }
+    }
+}
+
+void igraph_i_cutheap_shift_up(igraph_i_cutheap_t *ch, long int hidx) {
+    if (hidx == 0 || VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[PARENT(hidx)]) {
+        /* at the top */
+    } else {
+        igraph_i_cutheap_switch(ch, hidx, PARENT(hidx));
+        igraph_i_cutheap_shift_up(ch, PARENT(hidx));
+    }
+}
+
+int igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes) {
+    ch->dnodes = nodes;
+    IGRAPH_VECTOR_INIT_FINALLY(&ch->heap, nodes); /* all zero */
+    IGRAPH_CHECK(igraph_vector_init_seq(&ch->index, 0, nodes - 1));
+    IGRAPH_FINALLY(igraph_vector_destroy, &ch->index);
+    IGRAPH_CHECK(igraph_vector_init_seq(&ch->hptr, INDEXINC, nodes + INDEXINC - 1));
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch) {
+    igraph_vector_destroy(&ch->hptr);
+    igraph_vector_destroy(&ch->index);
+    igraph_vector_destroy(&ch->heap);
+}
+
+igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch) {
+    return igraph_vector_empty(&ch->heap);
+}
+
+/* Number of active vertices */
+
+igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch) {
+    return (igraph_integer_t) igraph_vector_size(&ch->heap);
+}
+
+/* Number of all (defined) vertices */
+
+igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch) {
+    return (igraph_integer_t) (ch->dnodes);
+}
+
+igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch) {
+    return VECTOR(ch->heap)[0];
+}
+
+igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch) {
+    long int size = igraph_vector_size(&ch->heap);
+    igraph_integer_t maxindex = (igraph_integer_t) VECTOR(ch->index)[0];
+    /* put the last element to the top */
+    igraph_i_cutheap_switch(ch, 0, size - 1);
+    /* remove the last element */
+    VECTOR(ch->hptr)[(long int) igraph_vector_tail(&ch->index)] = INACTIVE;
+    igraph_vector_pop_back(&ch->heap);
+    igraph_vector_pop_back(&ch->index);
+    igraph_i_cutheap_sink(ch, 0);
+
+    return maxindex;
+}
+
+/* Update the value of an active vertex, if not active it will be ignored */
+
+int igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index,
+                            igraph_real_t add) {
+    igraph_real_t hidx = VECTOR(ch->hptr)[(long int)index];
+    if (hidx != INACTIVE && hidx != UNDEFINED) {
+        long int hidx2 = (long int) (hidx - INDEXINC);
+        /*     printf("updating vertex %li, heap index %li\n", (long int) index, hidx2); */
+        VECTOR(ch->heap)[hidx2] += add;
+        igraph_i_cutheap_sink(ch, hidx2);
+        igraph_i_cutheap_shift_up(ch, hidx2);
+    }
+    return 0;
+}
+
+/* Reset the value of all vertices to zero and make them active */
+
+int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex) {
+    long int i, j, n = igraph_vector_size(&ch->hptr);
+    /* undefine */
+    VECTOR(ch->hptr)[vertex] = UNDEFINED;
+    ch->dnodes -= 1;
+
+    IGRAPH_CHECK(igraph_vector_resize(&ch->heap, ch->dnodes));
+    igraph_vector_null(&ch->heap);
+
+    IGRAPH_CHECK(igraph_vector_resize(&ch->index, ch->dnodes));
+
+    j = 0;
+    for (i = 0; i < n; i++) {
+        if (VECTOR(ch->hptr)[i] != UNDEFINED) {
+            VECTOR(ch->index)[j] = i;
+            VECTOR(ch->hptr)[i] = j + INDEXINC;
+            j++;
+        }
+    }
+
+    return 0;
+}
+
+/* -------------------------------------------------- */
+/* Two-way indexed heap                               */
+/* -------------------------------------------------- */
+
+#undef PARENT
+#undef LEFTCHILD
+#undef RIGHTCHILD
+#define PARENT(x)     (((x)+1)/2-1)
+#define LEFTCHILD(x)  (((x)+1)*2-1)
+#define RIGHTCHILD(x) (((x)+1)*2)
+
+/* This is a smart indexed heap. In addition to the "normal" indexed heap
+   it allows to access every element through its index in O(1) time.
+   In other words, for this heap the indexing operation is O(1), the
+   normal heap does this in O(n) time.... */
+
+void igraph_i_2wheap_switch(igraph_2wheap_t *h,
+                            long int e1, long int e2) {
+    if (e1 != e2) {
+        long int tmp1, tmp2;
+        igraph_real_t tmp3 = VECTOR(h->data)[e1];
+        VECTOR(h->data)[e1] = VECTOR(h->data)[e2];
+        VECTOR(h->data)[e2] = tmp3;
+
+        tmp1 = VECTOR(h->index)[e1];
+        tmp2 = VECTOR(h->index)[e2];
+
+        VECTOR(h->index2)[tmp1] = e2 + 2;
+        VECTOR(h->index2)[tmp2] = e1 + 2;
+
+        VECTOR(h->index)[e1] = tmp2;
+        VECTOR(h->index)[e2] = tmp1;
+    }
+}
+
+void igraph_i_2wheap_shift_up(igraph_2wheap_t *h,
+                              long int elem) {
+    if (elem == 0 || VECTOR(h->data)[elem] < VECTOR(h->data)[PARENT(elem)]) {
+        /* at the top */
+    } else {
+        igraph_i_2wheap_switch(h, elem, PARENT(elem));
+        igraph_i_2wheap_shift_up(h, PARENT(elem));
+    }
+}
+
+void igraph_i_2wheap_sink(igraph_2wheap_t *h,
+                          long int head) {
+    long int size = igraph_2wheap_size(h);
+    if (LEFTCHILD(head) >= size) {
+        /* no subtrees */
+    } else if (RIGHTCHILD(head) == size ||
+               VECTOR(h->data)[LEFTCHILD(head)] >= VECTOR(h->data)[RIGHTCHILD(head)]) {
+        /* sink to the left if needed */
+        if (VECTOR(h->data)[head] < VECTOR(h->data)[LEFTCHILD(head)]) {
+            igraph_i_2wheap_switch(h, head, LEFTCHILD(head));
+            igraph_i_2wheap_sink(h, LEFTCHILD(head));
+        }
+    } else {
+        /* sink to the right */
+        if (VECTOR(h->data)[head] < VECTOR(h->data)[RIGHTCHILD(head)]) {
+            igraph_i_2wheap_switch(h, head, RIGHTCHILD(head));
+            igraph_i_2wheap_sink(h, RIGHTCHILD(head));
+        }
+    }
+}
+
+/* ------------------ */
+/* These are public   */
+/* ------------------ */
+
+int igraph_2wheap_init(igraph_2wheap_t *h, long int size) {
+    h->size = size;
+    /* We start with the biggest */
+    IGRAPH_CHECK(igraph_vector_long_init(&h->index2, size));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &h->index2);
+    IGRAPH_VECTOR_INIT_FINALLY(&h->data, 0);
+    IGRAPH_CHECK(igraph_vector_long_init(&h->index, 0));
+    /* IGRAPH_FINALLY(igraph_vector_long_destroy, &h->index); */
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+void igraph_2wheap_destroy(igraph_2wheap_t *h) {
+    igraph_vector_destroy(&h->data);
+    igraph_vector_long_destroy(&h->index);
+    igraph_vector_long_destroy(&h->index2);
+}
+
+int igraph_2wheap_clear(igraph_2wheap_t *h) {
+    igraph_vector_clear(&h->data);
+    igraph_vector_long_clear(&h->index);
+    igraph_vector_long_null(&h->index2);
+    return 0;
+}
+
+igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h) {
+    return igraph_vector_empty(&h->data);
+}
+
+int igraph_2wheap_push_with_index(igraph_2wheap_t *h,
+                                  long int idx, igraph_real_t elem) {
+
+    /*   printf("-> %.2g [%li]\n", elem, idx); */
+
+    long int size = igraph_vector_size(&h->data);
+    IGRAPH_CHECK(igraph_vector_push_back(&h->data, elem));
+    IGRAPH_CHECK(igraph_vector_long_push_back(&h->index, idx));
+    VECTOR(h->index2)[idx] = size + 2;
+
+    /* maintain heap */
+    igraph_i_2wheap_shift_up(h, size);
+    return 0;
+}
+
+long int igraph_2wheap_size(const igraph_2wheap_t *h) {
+    return igraph_vector_size(&h->data);
+}
+
+long int igraph_2wheap_max_size(const igraph_2wheap_t *h) {
+    return h->size;
+}
+
+igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h) {
+    return VECTOR(h->data)[0];
+}
+
+long int igraph_2wheap_max_index(const igraph_2wheap_t *h) {
+    return VECTOR(h->index)[0];
+}
+
+igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, long int idx) {
+    return VECTOR(h->index2)[idx] != 0;
+}
+
+igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx) {
+    return VECTOR(h->index2)[idx] > 1;
+}
+
+igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, long int idx) {
+    long int i = VECTOR(h->index2)[idx] - 2;
+    return VECTOR(h->data)[i];
+}
+
+igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h) {
+
+    igraph_real_t tmp = VECTOR(h->data)[0];
+    long int tmpidx = VECTOR(h->index)[0];
+    igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1);
+    igraph_vector_pop_back(&h->data);
+    igraph_vector_long_pop_back(&h->index);
+    VECTOR(h->index2)[tmpidx] = 0;
+    igraph_i_2wheap_sink(h, 0);
+
+    /*   printf("<-max %.2g\n", tmp); */
+
+    return tmp;
+}
+
+igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h) {
+
+    igraph_real_t tmp = VECTOR(h->data)[0];
+    long int tmpidx = VECTOR(h->index)[0];
+    igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1);
+    igraph_vector_pop_back(&h->data);
+    igraph_vector_long_pop_back(&h->index);
+    VECTOR(h->index2)[tmpidx] = 1;
+    igraph_i_2wheap_sink(h, 0);
+
+    return tmp;
+}
+
+igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx) {
+
+    igraph_real_t tmp = VECTOR(h->data)[0];
+    long int tmpidx = VECTOR(h->index)[0];
+    igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1);
+    igraph_vector_pop_back(&h->data);
+    igraph_vector_long_pop_back(&h->index);
+    VECTOR(h->index2)[tmpidx] = 0;
+    igraph_i_2wheap_sink(h, 0);
+
+    if (idx) {
+        *idx = tmpidx;
+    }
+    return tmp;
+}
+
+int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem) {
+
+    long int pos = VECTOR(h->index2)[idx] - 2;
+
+    /*   printf("-- %.2g -> %.2g\n", VECTOR(h->data)[pos], elem); */
+
+    VECTOR(h->data)[pos] = elem;
+    igraph_i_2wheap_sink(h, pos);
+    igraph_i_2wheap_shift_up(h, pos);
+
+    return 0;
+}
+
+/* Check that the heap is in a consistent state */
+
+int igraph_2wheap_check(igraph_2wheap_t *h) {
+    long int size = igraph_2wheap_size(h);
+    long int i;
+    igraph_bool_t error = 0;
+
+    /* Check the heap property */
+    for (i = 0; i < size; i++) {
+        if (LEFTCHILD(i) >= size) {
+            break;
+        }
+        if (VECTOR(h->data)[LEFTCHILD(i)] > VECTOR(h->data)[i]) {
+            error = 1; break;
+        }
+        if (RIGHTCHILD(i) >= size) {
+            break;
+        }
+        if (VECTOR(h->data)[RIGHTCHILD(i)] > VECTOR(h->data)[i]) {
+            error = 1; break;
+        }
+    }
+
+    if (error) {
+        IGRAPH_ERROR("Inconsistent heap", IGRAPH_EINTERNAL);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/hl_ge.c b/igraph/src/hl_ge.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/hl_ge.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+shortlogical hl_ge(a,b,la,lb) char *a, *b; ftnlen la, lb;
+#else
+extern integer s_cmp(char *, char *, ftnlen, ftnlen);
+shortlogical hl_ge(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+return(s_cmp(a,b,la,lb) >= 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/hl_gt.c b/igraph/src/hl_gt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/hl_gt.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+shortlogical hl_gt(a,b,la,lb) char *a, *b; ftnlen la, lb;
+#else
+extern integer s_cmp(char *, char *, ftnlen, ftnlen);
+shortlogical hl_gt(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+return(s_cmp(a,b,la,lb) > 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/hl_le.c b/igraph/src/hl_le.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/hl_le.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+shortlogical hl_le(a,b,la,lb) char *a, *b; ftnlen la, lb;
+#else
+extern integer s_cmp(char *, char *, ftnlen, ftnlen);
+shortlogical hl_le(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+return(s_cmp(a,b,la,lb) <= 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/hl_lt.c b/igraph/src/hl_lt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/hl_lt.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+shortlogical hl_lt(a,b,la,lb) char *a, *b; ftnlen la, lb;
+#else
+extern integer s_cmp(char *, char *, ftnlen, ftnlen);
+shortlogical hl_lt(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+return(s_cmp(a,b,la,lb) < 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/i77vers.c b/igraph/src/i77vers.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i77vers.c
@@ -0,0 +1,343 @@
+ char
+_libi77_version_f2c[] = "\n@(#) LIBI77 VERSION (f2c) pjw,dmg-mods 20030321\n";
+
+/*
+2.01	$ format added
+2.02	Coding bug in open.c repaired
+2.03	fixed bugs in lread.c (read * with negative f-format) and lio.c
+	and lio.h (e-format conforming to spec)
+2.04	changed open.c and err.c (fopen and freopen respectively) to
+	update to new c-library (append mode)
+2.05	added namelist capability
+2.06	allow internal list and namelist I/O
+*/
+
+/*
+close.c:
+	allow upper-case STATUS= values
+endfile.c
+	create fort.nnn if unit nnn not open;
+	else if (file length == 0) use creat() rather than copy;
+	use local copy() rather than forking /bin/cp;
+	rewind, fseek to clear buffer (for no reading past EOF)
+err.c
+	use neither setbuf nor setvbuf; make stderr buffered
+fio.h
+	#define _bufend
+inquire.c
+	upper case responses;
+	omit byfile test from SEQUENTIAL=
+	answer "YES" to DIRECT= for unopened file (open to debate)
+lio.c
+	flush stderr, stdout at end of each stmt
+	space before character strings in list output only at line start
+lio.h
+	adjust LEW, LED consistent with old libI77
+lread.c
+	use atof()
+	allow "nnn*," when reading complex constants
+open.c
+	try opening for writing when open for read fails, with
+	special uwrt value (2) delaying creat() to first write;
+	set curunit so error messages don't drop core;
+	no file name ==> fort.nnn except for STATUS='SCRATCH'
+rdfmt.c
+	use atof(); trust EOF == end-of-file (so don't read past
+	end-of-file after endfile stmt)
+sfe.c
+	flush stderr, stdout at end of each stmt
+wrtfmt.c:
+	use upper case
+	put wrt_E and wrt_F into wref.c, use sprintf()
+		rather than ecvt() and fcvt() [more accurate on VAX]
+*/
+
+/* 16 Oct. 1988: uwrt = 3 after write, rewind, so close won't zap the file. */
+
+/* 10 July 1989: change _bufend to buf_end in fio.h, wsfe.c, wrtfmt.c */
+
+/* 28 Nov. 1989: corrections for IEEE and Cray arithmetic */
+/* 29 Nov. 1989: change various int return types to long for f2c */
+/* 30 Nov. 1989: various types from f2c.h */
+/*  6 Dec. 1989: types corrected various places */
+/* 19 Dec. 1989: make iostat= work right for internal I/O */
+/*  8 Jan. 1990: add rsne, wsne -- routines for handling NAMELIST */
+/* 28 Jan. 1990: have NAMELIST read treat $ as &, general white
+		 space as blank */
+/* 27 Mar. 1990: change an = to == in rd_L(rdfmt.c) so formatted reads
+		 of logical values reject letters other than fFtT;
+		 have nowwriting reset cf */
+/* 14 Aug. 1990: adjust lread.c to treat tabs as spaces in list input */
+/* 17 Aug. 1990: adjust open.c to recognize blank='Z...' as well as
+		 blank='z...' when reopening an open file */
+/* 30 Aug. 1990: prevent embedded blanks in list output of complex values;
+		 omit exponent field in list output of values of
+		 magnitude between 10 and 1e8; prevent writing stdin
+		 and reading stdout or stderr; don't close stdin, stdout,
+		 or stderr when reopening units 5, 6, 0. */
+/* 18 Sep. 1990: add component udev to unit and consider old == new file
+		 iff uinode and udev values agree; use stat rather than
+		 access to check existence of file (when STATUS='OLD')*/
+/* 2 Oct. 1990:  adjust rewind.c so two successive rewinds after a write
+		 don't clobber the file. */
+/* 9 Oct. 1990:  add #include "fcntl.h" to endfile.c, err.c, open.c;
+		 adjust g_char in util.c for segmented memories. */
+/* 17 Oct. 1990: replace abort() and _cleanup() with calls on
+		 sig_die(...,1) (defined in main.c). */
+/* 5 Nov. 1990:  changes to open.c: complain if new= is specified and the
+		 file already exists; allow file= to be omitted in open stmts
+		 and allow status='replace' (Fortran 90 extensions). */
+/* 11 Dec. 1990: adjustments for POSIX. */
+/* 15 Jan. 1991: tweak i_ungetc in rsli.c to allow reading from
+		 strings in read-only memory. */
+/* 25 Apr. 1991: adjust namelist stuff to work with f2c -i2 */
+/* 26 Apr. 1991: fix some bugs with NAMELIST read of multi-dim. arrays */
+/* 16 May 1991:  increase LEFBL in lio.h to bypass NeXT bug */
+/* 17 Oct. 1991: change type of length field in sequential unformatted
+		 records from int to long (for systems where sizeof(int)
+		 can vary, depending on the compiler or compiler options). */
+/* 14 Nov. 1991: change uint to Uint in fmt.h, rdfmt.c, wrtfmt.c. */
+/* 25 Nov. 1991: change uint to Uint in lwrite.c; change sizeof(int) to
+		 sizeof(uioint) in fseeks in sue.c (missed on 17 Oct.). */
+/* 1 Dec. 1991:  uio.c: add test for read failure (seq. unformatted reads);
+		 adjust an error return from EOF to off end of record */
+/* 12 Dec. 1991: rsli.c: fix bug with internal list input that caused
+		 the last character of each record to be ignored.
+		 iio.c: adjust error message in internal formatted
+		 input from "end-of-file" to "off end of record" if
+		 the format specifies more characters than the
+		 record contains. */
+/* 17 Jan. 1992: lread.c, rsne.c: in list and namelist input,
+		 treat "r* ," and "r*," alike (where r is a
+		 positive integer constant), and fix a bug in
+		 handling null values following items with repeat
+		 counts (e.g., 2*1,,3); for namelist reading
+		 of a numeric array, allow a new name-value subsequence
+		 to terminate the current one (as though the current
+		 one ended with the right number of null values).
+		 lio.h, lwrite.c: omit insignificant zeros in
+		 list and namelist output. To get the old
+		 behavior, compile with -DOld_list_output . */
+/* 18 Jan. 1992: make list output consistent with F format by
+		 printing .1 rather than 0.1 (introduced yesterday). */
+/* 3 Feb. 1992:  rsne.c: fix namelist read bug that caused the
+		 character following a comma to be ignored. */
+/* 19 May 1992:  adjust iio.c, ilnw.c, rdfmt.c and rsli.c to make err=
+		 work with internal list and formatted I/O. */
+/* 18 July 1992: adjust rsne.c to allow namelist input to stop at
+		 an & (e.g. &end). */
+/* 23 July 1992: switch to ANSI prototypes unless KR_headers is #defined ;
+		 recognize Z format (assuming 8-bit bytes). */
+/* 14 Aug. 1992: tweak wrt_E in wref.c to avoid -NaN */
+/* 23 Oct. 1992: Supply missing l_eof = 0 assignment to s_rsne() in rsne.c
+		 (so end-of-file on other files won't confuse namelist
+		 reads of external files).  Prepend f__ to external
+		 names that are only of internal interest to lib[FI]77. */
+/* 1 Feb. 1993:  backspace.c: fix bug that bit when last char of 2nd
+		 buffer == '\n'.
+		 endfile.c: guard against tiny L_tmpnam; close and reopen
+		 files in t_runc().
+		 lio.h: lengthen LINTW (buffer size in lwrite.c).
+		 err.c, open.c: more prepending of f__ (to [rw]_mode). */
+/* 5 Feb. 1993:  tweaks to NAMELIST: rsne.c: ? prints the namelist being
+		 sought; namelists of the wrong name are skipped (after
+		 an error message; xwsne.c: namelist writes have a
+		 newline before each new variable.
+		 open.c: ACCESS='APPEND' positions sequential files
+		 at EOF (nonstandard extension -- that doesn't require
+		 changing data structures). */
+/* 9 Feb. 1993:  Change some #ifdef MSDOS lines to #ifdef NON_UNIX_STDIO.
+		 err.c: under NON_UNIX_STDIO, avoid close(creat(name,0666))
+		 when the unit has another file descriptor for name. */
+/* 4 March 1993: err.c, open.c: take declaration of fdopen from rawio.h;
+		 open.c: always give f__w_mode[] 4 elements for use
+		 in t_runc (in endfile.c -- for change of 1 Feb. 1993). */
+/* 6 March 1993: uio.c: adjust off-end-of-record test for sequential
+		 unformatted reads to respond to err= rather than end=. */
+/* 12 March 1993: various tweaks for C++ */
+/* 6 April 1993: adjust error returns for formatted inputs to flush
+		 the current input line when err=label is specified.
+		 To restore the old behavior (input left mid-line),
+		 either adjust the #definition of errfl in fio.h or
+		 omit the invocation of f__doend in err__fl (in err.c).	*/
+/* 23 June 1993: iio.c: fix bug in format reversions for internal writes. */
+/* 5 Aug. 1993:  lread.c: fix bug in handling repetition counts for
+		 logical data (during list or namelist input).
+		 Change struct f__syl to struct syl (for buggy compilers). */
+/* 7 Aug. 1993:  lread.c: fix bug in namelist reading of incomplete
+		 logical arrays. */
+/* 9 Aug. 1993:  lread.c: fix bug in namelist reading of an incomplete
+		 array of numeric data followed by another namelist
+		 item whose name starts with 'd', 'D', 'e', or 'E'. */
+/* 8 Sept. 1993: open.c: protect #include "sys/..." with
+		 #ifndef NON_UNIX_STDIO; Version date not changed. */
+/* 10 Nov. 1993: backspace.c: add nonsense for #ifdef MSDOS */
+/* 8 Dec. 1993:  iio.c: adjust internal formatted reads to treat
+		 short records as though padded with blanks
+		 (rather than causing an "off end of record" error). */
+/* 22 Feb. 1994: lread.c: check that realloc did not return NULL. */
+/* 6 June 1994:  Under NON_UNIX_STDIO, use binary mode for direct
+		 formatted files (avoiding any confusion regarding \n). */
+/* 5 July 1994:  Fix bug (introduced 6 June 1994?) in reopening files
+		 under NON_UNIX_STDIO. */
+/* 6 July 1994:  wref.c: protect with #ifdef GOOD_SPRINTF_EXPONENT an
+		 optimization that requires exponents to have 2 digits
+		 when 2 digits suffice.
+		 lwrite.c wsfe.c (list and formatted external output):
+		 omit ' ' carriage-control when compiled with
+		 -DOMIT_BLANK_CC .  Off-by-one bug fixed in character
+		 count for list output of character strings.
+		 Omit '.' in list-directed printing of Nan, Infinity. */
+/* 12 July 1994: wrtfmt.c: under G11.4, write 0. as "  .0000    " rather
+		 than "  .0000E+00". */
+/* 3 Aug. 1994:  lwrite.c: do not insert a newline when appending an
+		 oversize item to an empty line. */
+/* 12 Aug. 1994: rsli.c rsne.c: fix glitch (reset nml_read) that kept
+		 ERR= (in list- or format-directed input) from working
+		 after a NAMELIST READ. */
+/* 7 Sept. 1994: typesize.c: adjust to allow types LOGICAL*1, LOGICAL*2,
+		 INTEGER*1, and (under -DAllow_TYQUAD) INTEGER*8
+		 in NAMELISTs. */
+/* 6 Oct. 1994:  util.c: omit f__mvgbt, as it is never used. */
+/* 2 Nov. 1994:  add #ifdef ALWAYS_FLUSH logic. */
+/* 26 Jan. 1995: wref.c: fix glitch in printing the exponent of 0 when
+		 GOOD_SPRINTF_EXPONENT is not #defined. */
+/* 24 Feb. 1995: iio.c: z_getc: insert (unsigned char *) to allow
+		 internal reading of characters with high-bit set
+		 (on machines that sign-extend characters). */
+/* 14 March 1995:lread.c and rsfe.c: adjust s_rsle and s_rsfe to
+		 check for end-of-file (to prevent infinite loops
+		 with empty read statements). */
+/* 26 May 1995:  iio.c: z_wnew: fix bug in handling T format items
+		 in internal writes whose last item is written to
+		 an earlier position than some previous item. */
+/* 29 Aug. 1995: backspace.c: adjust MSDOS logic. */
+/* 6 Sept. 1995: Adjust namelist input to treat a subscripted name
+		 whose subscripts do not involve colons similarly
+		 to the name without a subscript: accept several
+		 values, stored in successive elements starting at
+		 the indicated subscript.  Adjust namelist output
+		 to quote character strings (avoiding confusion with
+		 arrays of character strings).  Adjust f_init calls
+		 for people who don't use libF77's main(); now open and
+		 namelist read statements invoke f_init if needed. */
+/* 7 Sept. 1995: Fix some bugs with -DAllow_TYQUAD (for integer*8).
+		 Add -DNo_Namelist_Comments lines to rsne.c. */
+/* 5 Oct. 1995:  wrtfmt.c: fix bug with t editing (f__cursor was not
+		 always zeroed in mv_cur). */
+/* 11 Oct. 1995: move defs of f__hiwater, f__svic, f__icptr from wrtfmt.c
+		 to err.c */
+/* 15 Mar. 1996: lread.c, rsfe.c: honor END= in READ stmt with empty iolist */
+
+/* 13 May 1996:  add ftell_.c and fseek_.c */
+/* 9 June 1996:  Adjust rsli.c and lread.c so internal list input with
+		 too few items in the input string will honor end= . */
+/* 12 Sept. 1995:fmtlib.c: fix glitch in printing the most negative integer. */
+/* 25 Sept. 1995:fmt.h: for formatted writes of negative integer*1 values,
+		 make ic signed on ANSI systems.  If formatted writes of
+		 integer*1 values trouble you when using a K&R C compiler,
+		 switch to an ANSI compiler or use a compiler flag that
+		 makes characters signed. */
+/* 9 Dec. 1996:	 d[fu]e.c, err.c: complain about non-positive rec=
+		 in direct read and write statements.
+		 ftell_.c: change param "unit" to "Unit" for -DKR_headers. */
+/* 26 Feb. 1997: ftell_.c: on systems that define SEEK_SET, etc., use
+		 SEEK_SET, SEEK_CUR, SEEK_END for *whence = 0, 1, 2. */
+/* 7 Apr. 1997:	 fmt.c: adjust to complain at missing numbers in formats
+		 (but still treat missing ".nnn" as ".0"). */
+/* 11 Apr. 1997: err.c: attempt to make stderr line buffered rather
+		 than fully buffered.  (Buffering is needed for format
+		 items T and TR.) */
+/* 27 May 1997:  ftell_.c: fix typo (that caused the third argument to be
+		 treated as 2 on some systems). */
+/* 5 Aug. 1997:  lread.c: adjust to accord with a change to the Fortran 8X
+		 draft (in 1990 or 1991) that rescinded permission to elide
+		 quote marks in namelist input of character data; compile
+		 with -DF8X_NML_ELIDE_QUOTES to get the old behavior.
+		 wrtfmt.o: wrt_G: tweak to print the right number of 0's
+		 for zero under G format. */
+/* 16 Aug. 1997: iio.c: fix bug in internal writes to an array of character
+		 strings that sometimes caused one more array element than
+		 required by the format to be blank-filled.  Example:
+		 format(1x). */
+/* 16 Sept. 1997:fmt.[ch] rdfmt.c wrtfmt.c: tweak struct syl for machines
+		 with 64-bit pointers and 32-bit ints that did not 64-bit
+		 align struct syl (e.g., Linux on the DEC Alpha). */
+/* 19 Jan. 1998: backspace.c: for b->ufmt==0, change sizeof(int) to
+		 sizeof(uiolen).  On machines where this would make a
+		 difference, it is best for portability to compile libI77 with
+		 -DUIOLEN_int (which will render the change invisible). */
+/* 4 March 1998: open.c: fix glitch in comparing file names under
+		-DNON_UNIX_STDIO */
+/* 17 March 1998: endfile.c, open.c: acquire temporary files from tmpfile(),
+		 unless compiled with -DNON_ANSI_STDIO, which uses mktemp().
+		 New buffering scheme independent of NON_UNIX_STDIO for
+		 handling T format items.  Now -DNON_UNIX_STDIO is no
+		 longer be necessary for Linux, and libf2c no longer
+		 causes stderr to be buffered -- the former setbuf or
+		 setvbuf call for stderr was to make T format items work.
+		 open.c: use the Posix access() function to check existence
+		 or nonexistence of files, except under -DNON_POSIX_STDIO,
+		 where trial fopen calls are used. */
+/* 5 April 1998: wsfe.c: make $ format item work: this was lost in the
+		 changes of 17 March 1998. */
+/* 28 May 1998:	 backspace.c dfe.c due.c iio.c lread.c rsfe.c sue.c wsfe.c:
+		 set f__curunit sooner so various error messages will
+		 correctly identify the I/O unit involved. */
+/* 17 June 1998: lread.c: unless compiled with
+		 ALLOW_FLOAT_IN_INTEGER_LIST_INPUT #defined, treat
+		 floating-point numbers (containing either a decimal point
+		 or an exponent field) as errors when they appear as list
+		 input for integer data. */
+/* 7 Sept. 1998: move e_wdfe from sfe.c to dfe.c, where it was originally.
+		 Why did it ever move to sfe.c? */
+/* 2 May 1999:	 open.c: set f__external (to get "external" versus "internal"
+		 right in the error message if we cannot open the file).
+		 err.c: cast a pointer difference to (int) for %d.
+		 rdfmt.c: omit fixed-length buffer that could be overwritten
+		 by formats Inn or Lnn with nn > 83. */
+/* 3 May 1999:	open.c: insert two casts for machines with 64-bit longs. */
+/* 18 June 1999: backspace.c: allow for b->ufd changing in t_runc */
+/* 27 June 1999: rsne.c: fix bug in namelist input: a misplaced increment */
+/*		 could cause wrong array elements to be assigned; e.g.,	*/
+/*		 "&input k(5)=10*1 &end" assigned k(5) and k(15..23)	*/
+/* 15 Nov. 1999: endfile.c: set state to writing (b->uwrt = 1) when an */
+/*		endfile statement requires copying the file. */
+/*		(Otherwise an immediately following rewind statement */
+/*		could make the file appear empty.)  Also, supply a */
+/*		missing (long) cast in the sprintf call. */
+/*		 sfe.c: add #ifdef ALWAYS_FLUSH logic, for formatted I/O: */
+/*		Compiling libf2c with -DALWAYS_FLUSH should prevent losing */
+/*		any data in buffers should the program fault.  It also */
+/*		makes the program run more slowly. */
+/* 20 April 2000: rsne.c, xwsne.c: tweaks that only matter if ftnint and */
+/*		ftnlen are of different fundamental types (different numbers */
+/*		of bits).  Since these files will not compile when this */
+/*		change matters, the above VERSION string remains unchanged. */
+/* 4 July 2000: adjustments to permit compilation by C++ compilers; */
+/*		VERSION string remains unchanged. */
+/* 5 Dec. 2000: lread.c: under namelist input, when reading a logical array, */
+/*		treat Tstuff= and Fstuff= as new assignments rather than as */
+/*		logical constants. */
+/* 22 Feb. 2001: endfile.c: adjust to use truncate() unless compiled with */
+/*		-DNO_TRUNCATE (or with -DMSDOS). */
+/* 1 March 2001: endfile.c:  switch to ftruncate (absent -DNO_TRUNCATE), */
+/*		thus permitting truncation of scratch files on true Unix */
+/*		systems, where scratch files have no name.  Add an fflush() */
+/*		(surprisingly) needed on some Linux systems. */
+/* 11 Oct. 2001: backspac.c dfe.c due.c endfile.c err.c fio.h fmt.c fmt.h */
+/*		inquire.c open.c rdfmt.c sue.c util.c: change fseek and */
+/*		ftell to FSEEK and FTELL (#defined to be fseek and ftell, */
+/*		respectively, in fio.h unless otherwise #defined), and use */
+/*		type OFF_T (#defined to be long unless otherwise #defined) */
+/*		to permit handling files over 2GB long where possible, */
+/*		with suitable -D options, provided for some systems in new */
+/*		header file sysdep1.h (copied from sysdep1.h0 by default). */
+/* 15 Nov. 2001: endfile.c: add FSEEK after FTRUNCATE. */
+/* 28 Nov. 2001: fmt.h lwrite.c wref.c and (new) signbit.c: on IEEE systems, */
+/*		print -0 as -0 when compiled with -DSIGNED_ZEROS.  See */
+/*		comments in makefile or (better) libf2c/makefile.* . */
+/* 6 Sept. 2002: rsne.c: fix bug with multiple repeat counts in reading */
+/*		namelists, e.g., &nl a(2) = 3*1.0, 2*2.0, 3*3.0 /  */
+/* 21 March 2003: err.c: before writing to a file after reading from it, */
+/*		f_seek(file, 0, SEEK_CUR) to make writing legal in ANSI C. */
diff --git a/igraph/src/i_abs.c b/igraph/src/i_abs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i_abs.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+integer i_abs(x) integer *x;
+#else
+integer i_abs(integer *x)
+#endif
+{
+if(*x >= 0)
+	return(*x);
+return(- *x);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/i_dim.c b/igraph/src/i_dim.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i_dim.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+integer i_dim(a,b) integer *a, *b;
+#else
+integer i_dim(integer *a, integer *b)
+#endif
+{
+return( *a > *b ? *a - *b : 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/i_dnnt.c b/igraph/src/i_dnnt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i_dnnt.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double floor();
+integer i_dnnt(x) doublereal *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+integer i_dnnt(doublereal *x)
+#endif
+{
+return (integer)(*x >= 0. ? floor(*x + .5) : -floor(.5 - *x));
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/i_indx.c b/igraph/src/i_indx.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i_indx.c
@@ -0,0 +1,32 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+integer i_indx(a, b, la, lb) char *a, *b; ftnlen la, lb;
+#else
+integer i_indx(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+ftnlen i, n;
+char *s, *t, *bend;
+
+n = la - lb + 1;
+bend = b + lb;
+
+for(i = 0 ; i < n ; ++i)
+	{
+	s = a + i;
+	t = b;
+	while(t < bend)
+		if(*s++ != *t++)
+			goto no;
+	return(i+1);
+	no: ;
+	}
+return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/i_len.c b/igraph/src/i_len.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i_len.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+integer i_len(s, n) char *s; ftnlen n;
+#else
+integer i_len(char *s, ftnlen n)
+#endif
+{
+return(n);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/i_mod.c b/igraph/src/i_mod.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i_mod.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+integer i_mod(a,b) integer *a, *b;
+#else
+integer i_mod(integer *a, integer *b)
+#endif
+{
+return( *a % *b);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/i_nint.c b/igraph/src/i_nint.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i_nint.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double floor();
+integer i_nint(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+integer i_nint(real *x)
+#endif
+{
+return (integer)(*x >= 0 ? floor(*x + .5) : -floor(.5 - *x));
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/i_sign.c b/igraph/src/i_sign.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/i_sign.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+integer i_sign(a,b) integer *a, *b;
+#else
+integer i_sign(integer *a, integer *b)
+#endif
+{
+integer x;
+x = (*a >= 0 ? *a : - *a);
+return( *b >= 0 ? x : -x);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/idamax.c b/igraph/src/idamax.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/idamax.c
@@ -0,0 +1,82 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+integer igraphidamax_(integer *n, doublereal *dx, integer *incx)
+{
+    /* System generated locals */
+    integer ret_val, i__1;
+    doublereal d__1;
+
+    /* Local variables */
+    integer i__, ix;
+    doublereal dmax__;
+
+
+/*  Purpose   
+    =======   
+
+       IDAMAX finds the index of element having max. absolute value.   
+
+    Further Details   
+    ===============   
+
+       jack dongarra, linpack, 3/11/78.   
+       modified 3/93 to return if incx .le. 0.   
+       modified 12/3/93, array(1) declarations changed to array(*)   
+
+    =====================================================================   
+
+       Parameter adjustments */
+    --dx;
+
+    /* Function Body */
+    ret_val = 0;
+    if (*n < 1 || *incx <= 0) {
+	return ret_val;
+    }
+    ret_val = 1;
+    if (*n == 1) {
+	return ret_val;
+    }
+    if (*incx == 1) {
+
+/*        code for increment equal to 1 */
+
+	dmax__ = abs(dx[1]);
+	i__1 = *n;
+	for (i__ = 2; i__ <= i__1; ++i__) {
+	    if ((d__1 = dx[i__], abs(d__1)) > dmax__) {
+		ret_val = i__;
+		dmax__ = (d__1 = dx[i__], abs(d__1));
+	    }
+	}
+    } else {
+
+/*        code for increment not equal to 1 */
+
+	ix = 1;
+	dmax__ = abs(dx[1]);
+	ix += *incx;
+	i__1 = *n;
+	for (i__ = 2; i__ <= i__1; ++i__) {
+	    if ((d__1 = dx[ix], abs(d__1)) > dmax__) {
+		ret_val = i__;
+		dmax__ = (d__1 = dx[ix], abs(d__1));
+	    }
+	    ix += *incx;
+	}
+    }
+    return ret_val;
+} /* igraphidamax_ */
+
diff --git a/igraph/src/ieeeck.c b/igraph/src/ieeeck.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ieeeck.c
@@ -0,0 +1,218 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b IEEECK   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download IEEECK + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/ieeeck.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/ieeeck.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/ieeeck.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         INTEGER          FUNCTION IEEECK( ISPEC, ZERO, ONE )   
+
+         INTEGER            ISPEC   
+         REAL               ONE, ZERO   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > IEEECK is called from the ILAENV to verify that Infinity and   
+   > possibly NaN arithmetic is safe (i.e. will not trap).   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] ISPEC   
+   > \verbatim   
+   >          ISPEC is INTEGER   
+   >          Specifies whether to test just for inifinity arithmetic   
+   >          or whether to test for infinity and NaN arithmetic.   
+   >          = 0: Verify infinity arithmetic only.   
+   >          = 1: Verify infinity and NaN arithmetic.   
+   > \endverbatim   
+   >   
+   > \param[in] ZERO   
+   > \verbatim   
+   >          ZERO is REAL   
+   >          Must contain the value 0.0   
+   >          This is passed to prevent the compiler from optimizing   
+   >          away this code.   
+   > \endverbatim   
+   >   
+   > \param[in] ONE   
+   > \verbatim   
+   >          ONE is REAL   
+   >          Must contain the value 1.0   
+   >          This is passed to prevent the compiler from optimizing   
+   >          away this code.   
+   >   
+   >  RETURN VALUE:  INTEGER   
+   >          = 0:  Arithmetic failed to produce the correct answers   
+   >          = 1:  Arithmetic produced the correct answers   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERauxiliary   
+
+    ===================================================================== */
+integer igraphieeeck_(integer *ispec, real *zero, real *one)
+{
+    /* System generated locals */
+    integer ret_val;
+
+    /* Local variables */
+    real nan1, nan2, nan3, nan4, nan5, nan6, neginf, posinf, negzro, newzro;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    ===================================================================== */
+
+    ret_val = 1;
+
+    posinf = *one / *zero;
+    if (posinf <= *one) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    neginf = -(*one) / *zero;
+    if (neginf >= *zero) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    negzro = *one / (neginf + *one);
+    if (negzro != *zero) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    neginf = *one / negzro;
+    if (neginf >= *zero) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    newzro = negzro + *zero;
+    if (newzro != *zero) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    posinf = *one / newzro;
+    if (posinf <= *one) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    neginf *= posinf;
+    if (neginf >= *zero) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    posinf *= posinf;
+    if (posinf <= *one) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+
+
+
+/*     Return if we were only asked to check infinity arithmetic */
+
+    if (*ispec == 0) {
+	return ret_val;
+    }
+
+    nan1 = posinf + neginf;
+
+    nan2 = posinf / neginf;
+
+    nan3 = posinf / posinf;
+
+    nan4 = posinf * *zero;
+
+    nan5 = neginf * negzro;
+
+    nan6 = nan5 * *zero;
+
+    if (nan1 == nan1) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    if (nan2 == nan2) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    if (nan3 == nan3) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    if (nan4 == nan4) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    if (nan5 == nan5) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    if (nan6 == nan6) {
+	ret_val = 0;
+	return ret_val;
+    }
+
+    return ret_val;
+} /* igraphieeeck_ */
+
diff --git a/igraph/src/igraph_buckets.c b/igraph/src/igraph_buckets.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_buckets.c
@@ -0,0 +1,198 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_types_internal.h"
+#include "config.h"
+
+#include <stdio.h>
+
+/* The igraph_buckets_t data structure can store at most 'size'
+ * unique integers in 'bsize' buckets. It has the following simple
+ * operations (in addition to _init() and _destroy():
+ * - _add() adding an element to the given bucket.
+ * - _popmax() removing an element from the bucket with the highest
+ *   id.
+ *   Currently buckets work as stacks, last-in-first-out mode.
+ * - _empty() queries whether the buckets is empty.
+ *
+ * Internal representation: we use a vector to create single linked
+ * lists, and another vector that points to the starting element of
+ * each bucket. Zero means the end of the chain. So bucket i contains
+ * elements bptr[i], buckets[bptr[i]], buckets[buckets[bptr[i]]],
+ * etc., until a zero is found.
+ *
+ * We also keep the total number of elements in the buckets and the
+ * id of the non-empty bucket with the highest id, to facilitate the
+ * _empty() and _popmax() operations.
+ */
+
+int igraph_buckets_init(igraph_buckets_t *b, long int bsize, long int size) {
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->bptr, bsize);
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->buckets, size);
+    b->max = -1; b->no = 0;
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+void igraph_buckets_destroy(igraph_buckets_t *b) {
+    igraph_vector_long_destroy(&b->bptr);
+    igraph_vector_long_destroy(&b->buckets);
+}
+
+long int igraph_buckets_popmax(igraph_buckets_t *b) {
+    /* Precondition: there is at least a non-empty bucket */
+    /* Search for the highest bucket first */
+    long int max;
+    while ( (max = (long int) VECTOR(b->bptr)[(long int) b->max]) == 0) {
+        b->max --;
+    }
+    VECTOR(b->bptr)[(long int) b->max] = VECTOR(b->buckets)[max - 1];
+    b->no--;
+
+    return max - 1;
+}
+
+long int igraph_buckets_pop(igraph_buckets_t *b, long int bucket) {
+    long int ret = VECTOR(b->bptr)[bucket] - 1;
+    VECTOR(b->bptr)[bucket] = VECTOR(b->buckets)[ret];
+    b->no--;
+    return ret;
+}
+
+igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b) {
+    return (b->no == 0);
+}
+
+igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b,
+        long int bucket) {
+    return VECTOR(b->bptr)[bucket] == 0;
+}
+
+void igraph_buckets_add(igraph_buckets_t *b, long int bucket,
+                        long int elem) {
+
+    VECTOR(b->buckets)[(long int) elem] = VECTOR(b->bptr)[(long int) bucket];
+    VECTOR(b->bptr)[(long int) bucket] = elem + 1;
+    if (bucket > b->max) {
+        b->max = (int) bucket;
+    }
+    b->no++;
+}
+
+void igraph_buckets_clear(igraph_buckets_t *b) {
+    igraph_vector_long_null(&b->bptr);
+    igraph_vector_long_null(&b->buckets);
+    b->max = -1;
+    b->no = 0;
+}
+
+int igraph_dbuckets_init(igraph_dbuckets_t *b, long int bsize, long int size) {
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->bptr, bsize);
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->next, size);
+    IGRAPH_VECTOR_LONG_INIT_FINALLY(&b->prev, size);
+    b->max = -1; b->no = 0;
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+void igraph_dbuckets_destroy(igraph_dbuckets_t *b) {
+    igraph_vector_long_destroy(&b->bptr);
+    igraph_vector_long_destroy(&b->next);
+    igraph_vector_long_destroy(&b->prev);
+}
+
+void igraph_dbuckets_clear(igraph_dbuckets_t *b) {
+    igraph_vector_long_null(&b->bptr);
+    igraph_vector_long_null(&b->next);
+    igraph_vector_long_null(&b->prev);
+    b->max = -1;
+    b->no = 0;
+}
+
+long int igraph_dbuckets_popmax(igraph_dbuckets_t *b) {
+    long int max;
+    while ( (max = (long int) VECTOR(b->bptr)[(long int) b->max]) == 0) {
+        b->max --;
+    }
+    return igraph_dbuckets_pop(b, b->max);
+}
+
+long int igraph_dbuckets_pop(igraph_dbuckets_t *b, long int bucket) {
+    long int ret = VECTOR(b->bptr)[bucket] - 1;
+    long int next = VECTOR(b->next)[ret];
+    VECTOR(b->bptr)[bucket] = next;
+    if (next != 0) {
+        VECTOR(b->prev)[next - 1] = 0;
+    }
+
+    b->no--;
+    return ret;
+}
+
+igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b) {
+    return (b->no == 0);
+}
+
+igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b,
+        long int bucket) {
+    return VECTOR(b->bptr)[bucket] == 0;
+}
+
+void igraph_dbuckets_add(igraph_dbuckets_t *b, long int bucket,
+                         long int elem) {
+    long int oldfirst = VECTOR(b->bptr)[bucket];
+    VECTOR(b->bptr)[bucket] = elem + 1;
+    VECTOR(b->next)[elem] = oldfirst;
+    if (oldfirst != 0) {
+        VECTOR(b->prev)[oldfirst - 1] = elem + 1;
+    }
+    if (bucket > b->max) {
+        b->max = (int) bucket;
+    }
+    b->no++;
+}
+
+/* Remove an arbitrary element */
+
+void igraph_dbuckets_delete(igraph_dbuckets_t *b, long int bucket,
+                            long int elem) {
+    if (VECTOR(b->bptr)[bucket] == elem + 1) {
+        /* First element in bucket */
+        long int next = VECTOR(b->next)[elem];
+        if (next != 0) {
+            VECTOR(b->prev)[next - 1] = 0;
+        }
+        VECTOR(b->bptr)[bucket] = next;
+    } else {
+        long int next = VECTOR(b->next)[elem];
+        long int prev = VECTOR(b->prev)[elem];
+        if (next != 0) {
+            VECTOR(b->prev)[next - 1] = prev;
+        }
+        if (prev != 0) {
+            VECTOR(b->next)[prev - 1] = next;
+        }
+    }
+    b->no--;
+}
diff --git a/igraph/src/igraph_cliquer.c b/igraph/src/igraph_cliquer.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_cliquer.c
@@ -0,0 +1,399 @@
+
+#include "igraph_cliquer.h"
+#include "igraph_memory.h"
+#include "igraph_constants.h"
+#include "igraph_interrupt_internal.h"
+#include "cliquer/cliquer.h"
+#include "config.h"
+
+#include <assert.h>
+
+
+/* Call this to allow for interruption in Cliquer callback functions */
+#define CLIQUER_ALLOW_INTERRUPTION() \
+    { \
+        if (igraph_i_interruption_handler) \
+            if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { \
+                cliquer_interrupted = 1; \
+                return FALSE; \
+            } \
+    }
+
+/* Interruptable Cliquer functions must be wrapped in CLIQUER_INTERRUPTABLE when called */
+#define CLIQUER_INTERRUPTABLE(x) \
+    { \
+        cliquer_interrupted = 0; \
+        x; \
+        if (cliquer_interrupted) return IGRAPH_INTERRUPTED; \
+    }
+
+
+/* Nonzero value signals interuption from Cliquer callback function */
+static IGRAPH_THREAD_LOCAL int cliquer_interrupted;
+
+
+/* For use with IGRAPH_FINALLY */
+static void free_clique_list(igraph_vector_ptr_t *vp) {
+    igraph_integer_t i, len;
+    len = igraph_vector_ptr_size(vp);
+    for (i = 0; i < len; ++i) {
+        igraph_vector_destroy((igraph_vector_t *) VECTOR(*vp)[i]);
+    }
+    igraph_vector_ptr_free_all(vp);
+}
+
+/* We shall use this option struct for all calls to Cliquer */
+static IGRAPH_THREAD_LOCAL clique_options igraph_cliquer_opt = {
+    reorder_by_default, NULL, NULL, NULL, NULL, NULL, NULL, 0
+};
+
+
+/* Convert an igraph graph to a Cliquer graph */
+static void igraph_to_cliquer(const igraph_t *ig, graph_t **cg) {
+    igraph_integer_t vcount, ecount;
+    int i;
+
+    if (igraph_is_directed(ig)) {
+        IGRAPH_WARNING("Edge directions are ignored for clique calculations");
+    }
+
+    vcount = igraph_vcount(ig);
+    ecount = igraph_ecount(ig);
+
+    *cg = graph_new(vcount);
+
+    for (i = 0; i < ecount; ++i) {
+        long s, t;
+        s = IGRAPH_FROM(ig, i);
+        t = IGRAPH_TO(ig, i);
+        if (s != t) {
+            GRAPH_ADD_EDGE(*cg, s, t);
+        }
+    }
+}
+
+
+/* Copy weights to a Cliquer graph */
+static int set_weights(const igraph_vector_t *vertex_weights, graph_t *g) {
+    int i;
+
+    assert(vertex_weights != NULL);
+
+    if (igraph_vector_size(vertex_weights) != g->n) {
+        IGRAPH_ERROR("Invalid vertex weight vector length", IGRAPH_EINVAL);
+    }
+
+    for (i = 0; i < g->n; ++i) {
+        g->weights[i] = VECTOR(*vertex_weights)[i];
+        if (g->weights[i] != VECTOR(*vertex_weights)[i]) {
+            IGRAPH_WARNING("Only integer vertex weights are supported; weights will be truncated to their integer parts");
+        }
+        if (g->weights[i] <= 0) {
+            IGRAPH_ERROR("Vertex weights must be positive", IGRAPH_EINVAL);
+        }
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/* Find all cliques. */
+
+static boolean collect_cliques_callback(set_t s, graph_t *g, clique_options *opt) {
+    igraph_vector_ptr_t *list;
+    igraph_vector_t *clique;
+    int i, j;
+
+    CLIQUER_ALLOW_INTERRUPTION();
+
+    list = (igraph_vector_ptr_t *) opt->user_data;
+    clique = (igraph_vector_t *) malloc(sizeof(igraph_vector_t));
+    igraph_vector_init(clique, set_size(s));
+
+    i = -1; j = 0;
+    while ((i = set_return_next(s, i)) >= 0) {
+        VECTOR(*clique)[j++] = i;
+    }
+
+    igraph_vector_ptr_push_back(list, clique);
+
+    return TRUE;
+}
+
+int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
+                             igraph_integer_t min_size, igraph_integer_t max_size) {
+    graph_t *g;
+    igraph_integer_t vcount = igraph_vcount(graph);
+
+    if (vcount == 0) {
+        igraph_vector_ptr_clear(res);
+        return IGRAPH_SUCCESS;
+    }
+
+    if (min_size <= 0) {
+        min_size = 1;
+    }
+    if (max_size <= 0) {
+        max_size = 0;
+    }
+
+    if (max_size > 0 && max_size < min_size) {
+        IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL);
+    }
+
+    igraph_to_cliquer(graph, &g);
+    IGRAPH_FINALLY(graph_free, g);
+
+    igraph_vector_ptr_clear(res);
+    igraph_cliquer_opt.user_data = res;
+    igraph_cliquer_opt.user_function = &collect_cliques_callback;
+
+    IGRAPH_FINALLY(free_clique_list, res);
+    CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt));
+    IGRAPH_FINALLY_CLEAN(1);
+
+    graph_free(g);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/* Count cliques of each size. */
+
+static boolean count_cliques_callback(set_t s, graph_t *g, clique_options *opt) {
+    igraph_vector_t *hist;
+
+    CLIQUER_ALLOW_INTERRUPTION();
+
+    hist = (igraph_vector_t *) opt->user_data;
+    VECTOR(*hist)[set_size(s) - 1] += 1;
+
+    return TRUE;
+}
+
+int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist,
+                               igraph_integer_t min_size, igraph_integer_t max_size) {
+    graph_t *g;
+    int i;
+    igraph_integer_t vcount = igraph_vcount(graph);
+
+    if (vcount == 0) {
+        igraph_vector_clear(hist);
+        return IGRAPH_SUCCESS;
+    }
+
+    if (min_size <= 0) {
+        min_size = 1;
+    }
+    if (max_size <= 0) {
+        max_size = vcount;    /* also used for initial hist vector size, do not set to zero */
+    }
+
+    if (max_size < min_size) {
+        IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL);
+    }
+
+    igraph_to_cliquer(graph, &g);
+    IGRAPH_FINALLY(graph_free, g);
+
+    igraph_vector_resize(hist, max_size);
+    igraph_vector_null(hist);
+    igraph_cliquer_opt.user_data = hist;
+    igraph_cliquer_opt.user_function = &count_cliques_callback;
+
+    CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt));
+
+    for (i = max_size; i > 0; --i)
+        if (VECTOR(*hist)[i - 1] > 0) {
+            break;
+        }
+    igraph_vector_resize(hist, i);
+    igraph_vector_resize_min(hist);
+
+    graph_free(g);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/* Call function for each clique. */
+
+struct callback_data {
+    igraph_clique_handler_t *handler;
+    void *arg;
+};
+
+static boolean callback_callback(set_t s, graph_t *g, clique_options *opt) {
+    igraph_vector_t *clique;
+    struct callback_data *cd;
+    int i, j;
+
+    CLIQUER_ALLOW_INTERRUPTION();
+
+    cd = (struct callback_data *) opt->user_data;
+
+    clique = (igraph_vector_t *) malloc(sizeof(igraph_vector_t));
+    igraph_vector_init(clique, set_size(s));
+
+    i = -1; j = 0;
+    while ((i = set_return_next(s, i)) >= 0) {
+        VECTOR(*clique)[j++] = i;
+    }
+
+    return (*(cd->handler))(clique, cd->arg);
+}
+
+int igraph_i_cliquer_callback(const igraph_t *graph,
+                              igraph_integer_t min_size, igraph_integer_t max_size,
+                              igraph_clique_handler_t *cliquehandler_fn, void *arg) {
+    graph_t *g;
+    struct callback_data cd;
+    igraph_integer_t vcount = igraph_vcount(graph);
+
+    if (vcount == 0) {
+        return IGRAPH_SUCCESS;
+    }
+
+    if (min_size <= 0) {
+        min_size = 1;
+    }
+    if (max_size <= 0) {
+        max_size = 0;
+    }
+
+    if (max_size > 0 && max_size < min_size) {
+        IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL);
+    }
+
+    igraph_to_cliquer(graph, &g);
+    IGRAPH_FINALLY(graph_free, g);
+
+    cd.handler = cliquehandler_fn;
+    cd.arg = arg;
+    igraph_cliquer_opt.user_data = &cd;
+    igraph_cliquer_opt.user_function = &callback_callback;
+
+    CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt));
+
+    graph_free(g);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/* Find weighted cliques in given weight range. */
+
+int igraph_i_weighted_cliques(const igraph_t *graph,
+                              const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res,
+                              igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal) {
+    graph_t *g;
+    igraph_integer_t vcount = igraph_vcount(graph);
+
+    if (vcount == 0) {
+        igraph_vector_ptr_clear(res);
+        return IGRAPH_SUCCESS;
+    }
+
+    if (min_weight != (int) min_weight) {
+        IGRAPH_WARNING("Only integer vertex weights are supported; the minimum weight will be truncated to its integer part");
+        min_weight  = (int) min_weight;
+    }
+
+    if (max_weight != (int) max_weight) {
+        IGRAPH_WARNING("Only integer vertex weights are supported; the maximum weight will be truncated to its integer part");
+        max_weight = (int) max_weight;
+    }
+
+    if (min_weight <= 0) {
+        min_weight = 1;
+    }
+    if (max_weight <= 0) {
+        max_weight = 0;
+    }
+
+    if (max_weight > 0 && max_weight < min_weight) {
+        IGRAPH_ERROR("max_weight must not be smaller than min_weight", IGRAPH_EINVAL);
+    }
+
+    igraph_to_cliquer(graph, &g);
+    IGRAPH_FINALLY(graph_free, g);
+
+    IGRAPH_CHECK(set_weights(vertex_weights, g));
+
+    igraph_vector_ptr_clear(res);
+    igraph_cliquer_opt.user_data = res;
+    igraph_cliquer_opt.user_function = &collect_cliques_callback;
+
+    IGRAPH_FINALLY(free_clique_list, res);
+    CLIQUER_INTERRUPTABLE(clique_find_all(g, min_weight, max_weight, maximal, &igraph_cliquer_opt));
+    IGRAPH_FINALLY_CLEAN(1);
+
+    graph_free(g);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/* Find largest weighted cliques. */
+
+int igraph_i_largest_weighted_cliques(const igraph_t *graph,
+                                      const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res) {
+    graph_t *g;
+    igraph_integer_t vcount = igraph_vcount(graph);
+
+    if (vcount == 0) {
+        igraph_vector_ptr_clear(res);
+        return IGRAPH_SUCCESS;
+    }
+
+    igraph_to_cliquer(graph, &g);
+    IGRAPH_FINALLY(graph_free, g);
+
+    IGRAPH_CHECK(set_weights(vertex_weights, g));
+
+    igraph_vector_ptr_clear(res);
+    igraph_cliquer_opt.user_data = res;
+    igraph_cliquer_opt.user_function = &collect_cliques_callback;
+
+    IGRAPH_FINALLY(free_clique_list, res);
+    CLIQUER_INTERRUPTABLE(clique_find_all(g, 0, 0, FALSE, &igraph_cliquer_opt));
+    IGRAPH_FINALLY_CLEAN(1);
+
+    graph_free(g);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/* Find weight of largest weight clique. */
+
+int igraph_i_weighted_clique_number(const igraph_t *graph,
+                                    const igraph_vector_t *vertex_weights, igraph_real_t *res) {
+    graph_t *g;
+    igraph_integer_t vcount = igraph_vcount(graph);
+
+    if (vcount == 0) {
+        *res = 0;
+        return IGRAPH_SUCCESS;
+    }
+
+    igraph_to_cliquer(graph, &g);
+    IGRAPH_FINALLY(graph_free, g);
+
+    IGRAPH_CHECK(set_weights(vertex_weights, g));
+
+    igraph_cliquer_opt.user_function = NULL;
+
+    /* we are not using a callback function, thus this is not interruptable */
+    *res = clique_max_weight(g, &igraph_cliquer_opt);
+
+    graph_free(g);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/igraph_error.c b/igraph/src/igraph_error.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_error.c
@@ -0,0 +1,290 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include "igraph_error.h"
+#include "igraph_types.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdarg.h>
+
+static IGRAPH_THREAD_LOCAL igraph_error_handler_t *igraph_i_error_handler = 0;
+static IGRAPH_THREAD_LOCAL char igraph_i_errormsg_buffer[500];
+static IGRAPH_THREAD_LOCAL char igraph_i_warningmsg_buffer[500];
+
+/* Error strings corresponding to each igraph_error_type_t enum value. */
+static const char *igraph_i_error_strings[] = {
+    /*  0 */ "No error",
+    /*  1 */ "Failed",
+    /*  2 */ "Out of memory",
+    /*  3 */ "Parse error",
+    /*  4 */ "Invalid value",
+    /*  5 */ "Already exists",
+    /*  6 */ "Invalid edge vector",
+    /*  7 */ "Invalid vertex id",
+    /*  8 */ "Non-square matrix",
+    /*  9 */ "Invalid mode",
+    /* 10 */ "File operation error",
+    /* 11 */ "Unfold infinite iterator",
+    /* 12 */ "Unimplemented function call",
+    /* 13 */ "Interrupted",
+    /* 14 */ "Numeric procedure did not converge",
+    /* 15 */ "Matrix-vector product failed",
+    /* 16 */ "N must be positive",
+    /* 17 */ "NEV must be positive",
+    /* 18 */ "NCV must be greater than NEV and less than or equal to N "
+    "(and for the non-symmetric solver NCV-NEV >=2 must also hold)",
+    /* 19 */ "Maximum number of iterations should be positive",
+    /* 20 */ "Invalid WHICH parameter",
+    /* 21 */ "Invalid BMAT parameter",
+    /* 22 */ "WORKL is too small",
+    /* 23 */ "LAPACK error in tridiagonal eigenvalue calculation",
+    /* 24 */ "Starting vector is zero",
+    /* 25 */ "MODE is invalid",
+    /* 26 */ "MODE and BMAT are not compatible",
+    /* 27 */ "ISHIFT must be 0 or 1",
+    /* 28 */ "NEV and WHICH='BE' are incompatible",
+    /* 29 */ "Could not build an Arnoldi factorization",
+    /* 30 */ "No eigenvalues to sufficient accuracy",
+    /* 31 */ "HOWMNY is invalid",
+    /* 32 */ "HOWMNY='S' is not implemented",
+    /* 33 */ "Different number of converged Ritz values",
+    /* 34 */ "Error from calculation of a real Schur form",
+    /* 35 */ "LAPACK (dtrevc) error for calculating eigenvectors",
+    /* 36 */ "Unknown ARPACK error",
+    /* 37 */ "Negative loop detected while calculating shortest paths",
+    /* 38 */ "Internal error, likely a bug in igraph",
+    /* 39 */ "Maximum number of iterations reached",
+    /* 40 */ "No shifts could be applied during a cycle of the "
+    "Implicitly restarted Arnoldi iteration. One possibility "
+    "is to increase the size of NCV relative to NEV",
+    /* 41 */ "The Schur form computed by LAPACK routine dlahqr "
+    "could not be reordered by LAPACK routine dtrsen.",
+    /* 42 */ "Big integer division by zero",
+    /* 43 */ "GLPK Error, GLP_EBOUND",
+    /* 44 */ "GLPK Error, GLP_EROOT",
+    /* 45 */ "GLPK Error, GLP_ENOPFS",
+    /* 46 */ "GLPK Error, GLP_ENODFS",
+    /* 47 */ "GLPK Error, GLP_EFAIL",
+    /* 48 */ "GLPK Error, GLP_EMIPGAP",
+    /* 49 */ "GLPK Error, GLP_ETMLIM",
+    /* 50 */ "GLPK Error, GLP_STOP",
+    /* 51 */ "Internal attribute handler error",
+    /* 52 */ "Unimplemented attribute combination for this type",
+    /* 53 */ "LAPACK call resulted an error",
+    /* 54 */ "Internal DrL error",
+    /* 55 */ "Integer or double overflow",
+    /* 56 */ "Internal GPLK error",
+    /* 57 */ "CPU time exceeded",
+    /* 58 */ "Integer or double underflow",
+    /* 59 */ "Random walk got stuck",
+    /* 60 */ "Search stopped; this error should never be visible to the user, "
+    "please report this error along with the steps to reproduce it."
+};
+
+const char* igraph_strerror(const int igraph_errno) {
+    if (igraph_errno < 0 || igraph_errno >= sizeof(igraph_i_error_strings) / sizeof(char *)) {
+        return "Invalid error code; no error string available.";
+    }
+    return igraph_i_error_strings[igraph_errno];
+}
+
+int igraph_error(const char *reason, const char *file, int line,
+                 int igraph_errno) {
+
+    if (igraph_i_error_handler) {
+        igraph_i_error_handler(reason, file, line, igraph_errno);
+#ifndef USING_R
+    }  else {
+        igraph_error_handler_abort(reason, file, line, igraph_errno);
+#endif
+    }
+    return igraph_errno;
+}
+
+int igraph_errorf(const char *reason, const char *file, int line,
+                  int igraph_errno, ...) {
+    va_list ap;
+    va_start(ap, igraph_errno);
+    vsnprintf(igraph_i_errormsg_buffer,
+              sizeof(igraph_i_errormsg_buffer) / sizeof(char), reason, ap);
+    return igraph_error(igraph_i_errormsg_buffer, file, line, igraph_errno);
+}
+
+int igraph_errorvf(const char *reason, const char *file, int line,
+                   int igraph_errno, va_list ap) {
+    vsnprintf(igraph_i_errormsg_buffer,
+              sizeof(igraph_i_errormsg_buffer) / sizeof(char), reason, ap);
+    return igraph_error(igraph_i_errormsg_buffer, file, line, igraph_errno);
+}
+
+#ifndef USING_R
+void igraph_error_handler_abort (const char *reason, const char *file,
+                                 int line, int igraph_errno) {
+    fprintf(stderr, "Error at %s:%i :%s, %s\n", file, line, reason,
+            igraph_strerror(igraph_errno));
+    abort();
+}
+#endif
+
+void igraph_error_handler_ignore (const char *reason, const char *file,
+                                  int line, int igraph_errno) {
+    IGRAPH_UNUSED(reason);
+    IGRAPH_UNUSED(file);
+    IGRAPH_UNUSED(line);
+    IGRAPH_UNUSED(igraph_errno);
+
+    IGRAPH_FINALLY_FREE();
+}
+
+#ifndef USING_R
+void igraph_error_handler_printignore (const char *reason, const char *file,
+                                       int line, int igraph_errno) {
+    IGRAPH_FINALLY_FREE();
+    fprintf(stderr, "Error at %s:%i :%s, %s\n", file, line, reason,
+            igraph_strerror(igraph_errno));
+}
+#endif
+
+igraph_error_handler_t *
+igraph_set_error_handler (igraph_error_handler_t * new_handler) {
+    igraph_error_handler_t * previous_handler = igraph_i_error_handler;
+    igraph_i_error_handler = new_handler;
+    return previous_handler;
+}
+
+IGRAPH_THREAD_LOCAL struct igraph_i_protectedPtr igraph_i_finally_stack[100];
+
+/*
+ * Adds another element to the free list
+ */
+
+void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr) {
+    int no = igraph_i_finally_stack[0].all;
+    assert (no < 100);
+    assert (no >= 0);
+    igraph_i_finally_stack[no].ptr = ptr;
+    igraph_i_finally_stack[no].func = func;
+    igraph_i_finally_stack[0].all ++;
+    /* printf("--> Finally stack contains now %d elements\n", igraph_i_finally_stack[0].all); */
+}
+
+void IGRAPH_FINALLY_CLEAN(int minus) {
+    igraph_i_finally_stack[0].all -= minus;
+    if (igraph_i_finally_stack[0].all < 0) {
+        /* fprintf(stderr, "corrupt finally stack, popping %d elements when only %d left\n", minus, igraph_i_finally_stack[0].all+minus); */
+        igraph_i_finally_stack[0].all = 0;
+    }
+    /* printf("<-- Finally stack contains now %d elements\n", igraph_i_finally_stack[0].all); */
+}
+
+void IGRAPH_FINALLY_FREE(void) {
+    int p;
+    /*   printf("[X] Finally stack will be cleaned (contained %d elements)\n", igraph_i_finally_stack[0].all);  */
+    for (p = igraph_i_finally_stack[0].all - 1; p >= 0; p--) {
+        igraph_i_finally_stack[p].func(igraph_i_finally_stack[p].ptr);
+    }
+    igraph_i_finally_stack[0].all = 0;
+}
+
+int IGRAPH_FINALLY_STACK_SIZE(void) {
+    return igraph_i_finally_stack[0].all;
+}
+
+static IGRAPH_THREAD_LOCAL igraph_warning_handler_t *igraph_i_warning_handler = 0;
+
+/**
+ * \function igraph_warning_handler_ignore
+ * Ignore all warnings
+ *
+ * This warning handler function simply ignores all warnings.
+ * \param reason Textual description of the warning.
+ * \param file The source file in which the warning was noticed.
+ * \param line The number of line in the source file which triggered the
+ *         warning..
+ * \param igraph_errno Warnings could have potentially error codes as well,
+ *        but this is currently not used in igraph.
+ */
+
+void igraph_warning_handler_ignore (const char *reason, const char *file,
+                                    int line, int igraph_errno) {
+    IGRAPH_UNUSED(reason);
+    IGRAPH_UNUSED(file);
+    IGRAPH_UNUSED(line);
+    IGRAPH_UNUSED(igraph_errno);
+}
+
+#ifndef USING_R
+
+/**
+ * \function igraph_warning_handler_print
+ * Print all warning to the standard error
+ *
+ * This warning handler function simply prints all warnings to the
+ * standard error.
+ * \param reason Textual description of the warning.
+ * \param file The source file in which the warning was noticed.
+ * \param line The number of line in the source file which triggered the
+ *         warning..
+ * \param igraph_errno Warnings could have potentially error codes as well,
+ *        but this is currently not used in igraph.
+ */
+
+void igraph_warning_handler_print (const char *reason, const char *file,
+                                   int line, int igraph_errno) {
+    IGRAPH_UNUSED(igraph_errno);
+    fprintf(stderr, "Warning: %s in file %s, line %i\n", reason, file, line);
+}
+#endif
+
+int igraph_warning(const char *reason, const char *file, int line,
+                   int igraph_errno) {
+
+    if (igraph_i_warning_handler) {
+        igraph_i_warning_handler(reason, file, line, igraph_errno);
+#ifndef USING_R
+    }  else {
+        igraph_warning_handler_print(reason, file, line, igraph_errno);
+#endif
+    }
+    return igraph_errno;
+}
+
+int igraph_warningf(const char *reason, const char *file, int line,
+                    int igraph_errno, ...) {
+    va_list ap;
+    va_start(ap, igraph_errno);
+    vsnprintf(igraph_i_warningmsg_buffer,
+              sizeof(igraph_i_warningmsg_buffer) / sizeof(char), reason, ap);
+    return igraph_warning(igraph_i_warningmsg_buffer, file, line,
+                          igraph_errno);
+}
+
+igraph_warning_handler_t *
+igraph_set_warning_handler (igraph_warning_handler_t * new_handler) {
+    igraph_warning_handler_t * previous_handler = igraph_i_warning_handler;
+    igraph_i_warning_handler = new_handler;
+    return previous_handler;
+}
diff --git a/igraph/src/igraph_estack.c b/igraph/src/igraph_estack.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_estack.c
@@ -0,0 +1,67 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_estack.h"
+
+int igraph_estack_init(igraph_estack_t *s, long int setsize,
+                       long int stacksize) {
+    IGRAPH_CHECK(igraph_vector_bool_init(&s->isin, setsize));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &s->isin);
+    IGRAPH_CHECK(igraph_stack_long_init(&s->stack, stacksize));
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+void igraph_estack_destroy(igraph_estack_t *s) {
+    igraph_stack_long_destroy(&s->stack);
+    igraph_vector_bool_destroy(&s->isin);
+}
+
+int igraph_estack_push(igraph_estack_t *s,  long int elem) {
+    if ( !VECTOR(s->isin)[elem] ) {
+        IGRAPH_CHECK(igraph_stack_long_push(&s->stack, elem));
+        VECTOR(s->isin)[elem] = 1;
+    }
+    return 0;
+}
+
+long int igraph_estack_pop(igraph_estack_t *s) {
+    long int elem = igraph_stack_long_pop(&s->stack);
+    VECTOR(s->isin)[elem] = 0;
+    return elem;
+}
+
+igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s,
+                                      long int elem) {
+    return VECTOR(s->isin)[elem];
+}
+
+long int igraph_estack_size(const igraph_estack_t *s) {
+    return igraph_stack_long_size(&s->stack);
+}
+
+#ifndef USING_R
+int igraph_estack_print(const igraph_estack_t *s) {
+    return igraph_stack_long_print(&s->stack);
+}
+#endif
diff --git a/igraph/src/igraph_fixed_vectorlist.c b/igraph/src/igraph_fixed_vectorlist.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_fixed_vectorlist.c
@@ -0,0 +1,80 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types_internal.h"
+#include "igraph_memory.h"
+
+void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l) {
+    long int i, n = igraph_vector_ptr_size(&l->v);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *v = VECTOR(l->v)[i];
+        if (v) {
+            igraph_vector_destroy(v);
+        }
+    }
+    igraph_vector_ptr_destroy(&l->v);
+    igraph_free(l->vecs);
+}
+
+int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l,
+                                    const igraph_vector_t *from,
+                                    long int size) {
+
+    igraph_vector_t sizes;
+    long int i, no = igraph_vector_size(from);
+
+    l->vecs = igraph_Calloc(size, igraph_vector_t);
+    if (!l->vecs) {
+        IGRAPH_ERROR("Cannot merge attributes for simplify",
+                     IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, l->vecs);
+    IGRAPH_CHECK(igraph_vector_ptr_init(&l->v, size));
+    IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &l->v);
+    IGRAPH_VECTOR_INIT_FINALLY(&sizes, size);
+
+    for (i = 0; i < no; i++) {
+        long int to = (long int) VECTOR(*from)[i];
+        if (to >= 0) {
+            VECTOR(sizes)[to] += 1;
+        }
+    }
+    for (i = 0; i < size; i++) {
+        igraph_vector_t *v = &(l->vecs[i]);
+        IGRAPH_CHECK(igraph_vector_init(v, (long int) VECTOR(sizes)[i]));
+        igraph_vector_clear(v);
+        VECTOR(l->v)[i] = v;
+    }
+    for (i = 0; i < no; i++) {
+        long int to = (long int) VECTOR(*from)[i];
+        if (to >= 0) {
+            igraph_vector_t *v = &(l->vecs[to]);
+            igraph_vector_push_back(v, i);
+        }
+    }
+
+    igraph_vector_destroy(&sizes);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
diff --git a/igraph/src/igraph_grid.c b/igraph/src/igraph_grid.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_grid.c
@@ -0,0 +1,543 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph R package.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_types_internal.h"
+#include "igraph_memory.h"
+#include "config.h"
+
+#include <math.h>
+
+/* internal function */
+
+int igraph_2dgrid_which(igraph_2dgrid_t *grid, igraph_real_t xc, igraph_real_t yc,
+                        long int *x, long int *y) {
+
+    if (xc <= grid->minx) {
+        *x = 0;
+    } else if (xc >= grid->maxx) {
+        *x = grid->stepsx - 1;
+    } else {
+        *x = (long int) floor((xc - (grid->minx)) / (grid->deltax));
+    }
+
+    if (yc <= grid->miny) {
+        *y = 0;
+    } else if (yc >= grid->maxy) {
+        *y = grid->stepsy - 1;
+    } else {
+        *y = (long int) floor((yc - (grid->miny)) / (grid->deltay));
+    }
+
+    return 0;
+}
+
+int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords,
+                       igraph_real_t minx, igraph_real_t maxx, igraph_real_t deltax,
+                       igraph_real_t miny, igraph_real_t maxy, igraph_real_t deltay) {
+    long int i;
+
+    grid->coords = coords;
+    grid->minx = minx;
+    grid->maxx = maxx;
+    grid->deltax = deltax;
+    grid->miny = miny;
+    grid->maxy = maxy;
+    grid->deltay = deltay;
+
+    grid->stepsx = (long int) ceil((maxx - minx) / deltax);
+    grid->stepsy = (long int) ceil((maxy - miny) / deltay);
+
+    IGRAPH_CHECK(igraph_matrix_init(&grid->startidx,
+                                    grid->stepsx, grid->stepsy));
+    IGRAPH_FINALLY(igraph_matrix_destroy, &grid->startidx);
+    IGRAPH_VECTOR_INIT_FINALLY(&grid->next, igraph_matrix_nrow(coords));
+    IGRAPH_VECTOR_INIT_FINALLY(&grid->prev, igraph_matrix_nrow(coords));
+
+    for (i = 0; i < igraph_vector_size(&grid->next); i++) {
+        VECTOR(grid->next)[i] = -1;
+    }
+
+    grid->massx = 0;
+    grid->massy = 0;
+    grid->vertices = 0;
+
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+void igraph_2dgrid_destroy(igraph_2dgrid_t *grid) {
+    igraph_matrix_destroy(&grid->startidx);
+    igraph_vector_destroy(&grid->next);
+    igraph_vector_destroy(&grid->prev);
+}
+
+void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem,
+                       igraph_real_t xc, igraph_real_t yc) {
+    long int x, y;
+    long int first;
+
+    MATRIX(*grid->coords, elem, 0) = xc;
+    MATRIX(*grid->coords, elem, 1) = yc;
+
+    /* add to cell */
+    igraph_2dgrid_which(grid, xc, yc, &x, &y);
+    first = (long int) MATRIX(grid->startidx, x, y);
+    VECTOR(grid->prev)[elem] = 0;
+    VECTOR(grid->next)[elem] = first;
+    if (first != 0) {
+        VECTOR(grid->prev)[first - 1] = elem + 1;
+    }
+    MATRIX(grid->startidx, x, y) = elem + 1;
+
+    grid->massx += xc;
+    grid->massy += yc;
+    grid->vertices += 1;
+}
+
+void igraph_2dgrid_add2(igraph_2dgrid_t *grid, long int elem) {
+    long int x, y;
+    long int first;
+    igraph_real_t xc, yc;
+
+    xc = MATRIX(*grid->coords, elem, 0);
+    yc = MATRIX(*grid->coords, elem, 1);
+
+    /* add to cell */
+    igraph_2dgrid_which(grid, xc, yc, &x, &y);
+    first = (long int) MATRIX(grid->startidx, x, y);
+    VECTOR(grid->prev)[elem] = 0;
+    VECTOR(grid->next)[elem] = first;
+    if (first != 0) {
+        VECTOR(grid->prev)[first - 1] = elem + 1;
+    }
+    MATRIX(grid->startidx, x, y) = elem + 1;
+
+    grid->massx += xc;
+    grid->massy += yc;
+    grid->vertices += 1;
+}
+
+void igraph_2dgrid_move(igraph_2dgrid_t *grid, long int elem,
+                        igraph_real_t xc, igraph_real_t yc) {
+    long int oldx, oldy;
+    long int newx, newy;
+    igraph_real_t oldxc = MATRIX(*grid->coords, elem, 0);
+    igraph_real_t oldyc = MATRIX(*grid->coords, elem, 1);
+    long int first;
+
+    xc = oldxc + xc; yc = oldyc + yc;
+
+    igraph_2dgrid_which(grid, oldxc, oldyc, &oldx, &oldy);
+    igraph_2dgrid_which(grid, xc, yc, &newx, &newy);
+    if (oldx != newx || oldy != newy) {
+        /* remove from this cell */
+        if (VECTOR(grid->prev)[elem] != 0) {
+            VECTOR(grid->next) [ (long int) VECTOR(grid->prev)[elem] - 1 ] =
+                VECTOR(grid->next)[elem];
+        } else {
+            MATRIX(grid->startidx, oldx, oldy) = VECTOR(grid->next)[elem];
+        }
+        if (VECTOR(grid->next)[elem] != 0) {
+            VECTOR(grid->prev)[ (long int) VECTOR(grid->next)[elem] - 1 ] =
+                VECTOR(grid->prev)[elem];
+        }
+
+        /* add to this cell */
+        first = (long int) MATRIX(grid->startidx, newx, newy);
+        VECTOR(grid->prev)[elem] = 0;
+        VECTOR(grid->next)[elem] = first;
+        if (first != 0) {
+            VECTOR(grid->prev)[first - 1] = elem + 1;
+        }
+        MATRIX(grid->startidx, newx, newy) = elem + 1;
+    }
+
+    grid->massx += -oldxc + xc;
+    grid->massy += -oldyc + yc;
+
+    MATRIX(*grid->coords, elem, 0) = xc;
+    MATRIX(*grid->coords, elem, 1) = yc;
+
+}
+
+void igraph_2dgrid_getcenter(const igraph_2dgrid_t *grid,
+                             igraph_real_t *massx, igraph_real_t *massy) {
+    *massx = (grid->massx) / (grid->vertices);
+    *massy = (grid->massy) / (grid->vertices);
+}
+
+igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, long int elem) {
+    return VECTOR(grid->next)[elem] != -1;
+}
+
+igraph_real_t igraph_2dgrid_dist(const igraph_2dgrid_t *grid,
+                                 long int e1, long int e2) {
+    igraph_real_t x = MATRIX(*grid->coords, e1, 0) - MATRIX(*grid->coords, e2, 0);
+    igraph_real_t y = MATRIX(*grid->coords, e1, 1) - MATRIX(*grid->coords, e2, 1);
+
+    return sqrt(x * x + y * y);
+}
+
+igraph_real_t igraph_2dgrid_dist2(const igraph_2dgrid_t *grid,
+                                  long int e1, long int e2) {
+    igraph_real_t x = MATRIX(*grid->coords, e1, 0) - MATRIX(*grid->coords, e2, 0);
+    igraph_real_t y = MATRIX(*grid->coords, e1, 1) - MATRIX(*grid->coords, e2, 1);
+
+    return x * x + y * y;
+}
+
+int igraph_i_2dgrid_addvertices(igraph_2dgrid_t *grid, igraph_vector_t *eids,
+                                igraph_integer_t vid, igraph_real_t r,
+                                long int x, long int y) {
+    long int act;
+    igraph_real_t *v = VECTOR(grid->next);
+
+    r = r * r;
+    act = (long int) MATRIX(grid->startidx, x, y);
+    while (act != 0) {
+        if (igraph_2dgrid_dist2(grid, vid, act - 1) < r) {
+            IGRAPH_CHECK(igraph_vector_push_back(eids, act - 1));
+        }
+        act = (long int) v[act - 1];
+    }
+    return 0;
+}
+
+int igraph_2dgrid_neighbors(igraph_2dgrid_t *grid, igraph_vector_t *eids,
+                            igraph_integer_t vid, igraph_real_t r) {
+    igraph_real_t xc = MATRIX(*grid->coords, (long int)vid, 0);
+    igraph_real_t yc = MATRIX(*grid->coords, (long int)vid, 1);
+    long int x, y;
+    igraph_vector_clear(eids);
+
+    igraph_2dgrid_which(grid, xc, yc, &x, &y);
+
+    /* this cell */
+    igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y);
+
+    /* left */
+    if (x != 0) {
+        igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y);
+    }
+    /* right */
+    if (x != grid->stepsx - 1) {
+        igraph_i_2dgrid_addvertices(grid, eids, vid, r, x + 1, y);
+    }
+    /* up */
+    if (y != 0) {
+        igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y - 1);
+    }
+    /* down */
+    if (y != grid->stepsy - 1) {
+        igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y + 1);
+    }
+    /* up & left */
+    if (x != 0 && y != 0) {
+        igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y - 1);
+    }
+    /* up & right */
+    if (x != grid->stepsx - 1 && y != 0) {
+        igraph_i_2dgrid_addvertices(grid, eids, vid, r, x + 1, y - 1);
+    }
+    /* down & left */
+    if (x != 0 && y != grid->stepsy - 1) {
+        igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y + 1);
+    }
+    /* down & right */
+    if (x != grid->stepsx - 1 && y != grid->stepsy - 1) {
+        igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y + 1);
+    }
+
+    return 0;
+}
+
+void igraph_2dgrid_reset(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it) {
+    /* Search for the first cell containing a vertex */
+    it->x = 0; it->y = 0; it->vid = (long int) MATRIX(grid->startidx, 0, 0);
+    while ( it->vid == 0 && (it->x < grid->stepsx - 1 || it->y < grid->stepsy - 1)) {
+        it->x += 1;
+        if (it->x == grid->stepsx) {
+            it->x = 0; it->y += 1;
+        }
+        it->vid = (long int) MATRIX(grid->startidx, it->x, it->y);
+    }
+}
+
+igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid,
+                                    igraph_2dgrid_iterator_t *it) {
+    long int ret = it->vid;
+
+    if (ret == 0) {
+        return 0;
+    }
+
+    /* First neighbor */
+    it->ncells = -1;
+    if (it->x != grid->stepsx - 1) {
+        it->ncells += 1;
+        it->nx[it->ncells] = it->x + 1;
+        it->ny[it->ncells] = it->y;
+    }
+    if (it->y != grid->stepsy - 1) {
+        it->ncells += 1;
+        it->nx[it->ncells] = it->x;
+        it->ny[it->ncells] = it->y + 1;
+    }
+    if (it->ncells == 1) {
+        it->ncells += 1;
+        it->nx[it->ncells] = it->x + 1;
+        it->ny[it->ncells] = it->y + 1;
+    }
+    it->ncells += 1;
+    it->nx[it->ncells] = it->x;
+    it->ny[it->ncells] = it->y;
+
+    it->nei = (long int) VECTOR(grid->next) [ ret - 1 ];
+    while (it->ncells > 0 && it->nei == 0 ) {
+        it->ncells -= 1;
+        it->nei = (long int) MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]);
+    }
+
+    /* Next vertex */
+    it->vid = (long int) VECTOR(grid->next)[ it->vid - 1 ];
+    while ( (it->x < grid->stepsx - 1 || it->y < grid->stepsy - 1) &&
+            it->vid == 0) {
+        it->x += 1;
+        if (it->x == grid->stepsx) {
+            it->x = 0; it->y += 1;
+        }
+        it->vid = (long int) MATRIX(grid->startidx, it->x, it->y);
+    }
+
+    return (igraph_integer_t) ret;
+}
+
+igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid,
+                                        igraph_2dgrid_iterator_t *it) {
+    long int ret = it->nei;
+
+    if (it->nei != 0) {
+        it->nei = (long int) VECTOR(grid->next) [ ret - 1 ];
+    }
+    while (it->ncells > 0 && it->nei == 0 ) {
+        it->ncells -= 1;
+        it->nei = (long int) MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]);
+    }
+
+    return (igraph_integer_t) ret;
+}
+
+/*-----------------------------------------------------------------------*/
+
+int igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid,
+                                    igraph_real_t xc, igraph_real_t yc,
+                                    long int *x, long int *y) {
+    if (xc <= grid->minx) {
+        *x = 0;
+    } else if (xc >= grid->maxx) {
+        *x = grid->stepsx - 1;
+    } else {
+        *x = (long int) floor((xc - (grid->minx)) / (grid->deltax));
+    }
+
+    if (yc <= grid->miny) {
+        *y = 0;
+    } else if (yc >= grid->maxy) {
+        *y = grid->stepsy - 1;
+    } else {
+        *y = (long int) floor((yc - (grid->miny)) / (grid->deltay));
+    }
+
+    return 0;
+}
+
+int igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid,
+                                   igraph_real_t minx, igraph_real_t maxx, long int stepsx,
+                                   igraph_real_t miny, igraph_real_t maxy, long int stepsy) {
+    grid->minx = minx;
+    grid->maxx = maxx;
+    grid->stepsx = stepsx;
+    grid->deltax = (maxx - minx) / stepsx;
+    grid->miny = miny;
+    grid->maxy = maxy;
+    grid->stepsy = stepsy;
+    grid->deltay = (maxy - miny) / stepsy;
+
+    grid->data = igraph_Calloc(stepsx * stepsy, long int);
+    if (grid->data == 0) {
+        IGRAPH_ERROR("Cannot create grid", IGRAPH_ENOMEM);
+    }
+    return 0;
+}
+
+void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid) {
+    igraph_Free(grid->data);
+}
+
+#define MAT(i,j) (grid->data[(grid->stepsy)*(j)+(i)])
+#define DIST2(x2,y2) (sqrt(pow(x-(x2),2)+pow(y-(y2), 2)))
+
+int igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid,
+                                       igraph_real_t x, igraph_real_t y, igraph_real_t r,
+                                       long int id) {
+    long int cx, cy;
+    long int i, j;
+
+    igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy);
+
+    MAT(cx, cy) = id + 1;
+
+#define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \
+                         grid->miny+(cy+(j))*grid->deltay))
+
+    for (i = 0; cx + i < grid->stepsx && DIST(i, 0) < r; i++) {
+        for (j = 0; cy + j < grid->stepsy && DIST(i, j) < r; j++) {
+            MAT(cx + i, cy + j) = id + 1;
+        }
+    }
+
+#undef DIST
+#define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \
+                         grid->miny+(cy-(j)+1)*grid->deltay))
+
+    for (i = 0; cx + i < grid->stepsx && DIST(i, 0) < r; i++) {
+        for (j = 1; cy - j > 0 && DIST(i, j) < r; j++) {
+            MAT(cx + i, cy - j) = id + 1;
+        }
+    }
+
+#undef DIST
+#define DIST(i,j) (DIST2(grid->minx+(cx-(i)+1)*grid->deltax, \
+                         grid->miny+(cy+(j))*grid->deltay))
+
+    for (i = 1; cx - i > 0 && DIST(i, 0) < r; i++) {
+        for (j = 0; cy + j < grid->stepsy && DIST(i, j) < r; j++) {
+            MAT(cx - i, cy + j) = id + 1;
+        }
+    }
+
+#undef DIST
+#define DIST(i,j) (DIST2(grid->minx+(cx-(i)+1)*grid->deltax, \
+                         grid->miny+(cy-(j)+1)*grid->deltay))
+
+    for (i = 1; cx - i > 0 && DIST(i, 0) < r; i++) {
+        for (j = 1; cy - j > 0 && DIST(i, j) < r; j++) {
+            MAT(cx - i, cy - j) = id + 1;
+        }
+    }
+
+#undef DIST
+#undef DIST2
+
+    return 0;
+}
+
+long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid,
+                                       igraph_real_t x, igraph_real_t y) {
+    long int cx, cy;
+    long int res;
+
+    if (x <= grid->minx || x >= grid->maxx ||
+        y <= grid->miny || y >= grid->maxy) {
+        res = -1;
+    } else {
+        igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy);
+        res = MAT(cx, cy) - 1;
+    }
+
+    return res;
+}
+
+#define DIST2(x2,y2) (sqrt(pow(x-(x2),2)+pow(y-(y2), 2)))
+
+long int igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *grid,
+        igraph_real_t x, igraph_real_t y, igraph_real_t r) {
+    long int cx, cy;
+    long int i, j;
+    long int ret;
+
+    if (x - r <= grid->minx || x + r >= grid->maxx ||
+        y - r <= grid->miny || y + r >= grid->maxy) {
+        ret = -1;
+    } else {
+        igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy);
+
+        ret = MAT(cx, cy) - 1;
+
+#define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \
+                         grid->miny+(cy+(j))*grid->deltay))
+
+        for (i = 0; ret < 0 && cx + i < grid->stepsx && DIST(i, 0) < r; i++) {
+            for (j = 0; ret < 0 && cy + j < grid->stepsy && DIST(i, j) < r; j++) {
+                ret = MAT(cx + i, cy + j) - 1;
+            }
+        }
+
+#undef DIST
+#define DIST(i,j) (DIST2(grid->minx+(cx+(i))*grid->deltax, \
+                         grid->miny+(cy-(j)+1)*grid->deltay))
+
+        for (i = 0; ret < 0 && cx + i < grid->stepsx && DIST(i, 0) < r; i++) {
+            for (j = 1; ret < 0 && cy - j > 0 && DIST(i, j) < r; j++) {
+                ret = MAT(cx + i, cy - j) - 1;
+            }
+        }
+
+#undef DIST
+#define DIST(i,j) (DIST2(grid->minx+(cx-(i)+1)*grid->deltax, \
+                         grid->miny+(cy+(j))*grid->deltay))
+
+        for (i = 1; ret < 0 && cx - i > 0 && DIST(i, 0) < r; i++) {
+            for (j = 0; ret < 0 && cy + j < grid->stepsy && DIST(i, j) < r; j++) {
+                ret = MAT(cx - i, cy + j) - 1;
+            }
+        }
+
+#undef DIST
+#define DIST(i,j) (DIST2(grid->minx+(cx-(i)+1)*grid->deltax, \
+                         grid->miny+(cy-(j)+1)*grid->deltay))
+
+        for (i = 1; ret < 0 && cx + i > 0 && DIST(i, 0) < r; i++) {
+            for (j = 1; ret < 0 && cy + i > 0 && DIST(i, j) < r; j++) {
+                ret = MAT(cx - i, cy - j) - 1;
+            }
+        }
+
+#undef DIST
+
+    }
+
+    return ret;
+}
+
+/* int print_grid(igraph_i_layout_mergegrid_t *grid) { */
+/*   long int i,j; */
+
+/*   for (i=0; i<grid->stepsx; i++) { */
+/*     for (j=0; j<grid->stepsy; j++) { */
+/*       printf("%li ", MAT(i,j)-1); */
+/*     } */
+/*     printf("\n"); */
+/*   } */
+/* } */
diff --git a/igraph/src/igraph_hashtable.c b/igraph/src/igraph_hashtable.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_hashtable.c
@@ -0,0 +1,128 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_types_internal.h"
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "config.h"
+#include <string.h>
+
+int igraph_hashtable_init(igraph_hashtable_t *ht) {
+    IGRAPH_CHECK(igraph_trie_init(&ht->keys, 1));
+    IGRAPH_FINALLY(igraph_trie_destroy, &ht->keys);
+    IGRAPH_CHECK(igraph_strvector_init(&ht->elements, 0));
+    IGRAPH_FINALLY(igraph_trie_destroy, &ht->elements);
+    IGRAPH_CHECK(igraph_strvector_init(&ht->defaults, 0));
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+void igraph_hashtable_destroy(igraph_hashtable_t *ht) {
+    igraph_trie_destroy(&ht->keys);
+    igraph_strvector_destroy(&ht->elements);
+    igraph_strvector_destroy(&ht->defaults);
+}
+
+/* Note: may leave the hash table in an inconsistent state if a new
+   element is added, but this is not a big problem, since while the
+   defaults, or the defaults plus the elements may contain more elements
+   than the keys trie, but the data is always retrieved based on the trie
+*/
+
+int igraph_hashtable_addset(igraph_hashtable_t *ht,
+                            const char *key, const char *def,
+                            const char *elem) {
+    long int size = igraph_trie_size(&ht->keys);
+    long int newid;
+    IGRAPH_CHECK(igraph_trie_get(&ht->keys, key, &newid));
+
+    if (newid == size) {
+        /* this is a new element */
+        IGRAPH_CHECK(igraph_strvector_resize(&ht->defaults, newid + 1));
+        IGRAPH_CHECK(igraph_strvector_resize(&ht->elements, newid + 1));
+        IGRAPH_CHECK(igraph_strvector_set(&ht->defaults, newid, def));
+        IGRAPH_CHECK(igraph_strvector_set(&ht->elements, newid, elem));
+    } else {
+        /* set an already existing element */
+        IGRAPH_CHECK(igraph_strvector_set(&ht->elements, newid, elem));
+    }
+
+    return 0;
+}
+
+/* Previous comment also applies here */
+
+int igraph_hashtable_addset2(igraph_hashtable_t *ht,
+                             const char *key, const char *def,
+                             const char *elem, int elemlen) {
+    long int size = igraph_trie_size(&ht->keys);
+    long int newid;
+    char *tmp;
+
+    IGRAPH_CHECK(igraph_trie_get(&ht->keys, key, &newid));
+
+    tmp = igraph_Calloc(elemlen + 1, char);
+    if (tmp == 0) {
+        IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, tmp);
+    strncpy(tmp, elem, elemlen);
+    tmp[elemlen] = '\0';
+
+    if (newid == size) {
+        IGRAPH_CHECK(igraph_strvector_resize(&ht->defaults, newid + 1));
+        IGRAPH_CHECK(igraph_strvector_resize(&ht->elements, newid + 1));
+        IGRAPH_CHECK(igraph_strvector_set(&ht->defaults, newid, def));
+        IGRAPH_CHECK(igraph_strvector_set(&ht->elements, newid, tmp));
+    } else {
+        IGRAPH_CHECK(igraph_strvector_set(&ht->elements, newid, tmp));
+    }
+
+    igraph_Free(tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_hashtable_get(igraph_hashtable_t *ht,
+                         const char *key, char **elem) {
+    long int newid;
+    IGRAPH_CHECK(igraph_trie_get(&ht->keys, key, &newid));
+
+    igraph_strvector_get(&ht->elements, newid, elem);
+
+    return 0;
+}
+
+int igraph_hashtable_reset(igraph_hashtable_t *ht) {
+    igraph_strvector_destroy(&ht->elements);
+    IGRAPH_CHECK(igraph_strvector_copy(&ht->elements, &ht->defaults));
+    return 0;
+}
+
+int igraph_hashtable_getkeys(igraph_hashtable_t *ht,
+                             const igraph_strvector_t **sv) {
+    return igraph_trie_getkeys(&ht->keys, sv);
+}
diff --git a/igraph/src/igraph_heap.c b/igraph/src/igraph_heap.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_heap.c
@@ -0,0 +1,64 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_heap.h"
+
+#define BASE_IGRAPH_REAL
+#define HEAP_TYPE_MAX
+#include "igraph_pmt.h"
+#include "heap.pmt"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MAX
+#define HEAP_TYPE_MIN
+#include "igraph_pmt.h"
+#include "heap.pmt"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MIN
+#undef BASE_IGRAPH_REAL
+
+#define BASE_LONG
+#define HEAP_TYPE_MAX
+#include "igraph_pmt.h"
+#include "heap.pmt"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MAX
+#define HEAP_TYPE_MIN
+#include "igraph_pmt.h"
+#include "heap.pmt"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MIN
+#undef BASE_LONG
+
+#define BASE_CHAR
+#define HEAP_TYPE_MAX
+#include "igraph_pmt.h"
+#include "heap.pmt"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MAX
+#define HEAP_TYPE_MIN
+#include "igraph_pmt.h"
+#include "heap.pmt"
+#include "igraph_pmt_off.h"
+#undef HEAP_TYPE_MIN
+#undef BASE_CHAR
diff --git a/igraph/src/igraph_hrg.cc b/igraph/src/igraph_hrg.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_hrg.cc
@@ -0,0 +1,1074 @@
+/* -*- mode: C++ -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_interface.h"
+#include "igraph_community.h"
+#include "igraph_memory.h"
+#include "igraph_constructors.h"
+#include "igraph_attributes.h"
+#include "igraph_foreign.h"
+#include "igraph_hrg.h"
+#include "igraph_random.h"
+
+#include "hrg_dendro.h"
+#include "hrg_graph.h"
+#include "hrg_graph_simp.h"
+
+using namespace fitHRG;
+
+/**
+ * \section hrg_intro Introduction
+ *
+ * <para>A hierarchical random graph is an ensemble of undirected
+ * graphs with \c n vertices. It is defined via a binary tree with \c
+ * n leaf and \c n-1 internal vertices, where the
+ * internal vertices are labeled with probabilities.
+ * The probability that two vertices are connected in the random graph
+ * is given by the probability label at their closest common
+ * ancestor.
+ * </para>
+ *
+ * <para>Please read the following two articles for more about
+ * hierarchical random graphs: A. Clauset, C. Moore, and M.E.J. Newman.
+ * Hierarchical structure and the prediction of missing links in networks.
+ * Nature 453, 98 - 101 (2008); and A. Clauset, C. Moore, and M.E.J. Newman.
+ * Structural Inference of Hierarchies in Networks. In E. M. Airoldi
+ * et al. (Eds.): ICML 2006 Ws, Lecture Notes in Computer Science
+ * 4503, 1-13. Springer-Verlag, Berlin Heidelberg (2007).
+ * </para>
+ *
+ * <para>
+ * igraph contains functions for fitting HRG models to a given network
+ * (\ref igraph_hrg_fit), for generating networks from a given HRG
+ * ensemble (\ref igraph_hrg_game, \ref igraph_hrg_sample), converting
+ * an igraph graph to a HRG and back (\ref igraph_hrg_create, \ref
+ * igraph_hrg_dendrogram), for calculating a consensus tree from a
+ * set of sampled HRGs (\ref igraph_hrg_consensus) and for predicting
+ * missing edges in a network based on its HRG models (\ref
+ * igraph_hrg_predict).
+ * </para>
+ *
+ * <para>The igraph HRG implementation is heavily based on the code
+ * published by Aaron Clauset, at his website,
+ * http://tuvalu.santafe.edu/~aaronc/hierarchy/
+ * </para>
+ */
+
+namespace fitHRG {
+struct pblock {
+    double L;
+    int i;
+    int j;
+};
+}
+
+int markovChainMonteCarlo(dendro *d, unsigned int period,
+                          igraph_hrg_t *hrg) {
+
+    igraph_real_t bestL = d->getLikelihood();
+    double  dL;
+    bool    flag_taken;
+
+    // Because moves in the dendrogram space are chosen (Monte
+    // Carlo) so that we sample dendrograms with probability
+    // proportional to their likelihood, a likelihood-proportional
+    // sampling of the dendrogram models would be equivalent to a
+    // uniform sampling of the walk itself. We would still have to
+    // decide how often to sample the walk (at most once every n
+    // steps is recommended) but for simplicity, the code here
+    // simply runs the MCMC itself. To actually compute something
+    // over the set of sampled dendrogram models (in a Bayesian
+    // model averaging sense), you'll need to code that yourself.
+
+    // do 'period' MCMC moves before doing anything else
+    for (unsigned int i = 0; i < period; i++) {
+
+        // make a MCMC move
+        IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0));
+
+        // get likelihood of this D given G
+        igraph_real_t cl = d->getLikelihood();
+        if (cl > bestL) {
+            // store the current best likelihood
+            bestL = cl;
+            // record the HRG structure
+            d->recordDendrogramStructure(hrg);
+        }
+    }
+    // corrects floating-point errors O(n)
+    d->refreshLikelihood();
+
+    return 0;
+}
+
+int markovChainMonteCarlo2(dendro *d, int num_samples) {
+    bool flag_taken;
+    double dL, ptest = 1.0 / (50.0 * (double)(d->g->numNodes()));
+    int sample_num = 0, t = 1, thresh = 200 * d->g->numNodes();
+
+    // Since we're sampling uniformly at random over the equilibrium
+    // walk, we just need to do a bunch of MCMC moves and let the
+    // sampling happen on its own.
+    while (sample_num < num_samples) {
+        // Make a single MCMC move
+        d->monteCarloMove(dL, flag_taken, 1.0);
+
+        // We sample the dendrogram space once every n MCMC moves (on
+        // average). Depending on the flags on the command line, we sample
+        // different aspects of the dendrograph structure.
+        if (t > thresh && RNG_UNIF01() < ptest) {
+            sample_num++;
+            d->sampleSplitLikelihoods(sample_num);
+        }
+
+        t++;
+
+        // correct floating-point errors O(n)
+        d->refreshLikelihood(); // TODO: less frequently
+    }
+
+    return 0;
+}
+
+int MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) {
+
+    // We want to run the MCMC until we've found equilibrium; we
+    // use the heuristic of the average log-likelihood (which is
+    // exactly the entropy) over X steps being very close to the
+    // average log-likelihood (entropy) over the X steps that
+    // preceded those. In other words, we look for an apparent
+    // local convergence of the entropy measure of the MCMC.
+
+    bool flag_taken;
+    igraph_real_t dL, Likeli;
+    igraph_real_t oldMeanL;
+    igraph_real_t newMeanL = -1e-49;
+
+    while (1) {
+        oldMeanL = newMeanL;
+        newMeanL = 0.0;
+        for (int i = 0; i < 65536; i++) {
+            IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0));
+            Likeli = d->getLikelihood();
+            newMeanL += Likeli;
+        }
+        // corrects floating-point errors O(n)
+        d->refreshLikelihood();
+        if (fabs(newMeanL - oldMeanL) / 65536.0 < 1.0) {
+            break;
+        }
+    }
+
+    // Record the result
+    if (hrg) {
+        d->recordDendrogramStructure(hrg);
+    }
+
+    return 0;
+}
+
+int igraph_i_hrg_getgraph(const igraph_t *igraph,
+                          dendro *d) {
+
+    int no_of_nodes = igraph_vcount(igraph);
+    int no_of_edges = igraph_ecount(igraph);
+    int i;
+
+    // Create graph
+    d->g = new graph(no_of_nodes);
+
+    // Add edges
+    for (i = 0; i < no_of_edges; i++) {
+        int from = IGRAPH_FROM(igraph, i);
+        int to = IGRAPH_TO(igraph, i);
+        if (from == to) {
+            continue;
+        }
+        if (!d->g->doesLinkExist(from, to)) {
+            d->g->addLink(from, to);
+        }
+        if (!d->g->doesLinkExist(to, from)) {
+            d->g->addLink(to, from);
+        }
+    }
+
+    d->buildDendrogram();
+
+    return 0;
+}
+
+int igraph_i_hrg_getsimplegraph(const igraph_t *igraph,
+                                dendro *d, simpleGraph **sg,
+                                int num_bins) {
+
+    int no_of_nodes = igraph_vcount(igraph);
+    int no_of_edges = igraph_ecount(igraph);
+    int i;
+
+    // Create graphs
+    d->g = new graph(no_of_nodes, true);
+    d->g->setAdjacencyHistograms(num_bins);
+    (*sg) = new simpleGraph(no_of_nodes);
+
+    for (i = 0; i < no_of_edges; i++) {
+        int from = IGRAPH_FROM(igraph, i);
+        int to = IGRAPH_TO(igraph, i);
+        if (from == to) {
+            continue;
+        }
+        if (!d->g->doesLinkExist(from, to)) {
+            d->g->addLink(from, to);
+        }
+        if (!d->g->doesLinkExist(to, from)) {
+            d->g->addLink(to, from);
+        }
+        if (!(*sg)->doesLinkExist(from, to)) {
+            (*sg)->addLink(from, to);
+        }
+        if (!(*sg)->doesLinkExist(to, from)) {
+            (*sg)->addLink(to, from);
+        }
+    }
+
+    d->buildDendrogram();
+
+    return 0;
+}
+
+/**
+ * \function igraph_hrg_init
+ * Allocate memory for a HRG.
+ *
+ * This function must be called before passing an \ref igraph_hrg_t to
+ * an igraph function.
+ * \param hrg Pointer to the HRG data structure to initialize.
+ * \param n The number of vertices in the graph that is modeled by
+ *    this HRG. It can be zero, if this is not yet known.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of vertices in the graph.
+ */
+
+int igraph_hrg_init(igraph_hrg_t *hrg, int n) {
+    IGRAPH_VECTOR_INIT_FINALLY(&hrg->left,      n - 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&hrg->right,     n - 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&hrg->prob,      n - 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&hrg->edges,     n - 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&hrg->vertices,  n - 1);
+    IGRAPH_FINALLY_CLEAN(5);
+    return 0;
+}
+
+/**
+ * \function igraph_hrg_destroy
+ * Deallocate memory for an HRG.
+ *
+ * The HRG data structure can be reinitialized again with an \ref
+ * igraph_hrg_destroy call.
+ * \param hrg Pointer to the HRG data structure to deallocate.
+ *
+ * Time complexity: operating system dependent.
+ */
+
+void igraph_hrg_destroy(igraph_hrg_t *hrg) {
+    igraph_vector_destroy(&hrg->left);
+    igraph_vector_destroy(&hrg->right);
+    igraph_vector_destroy(&hrg->prob);
+    igraph_vector_destroy(&hrg->edges);
+    igraph_vector_destroy(&hrg->vertices);
+}
+
+/**
+ * \function igraph_hrg_size
+ * Returns the size of the HRG, the number of leaf nodes.
+ *
+ * \param hrg Pointer to the HRG.
+ * \return The number of leaf nodes in the HRG.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_hrg_size(const igraph_hrg_t *hrg) {
+    return igraph_vector_size(&hrg->left) + 1;
+}
+
+/**
+ * \function igraph_hrg_resize
+ * Resize a HRG.
+ *
+ * \param hrg Pointer to an initialized (see \ref igraph_hrg_init)
+ *   HRG.
+ * \param newsize The new size, i.e. the number of leaf nodes.
+ * \return Error code.
+ *
+ * Time complexity: O(n), n is the new size.
+ */
+
+int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize) {
+    int origsize = igraph_hrg_size(hrg);
+    int ret = 0;
+    igraph_error_handler_t *oldhandler =
+        igraph_set_error_handler(igraph_error_handler_ignore);
+
+    ret  = igraph_vector_resize(&hrg->left, newsize - 1);
+    ret |= igraph_vector_resize(&hrg->right, newsize - 1);
+    ret |= igraph_vector_resize(&hrg->prob, newsize - 1);
+    ret |= igraph_vector_resize(&hrg->edges, newsize - 1);
+    ret |= igraph_vector_resize(&hrg->vertices, newsize - 1);
+
+    igraph_set_error_handler(oldhandler);
+
+    if (ret) {
+        igraph_vector_resize(&hrg->left, origsize);
+        igraph_vector_resize(&hrg->right, origsize);
+        igraph_vector_resize(&hrg->prob, origsize);
+        igraph_vector_resize(&hrg->edges, origsize);
+        igraph_vector_resize(&hrg->vertices, origsize);
+        IGRAPH_ERROR("Cannot resize HRG", ret);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_hrg_fit
+ * Fit a hierarchical random graph model to a network
+ *
+ * \param graph The igraph graph to fit the model to. Edge directions
+ *   are ignored in directed graphs.
+ * \param hrg Pointer to an initialized HRG, the result of the fitting
+ *   is stored here. It can also be used to pass a HRG to the
+ *   function, that can be used as the starting point of the Markov
+ *   Chain Monte Carlo fitting, if the \c start argument is true.
+ * \param start Logical, whether to start the fitting from the given
+ *   HRG.
+ * \param steps Integer, the number of MCMC steps to take in the
+ *   fitting procedure. If this is zero, then the fitting stop is a
+ *   convergence criteria is fulfilled.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_hrg_fit(const igraph_t *graph,
+                   igraph_hrg_t *hrg,
+                   igraph_bool_t start,
+                   int steps) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    dendro *d;
+
+    RNG_BEGIN();
+
+    d = new dendro;
+
+    // If we want to start from HRG
+    if (start) {
+        d->clearDendrograph();
+        if (igraph_hrg_size(hrg) != no_of_nodes) {
+            delete d;
+            IGRAPH_ERROR("Invalid HRG to start from", IGRAPH_EINVAL);
+        }
+        // Convert the igraph graph
+        IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d));
+        d->importDendrogramStructure(hrg);
+    } else {
+        // Convert the igraph graph
+        IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d));
+        IGRAPH_CHECK(igraph_hrg_resize(hrg, no_of_nodes));
+    }
+
+    // Run fixed number of steps, or until convergence
+    if (steps > 0) {
+        IGRAPH_CHECK(markovChainMonteCarlo(d, steps, hrg));
+    } else {
+        IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg));
+    }
+
+    delete d;
+
+    RNG_END();
+
+    return 0;
+
+}
+
+/**
+ * \function igraph_hrg_sample
+ * Sample from a hierarchical random graph model
+ *
+ * Sample from a hierarchical random graph ensemble. The ensemble can
+ * be given as a graph (\c input_graph), or as a HRG object (\c hrg).
+ * If a graph is given, then first an MCMC optimization is performed
+ * to find the optimal fitting model; then the MCMC is used to sample
+ * the graph(s).
+ * \param input_graph An igraph graph, or a null pointer. If not a
+ *   null pointer, then a HRG is first fitted to the graph, possibly
+ *   starting from the given HRG, if the \c start argument is true. If
+ *   is is a null pointer, then the given HRG is used as a starting
+ *   point, to  find the optimum of the Markov chain, before the
+ *   sampling.
+ * \param sample Pointer to an uninitialized graph, or a null
+ *   pointer. If only one sample is requested, and it is not a null
+ *   pointer, then the sample is stored here.
+ * \param samples An initialized vector of pointers. If more than one
+ *   samples are requested, then they are stored here. Note that to
+ *   free this data structure, you need to call \ref igraph_destroy on
+ *   each graph first, then \c free() on all pointers, and finally
+ *   \ref igraph_vector_ptr_destroy.
+ * \param no_samples The number of samples to generate.
+ * \param hrg A HRG. It is modified during the sampling.
+ * \param start Logical, whether to start the MCMC from the given
+ *   HRG.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_hrg_sample(const igraph_t *input_graph,
+                      igraph_t *sample,
+                      igraph_vector_ptr_t *samples,
+                      int no_samples,
+                      igraph_hrg_t *hrg,
+                      igraph_bool_t start) {
+
+    int i;
+    dendro *d;
+
+    if (no_samples < 0) {
+        IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL);
+    }
+
+    if (!sample && !samples) {
+        IGRAPH_ERROR("Give at least one of `sample' and `samples'",
+                     IGRAPH_EINVAL);
+    }
+
+    if (no_samples != 1 && sample) {
+        IGRAPH_ERROR("Number of samples should be one if `sample' is given",
+                     IGRAPH_EINVAL);
+    }
+
+    if (no_samples > 1 && !samples) {
+        IGRAPH_ERROR("`samples' must be non-null if number of samples "
+                     "is larger than 1", IGRAPH_EINVAL);
+    }
+
+    if (!start && !input_graph) {
+        IGRAPH_ERROR("Input graph must be given if initial HRG is not used",
+                     IGRAPH_EINVAL);
+    }
+
+    if (!start) {
+        IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(input_graph)));
+    }
+
+    if (input_graph && igraph_hrg_size(hrg) != igraph_vcount(input_graph)) {
+        IGRAPH_ERROR("Invalid HRG size, should match number of nodes",
+                     IGRAPH_EINVAL);
+    }
+
+    RNG_BEGIN();
+
+    d = new dendro;
+
+    // Need to find equilibrium first?
+    if (start) {
+        d->clearDendrograph();
+        d->importDendrogramStructure(hrg);
+    } else {
+        IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg));
+    }
+
+    // TODO: free on error
+
+    if (sample) {
+        // A single graph
+        d->makeRandomGraph();
+        d->recordGraphStructure(sample);
+        if (samples) {
+            igraph_t *G = igraph_Calloc(1, igraph_t);
+            if (!G) {
+                IGRAPH_ERROR("Cannot sample HRG graphs", IGRAPH_ENOMEM);
+            }
+            d->recordGraphStructure(G);
+            IGRAPH_CHECK(igraph_vector_ptr_resize(samples, 1));
+            VECTOR(*samples)[0] = G;
+        }
+    } else {
+        // Sample many
+        IGRAPH_CHECK(igraph_vector_ptr_resize(samples, no_samples));
+        for (i = 0; i < no_samples; i++) {
+            igraph_t *G = igraph_Calloc(1, igraph_t);
+            if (!G) {
+                IGRAPH_ERROR("Cannot sample HRG graphs", IGRAPH_ENOMEM);
+            }
+            d->makeRandomGraph();
+            d->recordGraphStructure(G);
+            VECTOR(*samples)[i] = G;
+        }
+    }
+
+    delete d;
+
+    RNG_END();
+
+    return 0;
+}
+
+/**
+ * \function igraph_hrg_game
+ * Generate a hierarchical random graph
+ *
+ * This function is a simple shortcut to \ref igraph_hrg_sample.
+ * It creates a single graph, from the given HRG.
+ * \param graph Pointer to an uninitialized graph, the new graph is
+ *   created here.
+ * \param hrg The hierarchical random graph model to sample from. It
+ *   is modified during the MCMC process.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_hrg_game(igraph_t *graph,
+                    const igraph_hrg_t *hrg) {
+    return igraph_hrg_sample(/* input_graph= */ 0, /* sample= */ graph,
+            /* samples= */ 0, /* no_samples=*/ 1,
+            /* hrg= */ (igraph_hrg_t*) hrg,
+            /* start= */ 1);
+}
+
+/**
+ * \function igraph_hrg_dendrogram
+ * Create a dendrogram from a hierarchical random graph.
+ *
+ * Creates the igraph graph equivalent of an \ref igraph_hrg_t data
+ * structure.
+ * \param graph Pointer to an uninitialized graph, the result is
+ *   stored here.
+ * \param hrg The hierarchical random graph to convert.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of vertices in the graph.
+ */
+
+int igraph_hrg_dendrogram(igraph_t *graph,
+                          const igraph_hrg_t *hrg) {
+
+    int orig_nodes = igraph_hrg_size(hrg);
+    int no_of_nodes = orig_nodes * 2 - 1;
+    int no_of_edges = no_of_nodes - 1;
+    igraph_vector_t edges;
+    int i, idx = 0;
+    igraph_vector_ptr_t vattrs;
+    igraph_vector_t prob;
+    igraph_attribute_record_t rec = { "probability",
+                                      IGRAPH_ATTRIBUTE_NUMERIC,
+                                      &prob
+                                    };
+
+    // Probability labels, for leaf nodes they are IGRAPH_NAN
+    IGRAPH_VECTOR_INIT_FINALLY(&prob, no_of_nodes);
+    for (i = 0; i < orig_nodes; i++) {
+        VECTOR(prob)[i] = IGRAPH_NAN;
+    }
+    for (i = 0; i < orig_nodes - 1; i++) {
+        VECTOR(prob)[orig_nodes + i] = VECTOR(hrg->prob)[i];
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+    IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 1));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattrs);
+    VECTOR(vattrs)[0] = &rec;
+
+    for (i = 0; i < orig_nodes - 1; i++) {
+        int left = VECTOR(hrg->left)[i];
+        int right = VECTOR(hrg->right)[i];
+
+        VECTOR(edges)[idx++] = orig_nodes + i;
+        VECTOR(edges)[idx++] = left < 0 ? orig_nodes - left - 1 : left;
+        VECTOR(edges)[idx++] = orig_nodes + i;
+        VECTOR(edges)[idx++] = right < 0 ? orig_nodes - right - 1 : right;
+    }
+
+    IGRAPH_CHECK(igraph_empty(graph, 0, IGRAPH_DIRECTED));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+    IGRAPH_CHECK(igraph_add_vertices(graph, no_of_nodes, &vattrs));
+    IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0));
+
+    igraph_vector_ptr_destroy(&vattrs);
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&prob);
+    IGRAPH_FINALLY_CLEAN(4);  // + 1 for graph
+
+    return 0;
+}
+
+/**
+ * \function igraph_hrg_consensus
+ * Calculate a consensus tree for a HRG.
+ *
+ * The calculation can be started from the given HRG (\c hrg), or (if
+ * \c start is false), a HRG is first fitted to the given graph.
+ *
+ * \param graph The input graph.
+ * \param parents An initialized vector, the results are stored
+ *   here. For each vertex, the id of its parent vertex is stored, or
+ *   -1, if the vertex is the root vertex in the tree. The first n
+ *   vertex ids (from 0) refer to the original vertices of the graph,
+ *   the other ids refer to vertex groups.
+ * \param weights Numeric vector, counts the number of times a given
+ *   tree split occured in the generated network samples, for each
+ *   internal vertices. The order is the same as in \c parents.
+ * \param hrg A hierarchical random graph. It is used as a starting
+ *   point for the sampling, if the \c start argument is true. It is
+ *   modified along the MCMC.
+ * \param start Logical, whether to use the supplied HRG (in \c hrg)
+ *   as a starting point for the MCMC.
+ * \param num_samples The number of samples to generate for creating
+ *   the consensus tree.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_hrg_consensus(const igraph_t *graph,
+                         igraph_vector_t *parents,
+                         igraph_vector_t *weights,
+                         igraph_hrg_t *hrg,
+                         igraph_bool_t start,
+                         int num_samples) {
+
+    dendro *d;
+
+    if (start && !hrg) {
+        IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL);
+    }
+
+    RNG_BEGIN();
+
+    d = new dendro;
+
+    if (start) {
+        d->clearDendrograph();
+        IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d));
+        d->importDendrogramStructure(hrg);
+    } else {
+        IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d));
+        if (hrg) {
+            igraph_hrg_resize(hrg, igraph_vcount(graph));
+        }
+        IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg));
+    }
+
+    IGRAPH_CHECK(markovChainMonteCarlo2(d, num_samples));
+
+    d->recordConsensusTree(parents, weights);
+
+    delete d;
+
+    RNG_END();
+
+    return 0;
+}
+
+int MCMCEquilibrium_Sample(dendro *d, int num_samples) {
+
+    // Because moves in the dendrogram space are chosen (Monte
+    // Carlo) so that we sample dendrograms with probability
+    // proportional to their likelihood, a likelihood-proportional
+    // sampling of the dendrogram models would be equivalent to a
+    // uniform sampling of the walk itself. We would still have to
+    // decide how often to sample the walk (at most once every n steps
+    // is recommended) but for simplicity, the code here simply runs the
+    // MCMC itself. To actually compute something over the set of
+    // sampled dendrogram models (in a Bayesian model averaging sense),
+    // you'll need to code that yourself.
+
+    double dL;
+    bool flag_taken;
+    int sample_num = 0;
+    int t = 1, thresh = 100 * d->g->numNodes();
+    double ptest = 1.0 / 10.0 / d->g->numNodes();
+
+    while (sample_num < num_samples) {
+        d->monteCarloMove(dL, flag_taken, 1.0);
+        if (t > thresh && RNG_UNIF01() < ptest) {
+            sample_num++;
+            d->sampleAdjacencyLikelihoods();
+        }
+        d->refreshLikelihood(); // TODO: less frequently
+        t++;
+    }
+
+    return 0;
+}
+
+int QsortPartition (pblock* array, int left, int right, int index) {
+    pblock p_value, temp;
+    p_value.L = array[index].L;
+    p_value.i = array[index].i;
+    p_value.j = array[index].j;
+
+    // swap(array[p_value], array[right])
+    temp.L = array[right].L;
+    temp.i = array[right].i;
+    temp.j = array[right].j;
+    array[right].L = array[index].L;
+    array[right].i = array[index].i;
+    array[right].j = array[index].j;
+    array[index].L = temp.L;
+    array[index].i = temp.i;
+    array[index].j = temp.j;
+
+    int stored = left;
+    for (int i = left; i < right; i++) {
+        if (array[i].L <= p_value.L) {
+            // swap(array[stored], array[i])
+            temp.L = array[i].L;
+            temp.i = array[i].i;
+            temp.j = array[i].j;
+            array[i].L = array[stored].L;
+            array[i].i = array[stored].i;
+            array[i].j = array[stored].j;
+            array[stored].L = temp.L;
+            array[stored].i = temp.i;
+            array[stored].j = temp.j;
+            stored++;
+        }
+    }
+    // swap(array[right], array[stored])
+    temp.L = array[stored].L;
+    temp.i = array[stored].i;
+    temp.j = array[stored].j;
+    array[stored].L = array[right].L;
+    array[stored].i = array[right].i;
+    array[stored].j = array[right].j;
+    array[right].L  = temp.L;
+    array[right].i  = temp.i;
+    array[right].j  = temp.j;
+
+    return stored;
+}
+
+void QsortMain (pblock* array, int left, int right) {
+    if (right > left) {
+        int pivot = left;
+        int part  = QsortPartition(array, left, right, pivot);
+        QsortMain(array, left,   part - 1);
+        QsortMain(array, part + 1, right  );
+    }
+    return;
+}
+
+int rankCandidatesByProbability(simpleGraph *sg, dendro *d,
+                                pblock *br_list, int mk) {
+    int mkk = 0;
+    int n = sg->getNumNodes();
+    for (int i = 0; i < n; i++) {
+        for (int j = i + 1; j < n; j++) {
+            if (sg->getAdjacency(i, j) < 0.5) {
+                double temp = d->g->getAdjacencyAverage(i, j);
+                br_list[mkk].L = temp * (1.0 + RNG_UNIF01() / 1000.0);
+                br_list[mkk].i = i;
+                br_list[mkk].j = j;
+                mkk++;
+            }
+        }
+    }
+
+    // Sort the candidates by their average probability
+    QsortMain(br_list, 0, mk - 1);
+
+    return 0;
+}
+
+int recordPredictions(pblock *br_list, igraph_vector_t *edges,
+                      igraph_vector_t *prob, int mk) {
+
+    IGRAPH_CHECK(igraph_vector_resize(edges, mk * 2));
+    IGRAPH_CHECK(igraph_vector_resize(prob, mk));
+
+    for (int i = mk - 1, idx = 0, idx2 = 0; i >= 0; i--) {
+        VECTOR(*edges)[idx++] = br_list[i].i;
+        VECTOR(*edges)[idx++] = br_list[i].j;
+        VECTOR(*prob)[idx2++] = br_list[i].L;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_hrg_predict
+ * Predict missing edges in a graph, based on HRG models
+ *
+ * Samples HRG models for a network, and estimated the probability
+ * that an edge was falsely observed as non-existent in the network.
+ * \param graph The input graph.
+ * \param edges The list of missing edges is stored here, the first
+ *   two elements are the first edge, the next two the second edge,
+ *   etc.
+ * \param prob Vector of probabilies for the existence of missing
+ *   edges, in the order corresponding to \c edges.
+ * \param hrg A HRG, it is used as a starting point if \c start is
+ *   true. It is also modified during the MCMC sampling.
+ * \param start Logical, whether to start the MCMC from the given HRG.
+ * \param num_samples The number of samples to generate.
+ * \param num_bins Controls the resolution of the edge
+ *   probabilities. Higher numbers result higher resolution.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_hrg_predict(const igraph_t *graph,
+                       igraph_vector_t *edges,
+                       igraph_vector_t *prob,
+                       igraph_hrg_t *hrg,
+                       igraph_bool_t start,
+                       int num_samples,
+                       int num_bins) {
+
+    dendro *d;
+    pblock *br_list;
+    int mk;
+    simpleGraph *sg;
+
+    if (start && !hrg) {
+        IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL);
+    }
+
+    RNG_BEGIN();
+
+    d = new dendro;
+
+    IGRAPH_CHECK(igraph_i_hrg_getsimplegraph(graph, d, &sg, num_bins));
+
+    mk = sg->getNumNodes() * (sg->getNumNodes() - 1) / 2 - sg->getNumLinks() / 2;
+    br_list = new pblock[mk];
+    for (int i = 0; i < mk; i++) {
+        br_list[i].L = 0.0;
+        br_list[i].i = -1;
+        br_list[i].j = -1;
+    }
+
+    if (start) {
+        d->clearDendrograph();
+        // this has cleared the graph as well.... bug?
+        IGRAPH_CHECK(igraph_i_hrg_getsimplegraph(graph, d, &sg, num_bins));
+        d->importDendrogramStructure(hrg);
+    } else {
+        if (hrg) {
+            igraph_hrg_resize(hrg, igraph_vcount(graph));
+        }
+        IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg));
+    }
+
+    IGRAPH_CHECK(MCMCEquilibrium_Sample(d, num_samples));
+    IGRAPH_CHECK(rankCandidatesByProbability(sg, d, br_list, mk));
+    IGRAPH_CHECK(recordPredictions(br_list, edges, prob, mk));
+
+    delete d;
+    delete sg;
+    delete [] br_list;
+
+    RNG_END();
+
+    return 0;
+}
+
+/**
+ * \function igraph_hrg_create
+ * Create a HRG from an igraph graph.
+ *
+ * \param hrg Pointer to an initialized \ref igraph_hrg_t. The result
+ *    is stored here.
+ * \param graph The igraph graph to convert. It must be a directed
+ *    binary tree, with n-1 internal and n leaf vertices. The root
+ *    vertex must have in-degree zero.
+ * \param prob The vector of probabilities, this is used to label the
+ *    internal nodes of the hierarchical random graph. The values
+ *    corresponding to the leaves are ignored.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of vertices in the tree.
+ */
+
+int igraph_hrg_create(igraph_hrg_t *hrg,
+                      const igraph_t *graph,
+                      const igraph_vector_t *prob) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    int no_of_internal = (no_of_nodes - 1) / 2;
+    igraph_vector_t deg, idx;
+    int root = 0;
+    int d0 = 0, d1 = 0, d2 = 0;
+    int ii = 0, il = 0;
+    igraph_vector_t neis;
+    igraph_vector_t path;
+
+    // --------------------------------------------------------
+    // CHECKS
+    // --------------------------------------------------------
+
+    // At least three vertices are required
+    if (no_of_nodes < 3) {
+        IGRAPH_ERROR("HRG tree must have at least three vertices",
+                     IGRAPH_EINVAL);
+    }
+
+    // Prob vector was given
+    if (!prob) {
+        IGRAPH_ERROR("Probability vector must be given for HRG",
+                     IGRAPH_EINVAL);
+    }
+
+    // Length of prob vector
+    if (igraph_vector_size(prob) != no_of_nodes) {
+        IGRAPH_ERROR("HRG probability vector of wrong size", IGRAPH_EINVAL);
+    }
+
+    // Must be a directed graph
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_ERROR("HRG graph must be directed", IGRAPH_EINVAL);
+    }
+
+    // Number of nodes must be odd
+    if (no_of_nodes % 2 == 0) {
+        IGRAPH_ERROR("Complete HRG graph must have odd number of vertices",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&deg, 0);
+
+    // Every vertex, except for the root must have in-degree one.
+    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(), IGRAPH_IN,
+                               IGRAPH_LOOPS));
+    for (int i = 0; i < no_of_nodes; i++) {
+        int d = VECTOR(deg)[i];
+        switch (d) {
+        case 0: d0++; root = i; break;
+        case 1: d1++; break;
+        default:
+            IGRAPH_ERROR("HRG nodes must have in-degree one, except for the "
+                         "root vertex", IGRAPH_EINVAL);
+        }
+    }
+    if (d1 != no_of_nodes - 1 || d0 != 1) {
+        IGRAPH_ERROR("HRG nodes must have in-degree one, except for the "
+                     "root vertex", IGRAPH_EINVAL);
+    }
+
+    // Every internal vertex must have out-degree two,
+    // leaves out-degree zero
+    d0 = d1 = d2 = 0;
+    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(), IGRAPH_OUT,
+                               IGRAPH_LOOPS));
+    for (int i = 0; i < no_of_nodes; i++) {
+        int d = VECTOR(deg)[i];
+        switch (d) {
+        case 0: d0++; break;
+        case 2: d2++; break;
+        default:
+            IGRAPH_ERROR("HRG nodes must have out-degree 2 (internal nodes) or "
+                         "degree 0 (leaves)", IGRAPH_EINVAL);
+        }
+    }
+
+    // Number of internal and external nodes is correct
+    // This basically checks that the graph has one component
+    if (d0 != d2 + 1) {
+        IGRAPH_ERROR("HRG degrees are incorrect, maybe multiple components?",
+                     IGRAPH_EINVAL);
+    }
+
+    // --------------------------------------------------------
+    // Graph is good, do the conversion
+    // --------------------------------------------------------
+
+    // Create an index, that maps the root node as first, then
+    // the internal nodes, then the leaf nodes
+    IGRAPH_VECTOR_INIT_FINALLY(&idx, no_of_nodes);
+    VECTOR(idx)[root] = - (ii++) - 1;
+    for (int i = 0; i < no_of_nodes; i++) {
+        int d = VECTOR(deg)[i];
+        if (i == root) {
+            continue;
+        }
+        if (d == 2) {
+            VECTOR(idx)[i] = - (ii++) - 1;
+        }
+        if (d == 0) {
+            VECTOR(idx)[i] = (il++);
+        }
+    }
+
+    igraph_hrg_resize(hrg, no_of_internal + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    for (int i = 0; i < no_of_nodes; i++) {
+        int ri = VECTOR(idx)[i];
+        if (ri >= 0) {
+            continue;
+        }
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT));
+        VECTOR(hrg->left )[-ri - 1] = VECTOR(idx)[ (int) VECTOR(neis)[0] ];
+        VECTOR(hrg->right)[-ri - 1] = VECTOR(idx)[ (int) VECTOR(neis)[1] ];
+        VECTOR(hrg->prob )[-ri - 1] = VECTOR(*prob)[i];
+    }
+
+    // Calculate the number of vertices and edges in each subtree
+    igraph_vector_null(&hrg->edges);
+    igraph_vector_null(&hrg->vertices);
+    IGRAPH_VECTOR_INIT_FINALLY(&path, 0);
+    IGRAPH_CHECK(igraph_vector_push_back(&path, VECTOR(idx)[root]));
+    while (!igraph_vector_empty(&path)) {
+        int ri = igraph_vector_tail(&path);
+        int lc = VECTOR(hrg->left)[-ri - 1];
+        int rc = VECTOR(hrg->right)[-ri - 1];
+        if (lc < 0 && VECTOR(hrg->vertices)[-lc - 1] == 0) {
+            // Go left
+            IGRAPH_CHECK(igraph_vector_push_back(&path, lc));
+        } else if (rc < 0 && VECTOR(hrg->vertices)[-rc - 1] == 0) {
+            // Go right
+            IGRAPH_CHECK(igraph_vector_push_back(&path, rc));
+        } else {
+            // Subtrees are done, update node and go up
+            VECTOR(hrg->vertices)[-ri - 1] +=
+                lc < 0 ? VECTOR(hrg->vertices)[-lc - 1] : 1;
+            VECTOR(hrg->vertices)[-ri - 1] +=
+                rc < 0 ? VECTOR(hrg->vertices)[-rc - 1] : 1;
+            VECTOR(hrg->edges)[-ri - 1] += lc < 0 ? VECTOR(hrg->edges)[-lc - 1] + 1 : 1;
+            VECTOR(hrg->edges)[-ri - 1] += rc < 0 ? VECTOR(hrg->edges)[-rc - 1] + 1 : 1;
+            igraph_vector_pop_back(&path);
+        }
+    }
+
+    igraph_vector_destroy(&path);
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&idx);
+    igraph_vector_destroy(&deg);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
diff --git a/igraph/src/igraph_hrg_types.cc b/igraph/src/igraph_hrg_types.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_hrg_types.cc
@@ -0,0 +1,3725 @@
+// ***********************************************************************
+// *** COPYRIGHT NOTICE **************************************************
+// rbtree - red-black tree (self-balancing binary tree data structure)
+// Copyright (C) 2004 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// ***********************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu |
+//                                 http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science
+//                AND Santa Fe Institute
+// Created      : Spring 2004
+// Modified     : many, many times
+//
+// ***********************************************************************
+
+#include "hrg_rbtree.h"
+#include "hrg_dendro.h"
+#include "hrg_graph.h"
+#include "hrg_splittree_eq.h"
+#include "hrg_graph_simp.h"
+
+#include "igraph_hrg.h"
+#include "igraph_constructors.h"
+#include "igraph_random.h"
+
+using namespace fitHRG;
+
+// ******** Red-Black Tree Methods ***************************************
+
+rbtree::rbtree() {
+    root = new elementrb;
+    leaf = new elementrb;
+
+    leaf->parent = root;
+
+    root->left = leaf;
+    root->right = leaf;
+    support = 0;
+}
+
+rbtree::~rbtree() {
+    if (root != NULL &&
+        (root->left != leaf || root->right != leaf)) {
+        deleteSubTree(root);
+    }
+    if (root) {
+        delete root;
+    }
+    delete leaf;
+    support = 0;
+    root = 0;
+    leaf = 0;
+}
+
+void rbtree::deleteTree() {
+    if (root != NULL) {
+        deleteSubTree(root);
+    }
+} // does not leak memory
+
+void rbtree::deleteSubTree(elementrb *z) {
+    if (z->left  != leaf) {
+        deleteSubTree(z->left);
+    }
+    if (z->right != leaf) {
+        deleteSubTree(z->right);
+    }
+    delete z;
+}
+
+// ******** Search Functions *********************************************
+// public search function - if there exists a elementrb in the tree
+// with key=searchKey, it returns TRUE and foundNode is set to point
+// to the found node; otherwise, it sets foundNode=NULL and returns
+// FALSE
+elementrb* rbtree::findItem(const int searchKey) {
+    elementrb *current = root;
+
+    // empty tree; bail out
+    if (current->key == -1) {
+        return NULL;
+    }
+
+    while (current != leaf) {
+        // left-or-right?
+        if (searchKey < current->key) {
+            // try moving down-left
+            if (current->left  != leaf) {
+                current = current->left;
+            } else {
+                //   failure; bail out
+                return NULL;
+            }
+        } else {
+            // left-or-right?
+            if (searchKey > current->key) {
+                // try moving down-left
+                if (current->right  != leaf) {
+                    current = current->right;
+                } else {
+                    // failure; bail out
+                    return NULL;
+                }
+            } else {
+                // found (searchKey==current->key)
+                return current;
+            }
+        }
+    }
+    return NULL;
+}
+
+int rbtree::returnValue(const int searchKey) {
+    elementrb* test = findItem(searchKey);
+    if (!test) {
+        return 0;
+    } else {
+        return test->value;
+    }
+}
+
+
+// ******** Return Item Functions ****************************************
+
+int* rbtree::returnArrayOfKeys() {
+    int* array;
+    array = new int [support];
+    bool flag_go = true;
+    int index = 0;
+    elementrb *curr;
+
+    if (support == 1) {
+        array[0] = root->key;
+    } else if (support == 2) {
+        array[0] = root->key;
+        if (root->left == leaf) {
+            array[1] = root->right->key;
+        } else {
+            array[1] = root->left->key;
+        }
+    } else {
+        for (int i = 0; i < support; i++) {
+            array[i] = -1;
+        }
+        // non-recursive traversal of tree structure
+        curr = root;
+        curr->mark = 1;
+        while (flag_go) {
+            // - is it time, and is left child the leaf node?
+            if (curr->mark == 1 && curr->left == leaf) {
+                curr->mark = 2;
+            }
+            // - is it time, and is right child the leaf node?
+            if (curr->mark == 2 && curr->right == leaf) {
+                curr->mark = 3;
+            }
+            if (curr->mark == 1) {
+                // - go left
+                curr->mark = 2;
+                curr = curr->left;
+                curr->mark = 1;
+            } else if (curr->mark == 2) {
+                // - else go right
+                curr->mark = 3;
+                curr = curr->right;
+                curr->mark = 1;
+            } else {
+                // - else go up a level
+                curr->mark = 0;
+                array[index++] = curr->key;
+                curr = curr->parent;
+                if (curr == NULL) {
+                    flag_go = false;
+                }
+            }
+        }
+    }
+
+    return array;
+}
+
+list* rbtree::returnListOfKeys() {
+    keyValuePair *curr, *prev;
+    list *head = 0, *tail = 0, *newlist;
+
+    curr = returnTreeAsList();
+    while (curr != NULL) {
+        newlist = new list;
+        newlist->x = curr->x;
+        if (head == NULL) {
+            head       = newlist; tail = head;
+        } else {
+            tail->next = newlist; tail = newlist;
+        }
+        prev = curr;
+        curr = curr->next;
+        delete prev;
+        prev = NULL;
+    }
+    return head;
+}
+
+keyValuePair* rbtree::returnTreeAsList() {
+    // pre-order traversal
+    keyValuePair  *head, *tail;
+
+    head = new keyValuePair;
+    head->x = root->key;
+    head->y = root->value;
+    tail = head;
+
+    if (root->left  != leaf) {
+        tail = returnSubtreeAsList(root->left,  tail);
+    }
+    if (root->right != leaf) {
+        tail = returnSubtreeAsList(root->right, tail);
+    }
+
+    if (head->x == -1) {
+        return NULL; /* empty tree */
+    } else {
+        return head;
+    }
+}
+
+keyValuePair* rbtree::returnSubtreeAsList(elementrb *z, keyValuePair *head) {
+    keyValuePair *newnode, *tail;
+
+    newnode = new keyValuePair;
+    newnode->x = z->key;
+    newnode->y = z->value;
+    head->next = newnode;
+    tail = newnode;
+
+    if (z->left  != leaf) {
+        tail = returnSubtreeAsList(z->left,  tail);
+    }
+    if (z->right != leaf) {
+        tail = returnSubtreeAsList(z->right, tail);
+    }
+
+    return tail;
+}
+
+keyValuePair rbtree::returnMaxKey() {
+    keyValuePair themax;
+    elementrb *current;
+    current  = root;
+
+    // search to bottom-right corner of tree
+    while (current->right != leaf) {
+        current  = current->right;
+    }
+    themax.x = current->key;
+    themax.y = current->value;
+
+    return themax;
+}
+
+keyValuePair rbtree::returnMinKey() {
+    keyValuePair themin;
+    elementrb *current;
+    current = root;
+    // search to bottom-left corner of tree
+    while (current->left != leaf) {
+        current = current->left;
+    }
+    themin.x = current->key;
+    themin.y = current->value;
+
+    return themin;
+}
+
+// private functions for deleteItem() (although these could easily be
+// made public, I suppose)
+elementrb* rbtree::returnMinKey(elementrb *z) {
+    elementrb *current;
+
+    current = z;
+    // search to bottom-right corner of tree
+    while (current->left != leaf) {
+        current = current->left;
+    }
+    return current;
+}
+
+elementrb* rbtree::returnSuccessor(elementrb *z) {
+    elementrb *current, *w;
+
+    w = z;
+    // if right-subtree exists, return min of it
+    if (w->right != leaf) {
+        return returnMinKey(w->right);
+    }
+    // else search up in tree
+    current = w->parent;
+    while ((current != NULL) && (w == current->right)) {
+        w = current;
+        // move up in tree until find a non-right-child
+        current = current->parent;
+    }
+    return current;
+}
+
+int rbtree::returnNodecount() {
+    return support;
+}
+
+// ******** Insert Functions *********************************************
+// public insert function
+void rbtree::insertItem(int newKey, int newValue) {
+
+    // first we check to see if newKey is already present in the tree;
+    // if so, we do nothing; if not, we must find where to insert the
+    // key
+    elementrb *newNode, *current;
+
+    // find newKey in tree; return pointer to it O(log k)
+    current = findItem(newKey);
+    if (current == NULL) {
+        newNode = new elementrb;    // elementrb for the rbtree
+        newNode->key = newKey;
+        newNode->value = newValue;
+        newNode->color = true;  // new nodes are always RED
+        newNode->parent = NULL; // new node initially has no parent
+        newNode->left = leaf;   // left leaf
+        newNode->right = leaf;  // right leaf
+        support++;          // increment node count in rbtree
+
+        // must now search for where to insert newNode, i.e., find the
+        // correct parent and set the parent and child to point to each
+        // other properly
+        current = root;
+        if (current->key == -1) {    // insert as root
+            delete root;           // delete old root
+            root = newNode;            // set root to newNode
+            leaf->parent = newNode;        // set leaf's parent
+            current = leaf;            // skip next loop
+        }
+
+        // search for insertion point
+        while (current != leaf) {
+            // left-or-right?
+            if (newKey < current->key) {
+                // try moving down-left
+                if (current->left  != leaf) {
+                    current = current->left;
+                } else {
+                    // else found new parent
+                    newNode->parent = current; // set parent
+                    current->left = newNode;   // set child
+                    current = leaf;        // exit search
+                }
+            } else {
+                // try moving down-right
+                if (current->right != leaf) {
+                    current = current->right;
+                } else {
+                    // else found new parent
+                    newNode->parent = current; // set parent
+                    current->right = newNode;  // set child
+                    current = leaf;        // exit search
+                }
+            }
+        }
+
+        // now do the house-keeping necessary to preserve the red-black
+        // properties
+        insertCleanup(newNode);
+    }
+    return;
+}
+
+// private house-keeping function for insertion
+void rbtree::insertCleanup(elementrb *z) {
+
+    // fix now if z is root
+    if (z->parent == NULL) {
+        z->color = false;
+        return;
+    }
+
+    elementrb *temp;
+
+    // while z is not root and z's parent is RED
+    while (z->parent != NULL && z->parent->color) {
+        if (z->parent == z->parent->parent->left) {
+
+            // z's parent is LEFT-CHILD
+
+            temp = z->parent->parent->right;   // grab z's uncle
+            if (temp->color) {
+                z->parent->color = false;        // color z's parent BLACK  (Case 1)
+                temp->color = false;             // color z's uncle BLACK   (Case 1)
+                z->parent->parent->color = true; // color z's grandpar. RED (Case 1)
+                z = z->parent->parent;           // set z = z's grandparent (Case 1)
+            } else {
+                if (z == z->parent->right) {
+                    // z is RIGHT-CHILD
+                    z = z->parent;             // set z = z's parent      (Case 2)
+                    rotateLeft(z);             // perform left-rotation   (Case 2)
+                }
+                z->parent->color = false;        // color z's parent BLACK  (Case 3)
+                z->parent->parent->color = true; // color z's grandpar. RED (Case 3)
+                rotateRight(z->parent->parent);  // perform right-rotation  (Case 3)
+            }
+        } else {
+
+            // z's parent is RIGHT-CHILD
+
+            temp = z->parent->parent->left;    // grab z's uncle
+            if (temp->color) {
+                z->parent->color = false;        // color z's parent BLACK  (Case 1)
+                temp->color = false;             // color z's uncle BLACK   (Case 1)
+                z->parent->parent->color = true; // color z's grandpar. RED (Case 1)
+                z = z->parent->parent;           // set z = z's grandparent (Case 1)
+            } else {
+                if (z == z->parent->left) {
+                    // z is LEFT-CHILD
+                    z = z->parent;                 // set z = z's parent      (Case 2)
+                    rotateRight(z);                // perform right-rotation  (Case 2)
+                }
+                z->parent->color = false;        // color z's parent BLACK  (Case 3)
+                z->parent->parent->color = true; // color z's grandpar. RED (Case 3)
+                rotateLeft(z->parent->parent);   // perform left-rotation   (Case 3)
+            }
+        }
+    }
+
+    root->color = false;               // color the root BLACK
+    return;
+}
+
+// ******** Delete
+// ******** Functions *********************************************
+
+void rbtree::replaceItem(int key, int newValue) {
+    elementrb* ptr;
+    ptr = findItem(key);
+    ptr->value = newValue;
+    return;
+}
+
+void rbtree::incrementValue(int key) {
+    elementrb* ptr;
+    ptr = findItem(key);
+    ptr->value = 1 + ptr->value;
+    return;
+}
+
+// public delete function
+void rbtree::deleteItem(int killKey) {
+    elementrb *x, *y, *z;
+
+    z = findItem(killKey);
+    if (z == NULL) {
+        return;    // item not present; bail out
+    }
+
+    if (support == 1) {     // attempt to delete the root
+        root->key = -1;       // restore root node to default state
+        root->value = -1;
+        root->color = false;
+        root->parent = NULL;
+        root->left = leaf;
+        root->right = leaf;
+        support--;            // set support to zero
+        return;           // exit - no more work to do
+    }
+
+    if (z != NULL) {
+        support--;            // decrement node count
+        if ((z->left == leaf) || (z->right == leaf)) {
+            y = z;                      // case of less than two children,
+            // set y to be z
+        } else {
+            y = returnSuccessor(z);     // set y to be z's key-successor
+        }
+
+        if (y->left != leaf) {
+            x = y->left;        // pick y's one child (left-child)
+        } else {
+            x = y->right;       // (right-child)
+        }
+        x->parent = y->parent;        // make y's child's parent be y's parent
+
+        if (y->parent == NULL) {
+            root = x;           // if y is the root, x is now root
+        } else {
+            if (y == y->parent->left) { // decide y's relationship with y's parent
+                y->parent->left  = x;     // replace x as y's parent's left child
+            } else {
+                y->parent->right = x;     // replace x as y's parent's left child
+            }
+        }
+
+        if (y != z) {         // insert y into z's spot
+            z->key = y->key;        // copy y data into z
+            z->value = y->value;
+        }
+
+        // do house-keeping to maintain balance
+        if (y->color == false) {
+            deleteCleanup(x);
+        }
+
+        delete y;
+        y = NULL;
+    }
+
+    return;
+}
+
+void rbtree::deleteCleanup(elementrb *x) {
+    elementrb *w, *t;
+
+    // until x is the root, or x is RED
+    while ((x != root) && (x->color == false)) {
+        if (x == x->parent->left) {   // branch on x being a LEFT-CHILD
+            w = x->parent->right;   // grab x's sibling
+            if (w->color == true) { // if x's sibling is RED
+                w->color = false;     // color w BLACK (case 1)
+                x->parent->color = true;  // color x's parent RED            (case 1)
+                rotateLeft(x->parent);    // left rotation on x's parent     (case 1)
+                w = x->parent->right;     // make w be x's right sibling     (case 1)
+            }
+            if ((w->left->color == false) && (w->right->color == false)) {
+                w->color = true;      // color w RED                     (case 2)
+                x = x->parent;        // examine x's parent              (case 2)
+            } else {
+                if (w->right->color == false) {
+                    w->left->color = false; // color w's left child BLACK      (case 3)
+                    w->color = true;    // color w RED                     (case 3)
+                    t = x->parent;      // store x's parent                (case 3)
+                    rotateRight(w);     // right rotation on w             (case 3)
+                    x->parent = t;      // restore x's parent              (case 3)
+                    w = x->parent->right;   // make w be x's right sibling     (case 3)
+                }
+                w->color = x->parent->color; // w's color := x's parent's    (case 4)
+                x->parent->color = false; // color x's parent BLACK          (case 4)
+                w->right->color = false;  // color w's right child BLACK     (case 4)
+                rotateLeft(x->parent);    // left rotation on x's parent     (case 4)
+                x = root;                 // finished work. bail out         (case 4)
+            }
+        } else {                // x is RIGHT-CHILD
+            w = x->parent->left;      // grab x's sibling
+            if (w->color == true) {   // if x's sibling is RED
+                w->color = false;       // color w BLACK                 (case 1)
+                x->parent->color    = true; // color x's parent RED          (case 1)
+                rotateRight(x->parent);     // right rotation on x's parent  (case 1)
+                w = x->parent->left;        // make w be x's left sibling    (case 1)
+            }
+            if ((w->right->color == false) && (w->left->color == false)) {
+                w->color = true;        // color w RED                   (case 2)
+                x = x->parent;          // examine x's parent            (case 2)
+            } else {
+                if (w->left->color == false) {
+                    w->right->color = false;  // color w's right child BLACK   (case 3)
+                    w->color = true;      // color w RED                   (case 3)
+                    t = x->parent;        // store x's parent              (case 3)
+                    rotateLeft(w);        // left rotation on w            (case 3)
+                    x->parent = t;        // restore x's parent            (case 3)
+                    w = x->parent->left;      // make w be x's left sibling    (case 3)
+                }
+                w->color = x->parent->color; // w's color := x's parent's    (case 4)
+                x->parent->color    = false; // color x's parent BLACK       (case 4)
+                w->left->color = false;      // color w's left child BLACK   (case 4)
+                rotateRight(x->parent);      // right rotation on x's parent (case 4)
+                x = root;            // x is now the root            (case 4)
+            }
+        }
+    }
+    x->color = false;          // color x (the root) BLACK (exit)
+
+    return;
+}
+
+// ******** Rotation Functions ******************************************
+
+void rbtree::rotateLeft(elementrb *x) {
+    elementrb *y;
+    // do pointer-swapping operations for left-rotation
+    y = x->right;          // grab right child
+    x->right = y->left;        // make x's RIGHT-CHILD be y's LEFT-CHILD
+    y->left->parent = x;       // make x be y's LEFT-CHILD's parent
+    y->parent = x->parent;     // make y's new parent be x's old parent
+
+    if (x->parent == NULL) {
+        root = y;           // if x was root, make y root
+    } else {
+        // if x is LEFT-CHILD, make y be x's parent's
+        if (x == x->parent->left) {
+            x->parent->left  = y; // left-child
+        } else {
+            x->parent->right = y; //  right-child
+        }
+    }
+    y->left   = x;        // make x be y's LEFT-CHILD
+    x->parent = y;        // make y be x's parent
+
+    return;
+}
+
+void rbtree::rotateRight(elementrb *y) {
+    elementrb *x;
+    // do pointer-swapping operations for right-rotation
+    x = y->left;        // grab left child
+    y->left = x->right;     // replace left child yith x's right subtree
+    x->right->parent = y;   // replace y as x's right subtree's parent
+
+    x->parent = y->parent;  // make x's new parent be y's old parent
+
+    // if y was root, make x root
+    if (y->parent == NULL) {
+        root = x;
+    } else {
+        // if y is RIGHT-CHILD, make x be y's parent's
+        if (y == y->parent->right) {
+            // right-child
+            y->parent->right = x;
+        } else {
+            // left-child
+            y->parent->left = x;
+        }
+    }
+    x->right  = y;        // make y be x's RIGHT-CHILD
+    y->parent = x;        // make x be y's parent
+
+    return;
+}
+
+// ***********************************************************************
+// *** COPYRIGHT NOTICE **************************************************
+// dendro.h - hierarchical random graph (hrg) data structure
+// Copyright (C) 2005-2009 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// ***********************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu |
+//                                 http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark E.J. Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science
+//                AND Santa Fe Institute
+// Created      : 26 October 2005 - 7 December 2005
+// Modified     : 23 December 2007 (cleaned up for public consumption)
+//
+// ***********************************************************************
+//
+// Maximum likelihood dendrogram data structure. This is the heart of
+// the HRG algorithm: all manipulations are done here and all data is
+// stored here. The data structure uses the separate graph data
+// structure to store the basic adjacency information (in a
+// dangerously mutable way).
+//
+// ***********************************************************************
+
+// ******** Dendrogram Methods *******************************************
+
+dendro::dendro(): root(0), internal(0), leaf(0), d(0), splithist(0),
+    paths(0), ctree(0), cancestor(0), g(0) { }
+dendro::~dendro() {
+    list *curr, *prev;
+
+    if (g)        {
+        delete    g;            // O(m)
+        g        = 0;
+    }
+    if (internal) {
+        delete [] internal;     // O(n)
+        internal = 0;
+    }
+    if (leaf)     {
+        delete [] leaf;         // O(n)
+        leaf     = 0;
+    }
+    if (d)        {
+        delete    d;            // O(n)
+        d        = 0;
+    }
+    if (splithist) {
+        delete    splithist;    // potentially long
+        splithist = 0;
+    }
+
+    if (paths) {
+        for (int i = 0; i < n; i++) {
+            curr = paths[i];
+            while (curr) {
+                prev = curr;
+                curr = curr->next;
+                delete prev;
+                prev = 0;
+            }
+            paths[i] = 0;
+        }
+        delete [] paths;
+    }
+    paths = 0;
+
+    if (ctree)    {
+        delete [] ctree;        // O(n)
+        ctree     = 0;
+    }
+    if (cancestor) {
+        delete [] cancestor;    // O(n)
+        cancestor = 0;
+    }
+}
+
+// *********************************************************************
+
+void dendro::binarySearchInsert(elementd* x, elementd* y) {
+    if (y->p < x->p) {        // go to left subtree
+        if (x->L == NULL) {     // check if left subtree is empty
+            x->L = y;         // make x left child
+            y->M = x;         // make y parent of child
+            return;
+        } else {
+            binarySearchInsert(x->L, y);
+        }
+    } else {          // go to right subtree
+        if (x->R == NULL) {     // check if right subtree is empty
+            x->R = y;         // make x right child
+            y->M = x;         // make y parent of child
+            return;
+        } else {
+            binarySearchInsert(x->R, y);
+        }
+    }
+    return;
+}
+
+// **********************************************************************
+
+list* dendro::binarySearchFind(const double v) {
+    list *head = NULL, *tail = NULL, *newlist;
+    elementd *current = root;
+    bool flag_stopSearch = false;
+
+    while (!flag_stopSearch) {    // continue until we're finished
+        newlist    = new list;  // add this node to the path
+        newlist->x = current->label;
+        if (current == root) {
+            head = newlist; tail = head;
+        } else {
+            tail->next = newlist; tail = newlist;
+        }
+        if (v < current->p) {   // now try left subtree
+            if (current->L->type == GRAPH) {
+                flag_stopSearch = true;
+            } else {
+                current = current->L;
+            }
+        } else {            // else try right subtree
+            if (current->R->type == GRAPH) {
+                flag_stopSearch = true;
+            } else  {
+                current = current->R;
+            }
+        }
+    }
+    return head;
+}
+
+// ***********************************************************************
+
+string dendro::buildSplit(elementd* thisNode) {
+    // A "split" is defined as the bipartition of vertices into the sets
+    // of leaves below the internal vertex in the tree (denoted by "C"),
+    // and those above it (denoted as "M"). For simplicity, we represent
+    // this bipartition as a character string of length n, where the ith
+    // character denotes the partition membership (C,M) of the ith leaf
+    // node.
+
+    bool flag_go = true;
+    const short int k = 1 + DENDRO + GRAPH;
+    elementd* curr;
+    split sp;
+
+    sp.initializeSplit(n);      // default split string O(n)
+
+    curr = thisNode;        // - set start node as top this sub-tree
+    curr->type = k + 1;     // - initialize in-order tree traversal
+    while (flag_go) {
+
+        // - is it time, and is left child a graph node?
+        if (curr->type == k + 1 && curr->L->type == GRAPH) {
+            sp.s[curr->L->index] = 'C'; // - mark this leaf
+            curr->type = k + 2;
+        }
+
+        // - is it time, and is right child a graph node?
+        if (curr->type == k + 2 && curr->R->type == GRAPH) {
+            sp.s[curr->R->index] = 'C'; // - mark this leaf
+            curr->type           = k + 3;
+        }
+        if (curr->type == k + 1) {    // - go left
+            curr->type = k + 2;
+            curr       = curr->L;
+            curr->type = k + 1;
+        } else if (curr->type == k + 2) { // - else go right
+            curr->type = k + 3;
+            curr       = curr->R;
+            curr->type = k + 1;
+        } else {              // - else go up a level
+            curr->type = DENDRO;
+            if (curr->index == thisNode->index || curr->M == NULL) {
+                flag_go = false; curr = NULL;
+            } else {
+                curr = curr->M;
+            }
+        }
+    }
+
+    // any leaf that was not already marked must be in the remainder of
+    // the tree
+    for (int i = 0; i < n; i++) {
+        if (sp.s[i] != 'C') {
+            sp.s[i] = 'M';
+        }
+    }
+
+    return sp.s;
+}
+
+// **********************************************************************
+
+void dendro::buildDendrogram() {
+
+    /* the initialization of the dendrogram structure goes like this:
+     * 1) we allocate space for the n-1 internal nodes of the
+     *    dendrogram, and then the n leaf nodes
+     * 2) we build a random binary tree structure out of the internal
+     *    nodes by assigning each a uniformly random value over [0,1] and
+     *    then inserting it into the tree according to the
+     *    binary-search rule.
+     * 3) next, we make a random permutation of the n leaf nodes and add
+     *    them to the dendrogram D by replacing the emptpy spots in-order
+     * 4) then, we compute the path from the root to each leaf and store
+     *    that in each leaf (this is prep work for the next step)
+     * 5) finally, we compute the values for nL, nR, e (and thus p) and
+     *    the label for each internal node by allocating each of the m
+     *    edges in g to the appropriate internal node
+     */
+
+    // --- Initialization and memory allocation for data structures
+    // After allocating the memory for D and G, we need to mark the
+    // nodes for G as being non-internal vertices, and then insert them
+    // into a random binary tree structure. For simplicity, we make the
+    // first internal node in the array the root.
+
+    n = g->numNodes();          // size of graph
+    leaf = new elementd [n];    // allocate memory for G, O(n)
+    internal  = new elementd [n - 1]; // allocate memory for D, O(n)
+    d = new interns(n - 2);         // allocate memory for internal
+    // edges of D, O(n)
+    for (int i = 0; i < n; i++) { // initialize leaf nodes
+        leaf[i].type   = GRAPH;
+        leaf[i].label  = i;
+        leaf[i].index  = i;
+        leaf[i].n = 1;
+    }
+
+// initialize internal nodes
+    root = &internal[0];
+    root->label = 0;
+    root->index = 0;
+    root->p = RNG_UNIF01();
+
+    // insert remaining internal vertices, O(n log n)
+    for (int i = 1; i < (n - 1); i++) {
+        internal[i].label = i;
+        internal[i].index = i;
+        internal[i].p = RNG_UNIF01();
+        binarySearchInsert(root, &internal[i]);
+    }
+
+    // --- Hang leaf nodes off end of dendrogram O(n log n)
+    // To impose this random hierarchical relationship on G, we first
+    // take a random permutation of the leaf vertices and then replace
+    // the NULLs at the bottom of the tree in-order with the leafs. As a
+    // hack to ensure that we can find the leafs later using a binary
+    // search, we assign each of them the p value of their parent,
+    // perturbed slightly so as to preserve the binary search property.
+
+    block* array; array = new block [n];
+    for (int i = 0; i < n; i++) {
+        array[i].x = RNG_UNIF01();
+        array[i].y = i;
+    }
+    QsortMain(array, 0, n - 1);
+
+    int k = 0;        // replace NULLs with leaf nodes, and
+    for (int i = 0; i < (n - 1); i++) { // maintain binary search property, O(n)
+        if (internal[i].L == NULL) {
+            internal[i].L = &leaf[array[k].y];
+            leaf[array[k].y].M = &internal[i];
+            leaf[array[k++].y].p = internal[i].p - 0.0000000000001;
+        }
+        if (internal[i].R == NULL) {
+            internal[i].R = &leaf[array[k].y];
+            leaf[array[k].y].M = &internal[i];
+            leaf[array[k++].y].p = internal[i].p + 0.0000000000001;
+        }
+    }
+    delete [] array;
+
+    // --- Compute the path from root -> leaf for each leaf O(n log n)
+    // Using the binary search property, we can find each leaf node in
+    // O(log n) time. The binarySearchFind() function returns the list
+    // of internal node indices that the search crossed, in the order of
+    // root -> ... -> leaf, for use in the subsequent few operations.
+
+    if (paths != NULL) {
+        list *curr, *prev;
+        for (int i = 0; i < n; i++) {
+            curr = paths[i];
+            while (curr != NULL) {
+                prev = curr;
+                curr = curr->next;
+                delete prev;
+                prev = NULL;
+            }
+            paths[i] = NULL;
+        }
+        delete [] paths;
+    }
+    paths = NULL;
+    paths = new list* [n];
+    for (int i = 0; i < n; i++) {
+        paths[i] = binarySearchFind(leaf[i].p);
+    }
+
+    // --- Count e for each internal node O(m)
+    // To count the number of edges that span the L and R subtrees for
+    // each internal node, we use the path information we just
+    // computed. Then, we loop over all edges in G and find the common
+    // ancestor in D of the two endpoints and increment that internal
+    // node's e count. This process takes O(m) time because in a roughly
+    // balanced binary tree (given by our random dendrogram), the vast
+    // majority of vertices take basically constant time to find their
+    // common ancestor. Note that because our adjacency list is
+    // symmetric, we overcount each e by a factor of 2, so we need to
+    // correct this after.
+
+    elementd* ancestor; edge* curr;
+    for (int i = 0; i < (n - 1); i++) {
+        internal[i].e = 0;
+        internal[i].label = -1;
+    }
+    for (int i = 0; i < n; i++) {
+        curr = g->getNeighborList(i);
+        while (curr != NULL) {
+            ancestor = findCommonAncestor(paths, i, curr->x);
+            ancestor->e += 1;
+            curr = curr->next;
+        }
+    }
+    for (int i = 0; i < (n - 1); i++) {
+        internal[i].e /= 2;
+    }
+
+    // --- Count n for each internal node O(n log n)
+    // To tabulate the number of leafs in each subtree rooted at an
+    // internal node, we use the path information computed above.
+    for (int i = 0; i < n; i++) {
+        ancestor = &leaf[i];
+        ancestor = ancestor->M;
+        while (ancestor != NULL) {
+            ancestor->n++;
+            ancestor = ancestor->M;
+        }
+    }
+
+    // --- Label all internal vertices O(n log n)
+    // We want to label each internal vertex with the smallest leaf
+    // index of its children. This will allow us to collapse many
+    // leaf-orderings into a single dendrogram structure that is
+    // independent of child-exhanges (since these have no impact on the
+    // likelihood of the hierarchical structure). To do this, we loop
+    // over the leaf vertices from smallest to largest and walk along
+    // that leaf's path from the root. If we find an unlabeled internal
+    // node, then we mark it with this leaf's index.
+
+    for (int i = 0; i < n; i++) {
+        ancestor = &leaf[i];
+        while (ancestor != NULL) {
+            if (ancestor->label == -1 || ancestor->label > leaf[i].label) {
+                ancestor->label = leaf[i].label;
+            }
+            ancestor = ancestor->M;
+        }
+    }
+
+    // --- Exchange children to enforce order-property O(n)
+    // We state that the order-property requires that an internal node's
+    // label is the smallest index of its left subtree. The dendrogram
+    // so far doesn't reflect this, so we need to step through each
+    // internal vertex and make that adjustment (swapping nL and nR if
+    // we make a change).
+
+    elementd *tempe;
+    for (int i = 0; i < (n - 1); i++) {
+        if (internal[i].L->label > internal[i].label) {
+            tempe = internal[i].L;
+            internal[i].L = internal[i].R;
+            internal[i].R = tempe;
+        }
+    }
+
+    // --- Tabulate internal dendrogram edges O(n^2)
+    // For the MCMC moves later on, we'll need to be able to choose,
+    // uniformly at random, an internal edge of the dendrogram to
+    // manipulate. There are always n-2 of them, and we can find them
+    // simply by scanning across the internal vertices and observing
+    // which have children that are also internal vertices. Note: very
+    // important that the order property be enforced before this step is
+    // taken; otherwise, the internal edges wont reflect the actual
+    // dendrogram structure.
+
+    for (int i = 0; i < (n - 1); i++) {
+        if (internal[i].L->type == DENDRO) {
+            d->addEdge(i, internal[i].L->index, LEFT);
+        }
+        if (internal[i].R->type == DENDRO) {
+            d->addEdge(i, internal[i].R->index, RIGHT);
+        }
+    }
+
+    // --- Clear memory for paths O(n log n)
+    // Now that we're finished using the paths, we need to deallocate
+    // them manually.
+
+    list *current, *previous;
+    for (int i = 0; i < n; i++) {
+        current = paths[i];
+        while (current) {
+            previous = current;
+            current = current->next;
+            delete previous;
+            previous = NULL;
+        }
+        paths[i] = NULL;
+    }
+    delete [] paths;
+    paths = NULL;
+
+    // --- Compute p_i for each internal node O(n)
+    // Each internal node's p_i = e_i / (nL_i*nR_i), and now that we
+    // have each of those pieces, we may calculate this value for each
+    // internal node. Given these, we can then calculate the
+    // log-likelihood of the entire dendrogram structure \log(L) =
+    // \sum_{i=1}^{n} ( ( e_i \log[p_i] ) + ( (nL_i*nR_i - e_i)
+    // \log[1-p_i] ) )
+
+    L = 0.0; double dL;
+    int nL_nR, ei;
+    for (int i = 0; i < (n - 1); i++) {
+        nL_nR = internal[i].L->n * internal[i].R->n;
+        ei = internal[i].e;
+        internal[i].p = (double)(ei) / (double)(nL_nR);
+        if (ei == 0 || ei == nL_nR) {
+            dL = 0.0;
+        } else {
+            dL = ei * log(internal[i].p) + (nL_nR - ei) * log(1.0 - internal[i].p);
+        }
+        internal[i].logL = dL;
+        L += dL;
+    }
+
+    for (int i = 0; i < (n - 1); i++) {
+        if (internal[i].label > internal[i].L->label) {
+            tempe = internal[i].L;
+            internal[i].L = internal[i].R;
+            internal[i].R = tempe;
+        }
+    }
+
+    // Dendrogram is now built
+
+    return;
+}
+
+// ***********************************************************************
+
+void dendro::clearDendrograph() {
+    // Clear out the memory and references used by the dendrograph
+    // structure - this is  intended to be called just before an
+    // importDendrogramStructure call so as to avoid memory leaks and
+    // overwriting the references therein.
+
+    if (g        != NULL) {
+        delete    g;           // O(m)
+        g        = NULL;
+    }
+    if (leaf     != NULL) {
+        delete [] leaf;        // O(n)
+        leaf     = NULL;
+    }
+    if (internal != NULL) {
+        delete [] internal;    // O(n)
+        internal = NULL;
+    }
+    if (d        != NULL) {
+        delete    d;        // O(n)
+        d           = NULL;
+    }
+    root = NULL;
+
+    return;
+}
+
+// **********************************************************************
+
+int dendro::computeEdgeCount(const int a, const short int atype,
+                             const int b, const short int btype) {
+    // This function computes the number of edges that cross between the
+    // subtree internal[a] and the subtree internal[b]. To do this, we
+    // use an array A[1..n] integers which take values -1 if A[i] is in
+    // the subtree defined by internal[a], +1 if A[i] is in the subtree
+    // internal[b], and 0 otherwise. Taking the smaller of the two sets,
+    // we then scan over the edges attached  to that set of vertices and
+    // count the number of endpoints we see in the other set.
+
+    bool flag_go    = true;
+    int nA, nB;
+    int         count = 0;
+    const short int k = 1 + DENDRO + GRAPH;
+
+    elementd* curr;
+
+    // First, we push the leaf nodes in the L and R subtrees into
+    // balanced binary tree structures so that we can search them
+    // quickly later on.
+
+    if (atype == GRAPH) {
+        // default case, subtree A is size 1
+        // insert single node as member of left subtree
+        subtreeL.insertItem(a, -1);
+        nA = 1; //
+    } else {
+        // explore subtree A, O(|A|)
+        curr  = &internal[a];
+        curr->type = k + 1;
+        nA = 0;
+        while (flag_go) {
+            if (curr->index == internal[a].M->index) {
+                internal[a].type = DENDRO;
+                flag_go = false;
+            } else {
+                // - is it time, and is left child a graph node?
+                if (curr->type == k + 1 && curr->L->type == GRAPH) {
+                    subtreeL.insertItem(curr->L->index, -1);
+                    curr->type = k + 2;
+                    nA++;
+                }
+                // - is it time, and is right child a graph node?
+                if (curr->type == k + 2 && curr->R->type == GRAPH) {
+                    subtreeL.insertItem(curr->R->index, -1);
+                    curr->type = k + 3;
+                    nA++;
+                }
+                if (curr->type == k + 1) {    // - go left
+                    curr->type = k + 2;
+                    curr       = curr->L;
+                    curr->type = k + 1;
+                } else if (curr->type == k + 2) { // - else go right
+                    curr->type = k + 3;
+                    curr       = curr->R;
+                    curr->type = k + 1;
+                } else {              // - else go up a level
+                    curr->type = DENDRO;
+                    curr       = curr->M;
+                    if (curr == NULL) {
+                        flag_go = false;
+                    }
+                }
+            }
+        }
+    }
+
+    if (btype == GRAPH) {
+        // default case, subtree A is size 1
+        // insert node as single member of right subtree
+        subtreeR.insertItem(b, 1);
+        nB = 1;
+    } else {
+        flag_go = true;
+        // explore subtree B, O(|B|)
+        curr = &internal[b];
+        curr->type = k + 1;
+        nB  = 0;
+        while (flag_go) {
+            if (curr->index == internal[b].M->index) {
+                internal[b].type = DENDRO;
+                flag_go = false;
+            } else {
+                // - is it time, and is left child a graph node?
+                if (curr->type == k + 1 && curr->L->type == GRAPH) {
+                    subtreeR.insertItem(curr->L->index, 1);
+                    curr->type = k + 2;
+                    nB++;
+                }
+                // - is it time, and is right child a graph node?
+                if (curr->type == k + 2 && curr->R->type == GRAPH) {
+                    subtreeR.insertItem(curr->R->index, 1);
+                    curr->type = k + 3;
+                    nB++;
+                }
+                if (curr->type == k + 1) {    // - look left
+                    curr->type = k + 2;
+                    curr       = curr->L;
+                    curr->type = k + 1;
+                } else if (curr->type == k + 2) { // - look right
+                    curr->type = k + 3;
+                    curr       = curr->R;
+                    curr->type = k + 1;
+                } else {              // - else go up a level
+                    curr->type = DENDRO;
+                    curr       = curr->M;
+                    if (curr == NULL) {
+                        flag_go = false;
+                    }
+                }
+            }
+        }
+    }
+
+    // Now, we take the smaller subtree and ask how many of its
+    // emerging edges have their partner in the other subtree. O(|A| log
+    // |A|) time
+
+    edge* current;
+    int*  treeList;
+    if (nA < nB) {
+        // subtreeL is smaller
+        treeList = subtreeL.returnArrayOfKeys();
+        for (int i = 0; i < nA; i++) {
+            current = g->getNeighborList(treeList[i]);
+            // loop over each of its neighbors v_j
+            while (current != NULL) {
+                // to see if v_j is in A
+                if (subtreeR.findItem(current->x) != NULL) {
+                    count++;
+                }
+                current = current->next;
+            }
+            subtreeL.deleteItem(treeList[i]);
+        }
+        delete [] treeList;
+        treeList = subtreeR.returnArrayOfKeys();
+        for (int i = 0; i < nB; i++) {
+            subtreeR.deleteItem(treeList[i]);
+        }
+        delete [] treeList;
+    } else {
+        // subtreeR is smaller
+        treeList = subtreeR.returnArrayOfKeys();
+        for (int i = 0; i < nB; i++) {
+            current = g->getNeighborList(treeList[i]);
+            // loop over each of its neighbors v_j
+            while (current != NULL) {
+                // to see if v_j is in B
+                if (subtreeL.findItem(current->x) != NULL) {
+                    count++;
+                }
+                current = current->next;
+            }
+            subtreeR.deleteItem(treeList[i]);
+        }
+        delete [] treeList;
+        treeList = subtreeL.returnArrayOfKeys();
+        for (int i = 0; i < nA; i++) {
+            subtreeL.deleteItem(treeList[i]);
+        }
+        delete [] treeList;
+    }
+
+    return count;
+}
+
+// ***********************************************************************
+
+int dendro::countChildren(const string s) {
+    int len = s.size();
+    int numC = 0;
+    for (int i = 0; i < len; i++) {
+        if (s[i] == 'C') {
+            numC++;
+        }
+    }
+    return numC;
+}
+
+// ***********************************************************************
+
+void dendro::cullSplitHist() {
+    string* array;
+    int tot, leng;
+
+    array = splithist->returnArrayOfKeys();
+    tot   = splithist->returnTotal();
+    leng  = splithist->returnNodecount();
+    for (int i = 0; i < leng; i++) {
+        if ((splithist->returnValue(array[i]) / tot) < 0.5) {
+            splithist->deleteItem(array[i]);
+        }
+    }
+    delete [] array; array = NULL;
+
+    return;
+}
+
+// **********************************************************************
+
+elementd* dendro::findCommonAncestor(list** paths, const int i, const int j) {
+    list* headOne = paths[i];
+    list* headTwo = paths[j];
+    elementd* lastStep = NULL;
+    while (headOne->x == headTwo->x) {
+        lastStep = &internal[headOne->x];
+        headOne  = headOne->next;
+        headTwo  = headTwo->next;
+        if (headOne == NULL || headTwo == NULL) {
+            break;
+        }
+    }
+    return lastStep; // Returns address of an internal node; do not deallocate
+}
+
+// **********************************************************************
+
+int dendro::getConsensusSize() {
+    string    *array;
+    double     value, tot;
+    int  numSplits, numCons;
+    numSplits = splithist->returnNodecount();
+    array     = splithist->returnArrayOfKeys();
+    tot       = splithist->returnTotal();
+    numCons = 0;
+    for (int i = 0; i < numSplits; i++) {
+        value = splithist->returnValue(array[i]);
+        if (value / tot > 0.5) {
+            numCons++;
+        }
+    }
+    delete [] array; array = NULL;
+    return numCons;
+}
+
+// **********************************************************************
+
+splittree* dendro::getConsensusSplits() {
+    string    *array;
+    splittree *consensusTree;
+    double     value, tot;
+    consensusTree  = new splittree;
+    int numSplits;
+
+    // We look at all of the splits in our split histogram and add any
+    // one that's in the majority to our consensusTree, which we then
+    // return (note that consensusTree needs to be deallocated by the
+    // user).
+    numSplits = splithist->returnNodecount();
+    array     = splithist->returnArrayOfKeys();
+    tot       = splithist->returnTotal();
+    for (int i = 0; i < numSplits; i++) {
+        value = splithist->returnValue(array[i]);
+        if (value / tot > 0.5) {
+            consensusTree->insertItem(array[i], value / tot);
+        }
+    }
+    delete [] array; array = NULL;
+    return consensusTree;
+}
+
+// ***********************************************************************
+
+double dendro::getLikelihood() {
+    return L;
+}
+
+// ***********************************************************************
+
+void dendro::getSplitList(splittree* split_tree) {
+    string sp;
+    for (int i = 0; i < (n - 1); i++) {
+        sp = d->getSplit(i);
+        if (!sp.empty() && sp[1] != '-') {
+            split_tree->insertItem(sp, 0.0);
+        }
+    }
+    return;
+}
+
+// ***********************************************************************
+
+double dendro::getSplitTotalWeight() {
+    if (splithist) {
+        return splithist->returnTotal();
+    } else {
+        return 0;
+    }
+}
+
+// ***********************************************************************
+
+bool dendro::importDendrogramStructure(const igraph_hrg_t *hrg) {
+    n = igraph_hrg_size(hrg);
+
+    // allocate memory for G, O(n)
+    leaf = new elementd[n];
+    // allocate memory for D, O(n)
+    internal = new elementd[n - 1];
+    // allocate memory for internal edges of D, O(n)
+    d = new interns(n - 2);
+
+    // initialize leaf nodes
+    for (int i = 0; i < n; i++) {
+        leaf[i].type  = GRAPH;
+        leaf[i].label = i;
+        leaf[i].index = i;
+        leaf[i].n     = 1;
+    }
+
+    // initialize internal nodes
+    root = &internal[0];
+    root->label = 0;
+    for (int i = 1; i < n - 1; i++) {
+        internal[i].index = i;
+        internal[i].label = -1;
+    }
+
+    // import basic structure from hrg object, O(n)
+    for (int i = 0; i < n - 1; i++) {
+        int L = VECTOR(hrg->left)[i];
+        int R = VECTOR(hrg->right)[i];
+
+        if (L < 0) {
+            internal[i].L = &internal[-L - 1];
+            internal[-L - 1].M = &internal[i];
+        } else {
+            internal[i].L = &leaf[L];
+            leaf[L].M = &internal[i];
+        }
+
+        if (R < 0) {
+            internal[i].R = &internal[-R - 1];
+            internal[-R - 1].M = &internal[i];
+        } else {
+            internal[i].R = &leaf[R];
+            leaf[R].M = &internal[i];
+        }
+
+        internal[i].p = VECTOR(hrg->prob)[i];
+        internal[i].e = VECTOR(hrg->edges)[i];
+        internal[i].n = VECTOR(hrg->vertices)[i];
+        internal[i].index = i;
+    }
+
+    // --- Label all internal vertices O(n log n)
+    elementd *curr;
+    for (int i = 0; i < n; i++) {
+        curr = &leaf[i];
+        while (curr) {
+            if (curr->label == -1 || curr->label > leaf[i].label) {
+                curr->label = leaf[i].label;
+            }
+            curr = curr -> M;
+        }
+    }
+
+    // --- Exchange children to enforce order-property O(n)
+    elementd *tempe;
+    for (int i = 0; i < n - 1; i++) {
+        if (internal[i].L->label > internal[i].label) {
+            tempe          = internal[i].L;
+            internal[i].L  = internal[i].R;
+            internal[i].R  = tempe;
+        }
+    }
+
+    // --- Tabulate internal dendrogram edges O(n)
+    for (int i = 0; i < (n - 1); i++) {
+        if (internal[i].L->type == DENDRO) {
+            d->addEdge(i, internal[i].L->index, LEFT);
+        }
+        if (internal[i].R->type == DENDRO) {
+            d->addEdge(i, internal[i].R->index, RIGHT);
+        }
+    }
+
+    // --- Compute p_i for each internal node O(n)
+    // Each internal node's p_i = e_i / (nL_i*nR_i), and now that we
+    // have each of those pieces, we may calculate this value for each
+    // internal node. Given these, we can then calculate the
+    // log-likelihood of the entire dendrogram structure
+    // \log(L) = \sum_{i=1}^{n} ( ( e_i \log[p_i] ) +
+    // ( (nL_i*nR_i - e_i) \log[1-p_i] ) )
+    L = 0.0; double dL;
+    int nL_nR, ei;
+    for (int i = 0; i < (n - 1); i++) {
+        nL_nR = internal[i].L->n * internal[i].R->n;
+        ei    = internal[i].e;
+        if (ei == 0 || ei == nL_nR) {
+            dL = 0.0;
+        } else {
+            dL = (double)(ei) * log(internal[i].p) +
+                 (double)(nL_nR - ei) * log(1.0 - internal[i].p);
+        }
+        internal[i].logL = dL;
+        L += dL;
+    }
+
+    return true;
+}
+
+// ***********************************************************************
+
+void dendro::makeRandomGraph() {
+    if (g != NULL) {
+        delete g;
+    } g = NULL; g = new graph(n);
+
+    list *curr, *prev;
+    if (paths) {
+        for (int i = 0; i < n; i++) {
+            curr = paths[i];
+            while (curr != NULL) {
+                prev = curr;
+                curr = curr->next;
+                delete prev;
+                prev = NULL;
+            }
+            paths[i] = NULL;
+        }
+        delete [] paths;
+    }
+// build paths from root O(n d)
+    paths = new list* [n];
+    for (int i = 0; i < n; i++) {
+        paths[i] = reversePathToRoot(i);
+    }
+
+    elementd* commonAncestor;
+// O((h+d)*n^2) - h: height of D; d: average degree in G
+    for (int i = 0; i < n; i++) {
+        // decide neighbors of v_i
+        for (int j = (i + 1); j < n; j++) {
+            commonAncestor = findCommonAncestor(paths, i, j);
+            if (RNG_UNIF01() < commonAncestor->p) {
+                if (!(g->doesLinkExist(i, j))) {
+                    g->addLink(i, j);
+                }
+                if (!(g->doesLinkExist(j, i))) {
+                    g->addLink(j, i);
+                }
+            }
+        }
+    }
+
+    for (int i = 0; i < n; i++) {
+        curr = paths[i];
+        while (curr != NULL) {
+            prev = curr;
+            curr = curr->next;
+            delete prev;
+            prev = NULL;
+        }
+        paths[i] = NULL;
+    }
+    delete [] paths; // delete paths data structure O(n log n)
+    paths = NULL;
+
+    return;
+}
+
+// **********************************************************************
+
+bool dendro::monteCarloMove(double& delta, bool& ftaken, const double T) {
+    // A single MC move begins with the selection of a random internal
+    // edge (a,b) of the dendrogram. This also determines the three
+    // subtrees i, j, k that we will rearrange, and we choose uniformly
+    // from among the options.
+    //
+    // If (a,b) is a left-edge, then we have ((i,j),k), and moves
+    // ((i,j),k) -> ((i,k),j) (alpha move)
+    //           -> (i,(j,k)) + enforce order-property for (j,k) (beta move)
+    //
+    // If (a,b) is a right-edge, then we have (i,(j,k)), and moves
+    // (i,(j,k)) -> ((i,k),j) (alpha move)
+    //           -> ((i,j),k) (beta move)
+    //
+    // For each of these moves, we need to know what the change in
+    // likelihood will be, so that we can determine with what
+    // probability we execute the move.
+
+    elementd *temp;
+    ipair *tempPair;
+    int x, y, e_x, e_y, n_i, n_j, n_k, n_x, n_y;
+    short int t;
+    double p_x, p_y, L_x, L_y, dLogL;
+    string new_split;
+
+    // The remainder of the code executes a single MCMC move, where we
+    // sample the dendrograms proportionally to their likelihoods (i.e.,
+    // temperature=1, if you're comparing it to the usual MCMC
+    // framework).
+
+    delta    = 0.0;
+    ftaken   = false;
+    tempPair = d->getRandomEdge(); // returns address; no need to deallocate
+    x        = tempPair->x;        // copy contents of referenced random edge
+    y        = tempPair->y;        // into local variables
+    t        = tempPair->t;
+
+    if (t == LEFT) {
+        if (RNG_UNIF01() < 0.5) { // ## LEFT ALPHA move: ((i,j),k) -> ((i,k),j)
+            // We need to calculate the change in the likelihood (dLogL)
+            // that would result from this move. Most of the information
+            // needed to do this is already available, the exception being
+            // e_ik, the number of edges that span the i and k subtrees. I
+            // use a slow algorithm O(n) to do this, since I don't know of a
+            // better way at this point. (After several attempts to find a
+            // faster method, no luck.)
+
+            n_i = internal[y].L->n;
+            n_j = internal[y].R->n;
+            n_k = internal[x].R->n;
+
+            n_y = n_i * n_k;
+            e_y = computeEdgeCount(internal[y].L->index, internal[y].L->type,
+                                   internal[x].R->index, internal[x].R->type);
+            p_y  = (double)(e_y) / (double)(n_y);
+            if (e_y == 0 || e_y == n_y) {
+                L_y = 0.0;
+            } else {
+                L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0 - p_y);
+            }
+
+            n_x  = (n_i + n_k) * n_j;
+            e_x  = internal[x].e + internal[y].e - e_y; // e_yj
+            p_x  = (double)(e_x) / (double)(n_x);
+            if (e_x == 0 || e_x == n_x) {
+                L_x = 0.0;
+            } else {
+                L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0 - p_x);
+            }
+
+            dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL);
+            if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T * dLogL))) {
+
+                // make LEFT ALPHA move
+
+                ftaken = true;
+                d->swapEdges(x, internal[x].R->index, RIGHT, y,
+                             internal[y].R->index, RIGHT);
+                temp             = internal[x].R; // - swap j and k
+                internal[x].R    = internal[y].R;
+                internal[y].R    = temp;
+                internal[x].R->M = &internal[x];  // - adjust parent pointers
+                internal[y].R->M = &internal[y];
+                internal[y].n    = n_i + n_k;     // - update n for [y]
+                internal[x].e    = e_x;           // - update e_i for [x] and [y]
+                internal[y].e    = e_y;
+                internal[x].p    = p_x;           // - update p_i for [x] and [y]
+                internal[y].p    = p_y;
+                internal[x].logL = L_x;           // - update L_i for [x] and [y]
+                internal[y].logL = L_y;
+                // - order-property maintained
+                L  += dLogL;                  // - update LogL
+                delta            = dLogL;
+
+            }
+        } else {
+
+            // ## LEFT BETA move:  ((i,j),k) -> (i,(j,k))
+
+            n_i = internal[y].L->n;
+            n_j = internal[y].R->n;
+            n_k = internal[x].R->n;
+
+            n_y  = n_j * n_k;
+            e_y  = computeEdgeCount(internal[y].R->index, internal[y].R->type,
+                                    internal[x].R->index, internal[x].R->type);
+            p_y  = (double)(e_y) / (double)(n_y);
+            if (e_y == 0 || e_y == n_y)   {
+                L_y = 0.0;
+            } else {
+                L_y = (double)(e_y) * log(p_y) +
+                      (double)(n_y - e_y) * log(1.0 - p_y);
+            }
+
+            n_x  = (n_j + n_k) * n_i;
+            e_x  = internal[x].e + internal[y].e - e_y; // e_yj
+            p_x  = (double)(e_x) / (double)(n_x);
+            if (e_x == 0 || e_x == n_x) {
+                L_x = 0.0;
+            } else {
+                L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0 - p_x);
+            }
+
+            dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL);
+            if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T * dLogL))) {
+
+                // make LEFT BETA move
+
+                ftaken = true;
+                d->swapEdges(y, internal[y].L->index, LEFT, y,
+                             internal[y].R->index, RIGHT);
+                temp   = internal[y].L;       // - swap L and R of [y]
+                internal[y].L    = internal[y].R;
+                internal[y].R    = temp;
+                d->swapEdges(x, internal[x].R->index, RIGHT,
+                             y, internal[y].R->index, RIGHT);
+                temp   = internal[x].R;       // - swap i and k
+                internal[x].R    = internal[y].R;
+                internal[y].R    = temp;
+                internal[x].R->M = &internal[x];  // - adjust parent pointers
+                internal[y].R->M = &internal[y];
+                d->swapEdges(x, internal[x].L->index, LEFT,
+                             x, internal[x].R->index, RIGHT);
+                temp   = internal[x].L;       // - swap L and R of [x]
+                internal[x].L    = internal[x].R;
+                internal[x].R    = temp;
+                internal[y].n    = n_j + n_k;     // - update n
+                internal[x].e    = e_x;       // - update e_i
+                internal[y].e    = e_y;
+                internal[x].p    = p_x;           // - update p_i
+                internal[y].p    = p_y;
+                internal[x].logL = L_x;           // - update logL_i
+                internal[y].logL = L_y;
+                if (internal[y].R->label < internal[y].L->label) {
+                    // - enforce order-property if necessary
+                    d->swapEdges(y, internal[y].L->index, LEFT,
+                                 y, internal[y].R->index, RIGHT);
+                    temp = internal[y].L;
+                    internal[y].L = internal[y].R;
+                    internal[y].R = temp;
+                } //
+                internal[y].label = internal[y].L->label;
+                L += dLogL;        // - update LogL
+                delta = dLogL;
+            }
+        }
+    } else {
+
+        // right-edge: t == RIGHT
+
+        if (RNG_UNIF01() < 0.5) {
+
+            // alpha move: (i,(j,k)) -> ((i,k),j)
+
+            n_i = internal[x].L->n;
+            n_j = internal[y].L->n;
+            n_k = internal[y].R->n;
+
+            n_y  = n_i * n_k;
+            e_y  = computeEdgeCount(internal[x].L->index, internal[x].L->type,
+                                    internal[y].R->index, internal[y].R->type);
+            p_y  = (double)(e_y) / (double)(n_y);
+            if (e_y == 0 || e_y == n_y)   {
+                L_y = 0.0;
+            } else {
+                L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0 - p_y);
+            }
+
+            n_x  = (n_i + n_k) * n_j;
+            e_x  = internal[x].e + internal[y].e - e_y; // e_yj
+            p_x  = (double)(e_x) / (double)(n_x);
+            if (e_x == 0 || e_x == n_x) {
+                L_x = 0.0;
+            } else {
+                L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0 - p_x);
+            }
+
+            dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL);
+            if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T * dLogL))) {
+
+                // make RIGHT ALPHA move
+
+                ftaken = true;
+                d->swapEdges(x, internal[x].L->index, LEFT,
+                             x, internal[x].R->index, RIGHT);
+                temp    = internal[x].L;       // - swap L and R of [x]
+                internal[x].L     = internal[x].R;
+                internal[x].R     = temp;
+                d->swapEdges(y, internal[y].L->index, LEFT,
+                             x, internal[x].R->index, RIGHT);
+                temp    = internal[y].L;       // - swap i and j
+                internal[y].L     = internal[x].R;
+                internal[x].R     = temp;
+                internal[x].R->M  = &internal[x];  // - adjust parent pointers
+                internal[y].L->M  = &internal[y];
+                internal[y].n     = n_i + n_k;     // - update n
+                internal[x].e     = e_x;       // - update e_i
+                internal[y].e     = e_y;
+                internal[x].p     = p_x;           // - update p_i
+                internal[y].p     = p_y;
+                internal[x].logL  = L_x;           // - update logL_i
+                internal[y].logL  = L_y;
+                internal[y].label = internal[x].label; // - update order property
+                L   += dLogL;                  // - update LogL
+                delta             = dLogL;
+            }
+        } else {
+
+            // beta move:  (i,(j,k)) -> ((i,j),k)
+
+            n_i = internal[x].L->n;
+            n_j = internal[y].L->n;
+            n_k = internal[y].R->n;
+
+            n_y  = n_i * n_j;
+            e_y  = computeEdgeCount(internal[x].L->index, internal[x].L->type,
+                                    internal[y].L->index, internal[y].L->type);
+            p_y  = (double)(e_y) / (double)(n_y);
+            if (e_y == 0 || e_y == n_y)   {
+                L_y = 0.0;
+            } else {
+                L_y = (double)(e_y) * log(p_y) + (double)(n_y - e_y) * log(1.0 - p_y);
+            }
+
+            n_x  = (n_i + n_j) * n_k;
+            e_x  = internal[x].e + internal[y].e - e_y; // e_yk
+            p_x  = (double)(e_x) / (double)(n_x);
+            if (e_x == 0 || e_x == n_x) {
+                L_x = 0.0;
+            } else {
+                L_x = (double)(e_x) * log(p_x) + (double)(n_x - e_x) * log(1.0 - p_x);
+            }
+
+            dLogL = (L_x - internal[x].logL) + (L_y - internal[y].logL);
+            if ((dLogL > 0.0) || (RNG_UNIF01() < exp(T * dLogL))) {
+
+                // make RIGHT BETA move
+
+                ftaken = true;
+                d->swapEdges(x, internal[x].L->index, LEFT,
+                             x, internal[x].R->index, RIGHT);
+                temp    = internal[x].L;       // - swap L and R of [x]
+                internal[x].L     = internal[x].R;
+                internal[x].R     = temp;
+                d->swapEdges(x, internal[x].R->index, RIGHT,
+                             y, internal[y].R->index, RIGHT);
+                temp    = internal[x].R;       // - swap i and k
+                internal[x].R     = internal[y].R;
+                internal[y].R     = temp;
+                internal[x].R->M  = &internal[x];  // - adjust parent pointers
+                internal[y].R->M  = &internal[y];
+                d->swapEdges(y, internal[y].L->index, LEFT,
+                             y, internal[y].R->index, RIGHT);
+                temp    = internal[y].L;       // - swap L and R of [y]
+                internal[y].L     = internal[y].R;
+                internal[y].R     = temp;
+                internal[y].n     = n_i + n_j;     // - update n
+                internal[x].e     = e_x;       // - update e_i
+                internal[y].e     = e_y;
+                internal[x].p     = p_x;       // - update p_i
+                internal[y].p     = p_y;
+                internal[x].logL  = L_x;       // - update logL_i
+                internal[y].logL  = L_y;
+                internal[y].label = internal[x].label; // - order-property
+                L   += dLogL;                  // - update LogL
+                delta             = dLogL;
+            }
+        }
+    }
+    return true;
+}
+
+// **********************************************************************
+
+void dendro::refreshLikelihood() {
+    // recalculates the log-likelihood of the dendrogram structure
+    L = 0.0; double dL;
+    int nL_nR, ei;
+    for (int i = 0; i < (n - 1); i++) {
+        nL_nR = internal[i].L->n * internal[i].R->n;
+        ei    = internal[i].e;
+        internal[i].p = (double)(ei) / (double)(nL_nR);
+        if (ei == 0 || ei == nL_nR) {
+            dL = 0.0;
+        } else {
+            dL = ei * log(internal[i].p) + (nL_nR - ei) * log(1.0 - internal[i].p);
+        }
+        internal[i].logL = dL;
+        L += dL;
+    }
+    return;
+}
+
+// **********************************************************************
+
+void dendro::QsortMain (block* array, int left, int right) {
+    if (right > left) {
+        int pivot = left;
+        int part  = QsortPartition(array, left, right, pivot);
+        QsortMain(array, left,   part - 1);
+        QsortMain(array, part + 1, right  );
+    }
+    return;
+}
+
+int dendro::QsortPartition (block* array, int left, int right, int index) {
+    block p_value, temp;
+    p_value.x = array[index].x;
+    p_value.y = array[index].y;
+
+    // swap(array[p_value], array[right])
+    temp.x = array[right].x;
+    temp.y = array[right].y;
+    array[right].x = array[index].x;
+    array[right].y = array[index].y;
+    array[index].x = temp.x;
+    array[index].y = temp.y;
+
+    int stored = left;
+    for (int i = left; i < right; i++) {
+        if (array[i].x <= p_value.x) {
+            // swap(array[stored], array[i])
+            temp.x = array[i].x;
+            temp.y = array[i].y;
+            array[i].x = array[stored].x;
+            array[i].y = array[stored].y;
+            array[stored].x = temp.x;
+            array[stored].y = temp.y;
+            stored++;
+        }
+    }
+    // swap(array[right], array[stored])
+    temp.x = array[stored].x;
+    temp.y = array[stored].y;
+    array[stored].x = array[right].x;
+    array[stored].y = array[right].y;
+    array[right].x = temp.x;
+    array[right].y  = temp.y;
+
+    return stored;
+}
+
+void dendro::recordConsensusTree(igraph_vector_t *parents,
+                                 igraph_vector_t *weights) {
+
+    keyValuePairSplit *curr, *prev;
+    child *newChild;
+    int orig_nodes = g->numNodes();
+
+    // First, cull the split hist so that only splits with weight >= 0.5
+    // remain
+    cullSplitHist();
+    int treesize = splithist->returnNodecount();
+
+    // Now, initialize the various arrays we use to keep track of the
+    // internal structure of the consensus tree.
+    ctree  = new cnode[treesize];
+    cancestor = new int[n];
+    for (int i = 0; i < treesize; i++) {
+        ctree[i].index = i;
+    }
+    for (int i = 0; i < n; i++)        {
+        cancestor[i]   = -1;
+    }
+    int ii = 0;
+
+    // To build the majority consensus tree, we do the following: For
+    // each possible number of Ms in the split string (a number that
+    // ranges from n-2 down to 0), and for each split with that number
+    // of Ms, we create a new internal node of the tree, and connect the
+    // oldest ancestor of each C to that node (at most once). Then, we
+    // update our list of oldest ancestors to reflect this new join, and
+    // proceed.
+    for (int i = n - 2; i >= 0; i--) {
+        // First, we get a list of all the splits with this exactly i Ms
+        curr = splithist->returnTheseSplits(i);
+
+        // Now we loop over that list
+        while (curr != NULL) {
+            splithist->deleteItem(curr->x);
+            // add weight to this internal node
+            ctree[ii].weight = curr->y;
+            // examine each letter of this split
+            for (int j = 0; j < n; j++) {
+                if (curr->x[j] == 'C') {
+                    // - node is child of this internal node
+                    if (cancestor[j] == -1) {
+                        // - first time this leaf has ever been seen
+                        newChild        = new child;
+                        newChild->type  = GRAPH;
+                        newChild->index = j;
+                        newChild->next  = NULL;
+                        // - attach child to list
+                        if (ctree[ii].lastChild == NULL) {
+                            ctree[ii].children  = newChild;
+                            ctree[ii].lastChild = newChild;
+                            ctree[ii].degree    = 1;
+                        } else {
+                            ctree[ii].lastChild->next = newChild;
+                            ctree[ii].lastChild       = newChild;
+                            ctree[ii].degree   += 1;
+                        }
+                    } else {
+                        // - this leaf has been seen before
+                        // If the parent of the ancestor of this leaf is the
+                        // current internal node then this leaf is already a
+                        // descendant of this internal node, and we can move on;
+                        // otherwise, we need to add that ancestor to this
+                        // internal node's child list, and update various
+                        // relations
+                        if (ctree[cancestor[j]].parent != ii) {
+                            ctree[cancestor[j]].parent = ii;
+                            newChild        = new child;
+                            newChild->type  = DENDRO;
+                            newChild->index = cancestor[j];
+                            newChild->next  = NULL;
+                            // - attach child to list
+                            if (ctree[ii].lastChild == NULL) {
+                                ctree[ii].children  = newChild;
+                                ctree[ii].lastChild = newChild;
+                                ctree[ii].degree    = 1;
+                            } else {
+                                ctree[ii].lastChild->next = newChild;
+                                ctree[ii].lastChild       = newChild;
+                                ctree[ii].degree   += 1;
+                            }
+                        }
+                    }
+                    // note new ancestry for this leaf
+                    cancestor[j] = ii;
+                }
+            }
+            // update internal node index
+            ii++;
+            prev = curr;
+            curr = curr->next;
+            delete prev;
+        }
+    }
+
+    // Return the consensus tree
+    igraph_vector_resize(parents, ii + orig_nodes);
+    if (weights) {
+        igraph_vector_resize(weights, ii);
+    }
+
+    for (int i = 0; i < ii; i++) {
+        child *sat, *sit = ctree[i].children;
+        while (sit) {
+            VECTOR(*parents)[orig_nodes + i] =
+                ctree[i].parent < 0 ? -1 : orig_nodes + ctree[i].parent;
+            if (sit->type == GRAPH) {
+                VECTOR(*parents)[sit->index] = orig_nodes + i;
+            }
+            sat = sit;
+            sit = sit->next;
+            delete sat;
+        }
+        if (weights) {
+            VECTOR(*weights)[i] = ctree[i].weight;
+        }
+        ctree[i].children = 0;
+    }
+
+    // Plus the isolate nodes
+    for (int i = 0; i < n; i++) {
+        if (cancestor[i] == -1) {
+            VECTOR(*parents)[i] = -1;
+        }
+    }
+
+
+}
+
+// **********************************************************************
+
+void dendro::recordDendrogramStructure(igraph_hrg_t *hrg) {
+    for (int i = 0; i < n - 1; i++) {
+        int li = internal[i].L->index;
+        int ri = internal[i].R->index;
+        VECTOR(hrg->left )[i] = internal[i].L->type == DENDRO ? -li - 1 : li;
+        VECTOR(hrg->right)[i] = internal[i].R->type == DENDRO ? -ri - 1 : ri;
+        VECTOR(hrg->prob )[i] = internal[i].p;
+        VECTOR(hrg->edges)[i] = internal[i].e;
+        VECTOR(hrg->vertices)[i] = internal[i].n;
+    }
+}
+
+void dendro::recordGraphStructure(igraph_t *graph) {
+    igraph_vector_t edges;
+    int no_of_nodes = g->numNodes();
+    int no_of_edges = g->numLinks() / 2;
+    int idx = 0;
+
+    igraph_vector_init(&edges, no_of_edges * 2);
+    IGRAPH_FINALLY(igraph_vector_destroy, &edges);
+
+    for (int i = 0; i < n; i++) {
+        edge *curr = g->getNeighborList(i);
+        while (curr) {
+            if (i < curr->x) {
+                VECTOR(edges)[idx++] = i;
+                VECTOR(edges)[idx++] = curr->x;
+            }
+            curr = curr->next;
+        }
+    }
+
+    igraph_create(graph, &edges, no_of_nodes, /* directed= */ 0);
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+}
+
+// **********************************************************************
+
+list* dendro::reversePathToRoot(const int leafIndex) {
+    list *head, *subhead, *newlist;
+    head = subhead = newlist = NULL;
+    elementd *current = &leaf[leafIndex];
+
+    // continue until we're finished
+    while (current != NULL) {
+        // add this node to the path
+        newlist = new list;
+        newlist->x = current->index;
+        newlist->next = NULL;
+        if (head == NULL) {
+            head    = newlist;
+        } else {
+            subhead = head;
+            head = newlist;
+            head->next = subhead;
+        }
+        current = current->M;
+    }
+    return head;
+}
+
+// ***********************************************************************
+
+bool dendro::sampleSplitLikelihoods(int &sample_num) {
+    // In order to compute the majority agreement dendrogram at
+    // equilibrium, we need to calculate the leaf partition defined by
+    // each split (internal edge) of the tree. Because splits are only
+    // defined on a Cayley tree, the buildSplit() function returns the
+    // default "--...--"  string for the root and the root's left
+    // child. When tabulating the frequency of splits, one of these
+    // needs to be excluded.
+
+    IGRAPH_UNUSED(sample_num);
+
+    string* array;
+    int     k;
+    double  tot;
+
+    string new_split;
+    // To decompose the tree into its splits, we simply loop over all
+    // the internal nodes and replace the old split for the ith internal
+    // node with its new split. This is a bit time consuming to do
+    // O(n^2), so try not to do this very often. Once the decomposition
+    // is had, we insert them into the split histogram, which tracks the
+    // cumulative weight for each respective split observed.
+
+    if (splithist == NULL) {
+        splithist = new splittree;
+    }
+    for (int i = 0; i < (n - 1); i++) {
+        new_split = buildSplit(&internal[i]);
+        d->replaceSplit(i, new_split);
+        if (!new_split.empty() && new_split[1] != '-') {
+            if (!splithist->insertItem(new_split, 1.0)) {
+                return false;
+            }
+        }
+    }
+    splithist->finishedThisRound();
+
+    // For large graphs, the split histogram can get extremely large, so
+    // we need to employ some measures to prevent it from swamping the
+    // available memory. When the number of splits exceeds  a threshold
+    // (say, a million), we progressively delete splits that have a
+    // weight less than  a rising (k*0.001 of the total weight) fraction
+    // of the splits, on the assumption that losing such weight is
+    // unlikely to effect the ultimate split statistics. This deletion
+    // procedure is slow O(m lg m), but should only happen very rarely.
+
+    int split_max = n * 500;
+    int leng;
+    if (splithist->returnNodecount() > split_max) {
+        k = 1;
+        while (splithist->returnNodecount() > split_max) {
+            array = splithist->returnArrayOfKeys();
+            tot   = splithist->returnTotal();
+            leng  = splithist->returnNodecount();
+            for (int i = 0; i < leng; i++) {
+                if ((splithist->returnValue(array[i]) / tot) < k * 0.001) {
+                    splithist->deleteItem(array[i]);
+                }
+            }
+            delete [] array; array = NULL;
+            k++;
+        }
+    }
+
+    return true;
+}
+
+void dendro::sampleAdjacencyLikelihoods() {
+    // Here, we sample the probability values associated with every
+    // adjacency in A, weighted by their likelihood. The weighted
+    // histogram is stored in the graph data structure, so we simply
+    // need to add an observation to each node-pair that corresponds to
+    // the associated branch point's probability and the dendrogram's
+    // overall likelihood.
+
+    double nn;
+    double norm = ((double)(n) * (double)(n)) / 4.0;
+
+    if (L > 0.0) {
+        L = 0.0;
+    }
+    elementd* ancestor;
+    list *currL, *prevL;
+    if (paths != NULL) {
+        for (int i = 0; i < n; i++) {
+            currL = paths[i];
+            while (currL != NULL) {
+                prevL = currL;
+                currL = currL->next;
+                delete prevL;
+                prevL = NULL;
+            }
+            paths[i] = NULL;
+        }
+        delete [] paths;
+    }
+    paths = NULL;
+    paths = new list* [n];
+    for (int i = 0; i < n; i++) {
+        // construct paths from root, O(n^2) at worst
+        paths[i] = reversePathToRoot(i);
+    }
+
+    // add obs for every node-pair, always O(n^2)
+    for (int i = 0; i < n; i++) {
+        for (int j = i + 1; j < n; j++) {
+            // find internal node, O(n) at worst
+            ancestor = findCommonAncestor(paths, i, j);
+            nn = ((double)(ancestor->L->n) * (double)(ancestor->R->n)) / norm;
+            // add obs of ->p to (i,j) histogram, and
+            g->addAdjacencyObs(i, j, ancestor->p, nn);
+            // add obs of ->p to (j,i) histogram
+            g->addAdjacencyObs(j, i, ancestor->p, nn);
+        }
+    }
+
+    // finish-up: upate total weight in histograms
+    g->addAdjacencyEnd();
+
+    return;
+}
+
+void dendro::resetDendrograph() {
+    // Reset the dendrograph structure for the next trial
+    if (leaf      != NULL) {
+        delete [] leaf;        // O(n)
+        leaf      = NULL;
+    }
+    if (internal  != NULL) {
+        delete [] internal;    // O(n)
+        internal  = NULL;
+    }
+    if (d         != NULL) {
+        delete d;              // O(n)
+        d         = NULL;
+    }
+    root = NULL;
+    if (paths != NULL) {
+        list *curr, *prev;
+        for (int i = 0; i < n; i++) {
+            curr = paths[i];
+            while (curr != NULL) {
+                prev = curr;
+                curr = curr->next;
+                delete prev;
+                prev = NULL;
+            }
+            paths[i] = NULL;
+        }
+        delete [] paths;
+    }
+    paths = NULL;
+    L = 1.0;
+
+    return;
+}
+
+// **********************************************************************
+// *** COPYRIGHT NOTICE *************************************************
+// graph.h - graph data structure for hierarchical random graphs
+// Copyright (C) 2005-2008 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// **********************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu |
+//                                 http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark E.J. Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science
+//                AND Santa Fe Institute
+// Created      : 8 November 2005
+// Modified     : 23 December 2007 (cleaned up for public consumption)
+//
+// ***********************************************************************
+//
+// Graph data structure for hierarchical random graphs. The basic
+// structure is an adjacency list of edges; however, many additional
+// pieces of metadata are stored as well. Each node stores its
+// external name, its degree and (if assigned) its group index.
+//
+// ***********************************************************************
+
+// ******** Constructor / Destructor *************************************
+
+graph::graph(const int size, bool predict) : predict(predict)  {
+    n = size;
+    m = 0;
+    nodes = new vert  [n];
+    nodeLink = new edge* [n];
+    nodeLinkTail   = new edge* [n];
+    for (int i = 0; i < n; i++) {
+        nodeLink[i] = NULL;
+        nodeLinkTail[i] = NULL;
+    }
+    if (predict) {
+        A = new double** [n];
+        for (int i = 0; i < n; i++) {
+            A[i] = new double* [n];
+        }
+        obs_count = 0;
+        total_weight = 0.0;
+        bin_resolution = 0.0;
+        num_bins = 0;
+    }
+}
+
+graph::~graph() {
+    edge *curr, *prev;
+    for (int i = 0; i < n; i++) {
+        curr = nodeLink[i];
+        while (curr != NULL) {
+            prev = curr;
+            curr = curr->next;
+            delete prev;
+        }
+    }
+    delete [] nodeLink; nodeLink = NULL;
+    delete [] nodeLinkTail;  nodeLinkTail   = NULL;
+    delete [] nodes; nodes = NULL;
+
+    if (predict) {
+        for (int i = 0; i < n; i++) {
+            for (int j = 0; j < n; j++) {
+                delete [] A[i][j];
+            }
+            delete [] A[i];
+        }
+        delete [] A; A = NULL;
+    }
+}
+
+// **********************************************************************
+
+bool graph::addLink(const int i, const int j) {
+    // Adds the directed edge (i,j) to the adjacency list for v_i
+    edge* newedge;
+    if (i >= 0 && i < n && j >= 0 && j < n) {
+        newedge  = new edge;
+        newedge->x = j;
+        if (nodeLink[i] == NULL) {
+            // first neighbor
+            nodeLink[i]  = newedge;
+            nodeLinkTail[i] = newedge;
+            nodes[i].degree = 1;
+        } else {
+            // subsequent neighbor
+            nodeLinkTail[i]->next = newedge;
+            nodeLinkTail[i]       = newedge;
+            nodes[i].degree++;
+        }
+        // increment edge count
+        m++;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// ***********************************************************************
+
+bool graph::addAdjacencyObs(const int i, const int j,
+                            const double probability, const double size) {
+    // Adds the observation obs to the histogram of the edge (i,j)
+    // Note: user must manually add observation to edge (j,i) by calling
+    // this function with that argument
+    if (bin_resolution > 0.0 && probability >= 0.0 && probability <= 1.0
+        && size >= 0.0 && size <= 1.0
+        && i >= 0 && i < n && j >= 0 && j < n) {
+        int index = (int)(probability / bin_resolution + 0.5);
+        if (index < 0) {
+            index = 0;
+        } else if (index > num_bins) {
+            index = num_bins;
+        }
+
+        // Add the weight to the proper probability bin
+        if (A[i][j][index] < 0.5) {
+            A[i][j][index] = 1.0;
+        } else {
+            A[i][j][index] += 1.0;
+        }
+        return true;
+    }
+    return false;
+}
+
+// **********************************************************************
+
+void graph::addAdjacencyEnd() {
+    // We need to also keep a running total of how much weight has been added
+    // to the histogram, and the number of observations in the histogram.
+    if (obs_count == 0) {
+        total_weight  = 1.0; obs_count = 1;
+    } else {
+        total_weight += 1.0; obs_count++;
+    }
+    return;
+}
+
+bool graph::doesLinkExist(const int i, const int j) {
+    // This function determines if the edge (i,j) already exists in the
+    // adjacency list of v_i
+    edge* curr;
+    if (i >= 0 && i < n && j >= 0 && j < n) {
+        curr = nodeLink[i];
+        while (curr != NULL) {
+            if (curr->x == j) {
+                return true;
+            }
+            curr = curr->next;
+        }
+    }
+    return false;
+}
+
+// **********************************************************************
+
+int graph::getDegree(const int i) {
+    if (i >= 0 && i < n) {
+        return nodes[i].degree;
+    } else {
+        return -1;
+    }
+}
+
+string graph::getName(const int i)  {
+    if (i >= 0 && i < n) {
+        return nodes[i].name;
+    } else {
+        return "";
+    }
+}
+
+// NOTE: Returns address; deallocation of returned object is dangerous
+edge* graph::getNeighborList(const int i) {
+    if (i >= 0 && i < n) {
+        return nodeLink[i];
+    } else {
+        return NULL;
+    }
+}
+
+double* graph::getAdjacencyHist(const int i, const int j) {
+    if (i >= 0 && i < n && j >= 0 && j < n) {
+        return A[i][j];
+    } else {
+        return NULL;
+    }
+}
+
+// **********************************************************************
+
+double graph::getAdjacencyAverage(const int i, const int j) {
+    double average = 0.0;
+    if (i != j) {
+        for (int k = 0; k < num_bins; k++) {
+            if (A[i][j][k] > 0.0) {
+                average += (A[i][j][k] / total_weight) * ((double)(k) * bin_resolution);
+            }
+        }
+    }
+    return average;
+}
+
+int graph::numLinks() {
+    return m;
+}
+
+int graph::numNodes() {
+    return n;
+}
+
+double graph::getBinResolution() {
+    return bin_resolution;
+}
+
+int graph::getNumBins() {
+    return num_bins;
+}
+
+double graph::getTotalWeight() {
+    return total_weight;
+}
+
+// ***********************************************************************
+
+void graph::resetAllAdjacencies() {
+    for (int i = 0; i < n; i++) {
+        for (int j = 0; j < n; j++) {
+            for (int k = 0; k < num_bins; k++) {
+                A[i][j][k] = 0.0;
+            }
+        }
+    }
+    obs_count    = 0;
+    total_weight = 0.0;
+    return;
+}
+
+// **********************************************************************
+
+void graph::resetAdjacencyHistogram(const int i, const int j) {
+    if (i >= 0 && i < n && j >= 0 && j < n) {
+        for (int k = 0; k < num_bins; k++) {
+            A[i][j][k] = 0.0;
+        }
+    }
+    return;
+}
+
+// **********************************************************************
+
+void graph::resetLinks() {
+    edge *curr, *prev;
+    for (int i = 0; i < n; i++) {
+        curr = nodeLink[i];
+        while (curr != NULL) {
+            prev = curr;
+            curr = curr->next;
+            delete prev;
+        }
+        nodeLink[i]     = NULL;
+        nodeLinkTail[i] = NULL;
+        nodes[i].degree = 0;
+    }
+    m = 0;
+    return;
+}
+
+// **********************************************************************
+
+void graph::setAdjacencyHistograms(const int bin_count) {
+    // For all possible adjacencies, setup an edge histograms
+    num_bins = bin_count + 1;
+    bin_resolution = 1.0 / (double)(bin_count);
+    for (int i = 0; i < n; i++) {
+        for (int j = 0; j < n; j++) {
+            A[i][j] = new double [num_bins];
+            for (int k = 0; k < num_bins; k++) {
+                A[i][j][k] = 0.0;
+            }
+        }
+    }
+    return;
+}
+
+bool graph::setName(const int i, const string text) {
+    if (i >= 0 && i < n) {
+        nodes[i].name = text;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// **********************************************************************
+
+interns::interns(const int n)  {
+    q         = n;
+    count     = 0;
+    edgelist  = new ipair  [q];
+    splitlist = new string [q + 1];
+    indexLUT  = new int*   [q + 1];
+    for (int i = 0; i < (q + 1); i++) {
+        indexLUT[i]    = new int [2];
+        indexLUT[i][0] = indexLUT[i][1] = -1;
+    }
+}
+interns::~interns() {
+    delete [] edgelist;
+    delete [] splitlist;
+    for (int i = 0; i < (q + 1); i++) {
+        delete [] indexLUT[i];
+    }
+    delete [] indexLUT;
+}
+
+// ***********************************************************************
+
+// NOTE: Returns an address to another object -- do not deallocate
+ipair* interns::getEdge(const int i) {
+    return &edgelist[i];
+}
+
+// ***********************************************************************
+
+// NOTE: Returns an address to another object -- do not deallocate
+ipair* interns::getRandomEdge() {
+    return &edgelist[(int)(floor((double)(q) * RNG_UNIF01()))];
+}
+
+// ***********************************************************************
+
+string interns::getSplit(const int i) {
+    if (i >= 0 && i <= q) {
+        return splitlist[i];
+    } else {
+        return "";
+    }
+}
+
+// **********************************************************************
+
+bool interns::addEdge(const int new_x, const int new_y,
+                      const short int new_type) {
+    // This function adds a new edge (i,j,t,sp) to the list of internal
+    // edges. After checking that the inputs fall in the appropriate
+    // range of values, it records the new edgelist index in the
+    // indexLUT and then puts the input values into that edgelist
+    // location.
+
+    if (count < q && new_x >= 0 && new_x < (q + 1) && new_y >= 0 &&
+        new_y < (q + 2) && (new_type == LEFT || new_type == RIGHT)) {
+        if (new_type == LEFT) {
+            indexLUT[new_x][0] = count;
+        } else {
+            indexLUT[new_x][1] = count;
+        }
+        edgelist[count].x = new_x;
+        edgelist[count].y = new_y;
+        edgelist[count].t = new_type;
+        count++;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// **********************************************************************
+
+bool interns::replaceSplit(const int i, const string sp) {
+    // When an internal edge is changed, its split must be replaced as
+    // well. This function provides that access; it stores the split
+    // defined by an internal edge (x,y) at the location [y], which
+    // is unique.
+
+    if (i >= 0 && i <= q) {
+        splitlist[i] = sp;
+        return true;
+    }
+    return false;
+}
+
+// ***********************************************************************
+
+bool interns::swapEdges(const int one_x, const int one_y,
+                        const short int one_type, const int two_x,
+                        const int two_y, const short int two_type) {
+    // The moves on the dendrogram always swap edges, either of which
+    // (or both, or neither) can by internal edges. So, this function
+    // mirrors that operation for the internal edgelist and indexLUT.
+
+    int index, jndex, temp;
+    bool one_isInternal = false;
+    bool two_isInternal = false;
+
+    if (one_x >= 0 && one_x < (q + 1) && two_x >= 0 && two_x < (q + 1) &&
+        (two_type == LEFT || two_type == RIGHT) &&
+        one_y >= 0 && one_y < (q + 2) && two_y >= 0 &&
+        two_y < (q + 2) && (one_type == LEFT || one_type == RIGHT)) {
+
+        if (one_type == LEFT) {
+            temp = 0;
+        } else {
+            temp = 1;
+        }
+        if (indexLUT[one_x][temp] > -1) {
+            one_isInternal = true;
+        }
+        if (two_type == LEFT) {
+            temp = 0;
+        } else {
+            temp = 1;
+        }
+        if (indexLUT[two_x][temp] > -1) {
+            two_isInternal = true;
+        }
+
+        if (one_isInternal && two_isInternal) {
+            if (one_type == LEFT)  {
+                index = indexLUT[one_x][0];
+            } else {
+                index = indexLUT[one_x][1];
+            }
+            if (two_type == LEFT)  {
+                jndex = indexLUT[two_x][0];
+            } else {
+                jndex = indexLUT[two_x][1];
+            }
+            temp              = edgelist[index].y;
+            edgelist[index].y = edgelist[jndex].y;
+            edgelist[jndex].y = temp;
+
+        } else if (one_isInternal) {
+            if (one_type == LEFT)  {
+                index = indexLUT[one_x][0]; indexLUT[one_x][0] = -1;
+            } else {
+                index = indexLUT[one_x][1]; indexLUT[one_x][1] = -1;
+            }
+            edgelist[index].x = two_x;
+            edgelist[index].t = two_type;
+            if (two_type == LEFT) {
+                indexLUT[two_x][0] = index;
+            } else {
+                indexLUT[two_x][1] = index;
+            } // add new
+
+        } else if (two_isInternal) {
+            if (two_type == LEFT)  {
+                index = indexLUT[two_x][0]; indexLUT[two_x][0] = -1;
+            } else {
+                index = indexLUT[two_x][1]; indexLUT[two_x][1] = -1;
+            }
+            edgelist[index].x = one_x;
+            edgelist[index].t = one_type;
+            if (one_type == LEFT) {
+                indexLUT[one_x][0] = index;
+            } else {
+                indexLUT[one_x][1] = index;
+            } // add new
+        } else {
+            ;
+        } // else neither is internal
+
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// ******** Red-Black Tree Methods ***************************************
+
+splittree::splittree() {
+    root = new elementsp;
+    leaf = new elementsp;
+
+    leaf->parent   = root;
+
+    root->left = leaf;
+    root->right    = leaf;
+    support = 0;
+    total_weight = 0.0;
+    total_count = 0;
+}
+
+splittree::~splittree() {
+    if (root != NULL && (root->left != leaf || root->right != leaf)) {
+        deleteSubTree(root); root = NULL;
+    }
+    support      = 0;
+    total_weight = 0.0;
+    total_count  = 0;
+    if (root) {
+        delete root;
+    }
+    delete leaf;
+    root    = NULL;
+    leaf    = NULL;
+}
+
+void splittree::deleteTree() {
+    if (root != NULL) {
+        deleteSubTree(root);
+        root = NULL;
+    }
+    return;
+}
+
+void splittree::deleteSubTree(elementsp *z) {
+    if (z->left  != leaf) {
+        deleteSubTree(z->left);
+        z->left = NULL;
+    }
+    if (z->right != leaf) {
+        deleteSubTree(z->right);
+        z->right = NULL;
+    }
+    delete z;
+    /* No point in setting z to NULL here because z is passed by value */
+    /* z = NULL; */
+    return;
+}
+
+// ******** Reset Functions *********************************************
+
+// O(n lg n)
+void splittree::clearTree() {
+    string *array = returnArrayOfKeys();
+    for (int i = 0; i < support; i++) {
+        deleteItem(array[i]);
+    }
+    delete [] array;
+    return;
+}
+
+// ******** Search Functions *********************************************
+// public search function - if there exists a elementsp in the tree
+// with key=searchKey, it returns TRUE and foundNode is set to point
+// to the found node; otherwise, it sets foundNode=NULL and returns
+// FALSE
+elementsp* splittree::findItem(const string searchKey) {
+
+    elementsp *current = root;
+    if (current->split.empty()) {
+        return NULL;    // empty tree; bail out
+    }
+    while (current != leaf) {
+        if (searchKey.compare(current->split) < 0) { // left-or-right?
+            // try moving down-left
+            if (current->left  != leaf) {
+                current = current->left;
+            } else {
+                // failure; bail out
+                return NULL;
+            }
+        } else {
+            if (searchKey.compare(current->split) > 0) {
+                // left-or-right?
+                if (current->right != leaf) {
+                    // try moving down-left
+                    current = current->right;
+                } else {
+                    //   failure; bail out
+                    return NULL;
+                }
+            } else {
+                // found (searchKey==current->split)
+                return current;
+            }
+        }
+    }
+    return NULL;
+}
+
+double splittree::returnValue(const string searchKey) {
+    elementsp* test = findItem(searchKey);
+    if (test == NULL) {
+        return 0.0;
+    } else {
+        return test->weight;
+    }
+}
+
+
+// ******** Return Item Functions ***************************************
+// public function which returns the tree, via pre-order traversal, as
+// a linked list
+
+string* splittree::returnArrayOfKeys() {
+    string* array;
+    array = new string [support];
+    bool flag_go = true;
+    int index = 0;
+    elementsp *curr;
+
+    if (support == 1) {
+        array[0] = root->split;
+    } else if (support == 2) {
+        array[0] = root->split;
+        if (root->left == leaf) {
+            array[1] = root->right->split;
+        } else {
+            array[1] = root->left->split;
+        }
+    } else {
+        for (int i = 0; i < support; i++) {
+            array[i] = -1;
+        }
+        // non-recursive traversal of tree structure
+        curr  = root;
+        curr->mark = 1;
+        while (flag_go) {
+
+            // - is it time, and is left child the leaf node?
+            if (curr->mark == 1 && curr->left == leaf) {
+                curr->mark = 2;
+            }
+            // - is it time, and is right child the leaf node?
+            if (curr->mark == 2 && curr->right == leaf) {
+                curr->mark = 3;
+            }
+            if (curr->mark == 1) {               // - go left
+                curr->mark = 2;
+                curr = curr->left;
+                curr->mark = 1;
+            } else if (curr->mark == 2) {        // - else go right
+                curr->mark = 3;
+                curr = curr->right;
+                curr->mark = 1;
+            } else {                     // - else go up a level
+                curr->mark = 0;
+                array[index++] = curr->split;
+                curr = curr->parent;
+                if (curr == NULL) {
+                    flag_go = false;
+                }
+            }
+        }
+    }
+
+    return array;
+}
+
+slist* splittree::returnListOfKeys() {
+    keyValuePairSplit *curr, *prev;
+    slist *head = NULL, *tail = NULL, *newlist;
+
+    curr = returnTreeAsList();
+    while (curr != NULL) {
+        newlist = new slist;
+        newlist->x = curr->x;
+        if (head == NULL) {
+            head = newlist; tail = head;
+        } else {
+            tail->next = newlist; tail = newlist;
+        }
+        prev = curr;
+        curr = curr->next;
+        delete prev;
+        prev = NULL;
+    }
+    return head;
+}
+
+// pre-order traversal
+keyValuePairSplit* splittree::returnTreeAsList() {
+    keyValuePairSplit  *head, *tail;
+
+    head    = new keyValuePairSplit;
+    head->x = root->split;
+    head->y = root->weight;
+    head->c = root->count;
+    tail    = head;
+
+    if (root->left  != leaf) {
+        tail = returnSubtreeAsList(root->left,  tail);
+    }
+    if (root->right != leaf) {
+        tail = returnSubtreeAsList(root->right, tail);
+    }
+
+    if (head->x.empty()) {
+        return NULL; /* empty tree */
+    } else {
+        return head;
+    }
+}
+
+keyValuePairSplit* splittree::returnSubtreeAsList(elementsp *z,
+        keyValuePairSplit *head) {
+    keyValuePairSplit *newnode, *tail;
+
+    newnode    = new keyValuePairSplit;
+    newnode->x = z->split;
+    newnode->y = z->weight;
+    newnode->c = z->count;
+    head->next = newnode;
+    tail       = newnode;
+
+    if (z->left  != leaf) {
+        tail = returnSubtreeAsList(z->left,  tail);
+    }
+    if (z->right != leaf) {
+        tail = returnSubtreeAsList(z->right, tail);
+    }
+
+    return tail;
+}
+
+keyValuePairSplit splittree::returnMaxKey() {
+    keyValuePairSplit themax;
+    elementsp *current;
+    current = root;
+    // search to bottom-right corner of tree
+    while (current->right != leaf) {
+        current = current->right;
+    }
+    themax.x = current->split;
+    themax.y = current->weight;
+
+    return themax;
+}
+
+keyValuePairSplit splittree::returnMinKey() {
+    keyValuePairSplit themin;
+    elementsp *current;
+    current = root;
+    // search to bottom-left corner of tree
+    while (current->left != leaf) {
+        current = current->left;
+    }
+    themin.x = current->split;
+    themin.y = current->weight;
+
+    return themin;
+}
+
+// private functions for deleteItem() (although these could easily be
+// made public, I suppose)
+elementsp* splittree::returnMinKey(elementsp *z) {
+    elementsp *current;
+
+    current = z;
+    // search to bottom-right corner of tree
+    while (current->left != leaf) {
+        current = current->left;
+    }
+    // return pointer to the minimum
+    return current;
+}
+
+elementsp* splittree::returnSuccessor(elementsp *z) {
+    elementsp *current, *w;
+
+    w = z;
+// if right-subtree exists, return min of it
+    if (w->right != leaf) {
+        return returnMinKey(w->right);
+    }
+    // else search up in tree
+    // move up in tree until find a non-right-child
+    current = w->parent;
+    while ((current != NULL) && (w == current->right)) {
+        w = current;
+        current = current->parent;
+    }
+    return current;
+}
+
+int splittree::returnNodecount() {
+    return support;
+}
+
+keyValuePairSplit* splittree::returnTheseSplits(const int target) {
+    keyValuePairSplit *head, *curr, *prev, *newhead, *newtail, *newpair;
+    int count, len;
+
+    head = returnTreeAsList();
+    prev = newhead = newtail = newpair = NULL;
+    curr = head;
+
+    while (curr != NULL) {
+        count = 0;
+        len   = curr->x.size();
+        for (int i = 0; i < len; i++) {
+            if (curr->x[i] == 'M') {
+                count++;
+            }
+        }
+        if (count == target && curr->x[1] != '*') {
+            newpair       = new keyValuePairSplit;
+            newpair->x    = curr->x;
+            newpair->y    = curr->y;
+            newpair->next = NULL;
+            if (newhead == NULL) {
+                newhead = newpair; newtail = newpair;
+            } else {
+                newtail->next = newpair; newtail = newpair;
+            }
+        }
+        prev = curr;
+        curr = curr->next;
+        delete prev;
+        prev = NULL;
+    }
+
+    return newhead;
+}
+
+double splittree::returnTotal() {
+    return total_weight;
+}
+
+// ******** Insert Functions *********************************************
+
+void splittree::finishedThisRound() {
+    // We need to also keep a running total of how much weight has been
+    // added to the histogram.
+    if (total_count == 0) {
+        total_weight  = 1.0; total_count = 1;
+    } else {
+        total_weight += 1.0; total_count++;
+    }
+    return;
+}
+
+// public insert function
+bool splittree::insertItem(string newKey, double newValue) {
+
+    // first we check to see if newKey is already present in the tree;
+    // if so, we do nothing; if not, we must find where to insert the
+    // key
+    elementsp *newNode, *current;
+
+// find newKey in tree; return pointer to it O(log k)
+    current = findItem(newKey);
+    if (current != NULL) {
+        current->weight += 1.0;
+        // And finally, we keep track of how many observations went into
+        // the histogram
+        current->count++;
+        return true;
+    } else {
+        newNode = new elementsp;    // elementsp for the splittree
+        newNode->split = newKey;    //  store newKey
+        newNode->weight = newValue; //  store newValue
+        newNode->color = true;  //  new nodes are always RED
+        newNode->parent = NULL; //  new node initially has no parent
+        newNode->left = leaf;   //  left leaf
+        newNode->right = leaf;  //  right leaf
+        newNode->count = 1;
+        support++;          // increment node count in splittree
+
+        // must now search for where to insert newNode, i.e., find the
+        // correct parent and set the parent and child to point to each
+        // other properly
+        current = root;
+        if (current->split.empty()) {   // insert as root
+            delete root;      //   delete old root
+            root = newNode;       //   set root to newNode
+            leaf->parent   = newNode; //   set leaf's parent
+            current = leaf;       //   skip next loop
+        }
+
+        // search for insertion point
+        while (current != leaf) {
+            // left-or-right?
+            if (newKey.compare(current->split) < 0) {
+                // try moving down-left
+                if (current->left  != leaf) {
+                    current = current->left;
+                } else {
+                    // else found new parent
+                    newNode->parent = current; // set parent
+                    current->left = newNode;   // set child
+                    current = leaf;        // exit search
+                }
+            } else { //
+                if (current->right != leaf) {
+                    // try moving down-right
+                    current = current->right;
+                } else {
+                    // else found new parent
+                    newNode->parent = current; // set parent
+                    current->right = newNode;  // set child
+                    current = leaf;        // exit search
+                }
+            }
+        }
+
+        // now do the house-keeping necessary to preserve the red-black
+        // properties
+        insertCleanup(newNode);
+
+    }
+    return true;
+}
+
+// private house-keeping function for insertion
+void splittree::insertCleanup(elementsp *z) {
+
+    // fix now if z is root
+    if (z->parent == NULL) {
+        z->color = false; return;
+    }
+    elementsp *temp;
+    // while z is not root and z's parent is RED
+    while (z->parent != NULL && z->parent->color) {
+        if (z->parent == z->parent->parent->left) {  // z's parent is LEFT-CHILD
+            temp = z->parent->parent->right;       // grab z's uncle
+            if (temp->color) {
+                z->parent->color = false;          // color z's parent BLACK (Case 1)
+                temp->color = false;               // color z's uncle BLACK  (Case 1)
+                z->parent->parent->color = true;   // color z's grandpa  RED (Case 1)
+                z = z->parent->parent;             // set z = z's grandpa    (Case 1)
+            } else {
+                if (z == z->parent->right) {       // z is RIGHT-CHILD
+                    z = z->parent;                   // set z = z's parent     (Case 2)
+                    rotateLeft(z);                   // perform left-rotation  (Case 2)
+                }
+                z->parent->color = false;          // color z's parent BLACK (Case 3)
+                z->parent->parent->color = true;   // color z's grandpa RED  (Case 3)
+                rotateRight(z->parent->parent);    // perform right-rotation (Case 3)
+            }
+        } else {                       // z's parent is RIGHT-CHILD
+            temp = z->parent->parent->left;      // grab z's uncle
+            if (temp->color) {
+                z->parent->color = false;          // color z's parent BLACK (Case 1)
+                temp->color = false;               // color z's uncle BLACK  (Case 1)
+                z->parent->parent->color = true;   // color z's grandpa RED  (Case 1)
+                z = z->parent->parent;             // set z = z's grandpa    (Case 1)
+            } else {
+                if (z == z->parent->left) {        // z is LEFT-CHILD
+                    z = z->parent;                   // set z = z's parent     (Case 2)
+                    rotateRight(z);                  // perform right-rotation (Case 2)
+                }
+                z->parent->color = false;          // color z's parent BLACK (Case 3)
+                z->parent->parent->color = true;   // color z's grandpa RED  (Case 3)
+                rotateLeft(z->parent->parent);     // perform left-rotation  (Case 3)
+            }
+        }
+    }
+
+    root->color = false; // color the root BLACK
+    return;
+}
+
+// ******** Delete Functions ********************************************
+// public delete function
+void splittree::deleteItem(string killKey) {
+    elementsp *x, *y, *z;
+
+    z = findItem(killKey);
+    if (z == NULL) {
+        return;    // item not present; bail out
+    }
+
+    if (support == 1) {   // -- attempt to delete the root
+        root->split = "";       // restore root node to default state
+        root->weight = 0.0;     //
+        root->color = false;    //
+        root->parent = NULL;    //
+        root->left = leaf;      //
+        root->right = leaf;     //
+        support--;          // set support to zero
+        total_weight = 0.0;     // set total weight to zero
+        total_count--;      //
+        return;         // exit - no more work to do
+    }
+
+    if (z != NULL) {
+        support--;          // decrement node count
+        if ((z->left == leaf) || (z->right == leaf)) {
+            // case of less than two children
+            y = z;             // set y to be z
+        } else {
+            y = returnSuccessor(z);    // set y to be z's key-successor
+        }
+
+        if (y->left != leaf) {
+            x = y->left;       // pick y's one child (left-child)
+        } else {
+            x = y->right;              // (right-child)
+        }
+        x->parent = y->parent;       // make y's child's parent be y's parent
+
+        if (y->parent == NULL) {
+            root = x;          // if y is the root, x is now root
+        } else {
+            if (y == y->parent->left) {// decide y's relationship with y's parent
+                y->parent->left  = x;    // replace x as y's parent's left child
+            } else {
+                y->parent->right = x;
+            }  // replace x as y's parent's left child
+        }
+
+        if (y != z) {        // insert y into z's spot
+            z->split = y->split;   // copy y data into z
+            z->weight = y->weight;     //
+            z->count = y->count;   //
+        }                //
+
+        // do house-keeping to maintain balance
+        if (y->color == false) {
+            deleteCleanup(x);
+        }
+        delete y;            // deallocate y
+        y = NULL;            // point y to NULL for safety
+    }              //
+
+    return;
+}
+
+void splittree::deleteCleanup(elementsp *x) {
+    elementsp *w, *t;
+    // until x is the root, or x is RED
+    while ((x != root) && (x->color == false)) {
+        if (x == x->parent->left) {      // branch on x being a LEFT-CHILD
+            w = x->parent->right;      // grab x's sibling
+            if (w->color == true) {    // if x's sibling is RED
+                w->color = false;        // color w BLACK                (case 1)
+                x->parent->color = true;     // color x's parent RED         (case 1)
+                rotateLeft(x->parent);       // left rotation on x's parent  (case 1)
+                w = x->parent->right;        // make w be x's right sibling  (case 1)
+            }
+            if ((w->left->color == false) && (w->right->color == false)) {
+                w->color = true;         // color w RED                  (case 2)
+                x = x->parent;           // examine x's parent           (case 2)
+            } else {               //
+                if (w->right->color == false) {
+                    w->left->color = false;    // color w's left child BLACK   (case 3)
+                    w->color = true;       // color w RED                  (case 3)
+                    t = x->parent;         // store x's parent
+                    rotateRight(w);        // right rotation on w          (case 3)
+                    x->parent = t;         // restore x's parent
+                    w = x->parent->right;      // make w be x's right sibling  (case 3)
+                } //
+                w->color = x->parent->color; // w's color := x's parent's    (case 4)
+                x->parent->color    = false; // color x's parent BLACK       (case 4)
+                w->right->color = false;     // color w's right child BLACK  (case 4)
+                rotateLeft(x->parent);       // left rotation on x's parent  (case 4)
+                x = root;            // finished work. bail out      (case 4)
+            }                  //
+        } else {                 // x is RIGHT-CHILD
+            w = x->parent->left;       // grab x's sibling
+            if (w->color == true) {    // if x's sibling is RED
+                w->color = false;        // color w BLACK                (case 1)
+                x->parent->color    = true;  // color x's parent RED         (case 1)
+                rotateRight(x->parent);      // right rotation on x's parent (case 1)
+                w = x->parent->left;         // make w be x's left sibling   (case 1)
+            }
+            if ((w->right->color == false) && (w->left->color == false)) {
+                w->color = true;         // color w RED                  (case 2)
+                x = x->parent;           // examine x's parent           (case 2)
+            } else { //
+                if (w->left->color == false) { //
+                    w->right->color = false;   // color w's right child BLACK  (case 3)
+                    w->color = true;       // color w RED                  (case 3)
+                    t = x->parent;         // store x's parent
+                    rotateLeft(w);         // left rotation on w           (case 3)
+                    x->parent = t;         // restore x's parent
+                    w = x->parent->left;       // make w be x's left sibling   (case 3)
+                } //
+                w->color = x->parent->color; // w's color := x's parent's    (case 4)
+                x->parent->color    = false; // color x's parent BLACK       (case 4)
+                w->left->color = false;      // color w's left child BLACK   (case 4)
+                rotateRight(x->parent);      // right rotation on x's parent (case 4)
+                x = root;                    // x is now the root            (case 4)
+            }
+        }
+    }
+    x->color = false;          // color x (the root) BLACK (exit)
+
+    return;
+}
+
+// ******** Rotation Functions *******************************************
+
+void splittree::rotateLeft(elementsp *x) {
+    elementsp *y;
+    // do pointer-swapping operations for left-rotation
+    y = x->right;             // grab right child
+    x->right = y->left;           // make x's RIGHT-CHILD be y's LEFT-CHILD
+    y->left->parent = x;          // make x be y's LEFT-CHILD's parent
+    y->parent = x->parent;        // make y's new parent be x's old parent
+
+    if (x->parent == NULL) {
+        root = y;               // if x was root, make y root
+    } else {              //
+        if (x == x->parent->left) { // if x is LEFT-CHILD, make y be x's parent's
+            x->parent->left  = y; // left-child
+        } else {
+            x->parent->right = y; // right-child
+        }
+    }
+    y->left   = x;        // make x be y's LEFT-CHILD
+    x->parent = y;        // make y be x's parent
+
+    return;
+}
+
+void splittree::rotateRight(elementsp *y) {
+    elementsp *x;
+    // do pointer-swapping operations for right-rotation
+    x = y->left;               // grab left child
+    y->left = x->right;            // replace left child yith x's right subtree
+    x->right->parent = y;          // replace y as x's right subtree's parent
+
+    x->parent = y->parent;         // make x's new parent be y's old parent
+    if (y->parent == NULL) {
+        root = x;            // if y was root, make x root
+    } else {
+        if (y == y->parent->right) { // if y is R-CHILD, make x be y's parent's
+            y->parent->right = x;  // right-child
+        } else {
+            y->parent->left = x;   // left-child
+        }
+    }
+    x->right  = y;         // make y be x's RIGHT-CHILD
+    y->parent = x;         // make x be y's parent
+
+    return;
+}
+
+// ***********************************************************************
+// *** COPYRIGHT NOTICE **************************************************
+// graph_simp.h - graph data structure
+// Copyright (C) 2006-2008 Aaron Clauset
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// See http://www.gnu.org/licenses/gpl.txt for more details.
+//
+// ***********************************************************************
+// Author       : Aaron Clauset  ( aaronc@santafe.edu |
+//                                 http://www.santafe.edu/~aaronc/ )
+// Collaborators: Cristopher Moore and Mark E.J. Newman
+// Project      : Hierarchical Random Graphs
+// Location     : University of New Mexico, Dept. of Computer Science
+//                AND Santa Fe Institute
+// Created      : 21 June 2006
+// Modified     : 23 December 2007 (cleaned up for public consumption)
+//
+// ************************************************************************
+
+// ******** Constructor / Destructor *************************************
+
+simpleGraph::simpleGraph(const int size): n(size), m(0), num_groups(0) {
+    nodes = new simpleVert  [n];
+    nodeLink = new simpleEdge* [n];
+    nodeLinkTail = new simpleEdge* [n];
+    A = new double* [n];
+    for (int i = 0; i < n; i++) {
+        nodeLink[i] = NULL; nodeLinkTail[i] = NULL;
+        A[i] = new double [n];
+        for (int j = 0; j < n; j++) {
+            A[i][j] = 0.0;
+        }
+    }
+    E = NULL;
+}
+
+simpleGraph::~simpleGraph() {
+    simpleEdge *curr, *prev;
+    for (int i = 0; i < n; i++) {
+        curr = nodeLink[i];
+        delete [] A[i];
+        while (curr != NULL) {
+            prev = curr;
+            curr = curr->next;
+            delete prev;
+        }
+    }
+    curr = NULL; prev = NULL;
+    if (E != NULL) {
+        delete [] E;
+        E = NULL;
+    }
+    delete [] A; A = NULL;
+    delete [] nodeLink; nodeLink = NULL;
+    delete [] nodeLinkTail;  nodeLinkTail   = NULL;
+    delete [] nodes; nodes = NULL;
+}
+
+// ***********************************************************************
+
+bool simpleGraph::addGroup(const int i, const int group_index) {
+    if (i >= 0 && i < n) {
+        nodes[i].group_true = group_index;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// ***********************************************************************
+
+bool simpleGraph::addLink(const int i, const int j) {
+    // Adds the directed edge (i,j) to the adjacency list for v_i
+    simpleEdge* newedge;
+    if (i >= 0 && i < n && j >= 0 && j < n) {
+        A[i][j] = 1.0;
+        newedge  = new simpleEdge;
+        newedge->x = j;
+        if (nodeLink[i] == NULL) {  // first neighbor
+            nodeLink[i]  = newedge;
+            nodeLinkTail[i] = newedge;
+            nodes[i].degree = 1;
+        } else {            // subsequent neighbor
+            nodeLinkTail[i]->next = newedge;
+            nodeLinkTail[i]       = newedge;
+            nodes[i].degree++;
+        }
+        m++;            // increment edge count
+        newedge = NULL;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// ***********************************************************************
+
+bool simpleGraph::doesLinkExist(const int i, const int j) {
+    // This function determines if the edge (i,j) already exists in the
+    // adjacency list of v_i
+    if (i >= 0 && i < n && j >= 0 && j < n) {
+        if (A[i][j] > 0.1) {
+            return true;
+        } else {
+            return false;
+        }
+    } else {
+        return false;
+    }
+    return false;
+}
+
+// **********************************************************************
+
+double simpleGraph::getAdjacency(const int i, const int j) {
+    if (i >= 0 && i < n && j >= 0 && j < n) {
+        return A[i][j];
+    } else {
+        return -1.0;
+    }
+}
+
+int simpleGraph::getDegree(const int i) {
+    if (i >= 0 && i < n) {
+        return nodes[i].degree;
+    } else {
+        return -1;
+    }
+}
+
+int simpleGraph::getGroupLabel(const int i) {
+    if (i >= 0 && i < n) {
+        return nodes[i].group_true;
+    } else {
+        return -1;
+    }
+}
+
+string simpleGraph::getName(const int i) {
+    if (i >= 0 && i < n) {
+        return nodes[i].name;
+    } else {
+        return "";
+    }
+}
+
+// NOTE: The following three functions return addresses; deallocation
+// of returned object is dangerous
+simpleEdge* simpleGraph::getNeighborList(const int i) {
+    if (i >= 0 && i < n) {
+        return nodeLink[i];
+    } else {
+        return NULL;
+    }
+}
+// END-NOTE
+
+// *********************************************************************
+
+int simpleGraph::getNumGroups() {
+    return num_groups;
+}
+int simpleGraph::getNumLinks()  {
+    return m;
+}
+int simpleGraph::getNumNodes()  {
+    return n;
+}
+simpleVert* simpleGraph::getNode(const int i) {
+    if (i >= 0 && i < n) {
+        return &nodes[i];
+    } else {
+        return NULL;
+    }
+}
+
+// **********************************************************************
+
+bool simpleGraph::setName(const int i, const string text) {
+    if (i >= 0 && i < n) {
+        nodes[i].name = text;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// **********************************************************************
+
+void simpleGraph::QsortMain (block* array, int left, int right) {
+    if (right > left) {
+        int pivot = left;
+        int part  = QsortPartition(array, left, right, pivot);
+        QsortMain(array, left,   part - 1);
+        QsortMain(array, part + 1, right  );
+    }
+    return;
+}
+
+int simpleGraph::QsortPartition (block* array, int left, int right,
+                                 int index) {
+    block p_value, temp;
+    p_value.x = array[index].x;
+    p_value.y = array[index].y;
+
+    // swap(array[p_value], array[right])
+    temp.x = array[right].x;
+    temp.y = array[right].y;
+    array[right].x = array[index].x;
+    array[right].y = array[index].y;
+    array[index].x = temp.x;
+    array[index].y = temp.y;
+
+    int stored = left;
+    for (int i = left; i < right; i++) {
+        if (array[i].x <= p_value.x) {
+            // swap(array[stored], array[i])
+            temp.x = array[i].x;
+            temp.y = array[i].y;
+            array[i].x = array[stored].x;
+            array[i].y = array[stored].y;
+            array[stored].x = temp.x;
+            array[stored].y = temp.y;
+            stored++;
+        }
+    }
+    // swap(array[right], array[stored])
+    temp.x = array[stored].x;
+    temp.y = array[stored].y;
+    array[stored].x = array[right].x;
+    array[stored].y = array[right].y;
+    array[right].x = temp.x;
+    array[right].y = temp.y;
+
+    return stored;
+}
+
+// ***********************************************************************
diff --git a/igraph/src/igraph_marked_queue.c b/igraph/src/igraph_marked_queue.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_marked_queue.c
@@ -0,0 +1,115 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_marked_queue.h"
+
+#define BATCH_MARKER -1
+
+int igraph_marked_queue_init(igraph_marked_queue_t *q,
+                             long int size) {
+    IGRAPH_CHECK(igraph_dqueue_init(&q->Q, 0));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &q->Q);
+    IGRAPH_CHECK(igraph_vector_long_init(&q->set, size));
+    q->mark = 1;
+    q->size = 0;
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+void igraph_marked_queue_destroy(igraph_marked_queue_t *q) {
+    igraph_vector_long_destroy(&q->set);
+    igraph_dqueue_destroy(&q->Q);
+}
+
+void igraph_marked_queue_reset(igraph_marked_queue_t *q) {
+    igraph_dqueue_clear(&q->Q);
+    q->size = 0;
+    q->mark += 1;
+    if (q->mark == 0) {
+        igraph_vector_long_null(&q->set);
+        q->mark += 1;
+    }
+}
+
+igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q) {
+    return q->size == 0;
+}
+
+long int igraph_marked_queue_size(const igraph_marked_queue_t *q) {
+    return q->size;
+}
+
+igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q,
+        long int elem) {
+    return (VECTOR(q->set)[elem] == q->mark);
+}
+
+int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem) {
+    if (VECTOR(q->set)[elem] != q->mark) {
+        IGRAPH_CHECK(igraph_dqueue_push(&q->Q, elem));
+        VECTOR(q->set)[elem] = q->mark;
+        q->size += 1;
+    }
+    return 0;
+}
+
+int igraph_marked_queue_start_batch(igraph_marked_queue_t *q) {
+    IGRAPH_CHECK(igraph_dqueue_push(&q->Q, BATCH_MARKER));
+    return 0;
+}
+
+void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q) {
+    long int size = igraph_dqueue_size(&q->Q);
+    long int elem;
+    while (size > 0 &&
+           (elem = (long int) igraph_dqueue_pop_back(&q->Q)) != BATCH_MARKER) {
+        VECTOR(q->set)[elem] = 0;
+        size--;
+        q->size--;
+    }
+}
+
+#ifndef USING_R
+int igraph_marked_queue_print(const igraph_marked_queue_t *q) {
+    IGRAPH_CHECK(igraph_dqueue_print(&q->Q));
+    return 0;
+}
+#endif
+
+int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file) {
+    IGRAPH_CHECK(igraph_dqueue_fprint(&q->Q, file));
+    return 0;
+}
+
+int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q,
+                                  igraph_vector_t *vec) {
+    long int i, p, n = igraph_dqueue_size(&q->Q);
+    IGRAPH_CHECK(igraph_vector_resize(vec, q->size));
+    for (i = 0, p = 0; i < n; i++) {
+        igraph_real_t e = igraph_dqueue_e(&q->Q, i);
+        if (e != BATCH_MARKER) {
+            VECTOR(*vec)[p++] = e;
+        }
+    }
+    return 0;
+}
diff --git a/igraph/src/igraph_psumtree.c b/igraph/src/igraph_psumtree.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_psumtree.c
@@ -0,0 +1,102 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+   Copyright (C) 2006 Elliot Paquette <Elliot.Paquette05@kzoo.edu>
+   Kalamazoo College, 1200 Academy st, Kalamazoo, MI
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_psumtree.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <math.h>
+#include <stdio.h>
+
+double igraph_i_log2(double f) {
+    return log(f) / log(2.0);
+}
+
+int igraph_psumtree_init(igraph_psumtree_t *t, long int size) {
+    t->size = size;
+    t->offset = (long int) (pow(2, ceil(igraph_i_log2(size))) - 1);
+    IGRAPH_CHECK(igraph_vector_init((igraph_vector_t *)t, t->offset + t->size));
+    return 0;
+}
+
+void igraph_psumtree_reset(igraph_psumtree_t *t) {
+    igraph_vector_fill(&(t->v), 0);
+}
+
+void igraph_psumtree_destroy(igraph_psumtree_t *t) {
+    igraph_vector_destroy((igraph_vector_t *)t);
+}
+
+igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx) {
+    const igraph_vector_t *tree = &t->v;
+    return VECTOR(*tree)[t->offset + idx];
+}
+
+int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx,
+                           igraph_real_t search) {
+    const igraph_vector_t *tree = &t->v;
+    long int i = 1;
+    long int size = igraph_vector_size(tree);
+
+    while ( 2 * i + 1 <= size) {
+        if ( search <= VECTOR(*tree)[i * 2 - 1] ) {
+            i <<= 1;
+        } else {
+            search -= VECTOR(*tree)[i * 2 - 1];
+            i <<= 1;
+            i += 1;
+        }
+    }
+    if (2 * i <= size) {
+        i = 2 * i;
+    }
+
+    *idx = i - t->offset - 1;
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_psumtree_update(igraph_psumtree_t *t, long int idx,
+                           igraph_real_t new_value) {
+    const igraph_vector_t *tree = &t->v;
+    igraph_real_t difference;
+
+    idx = idx + t->offset + 1;
+    difference = new_value - VECTOR(*tree)[idx - 1];
+
+    while ( idx >= 1 ) {
+        VECTOR(*tree)[idx - 1] += difference;
+        idx >>= 1;
+    }
+    return IGRAPH_SUCCESS;
+}
+
+long int igraph_psumtree_size(const igraph_psumtree_t *t) {
+    return t->size;
+}
+
+igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t) {
+    return VECTOR(t->v)[0];
+}
diff --git a/igraph/src/igraph_set.c b/igraph/src/igraph_set.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_set.c
@@ -0,0 +1,320 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_memory.h"
+#include "igraph_error.h"
+#include "igraph_types_internal.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>     /* memmove */
+
+#define SET(s) ((s).stor_begin)
+
+/**
+ * \ingroup set
+ * \function igraph_set_init
+ * \brief Initializes a set.
+ *
+ * \param set pointer to the set to be initialized
+ * \param size the expected number of elements in the set
+ *
+ * \return error code:
+ *       \c IGRAPH_ENOMEM if there is not enough memory.
+ *
+ * Time complexity: operating system dependent, should be around
+ * O(n), n is the expected size of the set.
+ */
+int igraph_set_init(igraph_set_t *set, int long size) {
+    long int alloc_size = size > 0 ? size : 1;
+    if (size < 0) {
+        size = 0;
+    }
+    set->stor_begin = igraph_Calloc(alloc_size, igraph_integer_t);
+    set->stor_end = set->stor_begin + alloc_size;
+    set->end = set->stor_begin;
+
+    return 0;
+}
+
+/**
+ * \ingroup set
+ * \function igraph_set_destroy
+ * \brief Destroys a set object.
+ *
+ * \param set pointer to the set to be destroyed
+ *
+ * Time complexity: operating system dependent.
+ */
+void igraph_set_destroy(igraph_set_t* set) {
+    assert(set != 0);
+    if (set->stor_begin != 0) {
+        igraph_Free(set->stor_begin);
+        set->stor_begin = NULL;
+    }
+}
+
+/**
+ * \ingroup set
+ * \function igraph_set_inited
+ * \brief Determines whether a set is initialized or not.
+ *
+ * This function checks whether the internal storage for the members of the
+ * set has been allocated or not, and it assumes that the pointer for the
+ * internal storage area contains \c NULL if the area is not initialized yet.
+ * This only applies if you have allocated an array of sets with \c igraph_Calloc or
+ * if you used the \c IGRAPH_SET_NULL constant to initialize the set.
+ *
+ * \param set The set object.
+ *
+ * Time complexity: O(1)
+ */
+igraph_bool_t igraph_set_inited(igraph_set_t* set) {
+    return (set->stor_begin != 0);
+}
+
+/**
+ * \ingroup set
+ * \function igraph_set_reserve
+ * \brief Reserve memory for a set.
+ *
+ * \param set The set object.
+ * \param size the new \em allocated size of the set.
+ *
+ * Time complexity: operating system dependent, should be around
+ * O(n), n is the new allocated size of the set.
+ */
+int igraph_set_reserve(igraph_set_t* set, long int size) {
+    long int actual_size = igraph_set_size(set);
+    igraph_integer_t *tmp;
+    assert(set != NULL);
+    assert(set->stor_begin != NULL);
+    if (size <= actual_size) {
+        return 0;
+    }
+
+    tmp = igraph_Realloc(set->stor_begin, (size_t) size, igraph_integer_t);
+    if (tmp == 0) {
+        IGRAPH_ERROR("cannot reserve space for set", IGRAPH_ENOMEM);
+    }
+    set->stor_begin = tmp;
+    set->stor_end = set->stor_begin + size;
+    set->end = set->stor_begin + actual_size;
+
+    return 0;
+}
+
+/**
+ * \ingroup set
+ * \function igraph_set_empty
+ * \brief Decides whether the size of the set is zero.
+ *
+ * \param set The set object.
+ * \return Non-zero number if the size of the set is not zero and
+ *         zero otherwise.
+ *
+ * Time complexity: O(1).
+ */
+igraph_bool_t igraph_set_empty(const igraph_set_t* set) {
+    assert(set != NULL);
+    assert(set->stor_begin != NULL);
+    return set->stor_begin == set->end;
+}
+
+/**
+ * \ingroup set
+ * \function igraph_set_clear
+ * \brief Removes all elements from a set.
+ *
+ * </para><para>
+ * This function simply sets the size of the set to zero, it does
+ * not free any allocated memory. For that you have to call
+ * \ref igraph_set_destroy().
+ * \param v The set object.
+ *
+ * Time complexity: O(1).
+ */
+void igraph_set_clear(igraph_set_t* set) {
+    assert(set != NULL);
+    assert(set->stor_begin != NULL);
+    set->end = set->stor_begin;
+}
+
+
+/**
+ * \ingroup set
+ * \function igraph_set_size
+ * \brief Gives the size (=length) of the set.
+ *
+ * \param v The set object
+ * \return The size of the set.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_set_size(const igraph_set_t* set) {
+    assert(set != NULL);
+    assert(set->stor_begin != NULL);
+    return set->end - set->stor_begin;
+}
+
+
+/**
+ * \ingroup set
+ * \function igraph_set_add
+ * \brief Adds an element to the set.
+ *
+ * \param set The set object.
+ * \param e The element to be added.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM: not enough memory.
+ *
+ * Time complexity: O(log(n)), n is the number of elements in \p set.
+ */
+int igraph_set_add(igraph_set_t* set, igraph_integer_t e) {
+    long int left, right, middle;
+    long int size;
+    assert(set != NULL);
+    assert(set->stor_begin != NULL);
+
+    size = igraph_set_size(set);
+
+    /* search where to insert the new element */
+    left = 0;
+    right = size - 1;
+    while (left < right - 1) {
+        middle = (left + right) / 2;
+        if (SET(*set)[middle] > e) {
+            right = middle;
+        } else if (SET(*set)[middle] < e) {
+            left = middle;
+        } else {
+            left = middle;
+            break;
+        }
+    }
+
+    if (right >= 0 && SET(*set)[left] != e && SET(*set)[right] == e) {
+        left = right;
+    }
+
+    while (left < size && set->stor_begin[left] < e) {
+        left++;
+    }
+    if (left >= size || set->stor_begin[left] != e) {
+        /* full, allocate more storage */
+        if (set->stor_end == set->end) {
+            long int new_size = size * 2;
+            if (new_size == 0) {
+                new_size = 1;
+            }
+            IGRAPH_CHECK(igraph_set_reserve(set, new_size));
+        }
+
+        /* Element should be inserted at position 'left' */
+        if (left < size)
+            memmove(set->stor_begin + left + 1, set->stor_begin + left,
+                    (size_t) (size - left)*sizeof(set->stor_begin[0]));
+
+        set->stor_begin[left] = e;
+        set->end += 1;
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup set
+ * \function igraph_set_contains
+ * \brief Checks whether a given element is in the set or not.
+ *
+ * \param set The set object.
+ * \param e The element being sought.
+ * \return Positive integer (true) if \p e is found, zero (false) otherwise.
+ *
+ * Time complexity: O(log(n)), n is the number of elements in \p set.
+ */
+int igraph_set_contains(igraph_set_t* set, igraph_integer_t e) {
+    long int left, right, middle;
+
+    assert(set != NULL);
+    assert(set->stor_begin != NULL);
+
+    left = 0;
+    right = igraph_set_size(set) - 1;
+
+    if (right == -1) {
+        return 0;    /* the set is empty */
+    }
+
+    /* search for the new element */
+    while (left < right - 1) {
+        middle = (left + right) / 2;
+        if (SET(*set)[middle] > e) {
+            right = middle;
+        } else if (SET(*set)[middle] < e) {
+            left = middle;
+        } else {
+            return 1;
+        }
+    }
+
+    return SET(*set)[left] == e || SET(*set)[right] == e;
+}
+
+/**
+ * \ingroup set
+ * \function igraph_set_iterate
+ * \brief Iterates through the element to the set.
+ *
+ * Elements are returned in an arbitrary order.
+ *
+ * \param set The set object.
+ * \param state Internal state of the iteration.
+ *   This should be a pointer to a \c long variable
+ *   which must be zero for the first invocation.
+ *   The object should not be adjusted and its value should
+ *   not be used for anything during the iteration.
+ * \param element The next element or \c NULL (if the iteration
+ *   has ended) is returned here.
+ *
+ * \return Nonzero if there are more elements, zero otherwise.
+ */
+igraph_bool_t igraph_set_iterate(igraph_set_t* set, long int* state,
+                                 igraph_integer_t* element) {
+    assert(set != 0);
+    assert(set->stor_begin != 0);
+    assert(state != 0);
+    assert(element != 0);
+
+    if (*state < igraph_set_size(set)) {
+        *element = set->stor_begin[*state];
+        *state = *state + 1;
+        return 1;
+    } else {
+        *element = 0;
+        return 0;
+    }
+}
+
diff --git a/igraph/src/igraph_stack.c b/igraph/src/igraph_stack.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_stack.c
@@ -0,0 +1,89 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_stack.h"
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "stack.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "stack.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "stack.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "stack.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "stack.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_PTR
+#include "igraph_pmt.h"
+#include "stack.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_PTR
+
+/**
+ * \ingroup stack
+ * \brief Calls free() on all elements of a pointer stack.
+ */
+
+void igraph_stack_ptr_free_all   (igraph_stack_ptr_t* v) {
+    void **ptr;
+    assert(v != 0);
+    assert(v->stor_begin != 0);
+    for (ptr = v->stor_begin; ptr < v->end; ptr++) {
+        igraph_Free(*ptr);
+    }
+}
+
+/**
+ * \ingroup stack
+ * \brief Calls free() on all elements and destroys the stack.
+ */
+
+void igraph_stack_ptr_destroy_all   (igraph_stack_ptr_t* v) {
+    assert(v != 0);
+    assert(v->stor_begin != 0);
+    igraph_stack_ptr_free_all(v);
+    igraph_stack_ptr_destroy(v);
+}
+
+
diff --git a/igraph/src/igraph_strvector.c b/igraph/src/igraph_strvector.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_strvector.c
@@ -0,0 +1,592 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_strvector.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+
+/**
+ * \section igraph_strvector_t
+ * <para>The <type>igraph_strvector_t</type> type is a vector of strings.
+ * The current implementation is very simple and not too efficient. It
+ * works fine for not too many strings, e.g. the list of attribute
+ * names is returned in a string vector by \ref
+ * igraph_cattribute_list(). Do not expect great performance from this
+ * type.</para>
+ *
+ * <para>
+ * \example examples/simple/igraph_strvector.c
+ * </para>
+ */
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_init
+ * \brief Initialize
+ *
+ * Reserves memory for the string vector, a string vector must be
+ * first initialized before calling other functions on it.
+ * All elements of the string vector are set to the empty string.
+ * \param sv Pointer to an initialized string vector.
+ * \param len The (initial) length of the string vector.
+ * \return Error code.
+ *
+ * Time complexity: O(\p len).
+ */
+
+int igraph_strvector_init(igraph_strvector_t *sv, long int len) {
+    long int i;
+    sv->data = igraph_Calloc(len, char*);
+    if (sv->data == 0) {
+        IGRAPH_ERROR("strvector init failed", IGRAPH_ENOMEM);
+    }
+    for (i = 0; i < len; i++) {
+        sv->data[i] = igraph_Calloc(1, char);
+        if (sv->data[i] == 0) {
+            igraph_strvector_destroy(sv);
+            IGRAPH_ERROR("strvector init failed", IGRAPH_ENOMEM);
+        }
+        sv->data[i][0] = '\0';
+    }
+    sv->len = len;
+
+    return 0;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_destroy
+ * \brief Free allocated memory
+ *
+ * Destroy a string vector. It may be reinitialized with \ref
+ * igraph_strvector_init() later.
+ * \param sv The string vector.
+ *
+ * Time complexity: O(l), the total length of the strings, maybe less
+ * depending on the memory manager.
+ */
+
+void igraph_strvector_destroy(igraph_strvector_t *sv) {
+    long int i;
+    assert(sv != 0);
+    if (sv->data != 0) {
+        for (i = 0; i < sv->len; i++) {
+            if (sv->data[i] != 0) {
+                igraph_Free(sv->data[i]);
+            }
+        }
+        igraph_Free(sv->data);
+    }
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_get
+ * \brief Indexing
+ *
+ * Query an element of a string vector. See also the \ref STR macro
+ * for an easier way.
+ * \param sv The input string vector.
+ * \param idx The index of the element to query.
+ * \param Pointer to a <type>char*</type>, the address of the string
+ *   is stored here.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_strvector_get(const igraph_strvector_t *sv, long int idx,
+                          char **value) {
+    assert(sv != 0);
+    assert(sv->data != 0);
+    assert(sv->data[idx] != 0);
+    *value = sv->data[idx];
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_set
+ * \brief Set an element
+ *
+ * The provided \p value is copied into the \p idx position in the
+ * string vector.
+ * \param sv The string vector.
+ * \param idx The position to set.
+ * \param value The new value.
+ * \return Error code.
+ *
+ * Time complexity: O(l), the length of the new string. Maybe more,
+ * depending on the memory management, if reallocation is needed.
+ */
+
+int igraph_strvector_set(igraph_strvector_t *sv, long int idx,
+                         const char *value) {
+    assert(sv != 0);
+    assert(sv->data != 0);
+    if (sv->data[idx] == 0) {
+        sv->data[idx] = igraph_Calloc(strlen(value) + 1, char);
+        if (sv->data[idx] == 0) {
+            IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM);
+        }
+    } else {
+        char *tmp = igraph_Realloc(sv->data[idx], strlen(value) + 1, char);
+        if (tmp == 0) {
+            IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM);
+        }
+        sv->data[idx] = tmp;
+    }
+    strcpy(sv->data[idx], value);
+
+    return 0;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_set2
+ * \brief Sets an element
+ *
+ * This is almost the same as \ref igraph_strvector_set, but the new
+ * value is not a zero terminated string, but its length is given.
+ * \param sv The string vector.
+ * \param idx The position to set.
+ * \param value The new value.
+ * \param len The length of the new value.
+ * \return Error code.
+ *
+ * Time complexity: O(l), the length of the new string. Maybe more,
+ * depending on the memory management, if reallocation is needed.
+ */
+int igraph_strvector_set2(igraph_strvector_t *sv, long int idx,
+                          const char *value, int len) {
+    assert(sv != 0);
+    assert(sv->data != 0);
+    if (sv->data[idx] == 0) {
+        sv->data[idx] = igraph_Calloc(len + 1, char);
+        if (sv->data[idx] == 0) {
+            IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM);
+        }
+    } else {
+        char *tmp = igraph_Realloc(sv->data[idx], (size_t) len + 1, char);
+        if (tmp == 0) {
+            IGRAPH_ERROR("strvector set failed", IGRAPH_ENOMEM);
+        }
+        sv->data[idx] = tmp;
+    }
+    memcpy(sv->data[idx], value, (size_t) len * sizeof(char));
+    sv->data[idx][len] = '\0';
+
+    return 0;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_remove_section
+ * \brief Removes a section from a string vector.
+ * \todo repair realloc
+ */
+
+void igraph_strvector_remove_section(igraph_strvector_t *v, long int from,
+                                     long int to) {
+    long int i;
+    /*   char **tmp; */
+
+    assert(v != 0);
+    assert(v->data != 0);
+
+    for (i = from; i < to; i++) {
+        if (v->data[i] != 0) {
+            igraph_Free(v->data[i]);
+        }
+    }
+    for (i = 0; i < v->len - to; i++) {
+        v->data[from + i] = v->data[to + i];
+    }
+
+    v->len -= (to - from);
+
+    /* try to make it smaller */
+    /*   tmp=igraph_Realloc(v->data, v->len, char*); */
+    /*   if (tmp!=0) { */
+    /*     v->data=tmp; */
+    /*   } */
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_remove
+ * \brief Removes a single element from a string vector.
+ *
+ * The string will be one shorter.
+ * \param The string vector.
+ * \param elem The index of the element to remove.
+ *
+ * Time complexity: O(n), the length of the string.
+ */
+
+void igraph_strvector_remove(igraph_strvector_t *v, long int elem) {
+    assert(v != 0);
+    assert(v->data != 0);
+    igraph_strvector_remove_section(v, elem, elem + 1);
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_move_interval
+ * \brief Copies an interval of a string vector.
+ */
+
+void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin,
+                                    long int end, long int to) {
+    long int i;
+    assert(v != 0);
+    assert(v->data != 0);
+    for (i = to; i < to + end - begin; i++) {
+        if (v->data[i] != 0) {
+            igraph_Free(v->data[i]);
+        }
+    }
+    for (i = 0; i < end - begin; i++) {
+        if (v->data[begin + i] != 0) {
+            size_t len = strlen(v->data[begin + i]) + 1;
+            v->data[to + i] = igraph_Calloc(len, char);
+            memcpy(v->data[to + i], v->data[begin + i], sizeof(char)*len);
+        }
+    }
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_copy
+ * \brief Initialization by copying.
+ *
+ * Initializes a string vector by copying another string vector.
+ * \param to Pointer to an uninitialized string vector.
+ * \param from The other string vector, to be copied.
+ * \return Error code.
+ *
+ * Time complexity: O(l), the total length of the strings in \p from.
+ */
+
+int igraph_strvector_copy(igraph_strvector_t *to,
+                          const igraph_strvector_t *from) {
+    long int i;
+    char *str;
+    assert(from != 0);
+    /*   assert(from->data != 0); */
+    to->data = igraph_Calloc(from->len, char*);
+    if (to->data == 0) {
+        IGRAPH_ERROR("Cannot copy string vector", IGRAPH_ENOMEM);
+    }
+    to->len = from->len;
+
+    for (i = 0; i < from->len; i++) {
+        int ret;
+        igraph_strvector_get(from, i, &str);
+        ret = igraph_strvector_set(to, i, str);
+        if (ret != 0) {
+            igraph_strvector_destroy(to);
+            IGRAPH_ERROR("cannot copy string vector", ret);
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_strvector_append
+ * Concatenate two string vectors.
+ *
+ * \param to The first string vector, the result is stored here.
+ * \param from The second string vector, it is kept unchanged.
+ * \return Error code.
+ *
+ * Time complexity: O(n+l2), n is the number of strings in the new
+ * string vector, l2 is the total length of strings in the \p from
+ * string vector.
+ */
+
+int igraph_strvector_append(igraph_strvector_t *to,
+                            const igraph_strvector_t *from) {
+    long int len1 = igraph_strvector_size(to), len2 = igraph_strvector_size(from);
+    long int i;
+    igraph_bool_t error = 0;
+    IGRAPH_CHECK(igraph_strvector_resize(to, len1 + len2));
+    for (i = 0; i < len2; i++) {
+        if (from->data[i][0] != '\0') {
+            igraph_Free(to->data[len1 + i]);
+            to->data[len1 + i] = strdup(from->data[i]);
+            if (!to->data[len1 + i]) {
+                error = 1;
+                break;
+            }
+        }
+    }
+    if (error) {
+        igraph_strvector_resize(to, len1);
+        IGRAPH_ERROR("Cannot append string vector", IGRAPH_ENOMEM);
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_strvector_clear
+ * Remove all elements
+ *
+ * After this operation the string vector will be empty.
+ * \param sv The string vector.
+ *
+ * Time complexity: O(l), the total length of strings, maybe less,
+ * depending on the memory manager.
+ */
+
+void igraph_strvector_clear(igraph_strvector_t *sv) {
+    long int i, n = igraph_strvector_size(sv);
+    char **tmp;
+
+    for (i = 0; i < n; i++) {
+        igraph_Free(sv->data[i]);
+    }
+    sv->len = 0;
+    /* try to give back some memory */
+    tmp = igraph_Realloc(sv->data, 1, char*);
+    if (tmp != 0) {
+        sv->data = tmp;
+    }
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_resize
+ * \brief Resize
+ *
+ * If the new size is bigger then empty strings are added, if it is
+ * smaller then the unneeded elements are removed.
+ * \param v The string vector.
+ * \param newsize The new size.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of strings if the vector is made
+ * bigger, O(l), the total length of the deleted strings if it is made
+ * smaller, maybe less, depending on memory management.
+ */
+
+int igraph_strvector_resize(igraph_strvector_t* v, long int newsize) {
+    long int toadd = newsize - v->len, i, j;
+    char **tmp;
+    long int reallocsize = newsize;
+    if (reallocsize == 0) {
+        reallocsize = 1;
+    }
+
+    assert(v != 0);
+    assert(v->data != 0);
+    /*   printf("resize %li to %li\n", v->len, newsize); */
+    if (newsize < v->len) {
+        for (i = newsize; i < v->len; i++) {
+            igraph_Free(v->data[i]);
+        }
+        /* try to give back some space */
+        tmp = igraph_Realloc(v->data, (size_t) reallocsize, char*);
+        /*     printf("resize %li to %li, %p\n", v->len, newsize, tmp); */
+        if (tmp != 0) {
+            v->data = tmp;
+        }
+    } else if (newsize > v->len) {
+        igraph_bool_t error = 0;
+        tmp = igraph_Realloc(v->data, (size_t) reallocsize, char*);
+        if (tmp == 0) {
+            IGRAPH_ERROR("cannot resize string vector", IGRAPH_ENOMEM);
+        }
+        v->data = tmp;
+
+        for (i = 0; i < toadd; i++) {
+            v->data[v->len + i] = igraph_Calloc(1, char);
+            if (v->data[v->len + i] == 0) {
+                error = 1;
+                break;
+            }
+            v->data[v->len + i][0] = '\0';
+        }
+        if (error) {
+            /* There was an error, free everything we've allocated so far */
+            for (j = 0; j < i; j++) {
+                if (v->data[v->len + i] != 0) {
+                    igraph_Free(v->data[v->len + i]);
+                }
+            }
+            /* Try to give back space */
+            tmp = igraph_Realloc(v->data, (size_t) (v->len), char*);
+            if (tmp != 0) {
+                v->data = tmp;
+            }
+            IGRAPH_ERROR("Cannot resize string vector", IGRAPH_ENOMEM);
+        }
+    }
+    v->len = newsize;
+
+    return 0;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_size
+ * \brief Gives the size of a string vector.
+ *
+ * \param sv The string vector.
+ * \return The length of the string vector.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_strvector_size(const igraph_strvector_t *sv) {
+    assert(sv != 0);
+    assert(sv->data != 0);
+    return sv->len;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_add
+ * \brief Adds an element to the back of a string vector.
+ *
+ * \param v The string vector.
+ * \param value The string to add, it will be copied.
+ * \return Error code.
+ *
+ * Time complexity: O(n+l), n is the total number of strings, l is the
+ * length of the new string.
+ */
+
+int igraph_strvector_add(igraph_strvector_t *v, const char *value) {
+    long int s = igraph_strvector_size(v);
+    char **tmp;
+    assert(v != 0);
+    assert(v->data != 0);
+    tmp = igraph_Realloc(v->data, (size_t) s + 1, char*);
+    if (tmp == 0) {
+        IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM);
+    }
+    v->data = tmp;
+    v->data[s] = igraph_Calloc(strlen(value) + 1, char);
+    if (v->data[s] == 0) {
+        IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM);
+    }
+    strcpy(v->data[s], value);
+    v->len += 1;
+
+    return 0;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_permdelete
+ * \brief Removes elements from a string vector (for internal use)
+ */
+
+void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index,
+                                 long int nremove) {
+    long int i;
+    char **tmp;
+    assert(v != 0);
+    assert(v->data != 0);
+
+    for (i = 0; i < igraph_strvector_size(v); i++) {
+        if (VECTOR(*index)[i] != 0) {
+            v->data[ (long int) VECTOR(*index)[i] - 1 ] = v->data[i];
+        } else {
+            igraph_Free(v->data[i]);
+        }
+    }
+    /* Try to make it shorter */
+    tmp = igraph_Realloc(v->data, v->len - nremove ?
+                         (size_t) (v->len - nremove) : 1, char*);
+    if (tmp != 0) {
+        v->data = tmp;
+    }
+    v->len -= nremove;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_remove_negidx
+ * \brief Removes elements from a string vector (for internal use)
+ */
+
+void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg,
+                                    long int nremove) {
+    long int i, idx = 0;
+    char **tmp;
+    assert(v != 0);
+    assert(v->data != 0);
+    for (i = 0; i < igraph_strvector_size(v); i++) {
+        if (VECTOR(*neg)[i] >= 0) {
+            v->data[idx++] = v->data[i];
+        } else {
+            igraph_Free(v->data[i]);
+        }
+    }
+    /* Try to give back some memory */
+    tmp = igraph_Realloc(v->data, v->len - nremove ?
+                         (size_t) (v->len - nremove) : 1, char*);
+    if (tmp != 0) {
+        v->data = tmp;
+    }
+    v->len -= nremove;
+}
+
+int igraph_strvector_print(const igraph_strvector_t *v, FILE *file,
+                           const char *sep) {
+
+    long int i, n = igraph_strvector_size(v);
+    if (n != 0) {
+        fprintf(file, "%s", STR(*v, 0));
+    }
+    for (i = 1; i < n; i++) {
+        fprintf(file, "%s%s", sep, STR(*v, i));
+    }
+    return 0;
+
+}
+
+int igraph_strvector_index(const igraph_strvector_t *v,
+                           igraph_strvector_t *newv,
+                           const igraph_vector_t *idx) {
+
+    long int i, newlen = igraph_vector_size(idx);
+    IGRAPH_CHECK(igraph_strvector_resize(newv, newlen));
+
+    for (i = 0; i < newlen; i++) {
+        long int j = (long int) VECTOR(*idx)[i];
+        char *str;
+        igraph_strvector_get(v, j, &str);
+        igraph_strvector_set(newv, i, str);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/igraph_trie.c b/igraph/src/igraph_trie.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/igraph_trie.c
@@ -0,0 +1,391 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_types_internal.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+
+/**
+ * \ingroup igraphtrie
+ * \brief Creates a trie node (not to be called directly)
+ * \return Error code: errors by igraph_strvector_init(),
+ *         igraph_vector_ptr_init() and igraph_vector_init() might be returned.
+ */
+
+int igraph_i_trie_init_node(igraph_trie_node_t *t) {
+    IGRAPH_STRVECTOR_INIT_FINALLY(&t->strs, 0);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&t->values, 0);
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+void igraph_i_trie_destroy_node(igraph_trie_node_t *t, igraph_bool_t sfree);
+
+/**
+ * \ingroup igraphtrie
+ * \brief Creates a trie.
+ * \return Error code: errors by igraph_strvector_init(),
+ *         igraph_vector_ptr_init() and igraph_vector_init() might be returned.
+ */
+
+int igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys) {
+    t->maxvalue = -1;
+    t->storekeys = storekeys;
+    IGRAPH_CHECK(igraph_i_trie_init_node( (igraph_trie_node_t *)t ));
+    IGRAPH_FINALLY(igraph_i_trie_destroy_node, t);
+    if (storekeys) {
+        IGRAPH_CHECK(igraph_strvector_init(&t->keys, 0));
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \ingroup igraphtrie
+ * \brief Destroys a node of a trie (not to be called directly).
+ */
+
+void igraph_i_trie_destroy_node(igraph_trie_node_t *t, igraph_bool_t sfree) {
+    long int i;
+    igraph_strvector_destroy(&t->strs);
+    for (i = 0; i < igraph_vector_ptr_size(&t->children); i++) {
+        igraph_trie_node_t *child = VECTOR(t->children)[i];
+        if (child != 0) {
+            igraph_i_trie_destroy_node(child, 1);
+        }
+    }
+    igraph_vector_ptr_destroy(&t->children);
+    igraph_vector_destroy(&t->values);
+    if (sfree) {
+        igraph_Free(t);
+    }
+}
+
+/**
+ * \ingroup igraphtrie
+ * \brief Destroys a trie (frees allocated memory).
+ */
+
+void igraph_trie_destroy(igraph_trie_t *t) {
+    if (t->storekeys) {
+        igraph_strvector_destroy(&t->keys);
+    }
+    igraph_i_trie_destroy_node( (igraph_trie_node_t*) t, 0);
+}
+
+
+/**
+ * \ingroup igraphtrie
+ * \brief Internal helping function for igraph_trie_t
+ */
+
+long int igraph_i_strdiff(const char *str, const char *key) {
+
+    long int diff = 0;
+    while (key[diff] != '\0' && str[diff] != '\0' && str[diff] == key[diff]) {
+        diff++;
+    }
+    return diff;
+}
+
+/**
+ * \ingroup igraphtrie
+ * \brief Search/insert in a trie (not to be called directly).
+ *
+ * @return Error code:
+ *         - <b>IGRAPH_ENOMEM</b>: out of memory
+ */
+
+int igraph_trie_get_node(igraph_trie_node_t *t, const char *key,
+                         igraph_real_t newvalue, long int *id) {
+    char *str;
+    long int i;
+    igraph_bool_t add;
+
+    /* If newvalue is negative, we don't add the node if nonexistent, only check
+     * for its existence */
+    add = (newvalue >= 0);
+
+    for (i = 0; i < igraph_strvector_size(&t->strs); i++) {
+        long int diff;
+        igraph_strvector_get(&t->strs, i, &str);
+        diff = igraph_i_strdiff(str, key);
+
+        if (diff == 0) {
+
+            /* ------------------------------------ */
+            /* No match, next */
+
+        } else if (str[diff] == '\0' && key[diff] == '\0') {
+
+            /* ------------------------------------ */
+            /* They are exactly the same */
+            if (VECTOR(t->values)[i] != -1) {
+                *id = (long int) VECTOR(t->values)[i];
+                return 0;
+            } else {
+                VECTOR(t->values)[i] = newvalue;
+                *id = (long int) newvalue;
+                return 0;
+            }
+
+        } else if (str[diff] == '\0') {
+
+            /* ------------------------------------ */
+            /* str is prefix of key, follow its link if there is one */
+            igraph_trie_node_t *node = VECTOR(t->children)[i];
+            if (node != 0) {
+                return igraph_trie_get_node(node, key + diff, newvalue, id);
+            } else if (add) {
+                igraph_trie_node_t *node = igraph_Calloc(1, igraph_trie_node_t);
+                if (node == 0) {
+                    IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM);
+                }
+                IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1);
+                IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1);
+                IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1);
+                IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, key + diff));
+                VECTOR(node->children)[0] = 0;
+                VECTOR(node->values)[0] = newvalue;
+
+                VECTOR(t->children)[i] = node;
+
+                *id = (long int) newvalue;
+                IGRAPH_FINALLY_CLEAN(3);
+                return 0;
+            } else {
+                *id = -1;
+                return 0;
+            }
+
+        } else if (key[diff] == '\0' && add) {
+
+            /* ------------------------------------ */
+            /* key is prefix of str, the node has to be cut */
+            char *str2;
+
+            igraph_trie_node_t *node = igraph_Calloc(1, igraph_trie_node_t);
+            if (node == 0) {
+                IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM);
+            }
+            IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1);
+            IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1);
+            IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1);
+            IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, str + diff));
+
+            VECTOR(node->children)[0] = VECTOR(t->children)[i];
+            VECTOR(node->values)[0] = VECTOR(t->values)[i];
+
+            str2 = strdup(str);
+            if (str2 == 0) {
+                IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM);
+            }
+            str2[diff] = '\0';
+            IGRAPH_FINALLY(free, str2);
+            IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2));
+            free(str2);
+            IGRAPH_FINALLY_CLEAN(4);
+
+            VECTOR(t->values)[i] = newvalue;
+            VECTOR(t->children)[i] = node;
+
+            *id = (long int) newvalue;
+            return 0;
+
+        } else if (add) {
+
+            /* ------------------------------------ */
+            /* the first diff characters match */
+            char *str2;
+
+            igraph_trie_node_t *node = igraph_Calloc(1, igraph_trie_node_t);
+            if (node == 0) {
+                IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM);
+            }
+            IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 2);
+            IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 2);
+            IGRAPH_VECTOR_INIT_FINALLY(&node->values, 2);
+            IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, str + diff));
+            IGRAPH_CHECK(igraph_strvector_set(&node->strs, 1, key + diff));
+            VECTOR(node->children)[0] = VECTOR(t->children)[i];
+            VECTOR(node->children)[1] = 0;
+            VECTOR(node->values)[0] = VECTOR(t->values)[i];
+            VECTOR(node->values)[1] = newvalue;
+
+            str2 = strdup(str);
+            if (str2 == 0) {
+                IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM);
+            }
+            str2[diff] = '\0';
+            IGRAPH_FINALLY(free, str2);
+            IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2));
+            free(str2);
+            IGRAPH_FINALLY_CLEAN(4);
+
+            VECTOR(t->values)[i] = -1;
+            VECTOR(t->children)[i] = node;
+
+            *id = (long int) newvalue;
+            return 0;
+        } else {
+
+            /* ------------------------------------------------- */
+            /* No match, but we requested not to add the new key */
+            *id = -1;
+            return 0;
+        }
+    }
+
+    /* ------------------------------------ */
+    /* Nothing matches */
+
+    if (add) {
+        IGRAPH_CHECK(igraph_vector_ptr_reserve(&t->children,
+                                               igraph_vector_ptr_size(&t->children) + 1));
+        IGRAPH_CHECK(igraph_vector_reserve(&t->values, igraph_vector_size(&t->values) + 1));
+        IGRAPH_CHECK(igraph_strvector_add(&t->strs, key));
+
+        igraph_vector_ptr_push_back(&t->children, 0); /* allocated */
+        igraph_vector_push_back(&t->values, newvalue); /* allocated */
+        *id = (long int) newvalue;
+    } else {
+        *id = -1;
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup igraphtrie
+ * \brief Search/insert in a trie.
+ */
+
+int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id) {
+    if (!t->storekeys) {
+        IGRAPH_CHECK(igraph_trie_get_node( (igraph_trie_node_t*) t,
+                                           key, t->maxvalue + 1, id));
+        if (*id > t->maxvalue) {
+            t->maxvalue = *id;
+        }
+        return 0;
+    } else {
+        int ret;
+        igraph_error_handler_t *oldhandler;
+        oldhandler = igraph_set_error_handler(igraph_error_handler_ignore);
+        /* Add it to the string vector first, we can undo this later */
+        ret = igraph_strvector_add(&t->keys, key);
+        if (ret != 0) {
+            igraph_set_error_handler(oldhandler);
+            IGRAPH_ERROR("cannot get element from trie", ret);
+        }
+        ret = igraph_trie_get_node( (igraph_trie_node_t*) t,
+                                    key, t->maxvalue + 1, id);
+        if (ret != 0) {
+            igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1);
+            igraph_set_error_handler(oldhandler);
+            IGRAPH_ERROR("cannot get element from trie", ret);
+        }
+
+        /* everything is fine */
+        if (*id > t->maxvalue) {
+            t->maxvalue = *id;
+        } else {
+            igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1);
+        }
+        igraph_set_error_handler(oldhandler);
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup igraphtrie
+ * \brief Search/insert in a trie (for internal use).
+ *
+ * @return Error code:
+ *         - <b>IGRAPH_ENOMEM</b>: out of memory
+ */
+
+int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length,
+                     long int *id) {
+    char *tmp = igraph_Calloc(length + 1, char);
+
+    if (tmp == 0) {
+        IGRAPH_ERROR("Cannot get from trie", IGRAPH_ENOMEM);
+    }
+
+    strncpy(tmp, key, length);
+    tmp[length] = '\0';
+    IGRAPH_FINALLY(free, tmp);
+    IGRAPH_CHECK(igraph_trie_get(t, tmp, id));
+    igraph_Free(tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \ingroup igraphtrie
+ * \brief Search in a trie.
+ * This variant does not add \c key to the trie if it does not exist.
+ * In this case, a negative id is returned.
+ */
+
+int igraph_trie_check(igraph_trie_t *t, const char *key, long int *id) {
+    IGRAPH_CHECK(igraph_trie_get_node( (igraph_trie_node_t*) t,
+                                       key, -1, id));
+    return 0;
+}
+
+/**
+ * \ingroup igraphtrie
+ * \brief Get an element of a trie based on its index.
+ */
+
+void igraph_trie_idx(igraph_trie_t *t, long int idx, char **str) {
+    igraph_strvector_get(&t->keys, idx, str);
+}
+
+/**
+ * \ingroup igraphtrie
+ * \brief Returns the size of a trie.
+ */
+
+long int igraph_trie_size(igraph_trie_t *t) {
+    return t->maxvalue + 1;
+}
+
+/* Hmmm, very dirty.... */
+
+int igraph_trie_getkeys(igraph_trie_t *t, const igraph_strvector_t **strv) {
+    *strv = &t->keys;
+    return 0;
+}
diff --git a/igraph/src/iio.c b/igraph/src/iio.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/iio.c
@@ -0,0 +1,159 @@
+#include "f2c.h"
+#include "fio.h"
+#include "fmt.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern char *f__icptr;
+char *f__icend;
+extern icilist *f__svic;
+int f__icnum;
+
+ int
+z_getc(Void)
+{
+	if(f__recpos++ < f__svic->icirlen) {
+		if(f__icptr >= f__icend) err(f__svic->iciend,(EOF),"endfile");
+		return(*(unsigned char *)f__icptr++);
+		}
+	return '\n';
+}
+
+ void
+#ifdef KR_headers
+z_putc(c)
+#else
+z_putc(int c)
+#endif
+{
+	if (f__icptr < f__icend && f__recpos++ < f__svic->icirlen)
+		*f__icptr++ = c;
+}
+
+ int
+z_rnew(Void)
+{
+	f__icptr = f__svic->iciunit + (++f__icnum)*f__svic->icirlen;
+	f__recpos = 0;
+	f__cursor = 0;
+	f__hiwater = 0;
+	return 1;
+}
+
+ static int
+z_endp(Void)
+{
+	(*f__donewrec)();
+	return 0;
+	}
+
+ int
+#ifdef KR_headers
+c_si(a) icilist *a;
+#else
+c_si(icilist *a)
+#endif
+{
+	f__elist = (cilist *)a;
+	f__fmtbuf=a->icifmt;
+	f__curunit = 0;
+	f__sequential=f__formatted=1;
+	f__external=0;
+	if(pars_f(f__fmtbuf)<0)
+		err(a->icierr,100,"startint");
+	fmt_bg();
+	f__cblank=f__cplus=f__scale=0;
+	f__svic=a;
+	f__icnum=f__recpos=0;
+	f__cursor = 0;
+	f__hiwater = 0;
+	f__icptr = a->iciunit;
+	f__icend = f__icptr + a->icirlen*a->icirnum;
+	f__cf = 0;
+	return(0);
+}
+
+ int
+iw_rev(Void)
+{
+	if(f__workdone)
+		z_endp();
+	f__hiwater = f__recpos = f__cursor = 0;
+	return(f__workdone=0);
+	}
+
+#ifdef KR_headers
+integer s_rsfi(a) icilist *a;
+#else
+integer s_rsfi(icilist *a)
+#endif
+{	int n;
+	if(n=c_si(a)) return(n);
+	f__reading=1;
+	f__doed=rd_ed;
+	f__doned=rd_ned;
+	f__getn=z_getc;
+	f__dorevert = z_endp;
+	f__donewrec = z_rnew;
+	f__doend = z_endp;
+	return(0);
+}
+
+ int
+z_wnew(Void)
+{
+	if (f__recpos < f__hiwater) {
+		f__icptr += f__hiwater - f__recpos;
+		f__recpos = f__hiwater;
+		}
+	while(f__recpos++ < f__svic->icirlen)
+		*f__icptr++ = ' ';
+	f__recpos = 0;
+	f__cursor = 0;
+	f__hiwater = 0;
+	f__icnum++;
+	return 1;
+}
+#ifdef KR_headers
+integer s_wsfi(a) icilist *a;
+#else
+integer s_wsfi(icilist *a)
+#endif
+{	int n;
+	if(n=c_si(a)) return(n);
+	f__reading=0;
+	f__doed=w_ed;
+	f__doned=w_ned;
+	f__putn=z_putc;
+	f__dorevert = iw_rev;
+	f__donewrec = z_wnew;
+	f__doend = z_endp;
+	return(0);
+}
+integer e_rsfi(Void)
+{	int n = en_fio();
+	f__fmtbuf = NULL;
+	return(n);
+}
+integer e_wsfi(Void)
+{
+	int n;
+	n = en_fio();
+	f__fmtbuf = NULL;
+	if(f__svic->icirnum != 1
+	 && (f__icnum >  f__svic->icirnum
+	 || (f__icnum == f__svic->icirnum && (f__recpos | f__hiwater))))
+		err(f__svic->icierr,110,"inwrite");
+	if (f__recpos < f__hiwater)
+		f__recpos = f__hiwater;
+	if (f__recpos >= f__svic->icirlen)
+		err(f__svic->icierr,110,"recend");
+	if (!f__recpos && f__icnum)
+		return n;
+	while(f__recpos++ < f__svic->icirlen)
+		*f__icptr++ = ' ';
+	return n;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/iladlc.c b/igraph/src/iladlc.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/iladlc.c
@@ -0,0 +1,134 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b ILADLC scans a matrix for its last non-zero column.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download ILADLC + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/iladlc.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/iladlc.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/iladlc.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         INTEGER FUNCTION ILADLC( M, N, A, LDA )   
+
+         INTEGER            M, N, LDA   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > ILADLC scans A for its last non-zero column.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The m by n matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A. LDA >= max(1,M).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    ===================================================================== */
+integer igraphiladlc_(integer *m, integer *n, doublereal *a, integer *lda)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, ret_val, i__1;
+
+    /* Local variables */
+    integer i__;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Quick test for the common case where one corner is non-zero.   
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    if (*n == 0) {
+	ret_val = *n;
+    } else if (a[*n * a_dim1 + 1] != 0. || a[*m + *n * a_dim1] != 0.) {
+	ret_val = *n;
+    } else {
+/*     Now scan each column from the end, returning with the first non-zero. */
+	for (ret_val = *n; ret_val >= 1; --ret_val) {
+	    i__1 = *m;
+	    for (i__ = 1; i__ <= i__1; ++i__) {
+		if (a[i__ + ret_val * a_dim1] != 0.) {
+		    return ret_val;
+		}
+	    }
+	}
+    }
+    return ret_val;
+} /* igraphiladlc_ */
+
diff --git a/igraph/src/iladlr.c b/igraph/src/iladlr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/iladlr.c
@@ -0,0 +1,135 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b ILADLR scans a matrix for its last non-zero row.   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download ILADLR + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/iladlr.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/iladlr.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/iladlr.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         INTEGER FUNCTION ILADLR( M, N, A, LDA )   
+
+         INTEGER            M, N, LDA   
+         DOUBLE PRECISION   A( LDA, * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > ILADLR scans A for its last non-zero row.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] M   
+   > \verbatim   
+   >          M is INTEGER   
+   >          The number of rows of the matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is INTEGER   
+   >          The number of columns of the matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] A   
+   > \verbatim   
+   >          A is DOUBLE PRECISION array, dimension (LDA,N)   
+   >          The m by n matrix A.   
+   > \endverbatim   
+   >   
+   > \param[in] LDA   
+   > \verbatim   
+   >          LDA is INTEGER   
+   >          The leading dimension of the array A. LDA >= max(1,M).   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date September 2012   
+
+   > \ingroup auxOTHERauxiliary   
+
+    ===================================================================== */
+integer igraphiladlr_(integer *m, integer *n, doublereal *a, integer *lda)
+{
+    /* System generated locals */
+    integer a_dim1, a_offset, ret_val, i__1;
+
+    /* Local variables */
+    integer i__, j;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.2) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       September 2012   
+
+
+    =====================================================================   
+
+
+       Quick test for the common case where one corner is non-zero.   
+       Parameter adjustments */
+    a_dim1 = *lda;
+    a_offset = 1 + a_dim1;
+    a -= a_offset;
+
+    /* Function Body */
+    if (*m == 0) {
+	ret_val = *m;
+    } else if (a[*m + a_dim1] != 0. || a[*m + *n * a_dim1] != 0.) {
+	ret_val = *m;
+    } else {
+/*     Scan up each column tracking the last zero row seen. */
+	ret_val = 0;
+	i__1 = *n;
+	for (j = 1; j <= i__1; ++j) {
+	    i__ = *m;
+	    while(a[max(i__,1) + j * a_dim1] == 0. && i__ >= 1) {
+		--i__;
+	    }
+	    ret_val = max(ret_val,i__);
+	}
+    }
+    return ret_val;
+} /* igraphiladlr_ */
+
diff --git a/igraph/src/ilaenv.c b/igraph/src/ilaenv.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ilaenv.c
@@ -0,0 +1,714 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+static real c_b163 = 0.f;
+static real c_b164 = 1.f;
+static integer c__0 = 0;
+
+/* > \brief \b ILAENV   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download ILAENV + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/ilaenv.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/ilaenv.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/ilaenv.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         INTEGER FUNCTION ILAENV( ISPEC, NAME, OPTS, N1, N2, N3, N4 )   
+
+         CHARACTER*( * )    NAME, OPTS   
+         INTEGER            ISPEC, N1, N2, N3, N4   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > ILAENV is called from the LAPACK routines to choose problem-dependent   
+   > parameters for the local environment.  See ISPEC for a description of   
+   > the parameters.   
+   >   
+   > ILAENV returns an INTEGER   
+   > if ILAENV >= 0: ILAENV returns the value of the parameter specified by ISPEC   
+   > if ILAENV < 0:  if ILAENV = -k, the k-th argument had an illegal value.   
+   >   
+   > This version provides a set of parameters which should give good,   
+   > but not optimal, performance on many of the currently available   
+   > computers.  Users are encouraged to modify this subroutine to set   
+   > the tuning parameters for their particular machine using the option   
+   > and problem size information in the arguments.   
+   >   
+   > This routine will not function correctly if it is converted to all   
+   > lower case.  Converting it to all upper case is allowed.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] ISPEC   
+   > \verbatim   
+   >          ISPEC is INTEGER   
+   >          Specifies the parameter to be returned as the value of   
+   >          ILAENV.   
+   >          = 1: the optimal blocksize; if this value is 1, an unblocked   
+   >               algorithm will give the best performance.   
+   >          = 2: the minimum block size for which the block routine   
+   >               should be used; if the usable block size is less than   
+   >               this value, an unblocked routine should be used.   
+   >          = 3: the crossover point (in a block routine, for N less   
+   >               than this value, an unblocked routine should be used)   
+   >          = 4: the number of shifts, used in the nonsymmetric   
+   >               eigenvalue routines (DEPRECATED)   
+   >          = 5: the minimum column dimension for blocking to be used;   
+   >               rectangular blocks must have dimension at least k by m,   
+   >               where k is given by ILAENV(2,...) and m by ILAENV(5,...)   
+   >          = 6: the crossover point for the SVD (when reducing an m by n   
+   >               matrix to bidiagonal form, if max(m,n)/min(m,n) exceeds   
+   >               this value, a QR factorization is used first to reduce   
+   >               the matrix to a triangular form.)   
+   >          = 7: the number of processors   
+   >          = 8: the crossover point for the multishift QR method   
+   >               for nonsymmetric eigenvalue problems (DEPRECATED)   
+   >          = 9: maximum size of the subproblems at the bottom of the   
+   >               computation tree in the divide-and-conquer algorithm   
+   >               (used by xGELSD and xGESDD)   
+   >          =10: ieee NaN arithmetic can be trusted not to trap   
+   >          =11: infinity arithmetic can be trusted not to trap   
+   >          12 <= ISPEC <= 16:   
+   >               xHSEQR or one of its subroutines,   
+   >               see IPARMQ for detailed explanation   
+   > \endverbatim   
+   >   
+   > \param[in] NAME   
+   > \verbatim   
+   >          NAME is CHARACTER*(*)   
+   >          The name of the calling subroutine, in either upper case or   
+   >          lower case.   
+   > \endverbatim   
+   >   
+   > \param[in] OPTS   
+   > \verbatim   
+   >          OPTS is CHARACTER*(*)   
+   >          The character options to the subroutine NAME, concatenated   
+   >          into a single character string.  For example, UPLO = 'U',   
+   >          TRANS = 'T', and DIAG = 'N' for a triangular routine would   
+   >          be specified as OPTS = 'UTN'.   
+   > \endverbatim   
+   >   
+   > \param[in] N1   
+   > \verbatim   
+   >          N1 is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] N2   
+   > \verbatim   
+   >          N2 is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] N3   
+   > \verbatim   
+   >          N3 is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] N4   
+   > \verbatim   
+   >          N4 is INTEGER   
+   >          Problem dimensions for the subroutine NAME; these may not all   
+   >          be required.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >  The following conventions have been used when calling ILAENV from the   
+   >  LAPACK routines:   
+   >  1)  OPTS is a concatenation of all of the character options to   
+   >      subroutine NAME, in the same order that they appear in the   
+   >      argument list for NAME, even if they are not used in determining   
+   >      the value of the parameter specified by ISPEC.   
+   >  2)  The problem dimensions N1, N2, N3, N4 are specified in the order   
+   >      that they appear in the argument list for NAME.  N1 is used   
+   >      first, N2 second, and so on, and unused problem dimensions are   
+   >      passed a value of -1.   
+   >  3)  The parameter value returned by ILAENV is checked for validity in   
+   >      the calling subroutine.  For example, ILAENV is used to retrieve   
+   >      the optimal blocksize for STRTRI as follows:   
+   >   
+   >      NB = ILAENV( 1, 'STRTRI', UPLO // DIAG, N, -1, -1, -1 )   
+   >      IF( NB.LE.1 ) NB = MAX( 1, N )   
+   > \endverbatim   
+   >   
+    ===================================================================== */
+integer igraphilaenv_(integer *ispec, char *name__, char *opts, integer *n1, 
+	integer *n2, integer *n3, integer *n4, ftnlen name_len, ftnlen 
+	opts_len)
+{
+    /* System generated locals */
+    integer ret_val;
+
+    /* Builtin functions   
+       Subroutine */ int s_copy(char *, char *, ftnlen, ftnlen);
+    integer s_cmp(char *, char *, ftnlen, ftnlen);
+
+    /* Local variables */
+    integer i__;
+    char c1[1], c2[2], c3[3], c4[2];
+    integer ic, nb, iz, nx;
+    logical cname;
+    integer nbmin;
+    logical sname;
+    extern integer igraphieeeck_(integer *, real *, real *);
+    char subnam[6];
+    extern integer igraphiparmq_(integer *, char *, char *, integer *, integer *, 
+	    integer *, integer *);
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    ===================================================================== */
+
+
+    switch (*ispec) {
+	case 1:  goto L10;
+	case 2:  goto L10;
+	case 3:  goto L10;
+	case 4:  goto L80;
+	case 5:  goto L90;
+	case 6:  goto L100;
+	case 7:  goto L110;
+	case 8:  goto L120;
+	case 9:  goto L130;
+	case 10:  goto L140;
+	case 11:  goto L150;
+	case 12:  goto L160;
+	case 13:  goto L160;
+	case 14:  goto L160;
+	case 15:  goto L160;
+	case 16:  goto L160;
+    }
+
+/*     Invalid value for ISPEC */
+
+    ret_val = -1;
+    return ret_val;
+
+L10:
+
+/*     Convert NAME to upper case if the first character is lower case. */
+
+    ret_val = 1;
+    s_copy(subnam, name__, (ftnlen)6, name_len);
+    ic = *(unsigned char *)subnam;
+    iz = 'Z';
+    if (iz == 90 || iz == 122) {
+
+/*        ASCII character set */
+
+	if (ic >= 97 && ic <= 122) {
+	    *(unsigned char *)subnam = (char) (ic - 32);
+	    for (i__ = 2; i__ <= 6; ++i__) {
+		ic = *(unsigned char *)&subnam[i__ - 1];
+		if (ic >= 97 && ic <= 122) {
+		    *(unsigned char *)&subnam[i__ - 1] = (char) (ic - 32);
+		}
+/* L20: */
+	    }
+	}
+
+    } else if (iz == 233 || iz == 169) {
+
+/*        EBCDIC character set */
+
+	if (ic >= 129 && ic <= 137 || ic >= 145 && ic <= 153 || ic >= 162 && 
+		ic <= 169) {
+	    *(unsigned char *)subnam = (char) (ic + 64);
+	    for (i__ = 2; i__ <= 6; ++i__) {
+		ic = *(unsigned char *)&subnam[i__ - 1];
+		if (ic >= 129 && ic <= 137 || ic >= 145 && ic <= 153 || ic >= 
+			162 && ic <= 169) {
+		    *(unsigned char *)&subnam[i__ - 1] = (char) (ic + 64);
+		}
+/* L30: */
+	    }
+	}
+
+    } else if (iz == 218 || iz == 250) {
+
+/*        Prime machines:  ASCII+128 */
+
+	if (ic >= 225 && ic <= 250) {
+	    *(unsigned char *)subnam = (char) (ic - 32);
+	    for (i__ = 2; i__ <= 6; ++i__) {
+		ic = *(unsigned char *)&subnam[i__ - 1];
+		if (ic >= 225 && ic <= 250) {
+		    *(unsigned char *)&subnam[i__ - 1] = (char) (ic - 32);
+		}
+/* L40: */
+	    }
+	}
+    }
+
+    *(unsigned char *)c1 = *(unsigned char *)subnam;
+    sname = *(unsigned char *)c1 == 'S' || *(unsigned char *)c1 == 'D';
+    cname = *(unsigned char *)c1 == 'C' || *(unsigned char *)c1 == 'Z';
+    if (! (cname || sname)) {
+	return ret_val;
+    }
+    s_copy(c2, subnam + 1, (ftnlen)2, (ftnlen)2);
+    s_copy(c3, subnam + 3, (ftnlen)3, (ftnlen)3);
+    s_copy(c4, c3 + 1, (ftnlen)2, (ftnlen)2);
+
+    switch (*ispec) {
+	case 1:  goto L50;
+	case 2:  goto L60;
+	case 3:  goto L70;
+    }
+
+L50:
+
+/*     ISPEC = 1:  block size   
+
+       In these examples, separate code is provided for setting NB for   
+       real and complex.  We assume that NB will take the same value in   
+       single or double precision. */
+
+    nb = 1;
+
+    if (s_cmp(c2, "GE", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nb = 64;
+	    } else {
+		nb = 64;
+	    }
+	} else if (s_cmp(c3, "QRF", (ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, 
+		"RQF", (ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "LQF", (ftnlen)
+		3, (ftnlen)3) == 0 || s_cmp(c3, "QLF", (ftnlen)3, (ftnlen)3) 
+		== 0) {
+	    if (sname) {
+		nb = 32;
+	    } else {
+		nb = 32;
+	    }
+	} else if (s_cmp(c3, "HRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nb = 32;
+	    } else {
+		nb = 32;
+	    }
+	} else if (s_cmp(c3, "BRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nb = 32;
+	    } else {
+		nb = 32;
+	    }
+	} else if (s_cmp(c3, "TRI", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nb = 64;
+	    } else {
+		nb = 64;
+	    }
+	}
+    } else if (s_cmp(c2, "PO", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nb = 64;
+	    } else {
+		nb = 64;
+	    }
+	}
+    } else if (s_cmp(c2, "SY", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nb = 64;
+	    } else {
+		nb = 64;
+	    }
+	} else if (sname && s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    nb = 32;
+	} else if (sname && s_cmp(c3, "GST", (ftnlen)3, (ftnlen)3) == 0) {
+	    nb = 64;
+	}
+    } else if (cname && s_cmp(c2, "HE", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) {
+	    nb = 64;
+	} else if (s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    nb = 32;
+	} else if (s_cmp(c3, "GST", (ftnlen)3, (ftnlen)3) == 0) {
+	    nb = 64;
+	}
+    } else if (sname && s_cmp(c2, "OR", (ftnlen)2, (ftnlen)2) == 0) {
+	if (*(unsigned char *)c3 == 'G') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nb = 32;
+	    }
+	} else if (*(unsigned char *)c3 == 'M') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nb = 32;
+	    }
+	}
+    } else if (cname && s_cmp(c2, "UN", (ftnlen)2, (ftnlen)2) == 0) {
+	if (*(unsigned char *)c3 == 'G') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nb = 32;
+	    }
+	} else if (*(unsigned char *)c3 == 'M') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nb = 32;
+	    }
+	}
+    } else if (s_cmp(c2, "GB", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		if (*n4 <= 64) {
+		    nb = 1;
+		} else {
+		    nb = 32;
+		}
+	    } else {
+		if (*n4 <= 64) {
+		    nb = 1;
+		} else {
+		    nb = 32;
+		}
+	    }
+	}
+    } else if (s_cmp(c2, "PB", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		if (*n2 <= 64) {
+		    nb = 1;
+		} else {
+		    nb = 32;
+		}
+	    } else {
+		if (*n2 <= 64) {
+		    nb = 1;
+		} else {
+		    nb = 32;
+		}
+	    }
+	}
+    } else if (s_cmp(c2, "TR", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRI", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nb = 64;
+	    } else {
+		nb = 64;
+	    }
+	}
+    } else if (s_cmp(c2, "LA", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "UUM", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nb = 64;
+	    } else {
+		nb = 64;
+	    }
+	}
+    } else if (sname && s_cmp(c2, "ST", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "EBZ", (ftnlen)3, (ftnlen)3) == 0) {
+	    nb = 1;
+	}
+    }
+    ret_val = nb;
+    return ret_val;
+
+L60:
+
+/*     ISPEC = 2:  minimum block size */
+
+    nbmin = 2;
+    if (s_cmp(c2, "GE", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "QRF", (ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "RQF", (
+		ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "LQF", (ftnlen)3, (
+		ftnlen)3) == 0 || s_cmp(c3, "QLF", (ftnlen)3, (ftnlen)3) == 0)
+		 {
+	    if (sname) {
+		nbmin = 2;
+	    } else {
+		nbmin = 2;
+	    }
+	} else if (s_cmp(c3, "HRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nbmin = 2;
+	    } else {
+		nbmin = 2;
+	    }
+	} else if (s_cmp(c3, "BRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nbmin = 2;
+	    } else {
+		nbmin = 2;
+	    }
+	} else if (s_cmp(c3, "TRI", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nbmin = 2;
+	    } else {
+		nbmin = 2;
+	    }
+	}
+    } else if (s_cmp(c2, "SY", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nbmin = 8;
+	    } else {
+		nbmin = 8;
+	    }
+	} else if (sname && s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    nbmin = 2;
+	}
+    } else if (cname && s_cmp(c2, "HE", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    nbmin = 2;
+	}
+    } else if (sname && s_cmp(c2, "OR", (ftnlen)2, (ftnlen)2) == 0) {
+	if (*(unsigned char *)c3 == 'G') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nbmin = 2;
+	    }
+	} else if (*(unsigned char *)c3 == 'M') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nbmin = 2;
+	    }
+	}
+    } else if (cname && s_cmp(c2, "UN", (ftnlen)2, (ftnlen)2) == 0) {
+	if (*(unsigned char *)c3 == 'G') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nbmin = 2;
+	    }
+	} else if (*(unsigned char *)c3 == 'M') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nbmin = 2;
+	    }
+	}
+    }
+    ret_val = nbmin;
+    return ret_val;
+
+L70:
+
+/*     ISPEC = 3:  crossover point */
+
+    nx = 0;
+    if (s_cmp(c2, "GE", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "QRF", (ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "RQF", (
+		ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "LQF", (ftnlen)3, (
+		ftnlen)3) == 0 || s_cmp(c3, "QLF", (ftnlen)3, (ftnlen)3) == 0)
+		 {
+	    if (sname) {
+		nx = 128;
+	    } else {
+		nx = 128;
+	    }
+	} else if (s_cmp(c3, "HRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nx = 128;
+	    } else {
+		nx = 128;
+	    }
+	} else if (s_cmp(c3, "BRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    if (sname) {
+		nx = 128;
+	    } else {
+		nx = 128;
+	    }
+	}
+    } else if (s_cmp(c2, "SY", (ftnlen)2, (ftnlen)2) == 0) {
+	if (sname && s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    nx = 32;
+	}
+    } else if (cname && s_cmp(c2, "HE", (ftnlen)2, (ftnlen)2) == 0) {
+	if (s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) {
+	    nx = 32;
+	}
+    } else if (sname && s_cmp(c2, "OR", (ftnlen)2, (ftnlen)2) == 0) {
+	if (*(unsigned char *)c3 == 'G') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nx = 128;
+	    }
+	}
+    } else if (cname && s_cmp(c2, "UN", (ftnlen)2, (ftnlen)2) == 0) {
+	if (*(unsigned char *)c3 == 'G') {
+	    if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", 
+		    (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, (
+		    ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) ==
+		     0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(
+		    c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", (
+		    ftnlen)2, (ftnlen)2) == 0) {
+		nx = 128;
+	    }
+	}
+    }
+    ret_val = nx;
+    return ret_val;
+
+L80:
+
+/*     ISPEC = 4:  number of shifts (used by xHSEQR) */
+
+    ret_val = 6;
+    return ret_val;
+
+L90:
+
+/*     ISPEC = 5:  minimum column dimension (not used) */
+
+    ret_val = 2;
+    return ret_val;
+
+L100:
+
+/*     ISPEC = 6:  crossover point for SVD (used by xGELSS and xGESVD) */
+
+    ret_val = (integer) ((real) min(*n1,*n2) * 1.6f);
+    return ret_val;
+
+L110:
+
+/*     ISPEC = 7:  number of processors (not used) */
+
+    ret_val = 1;
+    return ret_val;
+
+L120:
+
+/*     ISPEC = 8:  crossover point for multishift (used by xHSEQR) */
+
+    ret_val = 50;
+    return ret_val;
+
+L130:
+
+/*     ISPEC = 9:  maximum size of the subproblems at the bottom of the   
+                   computation tree in the divide-and-conquer algorithm   
+                   (used by xGELSD and xGESDD) */
+
+    ret_val = 25;
+    return ret_val;
+
+L140:
+
+/*     ISPEC = 10: ieee NaN arithmetic can be trusted not to trap   
+
+       ILAENV = 0 */
+    ret_val = 1;
+    if (ret_val == 1) {
+	ret_val = igraphieeeck_(&c__1, &c_b163, &c_b164);
+    }
+    return ret_val;
+
+L150:
+
+/*     ISPEC = 11: infinity arithmetic can be trusted not to trap   
+
+       ILAENV = 0 */
+    ret_val = 1;
+    if (ret_val == 1) {
+	ret_val = igraphieeeck_(&c__0, &c_b163, &c_b164);
+    }
+    return ret_val;
+
+L160:
+
+/*     12 <= ISPEC <= 16: xHSEQR or one of its subroutines. */
+
+    ret_val = igraphiparmq_(ispec, name__, opts, n1, n2, n3, n4)
+	    ;
+    return ret_val;
+
+/*     End of ILAENV */
+
+} /* igraphilaenv_ */
+
diff --git a/igraph/src/ilnw.c b/igraph/src/ilnw.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ilnw.c
@@ -0,0 +1,83 @@
+#include "f2c.h"
+#include "fio.h"
+#include "lio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern char *f__icptr;
+extern char *f__icend;
+extern icilist *f__svic;
+extern int f__icnum;
+#ifdef KR_headers
+extern void z_putc();
+#else
+extern void z_putc(int);
+#endif
+
+ static int
+z_wSL(Void)
+{
+	while(f__recpos < f__svic->icirlen)
+		z_putc(' ');
+	return z_rnew();
+	}
+
+ static void
+#ifdef KR_headers
+c_liw(a) icilist *a;
+#else
+c_liw(icilist *a)
+#endif
+{
+	f__reading = 0;
+	f__external = 0;
+	f__formatted = 1;
+	f__putn = z_putc;
+	L_len = a->icirlen;
+	f__donewrec = z_wSL;
+	f__svic = a;
+	f__icnum = f__recpos = 0;
+	f__cursor = 0;
+	f__cf = 0;
+	f__curunit = 0;
+	f__icptr = a->iciunit;
+	f__icend = f__icptr + a->icirlen*a->icirnum;
+	f__elist = (cilist *)a;
+	}
+
+ integer
+#ifdef KR_headers
+s_wsni(a) icilist *a;
+#else
+s_wsni(icilist *a)
+#endif
+{
+	cilist ca;
+
+	c_liw(a);
+	ca.cifmt = a->icifmt;
+	x_wsne(&ca);
+	z_wSL();
+	return 0;
+	}
+
+ integer
+#ifdef KR_headers
+s_wsli(a) icilist *a;
+#else
+s_wsli(icilist *a)
+#endif
+{
+	f__lioproc = l_write;
+	c_liw(a);
+	return(0);
+	}
+
+integer e_wsli(Void)
+{
+	z_wSL();
+	return(0);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/infomap.cc b/igraph/src/infomap.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/infomap.cc
@@ -0,0 +1,322 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+   ----
+   The original version of this file was written by Martin Rosvall
+   email: martin.rosvall@physics.umu.se
+   homePage: http://www.tp.umu.se/~rosvall/
+
+   It was integrated in igraph by Emmanuel Navarro
+   email: navarro@irit.fr
+   homePage: http://www.irit.fr/~Emmanuel.Navarro/
+*/
+
+#include <cmath>
+#include "igraph_interface.h"
+#include "igraph_community.h"
+#include "igraph_interrupt_internal.h"
+
+
+#include "infomap_Node.h"
+#include "infomap_Greedy.h"
+
+/****************************************************************************/
+int infomap_partition(FlowGraph * fgraph, bool rcall) {
+    Greedy * greedy;
+
+    // save the original graph
+    FlowGraph * cpy_fgraph = new FlowGraph(fgraph);
+    IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph);
+
+    int Nnode = cpy_fgraph->Nnode;
+    // "real" number of vertex, ie. number of vertex of the graph
+
+    int iteration = 0;
+    double outer_oldCodeLength, newCodeLength;
+
+    int *initial_move = NULL;
+    bool initial_move_done = true;
+
+    do { // Main loop
+        outer_oldCodeLength = fgraph->codeLength;
+
+        if (iteration > 0) {
+            /**********************************************************************/
+            //  FIRST PART: re-split the network (if need)
+            // ===========================================
+
+            // intial_move indicate current clustering
+            initial_move = new int[Nnode];
+            // new_cluster_id --> old_cluster_id (save curent clustering state)
+
+            IGRAPH_FINALLY(operator delete [], initial_move);
+            initial_move_done = false;
+
+            int *subMoveTo = NULL; // enventual new partitionment of original graph
+
+            if ((iteration % 2 == 0) && (fgraph->Nnode > 1)) {
+                // 0/ Submodule movements : partition each module of the
+                // current partition (rec. call)
+
+                subMoveTo = new int[Nnode];
+                // vid_cpy_fgraph  --> new_cluster_id (new partition)
+
+                IGRAPH_FINALLY(operator delete [], subMoveTo);
+
+                int subModIndex = 0;
+
+                for (int i = 0 ; i < fgraph->Nnode ; i++) {
+                    // partition each non trivial module
+                    int sub_Nnode = fgraph->node[i]->members.size();
+                    if (sub_Nnode > 1) { // If the module is not trivial
+                        int *sub_members  = new int[sub_Nnode];      // id_sub --> id
+                        IGRAPH_FINALLY(operator delete [], sub_members);
+
+                        for (int j = 0 ; j < sub_Nnode ; j++) {
+                            sub_members[j] = fgraph->node[i]->members[j];
+                        }
+
+                        // extraction of the subgraph
+                        FlowGraph *sub_fgraph = new FlowGraph(cpy_fgraph, sub_Nnode,
+                                                              sub_members);
+                        IGRAPH_FINALLY(delete_FlowGraph, sub_fgraph);
+                        sub_fgraph->initiate();
+
+                        // recursif call of partitionment on the subgraph
+                        infomap_partition(sub_fgraph, true);
+
+                        // Record membership changes
+                        for (int j = 0; j < sub_fgraph->Nnode; j++) {
+                            int Nmembers = sub_fgraph->node[j]->members.size();
+                            for (int k = 0; k < Nmembers; k++) {
+                                subMoveTo[sub_members[sub_fgraph->node[j]->members[k]]] =
+                                    subModIndex;
+                            }
+                            initial_move[subModIndex] = i;
+                            subModIndex++;
+                        }
+
+                        delete sub_fgraph;
+                        IGRAPH_FINALLY_CLEAN(1);
+                        delete [] sub_members;
+                        IGRAPH_FINALLY_CLEAN(1);
+                    } else {
+                        subMoveTo[fgraph->node[i]->members[0]] = subModIndex;
+                        initial_move[subModIndex] = i;
+                        subModIndex++;
+                    }
+                }
+            } else {
+                // 1/ Single-node movements : allows each node to move (again)
+                // save current modules
+                for (int i = 0; i < fgraph->Nnode; i++) { // for each module
+                    int Nmembers = fgraph->node[i]->members.size(); // Module size
+                    for (int j = 0; j < Nmembers; j++) { // for each vertex (of the module)
+                        initial_move[fgraph->node[i]->members[j]] = i;
+                    }
+                }
+            }
+
+            fgraph->back_to(cpy_fgraph);
+            if (subMoveTo) {
+                Greedy *cpy_greedy = new Greedy(fgraph);
+                IGRAPH_FINALLY(delete_Greedy, cpy_greedy);
+
+                cpy_greedy->setMove(subMoveTo);
+                cpy_greedy->apply(false);
+
+                delete_Greedy(cpy_greedy);
+                IGRAPH_FINALLY_CLEAN(1);
+                delete [] subMoveTo;
+                IGRAPH_FINALLY_CLEAN(1);
+            }
+        }
+        /**********************************************************************/
+        //  SECOND PART: greedy optimizing it self
+        // ===========================================
+        double oldCodeLength;
+
+        do {
+            // greedy optimizing object creation
+            greedy = new Greedy(fgraph);
+            IGRAPH_FINALLY(delete_Greedy, greedy);
+
+            // Initial move to apply ?
+            if (!initial_move_done && initial_move) {
+                initial_move_done = true;
+                greedy->setMove(initial_move);
+            }
+
+            oldCodeLength = greedy->codeLength;
+            bool moved = true;
+            int Nloops = 0;
+            //int count = 0;
+            double inner_oldCodeLength = 1000;
+
+            while (moved) { // main greedy optimizing loop
+                inner_oldCodeLength = greedy->codeLength;
+                moved = greedy->optimize();
+
+                Nloops++;
+                //count++;
+
+                if (fabs(greedy->codeLength - inner_oldCodeLength) < 1.0e-10)
+                    // if the move does'n reduce the codelenght -> exit !
+                {
+                    moved = false;
+                }
+
+                //if (count == 10) {
+                //  greedy->tune();
+                //  count = 0;
+                //}
+            }
+
+            // transform the network to network of modules:
+            greedy->apply(true);
+            newCodeLength = greedy->codeLength;
+
+            // destroy greedy object
+            delete greedy;
+            IGRAPH_FINALLY_CLEAN(1);
+
+        } while (oldCodeLength - newCodeLength >  1.0e-10);
+        // while there is some improvement
+
+        if (iteration > 0) {
+            delete [] initial_move;
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        iteration++;
+        if (!rcall) {
+            IGRAPH_ALLOW_INTERRUPTION();
+        }
+    } while (outer_oldCodeLength - newCodeLength > 1.0e-10);
+
+    delete cpy_fgraph;
+    IGRAPH_FINALLY_CLEAN(1);
+    return IGRAPH_SUCCESS;
+}
+
+
+/**
+ * \function igraph_community_infomap
+ * \brief Find community structure that minimizes the expected
+ * description length of a random walker trajectory.
+ *
+ * Implementation of the InfoMap community detection algorithm.of
+ * Martin Rosvall and Carl T. Bergstrom.
+ *
+ * See :
+ * Visualization of the math and the map generator: www.mapequation.org
+ * [2] The original paper: M. Rosvall and C. T. Bergstrom, Maps of
+ * information flow reveal community structure in complex networks, PNAS
+ * 105, 1118 (2008) [http://dx.doi.org/10.1073/pnas.0706851105 ,
+ * http://arxiv.org/abs/0707.0609 ]
+ * [3] A more detailed paper: M. Rosvall, D. Axelsson, and C. T. Bergstrom,
+ * The map equation, Eur. Phys. J. Special Topics 178, 13 (2009).
+ * [http://dx.doi.org/10.1140/epjst/e2010-01179-1 ,
+ * http://arxiv.org/abs/0906.1405 ]
+
+ * </para><para>
+ * The original C++ implementation of Martin Rosvall is used,
+ * see http://www.tp.umu.se/~rosvall/downloads/infomap_undir.tgz .
+ * Intergation in igraph has be done by Emmanuel Navarro (who is grateful to
+  * Martin Rosvall and Carl T. Bergstrom for providing this source code.)
+ *
+ * </para><para>
+ * Note that the graph must not contain isolated vertices.
+ *
+ * </para><para>
+ * If you want to specify a random seed (as in original
+ * implementation) you can use \ref igraph_rng_seed().
+ *
+ * \param graph The input graph.
+ * \param e_weights Numeric vector giving the weights of the edges.
+ *     If it is a NULL pointer then all edges will have equal
+ *     weights. The weights are expected to be positive.
+ * \param v_weights Numeric vector giving the weights of the vertices.
+ *     If it is a NULL pointer then all vertices will have equal
+ *     weights. The weights are expected to be positive.
+ * \param nb_trials The number of attempts to partition the network
+ *     (can be any integer value equal or larger than 1).
+ * \param membership Pointer to a vector. The membership vector is
+ *    stored here.
+ * \param codelength Pointer to a real. If not NULL the code length of the
+ *     partition is stored here.
+ * \return Error code.
+ *
+ * \sa \ref igraph_community_spinglass(), \ref
+ * igraph_community_edge_betweenness(), \ref igraph_community_walktrap().
+ *
+ * Time complexity: TODO.
+ */
+int igraph_community_infomap(const igraph_t * graph,
+                             const igraph_vector_t *e_weights,
+                             const igraph_vector_t *v_weights,
+                             int nb_trials,
+                             igraph_vector_t *membership,
+                             igraph_real_t *codelength) {
+
+    FlowGraph * fgraph = new FlowGraph(graph, e_weights, v_weights);
+    IGRAPH_FINALLY(delete_FlowGraph, fgraph);
+
+    // compute stationary distribution
+    fgraph->initiate();
+
+    FlowGraph * cpy_fgraph ;
+    double shortestCodeLength = 1000.0;
+
+    // create membership vector
+    int Nnode = fgraph->Nnode;
+    IGRAPH_CHECK(igraph_vector_resize(membership, Nnode));
+
+    for (int trial = 0; trial < nb_trials; trial++) {
+        cpy_fgraph = new FlowGraph(fgraph);
+        IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph);
+
+        //partition the network
+        IGRAPH_CHECK(infomap_partition(cpy_fgraph, false));
+
+        // if better than the better...
+        if (cpy_fgraph->codeLength < shortestCodeLength) {
+            shortestCodeLength = cpy_fgraph->codeLength;
+            // ... store the partition
+            for (int i = 0 ; i < cpy_fgraph->Nnode ; i++) {
+                int Nmembers = cpy_fgraph->node[i]->members.size();
+                for (int k = 0; k < Nmembers; k++) {
+                    //cluster[ cpy_fgraph->node[i]->members[k] ] = i;
+                    VECTOR(*membership)[cpy_fgraph->node[i]->members[k]] = i;
+                }
+            }
+        }
+
+        delete_FlowGraph(cpy_fgraph);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    *codelength = (igraph_real_t) shortestCodeLength / log(2.0);
+
+    delete fgraph;
+    IGRAPH_FINALLY_CLEAN(1);
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/infomap_FlowGraph.cc b/igraph/src/infomap_FlowGraph.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/infomap_FlowGraph.cc
@@ -0,0 +1,420 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "infomap_FlowGraph.h"
+
+#define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 )
+
+void FlowGraph::init(int n, const igraph_vector_t *v_weights) {
+    alpha = 0.15;
+    beta  = 1.0 - alpha;
+    Nnode = n;
+    node = new Node*[Nnode];
+    if (v_weights) {
+        for (int i = 0; i < Nnode; i++) {
+            node[i] = new Node(i, (double)VECTOR(*v_weights)[i]);
+        }
+    } else {
+        for (int i = 0; i < Nnode; i++) {
+            node[i] = new Node(i, 1.0);
+        }
+    }
+}
+
+FlowGraph::FlowGraph(int n) {
+    init(n, NULL);
+}
+
+FlowGraph::FlowGraph(int n, const igraph_vector_t *v_weights) {
+    init(n, v_weights);
+}
+
+/* Build the graph from igraph_t object
+ */
+FlowGraph::FlowGraph(const igraph_t * graph,
+                     const igraph_vector_t *e_weights,
+                     const igraph_vector_t *v_weights) {
+
+    int n = (int)igraph_vcount(graph);
+    init(n, v_weights);
+
+    int directed = (int) igraph_is_directed(graph);
+
+    double linkWeight = 1.0;
+    igraph_integer_t from, to;
+
+    long int Nlinks = (long int) igraph_ecount(graph);
+    if (!directed) {
+        Nlinks = Nlinks * 2 ;
+    }
+    for (int i = 0; i < Nlinks; i++) {
+        if (!directed) { // not directed
+            if (i % 2 == 0) {
+                linkWeight = e_weights ? (double)VECTOR(*e_weights)[i / 2] : 1.0;
+                igraph_edge(graph, i / 2, &from, &to);
+            } else {
+                igraph_edge(graph, (i - 1) / 2, &to,   &from);
+            }
+        } else {         // directed
+            linkWeight = e_weights ? (double)VECTOR(*e_weights)[i] : 1.0;
+            igraph_edge(graph, i, &from, &to);
+        }
+
+        // Populate node from igraph_graph
+        if (linkWeight > 0.0) {
+            if (from != to) {
+                node[(int) from]->outLinks.push_back(make_pair((int)to, linkWeight));
+                node[(int) to]->inLinks.push_back(make_pair((int) from, linkWeight));
+            }
+        }
+    }
+}
+
+FlowGraph::FlowGraph(FlowGraph * fgraph) {
+    int n = fgraph->Nnode;
+    init(n, NULL);
+    for (int i = 0; i < n; i++) {
+        cpyNode(node[i], fgraph->node[i]);
+    }
+
+    //XXX: quid de danglings et Ndanglings?
+
+    alpha = fgraph->alpha ;
+    beta  = fgraph->beta ;
+
+    exit = fgraph->exit;
+    exitFlow = fgraph->exitFlow;
+    exit_log_exit = fgraph->exit_log_exit;
+    size_log_size = fgraph->size_log_size ;
+    nodeSize_log_nodeSize = fgraph->nodeSize_log_nodeSize;
+
+    codeLength = fgraph->codeLength;
+}
+
+/** construct a graph by extracting a subgraph from the given graph
+ */
+FlowGraph::FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members) {
+    init(sub_Nnode, NULL);
+
+    //XXX: use set of integer to ensure that elements are sorted
+    set<int> sub_mem;
+    for (int j = 0 ; j < sub_Nnode ; j++) {
+        sub_mem.insert(sub_members[j]);
+    }
+    set<int>::iterator it_mem = sub_mem.begin();
+
+    vector<int> sub_renumber = vector<int>(fgraph->Nnode);
+    // id --> sub_id
+
+    for (int j = 0; j < fgraph->Nnode; j++) {
+        sub_renumber[j] = -1;
+    }
+
+
+    for (int j = 0; j < sub_Nnode; j++) {
+        //int orig_nr = sub_members[j];
+        int orig_nr = (*it_mem);
+
+        node[j]->teleportWeight = fgraph->node[orig_nr]->teleportWeight;
+        node[j]->selfLink       = fgraph->node[orig_nr]->selfLink;
+        // Take care of self-link
+
+        int orig_NoutLinks = fgraph->node[orig_nr]->outLinks.size();
+        int orig_NinLinks  = fgraph->node[orig_nr]->inLinks.size();
+
+        sub_renumber[orig_nr] = j;
+
+        for (int k = 0; k < orig_NoutLinks; k++) {
+            int to = fgraph->node[orig_nr]->outLinks[k].first;
+            int to_newnr = sub_renumber[to];
+            double link_weight = fgraph->node[orig_nr]->outLinks[k].second;
+
+            if (to < orig_nr) {
+                // we add links if the destination (to) has already be seen
+                // (ie. smaller than current id) => orig
+
+                if (sub_mem.find(to) != sub_mem.end()) {
+                    // printf("%2d | %4d to %4d\n", j, orig_nr, to);
+                    // printf("from %4d (%4d:%1.5f) to %4d (%4d)\n", j, orig_nr,
+                    //        node[j]->selfLink, to_newnr, to);
+                    node[j]->outLinks.push_back(make_pair(to_newnr, link_weight));
+                    node[to_newnr]->inLinks.push_back(make_pair(j, link_weight));
+                }
+            }
+        }
+
+        for (int k = 0; k < orig_NinLinks; k++) {
+            int to = fgraph->node[orig_nr]->inLinks[k].first;
+            int to_newnr = sub_renumber[to];
+            double link_weight = fgraph->node[orig_nr]->inLinks[k].second;
+            if (to < orig_nr) {
+                if (sub_mem.find(to) != sub_mem.end()) {
+                    node[j]->inLinks.push_back(make_pair(to_newnr, link_weight));
+                    node[to_newnr]->outLinks.push_back(make_pair(j, link_weight));
+                }
+            }
+        }
+        it_mem++;
+    }
+}
+
+
+FlowGraph::~FlowGraph() {
+    //printf("delete FlowGraph !\n");
+    for (int i = 0; i < Nnode; i++) {
+        delete node[i];
+    }
+    delete [] node;
+}
+
+void delete_FlowGraph(FlowGraph *fgraph) {
+    delete fgraph;
+}
+
+
+/** Swap the graph with the one given
+    the graph is "re" calibrate
+    but NOT the given one.
+ */
+void FlowGraph::swap(FlowGraph * fgraph) {
+    Node ** node_tmp = fgraph->node;
+    int Nnode_tmp    = fgraph->Nnode;
+
+    fgraph->node = node;
+    fgraph->Nnode = Nnode;
+
+    node = node_tmp;
+    Nnode = Nnode_tmp;
+
+    calibrate();
+}
+
+/** Initialisation of the graph, compute the flow inside the graph
+ *   - count danglings nodes
+ *   - normalized edge weights
+ *   - Call eigenvector() to compute steady state distribution
+ *   - call calibrate to compute codelenght
+ */
+void FlowGraph::initiate() {
+    // Take care of dangling nodes, normalize outLinks, and calculate
+    // total teleport weight
+    Ndanglings = 0;
+    double totTeleportWeight = 0.0;
+    for (int i = 0; i < Nnode; i++) {
+        totTeleportWeight += node[i]->teleportWeight;
+    }
+
+    for (int i = 0; i < Nnode; i++) {
+        node[i]->teleportWeight /= totTeleportWeight;
+        // normalize teleportation weight
+
+        if (node[i]->outLinks.empty() && (node[i]->selfLink <= 0.0)) {
+            danglings.push_back(i);
+            Ndanglings++;
+        } else { // Normalize the weights
+            int NoutLinks = node[i]->outLinks.size();
+            double sum = node[i]->selfLink; // Take care of self-links
+            for (int j = 0; j < NoutLinks; j++) {
+                sum += node[i]->outLinks[j].second;
+            }
+            node[i]->selfLink /= sum;
+            for (int j = 0; j < NoutLinks; j++) {
+                node[i]->outLinks[j].second /= sum;
+            }
+        }
+    }
+
+    // Calculate steady state matrix
+    eigenvector();
+
+    // Update links to represent flow
+    for (int i = 0; i < Nnode; i++) {
+        node[i]->selfLink = beta * node[i]->size * node[i]->selfLink;
+        //            (1 - \tau) *     \pi_i     *      P_{ii}
+
+        if (!node[i]->outLinks.empty()) {
+            int NoutLinks = node[i]->outLinks.size();
+            for (int j = 0; j < NoutLinks; j++) {
+                node[i]->outLinks[j].second = beta * node[i]->size *
+                                              node[i]->outLinks[j].second;
+                //                      (1 - \tau) *     \pi_i     *          P_{ij}
+            }
+
+            // Update values for corresponding inlink
+            for (int j = 0; j < NoutLinks; j++) {
+                int NinLinks = node[node[i]->outLinks[j].first]->inLinks.size();
+                for (int k = 0; k < NinLinks; k++) {
+                    if (node[node[i]->outLinks[j].first]->inLinks[k].first == i) {
+                        node[node[i]->outLinks[j].first]->inLinks[k].second =
+                            node[i]->outLinks[j].second;
+                        k = NinLinks;
+                    }
+                }
+            }
+        }
+    }
+
+    // To be able to handle dangling nodes efficiently
+    for (int i = 0; i < Nnode; i++)
+        if (node[i]->outLinks.empty() && (node[i]->selfLink <= 0.0)) {
+            node[i]->danglingSize = node[i]->size;
+        } else {
+            node[i]->danglingSize = 0.0;
+        }
+
+    nodeSize_log_nodeSize = 0.0 ;
+    // The exit flow from each node at initiation
+    for (int i = 0; i < Nnode; i++) {
+        node[i]->exit = node[i]->size // Proba to be on i
+                        - (alpha * node[i]->size + beta * node[i]->danglingSize) *
+                        node[i]->teleportWeight // Proba teleport back to i
+                        - node[i]->selfLink;  // Proba stay on i
+
+        // node[i]->exit == q_{i\exit}
+        nodeSize_log_nodeSize += plogp(node[i]->size);
+    }
+
+    calibrate();
+}
+
+
+/* Compute steady state distribution (ie. PageRank) over the network
+ * (for all i update node[i]->size)
+ */
+void FlowGraph::eigenvector() {
+    vector<double> size_tmp = vector<double>(Nnode, 1.0 / Nnode);
+
+    int Niterations = 0;
+    double danglingSize;
+
+    double sqdiff = 1.0;
+    double sqdiff_old;
+    double sum;
+    do {
+        // Calculate dangling size
+        danglingSize = 0.0;
+        for (int i = 0; i < Ndanglings; i++) {
+            danglingSize += size_tmp[danglings[i]];
+        }
+
+        // Flow from teleportation
+        for (int i = 0; i < Nnode; i++) {
+            node[i]->size = (alpha + beta * danglingSize) * node[i]->teleportWeight;
+        }
+
+        // Flow from network steps
+        for (int i = 0; i < Nnode; i++) {
+            node[i]->size += beta * node[i]->selfLink * size_tmp[i];
+            int Nlinks = node[i]->outLinks.size();
+            for (int j = 0; j < Nlinks; j++)
+                node[node[i]->outLinks[j].first]->size += beta *
+                        node[i]->outLinks[j].second * size_tmp[i];
+        }
+
+        // Normalize
+        sum = 0.0;
+        for (int i = 0; i < Nnode; i++) {
+            sum += node[i]->size;
+        }
+        sqdiff_old = sqdiff;
+        sqdiff = 0.0;
+        for (int i = 0; i < Nnode; i++) {
+            node[i]->size /= sum;
+            sqdiff += fabs(node[i]->size - size_tmp[i]);
+            size_tmp[i] = node[i]->size;
+        }
+        Niterations++;
+
+        if (sqdiff == sqdiff_old) {
+            alpha += 1.0e-10;
+            beta = 1.0 - alpha;
+        }
+
+    } while ((Niterations < 200) && (sqdiff > 1.0e-15 || Niterations < 50));
+
+    danglingSize = 0.0;
+    for (int i = 0; i < Ndanglings; i++) {
+        danglingSize += size_tmp[danglings[i]];
+    }
+    // cout << "done! (the error is " << sqdiff << " after " << Niterations
+    //      << " iterations)" << endl;
+}
+
+
+/* Compute the codeLength of the given network
+ * note: (in **node, one node == one module)
+ */
+void FlowGraph::calibrate() {
+    exit_log_exit = 0.0;
+    exitFlow = 0.0;
+    size_log_size = 0.0;
+
+    for (int i = 0; i < Nnode; i++) { // For each module
+        // own node/module codebook
+        size_log_size         += plogp(node[i]->exit + node[i]->size);
+
+        // use of index codebook
+        exitFlow      += node[i]->exit;
+        exit_log_exit += plogp(node[i]->exit);
+    }
+
+    exit = plogp(exitFlow);
+
+    codeLength = exit - 2.0 * exit_log_exit + size_log_size -
+                 nodeSize_log_nodeSize;
+}
+
+
+/* Restore the data from the given FlowGraph object
+ */
+void FlowGraph::back_to(FlowGraph * fgraph) {
+    // delete current nodes
+    for (int i = 0 ; i < Nnode ; i++) {
+        delete node[i];
+    }
+    delete [] node;
+
+    Nnode = fgraph->Nnode;
+
+    // copy original ones
+    node = new Node*[Nnode];
+    for (int i = 0; i < Nnode; i++) {
+        node[i] = new Node();
+        cpyNode(node[i], fgraph->node[i]);
+    }
+
+    // restore atributs
+    alpha = fgraph->alpha ;
+    beta  = fgraph->beta ;
+
+    exit = fgraph->exit;
+    exitFlow = fgraph->exitFlow;
+    exit_log_exit = fgraph->exit_log_exit;
+    size_log_size = fgraph->size_log_size ;
+    nodeSize_log_nodeSize = fgraph->nodeSize_log_nodeSize;
+
+    codeLength = fgraph->codeLength;
+}
+
+
diff --git a/igraph/src/infomap_Greedy.cc b/igraph/src/infomap_Greedy.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/infomap_Greedy.cc
@@ -0,0 +1,612 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "infomap_Greedy.h"
+#include <iterator>
+#define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 )
+
+Greedy::Greedy(FlowGraph * fgraph) {
+    graph = fgraph;
+    Nnode = graph->Nnode;
+
+    alpha = graph->alpha;// teleportation probability
+    beta = 1.0 - alpha;  // probability to take normal step
+
+    Nempty = 0;
+    vector<int>(Nnode).swap(mod_empty);
+
+    vector<int>(Nnode).swap(node_index);
+    vector<double>(Nnode).swap(mod_exit);
+    vector<double>(Nnode).swap(mod_size);
+    vector<double>(Nnode).swap(mod_danglingSize);
+    vector<double>(Nnode).swap(mod_teleportWeight);
+    vector<int>(Nnode).swap(mod_members);
+
+    nodeSize_log_nodeSize = graph->nodeSize_log_nodeSize;
+    exit_log_exit         = graph->exit_log_exit;
+    size_log_size         = graph->size_log_size;
+    exitFlow              = graph->exitFlow;
+
+    Node ** node = graph->node;
+    for (int i = 0; i < Nnode; i++) { // For each module
+        node_index[i]         = i;
+        mod_exit[i]           = node[i]->exit;
+        mod_size[i]           = node[i]->size;
+
+        mod_danglingSize[i]   = node[i]->danglingSize;
+        mod_teleportWeight[i] = node[i]->teleportWeight;
+        mod_members[i]        = node[i]->members.size();
+    }
+
+    exit = plogp(exitFlow);
+
+    codeLength = exit - 2.0 * exit_log_exit + size_log_size -
+                 nodeSize_log_nodeSize;
+}
+
+Greedy::~Greedy() {
+}
+
+void delete_Greedy(Greedy *greedy) {
+    delete greedy;
+}
+
+
+/** Greedy optimizing (as in Blodel and Al.) :
+ * for each vertex (selected in a random order) compute the best possible move within neighborhood
+ */
+bool Greedy::optimize() {
+    bool moved = false;
+    Node ** node = graph->node;
+
+    RNG_BEGIN();
+
+    // Generate random enumeration of nodes
+    vector<int> randomOrder(Nnode);
+    for (int i = 0; i < Nnode; i++) {
+        randomOrder[i] = i;
+    }
+
+    for (int i = 0; i < Nnode - 1; i++) {
+        //int randPos = i ; //XXX
+        int randPos = RNG_INTEGER(i, Nnode - 1);
+        // swap i & randPos
+        int tmp              = randomOrder[i];
+        randomOrder[i]       = randomOrder[randPos];
+        randomOrder[randPos] = tmp;
+    }
+
+    unsigned int offset = 1;
+    vector<unsigned int> redirect(Nnode, 0);
+    vector<pair<int, pair<double, double> > > flowNtoM(Nnode);
+
+    for (int k = 0; k < Nnode; k++) {
+
+        // Pick nodes in random order
+        int flip = randomOrder[k];
+        int oldM = node_index[flip];
+
+        // Reset offset when int overflows
+        if (offset > INT_MAX) {
+            for (int j = 0; j < Nnode; j++) {
+                redirect[j] = 0;
+            }
+            offset = 1;
+        }
+        // Size of vector with module links
+        int NmodLinks = 0;
+        // For all outLinks
+        int NoutLinks = node[flip]->outLinks.size();
+        if (NoutLinks == 0) { //dangling node, add node to calculate flow below
+            redirect[oldM] = offset + NmodLinks;
+            flowNtoM[NmodLinks].first = oldM;
+            flowNtoM[NmodLinks].second.first = 0.0;
+            flowNtoM[NmodLinks].second.second = 0.0;
+            NmodLinks++;
+        } else {
+            for (int j = 0; j < NoutLinks; j++) {
+                int nb_M       = node_index[node[flip]->outLinks[j].first];
+                // index destination du lien
+                double nb_flow = node[flip]->outLinks[j].second;
+                // wgt du lien
+                if (redirect[nb_M] >= offset) {
+                    flowNtoM[redirect[nb_M] - offset].second.first += nb_flow;
+                } else {
+                    redirect[nb_M] = offset + NmodLinks;
+                    flowNtoM[NmodLinks].first = nb_M;
+                    flowNtoM[NmodLinks].second.first = nb_flow;
+                    flowNtoM[NmodLinks].second.second = 0.0;
+                    NmodLinks++;
+                }
+            }
+        }
+        // For all inLinks
+        int NinLinks = node[flip]->inLinks.size();
+        for (int j = 0; j < NinLinks; j++) {
+            int nb_M = node_index[node[flip]->inLinks[j].first];
+            double nb_flow = node[flip]->inLinks[j].second;
+
+            if (redirect[nb_M] >= offset) {
+                flowNtoM[redirect[nb_M] - offset].second.second += nb_flow;
+            } else {
+                redirect[nb_M] = offset + NmodLinks;
+                flowNtoM[NmodLinks].first = nb_M;
+                flowNtoM[NmodLinks].second.first = 0.0;
+                flowNtoM[NmodLinks].second.second = nb_flow;
+                NmodLinks++;
+            }
+        }
+
+        // For teleportation and dangling nodes
+        for (int j = 0; j < NmodLinks; j++) {
+            int newM = flowNtoM[j].first;
+            if (newM == oldM) {
+                flowNtoM[j].second.first  +=
+                    (alpha * node[flip]->size + beta * node[flip]->danglingSize) *
+                    (mod_teleportWeight[oldM] - node[flip]->teleportWeight);
+                flowNtoM[j].second.second +=
+                    (alpha * (mod_size[oldM] - node[flip]->size) +
+                     beta * (mod_danglingSize[oldM] - node[flip]->danglingSize)) *
+                    node[flip]->teleportWeight;
+            } else {
+                flowNtoM[j].second.first  +=
+                    (alpha * node[flip]->size + beta * node[flip]->danglingSize) *
+                    mod_teleportWeight[newM];
+                flowNtoM[j].second.second +=
+                    (alpha * mod_size[newM]   + beta * mod_danglingSize[newM]  ) *
+                    node[flip]->teleportWeight;
+            }
+        }
+
+        // Calculate flow to/from own module (default value if no link to
+        // own module)
+        double outFlowOldM =
+            (alpha * node[flip]->size + beta * node[flip]->danglingSize) *
+            (mod_teleportWeight[oldM] - node[flip]->teleportWeight) ;
+        double inFlowOldM  =
+            (alpha * (mod_size[oldM] - node[flip]->size) +
+             beta * (mod_danglingSize[oldM] - node[flip]->danglingSize)) *
+            node[flip]->teleportWeight;
+        if (redirect[oldM] >= offset) {
+            outFlowOldM = flowNtoM[redirect[oldM] - offset].second.first;
+            inFlowOldM  = flowNtoM[redirect[oldM] - offset].second.second;
+        }
+
+        // Option to move to empty module (if node not already alone)
+        if (mod_members[oldM] > static_cast<int>(node[flip]->members.size())) {
+            if (Nempty > 0) {
+                flowNtoM[NmodLinks].first = mod_empty[Nempty - 1];
+                flowNtoM[NmodLinks].second.first = 0.0;
+                flowNtoM[NmodLinks].second.second = 0.0;
+                NmodLinks++;
+            }
+        }
+
+        // Randomize link order for optimized search
+        for (int j = 0; j < NmodLinks - 1; j++) {
+            //int randPos = j ; // XXX
+            int randPos = RNG_INTEGER(j, NmodLinks - 1);
+            int tmp_M = flowNtoM[j].first;
+            double tmp_outFlow = flowNtoM[j].second.first;
+            double tmp_inFlow = flowNtoM[j].second.second;
+            flowNtoM[j].first = flowNtoM[randPos].first;
+            flowNtoM[j].second.first = flowNtoM[randPos].second.first;
+            flowNtoM[j].second.second = flowNtoM[randPos].second.second;
+            flowNtoM[randPos].first = tmp_M;
+            flowNtoM[randPos].second.first = tmp_outFlow;
+            flowNtoM[randPos].second.second = tmp_inFlow;
+        }
+
+        int bestM = oldM;
+        double best_outFlow = 0.0;
+        double best_inFlow = 0.0;
+        double best_delta = 0.0;
+
+        // Find the move that minimizes the description length
+        for (int j = 0; j < NmodLinks; j++) {
+
+            int newM = flowNtoM[j].first;
+            double outFlowNewM = flowNtoM[j].second.first;
+            double inFlowNewM  = flowNtoM[j].second.second;
+
+            if (newM != oldM) {
+
+                double delta_exit = plogp(exitFlow + outFlowOldM + inFlowOldM -
+                                          outFlowNewM - inFlowNewM) - exit;
+
+                double delta_exit_log_exit = - plogp(mod_exit[oldM]) -
+                                             plogp(mod_exit[newM]) +
+                                             plogp(mod_exit[oldM] - node[flip]->exit + outFlowOldM + inFlowOldM)
+                                             + plogp(mod_exit[newM] + node[flip]->exit - outFlowNewM -
+                                                     inFlowNewM);
+
+                double delta_size_log_size = - plogp(mod_exit[oldM] + mod_size[oldM])
+                                             - plogp(mod_exit[newM] + mod_size[newM])
+                                             + plogp(mod_exit[oldM] + mod_size[oldM] - node[flip]->exit -
+                                                     node[flip]->size + outFlowOldM + inFlowOldM)
+                                             + plogp(mod_exit[newM] + mod_size[newM] + node[flip]->exit +
+                                                     node[flip]->size - outFlowNewM - inFlowNewM);
+
+                double deltaL = delta_exit - 2.0 * delta_exit_log_exit +
+                                delta_size_log_size;
+
+                if (deltaL - best_delta < -1e-10) {
+                    bestM = newM;
+                    best_outFlow = outFlowNewM;
+                    best_inFlow = inFlowNewM;
+                    best_delta = deltaL;
+                }
+            }
+        }
+
+        // Make best possible move
+        if (bestM != oldM) {
+            //Update empty module vector
+            if (mod_members[bestM] == 0) {
+                Nempty--;
+            }
+            if (mod_members[oldM] == static_cast<int>(node[flip]->members.size())) {
+                mod_empty[Nempty] = oldM;
+                Nempty++;
+            }
+
+            exitFlow -= mod_exit[oldM] + mod_exit[bestM];
+
+            exit_log_exit -= plogp(mod_exit[oldM]) + plogp(mod_exit[bestM]);
+            size_log_size -= plogp(mod_exit[oldM] + mod_size[oldM]) +
+                             plogp(mod_exit[bestM] + mod_size[bestM]);
+
+            mod_exit[oldM]            -= node[flip]->exit - outFlowOldM -
+                                         inFlowOldM;
+            mod_size[oldM]            -= node[flip]->size;
+            mod_danglingSize[oldM]    -= node[flip]->danglingSize;
+            mod_teleportWeight[oldM]  -= node[flip]->teleportWeight;
+            mod_members[oldM]         -= node[flip]->members.size();
+
+            mod_exit[bestM]           += node[flip]->exit - best_outFlow -
+                                         best_inFlow;
+            mod_size[bestM]           += node[flip]->size;
+            mod_danglingSize[bestM]   += node[flip]->danglingSize;
+            mod_teleportWeight[bestM] += node[flip]->teleportWeight;
+            mod_members[bestM]        += node[flip]->members.size();
+
+            exitFlow += mod_exit[oldM] + mod_exit[bestM];
+
+            // Update terms in map equation
+
+            exit_log_exit += plogp(mod_exit[oldM]) + plogp(mod_exit[bestM]);
+            size_log_size += plogp(mod_exit[oldM] + mod_size[oldM]) +
+                             plogp(mod_exit[bestM] + mod_size[bestM]);
+            exit = plogp(exitFlow);
+
+            // Update code length
+
+            codeLength = exit - 2.0 * exit_log_exit + size_log_size -
+                         nodeSize_log_nodeSize;
+
+            node_index[flip] = bestM;
+            moved = true;
+        }
+        offset += Nnode;
+    }
+
+    RNG_END();
+
+    return moved;
+}
+
+/** Apply the move to the given network
+ */
+void Greedy::apply(bool sort) {
+//void Greedy::level(Node ***node_tmp, bool sort) {
+
+    //old fct prepare(sort)
+    vector<int> modSnode;  // will give ids of no-empty modules (nodes)
+    int Nmod = 0;
+    if (sort) {
+        multimap<double, int> Msize;
+        for (int i = 0; i < Nnode; i++) {
+            if (mod_members[i] > 0) {
+                Nmod++;
+                Msize.insert(pair<const double, int>(mod_size[i], i));
+            }
+        }
+        for (multimap<double, int>::reverse_iterator it = Msize.rbegin();
+             it != Msize.rend(); it++) {
+            modSnode.push_back(it->second);
+        }
+    } else {
+        for (int i = 0; i < Nnode; i++) {
+            if (mod_members[i] > 0) {
+                Nmod++;
+                modSnode.push_back(i);
+            }
+        }
+    }
+    //modSnode[id_when_no_empty_node] = id_in_mod_tbl
+
+    // Create the new graph
+    FlowGraph * tmp_fgraph = new FlowGraph(Nmod);
+    IGRAPH_FINALLY(delete_FlowGraph, tmp_fgraph);
+    Node ** node_tmp = tmp_fgraph->node ;
+
+    Node ** node = graph->node;
+
+    vector<int> nodeInMod = vector<int>(Nnode);
+
+    // creation of new nodes
+    for (int i = 0; i < Nmod; i++) {
+        //node_tmp[i] = new Node();
+        vector<int>().swap(node_tmp[i]->members); // clear membership
+        node_tmp[i]->exit           =           mod_exit[modSnode[i]];
+        node_tmp[i]->size           =           mod_size[modSnode[i]];
+        node_tmp[i]->danglingSize   =   mod_danglingSize[modSnode[i]];
+        node_tmp[i]->teleportWeight = mod_teleportWeight[modSnode[i]];
+
+        nodeInMod[modSnode[i]]      = i;
+    }
+    //nodeInMode[id_in_mod_tbl] = id_when_no_empty_node
+
+    // Calculate outflow of links to different modules
+    vector<map<int, double> > outFlowNtoM(Nmod);
+    map<int, double>::iterator it_M;
+
+    for (int i = 0; i < Nnode; i++) {
+        int i_M = nodeInMod[node_index[i]]; //final id of the module of the node i
+        // add node members to the module
+        copy( node[i]->members.begin(), node[i]->members.end(),
+              back_inserter( node_tmp[i_M]->members ) );
+
+        int NoutLinks = node[i]->outLinks.size();
+        for (int j = 0; j < NoutLinks; j++) {
+            int nb         = node[i]->outLinks[j].first;
+            int nb_M       = nodeInMod[node_index[nb]];
+            double nb_flow = node[i]->outLinks[j].second;
+            if (nb != i) {
+                it_M = outFlowNtoM[i_M].find(nb_M);
+                if (it_M != outFlowNtoM[i_M].end()) {
+                    it_M->second += nb_flow;
+                } else {
+                    outFlowNtoM[i_M].insert(make_pair(nb_M, nb_flow));
+                }
+            }
+        }
+    }
+
+    // Create outLinks at new level
+    for (int i = 0; i < Nmod; i++) {
+        for (it_M = outFlowNtoM[i].begin(); it_M != outFlowNtoM[i].end(); it_M++) {
+            if (it_M->first != i) {
+                node_tmp[i]->outLinks.push_back(make_pair(it_M->first, it_M->second));
+            }
+        }
+    }
+
+    // Calculate inflow of links from different modules
+    vector<map<int, double> > inFlowNtoM(Nmod);
+
+    for (int i = 0; i < Nnode; i++) {
+        int i_M = nodeInMod[node_index[i]];
+        int NinLinks = node[i]->inLinks.size();
+        for (int j = 0; j < NinLinks; j++) {
+            int nb         = node[i]->inLinks[j].first;
+            int nb_M       = nodeInMod[node_index[nb]];
+            double nb_flow = node[i]->inLinks[j].second;
+            if (nb != i) {
+                it_M = inFlowNtoM[i_M].find(nb_M);
+                if (it_M != inFlowNtoM[i_M].end()) {
+                    it_M->second += nb_flow;
+                } else {
+                    inFlowNtoM[i_M].insert(make_pair(nb_M, nb_flow));
+                }
+            }
+        }
+    }
+
+    // Create inLinks at new level
+    for (int i = 0; i < Nmod; i++) {
+        for (it_M = inFlowNtoM[i].begin(); it_M != inFlowNtoM[i].end(); it_M++) {
+            if (it_M->first != i) {
+                node_tmp[i]->inLinks.push_back(make_pair(it_M->first, it_M->second));
+            }
+        }
+    }
+
+    // Option to move to empty module
+    vector<int>().swap(mod_empty);
+    Nempty = 0;
+
+    //swap node between tmp_graph and graph, then destroy tmp_fgraph
+    graph->swap(tmp_fgraph);
+    Nnode = Nmod;
+
+    delete tmp_fgraph;
+    IGRAPH_FINALLY_CLEAN(1);
+}
+
+
+/**
+ * RAZ et recalcul :
+ *  - mod_exit
+ *  - mod_size
+ *  - mod_danglingSize
+ *  - mod_teleportWeight
+ *  - mod_members
+ *  and
+ *  - exit_log_exit
+ *  - size_log_size
+ *  - exitFlow
+ *  - exit
+ *  - codeLength
+ * according to **node / node[i]->index
+ */
+void Greedy::tune(void) {
+
+    exit_log_exit = 0.0;
+    size_log_size = 0.0;
+    exitFlow = 0.0;
+
+    for (int i = 0; i < Nnode; i++) {
+        mod_exit[i] = 0.0;
+        mod_size[i] = 0.0;
+        mod_danglingSize[i] = 0.0;
+        mod_teleportWeight[i] = 0.0;
+        mod_members[i] = 0;
+    }
+
+    Node ** node = graph->node;
+    // Update all values except contribution from teleportation
+    for (int i = 0; i < Nnode; i++) {
+        int i_M = node_index[i]; // module id of node i
+        int Nlinks = node[i]->outLinks.size();
+
+        mod_size[i_M]           += node[i]->size;
+        mod_danglingSize[i_M]   += node[i]->danglingSize;
+        mod_teleportWeight[i_M] += node[i]->teleportWeight;
+        mod_members[i_M]++;
+
+        for (int j = 0; j < Nlinks; j++) {
+            int neighbor      = node[i]->outLinks[j].first;
+            double neighbor_w = node[i]->outLinks[j].second;
+            int neighbor_M    = node_index[neighbor];
+            if (i_M != neighbor_M) { // neighbor in an other module
+                mod_exit[i_M] += neighbor_w;
+            }
+        }
+    }
+
+    // Update contribution from teleportation
+    for (int i = 0; i < Nnode; i++) {
+        mod_exit[i] += (alpha * mod_size[i] + beta * mod_danglingSize[i]) *
+                       (1.0 - mod_teleportWeight[i]);
+    }
+
+    for (int i = 0; i < Nnode; i++) {
+        exit_log_exit += plogp(mod_exit[i]);
+        size_log_size += plogp(mod_exit[i] + mod_size[i]);
+        exitFlow += mod_exit[i];
+    }
+    exit = plogp(exitFlow);
+
+    codeLength = exit - 2.0 * exit_log_exit + size_log_size -
+                 nodeSize_log_nodeSize;
+}
+
+
+/* Compute the new CodeSize if modules are merged as indicated by moveTo
+ */
+void Greedy::setMove(int *moveTo) {
+    //void Greedy::determMove(int *moveTo) {
+    Node ** node = graph->node;
+    //printf("setMove nNode:%d \n", Nnode);
+    for (int i = 0 ; i < Nnode ; i++) { // pour chaque module
+        int oldM = i;
+        int newM = moveTo[i];
+        //printf("old -> new : %d -> %d \n", oldM, newM);
+        if (newM != oldM) {
+
+            // Si je comprend bien :
+            // outFlow... : c'est le "flow" de i-> autre sommet du meme module
+            // inFlow... : c'est le "flow" depuis un autre sommet du meme module --> i
+            double outFlowOldM = (alpha * node[i]->size + beta * node[i]->danglingSize) *
+                                 (mod_teleportWeight[oldM] - node[i]->teleportWeight);
+            double inFlowOldM  = (alpha * (mod_size[oldM] - node[i]->size) +
+                                  beta * (mod_danglingSize[oldM] -
+                                          node[i]->danglingSize)) *
+                                 node[i]->teleportWeight;
+            double outFlowNewM = (alpha * node[i]->size + beta * node[i]->danglingSize)
+                                 * mod_teleportWeight[newM];
+            double inFlowNewM  = (alpha * mod_size[newM] +
+                                  beta * mod_danglingSize[newM]) *
+                                 node[i]->teleportWeight;
+
+            // For all outLinks
+            int NoutLinks = node[i]->outLinks.size();
+            for (int j = 0; j < NoutLinks; j++) {
+                int nb_M = node_index[node[i]->outLinks[j].first];
+                double nb_flow = node[i]->outLinks[j].second;
+                if (nb_M == oldM) {
+                    outFlowOldM += nb_flow;
+                } else if (nb_M == newM) {
+                    outFlowNewM += nb_flow;
+                }
+            }
+
+            // For all inLinks
+            int NinLinks = node[i]->inLinks.size();
+            for (int j = 0; j < NinLinks; j++) {
+                int nb_M = node_index[node[i]->inLinks[j].first];
+                double nb_flow = node[i]->inLinks[j].second;
+                if (nb_M == oldM) {
+                    inFlowOldM += nb_flow;
+                } else if (nb_M == newM) {
+                    inFlowNewM += nb_flow;
+                }
+            }
+
+            // Update empty module vector
+            // RAZ de mod_empty et Nempty ds calibrate()
+            if (mod_members[newM] == 0) {
+                // si le nouveau etait vide, on a un vide de moins...
+                Nempty--;
+            }
+            if (mod_members[oldM] == static_cast<int>(node[i]->members.size())) {
+                // si l'ancien avait la taille de celui qui bouge, un vide de plus
+                mod_empty[Nempty] = oldM;
+                Nempty++;
+            }
+
+            exitFlow -= mod_exit[oldM] + mod_exit[newM];
+            exit_log_exit -= plogp(mod_exit[oldM]) + plogp(mod_exit[newM]);
+            size_log_size -= plogp(mod_exit[oldM] + mod_size[oldM]) +
+                             plogp(mod_exit[newM] + mod_size[newM]);
+
+            mod_exit[oldM] -= node[i]->exit - outFlowOldM - inFlowOldM;
+            mod_size[oldM] -= node[i]->size;
+            mod_danglingSize[oldM] -= node[i]->danglingSize;
+            mod_teleportWeight[oldM] -= node[i]->teleportWeight;
+            mod_members[oldM] -= node[i]->members.size();
+            mod_exit[newM] += node[i]->exit - outFlowNewM - inFlowNewM;
+            mod_size[newM] += node[i]->size;
+            mod_danglingSize[newM] += node[i]->danglingSize;
+            mod_teleportWeight[newM] += node[i]->teleportWeight;
+            mod_members[newM] += node[i]->members.size();
+
+            exitFlow += mod_exit[oldM] + mod_exit[newM];
+            exit_log_exit += plogp(mod_exit[oldM]) + plogp(mod_exit[newM]);
+            size_log_size += plogp(mod_exit[oldM] + mod_size[oldM]) +
+                             plogp(mod_exit[newM] + mod_size[newM]);
+            exit = plogp(exitFlow);
+
+            codeLength = exit - 2.0 * exit_log_exit + size_log_size -
+                         nodeSize_log_nodeSize;
+
+            node_index[i] = newM;
+
+        }
+
+    }
+}
+
+
diff --git a/igraph/src/infomap_Node.cc b/igraph/src/infomap_Node.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/infomap_Node.cc
@@ -0,0 +1,70 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "infomap_Node.h"
+
+Node::Node() {
+    exit = 0.0;
+    size = 0.0;
+    selfLink = 0.0;
+}
+
+Node::Node(int nodenr, double tpweight) {
+    teleportWeight = tpweight;
+    exit = 0.0;
+    size = 0.0;
+    selfLink = 0.0;
+    members.push_back(nodenr); // members = [nodenr]
+}
+
+void cpyNode(Node *newNode, Node *oldNode) {
+    newNode->exit = oldNode->exit;
+    newNode->size = oldNode->size;
+    newNode->teleportWeight = oldNode->teleportWeight;
+    newNode->danglingSize   = oldNode->danglingSize;
+
+    int Nmembers = oldNode->members.size();
+    newNode->members = vector<int>(Nmembers);
+    for (int i = 0; i < Nmembers; i++) {
+        newNode->members[i] = oldNode->members[i];
+    }
+
+    newNode->selfLink = oldNode->selfLink;
+
+    int NoutLinks = oldNode->outLinks.size();
+    newNode->outLinks = vector<pair<int, double> >(NoutLinks);
+    for (int i = 0; i < NoutLinks; i++) {
+        newNode->outLinks[i].first = oldNode->outLinks[i].first;
+        newNode->outLinks[i].second = oldNode->outLinks[i].second;
+    }
+
+    int NinLinks = oldNode->inLinks.size();
+    newNode->inLinks = vector<pair<int, double> >(NinLinks);
+    for (int i = 0; i < NinLinks; i++) {
+        newNode->inLinks[i].first = oldNode->inLinks[i].first;
+        newNode->inLinks[i].second = oldNode->inLinks[i].second;
+    }
+
+}
+
diff --git a/igraph/src/inquire.c b/igraph/src/inquire.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/inquire.c
@@ -0,0 +1,117 @@
+#include "f2c.h"
+#include "fio.h"
+#include "string.h"
+#ifdef NON_UNIX_STDIO
+#ifndef MSDOS
+#include "unistd.h" /* for access() */
+#endif
+#endif
+#ifdef KR_headers
+integer f_inqu(a) inlist *a;
+#else
+#ifdef __cplusplus
+extern "C" integer f_inqu(inlist*);
+#endif
+#ifdef MSDOS
+#undef abs
+#undef min
+#undef max
+#include "io.h"
+#endif
+integer f_inqu(inlist *a)
+#endif
+{	flag byfile;
+	int i;
+#ifndef NON_UNIX_STDIO
+	int n;
+#endif
+	unit *p;
+	char buf[256];
+	long x;
+	if(a->infile!=NULL)
+	{	byfile=1;
+		g_char(a->infile,a->infilen,buf);
+#ifdef NON_UNIX_STDIO
+		x = access(buf,0) ? -1 : 0;
+		for(i=0,p=NULL;i<MXUNIT;i++)
+			if(f__units[i].ufd != NULL
+			 && f__units[i].ufnm != NULL
+			 && !strcmp(f__units[i].ufnm,buf)) {
+				p = &f__units[i];
+				break;
+				}
+#else
+		x=f__inode(buf, &n);
+		for(i=0,p=NULL;i<MXUNIT;i++)
+			if(f__units[i].uinode==x
+			&& f__units[i].ufd!=NULL
+			&& f__units[i].udev == n) {
+				p = &f__units[i];
+				break;
+				}
+#endif
+	}
+	else
+	{
+		byfile=0;
+		if(a->inunit<MXUNIT && a->inunit>=0)
+		{
+			p= &f__units[a->inunit];
+		}
+		else
+		{
+			p=NULL;
+		}
+	}
+	if(a->inex!=NULL)
+		if(byfile && x != -1 || !byfile && p!=NULL)
+			*a->inex=1;
+		else *a->inex=0;
+	if(a->inopen!=NULL)
+		if(byfile) *a->inopen=(p!=NULL);
+		else *a->inopen=(p!=NULL && p->ufd!=NULL);
+	if(a->innum!=NULL) *a->innum= p-f__units;
+	if(a->innamed!=NULL)
+		if(byfile || p!=NULL && p->ufnm!=NULL)
+			*a->innamed=1;
+		else	*a->innamed=0;
+	if(a->inname!=NULL)
+		if(byfile)
+			b_char(buf,a->inname,a->innamlen);
+		else if(p!=NULL && p->ufnm!=NULL)
+			b_char(p->ufnm,a->inname,a->innamlen);
+	if(a->inacc!=NULL && p!=NULL && p->ufd!=NULL)
+		if(p->url)
+			b_char("DIRECT",a->inacc,a->inacclen);
+		else	b_char("SEQUENTIAL",a->inacc,a->inacclen);
+	if(a->inseq!=NULL)
+		if(p!=NULL && p->url)
+			b_char("NO",a->inseq,a->inseqlen);
+		else	b_char("YES",a->inseq,a->inseqlen);
+	if(a->indir!=NULL)
+		if(p==NULL || p->url)
+			b_char("YES",a->indir,a->indirlen);
+		else	b_char("NO",a->indir,a->indirlen);
+	if(a->infmt!=NULL)
+		if(p!=NULL && p->ufmt==0)
+			b_char("UNFORMATTED",a->infmt,a->infmtlen);
+		else	b_char("FORMATTED",a->infmt,a->infmtlen);
+	if(a->inform!=NULL)
+		if(p!=NULL && p->ufmt==0)
+		b_char("NO",a->inform,a->informlen);
+		else b_char("YES",a->inform,a->informlen);
+	if(a->inunf)
+		if(p!=NULL && p->ufmt==0)
+			b_char("YES",a->inunf,a->inunflen);
+		else if (p!=NULL) b_char("NO",a->inunf,a->inunflen);
+		else b_char("UNKNOWN",a->inunf,a->inunflen);
+	if(a->inrecl!=NULL && p!=NULL)
+		*a->inrecl=p->url;
+	if(a->innrec!=NULL && p!=NULL && p->url>0)
+		*a->innrec=(ftnint)(FTELL(p->ufd)/p->url+1);
+	if(a->inblank && p!=NULL && p->ufmt)
+		if(p->ublnk)
+			b_char("ZERO",a->inblank,a->inblanklen);
+		else	b_char("NULL",a->inblank,a->inblanklen);
+	return(0);
+}
diff --git a/igraph/src/interrupt.c b/igraph/src/interrupt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/interrupt.c
@@ -0,0 +1,46 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_interrupt.h"
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+IGRAPH_THREAD_LOCAL igraph_interruption_handler_t
+*igraph_i_interruption_handler = 0;
+
+int igraph_allow_interruption(void* data) {
+    if (igraph_i_interruption_handler) {
+        return igraph_i_interruption_handler(data);
+    }
+    return IGRAPH_SUCCESS;
+}
+
+igraph_interruption_handler_t *
+igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler) {
+    igraph_interruption_handler_t * previous_handler = igraph_i_interruption_handler;
+    igraph_i_interruption_handler = new_handler;
+    return previous_handler;
+}
diff --git a/igraph/src/iparmq.c b/igraph/src/iparmq.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/iparmq.c
@@ -0,0 +1,345 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* > \brief \b IPARMQ   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download IPARMQ + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/iparmq.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/iparmq.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/iparmq.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         INTEGER FUNCTION IPARMQ( ISPEC, NAME, OPTS, N, ILO, IHI, LWORK )   
+
+         INTEGER            IHI, ILO, ISPEC, LWORK, N   
+         CHARACTER          NAME*( * ), OPTS*( * )   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   >      This program sets problem and machine dependent parameters   
+   >      useful for xHSEQR and its subroutines. It is called whenever   
+   >      ILAENV is called with 12 <= ISPEC <= 16   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] ISPEC   
+   > \verbatim   
+   >          ISPEC is integer scalar   
+   >              ISPEC specifies which tunable parameter IPARMQ should   
+   >              return.   
+   >   
+   >              ISPEC=12: (INMIN)  Matrices of order nmin or less   
+   >                        are sent directly to xLAHQR, the implicit   
+   >                        double shift QR algorithm.  NMIN must be   
+   >                        at least 11.   
+   >   
+   >              ISPEC=13: (INWIN)  Size of the deflation window.   
+   >                        This is best set greater than or equal to   
+   >                        the number of simultaneous shifts NS.   
+   >                        Larger matrices benefit from larger deflation   
+   >                        windows.   
+   >   
+   >              ISPEC=14: (INIBL) Determines when to stop nibbling and   
+   >                        invest in an (expensive) multi-shift QR sweep.   
+   >                        If the aggressive early deflation subroutine   
+   >                        finds LD converged eigenvalues from an order   
+   >                        NW deflation window and LD.GT.(NW*NIBBLE)/100,   
+   >                        then the next QR sweep is skipped and early   
+   >                        deflation is applied immediately to the   
+   >                        remaining active diagonal block.  Setting   
+   >                        IPARMQ(ISPEC=14) = 0 causes TTQRE to skip a   
+   >                        multi-shift QR sweep whenever early deflation   
+   >                        finds a converged eigenvalue.  Setting   
+   >                        IPARMQ(ISPEC=14) greater than or equal to 100   
+   >                        prevents TTQRE from skipping a multi-shift   
+   >                        QR sweep.   
+   >   
+   >              ISPEC=15: (NSHFTS) The number of simultaneous shifts in   
+   >                        a multi-shift QR iteration.   
+   >   
+   >              ISPEC=16: (IACC22) IPARMQ is set to 0, 1 or 2 with the   
+   >                        following meanings.   
+   >                        0:  During the multi-shift QR sweep,   
+   >                            xLAQR5 does not accumulate reflections and   
+   >                            does not use matrix-matrix multiply to   
+   >                            update the far-from-diagonal matrix   
+   >                            entries.   
+   >                        1:  During the multi-shift QR sweep,   
+   >                            xLAQR5 and/or xLAQRaccumulates reflections and uses   
+   >                            matrix-matrix multiply to update the   
+   >                            far-from-diagonal matrix entries.   
+   >                        2:  During the multi-shift QR sweep.   
+   >                            xLAQR5 accumulates reflections and takes   
+   >                            advantage of 2-by-2 block structure during   
+   >                            matrix-matrix multiplies.   
+   >                        (If xTRMM is slower than xGEMM, then   
+   >                        IPARMQ(ISPEC=16)=1 may be more efficient than   
+   >                        IPARMQ(ISPEC=16)=2 despite the greater level of   
+   >                        arithmetic work implied by the latter choice.)   
+   > \endverbatim   
+   >   
+   > \param[in] NAME   
+   > \verbatim   
+   >          NAME is character string   
+   >               Name of the calling subroutine   
+   > \endverbatim   
+   >   
+   > \param[in] OPTS   
+   > \verbatim   
+   >          OPTS is character string   
+   >               This is a concatenation of the string arguments to   
+   >               TTQRE.   
+   > \endverbatim   
+   >   
+   > \param[in] N   
+   > \verbatim   
+   >          N is integer scalar   
+   >               N is the order of the Hessenberg matrix H.   
+   > \endverbatim   
+   >   
+   > \param[in] ILO   
+   > \verbatim   
+   >          ILO is INTEGER   
+   > \endverbatim   
+   >   
+   > \param[in] IHI   
+   > \verbatim   
+   >          IHI is INTEGER   
+   >               It is assumed that H is already upper triangular   
+   >               in rows and columns 1:ILO-1 and IHI+1:N.   
+   > \endverbatim   
+   >   
+   > \param[in] LWORK   
+   > \verbatim   
+   >          LWORK is integer scalar   
+   >               The amount of workspace available.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERauxiliary   
+
+   > \par Further Details:   
+    =====================   
+   >   
+   > \verbatim   
+   >   
+   >       Little is known about how best to choose these parameters.   
+   >       It is possible to use different values of the parameters   
+   >       for each of CHSEQR, DHSEQR, SHSEQR and ZHSEQR.   
+   >   
+   >       It is probably best to choose different parameters for   
+   >       different matrices and different parameters at different   
+   >       times during the iteration, but this has not been   
+   >       implemented --- yet.   
+   >   
+   >   
+   >       The best choices of most of the parameters depend   
+   >       in an ill-understood way on the relative execution   
+   >       rate of xLAQR3 and xLAQR5 and on the nature of each   
+   >       particular eigenvalue problem.  Experiment may be the   
+   >       only practical way to determine which choices are most   
+   >       effective.   
+   >   
+   >       Following is a list of default values supplied by IPARMQ.   
+   >       These defaults may be adjusted in order to attain better   
+   >       performance in any particular computational environment.   
+   >   
+   >       IPARMQ(ISPEC=12) The xLAHQR vs xLAQR0 crossover point.   
+   >                        Default: 75. (Must be at least 11.)   
+   >   
+   >       IPARMQ(ISPEC=13) Recommended deflation window size.   
+   >                        This depends on ILO, IHI and NS, the   
+   >                        number of simultaneous shifts returned   
+   >                        by IPARMQ(ISPEC=15).  The default for   
+   >                        (IHI-ILO+1).LE.500 is NS.  The default   
+   >                        for (IHI-ILO+1).GT.500 is 3*NS/2.   
+   >   
+   >       IPARMQ(ISPEC=14) Nibble crossover point.  Default: 14.   
+   >   
+   >       IPARMQ(ISPEC=15) Number of simultaneous shifts, NS.   
+   >                        a multi-shift QR iteration.   
+   >   
+   >                        If IHI-ILO+1 is ...   
+   >   
+   >                        greater than      ...but less    ... the   
+   >                        or equal to ...      than        default is   
+   >   
+   >                                0               30       NS =   2+   
+   >                               30               60       NS =   4+   
+   >                               60              150       NS =  10   
+   >                              150              590       NS =  **   
+   >                              590             3000       NS =  64   
+   >                             3000             6000       NS = 128   
+   >                             6000             infinity   NS = 256   
+   >   
+   >                    (+)  By default matrices of this order are   
+   >                         passed to the implicit double shift routine   
+   >                         xLAHQR.  See IPARMQ(ISPEC=12) above.   These   
+   >                         values of NS are used only in case of a rare   
+   >                         xLAHQR failure.   
+   >   
+   >                    (**) The asterisks (**) indicate an ad-hoc   
+   >                         function increasing from 10 to 64.   
+   >   
+   >       IPARMQ(ISPEC=16) Select structured matrix multiply.   
+   >                        (See ISPEC=16 above for details.)   
+   >                        Default: 3.   
+   > \endverbatim   
+   >   
+    ===================================================================== */
+integer igraphiparmq_(integer *ispec, char *name__, char *opts, integer *n, integer 
+	*ilo, integer *ihi, integer *lwork)
+{
+    /* System generated locals */
+    integer ret_val, i__1, i__2;
+    real r__1;
+
+    /* Builtin functions */
+    double log(doublereal);
+    integer i_nint(real *);
+
+    /* Local variables */
+    integer nh, ns;
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+    ================================================================ */
+    if (*ispec == 15 || *ispec == 13 || *ispec == 16) {
+
+/*        ==== Set the number simultaneous shifts ==== */
+
+	nh = *ihi - *ilo + 1;
+	ns = 2;
+	if (nh >= 30) {
+	    ns = 4;
+	}
+	if (nh >= 60) {
+	    ns = 10;
+	}
+	if (nh >= 150) {
+/* Computing MAX */
+	    r__1 = log((real) nh) / log(2.f);
+	    i__1 = 10, i__2 = nh / i_nint(&r__1);
+	    ns = max(i__1,i__2);
+	}
+	if (nh >= 590) {
+	    ns = 64;
+	}
+	if (nh >= 3000) {
+	    ns = 128;
+	}
+	if (nh >= 6000) {
+	    ns = 256;
+	}
+/* Computing MAX */
+	i__1 = 2, i__2 = ns - ns % 2;
+	ns = max(i__1,i__2);
+    }
+
+    if (*ispec == 12) {
+
+
+/*        ===== Matrices of order smaller than NMIN get sent   
+          .     to xLAHQR, the classic double shift algorithm.   
+          .     This must be at least 11. ==== */
+
+	ret_val = 75;
+
+    } else if (*ispec == 14) {
+
+/*        ==== INIBL: skip a multi-shift qr iteration and   
+          .    whenever aggressive early deflation finds   
+          .    at least (NIBBLE*(window size)/100) deflations. ==== */
+
+	ret_val = 14;
+
+    } else if (*ispec == 15) {
+
+/*        ==== NSHFTS: The number of simultaneous shifts ===== */
+
+	ret_val = ns;
+
+    } else if (*ispec == 13) {
+
+/*        ==== NW: deflation window size.  ==== */
+
+	if (nh <= 500) {
+	    ret_val = ns;
+	} else {
+	    ret_val = ns * 3 / 2;
+	}
+
+    } else if (*ispec == 16) {
+
+/*        ==== IACC22: Whether to accumulate reflections   
+          .     before updating the far-from-diagonal elements   
+          .     and whether to use 2-by-2 block structure while   
+          .     doing it.  A small amount of work could be saved   
+          .     by making this choice dependent also upon the   
+          .     NH=IHI-ILO+1. */
+
+	ret_val = 0;
+	if (ns >= 14) {
+	    ret_val = 1;
+	}
+	if (ns >= 14) {
+	    ret_val = 2;
+	}
+
+    } else {
+/*        ===== invalid value of ispec ===== */
+	ret_val = -1;
+
+    }
+
+/*     ==== End of IPARMQ ==== */
+
+    return ret_val;
+} /* igraphiparmq_ */
+
diff --git a/igraph/src/iterators.c b/igraph/src/iterators.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/iterators.c
@@ -0,0 +1,1917 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_iterators.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_interface.h"
+#include "config.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+/**
+ * \section about_iterators About selectors, iterators
+ *
+ * <para>Everything about vertices and vertex selectors also applies
+ * to edges and edge selectors unless explicitly noted otherwise.</para>
+ *
+ * <para>The vertex (and edge) selector notion was introduced in igraph 0.2.
+ * It is a way to reference a sequence of vertices or edges
+ * independently of the graph.</para>
+ *
+ * <para>While this might sound quite mysterious, it is actually very
+ * simple. For example, all vertices of a graph can be selected by
+ * \ref igraph_vs_all() and the graph independence means that
+ * \ref igraph_vs_all() is not parametrized by a graph object. That is,
+ * \ref igraph_vs_all() is the general \em concept of selecting all vertices
+ * of a graph. A vertex selector is then a way to specify the class of vertices
+ * to be visited. The selector might specify that all vertices of a graph or
+ * all the neighbours of a vertex are to be visited. A vertex selector is a
+ * way of saying that you want to visit a bunch of vertices, as opposed to a
+ * vertex iterator which is a concrete plan for visiting each of the
+ * chosen vertices of a specific graph.</para>
+ *
+ * <para>To determine the actual vertex IDs implied by a vertex selector, you
+ * need to apply the concept of selecting vertices to a specific graph object.
+ * This can be accomplished by instantiating a vertex iterator using a
+ * specific vertex selection concept and a specific graph object. The notion
+ * of vertex iterators can be thought of in the following way. Given a
+ * specific graph object and the class of vertices to be visited, a vertex
+ * iterator is a road map, plan or route for how to visit the chosen
+ * vertices.</para>
+ *
+ * <para>Some vertex selectors have \em immediate versions. These have the
+ * prefix \c igraph_vss instead of \c igraph_vs, e.g. \ref igraph_vss_all()
+ * instead of \ref igraph_vs_all(). The immediate versions are to be used in
+ * the parameter list of the igraph functions, such as \ref igraph_degree().
+ * These functions are not associated with any \type igraph_vs_t object, so
+ * they have no separate constructors and destructors
+ * (destroy functions).</para>
+ */
+
+/**
+ * \section about_vertex_selectors
+ *
+ * <para>Vertex selectors are created by vertex selector constructors,
+ * can be instantiated with \ref igraph_vit_create(), and are
+ * destroyed with \ref igraph_vs_destroy().</para>
+ */
+
+/**
+ * \function igraph_vs_all
+ * \brief Vertex set, all vertices of a graph.
+ *
+ * \param vs Pointer to an uninitialized \type igraph_vs_t object.
+ * \return Error code.
+ * \sa \ref igraph_vss_all(), \ref igraph_vs_destroy()
+ *
+ * This selector includes all vertices of a given graph in
+ * increasing vertex id order.
+ *
+ * </para><para>
+ * Time complexity: O(1).
+ */
+
+int igraph_vs_all(igraph_vs_t *vs) {
+    vs->type = IGRAPH_VS_ALL;
+    return 0;
+}
+
+/**
+ * \function igraph_vss_all
+ * \brief All vertices of a graph (immediate version).
+ *
+ * Immediate vertex selector for all vertices in a graph. It can
+ * be used conveniently when some vertex property (eg. betweenness,
+ * degree, etc.) should be calculated for all vertices.
+ *
+ * \return A vertex selector for all vertices in a graph.
+ * \sa \ref igraph_vs_all()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_vs_t igraph_vss_all(void) {
+    igraph_vs_t allvs;
+    allvs.type = IGRAPH_VS_ALL;
+    return allvs;
+}
+
+/**
+ * \function igraph_vs_adj
+ * \brief Adjacent vertices of a vertex.
+ *
+ * All neighboring vertices of a given vertex are selected by this
+ * selector. The \c mode argument controls the type of the neighboring
+ * vertices to be selected. The vertices are visited in increasing vertex
+ * ID order, as of igraph version 0.4.
+ *
+ * \param vs Pointer to an uninitialized vertex selector object.
+ * \param vid Vertex ID, the center of the neighborhood.
+ * \param mode Decides the type of the neighborhood for directed
+ *        graphs. This parameter is ignored for undirected graphs.
+ *        Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          All vertices to which there is a directed edge from \c vid. That
+ *          is, all the out-neighbors of \c vid.
+ *        \cli IGRAPH_IN
+ *          All vertices from which there is a directed edge to \c vid. In
+ *          other words, all the in-neighbors of \c vid.
+ *        \cli IGRAPH_ALL
+ *          All vertices to which or from which there is a directed edge
+ *          from/to \c vid. That is, all the neighbors of \c vid considered
+ *          as if the graph is undirected.
+ *        \endclist
+ * \return Error code.
+ * \sa \ref igraph_vs_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_vs_adj(igraph_vs_t *vs,
+                  igraph_integer_t vid, igraph_neimode_t mode) {
+    vs->type = IGRAPH_VS_ADJ;
+    vs->data.adj.vid = vid;
+    vs->data.adj.mode = mode;
+    return 0;
+}
+
+/**
+ * \function igraph_vs_nonadj
+ * \brief Non-adjacent vertices of a vertex.
+ *
+ * All non-neighboring vertices of a given vertex. The \p mode
+ * argument controls the type of neighboring vertices \em not to
+ * select. Instead of selecting immediate neighbors of \c vid as is done by
+ * \ref igraph_vs_adj(), the current function selects vertices that are \em not
+ * immediate neighbors of \c vid.
+ *
+ * \param vs Pointer to an uninitialized vertex selector object.
+ * \param vid Vertex ID, the \quote center \endquote of the
+ *        non-neighborhood.
+ * \param mode The type of neighborhood not to select in directed
+ *        graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          All vertices will be selected except those to which there is a
+ *          directed edge from \c vid. That is, we select all vertices
+ *          excluding the out-neighbors of \c vid.
+ *        \cli IGRAPH_IN
+ *          All vertices will be selected except those from which there is a
+ *          directed edge to \c vid. In other words, we select all vertices
+ *          but the in-neighbors of \c vid.
+ *        \cli IGRAPH_ALL
+ *          All vertices will be selected except those from or to which there
+ *          is a directed edge to or from \c vid. That is, we select all
+ *          vertices of \c vid except for its immediate neighbors.
+ *        \endclist
+ * \return Error code.
+ * \sa \ref igraph_vs_destroy()
+ *
+ * Time complexity: O(1).
+ *
+ * \example examples/simple/igraph_vs_nonadj.c
+ */
+
+int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid,
+                     igraph_neimode_t mode) {
+    vs->type = IGRAPH_VS_NONADJ;
+    vs->data.adj.vid = vid;
+    vs->data.adj.mode = mode;
+    return 0;
+}
+
+/**
+ * \function igraph_vs_none
+ * \brief Empty vertex set.
+ *
+ * Creates an empty vertex selector.
+ *
+ * \param vs Pointer to an uninitialized vertex selector object.
+ * \return Error code.
+ * \sa \ref igraph_vss_none(), \ref igraph_vs_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_vs_none(igraph_vs_t *vs) {
+    vs->type = IGRAPH_VS_NONE;
+    return 0;
+}
+
+/**
+ * \function igraph_vss_none
+ * \brief Empty vertex set (immediate version).
+ *
+ * The immediate version of the empty vertex selector.
+ *
+ * \return An empty vertex selector.
+ * \sa \ref igraph_vs_none()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_vs_t igraph_vss_none(void) {
+    igraph_vs_t nonevs;
+    nonevs.type = IGRAPH_VS_NONE;
+    return nonevs;
+}
+
+/**
+ * \function igraph_vs_1
+ * \brief Vertex set with a single vertex.
+ *
+ * This vertex selector selects a single vertex.
+ *
+ * \param vs Pointer to an uninitialized vertex selector object.
+ * \param vid The vertex id to be selected.
+ * \return Error Code.
+ * \sa \ref igraph_vss_1(), \ref igraph_vs_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid) {
+    vs->type = IGRAPH_VS_1;
+    vs->data.vid = vid;
+    return 0;
+}
+
+/**
+ * \function igraph_vss_1
+ * \brief Vertex set with a single vertex (immediate version).
+ *
+ * The immediate version of the single-vertex selector.
+ *
+ * \param vid The vertex to be selected.
+ * \return A vertex selector containing a single vertex.
+ * \sa \ref igraph_vs_1()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_vs_t igraph_vss_1(igraph_integer_t vid) {
+    igraph_vs_t onevs;
+    onevs.type = IGRAPH_VS_1;
+    onevs.data.vid = vid;
+    return onevs;
+}
+
+/**
+ * \function igraph_vs_vector
+ * \brief Vertex set based on a vector.
+ *
+ * This function makes it possible to handle a \type vector_t
+ * temporarily as a vertex selector. The vertex selector should be
+ * thought of like a \em view to the vector. If you make changes to
+ * the vector that also affects the vertex selector. Destroying the
+ * vertex selector does not destroy the vector. (Of course.) Do not
+ * destroy the vector before destroying the vertex selector, or you
+ * might get strange behavior.
+ *
+ * \param vs Pointer to an uninitialized vertex selector.
+ * \param v Pointer to a \type igraph_vector_t object.
+ * \return Error code.
+ * \sa \ref igraph_vss_vector(), \ref igraph_vs_destroy()
+ *
+ * Time complexity: O(1).
+ *
+ * \example examples/simple/igraph_vs_vector.c
+ */
+
+int igraph_vs_vector(igraph_vs_t *vs,
+                     const igraph_vector_t *v) {
+    vs->type = IGRAPH_VS_VECTORPTR;
+    vs->data.vecptr = v;
+    return 0;
+}
+
+/**
+ * \function igraph_vss_vector
+ * \brief Vertex set based on a vector (immediate version).
+ *
+ * This is the immediate version of \ref igraph_vs_vector.
+ *
+ * \param v Pointer to a \type igraph_vector_t object.
+ * \return A vertex selector object containing the vertices in the
+ *         vector.
+ * \sa \ref igraph_vs_vector()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_vs_t igraph_vss_vector(const igraph_vector_t *v) {
+    igraph_vs_t vecvs;
+    vecvs.type = IGRAPH_VS_VECTORPTR;
+    vecvs.data.vecptr = v;
+    return vecvs;
+}
+
+/**
+ * \function igraph_vs_vector_small
+ * \brief Create a vertex set by giving its elements.
+ *
+ * This function can be used to create a vertex selector with a couple
+ * of vertices. Do not forget to include a <code>-1</code> after the
+ * last vertex id. The behavior of the function is undefined if you
+ * don't use a <code>-1</code> properly.
+ *
+ * </para><para>
+ * Note that the vertex ids supplied will be parsed as
+ * <code>int</code>'s so you cannot supply arbitrarily large (too
+ * large for int) vertex ids here.
+ *
+ * \param vs Pointer to an uninitialized vertex selector object.
+ * \param ... Additional parameters, these will be the vertex ids to
+ *        be included in the vertex selector. Supply a <code>-1</code>
+ *        after the last vertex id.
+ * \return Error code.
+ * \sa \ref igraph_vs_destroy()
+ *
+ * Time complexity: O(n), the number of vertex ids supplied.
+ */
+
+int igraph_vs_vector_small(igraph_vs_t *vs, ...) {
+    va_list ap;
+    long int i, n = 0;
+    vs->type = IGRAPH_VS_VECTOR;
+    vs->data.vecptr = igraph_Calloc(1, igraph_vector_t);
+    if (vs->data.vecptr == 0) {
+        IGRAPH_ERROR("Cannot create vertex selector", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)vs->data.vecptr);
+
+    va_start(ap, vs);
+    while (1) {
+        int num = va_arg(ap, int);
+        if (num == -1) {
+            break;
+        }
+        n++;
+    }
+    va_end(ap);
+
+    IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)vs->data.vecptr, n);
+
+    va_start(ap, vs);
+    for (i = 0; i < n; i++) {
+        VECTOR(*vs->data.vecptr)[i] = (igraph_real_t) va_arg(ap, int);
+    }
+    va_end(ap);
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_vs_vector_copy
+ * \brief Vertex set based on a vector, with copying.
+ *
+ * This function makes it possible to handle a \type vector_t
+ * permanently as a vertex selector. The vertex selector creates a
+ * copy of the original vector, so the vector can safely be destroyed
+ * after creating the vertex selector. Changing the original vector
+ * will not affect the vertex selector. The vertex selector is
+ * responsible for deleting the copy made by itself.
+ *
+ * \param vs Pointer to an uninitialized vertex selector.
+ * \param v Pointer to a \type igraph_vector_t object.
+ * \return Error code.
+ * \sa \ref igraph_vs_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_vs_vector_copy(igraph_vs_t *vs,
+                          const igraph_vector_t *v) {
+    vs->type = IGRAPH_VS_VECTOR;
+    vs->data.vecptr = igraph_Calloc(1, igraph_vector_t);
+    if (vs->data.vecptr == 0) {
+        IGRAPH_ERROR("Cannot create vertex selector", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)vs->data.vecptr);
+    IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)vs->data.vecptr, v));
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_vs_seq
+ * \brief Vertex set, an interval of vertices.
+ *
+ * Creates a vertex selector containing all vertices with vertex id
+ * equal to or bigger than \c from and equal to or smaller than \c
+ * to.
+ *
+ * \param vs Pointer to an uninitialized vertex selector object.
+ * \param from The first vertex id to be included in the vertex
+ *        selector.
+ * \param to The last vertex id to be included in the vertex
+ *        selector.
+ * \return Error code.
+ * \sa \ref igraph_vss_seq(), \ref igraph_vs_destroy()
+ *
+ * Time complexity: O(1).
+ *
+ * \example examples/simple/igraph_vs_seq.c
+ */
+
+int igraph_vs_seq(igraph_vs_t *vs,
+                  igraph_integer_t from, igraph_integer_t to) {
+    vs->type = IGRAPH_VS_SEQ;
+    vs->data.seq.from = from;
+    vs->data.seq.to = to + 1;
+    return 0;
+}
+
+/**
+ * \function igraph_vss_seq
+ * \brief An interval of vertices (immediate version).
+ *
+ * The immediate version of \ref igraph_vs_seq().
+ *
+ * \param from The first vertex id to be included in the vertex
+ *        selector.
+ * \param to The last vertex id to be included in the vertex
+ *        selector.
+ * \return Error code.
+ * \sa \ref igraph_vs_seq()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to) {
+    igraph_vs_t vs;
+    vs.type = IGRAPH_VS_SEQ;
+    vs.data.seq.from = from;
+    vs.data.seq.to = to + 1;
+    return vs;
+}
+
+/**
+ * \function igraph_vs_destroy
+ * \brief Destroy a vertex set.
+ *
+ * This function should be called for all vertex selectors when they
+ * are not needed. The memory allocated for the vertex selector will
+ * be deallocated. Do not call this function on vertex selectors
+ * created with the immediate versions of the vertex selector
+ * constructors (starting with <code>igraph_vss</code>).
+ *
+ * \param vs Pointer to a vertex selector object.
+ *
+ * Time complexity: operating system dependent, usually O(1).
+ */
+
+void igraph_vs_destroy(igraph_vs_t *vs) {
+    switch (vs->type) {
+    case IGRAPH_VS_ALL:
+    case IGRAPH_VS_ADJ:
+    case IGRAPH_VS_NONE:
+    case IGRAPH_VS_1:
+    case IGRAPH_VS_VECTORPTR:
+    case IGRAPH_VS_SEQ:
+    case IGRAPH_VS_NONADJ:
+        break;
+    case IGRAPH_VS_VECTOR:
+        igraph_vector_destroy((igraph_vector_t*)vs->data.vecptr);
+        igraph_Free(vs->data.vecptr);
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * \function igraph_vs_is_all
+ * \brief Check whether all vertices are included.
+ *
+ * This function checks whether the vertex selector object was created
+ * by \ref igraph_vs_all() or \ref igraph_vss_all(). Note that the
+ * vertex selector might contain all vertices in a given graph but if
+ * it wasn't created by the two constructors mentioned here the return
+ * value will be FALSE.
+ *
+ * \param vs Pointer to a vertex selector object.
+ * \return TRUE (1) if the vertex selector contains all vertices and
+ *         FALSE (0) otherwise.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs) {
+    return vs->type == IGRAPH_VS_ALL;
+}
+
+int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs,
+                        igraph_vector_t *v) {
+    igraph_vit_t vit;
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vs, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    IGRAPH_CHECK(igraph_vit_as_vector(&vit, v));
+
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_vs_copy
+ * \brief Creates a copy of a vertex selector.
+ * \param src The selector being copied.
+ * \param dest An uninitialized selector that will contain the copy.
+ */
+int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src) {
+    memcpy(dest, src, sizeof(igraph_vs_t));
+    switch (dest->type) {
+    case IGRAPH_VS_VECTOR:
+        dest->data.vecptr = igraph_Calloc(1, igraph_vector_t);
+        if (!dest->data.vecptr) {
+            IGRAPH_ERROR("Cannot copy vertex selector", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.vecptr,
+                                        (igraph_vector_t*)src->data.vecptr));
+        break;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_vs_type
+ * \brief Returns the type of the vertex selector.
+ */
+int igraph_vs_type(const igraph_vs_t *vs) {
+    return vs->type;
+}
+
+/**
+ * \function igraph_vs_size
+ * \brief Returns the size of the vertex selector.
+ *
+ * The size of the vertex selector is the number of vertices it will
+ * yield when it is iterated over.
+ *
+ * \param graph The graph over which we will iterate.
+ * \param result The result will be returned here.
+ */
+int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs,
+                   igraph_integer_t *result) {
+    igraph_vector_t vec;
+    igraph_bool_t *seen;
+    long i;
+
+    switch (vs->type) {
+    case IGRAPH_VS_NONE:
+        *result = 0; return 0;
+
+    case IGRAPH_VS_1:
+        *result = 0;
+        if (vs->data.vid < igraph_vcount(graph) && vs->data.vid >= 0) {
+            *result = 1;
+        }
+        return 0;
+
+    case IGRAPH_VS_SEQ:
+        *result = vs->data.seq.to - vs->data.seq.from;
+        return 0;
+
+    case IGRAPH_VS_ALL:
+        *result = igraph_vcount(graph); return 0;
+
+    case IGRAPH_VS_ADJ:
+        IGRAPH_VECTOR_INIT_FINALLY(&vec, 0);
+        IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode));
+        *result = (igraph_integer_t) igraph_vector_size(&vec);
+        igraph_vector_destroy(&vec);
+        IGRAPH_FINALLY_CLEAN(1);
+        return 0;
+
+    case IGRAPH_VS_NONADJ:
+        IGRAPH_VECTOR_INIT_FINALLY(&vec, 0);
+        IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode));
+        *result = igraph_vcount(graph);
+        seen = igraph_Calloc(*result, igraph_bool_t);
+        if (seen == 0) {
+            IGRAPH_ERROR("Cannot calculate selector length", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, seen);
+        for (i = 0; i < igraph_vector_size(&vec); i++) {
+            if (!seen[(long int)VECTOR(vec)[i]]) {
+                (*result)--;
+                seen[(long int)VECTOR(vec)[i]] = 1;
+            }
+        }
+        igraph_free(seen);
+        igraph_vector_destroy(&vec);
+        IGRAPH_FINALLY_CLEAN(2);
+        return 0;
+
+    case IGRAPH_VS_VECTOR:
+    case IGRAPH_VS_VECTORPTR:
+        *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)vs->data.vecptr);
+        return 0;
+    }
+
+    IGRAPH_ERROR("Cannot calculate selector length, invalid selector type",
+                 IGRAPH_EINVAL);
+}
+
+/***************************************************/
+
+/**
+ * \function igraph_vit_create
+ * \brief Creates a vertex iterator from a vertex selector.
+ *
+ * This function instantiates a vertex selector object with a given
+ * graph. This is the step when the actual vertex ids are created from
+ * the \em logical notion of the vertex selector based on the graph.
+ * Eg. a vertex selector created with \ref igraph_vs_all() contains
+ * knowledge that \em all vertices are included in a (yet indefinite)
+ * graph. When instantiating it a vertex iterator object is created,
+ * this contains the actual vertex ids in the graph supplied as a
+ * parameter.
+ *
+ * </para><para>
+ * The same vertex selector object can be used to instantiate any
+ * number vertex iterators.
+ *
+ * \param graph An \type igraph_t object, a graph.
+ * \param vs A vertex selector object.
+ * \param vit Pointer to an uninitialized vertex iterator object.
+ * \return Error code.
+ * \sa \ref igraph_vit_destroy().
+ *
+ * Time complexity: it depends on the vertex selector type. O(1) for
+ * vertex selectors created with \ref igraph_vs_all(), \ref
+ * igraph_vs_none(), \ref igraph_vs_1, \ref igraph_vs_vector, \ref
+ * igraph_vs_seq(), \ref igraph_vs_vector(), \ref
+ * igraph_vs_vector_small(). O(d) for \ref igraph_vs_adj(), d is the
+ * number of vertex ids to be included in the iterator. O(|V|) for
+ * \ref igraph_vs_nonadj(), |V| is the number of vertices in the graph.
+ */
+
+int igraph_vit_create(const igraph_t *graph,
+                      igraph_vs_t vs, igraph_vit_t *vit) {
+    igraph_vector_t vec;
+    igraph_bool_t *seen;
+    long int i, j, n;
+
+    switch (vs.type) {
+    case IGRAPH_VS_ALL:
+        vit->type = IGRAPH_VIT_SEQ;
+        vit->pos = 0;
+        vit->start = 0;
+        vit->end = igraph_vcount(graph);
+        break;
+    case IGRAPH_VS_ADJ:
+        vit->type = IGRAPH_VIT_VECTOR;
+        vit->pos = 0;
+        vit->start = 0;
+        vit->vec = igraph_Calloc(1, igraph_vector_t);
+        if (vit->vec == 0) {
+            IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) vit->vec);
+        IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)vit->vec, 0);
+        IGRAPH_CHECK(igraph_neighbors(graph, (igraph_vector_t*)vit->vec,
+                                      vs.data.adj.vid, vs.data.adj.mode));
+        vit->end = igraph_vector_size(vit->vec);
+        IGRAPH_FINALLY_CLEAN(2);
+        break;
+    case IGRAPH_VS_NONADJ:
+        vit->type = IGRAPH_VIT_VECTOR;
+        vit->pos = 0;
+        vit->start = 0;
+        vit->vec = igraph_Calloc(1, igraph_vector_t);
+        if (vit->vec == 0) {
+            IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) vit->vec);
+        IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t *) vit->vec, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&vec, 0);
+        IGRAPH_CHECK(igraph_neighbors(graph, &vec,
+                                      vs.data.adj.vid, vs.data.adj.mode));
+        n = igraph_vcount(graph);
+        seen = igraph_Calloc(n, igraph_bool_t);
+        if (seen == 0) {
+            IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, seen);
+        for (i = 0; i < igraph_vector_size(&vec); i++) {
+            if (! seen [ (long int) VECTOR(vec)[i] ] ) {
+                n--;
+                seen[ (long int) VECTOR(vec)[i] ] = 1;
+            }
+        }
+        IGRAPH_CHECK(igraph_vector_resize((igraph_vector_t*)vit->vec, n));
+        for (i = 0, j = 0; j < n; i++) {
+            if (!seen[i]) {
+                VECTOR(*vit->vec)[j++] = i;
+            }
+        }
+
+        igraph_Free(seen);
+        igraph_vector_destroy(&vec);
+        vit->end = n;
+        IGRAPH_FINALLY_CLEAN(4);
+        break;
+    case IGRAPH_VS_NONE:
+        vit->type = IGRAPH_VIT_SEQ;
+        vit->pos = 0;
+        vit->start = 0;
+        vit->end = 0;
+        break;
+    case IGRAPH_VS_1:
+        vit->type = IGRAPH_VIT_SEQ;
+        vit->pos = vs.data.vid;
+        vit->start = vs.data.vid;
+        vit->end = vs.data.vid + 1;
+        if (vit->pos >= igraph_vcount(graph)) {
+            IGRAPH_ERROR("Cannot create iterator, invalid vertex id", IGRAPH_EINVVID);
+        }
+        break;
+    case IGRAPH_VS_VECTORPTR:
+    case IGRAPH_VS_VECTOR:
+        vit->type = IGRAPH_VIT_VECTORPTR;
+        vit->pos = 0;
+        vit->start = 0;
+        vit->vec = vs.data.vecptr;
+        vit->end = igraph_vector_size(vit->vec);
+        if (!igraph_vector_isininterval(vit->vec, 0, igraph_vcount(graph) - 1)) {
+            IGRAPH_ERROR("Cannot create iterator, invalid vertex id", IGRAPH_EINVVID);
+        }
+        break;
+    case IGRAPH_VS_SEQ:
+        vit->type = IGRAPH_VIT_SEQ;
+        vit->pos = vs.data.seq.from;
+        vit->start = vs.data.seq.from;
+        vit->end = vs.data.seq.to;
+        break;
+    default:
+        IGRAPH_ERROR("Cannot create iterator, invalid selector", IGRAPH_EINVAL);
+        break;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_vit_destroy
+ * \brief Destroys a vertex iterator.
+ *
+ * </para><para>
+ * Deallocates memory allocated for a vertex iterator.
+ *
+ * \param vit Pointer to an initialized vertex iterator object.
+ * \sa \ref igraph_vit_create()
+ *
+ * Time complexity: operating system dependent, usually O(1).
+ */
+
+void igraph_vit_destroy(const igraph_vit_t *vit) {
+    switch (vit->type) {
+    case IGRAPH_VIT_SEQ:
+    case IGRAPH_VIT_VECTORPTR:
+        break;
+    case IGRAPH_VIT_VECTOR:
+        igraph_vector_destroy((igraph_vector_t*)vit->vec);
+        igraph_free((igraph_vector_t*)vit->vec);
+        break;
+    default:
+        /*     IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */
+        break;
+    }
+}
+
+int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v) {
+
+    long int i;
+
+    IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_VIT_SIZE(*vit)));
+
+    switch (vit->type) {
+    case IGRAPH_VIT_SEQ:
+        for (i = 0; i < IGRAPH_VIT_SIZE(*vit); i++) {
+            VECTOR(*v)[i] = vit->start + i;
+        }
+        break;
+    case IGRAPH_VIT_VECTOR:
+    case IGRAPH_VIT_VECTORPTR:
+        for (i = 0; i < IGRAPH_VIT_SIZE(*vit); i++) {
+            VECTOR(*v)[i] = VECTOR(*vit->vec)[i];
+        }
+        break;
+    default:
+        IGRAPH_ERROR("Cannot convert to vector, unknown iterator type",
+                     IGRAPH_EINVAL);
+        break;
+    }
+
+    return 0;
+}
+
+/*******************************************************/
+
+/**
+ * \function igraph_es_all
+ * \brief Edge set, all edges.
+ *
+ * \param es Pointer to an uninitialized edge selector object.
+ * \param order Constant giving the order in which the edges will be
+ *        included in the selector. Possible values:
+ *        \c IGRAPH_EDGEORDER_ID, edge id order.
+ *        \c IGRAPH_EDGEORDER_FROM, vertex id order, the id of the
+ *           \em source vertex counts for directed graphs. The order
+ *           of the incident edges of a given vertex is arbitrary.
+ *        \c IGRAPH_EDGEORDER_TO, vertex id order, the id of the \em
+ *           target vertex counts for directed graphs. The order
+ *           of the incident edges of a given vertex is arbitrary.
+ *        For undirected graph the latter two is the same.
+ * \return Error code.
+ * \sa \ref igraph_ess_all(), \ref igraph_es_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_es_all(igraph_es_t *es,
+                  igraph_edgeorder_type_t order) {
+    switch (order) {
+    case IGRAPH_EDGEORDER_ID:
+        es->type = IGRAPH_ES_ALL;
+        break;
+    case IGRAPH_EDGEORDER_FROM:
+        es->type = IGRAPH_ES_ALLFROM;
+        break;
+    case IGRAPH_EDGEORDER_TO:
+        es->type = IGRAPH_ES_ALLTO;
+        break;
+    default:
+        IGRAPH_ERROR("Invalid edge order, cannot create selector", IGRAPH_EINVAL);
+        break;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_ess_all
+ * \brief Edge set, all edges (immediate version)
+ *
+ * The immediate version of the all-vertices selector.
+ *
+ * \param order Constant giving the order of the edges in the edge
+ *        selector. See \ref igraph_es_all() for the possible values.
+ * \return The edge selector.
+ * \sa \ref igraph_es_all()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order) {
+    igraph_es_t es;
+    igraph_es_all(&es, order); /* cannot fail */
+    return es;
+}
+
+/**
+ * \function igraph_es_adj
+ * \brief Adjacent edges of a vertex.
+ *
+ * This function was superseded by \ref igraph_es_incident() in igraph 0.6.
+ * Please use \ref igraph_es_incident() instead of this function.
+ *
+ * </para><para>
+ * Deprecated in version 0.6.
+ */
+int igraph_es_adj(igraph_es_t *es,
+                  igraph_integer_t vid, igraph_neimode_t mode) {
+    IGRAPH_WARNING("igraph_es_adj is deprecated, use igraph_es_incident");
+    return igraph_es_incident(es, vid, mode);
+}
+
+/**
+ * \function igraph_es_incident
+ * \brief Edges incident on a given vertex.
+ *
+ * \param es Pointer to an uninitialized edge selector object.
+ * \param vid Vertex id, of which the incident edges will be
+ *        selected.
+ * \param mode Constant giving the type of the incident edges to
+ *        select. This is ignored for undirected graphs. Possible values:
+ *        \c IGRAPH_OUT, outgoing edges;
+ *        \c IGRAPH_IN, incoming edges;
+ *        \c IGRAPH_ALL, all edges.
+ * \return Error code.
+ * \sa \ref igraph_es_destroy()
+ *
+ * Time complexity: O(1).
+ *
+ * \example examples/simple/igraph_es_adj.c
+ */
+
+int igraph_es_incident(igraph_es_t *es,
+                       igraph_integer_t vid, igraph_neimode_t mode) {
+    es->type = IGRAPH_ES_INCIDENT;
+    es->data.incident.vid = vid;
+    es->data.incident.mode = mode;
+    return 0;
+}
+
+/**
+ * \function igraph_es_none
+ * \brief Empty edge selector.
+ *
+ * \param es Pointer to an uninitialized edge selector object to
+ * initialize.
+ * \return Error code.
+ * \sa \ref igraph_ess_none(), \ref igraph_es_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_es_none(igraph_es_t *es) {
+    es->type = IGRAPH_ES_NONE;
+    return 0;
+}
+
+/**
+ * \function igraph_ess_none
+ * \brief Immediate empty edge selector.
+ *
+ * </para><para>
+ * Immediate version of the empty edge selector.
+ *
+ * \return Initialized empty edge selector.
+ * \sa \ref igraph_es_none()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_es_t igraph_ess_none(void) {
+    igraph_es_t es;
+    es.type = IGRAPH_ES_NONE;
+    return es;
+}
+
+/**
+ * \function igraph_es_1
+ * \brief Edge selector containing a single edge.
+ *
+ * \param es Pointer to an uninitialized edge selector object.
+ * \param eid Edge id of the edge to select.
+ * \return Error code.
+ * \sa \ref igraph_ess_1(), \ref igraph_es_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_es_1(igraph_es_t *es, igraph_integer_t eid) {
+    es->type = IGRAPH_ES_1;
+    es->data.eid = eid;
+    return 0;
+}
+
+/**
+ * \function igraph_ess_1
+ * \brief Immediate version of the single edge edge selector.
+ *
+ * \param eid The id of the edge.
+ * \return The edge selector.
+ * \sa \ref igraph_es_1()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_es_t igraph_ess_1(igraph_integer_t eid) {
+    igraph_es_t es;
+    es.type = IGRAPH_ES_1;
+    es.data.eid = eid;
+    return es;
+}
+
+/**
+ * \function igraph_es_vector
+ * \brief Handle a vector as an edge selector.
+ *
+ * </para><para>
+ * Creates an edge selector which serves as a view to a vector
+ * containing edge ids. Do not destroy the vector before destroying
+ * the view.
+ *
+ * Many views can be created to the same vector.
+ *
+ * \param es Pointer to an uninitialized edge selector.
+ * \param v Vector containing edge ids.
+ * \return Error code.
+ * \sa \ref igraph_ess_vector(), \ref igraph_es_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_es_vector(igraph_es_t *es,
+                     const igraph_vector_t *v) {
+    es->type = IGRAPH_ES_VECTORPTR;
+    es->data.vecptr = v;
+    return 0;
+}
+
+/**
+ * \function igraph_es_vector_copy
+ * \brief Edge set, based on a vector, with copying.
+ *
+ *
+ * This function makes it possible to handle a \type vector_t
+ * permanently as an edge selector. The edge selector creates a
+ * copy of the original vector, so the vector can safely be destroyed
+ * after creating the edge selector. Changing the original vector
+ * will not affect the edge selector. The edge selector is
+ * responsible for deleting the copy made by itself.
+ *
+ * \param es Pointer to an uninitialized edge selector.
+ * \param v Pointer to a \type igraph_vector_t object.
+ * \return Error code.
+ * \sa \ref igraph_es_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v) {
+    es->type = IGRAPH_ES_VECTOR;
+    es->data.vecptr = igraph_Calloc(1, igraph_vector_t);
+    if (es->data.vecptr == 0) {
+        IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.vecptr);
+    IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)es->data.vecptr, v));
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_ess_vector
+ * \brief Immediate vector view edge selector.
+ *
+ * </para><para>
+ * This is the immediate version of the vector of edge ids edge
+ * selector.
+ *
+ * \param v The vector of edge ids.
+ * \return Edge selector, initialized.
+ * \sa \ref igraph_es_vector()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_es_t igraph_ess_vector(const igraph_vector_t *v) {
+    igraph_es_t es;
+    es.type = IGRAPH_ES_VECTORPTR;
+    es.data.vecptr = v;
+    return es;
+}
+
+/**
+ * \function igraph_es_fromto
+ * \brief Edge selector, all edges between two vertex sets.
+ *
+ * </para><para>
+ * This function is not implemented yet.
+ *
+ * \param es Pointer to an uninitialized edge selector.
+ * \param from Vertex selector, their outgoing edges will be
+ *        selected.
+ * \param to Vertex selector, their incoming edges will be selected
+ *        from the previous selection.
+ * \return Error code.
+ * \sa \ref igraph_es_destroy()
+ *
+ * Time complexity: O(1).
+ *
+ * \example examples/simple/igraph_es_fromto.c
+ */
+
+int igraph_es_fromto(igraph_es_t *es,
+                     igraph_vs_t from, igraph_vs_t to) {
+
+    IGRAPH_UNUSED(es); IGRAPH_UNUSED(from); IGRAPH_UNUSED(to);
+    IGRAPH_ERROR("igraph_es_fromto not implemented yet", IGRAPH_UNIMPLEMENTED);
+    /* TODO */
+    return 0;
+}
+
+/**
+ * \function igraph_es_seq
+ * \brief Edge selector, a sequence of edge ids.
+ *
+ * All edge ids between <code>from</code> and <code>to</code> will be
+ * included in the edge selection.
+ *
+ * \param es Pointer to an uninitialized edge selector object.
+ * \param from The first edge id to be included.
+ * \param to The last edge id to be included.
+ * \return Error code.
+ * \sa \ref igraph_ess_seq(), \ref igraph_es_destroy()
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_es_seq(igraph_es_t *es,
+                  igraph_integer_t from, igraph_integer_t to) {
+    es->type = IGRAPH_ES_SEQ;
+    es->data.seq.from = from;
+    es->data.seq.to = to;
+    return 0;
+}
+
+/**
+ * \function igraph_ess_seq
+ * \brief Immediate version of the sequence edge selector.
+ *
+ * \param from The first edge id to include.
+ * \param to The last edge id to include.
+ * \return The initialized edge selector.
+ * \sa \ref igraph_es_seq()
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to) {
+    igraph_es_t es;
+    es.type = IGRAPH_ES_SEQ;
+    es.data.seq.from = from;
+    es.data.seq.to = to;
+    return es;
+}
+
+/**
+ * \function igraph_es_pairs
+ * \brief Edge selector, multiple edges defined by their endpoints in a vector.
+ *
+ * The edges between the given pairs of vertices will be included in the
+ * edge selection. The vertex pairs must be defined in the vector <code>v</code>,
+ * the first element of the vector is the first vertex of the first edge
+ * to be selected, the second element is the second vertex of the first
+ * edge, the third element is the first vertex of the second edge and
+ * so on.
+ *
+ * \param es Pointer to an uninitialized edge selector object.
+ * \param v The vector containing the endpoints of the edges.
+ * \param directed Whether the graph is directed or not.
+ * \return Error code.
+ * \sa \ref igraph_es_pairs_small(), \ref igraph_es_destroy()
+ *
+ * Time complexity: O(n), the number of edges being selected.
+ *
+ * \example examples/simple/igraph_es_pairs.c
+ */
+
+int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v,
+                    igraph_bool_t directed) {
+    es->type = IGRAPH_ES_PAIRS;
+    es->data.path.mode = directed;
+    es->data.path.ptr = igraph_Calloc(1, igraph_vector_t);
+    if (es->data.path.ptr == 0) {
+        IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr);
+
+    IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v));
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_es_pairs_small
+ * \brief Edge selector, multiple edges defined by their endpoints as arguments.
+ *
+ * The edges between the given pairs of vertices will be included in the
+ * edge selection. The vertex pairs must be given as the arguments of the
+ * function call, the third argument is the first vertex of the first edge,
+ * the fourth argument is the second vertex of the first edge, the fifth
+ * is the first vertex of the second edge and so on. The last element of the
+ * argument list must be -1 to denote the end of the argument list.
+ *
+ * \param es Pointer to an uninitialized edge selector object.
+ * \param directed Whether the graph is directed or not.
+ * \return Error code.
+ * \sa \ref igraph_es_pairs(), \ref igraph_es_destroy()
+ *
+ * Time complexity: O(n), the number of edges being selected.
+ */
+
+int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...) {
+    va_list ap;
+    long int i, n = 0;
+    es->type = IGRAPH_ES_PAIRS;
+    es->data.path.mode = directed;
+    es->data.path.ptr = igraph_Calloc(1, igraph_vector_t);
+    if (es->data.path.ptr == 0) {
+        IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.path.ptr);
+
+    va_start(ap, directed);
+    while (1) {
+        int num = va_arg(ap, int);
+        if (num == -1) {
+            break;
+        }
+        n++;
+    }
+    va_end(ap);
+
+    IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n);
+
+    va_start(ap, directed);
+    for (i = 0; i < n; i++) {
+        VECTOR(*es->data.path.ptr)[i] = (igraph_real_t) va_arg(ap, int);
+    }
+    va_end(ap);
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v,
+                         igraph_bool_t directed) {
+    es->type = IGRAPH_ES_MULTIPAIRS;
+    es->data.path.mode = directed;
+    es->data.path.ptr = igraph_Calloc(1, igraph_vector_t);
+    if (es->data.path.ptr == 0) {
+        IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr);
+
+    IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v));
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \example examples/simple/igraph_es_path.c
+ */
+
+int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v,
+                   igraph_bool_t directed) {
+    es->type = IGRAPH_ES_PATH;
+    es->data.path.mode = directed;
+    es->data.path.ptr = igraph_Calloc(1, igraph_vector_t);
+    if (es->data.path.ptr == 0) {
+        IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) es->data.path.ptr);
+
+    IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v));
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...) {
+    va_list ap;
+    long int i, n = 0;
+    es->type = IGRAPH_ES_PATH;
+    es->data.path.mode = directed;
+    es->data.path.ptr = igraph_Calloc(1, igraph_vector_t);
+    if (es->data.path.ptr == 0) {
+        IGRAPH_ERROR("Cannot create edge selector", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)es->data.path.ptr);
+
+    va_start(ap, directed);
+    while (1) {
+        int num = va_arg(ap, int);
+        if (num == -1) {
+            break;
+        }
+        n++;
+    }
+    va_end(ap);
+
+    IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n);
+
+    va_start(ap, directed);
+    for (i = 0; i < n; i++) {
+        VECTOR(*es->data.path.ptr)[i] = (igraph_real_t) va_arg(ap, int);
+    }
+    va_end(ap);
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_es_destroy
+ * \brief Destroys an edge selector object.
+ *
+ * </para><para>
+ * Call this function on an edge selector when it is not needed any
+ * more. Do \em not call this function on edge selectors created by
+ * immediate constructors, those don't need to be destroyed.
+ *
+ * \param es Pointer to an edge selector object.
+ *
+ * Time complexity: operating system dependent, usually O(1).
+ */
+
+void igraph_es_destroy(igraph_es_t *es) {
+    switch (es->type) {
+    case IGRAPH_ES_ALL:
+    case IGRAPH_ES_ALLFROM:
+    case IGRAPH_ES_ALLTO:
+    case IGRAPH_ES_INCIDENT:
+    case IGRAPH_ES_NONE:
+    case IGRAPH_ES_1:
+    case IGRAPH_ES_VECTORPTR:
+    case IGRAPH_ES_SEQ:
+        break;
+    case IGRAPH_ES_VECTOR:
+        igraph_vector_destroy((igraph_vector_t*)es->data.vecptr);
+        igraph_Free(es->data.vecptr);
+        break;
+    case IGRAPH_ES_PAIRS:
+    case IGRAPH_ES_PATH:
+    case IGRAPH_ES_MULTIPAIRS:
+        igraph_vector_destroy((igraph_vector_t*)es->data.path.ptr);
+        igraph_Free(es->data.path.ptr);
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * \function igraph_es_is_all
+ * \brief Check whether an edge selector includes all edges.
+ *
+ * \param es Pointer to an edge selector object.
+ * \return TRUE (1) if <code>es</code> was created with \ref
+ * igraph_es_all() or \ref igraph_ess_all(), and FALSE (0) otherwise.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_bool_t igraph_es_is_all(const igraph_es_t *es) {
+    return es->type == IGRAPH_ES_ALL;
+}
+
+/**
+ * \function igraph_es_copy
+ * \brief Creates a copy of an edge selector.
+ * \param src The selector being copied.
+ * \param dest An uninitialized selector that will contain the copy.
+ * \sa \ref igraph_es_destroy()
+ */
+int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src) {
+    memcpy(dest, src, sizeof(igraph_es_t));
+    switch (dest->type) {
+    case IGRAPH_ES_VECTOR:
+        dest->data.vecptr = igraph_Calloc(1, igraph_vector_t);
+        if (!dest->data.vecptr) {
+            IGRAPH_ERROR("Cannot copy edge selector", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.vecptr,
+                                        (igraph_vector_t*)src->data.vecptr));
+        break;
+    case IGRAPH_ES_PATH:
+    case IGRAPH_ES_PAIRS:
+    case IGRAPH_ES_MULTIPAIRS:
+        dest->data.path.ptr = igraph_Calloc(1, igraph_vector_t);
+        if (!dest->data.path.ptr) {
+            IGRAPH_ERROR("Cannot copy edge selector", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*)dest->data.path.ptr,
+                                        (igraph_vector_t*)src->data.path.ptr));
+        break;
+    }
+    return 0;
+}
+
+int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es,
+                        igraph_vector_t *v) {
+    igraph_eit_t eit;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+    IGRAPH_CHECK(igraph_eit_as_vector(&eit, v));
+
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_es_type
+ * \brief Returns the type of the edge selector.
+ */
+int igraph_es_type(const igraph_es_t *es) {
+    return es->type;
+}
+
+int igraph_i_es_pairs_size(const igraph_t *graph,
+                           const igraph_es_t *es, igraph_integer_t *result);
+int igraph_i_es_path_size(const igraph_t *graph,
+                          const igraph_es_t *es, igraph_integer_t *result);
+int igraph_i_es_multipairs_size(const igraph_t *graph,
+                                const igraph_es_t *es, igraph_integer_t *result);
+
+/**
+ * \function igraph_es_size
+ * \brief Returns the size of the edge selector.
+ *
+ * The size of the edge selector is the number of edges it will
+ * yield when it is iterated over.
+ *
+ * \param graph The graph over which we will iterate.
+ * \param result The result will be returned here.
+ */
+int igraph_es_size(const igraph_t *graph, const igraph_es_t *es,
+                   igraph_integer_t *result) {
+    igraph_vector_t v;
+
+    switch (es->type) {
+    case IGRAPH_ES_ALL:
+        *result = igraph_ecount(graph);
+        return 0;
+
+    case IGRAPH_ES_ALLFROM:
+        *result = igraph_ecount(graph);
+        return 0;
+
+    case IGRAPH_ES_ALLTO:
+        *result = igraph_ecount(graph);
+        return 0;
+
+    case IGRAPH_ES_INCIDENT:
+        IGRAPH_VECTOR_INIT_FINALLY(&v, 0);
+        IGRAPH_CHECK(igraph_incident(graph, &v,
+                                     es->data.incident.vid, es->data.incident.mode));
+        *result = (igraph_integer_t) igraph_vector_size(&v);
+        igraph_vector_destroy(&v);
+        IGRAPH_FINALLY_CLEAN(1);
+        return 0;
+
+    case IGRAPH_ES_NONE:
+        *result = 0;
+        return 0;
+
+    case IGRAPH_ES_1:
+        if (es->data.eid < igraph_ecount(graph) && es->data.eid >= 0) {
+            *result = 1;
+        } else {
+            *result = 0;
+        }
+        return 0;
+
+    case IGRAPH_ES_VECTOR:
+    case IGRAPH_ES_VECTORPTR:
+        *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)es->data.vecptr);
+        return 0;
+
+    case IGRAPH_ES_SEQ:
+        *result = es->data.seq.to - es->data.seq.from;
+        return 0;
+
+    case IGRAPH_ES_PAIRS:
+        IGRAPH_CHECK(igraph_i_es_pairs_size(graph, es, result));
+        return 0;
+
+    case IGRAPH_ES_PATH:
+        IGRAPH_CHECK(igraph_i_es_path_size(graph, es, result));
+        return 0;
+
+    case IGRAPH_ES_MULTIPAIRS:
+        IGRAPH_CHECK(igraph_i_es_multipairs_size(graph, es, result));
+        return 0;
+
+    default:
+        IGRAPH_ERROR("Cannot calculate selector length, invalid selector type",
+                     IGRAPH_EINVAL);
+    }
+
+    return 0;
+}
+
+int igraph_i_es_pairs_size(const igraph_t *graph,
+                           const igraph_es_t *es, igraph_integer_t *result) {
+    long int n = igraph_vector_size(es->data.path.ptr);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+
+    if (n % 2 != 0) {
+        IGRAPH_ERROR("Cannot calculate edge selector length from odd number of vertices",
+                     IGRAPH_EINVAL);
+    }
+    if (!igraph_vector_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot calculate edge selector length", IGRAPH_EINVVID);
+    }
+
+    *result = (igraph_integer_t) (n / 2);
+    /* Check for the existence of all edges */
+    for (i = 0; i < *result; i++) {
+        long int from = (long int) VECTOR(*es->data.path.ptr)[2 * i];
+        long int to = (long int) VECTOR(*es->data.path.ptr)[2 * i + 1];
+        igraph_integer_t eid;
+        IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from,
+                                    (igraph_integer_t) to, es->data.path.mode,
+                                    /*error=*/ 1));
+    }
+
+    return 0;
+}
+
+int igraph_i_es_path_size(const igraph_t *graph,
+                          const igraph_es_t *es, igraph_integer_t *result) {
+    long int n = igraph_vector_size(es->data.path.ptr);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+
+    if (!igraph_vector_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot calculate selector length", IGRAPH_EINVVID);
+    }
+
+    if (n <= 1) {
+        *result = 0;
+    } else {
+        *result = (igraph_integer_t) (n - 1);
+    }
+    for (i = 0; i < *result; i++) {
+        long int from = (long int) VECTOR(*es->data.path.ptr)[i];
+        long int to = (long int) VECTOR(*es->data.path.ptr)[i + 1];
+        igraph_integer_t eid;
+        IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from,
+                                    (igraph_integer_t) to, es->data.path.mode,
+                                    /*error=*/ 1));
+    }
+
+    return 0;
+}
+
+int igraph_i_es_multipairs_size(const igraph_t *graph,
+                                const igraph_es_t *es, igraph_integer_t *result) {
+    IGRAPH_UNUSED(graph); IGRAPH_UNUSED(es); IGRAPH_UNUSED(result);
+    IGRAPH_ERROR("Cannot calculate edge selector length", IGRAPH_UNIMPLEMENTED);
+}
+
+/**************************************************/
+
+int igraph_i_eit_create_allfromto(const igraph_t *graph,
+                                  igraph_eit_t *eit,
+                                  igraph_neimode_t mode);
+int igraph_i_eit_pairs(const igraph_t *graph,
+                       igraph_es_t es, igraph_eit_t *eit);
+int igraph_i_eit_multipairs(const igraph_t *graph,
+                            igraph_es_t es, igraph_eit_t *eit);
+int igraph_i_eit_path(const igraph_t *graph,
+                      igraph_es_t es, igraph_eit_t *eit);
+
+int igraph_i_eit_create_allfromto(const igraph_t *graph,
+                                  igraph_eit_t *eit,
+                                  igraph_neimode_t mode) {
+    igraph_vector_t *vec;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+
+    vec = igraph_Calloc(1, igraph_vector_t);
+    if (vec == 0) {
+        IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, vec);
+    IGRAPH_VECTOR_INIT_FINALLY(vec, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(vec, igraph_ecount(graph)));
+
+    if (igraph_is_directed(graph)) {
+        igraph_vector_t adj;
+        IGRAPH_VECTOR_INIT_FINALLY(&adj, 0);
+        for (i = 0; i < no_of_nodes; i++) {
+            igraph_incident(graph, &adj, (igraph_integer_t) i, mode);
+            igraph_vector_append(vec, &adj);
+        }
+        igraph_vector_destroy(&adj);
+        IGRAPH_FINALLY_CLEAN(1);
+
+    } else {
+
+        igraph_vector_t adj;
+        igraph_bool_t *added;
+        long int j;
+        IGRAPH_VECTOR_INIT_FINALLY(&adj, 0);
+        added = igraph_Calloc(igraph_ecount(graph), igraph_bool_t);
+        if (added == 0) {
+            IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, added);
+        for (i = 0; i < no_of_nodes; i++) {
+            igraph_incident(graph, &adj, (igraph_integer_t) i, IGRAPH_ALL);
+            for (j = 0; j < igraph_vector_size(&adj); j++) {
+                if (!added[ (long int)VECTOR(adj)[j] ]) {
+                    igraph_vector_push_back(vec, VECTOR(adj)[j]);
+                    added[ (long int)VECTOR(adj)[j] ] += 1;
+                }
+            }
+        }
+        igraph_vector_destroy(&adj);
+        igraph_Free(added);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    eit->type = IGRAPH_EIT_VECTOR;
+    eit->pos = 0;
+    eit->start = 0;
+    eit->vec = vec;
+    eit->end = igraph_vector_size(eit->vec);
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+int igraph_i_eit_pairs(const igraph_t *graph,
+                       igraph_es_t es, igraph_eit_t *eit) {
+    long int n = igraph_vector_size(es.data.path.ptr);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+
+    if (n % 2 != 0) {
+        IGRAPH_ERROR("Cannot create edge iterator from odd number of vertices",
+                     IGRAPH_EINVAL);
+    }
+    if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID);
+    }
+
+    eit->type = IGRAPH_EIT_VECTOR;
+    eit->pos = 0;
+    eit->start = 0;
+    eit->end = n / 2;
+    eit->vec = igraph_Calloc(1, igraph_vector_t);
+    if (eit->vec == 0) {
+        IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec);
+    IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, n / 2);
+
+    for (i = 0; i < igraph_vector_size(eit->vec); i++) {
+        long int from = (long int) VECTOR(*es.data.path.ptr)[2 * i];
+        long int to = (long int) VECTOR(*es.data.path.ptr)[2 * i + 1];
+        igraph_integer_t eid;
+        IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from,
+                                    (igraph_integer_t) to, es.data.path.mode,
+                                    /*error=*/ 1));
+        VECTOR(*eit->vec)[i] = eid;
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+int igraph_i_eit_multipairs(const igraph_t *graph,
+                            igraph_es_t es, igraph_eit_t *eit) {
+    long int n = igraph_vector_size(es.data.path.ptr);
+    long int no_of_nodes = igraph_vcount(graph);
+
+    if (n % 2 != 0) {
+        IGRAPH_ERROR("Cannot create edge iterator from odd number of vertices",
+                     IGRAPH_EINVAL);
+    }
+    if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID);
+    }
+
+    eit->type = IGRAPH_EIT_VECTOR;
+    eit->pos = 0;
+    eit->start = 0;
+    eit->end = n / 2;
+    eit->vec = igraph_Calloc(1, igraph_vector_t);
+    if (eit->vec == 0) {
+        IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec);
+    IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, n / 2);
+
+    IGRAPH_CHECK(igraph_get_eids_multi(graph, (igraph_vector_t *) eit->vec,
+                                       /*pairs=*/ es.data.path.ptr, /*path=*/ 0,
+                                       es.data.path.mode, /*error=*/ 1));
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+int igraph_i_eit_path(const igraph_t *graph,
+                      igraph_es_t es, igraph_eit_t *eit) {
+    long int n = igraph_vector_size(es.data.path.ptr);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, len;
+
+    if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID);
+    }
+
+    if (n <= 1) {
+        len = 0;
+    } else {
+        len = n - 1;
+    }
+
+    eit->type = IGRAPH_EIT_VECTOR;
+    eit->pos = 0;
+    eit->start = 0;
+    eit->end = len;
+    eit->vec = igraph_Calloc(1, igraph_vector_t);
+    if (eit->vec == 0) {
+        IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (igraph_vector_t*)eit->vec);
+
+    IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t *)eit->vec, len);
+
+    for (i = 0; i < len; i++) {
+        long int from = (long int) VECTOR(*es.data.path.ptr)[i];
+        long int to = (long int) VECTOR(*es.data.path.ptr)[i + 1];
+        igraph_integer_t eid;
+        IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from,
+                                    (igraph_integer_t) to, es.data.path.mode,
+                                    /*error=*/ 1));
+        VECTOR(*eit->vec)[i] = eid;
+    }
+
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_eit_create
+ * \brief Creates an edge iterator from an edge selector.
+ *
+ * </para><para>
+ * This function creates an edge iterator based on an edge selector
+ * and a graph.
+ *
+ * </para><para>
+ * The same edge selector can be used to create many edge iterators,
+ * also for different graphs.
+ *
+ * \param graph An \type igraph_t object for which the edge selector
+ *        will be instantiated.
+ * \param es The edge selector to instantiate.
+ * \param eit Pointer to an uninitialized edge iterator.
+ * \return Error code.
+ * \sa \ref igraph_eit_destroy()
+ *
+ * Time complexity: depends on the type of the edge selector. For edge
+ * selectors created by \ref igraph_es_all(), \ref igraph_es_none(),
+ * \ref igraph_es_1(), igraph_es_vector(), igraph_es_seq() it is
+ * O(1). For \ref igraph_es_incident() it is O(d) where d is the number of
+ * incident edges of the vertex.
+ */
+
+int igraph_eit_create(const igraph_t *graph,
+                      igraph_es_t es, igraph_eit_t *eit) {
+    switch (es.type) {
+    case IGRAPH_ES_ALL:
+        eit->type = IGRAPH_EIT_SEQ;
+        eit->pos = 0;
+        eit->start = 0;
+        eit->end = igraph_ecount(graph);
+        break;
+    case IGRAPH_ES_ALLFROM:
+        IGRAPH_CHECK(igraph_i_eit_create_allfromto(graph, eit, IGRAPH_OUT));
+        break;
+    case IGRAPH_ES_ALLTO:
+        IGRAPH_CHECK(igraph_i_eit_create_allfromto(graph, eit, IGRAPH_IN));
+        break;
+    case IGRAPH_ES_INCIDENT:
+        eit->type = IGRAPH_EIT_VECTOR;
+        eit->pos = 0;
+        eit->start = 0;
+        eit->vec = igraph_Calloc(1, igraph_vector_t);
+        if (eit->vec == 0) {
+            IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, (igraph_vector_t*) eit->vec);
+        IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)eit->vec, 0);
+        IGRAPH_CHECK(igraph_incident(graph, (igraph_vector_t*)eit->vec,
+                                     es.data.incident.vid, es.data.incident.mode));
+        eit->end = igraph_vector_size(eit->vec);
+        IGRAPH_FINALLY_CLEAN(2);
+        break;
+    case IGRAPH_ES_NONE:
+        eit->type = IGRAPH_EIT_SEQ;
+        eit->pos = 0;
+        eit->start = 0;
+        eit->end = 0;
+        break;
+    case IGRAPH_ES_1:
+        eit->type = IGRAPH_EIT_SEQ;
+        eit->pos = es.data.eid;
+        eit->start = es.data.eid;
+        eit->end = es.data.eid + 1;
+        if (eit->pos >= igraph_ecount(graph)) {
+            IGRAPH_ERROR("Cannot create iterator, invalid edge id", IGRAPH_EINVVID);
+        }
+        break;
+    case IGRAPH_ES_VECTOR:
+    case IGRAPH_ES_VECTORPTR:
+        eit->type = IGRAPH_EIT_VECTORPTR;
+        eit->pos = 0;
+        eit->start = 0;
+        eit->vec = es.data.vecptr;
+        eit->end = igraph_vector_size(eit->vec);
+        if (!igraph_vector_isininterval(eit->vec, 0, igraph_ecount(graph) - 1)) {
+            IGRAPH_ERROR("Cannot create iterator, invalid edge id", IGRAPH_EINVVID);
+        }
+        break;
+    case IGRAPH_ES_SEQ:
+        eit->type = IGRAPH_EIT_SEQ;
+        eit->pos = es.data.seq.from;
+        eit->start = es.data.seq.from;
+        eit->end = es.data.seq.to;
+        break;
+    case IGRAPH_ES_PAIRS:
+        IGRAPH_CHECK(igraph_i_eit_pairs(graph, es, eit));
+        break;
+    case IGRAPH_ES_MULTIPAIRS:
+        IGRAPH_CHECK(igraph_i_eit_multipairs(graph, es, eit));
+        break;
+    case IGRAPH_ES_PATH:
+        IGRAPH_CHECK(igraph_i_eit_path(graph, es, eit));
+        break;
+    default:
+        IGRAPH_ERROR("Cannot create iterator, invalid selector", IGRAPH_EINVAL);
+        break;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_eit_destroy
+ * \brief Destroys an edge iterator.
+ *
+ * \param eit Pointer to an edge iterator to destroy.
+ * \sa \ref igraph_eit_create()
+ *
+ * Time complexity: operating system dependent, usually O(1).
+ */
+
+void igraph_eit_destroy(const igraph_eit_t *eit) {
+    switch (eit->type) {
+    case IGRAPH_EIT_SEQ:
+    case IGRAPH_EIT_VECTORPTR:
+        break;
+    case IGRAPH_EIT_VECTOR:
+        igraph_vector_destroy((igraph_vector_t*)eit->vec);
+        igraph_free((igraph_vector_t*)eit->vec);
+        break;
+    default:
+        /*     IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */
+        break;
+    }
+}
+
+int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v) {
+
+    long int i;
+
+    IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_EIT_SIZE(*eit)));
+
+    switch (eit->type) {
+    case IGRAPH_EIT_SEQ:
+        for (i = 0; i < IGRAPH_EIT_SIZE(*eit); i++) {
+            VECTOR(*v)[i] = eit->start + i;
+        }
+        break;
+    case IGRAPH_EIT_VECTOR:
+    case IGRAPH_EIT_VECTORPTR:
+        for (i = 0; i < IGRAPH_EIT_SIZE(*eit); i++) {
+            VECTOR(*v)[i] = VECTOR(*eit->vec)[i];
+        }
+        break;
+    default:
+        IGRAPH_ERROR("Cannot convert to vector, unknown iterator type",
+                     IGRAPH_EINVAL);
+        break;
+    }
+
+    return 0;
+}
diff --git a/igraph/src/ivout.c b/igraph/src/ivout.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/ivout.c
@@ -0,0 +1,278 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* -----------------------------------------------------------------------   
+    Routine:    IVOUT   
+
+    Purpose:    Integer vector output routine.   
+
+    Usage:      CALL IVOUT (LOUT, N, IX, IDIGIT, IFMT)   
+
+    Arguments   
+       N      - Length of array IX. (Input)   
+       IX     - Integer array to be printed. (Input)   
+       IFMT   - Format to be used in printing array IX. (Input)   
+       IDIGIT - Print up to ABS(IDIGIT) decimal digits / number. (Input)   
+                If IDIGIT .LT. 0, printing is done with 72 columns.   
+                If IDIGIT .GT. 0, printing is done with 132 columns.   
+
+   -----------------------------------------------------------------------   
+
+   Subroutine */ int igraphivout_(integer *lout, integer *n, integer *ix, integer *
+	idigit, char *ifmt, ftnlen ifmt_len)
+{
+    /* Format strings */
+    static char fmt_2000[] = "(/1x,a/1x,a)";
+    static char fmt_1000[] = "(1x,i4,\002 - \002,i4,\002:\002,20(1x,i5))";
+    static char fmt_1001[] = "(1x,i4,\002 - \002,i4,\002:\002,15(1x,i7))";
+    static char fmt_1002[] = "(1x,i4,\002 - \002,i4,\002:\002,10(1x,i11))";
+    static char fmt_1003[] = "(1x,i4,\002 - \002,i4,\002:\002,7(1x,i15))";
+    static char fmt_1004[] = "(1x,\002 \002)";
+
+    /* System generated locals */
+    integer i__1, i__2, i__3;
+
+    /* Builtin functions */
+    integer i_len(char *, ftnlen), s_wsfe(cilist *), do_fio(integer *, char *,
+	     ftnlen), e_wsfe(void);
+
+    /* Local variables */
+    integer i__, k1, k2, lll;
+    char line[80];
+    integer ndigit;
+
+    /* Fortran I/O blocks */
+    static cilist io___4 = { 0, 0, 0, fmt_2000, 0 };
+    static cilist io___8 = { 0, 0, 0, fmt_1000, 0 };
+    static cilist io___9 = { 0, 0, 0, fmt_1001, 0 };
+    static cilist io___10 = { 0, 0, 0, fmt_1002, 0 };
+    static cilist io___11 = { 0, 0, 0, fmt_1003, 0 };
+    static cilist io___12 = { 0, 0, 0, fmt_1000, 0 };
+    static cilist io___13 = { 0, 0, 0, fmt_1001, 0 };
+    static cilist io___14 = { 0, 0, 0, fmt_1002, 0 };
+    static cilist io___15 = { 0, 0, 0, fmt_1003, 0 };
+    static cilist io___16 = { 0, 0, 0, fmt_1004, 0 };
+
+
+/*     ...   
+       ... SPECIFICATIONS FOR ARGUMENTS   
+       ...   
+       ... SPECIFICATIONS FOR LOCAL VARIABLES   
+       ...   
+       ... SPECIFICATIONS INTRINSICS   
+
+
+       Parameter adjustments */
+    --ix;
+
+    /* Function Body   
+   Computing MIN */
+    i__1 = i_len(ifmt, ifmt_len);
+    lll = min(i__1,80);
+    i__1 = lll;
+    for (i__ = 1; i__ <= i__1; ++i__) {
+	*(unsigned char *)&line[i__ - 1] = '-';
+/* L1: */
+    }
+
+    for (i__ = lll + 1; i__ <= 80; ++i__) {
+	*(unsigned char *)&line[i__ - 1] = ' ';
+/* L2: */
+    }
+
+    io___4.ciunit = *lout;
+    s_wsfe(&io___4);
+    do_fio(&c__1, ifmt, ifmt_len);
+    do_fio(&c__1, line, lll);
+    e_wsfe();
+
+    if (*n <= 0) {
+	return 0;
+    }
+    ndigit = *idigit;
+    if (*idigit == 0) {
+	ndigit = 4;
+    }
+
+/* =======================================================================   
+               CODE FOR OUTPUT USING 72 COLUMNS FORMAT   
+   ======================================================================= */
+
+    if (*idigit < 0) {
+
+	ndigit = -(*idigit);
+	if (ndigit <= 4) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 10) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 9;
+		k2 = min(i__2,i__3);
+		io___8.ciunit = *lout;
+		s_wsfe(&io___8);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&ix[i__], (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+/* L10: */
+	    }
+
+	} else if (ndigit <= 6) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 7) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 6;
+		k2 = min(i__2,i__3);
+		io___9.ciunit = *lout;
+		s_wsfe(&io___9);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&ix[i__], (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+/* L30: */
+	    }
+
+	} else if (ndigit <= 10) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 5) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 4;
+		k2 = min(i__2,i__3);
+		io___10.ciunit = *lout;
+		s_wsfe(&io___10);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&ix[i__], (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+/* L50: */
+	    }
+
+	} else {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 3) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 2;
+		k2 = min(i__2,i__3);
+		io___11.ciunit = *lout;
+		s_wsfe(&io___11);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&ix[i__], (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+/* L70: */
+	    }
+	}
+
+/* =======================================================================   
+               CODE FOR OUTPUT USING 132 COLUMNS FORMAT   
+   ======================================================================= */
+
+    } else {
+
+	if (ndigit <= 4) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 20) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 19;
+		k2 = min(i__2,i__3);
+		io___12.ciunit = *lout;
+		s_wsfe(&io___12);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&ix[i__], (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+/* L90: */
+	    }
+
+	} else if (ndigit <= 6) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 15) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 14;
+		k2 = min(i__2,i__3);
+		io___13.ciunit = *lout;
+		s_wsfe(&io___13);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&ix[i__], (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+/* L110: */
+	    }
+
+	} else if (ndigit <= 10) {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 10) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 9;
+		k2 = min(i__2,i__3);
+		io___14.ciunit = *lout;
+		s_wsfe(&io___14);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&ix[i__], (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+/* L130: */
+	    }
+
+	} else {
+	    i__1 = *n;
+	    for (k1 = 1; k1 <= i__1; k1 += 7) {
+/* Computing MIN */
+		i__2 = *n, i__3 = k1 + 6;
+		k2 = min(i__2,i__3);
+		io___15.ciunit = *lout;
+		s_wsfe(&io___15);
+		do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer));
+		do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer));
+		i__2 = k2;
+		for (i__ = k1; i__ <= i__2; ++i__) {
+		    do_fio(&c__1, (char *)&ix[i__], (ftnlen)sizeof(integer));
+		}
+		e_wsfe();
+/* L150: */
+	    }
+	}
+    }
+    io___16.ciunit = *lout;
+    s_wsfe(&io___16);
+    e_wsfe();
+
+
+    return 0;
+} /* igraphivout_ */
+
diff --git a/igraph/src/kolmogorov.c b/igraph/src/kolmogorov.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/kolmogorov.c
@@ -0,0 +1,66 @@
+/* kolmogorov.c
+ *
+ * Copyright (C) 2010-2011 Tamas Nepusz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <math.h>
+#include "kolmogorov.h"
+
+double plfit_kolmogorov(double z) {
+    const double fj[4] = { -2, -8, -18, -32 };
+    const double w = 2.50662827;
+    const double c1 = -1.2337005501361697;   /* -pi^2 / 8 */
+    const double c2 = -11.103304951225528;   /*  9*c1 */
+    const double c3 = -30.842513753404244;   /* 25*c1 */
+
+    double u = fabs(z);
+    double v;
+
+    if (u < 0.2)
+        return 1;
+
+    if (u < 0.755) {
+        v = 1.0 / (u*u);
+        return 1 - w * (exp(c1*v) + exp(c2*v) + exp(c3*v)) / u;
+    }
+
+    if (u < 6.8116) {
+        double r[4] = { 0, 0, 0, 0 };
+        long int maxj = (long int)(3.0 / u + 0.5);
+        long int j;
+
+        if (maxj < 1)
+            maxj = 1;
+
+        v = u*u;
+        for (j = 0; j < maxj; j++) {
+            r[j] = exp(fj[j] * v);
+        }
+
+        return 2*(r[0] - r[1] + r[2] - r[3]);
+    }
+
+    return 0;
+}
+
+double plfit_ks_test_one_sample_p(double d, size_t n) {
+    return plfit_kolmogorov(d * sqrt(n));
+}
+
+double plfit_ks_test_two_sample_p(double d, size_t n1, size_t n2) {
+    return plfit_kolmogorov(d * sqrt(n1*n2 / ((double)(n1+n2))));
+}
diff --git a/igraph/src/l_ge.c b/igraph/src/l_ge.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/l_ge.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+logical l_ge(a,b,la,lb) char *a, *b; ftnlen la, lb;
+#else
+extern integer s_cmp(char *, char *, ftnlen, ftnlen);
+logical l_ge(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+return(s_cmp(a,b,la,lb) >= 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/l_gt.c b/igraph/src/l_gt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/l_gt.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+logical l_gt(a,b,la,lb) char *a, *b; ftnlen la, lb;
+#else
+extern integer s_cmp(char *, char *, ftnlen, ftnlen);
+logical l_gt(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+return(s_cmp(a,b,la,lb) > 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/l_le.c b/igraph/src/l_le.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/l_le.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+logical l_le(a,b,la,lb) char *a, *b; ftnlen la, lb;
+#else
+extern integer s_cmp(char *, char *, ftnlen, ftnlen);
+logical l_le(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+return(s_cmp(a,b,la,lb) <= 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/l_lt.c b/igraph/src/l_lt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/l_lt.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern integer s_cmp();
+logical l_lt(a,b,la,lb) char *a, *b; ftnlen la, lb;
+#else
+extern integer s_cmp(char *, char *, ftnlen, ftnlen);
+logical l_lt(char *a, char *b, ftnlen la, ftnlen lb)
+#endif
+{
+return(s_cmp(a,b,la,lb) < 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/lad.c b/igraph/src/lad.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lad.c
@@ -0,0 +1,1665 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/*
+  The contents of this file was originally taken from the LAD
+  homepage: http://liris.cnrs.fr/csolnon/LAD.html and then
+  modified to fit better into igraph.
+
+  Unfortunately LAD seems to have no version numbers. The files
+  were apparently last changed on the 29th of June, 2010.
+
+  The original copyright message follows here. The CeCILL-B V1 license
+  is GPL compatible, because instead of V1, one can freely choose to
+  use V2, and V2 is explicitly GPL compatible.
+*/
+
+/* This software has been written by Christine Solnon.
+   It is distributed under the CeCILL-B FREE SOFTWARE LICENSE
+   see http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+   for more details
+*/
+
+/* Several modifications had to be made to the original LAD implementation
+   to make it compile with non-C99-compliant compilers such as MSVC. In
+   particular, I had to remove all the variable-sized arrays.
+   -- Tamas Nepusz, 11 July 2013
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_vector.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_memory.h"
+#include "igraph_matrix.h"
+#include "igraph_interrupt_internal.h"
+
+/* define boolean type as char */
+#define true 1
+#define false 0
+#define bool char
+
+/* helper to allocate an array of given size and free it using IGRAPH_FINALLY
+ * when needed */
+#define ALLOC_ARRAY(VAR, SIZE, TYPE) { \
+        VAR = igraph_Calloc(SIZE, TYPE);   \
+        if (VAR == 0) {                    \
+            IGRAPH_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); \
+        }  \
+        IGRAPH_FINALLY(igraph_free, VAR);  \
+    }
+
+/* helper to allocate an array of given size and store its address in a
+ * pointer array */
+#define ALLOC_ARRAY_IN_HISTORY(VAR, SIZE, TYPE, HISTORY) { \
+        VAR = igraph_Calloc(SIZE, TYPE);   \
+        if (VAR == 0) {                    \
+            IGRAPH_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); \
+        }  \
+        IGRAPH_FINALLY(igraph_free, VAR);  \
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(HISTORY, VAR));  \
+        IGRAPH_FINALLY_CLEAN(1);           \
+    }
+
+/* ---------------------------------------------------------*/
+/* Coming from graph.c                                      */
+/* ---------------------------------------------------------*/
+
+typedef struct {
+    long int nbVertices; /* Number of vertices */
+    igraph_vector_t nbSucc;
+    igraph_adjlist_t succ;
+    igraph_matrix_char_t isEdge;
+} Tgraph;
+
+int igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) {
+    long int i, j, n;
+    long int no_of_nodes = igraph_vcount(igraph);
+    igraph_vector_int_t *neis;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&graph->nbSucc, no_of_nodes);
+    IGRAPH_CHECK(igraph_degree(igraph, &graph->nbSucc, igraph_vss_all(),
+                               IGRAPH_OUT, IGRAPH_LOOPS));
+
+    graph->nbVertices = no_of_nodes;
+
+    IGRAPH_CHECK(igraph_adjlist_init(igraph, &graph->succ, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &graph->succ);
+    IGRAPH_CHECK(igraph_matrix_char_init(&graph->isEdge,
+                                         no_of_nodes, no_of_nodes));
+    IGRAPH_FINALLY(igraph_matrix_char_destroy, &graph->isEdge);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        neis = igraph_adjlist_get(&graph->succ, i);
+        n = igraph_vector_int_size(neis);
+        for (j = 0; j < n; j++) {
+            int v = (int)VECTOR(*neis)[j];
+            if (MATRIX(graph->isEdge, i, v)) {
+                IGRAPH_ERROR("LAD functions only work on simple graphs, "
+                             "simplify your graph", IGRAPH_EINVAL);
+            }
+            MATRIX(graph->isEdge, i, v) = 1;
+        }
+    }
+
+    return 0;
+}
+
+/* ---------------------------------------------------------*/
+/* Coming from domains.c                                    */
+/* ---------------------------------------------------------*/
+
+typedef struct {
+    igraph_vector_int_t nbVal;    /* nbVal[u] = number of values in D[u] */
+    igraph_vector_int_t firstVal; /* firstVal[u] = pos in val of the
+                                   first value of D[u] */
+    igraph_vector_int_t val;      /* val[firstVal[u]..firstVal[u]+nbVal[u]-1] =
+                                   values of D[u] */
+    igraph_matrix_int_t posInVal;
+    /* If v in D[u] then firstVal[u] <= posInVal[u][v] < firstVal[u]+nbVal[u]
+       and val[posInVal[u][v]] = v
+       otherwise posInVal[u][v] >= firstVal[u]+nbVal[u] */
+    int valSize;    /* size of val */
+    igraph_matrix_int_t firstMatch;
+    /* firstMatch[u][v] = pos in match of the first vertex
+       of the covering matching of G_(u, v) */
+    igraph_vector_int_t matching;
+    /* matching[firstMatch[u][v]..firstMatch[u][v]+nbSucc[u]-1]
+       = covering matching of G_(u, v) */
+    int nextOutToFilter; /* position in toFilter of the next pattern node whose
+                          domain should be filtered (-1 if no domain to
+                          filter) */
+    int lastInToFilter; /* position in toFilter of the last pattern node whose
+                         domain should be filtered */
+    igraph_vector_int_t toFilter;  /* contain all pattern nodes whose
+                                    domain should be filtered */
+    igraph_vector_char_t markedToFilter;  /* markedToFilter[u]=true if u
+                                           is in toFilter; false otherwise */
+    igraph_vector_int_t globalMatchingP; /* globalMatchingP[u] = node of Gt
+                                          matched to u in globalAllDiff(Np) */
+    igraph_vector_int_t globalMatchingT;
+    /* globalMatchingT[v] = node of Gp matched to v in globalAllDiff(Np)
+       or -1 if v is not matched */
+} Tdomain;
+
+bool igraph_i_lad_toFilterEmpty(Tdomain* D) {
+    /* return true if there is no more nodes in toFilter */
+    return (D->nextOutToFilter < 0);
+}
+
+void igraph_i_lad_resetToFilter(Tdomain *D) {
+    /* empty to filter and unmark the vertices that are marked to be filtered */
+    igraph_vector_char_null(&D->markedToFilter);
+    D->nextOutToFilter = -1;
+}
+
+
+int igraph_i_lad_nextToFilter(Tdomain* D, int size) {
+    /* precondition: emptyToFilter = false
+       remove a node from toFilter (FIFO)
+       unmark this node and return it */
+    int u = VECTOR(D->toFilter)[D->nextOutToFilter];
+    VECTOR(D->markedToFilter)[u] = false;
+    if (D->nextOutToFilter == D->lastInToFilter) {
+        /* u was the last node in tofilter */
+        D->nextOutToFilter = -1;
+    } else if (D->nextOutToFilter == size - 1) {
+        D->nextOutToFilter = 0;
+    } else {
+        D->nextOutToFilter++;
+    }
+    return u;
+}
+
+void igraph_i_lad_addToFilter(int u, Tdomain* D, int size) {
+    /* if u is not marked, then add it to toFilter and mark it */
+    if (VECTOR(D->markedToFilter)[u]) {
+        return;
+    }
+    VECTOR(D->markedToFilter)[u] = true;
+    if (D->nextOutToFilter < 0) {
+        D->lastInToFilter = 0;
+        D->nextOutToFilter = 0;
+    } else if (D->lastInToFilter == size - 1) {
+        D->lastInToFilter = 0;
+    } else {
+        D->lastInToFilter++;
+    }
+    VECTOR(D->toFilter)[D->lastInToFilter] = u;
+}
+
+bool igraph_i_lad_isInD(int u, int v, Tdomain* D) {
+    /* returns true if v belongs to D(u); false otherwise */
+    return (MATRIX(D->posInVal, u, v) <
+            VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]);
+}
+
+int igraph_i_lad_augmentingPath(int u, Tdomain* D, int nbV, bool* result) {
+    /* return true if there exists an augmenting path starting from u and
+       ending on a free vertex v in the bipartite directed graph G=(U,
+       V, E) such that U=pattern nodes, V=target nodes, and
+       E={(u, v), v in D(u)} U {(v, u), D->globalMatchingP[u]=v}
+       update D-globalMatchingP and D->globalMatchingT consequently */
+    int *fifo, *pred;
+    bool *marked;
+    int nextIn = 0;
+    int nextOut = 0;
+    int i, v, v2, u2;
+
+    *result = false;
+
+    /* Allocate memory */
+    ALLOC_ARRAY(fifo, nbV, int);
+    ALLOC_ARRAY(pred, nbV, int);
+    ALLOC_ARRAY(marked, nbV, bool);
+
+    for (i = 0; i < VECTOR(D->nbVal)[u]; i++) {
+        v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */
+        if (VECTOR(D->globalMatchingT)[v] < 0) {
+            /* v is free => augmenting path found */
+            VECTOR(D->globalMatchingP)[u] = v;
+            VECTOR(D->globalMatchingT)[v] = u;
+            *result = true;
+            goto cleanup;
+        }
+        /* v is not free => add it to fifo */
+        pred[v] = u;
+        fifo[nextIn++] = v;
+        marked[v] = true;
+    }
+    while (nextOut < nextIn) {
+        u2 = VECTOR(D->globalMatchingT)[fifo[nextOut++]];
+        for (i = 0; i < VECTOR(D->nbVal)[u2]; i++) {
+            v = VECTOR(D->val)[ VECTOR(D->firstVal)[u2] + i ]; /* v in D(u2) */
+            if (VECTOR(D->globalMatchingT)[v] < 0) {
+                /* v is free => augmenting path found */
+                while (u2 != u) { /* update global matching wrt path */
+                    v2 = VECTOR(D->globalMatchingP)[u2];
+                    VECTOR(D->globalMatchingP)[u2] = v;
+                    VECTOR(D->globalMatchingT)[v] = u2;
+                    v = v2;
+                    u2 = pred[v];
+                }
+                VECTOR(D->globalMatchingP)[u] = v;
+                VECTOR(D->globalMatchingT)[v] = u;
+                *result = true;
+                goto cleanup;
+            }
+            if (!marked[v]) { /* v is not free and not marked => add it to fifo */
+                pred[v] = u2;
+                fifo[nextIn++] = v;
+                marked[v] = true;
+            }
+        }
+    }
+
+cleanup:
+    igraph_free(fifo);
+    igraph_free(pred);
+    igraph_free(marked);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+int igraph_i_lad_removeAllValuesButOne(int u, int v, Tdomain* D, Tgraph* Gp,
+                                       Tgraph* Gt, bool* result) {
+    /* remove all values but v from D(u) and add all successors of u in
+       toFilter return false if an inconsistency is detected wrt to
+       global all diff */
+    int j, oldPos, newPos;
+    igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u);
+    int n = (int) igraph_vector_int_size(uneis);
+    /* add all successors of u in toFilter */
+    for (j = 0; j < n; j++) {
+        igraph_i_lad_addToFilter((int) VECTOR(*uneis)[j], D,
+                                 (int) (Gp->nbVertices));
+    }
+    /* remove all values but v from D[u] */
+    oldPos = MATRIX(D->posInVal, u, v);
+    newPos = VECTOR(D->firstVal)[u];
+    VECTOR(D->val)[oldPos] = VECTOR(D->val)[newPos];
+    VECTOR(D->val)[newPos] = v;
+    MATRIX(D->posInVal, u, VECTOR(D->val)[newPos]) = newPos;
+    MATRIX(D->posInVal, u, VECTOR(D->val)[oldPos]) = oldPos;
+    VECTOR(D->nbVal)[u] = 1;
+    /* update global matchings that support the global all different
+       constraint */
+    if (VECTOR(D->globalMatchingP)[u] != v) {
+        VECTOR(D->globalMatchingT)[ VECTOR(D->globalMatchingP)[u] ] = -1;
+        VECTOR(D->globalMatchingP)[u] = -1;
+        IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, (int) (Gt->nbVertices), result));
+    } else {
+        *result = true;
+    }
+    return 0;
+}
+
+
+int igraph_i_lad_removeValue(int u, int v, Tdomain* D, Tgraph* Gp,
+                             Tgraph* Gt, bool* result) {
+    /* remove v from D(u) and add all successors of u in toFilter
+       return false if an inconsistency is detected wrt global all diff */
+    int j;
+    igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u);
+    int n = (int) igraph_vector_int_size(uneis);
+    int oldPos, newPos;
+
+    /* add all successors of u in toFilter */
+    for (j = 0; j < n; j++) {
+        igraph_i_lad_addToFilter((int) VECTOR(*uneis)[j], D,
+                                 (int) (Gp->nbVertices));
+    }
+    /* remove v from D[u] */
+    oldPos = MATRIX(D->posInVal, u, v);
+    VECTOR(D->nbVal)[u]--;
+    newPos = VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u];
+    VECTOR(D->val)[oldPos] = VECTOR(D->val)[newPos];
+    VECTOR(D->val)[newPos] = v;
+    MATRIX(D->posInVal, u, VECTOR(D->val)[oldPos]) = oldPos;
+    MATRIX(D->posInVal, u, VECTOR(D->val)[newPos]) = newPos;
+    /* update global matchings that support the global all different
+       constraint */
+    if (VECTOR(D->globalMatchingP)[u] == v) {
+        VECTOR(D->globalMatchingP)[u] = -1;
+        VECTOR(D->globalMatchingT)[v] = -1;
+        IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, (int) (Gt->nbVertices), result));
+    } else {
+        *result = true;
+    }
+    return 0;
+}
+
+
+int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched,
+                               bool induced, Tdomain* D, Tgraph* Gp,
+                               Tgraph* Gt, int *invalid) {
+    /* for each u in toBeMatched[0..nb-1], match u to
+       D->val[D->firstVal[u] and filter domains of other non matched
+       vertices wrt FC(Edges) and FC(diff) (this is not mandatory, as
+       LAD is stronger than FC(Edges) and GAC(allDiff) is stronger than
+       FC(diff), but this speeds up the solution process).
+       return false if an inconsistency is detected by FC(Edges) or
+       FC(diff); true otherwise; */
+    int j, u, v, u2, oldNbVal;
+    igraph_vector_int_t *vneis;
+    bool result = false;
+
+    while (nb > 0) {
+        u = VECTOR(*toBeMatched)[--nb];
+        v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ];
+        vneis = igraph_adjlist_get(&Gt->succ, v);
+        /* match u to v */
+        for (u2 = 0; u2 < Gp->nbVertices; u2++) {
+            if (u != u2) {
+                oldNbVal = VECTOR(D->nbVal)[u2];
+                if (igraph_i_lad_isInD(u2, v, D)) {
+                    IGRAPH_CHECK(igraph_i_lad_removeValue(u2, v, D, Gp, Gt, &result));
+                    if (!result) {
+                        *invalid = 1 ; return 0;
+                    }
+                }
+                if (MATRIX(Gp->isEdge, u, u2)) {
+                    /* remove from D[u2] vertices which are not adjacent to v */
+                    j = VECTOR(D->firstVal)[u2];
+                    while (j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]) {
+                        if (MATRIX(Gt->isEdge, v, VECTOR(D->val)[j])) {
+                            j++;
+                        } else {
+                            IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result));
+                            if (!result) {
+                                *invalid = 1; return 0;
+                            }
+                        }
+                    }
+                } else if (induced) {
+                    /* (u, u2) is not an edge => remove neighbors of v from D[u2] */
+                    if (VECTOR(D->nbVal)[u2] < VECTOR(Gt->nbSucc)[v]) {
+                        j = VECTOR(D->firstVal)[u2];
+                        while (j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]) {
+                            if (!MATRIX(Gt->isEdge, v, VECTOR(D->val)[j])) {
+                                j++;
+                            } else {
+                                IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result));
+                                if (!result) {
+                                    *invalid = 1; return 0;
+                                }
+                            }
+                        }
+                    } else {
+                        for (j = 0; j < VECTOR(Gt->nbSucc)[v]; j++) {
+                            if (igraph_i_lad_isInD(u2, (int) VECTOR(*vneis)[j], D)) {
+                                IGRAPH_CHECK(igraph_i_lad_removeValue(u2, (int) VECTOR(*vneis)[j], D, Gp, Gt, &result));
+                                if (!result) {
+                                    *invalid = 1; return 0;
+                                }
+                            }
+                        }
+                    }
+                }
+                if (VECTOR(D->nbVal)[u2] == 0) {
+                    *invalid = 1; /* D[u2] is empty */
+                    return 0;
+                }
+                if ((VECTOR(D->nbVal)[u2] == 1) && (oldNbVal > 1)) {
+                    VECTOR(*toBeMatched)[nb++] = u2;
+                }
+            }
+        }
+    }
+    *invalid = 0;
+    return 0;
+}
+
+
+bool igraph_i_lad_matchVertex(int u, bool induced, Tdomain* D, Tgraph* Gp,
+                              Tgraph *Gt) {
+    int invalid;
+    /* match u to D->val[D->firstVal[u]] and filter domains of other non
+       matched vertices wrt FC(Edges) and FC(diff) (this is not
+       mandatory, as LAD is stronger than FC(Edges) and GAC(allDiff)
+       is stronger than FC(diff), but this speeds up the solution process).
+       return false if an inconsistency is detected by FC(Edges) or
+       FC(diff); true otherwise; */
+    igraph_vector_int_t toBeMatched;
+    igraph_vector_int_init(&toBeMatched, Gp->nbVertices);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &toBeMatched);
+    VECTOR(toBeMatched)[0] = u;
+    igraph_i_lad_matchVertices(1, &toBeMatched, induced, D, Gp, Gt,
+                               &invalid);
+    igraph_vector_int_destroy(&toBeMatched);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return invalid ? false : true;
+}
+
+
+int igraph_i_lad_qcompare (void const *a, void const *b) {
+    /* function used by the qsort function */
+    int pa = *((int*)a) - *((int*)b);
+    return pa;
+}
+
+bool igraph_i_lad_compare(int size_mu, int* mu, int size_mv, int* mv) {
+    /* return true if for every element u of mu there exists
+       a different element v of mv such that u <= v;
+       return false otherwise */
+    int i, j;
+    qsort(mu, (size_t) size_mu, sizeof(int), igraph_i_lad_qcompare);
+    qsort(mv, (size_t) size_mv, sizeof(int), igraph_i_lad_qcompare);
+    i = size_mv - 1;
+    for (j = size_mu - 1; j >= 0; j--) {
+        if (mu[j] > mv[i]) {
+            return false;
+        }
+        i--;
+    }
+    return true;
+}
+
+int igraph_i_lad_initDomains(bool initialDomains,
+                             igraph_vector_ptr_t *domains, Tdomain* D,
+                             Tgraph* Gp, Tgraph* Gt, int *empty) {
+    /* for every pattern node u, initialize D(u) with every vertex v
+       such that for every neighbor u' of u there exists a different
+       neighbor v' of v such that degree(u) <= degree(v)
+       if initialDomains, then filter initial domains wrt
+       compatibilities given in file
+       return false if a domain is empty and true otherwise */
+    int *val;
+    bool *dom;
+    int *mu, *mv;
+    int matchingSize, u, v, i, j;
+    igraph_vector_t *vec;
+    igraph_vector_t *Gp_uneis;
+    igraph_vector_t *Gt_vneis;
+
+    val = igraph_Calloc(Gp->nbVertices * Gt->nbVertices, int);
+    if (val == 0) {
+        IGRAPH_ERROR("cannot allocated 'val' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM);
+    }
+
+    dom = igraph_Calloc(Gt->nbVertices, bool);
+    if (dom == 0) {
+        igraph_free(val);
+        IGRAPH_ERROR("cannot allocated 'dom' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM);
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_init(&D->globalMatchingP, Gp->nbVertices));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &D->globalMatchingP);
+    igraph_vector_int_fill(&D->globalMatchingP, -1L);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&D->globalMatchingT, Gt->nbVertices));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &D->globalMatchingT);
+    igraph_vector_int_fill(&D->globalMatchingT, -1L);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&D->nbVal, Gp->nbVertices));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &D->nbVal);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&D->firstVal, Gp->nbVertices));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &D->firstVal);
+
+    IGRAPH_CHECK(igraph_matrix_int_init(&D->posInVal,
+                                        Gp->nbVertices, Gt->nbVertices));
+    IGRAPH_FINALLY(igraph_matrix_int_destroy, &D->posInVal);
+
+    IGRAPH_CHECK(igraph_matrix_int_init(&D->firstMatch,
+                                        Gp->nbVertices, Gt->nbVertices));
+    IGRAPH_FINALLY(igraph_matrix_int_destroy, &D->firstMatch);
+
+    IGRAPH_CHECK(igraph_vector_char_init(&D->markedToFilter, Gp->nbVertices));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &D->markedToFilter);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&D->toFilter, Gp->nbVertices));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &D->toFilter);
+
+    D->valSize = 0;
+    matchingSize = 0;
+
+    for (u = 0; u < Gp->nbVertices; u++) {
+        igraph_vector_int_t *Gp_uneis = igraph_adjlist_get(&Gp->succ, u);
+        if (initialDomains) {
+            /* read the list of target vertices which are compatible with u */
+            vec = VECTOR(*domains)[u];
+            i = (int) igraph_vector_size(vec);
+            memset(dom, false, sizeof(bool) * (size_t)(Gt->nbVertices));
+            for (j = 0; j < i; j++) {
+                v = (int) VECTOR(*vec)[j];
+                dom[v] = true;
+            }
+        }
+        VECTOR(D->markedToFilter)[u] = true;
+        VECTOR(D->toFilter)[u] = u;
+        VECTOR(D->nbVal)[u] = 0;
+        VECTOR(D->firstVal)[u] = D->valSize;
+        for (v = 0; v < Gt->nbVertices; v++) {
+            igraph_vector_int_t *Gt_vneis = igraph_adjlist_get(&Gt->succ, v);
+            if ((initialDomains) && (!dom[v])) { /* v not in D(u) */
+                MATRIX(D->posInVal, u, v) = (int) (VECTOR(D->firstVal)[u] +
+                                                   Gt->nbVertices);
+            } else {
+                MATRIX(D->firstMatch, u, v) = matchingSize;
+                matchingSize += VECTOR(Gp->nbSucc)[u];
+                if (VECTOR(Gp->nbSucc)[u] <= VECTOR(Gt->nbSucc)[v]) {
+                    mu = igraph_Calloc((long int) VECTOR(Gp->nbSucc)[u], int);
+                    if (mu == 0) {
+                        igraph_free(val); igraph_free(dom);
+                        IGRAPH_ERROR("cannot allocate 'mu' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM);
+                    }
+                    mv = igraph_Calloc((long int) VECTOR(Gt->nbSucc)[v], int);
+                    if (mv == 0) {
+                        igraph_free(mu); igraph_free(val); igraph_free(dom);
+                        IGRAPH_ERROR("cannot allocate 'mv' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM);
+                    }
+                    for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) {
+                        mu[i] = (int) VECTOR(Gp->nbSucc)[(long int) VECTOR(*Gp_uneis)[i]];
+                    }
+                    for (i = 0; i < VECTOR(Gt->nbSucc)[v]; i++) {
+                        mv[i] = (int) VECTOR(Gt->nbSucc)[(long int) VECTOR(*Gt_vneis)[i]];
+                    }
+                    if (igraph_i_lad_compare((int) VECTOR(Gp->nbSucc)[u], mu,
+                                             (int) VECTOR(Gt->nbSucc)[v], mv) == 1) {
+                        val[D->valSize] = v;
+                        VECTOR(D->nbVal)[u]++;
+                        MATRIX(D->posInVal, u, v) = D->valSize++;
+                    } else {  /* v not in D(u) */
+                        MATRIX(D->posInVal, u, v) =
+                            (int)(VECTOR(D->firstVal)[u] + Gt->nbVertices);
+                    }
+                    igraph_free(mu); mu = 0;
+                    igraph_free(mv); mv = 0;
+                } else {  /* v not in D(u) */
+                    MATRIX(D->posInVal, u, v) =
+                        (int) (VECTOR(D->firstVal)[u] + Gt->nbVertices);
+                }
+            }
+        }
+        if (VECTOR(D->nbVal)[u] == 0) {
+            *empty = 1;  /* empty domain */
+            igraph_free(val);
+            igraph_free(dom);
+            return 0;
+        }
+    }
+    IGRAPH_CHECK(igraph_vector_int_init(&D->val, D->valSize));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &D->val);
+    for (i = 0; i < D->valSize; i++) {
+        VECTOR(D->val)[i] = val[i];
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_init(&D->matching, matchingSize));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &D->matching);
+    igraph_vector_int_fill(&D->matching, -1);
+
+    D->nextOutToFilter = 0;
+    D->lastInToFilter = (int) (Gp->nbVertices - 1);
+    *empty = 0;
+
+    igraph_free(val);
+    igraph_free(dom);
+    return 0;
+}
+
+/* ---------------------------------------------------------*/
+/* Coming from allDiff.c                                    */
+/* ---------------------------------------------------------*/
+
+#define white 0
+#define grey 1
+#define black 2
+#define toBeDeleted 3
+#define deleted 4
+
+void igraph_i_lad_addToDelete(int u, int* list, int* nb, int* marked) {
+    if (marked[u] < toBeDeleted) {
+        list[(*nb)++] = u;
+        marked[u] = toBeDeleted;
+    }
+}
+
+int igraph_i_lad_updateMatching(int sizeOfU, int sizeOfV,
+                                igraph_vector_int_t *degree,
+                                igraph_vector_int_t *firstAdj,
+                                igraph_vector_int_t *adj,
+                                igraph_vector_int_t * matchedWithU,
+                                int *invalid) {
+    /* input:
+       sizeOfU = number of vertices in U
+       sizeOfV = number of vertices in V
+       degree[u] = number of vertices of V which are adjacent to u
+       firstAdj[u] = pos in adj of the first vertex of V adjacent to u
+       adj[firstAdj[u]..firstAdj[u]+sizeOfU[u]-1] = vertices of V adjacent to u
+
+       input/output:
+       matchedWithU[u] = vertex of V matched with u
+
+       returns true if there exists a matching that covers U, i.e., if
+       for every u in 0..nbU-1, there exists a different v in 0..nb-1
+       such that v is adjacent to u; returns false otherwise */
+
+    int *matchedWithV; /* matchedWithV[matchedWithU[u]]=u */
+    int *nbPred; /* nbPred[i] = nb of predecessors of the ith
+                  vertex of V in the DAG */
+    int *pred; /* pred[i][j] = jth predecessor the ith
+                 vertex of V in the DAG */
+    int *nbSucc; /* nbSucc[i] = nb of successors of the ith
+                  vertex of U in the DAG */
+    int *succ; /* succ[i][j] = jth successor of the ith
+                 vertex of U in the DAG */
+    int *listV, *listU, *listDV, *listDU;
+    int nbV, nbU, nbDV, nbDU;
+    int i, j, k, stop, u, v, w;
+    int *markedV, *markedU;
+    /* markedX[i]=white if X[i] is not in the DAG
+       markedX[i]=grey if X[i] has been added to the DAG, but not its successors
+       markedX[i]=black if X[i] and its successors have been added to the DAG
+       markedX[i]=toBeDeleted if X[i] must be deleted from the DAG
+       markedX[i]=deleted if X[i] has been deleted from the DAG */
+    int nbUnmatched = 0; /* number of vertices of U that are not matched */
+    int *unmatched;      /* vertices of U that are not matched */
+    int *posInUnmatched; /* unmatched[posInUnmatched[u]]=u */
+    igraph_vector_int_t path;
+
+    if (sizeOfU > sizeOfV) {
+        *invalid = 1; /* trivial case of infeasibility */
+        return 0;
+    }
+
+    ALLOC_ARRAY(matchedWithV, sizeOfV, int);
+    ALLOC_ARRAY(nbPred, sizeOfV, int);
+    ALLOC_ARRAY(pred, sizeOfV * sizeOfU, int);
+    ALLOC_ARRAY(nbSucc, sizeOfU, int);
+    ALLOC_ARRAY(succ, sizeOfU * sizeOfV, int);
+    ALLOC_ARRAY(listV, sizeOfV, int);
+    ALLOC_ARRAY(listU, sizeOfU, int);
+    ALLOC_ARRAY(listDV, sizeOfV, int);
+    ALLOC_ARRAY(listDU, sizeOfU, int);
+    ALLOC_ARRAY(markedV, sizeOfV, int);
+    ALLOC_ARRAY(markedU, sizeOfU, int);
+    ALLOC_ARRAY(unmatched, sizeOfU, int);
+    ALLOC_ARRAY(posInUnmatched, sizeOfU, int);
+
+    IGRAPH_CHECK(igraph_vector_int_init(&path, 0));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &path);
+
+    /* initialize matchedWithV and unmatched */
+    memset(matchedWithV, -1, (size_t)sizeOfV * sizeof(int));
+    for (u = 0; u < sizeOfU; u++) {
+        if (VECTOR(*matchedWithU)[u] >= 0) {
+            matchedWithV[VECTOR(*matchedWithU)[u]] = u;
+        } else {
+            posInUnmatched[u] = nbUnmatched;
+            unmatched[nbUnmatched++] = u;
+        }
+    }
+    /* try to match unmatched vertices of U with free vertices of V */
+    j = 0;
+    while (j < nbUnmatched) {
+        u = unmatched[j];
+        for (i = VECTOR(*firstAdj)[u];
+             ((i < VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]) &&
+              (matchedWithV[VECTOR(*adj)[i]] >= 0)); i++) { }
+        if (i == VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]) {
+            j++; /* no free vertex for u */
+        } else {
+            v = VECTOR(*adj)[i]; /* v is free => match u with v */
+            VECTOR(*matchedWithU)[u] = v;
+            matchedWithV[v] = u;
+            unmatched[j] = unmatched[--nbUnmatched];
+            posInUnmatched[unmatched[j]] = j;
+        }
+    }
+
+    while (nbUnmatched > 0) {
+        /* Try to increase the number of matched vertices */
+        /* step 1 : build the DAG */
+        memset(markedU, white, (size_t) sizeOfU * sizeof(int));
+        memset(nbSucc, 0, (size_t) sizeOfU * sizeof(int));
+        memset(markedV, white, (size_t) sizeOfV * sizeof(int));
+        memset(nbPred, 0, (size_t) sizeOfV * sizeof(int));
+        /* first layer of the DAG from the free nodes of U */
+        nbV = 0;
+        for (j = 0; j < nbUnmatched; j++) {
+            u = unmatched[j]; /* u is a free node of U */
+            markedU[u] = black;
+            for (i = VECTOR(*firstAdj)[u];
+                 i < VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]; i++) {
+                v = VECTOR(*adj)[i]; /* add edge (u, v) to the DAG */
+                pred[v * sizeOfU + (nbPred[v]++)] = u;
+                succ[u * sizeOfV + (nbSucc[u]++)] = v;
+                if (markedV[v] == white) { /* first time v is added to the DAG*/
+                    markedV[v] = grey;
+                    listV[nbV++] = v;
+                }
+            }
+        }
+        stop = 0;
+        while ((stop == 0) && (nbV > 0)) {
+            /* build next layer from nodes of V to nodes of U */
+            nbU = 0;
+            for (i = 0; i < nbV; i++) {
+                v = listV[i];
+                markedV[v] = black;
+                u = matchedWithV[v];
+                if (markedU[u] == white) { /* edge (v, u) belongs to the DAG */
+                    markedU[u] = grey;
+                    listU[nbU++] = u;
+                }
+            }
+            /* build next layer from nodes of U to nodes of V */
+            nbV = 0;
+            for (j = 0; j < nbU; j++) {
+                u = listU[j];
+                markedU[u] = black;
+                for (i = VECTOR(*firstAdj)[u];
+                     i < VECTOR(*firstAdj)[u] + VECTOR(*degree)[u]; i++) {
+                    v = VECTOR(*adj)[i];
+                    if (markedV[v] != black) { /* add edge (u, v) to the DAG */
+                        pred[v * sizeOfU + (nbPred[v]++)] = u;
+                        succ[u * sizeOfV + (nbSucc[u]++)] = v;
+                        if (markedV[v] == white) { /* first time v is added to the DAG */
+                            markedV[v] = grey;
+                            listV[nbV++] = v;
+                        }
+                        if (matchedWithV[v] == -1) { /* we have found a free node ! */
+                            stop = 1;
+                        }
+                    }
+                }
+            }
+        }
+        if (nbV == 0) {
+            *invalid = 1;
+            /* I know it's ugly. */
+            goto cleanup;
+        }
+
+        /* step 2: look for augmenting paths */
+        for (k = 0; k < nbV; k++) {
+            v = listV[k];
+            if ((matchedWithV[v] == -1) && (nbPred[v] > 0)) {
+                /* v is the final node of an augmenting path */
+                IGRAPH_CHECK(igraph_vector_int_resize(&path, 1));
+                VECTOR(path)[0] = v;
+                nbDV = 0;
+                nbDU = 0;
+                igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV);
+                do {
+                    u = pred[v * sizeOfU + 0]; /* (u, v) belongs to the augmenting path */
+                    IGRAPH_CHECK(igraph_vector_int_push_back(&path, u));
+                    igraph_i_lad_addToDelete(u, listDU, &nbDU, markedU);
+                    if (VECTOR(*matchedWithU)[u] != -1) {
+                        /* u is not the initial node of the augmenting path */
+                        v = VECTOR(*matchedWithU)[u]; /* (v, u) belongs to the
+                                           augmenting path */
+                        IGRAPH_CHECK(igraph_vector_int_push_back(&path, v));
+                        igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV);
+                    }
+                } while (VECTOR(*matchedWithU)[u] != -1);
+
+                /* delete nodes of listDV and listDU */
+                while ((nbDV > 0) || (nbDU > 0)) {
+                    while (nbDV > 0) { /* delete v */
+                        v = listDV[--nbDV]; markedV[v] = deleted;
+                        u = matchedWithV[v];
+                        if (u != -1) {
+                            igraph_i_lad_addToDelete(u, listDU, &nbDU, markedU);
+                        }
+                        for (i = 0; i < nbPred[v]; i++) {
+                            u = pred[v * sizeOfU + i]; /* delete edge (u, v) */
+                            for (j = 0; ((j < nbSucc[u]) && (v != succ[u * sizeOfV + j])); j++) { }
+                            succ[u * sizeOfV + j] = succ[u * sizeOfV + (--nbSucc[u])];
+                            if (nbSucc[u] == 0) {
+                                igraph_i_lad_addToDelete(u, listDU, &nbDU, markedU);
+                            }
+                        }
+                    }
+                    while (nbDU > 0) { /* delete u */
+                        u = listDU[--nbDU]; markedU[u] = deleted;
+                        v = VECTOR(*matchedWithU)[u];
+                        if (v != -1) {
+                            igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV);
+                        }
+                        j = 0;
+                        for (i = 0; i < nbSucc[u]; i++) { /* delete edge (u, v) */
+                            v = succ[u * sizeOfV + i];
+                            for (j = 0; ((j < nbPred[v]) && (u != pred[v * sizeOfU + j])); j++) { }
+                            pred[v * sizeOfU + j] = pred[v * sizeOfU + (--nbPred[v])];
+                            if (nbPred[v] == 0) {
+                                igraph_i_lad_addToDelete(v, listDV, &nbDV, markedV);
+                            }
+                        }
+                    }
+                }
+                /* Remove the last node of the augmenting path from the set of
+                   unmatched vertices */
+                u = VECTOR(path)[igraph_vector_int_size(&path) - 1];
+                i = posInUnmatched[u];
+                unmatched[i] = unmatched[--nbUnmatched];
+                posInUnmatched[unmatched[i]] = i;
+                /* Update the matching wrt the augmenting path */
+                while (igraph_vector_int_size(&path) > 1) {
+                    u = igraph_vector_int_pop_back(&path);
+                    v = igraph_vector_int_pop_back(&path);
+                    w = matchedWithV[v]; /* match v with u instead of v with w */
+                    VECTOR(*matchedWithU)[u] = v;
+                    matchedWithV[v] = u;
+                }
+            }
+        }
+    }
+    *invalid = 0;
+
+cleanup:
+    /* Free the allocated arrays */
+    igraph_vector_int_destroy(&path);
+    igraph_free(posInUnmatched);
+    igraph_free(unmatched);
+    igraph_free(markedU);
+    igraph_free(markedV);
+    igraph_free(listDU);
+    igraph_free(listDV);
+    igraph_free(listU);
+    igraph_free(listV);
+    igraph_free(succ);
+    igraph_free(nbSucc);
+    igraph_free(pred);
+    igraph_free(nbPred);
+    igraph_free(matchedWithV);
+    IGRAPH_FINALLY_CLEAN(14);
+    return 0;
+}
+
+void igraph_i_lad_DFS(int nbU, int nbV, int u, bool* marked, int* nbSucc,
+                      int* succ, igraph_vector_int_t * matchedWithU,
+                      int* order, int* nb) {
+    /* perform a depth first search, starting from u, in the bipartite
+       graph Go=(U, V, E) such that
+       U = vertices of Gp
+       V = vertices of Gt
+       E = { (u, matchedWithU[u]) / u is a vertex of Gp } U
+           { (v, u) / v is a vertex of D[u] which is not matched to v}
+
+       Given a vertex v of Gt, nbSucc[v]=number of successors of v and
+       succ[v]=list of successors of v. order[nb^out+1..nb^in] contains
+       the vertices discovered by the DFS */
+    int i;
+    int v = VECTOR(*matchedWithU)[u]; /* the only one predecessor of v is u */
+    marked[u] = true;
+    if (v >= 0) {
+        for (i = 0; i < nbSucc[v]; i++) {
+            if (!marked[succ[v * nbU + i]]) {
+                igraph_i_lad_DFS(nbU, nbV, succ[v * nbU + i], marked, nbSucc, succ,
+                                 matchedWithU, order, nb);
+            }
+        }
+    }
+    /* we have finished with u => number it */
+    order[*nb] = u; (*nb)--;
+}
+
+int igraph_i_lad_SCC(int nbU, int nbV, int* numV, int* numU,
+                     int* nbSucc, int* succ,
+                     int* nbPred, int* pred,
+                     igraph_vector_int_t * matchedWithU,
+                     igraph_vector_int_t * matchedWithV) {
+    /* postrelation: numV[v]==numU[u] iff they belong to the same
+       strongly connected component in the bipartite graph Go=(U, V, E)
+       such that
+       U = vertices of Gp
+       V = vertices of Gt
+       E = { (u, matchedWithU[u]) / u is a vertex of Gp } U
+           { (v, u) / v is a vertex of D[u] which is not matched to v}
+
+       Given a vertex v of Gt, nbSucc[v]=number of sucessors of v and
+       succ[v]=list of successors of v */
+    int *order;
+    bool *marked;
+    int *fifo;
+    int u, v, i, j, k, nbSCC, nb;
+
+    /* Allocate memory */
+    ALLOC_ARRAY(order, nbU, int);
+    ALLOC_ARRAY(marked, nbU, bool);
+    ALLOC_ARRAY(fifo, nbV, int);
+
+    /* Order vertices of Gp wrt DFS */
+    nb = nbU - 1;
+    for (u = 0; u < nbU; u++) {
+        if (!marked[u]) {
+            igraph_i_lad_DFS(nbU, nbV, u, marked, nbSucc, succ, matchedWithU,
+                             order, &nb);
+        }
+    }
+
+    /* traversal starting from order[0], then order[1], ... */
+    nbSCC = 0;
+    memset(numU, -1, (size_t) nbU * sizeof(int));
+    memset(numV, -1, (size_t) nbV * sizeof(int));
+    for (i = 0; i < nbU; i++) {
+        u = order[i];
+        v = VECTOR(*matchedWithU)[u];
+        if (v == -1) {
+            continue;
+        }
+        if (numV[v] == -1) { /* v belongs to a new SCC */
+            nbSCC++;
+            k = 1; fifo[0] = v;
+            numV[v] = nbSCC;
+            while (k > 0) {
+                v = fifo[--k];
+                u = VECTOR(*matchedWithV)[v];
+                if (u != -1) {
+                    numU[u] = nbSCC;
+                    for (j = 0; j < nbPred[u]; j++) {
+                        v = pred[u * nbV + j];
+                        if (numV[v] == -1) {
+                            numV[v] = nbSCC;
+                            fifo[k++] = v;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* Free memory */
+    igraph_free(fifo);
+    igraph_free(marked);
+    igraph_free(order);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+
+int igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt,
+                                  Tdomain* D, int *invalid) {
+    /* precondition: D->globalMatchingP is an all different matching of
+       the pattern vertices
+       postcondition: filter domains wrt GAC(allDiff)
+       return false if an inconsistency is detected; true otherwise
+
+       Build the bipartite directed graph Go=(U, V, E) such that
+       E = { (u, v) / u is a vertex of Gp which is matched to v (i.e.,
+              v=D->globalMatchingP[u])} U
+           { (v, u) / v is a vertex of Gt which is in D(u) but is not
+             matched to u} */
+    int *nbPred;                 /* nbPred[u] = nb of predecessors of u in Go */
+    int *pred;                                /* pred[u][i] = ith
+                                               predecessor of u in Go */
+    int *nbSucc;                 /* nbSucc[v] = nb of successors of v in Go */
+    int *succ;                                /* succ[v][i] = ith
+                                               successor of v in Go */
+    int u, v, i, w, oldNbVal, nbToMatch;
+    int *numV, *numU;
+    igraph_vector_int_t toMatch;
+    bool *used;
+    int *list;
+    int nb = 0;
+    bool result;
+
+    /* Allocate memory */
+    ALLOC_ARRAY(nbPred, Gp->nbVertices, int);
+    ALLOC_ARRAY(pred, Gp->nbVertices * Gt->nbVertices, int);
+    ALLOC_ARRAY(nbSucc, Gt->nbVertices, int);
+    ALLOC_ARRAY(succ, Gt->nbVertices * Gp->nbVertices, int);
+    ALLOC_ARRAY(numV, Gt->nbVertices, int);
+    ALLOC_ARRAY(numU, Gp->nbVertices, int);
+    ALLOC_ARRAY(used, Gp->nbVertices * Gt->nbVertices, bool);
+    ALLOC_ARRAY(list, Gt->nbVertices, int);
+    IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp->nbVertices));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch);
+
+    for (u = 0; u < Gp->nbVertices; u++) {
+        for (i = 0; i < VECTOR(D->nbVal)[u]; i++) {
+            v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */
+            used[u * Gt->nbVertices + v] = false;
+            if (v != VECTOR(D->globalMatchingP)[u]) {
+                pred[u * Gt->nbVertices + (nbPred[u]++)] = v;
+                succ[v * Gp->nbVertices + (nbSucc[v]++)] = u;
+            }
+        }
+    }
+
+    /* mark as used all edges of paths starting from free vertices */
+    for (v = 0; v < Gt->nbVertices; v++) {
+        if (VECTOR(D->globalMatchingT)[v] < 0) { /* v is free */
+            list[nb++] = v;
+            numV[v] = true;
+        }
+    }
+    while (nb > 0) {
+        v = list[--nb];
+        for (i = 0; i < nbSucc[v]; i++) {
+            u = succ[v * Gp->nbVertices + i];
+            used[u * Gt->nbVertices + v] = true;
+            if (numU[u] == false) {
+                numU[u] = true;
+                w = VECTOR(D->globalMatchingP)[u];
+                used[u * Gt->nbVertices + w] = true;
+                if (numV[w] == false) {
+                    list[nb++] = w;
+                    numV[w] = true;
+                }
+            }
+        }
+    }
+
+    /* look for strongly connected components in Go */
+    IGRAPH_CHECK(
+        igraph_i_lad_SCC((int)(Gp->nbVertices), (int)(Gt->nbVertices), numV, numU,
+                         nbSucc, succ, nbPred, pred, &D->globalMatchingP, &D->globalMatchingT));
+
+    /* remove v from D[u] if (u, v) is not marked as used
+                          and u and v are not in the same SCC
+                          and D->globalMatchingP[u] != v */
+    nbToMatch = 0;
+    for (u = 0; u < Gp->nbVertices; u++) {
+        oldNbVal = VECTOR(D->nbVal)[u];
+        for (i = 0; i < VECTOR(D->nbVal)[u]; i++) {
+            v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */
+            if ((!used[u * Gt->nbVertices + v]) && (numV[v] != numU[u]) &&
+                (VECTOR(D->globalMatchingP)[u] != v)) {
+                IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result));
+                if (!result) {
+                    *invalid = 1;
+                    /* Yes, this is ugly. */
+                    goto cleanup;
+                }
+            }
+        }
+        if (VECTOR(D->nbVal)[u] == 0) {
+            *invalid = 1;
+            /* Yes, this is ugly. */
+            goto cleanup;
+        }
+        if ((oldNbVal > 1) && (VECTOR(D->nbVal)[u] == 1)) {
+            VECTOR(toMatch)[nbToMatch++] = u;
+        }
+    }
+    IGRAPH_CHECK(igraph_i_lad_matchVertices(nbToMatch, &toMatch, induced,
+                                            D, Gp, Gt, invalid));
+
+cleanup:
+    igraph_vector_int_destroy(&toMatch);
+    igraph_free(list);
+    igraph_free(used);
+    igraph_free(numU);
+    igraph_free(numV);
+    igraph_free(succ);
+    igraph_free(nbSucc);
+    igraph_free(pred);
+    igraph_free(nbPred);
+    IGRAPH_FINALLY_CLEAN(9);
+
+    return 0;
+}
+
+/* ---------------------------------------------------------*/
+/* Coming from lad.c                                        */
+/* ---------------------------------------------------------*/
+
+int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* Gt,
+                          bool *result) {
+    /* return true if G_(u, v) has a adj(u)-covering matching; false
+       otherwise */
+    int u2, v2, i, j;
+    int nbMatched = 0;
+    igraph_vector_int_t *Gp_uneis = igraph_adjlist_get(&Gp->succ, u);
+
+    int *num, *numInv;
+    igraph_vector_int_t nbComp;
+    igraph_vector_int_t firstComp;
+    igraph_vector_int_t comp;
+    int nbNum = 0;
+    int posInComp = 0;
+    igraph_vector_int_t matchedWithU;
+    int invalid;
+
+    /* special case when u has only 1 adjacent node => no need to call
+       Hopcroft and Karp */
+    if (VECTOR(Gp->nbSucc)[u] == 1) {
+        u2 = (int) VECTOR(*Gp_uneis)[0]; /* u2 is the only node adjacent to u */
+        v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ];
+        if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) {
+            *result = true;
+            return 0;
+        }
+        /* look for a support of edge (u, u2) for v */
+        for (i = VECTOR(D->firstVal)[u2];
+             i < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]; i++) {
+            if (MATRIX(Gt->isEdge, v, VECTOR(D->val)[i])) {
+                VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ] =
+                    VECTOR(D->val)[i];
+                *result = true;
+                return 0;
+            }
+        }
+        *result = false;
+        return 0;
+    }
+
+    /* general case (when u has more than 1 adjacent node) */
+    for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) {
+        /* remove from the matching of G_(u, v) edges which no longer
+           belong to G_(u, v) */
+        u2 = (int) VECTOR(*Gp_uneis)[i];
+        v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) + i];
+        if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) {
+            nbMatched++;
+        }
+    }
+    if (nbMatched == VECTOR(Gp->nbSucc)[u]) {
+        *result = true;
+        return 0;
+    } /* The matching still covers adj(u) */
+
+    /* Allocate memory */
+    ALLOC_ARRAY(num, Gt->nbVertices, int);
+    ALLOC_ARRAY(numInv, Gt->nbVertices, int);
+
+    /* Build the bipartite graph
+       let U be the set of nodes adjacent to u
+       let V be the set of nodes that are adjacent to v, and that belong
+       to domains of nodes of U */
+    /* nbComp[u]=number of elements of V that are compatible with u */
+    IGRAPH_CHECK(igraph_vector_int_init(&nbComp, (long int) VECTOR(Gp->nbSucc)[u]));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &nbComp);
+    IGRAPH_CHECK(igraph_vector_int_init(&firstComp, (long int) VECTOR(Gp->nbSucc)[u]));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &firstComp);
+    /* comp[firstComp[u]..firstComp[u]+nbComp[u]-1] = nodes of Gt that
+       are compatible with u */
+    IGRAPH_CHECK(igraph_vector_int_init(&comp, (long int) (VECTOR(Gp->nbSucc)[u] *
+                                        Gt->nbVertices)));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &comp);
+    IGRAPH_CHECK(igraph_vector_int_init(&matchedWithU, (long int) VECTOR(Gp->nbSucc)[u]));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &matchedWithU);
+    memset(num, -1, (size_t) (Gt->nbVertices) * sizeof(int));
+    for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) {
+        u2 = (int) VECTOR(*Gp_uneis)[i]; /* u2 is adjacent to u */
+        /* search for all nodes v2 in D[u2] which are adjacent to v */
+        VECTOR(nbComp)[i] = 0;
+        VECTOR(firstComp)[i] = posInComp;
+        if (VECTOR(D->nbVal)[u2] > VECTOR(Gt->nbSucc)[v]) {
+            for (j = VECTOR(D->firstVal)[u2];
+                 j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]; j++) {
+                v2 = VECTOR(D->val)[j]; /* v2 belongs to D[u2] */
+                if (MATRIX(Gt->isEdge, v, v2)) { /* v2 is a successor of v */
+                    if (num[v2] < 0) { /* v2 has not yet been added to V */
+                        num[v2] = nbNum;
+                        numInv[nbNum++] = v2;
+                    }
+                    VECTOR(comp)[posInComp++] = num[v2];
+                    VECTOR(nbComp)[i]++;
+                }
+            }
+        } else {
+            igraph_vector_int_t *Gt_vneis = igraph_adjlist_get(&Gt->succ, v);
+            for (j = 0; j < VECTOR(Gt->nbSucc)[v]; j++) {
+                v2 = (int) VECTOR(*Gt_vneis)[j]; /* v2 is a successor of v */
+                if (igraph_i_lad_isInD(u2, v2, D)) { /* v2 belongs to D[u2] */
+                    if (num[v2] < 0) { /* v2 has not yet been added to V */
+                        num[v2] = nbNum;
+                        numInv[nbNum++] = v2;
+                    }
+                    VECTOR(comp)[posInComp++] = num[v2];
+                    VECTOR(nbComp)[i]++;
+                }
+            }
+        }
+        if (VECTOR(nbComp)[i] == 0) {
+            *result = false; /* u2 has no compatible vertex in succ[v] */
+            goto cleanup;
+        }
+        /* u2 is matched to v2 in the matching that supports (u, v) */
+        v2 = VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) + i];
+        if ((v2 != -1) && (igraph_i_lad_isInD(u2, v2, D))) {
+            VECTOR(matchedWithU)[i] = num[v2];
+        } else {
+            VECTOR(matchedWithU)[i] = -1;
+        }
+    }
+    /* Call Hopcroft Karp to update the matching */
+    IGRAPH_CHECK(
+        igraph_i_lad_updateMatching((int) VECTOR(Gp->nbSucc)[u], nbNum, &nbComp,
+                                    &firstComp, &comp, &matchedWithU, &invalid)
+    );
+    if (invalid) {
+        *result = false;
+        goto cleanup;
+    }
+    for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) {
+        VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) + i] =
+            numInv[ VECTOR(matchedWithU)[i] ];
+    }
+    *result = true;
+
+cleanup:
+    igraph_free(numInv);
+    igraph_free(num);
+    igraph_vector_int_destroy(&matchedWithU);
+    igraph_vector_int_destroy(&comp);
+    igraph_vector_int_destroy(&firstComp);
+    igraph_vector_int_destroy(&nbComp);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return 0;
+}
+
+/* ---------------------------------------------------------*/
+/* Coming from main.c                                      */
+/* ---------------------------------------------------------*/
+
+int igraph_i_lad_filter(bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt,
+                        bool *result) {
+    /* filter domains of all vertices in D->toFilter wrt LAD and ensure
+       GAC(allDiff)
+       return false if some domain becomes empty; true otherwise */
+    int u, v, i, oldNbVal;
+    int invalid;
+    bool result2;
+    while (!igraph_i_lad_toFilterEmpty(D)) {
+        while (!igraph_i_lad_toFilterEmpty(D)) {
+            u = igraph_i_lad_nextToFilter(D, (int) (Gp->nbVertices));
+            oldNbVal = VECTOR(D->nbVal)[u];
+            i = VECTOR(D->firstVal)[u];
+            while (i < VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]) {
+                /* for every target node v in D(u), check if G_(u, v) has a
+                   covering matching */
+                v = VECTOR(D->val)[i];
+                IGRAPH_CHECK(igraph_i_lad_checkLAD(u, v, D, Gp, Gt, &result2));
+                if (result2) {
+                    i++;
+                } else {
+                    IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result2));
+                    if (!result2) {
+                        *result = false;
+                        return 0;
+                    }
+                }
+            }
+            if ((VECTOR(D->nbVal)[u] == 1) && (oldNbVal > 1) &&
+                (!igraph_i_lad_matchVertex(u, induced, D, Gp, Gt))) {
+                *result = false; return 0;
+            }
+            if (VECTOR(D->nbVal)[u] == 0) {
+                *result = false;
+                return 0;
+            }
+        }
+        igraph_i_lad_ensureGACallDiff(induced, Gp, Gt, D, &invalid);
+        if (invalid) {
+            *result = false;
+            return 0;
+        }
+    }
+    *result = true;
+    return 0;
+}
+
+
+
+int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced,
+                       Tdomain* D, Tgraph* Gp, Tgraph* Gt,
+                       int *invalid, igraph_bool_t *iso,
+                       igraph_vector_t *map, igraph_vector_ptr_t *maps,
+                       int *nbNodes, int *nbFail, int *nbSol,
+                       clock_t *begin, igraph_vector_ptr_t *alloc_history) {
+    /* if firstSol then search for the first solution; otherwise search
+       for all solutions if induced then search for induced subgraphs;
+       otherwise search for partial subgraphs
+       return false if CPU time limit exceeded before the search is
+       completed, return true otherwise */
+
+    int u, v, minDom, i;
+    int* nbVal;
+    int* globalMatching;
+    clock_t end = clock();
+    igraph_vector_t *vec;
+    int* val;
+    bool result;
+
+    (*nbNodes)++;
+
+    if ( (double)(end - *begin) / CLOCKS_PER_SEC >= timeLimit) {
+        /* CPU time limit exceeded */
+        IGRAPH_ERROR("LAD CPU time exceeded", IGRAPH_CPUTIME);
+    }
+
+    /* Allocate memory */
+    ALLOC_ARRAY_IN_HISTORY(nbVal, Gp->nbVertices, int, alloc_history);
+    ALLOC_ARRAY_IN_HISTORY(globalMatching, Gp->nbVertices, int, alloc_history);
+
+    IGRAPH_CHECK(igraph_i_lad_filter(induced, D, Gp, Gt, &result));
+    if (!result) {
+        /* filtering has detected an inconsistency */
+        (*nbFail)++;
+        igraph_i_lad_resetToFilter(D);
+        *invalid = 0;
+        goto cleanup;
+    }
+
+    /* The current node of the search tree is consistent wrt to LAD and
+       GAC(allDiff) Save domain sizes and global all different matching
+       and search for the non matched vertex minDom with smallest domain */
+    minDom = -1;
+    for (u = 0; u < Gp->nbVertices; u++) {
+        nbVal[u] = VECTOR(D->nbVal)[u];
+        if ((nbVal[u] > 1) && ((minDom < 0) || (nbVal[u] < nbVal[minDom]))) {
+            minDom = u;
+        }
+        globalMatching[u] = VECTOR(D->globalMatchingP)[u];
+    }
+
+    if (minDom == -1) {
+        /* All vertices are matched => Solution found */
+        if (iso) {
+            *iso = 1;
+        }
+        (*nbSol)++;
+        if (map && igraph_vector_size(map) == 0) {
+            IGRAPH_CHECK(igraph_vector_resize(map, Gp->nbVertices));
+            for (u = 0; u < Gp->nbVertices; u++) {
+                VECTOR(*map)[u] = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ];
+            }
+        }
+        if (maps) {
+            vec = igraph_Calloc(1, igraph_vector_t);
+            if (!vec) {
+                IGRAPH_ERROR("LAD failed", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, vec);
+            IGRAPH_CHECK(igraph_vector_init(vec, Gp->nbVertices));
+            IGRAPH_FINALLY(igraph_vector_destroy, vec);
+            for (u = 0; u < Gp->nbVertices; u++) {
+                VECTOR(*vec)[u] = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ];
+            }
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(maps, vec));
+            IGRAPH_FINALLY_CLEAN(2);
+        }
+        igraph_i_lad_resetToFilter(D);
+        *invalid = 0;
+        goto cleanup;
+    }
+
+    /* save the domain of minDom to iterate on its values */
+    ALLOC_ARRAY_IN_HISTORY(val, VECTOR(D->nbVal)[minDom], int, alloc_history);
+    for (i = 0; i < VECTOR(D->nbVal)[minDom]; i++) {
+        val[i] = VECTOR(D->val)[ VECTOR(D->firstVal)[minDom] + i ];
+    }
+
+    /* branch on minDom=v, for every target node v in D(u) */
+    for (i = 0; ((i < nbVal[minDom]) && ((firstSol == 0) || (*nbSol == 0))); i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        v = val[i];
+        IGRAPH_CHECK(igraph_i_lad_removeAllValuesButOne(minDom, v, D, Gp, Gt, &result));
+        if (!result || (!igraph_i_lad_matchVertex(minDom, induced, D, Gp, Gt))) {
+            (*nbFail)++;
+            (*nbNodes)++;
+            igraph_i_lad_resetToFilter(D);
+        } else {
+            IGRAPH_CHECK(igraph_i_lad_solve(timeLimit, firstSol, induced,
+                                            D, Gp, Gt, invalid, iso, map, maps,
+                                            nbNodes, nbFail, nbSol, begin,
+                                            alloc_history));
+        }
+        /* restore domain sizes and global all different matching */
+        igraph_vector_int_fill(&D->globalMatchingT, -1);
+        for (u = 0; u < Gp->nbVertices; u++) {
+            VECTOR(D->nbVal)[u] = nbVal[u];
+            VECTOR(D->globalMatchingP)[u] = globalMatching[u];
+            VECTOR(D->globalMatchingT)[globalMatching[u]] = u;
+        }
+    }
+    *invalid = 0;
+
+    igraph_free(val);
+    igraph_vector_ptr_pop_back(alloc_history);
+
+cleanup:
+    igraph_free(globalMatching);
+    igraph_vector_ptr_pop_back(alloc_history);
+    igraph_free(nbVal);
+    igraph_vector_ptr_pop_back(alloc_history);
+
+    return 0;
+}
+
+/**
+ * \section about_lad
+ *
+ * <para>
+ * The LAD algorithm can search for a subgraph in a larger graph, or check
+ * if two graphs are isomorphic.
+ * See Christine Solnon: AllDifferent-based Filtering for Subgraph
+ * Isomorphism. Artificial Intelligence, 174(12-13):850-864, 2010.
+ * https://doi.org/10.1016/j.artint.2010.05.002
+ * as well as the homepage of the LAD library at http://liris.cnrs.fr/csolnon/LAD.html
+ * The implementation in igraph is based on LADv1, but it is
+ * modified to use igraph's own memory allocation and error handling.
+ * </para>
+ *
+ * <para>
+ * LAD uses the concept of domains to indicate vertex compatibility when matching the
+ * pattern graph. Domains can be used to implement matching of colored vertices.
+ * </para>
+ *
+ * <para>
+ * LAD works with both directed and undirected graphs. Only simple graphs are supported.
+ * </para>
+ */
+
+/**
+ * \function igraph_subisomorphic_lad
+ * Check subgraph isomorphism with the LAD algorithm
+ *
+ * Check whether \p pattern is isomorphic to a subgraph os \p target.
+ * The original LAD implementation by Christine Solnon was used as the
+ * basis of this code.
+ *
+ * </para><para>
+ * See more about LAD at http://liris.cnrs.fr/csolnon/LAD.html and in
+ * Christine Solnon: AllDifferent-based Filtering for Subgraph
+ * Isomorphism. Artificial Intelligence, 174(12-13):850-864, 2010.
+ * https://doi.org/10.1016/j.artint.2010.05.002
+ *
+ * \param pattern The smaller graph, it can be directed or undirected.
+ * \param target The bigger graph, it can be directed or undirected.
+ * \param domains A pointer vector, or a null pointer. If a pointer
+ *    vector, then it must contain pointers to \c igraph_vector_t
+ *    objects and the length of the vector must match the number of
+ *    vertices in the \p pattern graph. For each vertex, the ids of
+ *    the compatible vertices in the target graph are listed.
+ * \param iso Pointer to a boolean, or a null pointer. If not a null
+ *    pointer, then the boolean is set to TRUE (1) if a subgraph
+ *    isomorphism is found, and to FALSE (0) otherwise.
+ * \param map Pointer to a vector or a null pointer. If not a null
+ *    pointer and a subgraph isomorphism is found, the matching
+ *    vertices from the target graph are listed here, for each vertex
+ *    (in vertex id order) from the pattern graph.
+ * \param maps Pointer vector or a null pointer. If not a null
+ *    pointer, then all subgraph isomorphisms are stored in the
+ *    pointer vector, in \c igraph_vector_t objects.
+ * \param induced Boolean, whether to search for induced matching
+ *    subgraphs.
+ * \param time_limit Processor time limit in seconds. Supply zero
+ *    here for no limit. If the time limit is over, then the function
+ *    signals an error.
+ * \return Error code
+ *
+ * \sa \ref igraph_subisomorphic_vf2() for the VF2 algorithm.
+ *
+ * Time complexity: exponential.
+ *
+ * \example examples/simple/igraph_subisomorphic_lad.c
+ */
+
+int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target,
+                             igraph_vector_ptr_t *domains,
+                             igraph_bool_t *iso, igraph_vector_t *map,
+                             igraph_vector_ptr_t *maps,
+                             igraph_bool_t induced, int time_limit) {
+
+    bool firstSol = maps == 0;
+    bool initialDomains = domains != 0;
+    Tgraph Gp, Gt;
+    Tdomain D;
+    int invalidDomain;
+    int u, nbToMatch = 0;
+    igraph_vector_int_t toMatch;
+    /* Number of nodes in the search tree */
+    int nbNodes = 0;
+    /* number of failed nodes in the search tree */
+    int nbFail = 0;
+    /* number of solutions found */
+    int nbSol = 0;
+    /* reusable structure to get CPU time usage */
+    clock_t begin = clock();
+    /* Stack to store memory blocks that are allocated during igraph_i_lad_solve */
+    igraph_vector_ptr_t alloc_history;
+
+    if (!iso && !map && !maps) {
+        IGRAPH_ERROR("Please give least one of `iso', `map' or `maps'",
+                     IGRAPH_EINVAL);
+    }
+
+    if (igraph_is_directed(pattern) != igraph_is_directed(target)) {
+        IGRAPH_ERROR("Cannot search for a directed pattern in an undirected target "
+                     "or vice versa", IGRAPH_EINVAL);
+    }
+    if (time_limit <= 0) {
+        time_limit = INT_MAX;
+    }
+
+    if (iso)  {
+        *iso = (igraph_vcount(pattern) == 0);
+    }
+    if (map)  {
+        igraph_vector_clear(map);
+    }
+    if (maps) {
+        igraph_vector_ptr_clear(maps);
+    }
+
+    if (igraph_vcount(pattern) == 0) {
+        /* Special case for empty graphs */
+        return IGRAPH_SUCCESS;
+    }
+
+    IGRAPH_CHECK(igraph_i_lad_createGraph(pattern, &Gp));
+    IGRAPH_CHECK(igraph_i_lad_createGraph(target, &Gt));
+
+    if (Gp.nbVertices > Gt.nbVertices) {
+        goto exit3;
+    }
+
+    IGRAPH_CHECK(igraph_i_lad_initDomains(initialDomains, domains, &D, &Gp,
+                                          &Gt, &invalidDomain));
+    if (invalidDomain) {
+        goto exit2;
+    }
+
+    IGRAPH_CHECK(igraph_i_lad_updateMatching((int) (Gp.nbVertices),
+                 (int) (Gt.nbVertices),
+                 &D.nbVal, &D.firstVal, &D.val,
+                 &D.globalMatchingP,
+                 &invalidDomain));
+    if (invalidDomain) {
+        goto exit;
+    }
+
+    IGRAPH_CHECK(igraph_i_lad_ensureGACallDiff((char) induced, &Gp, &Gt, &D,
+                 &invalidDomain));
+    if (invalidDomain) {
+        goto exit;
+    }
+
+    for (u = 0; u < Gp.nbVertices; u++) {
+        VECTOR(D.globalMatchingT)[ VECTOR(D.globalMatchingP)[u] ] = u;
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp.nbVertices));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch);
+
+    for (u = 0; u < Gp.nbVertices; u++) {
+        if (VECTOR(D.nbVal)[u] == 1) {
+            VECTOR(toMatch)[nbToMatch++] = u;
+        }
+    }
+    IGRAPH_CHECK(igraph_i_lad_matchVertices(nbToMatch, &toMatch, (char) induced,
+                                            &D, &Gp, &Gt, &invalidDomain));
+    igraph_vector_int_destroy(&toMatch);
+    IGRAPH_FINALLY_CLEAN(1);
+    if (invalidDomain) {
+        goto exit;
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&alloc_history, 0));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &alloc_history);
+
+    IGRAPH_CHECK(igraph_i_lad_solve(time_limit, firstSol, (char) induced, &D,
+                                    &Gp, &Gt, &invalidDomain, iso, map, maps,
+                                    &nbNodes, &nbFail, &nbSol, &begin,
+                                    &alloc_history));
+
+    igraph_vector_ptr_destroy_all(&alloc_history);
+    IGRAPH_FINALLY_CLEAN(1);
+
+exit:
+
+    igraph_vector_int_destroy(&D.val);
+    igraph_vector_int_destroy(&D.matching);
+    IGRAPH_FINALLY_CLEAN(2);
+
+exit2:
+
+    igraph_vector_int_destroy(&D.globalMatchingP);
+    igraph_vector_int_destroy(&D.globalMatchingT);
+    igraph_vector_int_destroy(&D.nbVal);
+    igraph_vector_int_destroy(&D.firstVal);
+    igraph_matrix_int_destroy(&D.posInVal);
+    igraph_matrix_int_destroy(&D.firstMatch);
+    igraph_vector_char_destroy(&D.markedToFilter);
+    igraph_vector_int_destroy(&D.toFilter);
+    IGRAPH_FINALLY_CLEAN(8);
+
+exit3:
+
+    igraph_matrix_char_destroy(&Gt.isEdge);
+    igraph_adjlist_destroy(&Gt.succ);
+    igraph_vector_destroy(&Gt.nbSucc);
+    igraph_matrix_char_destroy(&Gp.isEdge);
+    igraph_adjlist_destroy(&Gp.succ);
+    igraph_vector_destroy(&Gp.nbSucc);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return 0;
+}
diff --git a/igraph/src/lapack.c b/igraph/src/lapack.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lapack.c
@@ -0,0 +1,954 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_lapack.h"
+#include "igraph_lapack_internal.h"
+
+/**
+ * \function igraph_lapack_dgetrf
+ * LU factorization of a general M-by-N matrix
+ *
+ * The factorization has the form
+ *      A = P * L * U
+ * where P is a permutation matrix, L is lower triangular with unit
+ * diagonal elements (lower trapezoidal if m > n), and U is upper
+ * triangular (upper trapezoidal if m &lt; n).
+ * \param a The input/output matrix. On entry, the M-by-N matrix to be
+ *      factored. On exit, the factors L and U from the factorization
+ *      A = P * L * U; the unit diagonal elements of L are not
+ *      stored.
+ * \param ipiv An integer vector, the pivot indices are stored here,
+ *      unless it is a null pointer. Row i of the matrix was
+ *      interchanged with row ipiv[i].
+ * \param info LAPACK error code. Zero on successful exit. If positive
+ *      and i, then U(i,i) is exactly zero. The factorization has been
+ *      completed, but the factor U is exactly singular, and division
+ *      by zero will occur if it is used to solve a system of
+ *      equations. If LAPACK returns an error, i.e. a negative info
+ *      value, then an igraph error is generated as well.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv,
+                         int *info) {
+    int m = (int) igraph_matrix_nrow(a);
+    int n = (int) igraph_matrix_ncol(a);
+    int lda = m > 0 ? m : 1;
+    igraph_vector_int_t *myipiv = ipiv, vipiv;
+
+    if (!ipiv) {
+        IGRAPH_CHECK(igraph_vector_int_init(&vipiv, m < n ? m : n));
+        IGRAPH_FINALLY(igraph_vector_int_destroy, &vipiv);
+        myipiv = &vipiv;
+    }
+
+    igraphdgetrf_(&m, &n, VECTOR(a->data), &lda, VECTOR(*myipiv), info);
+
+    if (*info > 0) {
+        IGRAPH_WARNING("LU: factor is exactly singular");
+    } else if (*info < 0) {
+        switch (*info) {
+        case -1:
+            IGRAPH_ERROR("Invalid number of rows", IGRAPH_ELAPACK);
+            break;
+        case -2:
+            IGRAPH_ERROR("Invalid number of columns", IGRAPH_ELAPACK);
+            break;
+        case -3:
+            IGRAPH_ERROR("Invalid input matrix", IGRAPH_ELAPACK);
+            break;
+        case -4:
+            IGRAPH_ERROR("Invalid LDA parameter", IGRAPH_ELAPACK);
+            break;
+        case -5:
+            IGRAPH_ERROR("Invalid pivot vector", IGRAPH_ELAPACK);
+            break;
+        case -6:
+            IGRAPH_ERROR("Invalid info argument", IGRAPH_ELAPACK);
+            break;
+        default:
+            IGRAPH_ERROR("Unknown LAPACK error", IGRAPH_ELAPACK);
+            break;
+        }
+    }
+
+    if (!ipiv) {
+        igraph_vector_int_destroy(&vipiv);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_lapack_dgetrs
+ * Solve general system of linear equations using LU factorization
+ *
+ * This function calls LAPACK to solve a system of linear equations
+ *      A * X = B  or  A' * X = B
+ * with a general N-by-N matrix A using the LU factorization
+ * computed by \ref igraph_lapack_dgetrf.
+ * \param transpose Logical scalar, whether to transpose the input
+ *      matrix.
+ * \param a A matrix containing the L and U factors from the
+ *      factorization A = P*L*U.
+ * \param ipiv An integer vector, the pivot indices from \ref
+ *      igraph_lapack_dgetrf must be given here.
+ * \param b The right hand side matrix must be given here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a,
+                         igraph_vector_int_t *ipiv, igraph_matrix_t *b) {
+    char trans = transpose ? 'T' : 'N';
+    int n = (int) igraph_matrix_nrow(a);
+    int nrhs = (int) igraph_matrix_ncol(b);
+    int lda = n > 0 ? n : 1;
+    int ldb = n > 0 ? n : 1;
+    int info;
+
+    if (n != igraph_matrix_ncol(a)) {
+        IGRAPH_ERROR("Cannot LU solve matrix", IGRAPH_NONSQUARE);
+    }
+    if (n != igraph_matrix_nrow(b)) {
+        IGRAPH_ERROR("Cannot LU solve matrix, RHS of wrong size", IGRAPH_EINVAL);
+    }
+
+    igraphdgetrs_(&trans, &n, &nrhs, VECTOR(a->data), &lda, VECTOR(*ipiv),
+                  VECTOR(b->data), &ldb, &info);
+
+    if (info < 0) {
+        switch (info) {
+        case -1:
+            IGRAPH_ERROR("Invalid transpose argument", IGRAPH_ELAPACK);
+            break;
+        case -2:
+            IGRAPH_ERROR("Invalid number of rows/columns", IGRAPH_ELAPACK);
+            break;
+        case -3:
+            IGRAPH_ERROR("Invalid number of RHS vectors", IGRAPH_ELAPACK);
+            break;
+        case -4:
+            IGRAPH_ERROR("Invalid LU matrix", IGRAPH_ELAPACK);
+            break;
+        case -5:
+            IGRAPH_ERROR("Invalid LDA parameter", IGRAPH_ELAPACK);
+            break;
+        case -6:
+            IGRAPH_ERROR("Invalid pivot vector", IGRAPH_ELAPACK);
+            break;
+        case -7:
+            IGRAPH_ERROR("Invalid RHS matrix", IGRAPH_ELAPACK);
+            break;
+        case -8:
+            IGRAPH_ERROR("Invalid LDB parameter", IGRAPH_ELAPACK);
+            break;
+        case -9:
+            IGRAPH_ERROR("Invalid info argument", IGRAPH_ELAPACK);
+            break;
+        default:
+            IGRAPH_ERROR("Unknown LAPACK error", IGRAPH_ELAPACK);
+            break;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_lapack_dgesv
+ * Solve system of linear equations with LU factorization
+ *
+ * This function computes the solution to a real system of linear
+ * equations A * X = B, where A is an N-by-N matrix and X and B are
+ * N-by-NRHS matrices.
+ *
+ * </para><para>The LU decomposition with partial pivoting and row
+ * interchanges is used to factor A as
+ *    A = P * L * U,
+ * where P is a permutation matrix, L is unit lower triangular, and U is
+ * upper triangular.  The factored form of A is then used to solve the
+ * system of equations A * X = B.
+ * \param a Matrix. On entry the N-by-N coefficient matrix, on exit,
+ *        the factors L and U from the factorization A=P*L*U; the unit
+ *        diagonal elements of L are not stored.
+ * \param ipiv An integer vector or a null pointer. If not a null
+ *        pointer, then the pivot indices that define the permutation
+ *        matrix P, are stored here. Row i of the matrix was
+ *        interchanged with row IPIV(i).
+ * \param b Matrix, on entry the right hand side matrix should be
+ *        stored here. On exit, if there was no error, and the info
+ *        argument is zero, then it contains the solution matrix X.
+ * \param info The LAPACK info code. If it is positive, then
+ *        U(info,info) is exactly zero. In this case the factorization
+ *        has been completed, but the factor U is exactly
+ *        singular, so the solution could not be computed.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/igraph_lapack_dgesv.c
+ */
+
+int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv,
+                        igraph_matrix_t *b, int *info) {
+
+    int n = (int) igraph_matrix_nrow(a);
+    int nrhs = (int) igraph_matrix_ncol(b);
+    int lda = n > 0 ? n : 1;
+    int ldb = n > 0 ? n : 1;
+    igraph_vector_int_t *myipiv = ipiv, vipiv;
+
+    if (n != igraph_matrix_ncol(a)) {
+        IGRAPH_ERROR("Cannot LU solve matrix", IGRAPH_NONSQUARE);
+    }
+    if (n != igraph_matrix_nrow(b)) {
+        IGRAPH_ERROR("Cannot LU solve matrix, RHS of wrong size", IGRAPH_EINVAL);
+    }
+
+    if (!ipiv) {
+        IGRAPH_CHECK(igraph_vector_int_init(&vipiv, n));
+        IGRAPH_FINALLY(igraph_vector_int_destroy, &vipiv);
+        myipiv = &vipiv;
+    }
+
+    igraphdgesv_(&n, &nrhs, VECTOR(a->data), &lda, VECTOR(*myipiv),
+                 VECTOR(b->data), &ldb, info);
+
+    if (*info > 0) {
+        IGRAPH_WARNING("LU: factor is exactly singular");
+    } else if (*info < 0) {
+        switch (*info) {
+        case -1:
+            IGRAPH_ERROR("Invalid number of rows/column", IGRAPH_ELAPACK);
+            break;
+        case -2:
+            IGRAPH_ERROR("Invalid number of RHS vectors", IGRAPH_ELAPACK);
+            break;
+        case -3:
+            IGRAPH_ERROR("Invalid input matrix", IGRAPH_ELAPACK);
+            break;
+        case -4:
+            IGRAPH_ERROR("Invalid LDA parameter", IGRAPH_ELAPACK);
+            break;
+        case -5:
+            IGRAPH_ERROR("Invalid pivot vector", IGRAPH_ELAPACK);
+            break;
+        case -6:
+            IGRAPH_ERROR("Invalid RHS matrix", IGRAPH_ELAPACK);
+            break;
+        case -7:
+            IGRAPH_ERROR("Invalid LDB parameter", IGRAPH_ELAPACK);
+            break;
+        case -8:
+            IGRAPH_ERROR("Invalid info argument", IGRAPH_ELAPACK);
+            break;
+        default:
+            IGRAPH_ERROR("Unknown LAPACK error", IGRAPH_ELAPACK);
+            break;
+        }
+    }
+
+    if (!ipiv) {
+        igraph_vector_int_destroy(&vipiv);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_lapack_dsyevr
+ * Selected eigenvalues and optionally eigenvectors of a symmetric matrix
+ *
+ * Calls the DSYEVR LAPACK function to compute selected eigenvalues
+ * and, optionally, eigenvectors of a real symmetric matrix A.
+ * Eigenvalues and eigenvectors can be selected by specifying either
+ * a range of values or a range of indices for the desired eigenvalues.
+ *
+ * </para><para>See more in the LAPACK documentation.
+ * \param A Matrix, on entry it contains the symmetric input
+ *        matrix. Only the leading N-by-N upper triangular part is
+ *        used for the computation.
+ * \param which Constant that gives which eigenvalues (and possibly
+ *        the corresponding eigenvectors) to calculate. Possible
+ *        values are \c IGRAPH_LAPACK_DSYEV_ALL, all eigenvalues;
+ *        \c IGRAPH_LAPACK_DSYEV_INTERVAL, all eigenvalues in the
+ *        half-open interval (vl,vu];
+ *        \c IGRAPH_LAPACK_DSYEV_SELECT, the il-th through iu-th
+ *        eigenvalues.
+ * \param vl If \p which is \c IGRAPH_LAPACK_DSYEV_INTERVAL, then
+ *        this is the lower bound of the interval to be searched for
+ *        eigenvalues. See also the \p vestimate argument.
+ * \param vu If \p which is \c IGRAPH_LAPACK_DSYEV_INTERVAL, then
+ *        this is the upper bound of the interval to be searched for
+ *        eigenvalues. See also the \p vestimate argument.
+ * \param vestimate An upper bound for the number of eigenvalues in
+ *        the (vl,vu] interval, if \p which is \c
+ *        IGRAPH_LAPACK_DSYEV_INTERVAL. Memory is allocated only for
+ *        the given number of eigenvalues (and eigenvectors), so this
+ *        upper bound must be correct.
+ * \param il The index of the smallest eigenvalue to return, if \p
+ *        which is \c IGRAPH_LAPACK_DSYEV_SELECT.
+ * \param iu The index of the largets eigenvalue to return, if \p
+ *        which is \c IGRAPH_LAPACK_DSYEV_SELECT.
+ * \param abstol The absolute error tolerance for the eigevalues. An
+ *        approximate eigenvalue is accepted as converged when it is
+ *        determined to lie in an interval [a,b] of width less than or
+ *        equal to abstol + EPS * max(|a|,|b|), where EPS is the
+ *        machine precision.
+ * \param values An initialized vector, the eigenvalues are stored
+ *        here, unless it is a null pointer. It will be resized as
+ *        needed.
+ * \param vectors An initialized matrix, the eigenvectors are stored
+ *        in its columns, unless it is a null pointer. It will be
+ *        resized as needed.
+ * \param support An integer vector. If not a null pointer, then it
+ *        will be resized to (2*max(1,M)) (M is a the total number of
+ *        eigenvalues found). Then the support of the eigenvectors in
+ *        \p vectors is stored here, i.e., the indices
+ *        indicating the nonzero elements in \p vectors.
+ *        The i-th eigenvector is nonzero only in elements
+ *        support(2*i-1) through support(2*i).
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/igraph_lapack_dsyevr.c
+ */
+
+int igraph_lapack_dsyevr(const igraph_matrix_t *A,
+                         igraph_lapack_dsyev_which_t which,
+                         igraph_real_t vl, igraph_real_t vu, int vestimate,
+                         int il, int iu, igraph_real_t abstol,
+                         igraph_vector_t *values, igraph_matrix_t *vectors,
+                         igraph_vector_int_t *support) {
+
+    igraph_matrix_t Acopy;
+    char jobz = vectors ? 'V' : 'N', range, uplo = 'U';
+    int n = (int) igraph_matrix_nrow(A), lda = n, ldz = n;
+    int m, info;
+    igraph_vector_t *myvalues = values, vvalues;
+    igraph_vector_int_t *mysupport = support, vsupport;
+    igraph_vector_t work;
+    igraph_vector_int_t iwork;
+    int lwork = -1, liwork = -1;
+
+    if (n != igraph_matrix_ncol(A)) {
+        IGRAPH_ERROR("Cannot find eigenvalues/vectors", IGRAPH_NONSQUARE);
+    }
+    if (which == IGRAPH_LAPACK_DSYEV_INTERVAL &&
+        (vestimate < 1 || vestimate > n)) {
+        IGRAPH_ERROR("Estimated (upper bound) number of eigenvalues must be "
+                     "between 1 and n", IGRAPH_EINVAL);
+    }
+    if (which == IGRAPH_LAPACK_DSYEV_SELECT && iu - il < 0) {
+        IGRAPH_ERROR("Invalid 'il' and/or 'iu' values", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A));
+    IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&work, 1);
+    IGRAPH_CHECK(igraph_vector_int_init(&iwork, 1));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &iwork);
+
+    if (!values) {
+        IGRAPH_VECTOR_INIT_FINALLY(&vvalues, 0);
+        myvalues = &vvalues;
+    }
+    if (!support) {
+        IGRAPH_CHECK(igraph_vector_int_init(&vsupport, 0));
+        IGRAPH_FINALLY(igraph_vector_int_destroy, &vsupport);
+        mysupport = &vsupport;
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(myvalues, n));
+
+    switch (which) {
+    case IGRAPH_LAPACK_DSYEV_ALL:
+        range = 'A';
+        IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2 * n));
+        if (vectors) {
+            IGRAPH_CHECK(igraph_matrix_resize(vectors, n, n));
+        }
+        break;
+    case IGRAPH_LAPACK_DSYEV_INTERVAL:
+        range = 'V';
+        IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2 * vestimate));
+        if (vectors) {
+            IGRAPH_CHECK(igraph_matrix_resize(vectors, n, vestimate));
+        }
+        break;
+    case IGRAPH_LAPACK_DSYEV_SELECT:
+        range = 'I';
+        IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2 * (iu - il + 1)));
+        if (vectors) {
+            IGRAPH_CHECK(igraph_matrix_resize(vectors, n, iu - il + 1));
+        }
+        break;
+    }
+
+    igraphdsyevr_(&jobz, &range, &uplo, &n, &MATRIX(Acopy, 0, 0), &lda,
+                  &vl, &vu, &il, &iu, &abstol, &m, VECTOR(*myvalues),
+                  vectors ? &MATRIX(*vectors, 0, 0) : 0, &ldz, VECTOR(*mysupport),
+                  VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info);
+
+    if (info != 0) {
+        IGRAPH_ERROR("Invalid argument to dsyevr in workspace query", IGRAPH_EINVAL);
+    }
+
+    lwork = (int) VECTOR(work)[0];
+    liwork = VECTOR(iwork)[0];
+    IGRAPH_CHECK(igraph_vector_resize(&work, lwork));
+    IGRAPH_CHECK(igraph_vector_int_resize(&iwork, liwork));
+
+    igraphdsyevr_(&jobz, &range, &uplo, &n, &MATRIX(Acopy, 0, 0), &lda,
+                  &vl, &vu, &il, &iu, &abstol, &m, VECTOR(*myvalues),
+                  vectors ? &MATRIX(*vectors, 0, 0) : 0, &ldz, VECTOR(*mysupport),
+                  VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info);
+
+    if (info != 0) {
+        IGRAPH_ERROR("Invalid argument to dsyevr in calculation", IGRAPH_EINVAL);
+    }
+
+    if (values) {
+        IGRAPH_CHECK(igraph_vector_resize(values, m));
+    }
+    if (vectors) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectors, n, m));
+    }
+    if (support) {
+        IGRAPH_CHECK(igraph_vector_int_resize(support, m));
+    }
+
+    if (!support) {
+        igraph_vector_int_destroy(&vsupport);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (!values) {
+        igraph_vector_destroy(&vvalues);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_int_destroy(&iwork);
+    igraph_vector_destroy(&work);
+    igraph_matrix_destroy(&Acopy);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_lapack_dgeev
+ * Eigenvalues and optionally eigenvectors of a non-symmetric matrix
+ *
+ * This function calls LAPACK to compute, for an N-by-N real
+ * nonsymmetric matrix A, the eigenvalues and, optionally, the left
+ * and/or right eigenvectors.
+ *
+ * </para><para>
+ * The right eigenvector v(j) of A satisfies
+ *                    A * v(j) = lambda(j) * v(j)
+ * where lambda(j) is its eigenvalue.
+ * The left eigenvector u(j) of A satisfies
+ *                u(j)**H * A = lambda(j) * u(j)**H
+ * where u(j)**H denotes the conjugate transpose of u(j).
+ *
+ * </para><para>
+ * The computed eigenvectors are normalized to have Euclidean norm
+ * equal to 1 and largest component real.
+ *
+ * \param A matrix. On entry it contains the N-by-N input matrix.
+ * \param valuesreal Pointer to an initialized vector, or a null
+ *        pointer. If not a null pointer, then the real parts of the
+ *        eigenvalues are stored here. The vector will be resized as
+ *        needed.
+ * \param valuesimag Pointer to an initialized vector, or a null
+ *        pointer. If not a null pointer, then the imaginary parts of
+ *        the eigenvalues are stored here. The vector will be resized
+ *        as needed.
+ * \param vectorsleft Pointer to an initialized matrix, or a null
+ *        pointer. If not a null pointer, then the left eigenvectors
+ *        are stored in the columns of the matrix. The matrix will be
+ *        resized as needed.
+ * \param vectorsright Pointer to an initialized matrix, or a null
+ *        pointer. If not a null pointer, then the right eigenvectors
+ *        are stored in the columns of the matrix. The matrix will be
+ *        resized as needed.
+ * \param info This argument is used for two purposes. As an input
+ *        argument it gives whether an igraph error should be
+ *        generated if the QR algorithm fails to compute all
+ *        eigenvalues. If \p info is non-zero, then an error is
+ *        generated, otherwise only a warning is given.
+ *        On exit it contains the LAPACK error code.
+ *        Zero means successful exit.
+ *        A negative values means that some of the arguments had an
+ *        illegal value, this always triggers an igraph error. An i
+ *        positive  value means that the QR algorithm failed to
+ *        compute all the eigenvalues, and no eigenvectors have been
+ *        computed; element i+1:N of \p valuesreal and \p valuesimag
+ *        contain eigenvalues which have converged. This case only
+ *        generates an igraph error, if \p info was non-zero on entry.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/igraph_lapack_dgeev.c
+ */
+
+int igraph_lapack_dgeev(const igraph_matrix_t *A,
+                        igraph_vector_t *valuesreal,
+                        igraph_vector_t *valuesimag,
+                        igraph_matrix_t *vectorsleft,
+                        igraph_matrix_t *vectorsright,
+                        int *info) {
+
+    char jobvl = vectorsleft  ? 'V' : 'N';
+    char jobvr = vectorsright ? 'V' : 'N';
+    int n = (int) igraph_matrix_nrow(A);
+    int lda = n, ldvl = n, ldvr = n, lwork = -1;
+    igraph_vector_t work;
+    igraph_vector_t *myreal = valuesreal, *myimag = valuesimag, vreal, vimag;
+    igraph_matrix_t Acopy;
+    int error = *info;
+
+    if (igraph_matrix_ncol(A) != n) {
+        IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_NONSQUARE);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A));
+    IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&work, 1);
+
+    if (!valuesreal) {
+        IGRAPH_VECTOR_INIT_FINALLY(&vreal, n);
+        myreal = &vreal;
+    } else {
+        IGRAPH_CHECK(igraph_vector_resize(myreal, n));
+    }
+    if (!valuesimag) {
+        IGRAPH_VECTOR_INIT_FINALLY(&vimag, n);
+        myimag = &vimag;
+    } else {
+        IGRAPH_CHECK(igraph_vector_resize(myimag, n));
+    }
+    if (vectorsleft) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectorsleft, n, n));
+    }
+    if (vectorsright) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectorsright, n, n));
+    }
+
+    igraphdgeev_(&jobvl, &jobvr, &n, &MATRIX(Acopy, 0, 0), &lda,
+                 VECTOR(*myreal), VECTOR(*myimag),
+                 vectorsleft  ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl,
+                 vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr,
+                 VECTOR(work), &lwork, info);
+
+    lwork = (int) VECTOR(work)[0];
+    IGRAPH_CHECK(igraph_vector_resize(&work, lwork));
+
+    igraphdgeev_(&jobvl, &jobvr, &n, &MATRIX(Acopy, 0, 0), &lda,
+                 VECTOR(*myreal), VECTOR(*myimag),
+                 vectorsleft  ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl,
+                 vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr,
+                 VECTOR(work), &lwork, info);
+
+    if (*info < 0) {
+        IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_ELAPACK);
+    } else if (*info > 0) {
+        if (error) {
+            IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_ELAPACK);
+        } else {
+            IGRAPH_WARNING("Cannot calculate eigenvalues (dgeev)");
+        }
+    }
+
+    if (!valuesimag) {
+        igraph_vector_destroy(&vimag);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (!valuesreal) {
+        igraph_vector_destroy(&vreal);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&work);
+    igraph_matrix_destroy(&Acopy);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_lapack_dgeevx
+ * Eigenvalues/vectors of nonsymmetric matrices, expert mode
+ *
+ * This function calculates the eigenvalues and optionally the left
+ * and/or right eigenvectors of a nonsymmetric N-by-N real matrix.
+ *
+ * </para><para>
+ * Optionally also, it computes a balancing transformation to improve
+ * the conditioning of the eigenvalues and eigenvectors (\p ilo, \pihi,
+ * \p scale, and \p abnrm), reciprocal condition numbers for the
+ * eigenvalues (\p rconde), and reciprocal condition numbers for the
+ * right eigenvectors (\p rcondv).
+ *
+ * </para><para>
+ * The right eigenvector v(j) of A satisfies
+ *                   A * v(j) = lambda(j) * v(j)
+ * where lambda(j) is its eigenvalue.
+ * The left eigenvector u(j) of A satisfies
+ *               u(j)**H * A = lambda(j) * u(j)**H
+ * where u(j)**H denotes the conjugate transpose of u(j).
+ *
+ * </para><para>
+ * The computed eigenvectors are normalized to have Euclidean norm
+ * equal to 1 and largest component real.
+ *
+ * </para><para>
+ * Balancing a matrix means permuting the rows and columns to make it
+ * more nearly upper triangular, and applying a diagonal similarity
+ * transformation D * A * D**(-1), where D is a diagonal matrix, to
+ * make its rows and columns closer in norm and the condition numbers
+ * of its eigenvalues and eigenvectors smaller.  The computed
+ * reciprocal condition numbers correspond to the balanced matrix.
+ * Permuting rows and columns will not change the condition numbers
+ * (in exact arithmetic) but diagonal scaling will.  For further
+ * explanation of balancing, see section 4.10.2 of the LAPACK
+ * Users' Guide.
+ *
+ * \param balance Scalar that indicated, whether the input matrix
+ *   should be balanced. Possible values:
+ *   \clist
+ *     \cli IGRAPH_LAPACK_DGEEVX_BALANCE_NONE
+ *          no not diagonally scale or permute.
+ *     \cli IGRAPH_LAPACK_DGEEVX_BALANCE_PERM
+ *          perform permutations to make the matrix more nearly upper
+ *          triangular. Do not diagonally scale.
+ *     \cli IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE
+ *          diagonally scale the matrix, i.e. replace A by
+ *          D*A*D**(-1), where D is a diagonal matrix, chosen to make
+ *          the rows and columns of A more equal in norm. Do not
+ *          permute.
+ *     \cli IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH
+ *          both diagonally scale and permute A.
+ *   \endclist
+ * \param A The input matrix, must be square.
+ * \param valuesreal An initialized vector, or a NULL pointer. If not
+ *   a NULL pointer, then the real parts of the eigenvalues are stored
+ *   here. The vector will be resized, as needed.
+ * \param valuesimag An initialized vector, or a NULL pointer. If not
+ *   a NULL pointer, then the imaginary parts of the eigenvalues are stored
+ *   here. The vector will be resized, as needed.
+ * \param vectorsleft An initialized matrix or a NULL pointer. If not
+ *   a null pointer, then the left eigenvectors are stored here. The
+ *   order corresponds to the eigenvalues and the eigenvectors are
+ *   stored in a compressed form. If the j-th eigenvalue is real then
+ *   column j contains the corresponding eigenvector. If the j-th and
+ *   (j+1)-th eigenvalues form a complex conjugate pair, then the j-th
+ *   and (j+1)-th columns contain their corresponding eigenvectors.
+ * \param vectorsright An initialized matrix or a NULL pointer. If not
+ *   a null pointer, then the right eigenvectors are stored here. The
+ *   format is the same, as for the \p vectorsleft argument.
+ * \param ilo
+ * \param ihi \p ilo and \p ihi are integer values determined when A was
+ *   balanced.  The balanced A(i,j) = 0 if I>J and
+ *   J=1,...,ilo-1 or I=ihi+1,...,N.
+ * \param scale Pointer to an initialized vector or a NULL pointer. If
+ *   not a NULL pointer, then details of the permutations and scaling
+ *   factors applied when balancing \param A, are stored here.
+ *   If P(j) is the index of the row and column
+ *   interchanged with row and column j, and D(j) is the scaling
+ *   factor applied to row and column j, then
+ *   \clist
+ *      \cli scale(J) = P(J),    for J = 1,...,ilo-1
+ *      \cli scale(J) = D(J),    for J = ilo,...,ihi
+ *      \cli scale(J) = P(J)     for J = ihi+1,...,N.
+ *   \endclist
+ *   The order in which the interchanges are made is N to \p ihi+1,
+ *   then 1 to \p ilo-1.
+ * \param abnrm Pointer to a real variable, the one-norm of the
+ *   balanced matrix is stored here. (The one-norm is the maximum of
+ *   the sum of absolute values of elements in any column.)
+ * \param rconde An initialized vector or a NULL pointer. If not a
+ *   null pointer, then the reciprocal condition numbers of the
+ *   eigenvalues are stored here.
+ * \param rcondv An initialized vector or a NULL pointer. If not a
+ *   null pointer, then the reciprocal condition numbers of the right
+ *   eigenvectors are stored here.
+ * \param info This argument is used for two purposes. As an input
+ *        argument it gives whether an igraph error should be
+ *        generated if the QR algorithm fails to compute all
+ *        eigenvalues. If \p info is non-zero, then an error is
+ *        generated, otherwise only a warning is given.
+ *        On exit it contains the LAPACK error code.
+ *        Zero means successful exit.
+ *        A negative values means that some of the arguments had an
+ *        illegal value, this always triggers an igraph error. An i
+ *        positive  value means that the QR algorithm failed to
+ *        compute all the eigenvalues, and no eigenvectors have been
+ *        computed; element i+1:N of \p valuesreal and \p valuesimag
+ *        contain eigenvalues which have converged. This case only
+ *        generated an igraph error, if \p info was non-zero on entry.
+ * \return Error code.
+ *
+ * Time complexity: TODO
+ *
+ * \example examples/simple/igraph_lapack_dgeevx.c
+ */
+
+int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance,
+                         const igraph_matrix_t *A,
+                         igraph_vector_t *valuesreal,
+                         igraph_vector_t *valuesimag,
+                         igraph_matrix_t *vectorsleft,
+                         igraph_matrix_t *vectorsright,
+                         int *ilo, int *ihi, igraph_vector_t *scale,
+                         igraph_real_t *abnrm,
+                         igraph_vector_t *rconde,
+                         igraph_vector_t *rcondv,
+                         int *info) {
+
+    char balanc;
+    char jobvl = vectorsleft  ? 'V' : 'N';
+    char jobvr = vectorsright ? 'V' : 'N';
+    char sense;
+    int n = (int) igraph_matrix_nrow(A);
+    int lda = n, ldvl = n, ldvr = n, lwork = -1;
+    igraph_vector_t work;
+    igraph_vector_int_t iwork;
+    igraph_matrix_t Acopy;
+    int error = *info;
+    igraph_vector_t *myreal = valuesreal, *myimag = valuesimag, vreal, vimag;
+    igraph_vector_t *myscale = scale, vscale;
+
+    if (igraph_matrix_ncol(A) != n) {
+        IGRAPH_ERROR("Cannot calculate eigenvalues (dgeevx)", IGRAPH_NONSQUARE);
+    }
+
+    switch (balance) {
+    case IGRAPH_LAPACK_DGEEVX_BALANCE_NONE:
+        balanc = 'N';
+        break;
+    case IGRAPH_LAPACK_DGEEVX_BALANCE_PERM:
+        balanc = 'P';
+        break;
+    case IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE:
+        balanc = 'S';
+        break;
+    case IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH:
+        balanc = 'B';
+        break;
+    default:
+        IGRAPH_ERROR("Invalid 'balance' argument", IGRAPH_EINVAL);
+        break;
+    }
+
+    if (!rconde && !rcondv) {
+        sense = 'N';
+    } else if (rconde && !rcondv) {
+        sense = 'E';
+    } else if (!rconde && rcondv) {
+        sense = 'V';
+    } else {
+        sense = 'B';
+    }
+
+    IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A));
+    IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&work, 1);
+    IGRAPH_CHECK(igraph_vector_int_init(&iwork, n));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &iwork);
+
+    if (!valuesreal) {
+        IGRAPH_VECTOR_INIT_FINALLY(&vreal, n);
+        myreal = &vreal;
+    } else {
+        IGRAPH_CHECK(igraph_vector_resize(myreal, n));
+    }
+    if (!valuesimag) {
+        IGRAPH_VECTOR_INIT_FINALLY(&vimag, n);
+        myimag = &vimag;
+    } else {
+        IGRAPH_CHECK(igraph_vector_resize(myimag, n));
+    }
+    if (!scale) {
+        IGRAPH_VECTOR_INIT_FINALLY(&vscale, n);
+        myscale = &vscale;
+    } else {
+        IGRAPH_CHECK(igraph_vector_resize(scale, n));
+    }
+    if (vectorsleft) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectorsleft, n, n));
+    }
+    if (vectorsright) {
+        IGRAPH_CHECK(igraph_matrix_resize(vectorsright, n, n));
+    }
+
+    igraphdgeevx_(&balanc, &jobvl, &jobvr, &sense, &n, &MATRIX(Acopy, 0, 0),
+                  &lda, VECTOR(*myreal), VECTOR(*myimag),
+                  vectorsleft  ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl,
+                  vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr,
+                  ilo, ihi, VECTOR(*myscale), abnrm,
+                  rconde ? VECTOR(*rconde) : 0,
+                  rcondv ? VECTOR(*rcondv) : 0,
+                  VECTOR(work), &lwork, VECTOR(iwork), info);
+
+    lwork = (int) VECTOR(work)[0];
+    IGRAPH_CHECK(igraph_vector_resize(&work, lwork));
+
+    igraphdgeevx_(&balanc, &jobvl, &jobvr, &sense, &n, &MATRIX(Acopy, 0, 0),
+                  &lda, VECTOR(*myreal), VECTOR(*myimag),
+                  vectorsleft  ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl,
+                  vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr,
+                  ilo, ihi, VECTOR(*myscale), abnrm,
+                  rconde ? VECTOR(*rconde) : 0,
+                  rcondv ? VECTOR(*rcondv) : 0,
+                  VECTOR(work), &lwork, VECTOR(iwork), info);
+
+    if (*info < 0) {
+        IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_ELAPACK);
+    } else if (*info > 0) {
+        if (error) {
+            IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev)", IGRAPH_ELAPACK);
+        } else {
+            IGRAPH_WARNING("Cannot calculate eigenvalues (dgeev)");
+        }
+    }
+
+    if (!scale) {
+        igraph_vector_destroy(&vscale);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (!valuesimag) {
+        igraph_vector_destroy(&vimag);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (!valuesreal) {
+        igraph_vector_destroy(&vreal);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_int_destroy(&iwork);
+    igraph_vector_destroy(&work);
+    igraph_matrix_destroy(&Acopy);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+int igraph_lapack_dgehrd(const igraph_matrix_t *A,
+                         int ilo, int ihi,
+                         igraph_matrix_t *result) {
+
+    int n = (int) igraph_matrix_nrow(A);
+    int lda = n;
+    int lwork = -1;
+    igraph_vector_t work;
+    igraph_real_t optwork;
+    igraph_vector_t tau;
+    igraph_matrix_t Acopy;
+    int info = 0;
+    int i;
+
+    if (igraph_matrix_ncol(A) != n) {
+        IGRAPH_ERROR("Hessenberg reduction failed", IGRAPH_NONSQUARE);
+    }
+
+    if (ilo < 1 || ihi > n || ilo > ihi) {
+        IGRAPH_ERROR("Invalid `ilo' and/or `ihi'", IGRAPH_EINVAL);
+    }
+
+    if (n <= 1) {
+        IGRAPH_CHECK(igraph_matrix_update(result, A));
+        return 0;
+    }
+
+    IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A));
+    IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy);
+    IGRAPH_VECTOR_INIT_FINALLY(&tau, n - 1);
+
+    igraphdgehrd_(&n, &ilo, &ihi, &MATRIX(Acopy, 0, 0), &lda, VECTOR(tau),
+                  &optwork, &lwork, &info);
+
+    if (info != 0) {
+        IGRAPH_ERROR("Internal Hessenberg transformation error",
+                     IGRAPH_EINTERNAL);
+    }
+
+    lwork = (int) optwork;
+    IGRAPH_VECTOR_INIT_FINALLY(&work, lwork);
+
+    igraphdgehrd_(&n, &ilo, &ihi, &MATRIX(Acopy, 0, 0), &lda, VECTOR(tau),
+                  VECTOR(work), &lwork, &info);
+
+    if (info != 0) {
+        IGRAPH_ERROR("Internal Hessenberg transformation error",
+                     IGRAPH_EINTERNAL);
+    }
+
+    igraph_vector_destroy(&work);
+    igraph_vector_destroy(&tau);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_CHECK(igraph_matrix_update(result, &Acopy));
+
+    igraph_matrix_destroy(&Acopy);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    for (i = 0; i < n - 2; i++) {
+        int j;
+        for (j = i + 2; j < n; j++) {
+            MATRIX(*result, j, i) = 0.0;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_lapack_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2,
+                       igraph_real_t *res) {
+
+    int n = igraph_vector_size(v1);
+    int one = 1;
+
+    if (igraph_vector_size(v2) != n) {
+        IGRAPH_ERROR("Dot product of vectors with different dimensions",
+                     IGRAPH_EINVAL);
+    }
+
+    *res = igraphddot_(&n, VECTOR(*v1), &one, VECTOR(*v2), &one);
+
+    return 0;
+}
+
diff --git a/igraph/src/layout.c b/igraph/src/layout.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/layout.c
@@ -0,0 +1,2421 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph R package.
+   Copyright (C) 2003-2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_layout.h"
+#include "igraph_random.h"
+#include "igraph_memory.h"
+#include "igraph_iterators.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_progress.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_paths.h"
+#include "igraph_structural.h"
+#include "igraph_visitor.h"
+#include "igraph_topology.h"
+#include "igraph_components.h"
+#include "igraph_types_internal.h"
+#include "igraph_dqueue.h"
+#include "igraph_arpack.h"
+#include "igraph_blas.h"
+#include "igraph_centrality.h"
+#include "igraph_eigen.h"
+#include "config.h"
+#include <math.h>
+#include "igraph_math.h"
+#include <stdio.h> /* FIXME */
+
+
+/**
+ * \section about_layouts
+ *
+ * <para>Layout generator functions (or at least most of them) try to place the
+ * vertices and edges of a graph on a 2D plane or in 3D space in a way
+ * which visually pleases the human eye.</para>
+ *
+ * <para>They take a graph object and a number of parameters as arguments
+ * and return an \type igraph_matrix_t, in which each row gives the
+ * coordinates of a vertex.</para>
+ */
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_random
+ * \brief Places the vertices uniform randomly on a plane.
+ *
+ * \param graph Pointer to an initialized graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized as needed.
+ * \return Error code. The current implementation always returns with
+ * success.
+ *
+ * Time complexity: O(|V|), the
+ * number of vertices.
+ */
+
+int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
+
+    RNG_BEGIN();
+
+    for (i = 0; i < no_of_nodes; i++) {
+        MATRIX(*res, i, 0) = RNG_UNIF(-1, 1);
+        MATRIX(*res, i, 1) = RNG_UNIF(-1, 1);
+    }
+
+    RNG_END();
+
+    return 0;
+}
+
+/**
+ * \function igraph_layout_random_3d
+ * \brief Random layout in 3D
+ *
+ * \param graph The graph to place.
+ * \param res Pointer to an initialized matrix object. It will be
+ * resized to hold the result.
+ * \return Error code. The current implementation always returns with
+ * success.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(|V|), the number of vertices.
+ */
+
+int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3));
+
+    RNG_BEGIN();
+
+    for (i = 0; i < no_of_nodes; i++) {
+        MATRIX(*res, i, 0) = RNG_UNIF(-1, 1);
+        MATRIX(*res, i, 1) = RNG_UNIF(-1, 1);
+        MATRIX(*res, i, 2) = RNG_UNIF(-1, 1);
+    }
+
+    RNG_END();
+
+    return 0;
+}
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_circle
+ * \brief Places the vertices uniformly on a circle, in the order of vertex ids.
+ *
+ * \param graph Pointer to an initialized graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized as needed.
+ * \param order The order of the vertices on the circle. The vertices
+ *        not included here, will be placed at (0,0). Supply
+ *        \ref igraph_vss_all() here for all vertices, in the order of
+ *        their vertex ids.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|), the
+ * number of vertices.
+ */
+
+int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res,
+                         igraph_vs_t order) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_integer_t vs_size;
+    long int i;
+    igraph_vit_t vit;
+
+    IGRAPH_CHECK(igraph_vs_size(graph, &order, &vs_size));
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
+    igraph_matrix_null(res);
+
+    igraph_vit_create(graph, order, &vit);
+    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        igraph_real_t phi = 2 * M_PI / vs_size * i;
+        int idx = IGRAPH_VIT_GET(vit);
+        MATRIX(*res, idx, 0) = cos(phi);
+        MATRIX(*res, idx, 1) = sin(phi);
+    }
+    igraph_vit_destroy(&vit);
+
+    return 0;
+}
+
+/**
+ * \function igraph_layout_star
+ * Generate a star-like layout
+ *
+ * \param graph The input graph.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized as needed.
+ * \param center The id of the vertex to put in the center.
+ * \param order A numeric vector giving the order of the vertices
+ *      (including the center vertex!). If a null pointer, then the
+ *      vertices are placed in increasing vertex id order.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|), linear in the number of vertices.
+ *
+ * \sa \ref igraph_layout_circle() and other layout generators.
+ */
+
+int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res,
+                       igraph_integer_t center, const igraph_vector_t *order) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int c = center;
+    long int i;
+    igraph_real_t step;
+    igraph_real_t phi;
+
+    if (order && igraph_vector_size(order) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid order vector length", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
+
+    if (no_of_nodes == 1) {
+        MATRIX(*res, 0, 0) = MATRIX(*res, 0, 1) = 0.0;
+    } else {
+        for (i = 0, step = 2 * M_PI / (no_of_nodes - 1), phi = 0;
+             i < no_of_nodes; i++) {
+            long int node = order ? (long int) VECTOR(*order)[i] : i;
+            if (node != c) {
+                MATRIX(*res, node, 0) = cos(phi);
+                MATRIX(*res, node, 1) = sin(phi);
+                phi += step;
+            } else {
+                MATRIX(*res, node, 0) = MATRIX(*res, node, 1) = 0.0;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_layout_sphere
+ * \brief Places vertices (more or less) uniformly on a sphere.
+ *
+ * </para><para>
+ * The algorithm was described in the following paper:
+ * Distributing many points on a sphere by E.B. Saff and
+ * A.B.J. Kuijlaars, \emb Mathematical Intelligencer \eme 19.1 (1997)
+ * 5--11.
+ *
+ * \param graph Pointer to an initialized graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized as needed.
+ * \return Error code. The current implementation always returns with
+ * success.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(|V|), the number of vertices in the graph.
+ */
+
+int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+    igraph_real_t h;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3));
+
+    if (no_of_nodes != 0) {
+        MATRIX(*res, 0, 0) = M_PI;
+        MATRIX(*res, 0, 1) = 0;
+    }
+    for (i = 1; i < no_of_nodes - 1; i++) {
+        h = -1 + 2 * i / (double)(no_of_nodes - 1);
+        MATRIX(*res, i, 0) = acos(h);
+        MATRIX(*res, i, 1) = fmod((MATRIX(*res, i - 1, 1) +
+                                   3.6 / sqrt(no_of_nodes * (1 - h * h))), 2 * M_PI);
+        IGRAPH_ALLOW_INTERRUPTION();
+    }
+    if (no_of_nodes >= 2) {
+        MATRIX(*res, no_of_nodes - 1, 0) = 0;
+        MATRIX(*res, no_of_nodes - 1, 1) = 0;
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_real_t x = cos(MATRIX(*res, i, 1)) * sin(MATRIX(*res, i, 0));
+        igraph_real_t y = sin(MATRIX(*res, i, 1)) * sin(MATRIX(*res, i, 0));
+        igraph_real_t z = cos(MATRIX(*res, i, 0));
+        MATRIX(*res, i, 0) = x;
+        MATRIX(*res, i, 1) = y;
+        MATRIX(*res, i, 2) = z;
+        IGRAPH_ALLOW_INTERRUPTION();
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_grid
+ * \brief Places the vertices on a regular grid on the plane.
+ *
+ * \param graph Pointer to an initialized graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized as needed.
+ * \param width The number of vertices in a single row of the grid.
+ *        When zero or negative, the width of the grid will be the
+ *        square root of the number of vertices, rounded up if needed.
+ * \return Error code. The current implementation always returns with
+ *         success.
+ *
+ * Time complexity: O(|V|), the number of vertices.
+ */
+int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width) {
+    long int i, no_of_nodes = igraph_vcount(graph);
+    igraph_real_t x, y;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
+
+    if (width <= 0) {
+        width = (long int) ceil(sqrt(no_of_nodes));
+    }
+
+    x = y = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        MATRIX(*res, i, 0) = x++;
+        MATRIX(*res, i, 1) = y;
+        if (x == width) {
+            x = 0; y++;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_grid_3d
+ * \brief Places the vertices on a regular grid in the 3D space.
+ *
+ * \param graph Pointer to an initialized graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized as needed.
+ * \param width  The number of vertices in a single row of the grid. When
+ *               zero or negative, the width is determined automatically.
+ * \param height The number of vertices in a single column of the grid. When
+ *               zero or negative, the height is determined automatically.
+ *
+ * \return Error code. The current implementation always returns with
+ *         success.
+ *
+ * Time complexity: O(|V|), the number of vertices.
+ */
+int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res,
+                          long int width, long int height) {
+    long int i, no_of_nodes = igraph_vcount(graph);
+    igraph_real_t x, y, z;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3));
+
+    if (width <= 0 && height <= 0) {
+        width = height = (long int) ceil(pow(no_of_nodes, 1.0 / 3));
+    } else if (width <= 0) {
+        width = (long int) ceil(sqrt(no_of_nodes / (double)height));
+    } else if (height <= 0) {
+        height = (long int) ceil(sqrt(no_of_nodes / (double)width));
+    }
+
+    x = y = z = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        MATRIX(*res, i, 0) = x++;
+        MATRIX(*res, i, 1) = y;
+        MATRIX(*res, i, 2) = z;
+        if (x == width) {
+            x = 0; y++;
+            if (y == height) {
+                y = 0; z++;
+            }
+        }
+    }
+
+    return 0;
+}
+
+int igraph_layout_springs(const igraph_t *graph, igraph_matrix_t *res,
+                          igraph_real_t mass, igraph_real_t equil, igraph_real_t k,
+                          igraph_real_t repeqdis, igraph_real_t kfr, igraph_bool_t repulse) {
+
+    IGRAPH_UNUSED(graph); IGRAPH_UNUSED(res); IGRAPH_UNUSED(mass);
+    IGRAPH_UNUSED(equil); IGRAPH_UNUSED(k); IGRAPH_UNUSED(repeqdis);
+    IGRAPH_UNUSED(kfr); IGRAPH_UNUSED(repulse);
+    IGRAPH_ERROR("Springs layout not implemented", IGRAPH_UNIMPLEMENTED);
+    /* TODO */
+    return 0;
+}
+
+void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y);
+
+void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y) {
+    igraph_real_t len = sqrt((*x) * (*x) + (*y) * (*y));
+    if (len != 0) {
+        *x /= len;
+        *y /= len;
+    }
+}
+
+/**
+ * \function igraph_layout_lgl
+ * \brief Force based layout algorithm for large graphs.
+ *
+ * </para><para>
+ * This is a layout generator similar to the Large Graph Layout
+ * algorithm and program
+ * (http://lgl.sourceforge.net/). But unlike LGL, this
+ * version uses a Fruchterman-Reingold style simulated annealing
+ * algorithm for placing the vertices. The speedup is achieved by
+ * placing the vertices on a grid and calculating the repulsion only
+ * for vertices which are closer to each other than a limit.
+ *
+ * \param graph The (initialized) graph object to place.
+ * \param res Pointer to an initialized matrix object to hold the
+ *   result. It will be resized if needed.
+ * \param maxit The maximum number of cooling iterations to perform
+ *   for each layout step. A reasonable default is 150.
+ * \param maxdelta The maximum length of the move allowed for a vertex
+ *   in a single iteration. A reasonable default is the number of
+ *   vertices.
+ * \param area 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.
+ * \param coolexp The cooling exponent. A reasonable default value is
+ *   1.5.
+ * \param repulserad Determines the radius at which vertex-vertex
+ *   repulsion cancels out attraction of adjacent vertices. A
+ *   reasonable default value is \p area times the number of vertices.
+ * \param cellsize The size of the grid cells, one side of the
+ *   square. A reasonable default value is the fourth root of
+ *   \p area (or the square root of the number of vertices if \p area
+ *   is also left at its default value).
+ * \param proot The root vertex, this is placed first, its neighbors
+ *   in the first iteration, second neighbors in the second, etc. If
+ *   negative then a random vertex is chosen.
+ * \return Error code.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: ideally O(dia*maxit*(|V|+|E|)), |V| is the number
+ * of vertices,
+ * dia is the diameter of the graph, worst case complexity is still
+ * O(dia*maxit*(|V|^2+|E|)), this is the case when all vertices happen to be
+ * in the same grid cell.
+ */
+
+int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res,
+                      igraph_integer_t maxit, igraph_real_t maxdelta,
+                      igraph_real_t area, igraph_real_t coolexp,
+                      igraph_real_t repulserad, igraph_real_t cellsize,
+                      igraph_integer_t proot) {
+
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_t mst;
+    long int root;
+    long int no_of_layers, actlayer = 0;
+    igraph_vector_t vids;
+    igraph_vector_t layers;
+    igraph_vector_t parents;
+    igraph_vector_t edges;
+    igraph_2dgrid_t grid;
+    igraph_vector_t eids;
+    igraph_vector_t forcex;
+    igraph_vector_t forcey;
+
+    igraph_real_t frk = sqrt(area / no_of_nodes);
+    igraph_real_t H_n = 0;
+
+    IGRAPH_CHECK(igraph_minimum_spanning_tree_unweighted(graph, &mst));
+    IGRAPH_FINALLY(igraph_destroy, &mst);
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
+
+    /* Determine the root vertex, random pick right now */
+    if (proot < 0) {
+        root = RNG_INTEGER(0, no_of_nodes - 1);
+    } else {
+        root = proot;
+    }
+
+    /* Assign the layers */
+    IGRAPH_VECTOR_INIT_FINALLY(&vids, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&layers, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&parents, 0);
+    IGRAPH_CHECK(igraph_i_bfs(&mst, (igraph_integer_t) root, IGRAPH_ALL, &vids,
+                              &layers, &parents));
+    no_of_layers = igraph_vector_size(&layers) - 1;
+
+    /* We don't need the mst any more */
+    igraph_destroy(&mst);
+    igraph_empty(&mst, 0, IGRAPH_UNDIRECTED); /* to make finalization work */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges));
+    IGRAPH_VECTOR_INIT_FINALLY(&eids, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&forcex, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&forcey, no_of_nodes);
+
+    /* Place the vertices randomly */
+    IGRAPH_CHECK(igraph_layout_random(graph, res));
+    igraph_matrix_scale(res, 1e6);
+
+    /* This is the grid for calculating the vertices near to a given vertex */
+    IGRAPH_CHECK(igraph_2dgrid_init(&grid, res,
+                                    -sqrt(area / M_PI), sqrt(area / M_PI), cellsize,
+                                    -sqrt(area / M_PI), sqrt(area / M_PI), cellsize));
+    IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid);
+
+    /* Place the root vertex */
+    igraph_2dgrid_add(&grid, root, 0, 0);
+
+    for (actlayer = 1; actlayer < no_of_layers; actlayer++) {
+        H_n += 1.0 / actlayer;
+    }
+
+    for (actlayer = 1; actlayer < no_of_layers; actlayer++) {
+
+        igraph_real_t c = 1;
+        long int i, j;
+        igraph_real_t massx, massy;
+        igraph_real_t px, py;
+        igraph_real_t sx, sy;
+
+        long int it = 0;
+        igraph_real_t epsilon = 10e-6;
+        igraph_real_t maxchange = epsilon + 1;
+        long int pairs;
+        igraph_real_t sconst = sqrt(area / M_PI) / H_n;
+        igraph_2dgrid_iterator_t vidit;
+
+        /*     printf("Layer %li:\n", actlayer); */
+
+        /*-----------------------------------------*/
+        /* Step 1: place the next layer on spheres */
+        /*-----------------------------------------*/
+
+        RNG_BEGIN();
+
+        j = (long int) VECTOR(layers)[actlayer];
+        for (i = (long int) VECTOR(layers)[actlayer - 1];
+             i < VECTOR(layers)[actlayer]; i++) {
+
+            long int vid = (long int) VECTOR(vids)[i];
+            long int par = (long int) VECTOR(parents)[vid];
+            IGRAPH_ALLOW_INTERRUPTION();
+            igraph_2dgrid_getcenter(&grid, &massx, &massy);
+            igraph_i_norm2d(&massx, &massy);
+            px = MATRIX(*res, vid, 0) - MATRIX(*res, par, 0);
+            py = MATRIX(*res, vid, 1) - MATRIX(*res, par, 1);
+            igraph_i_norm2d(&px, &py);
+            sx = c * (massx + px) + MATRIX(*res, vid, 0);
+            sy = c * (massy + py) + MATRIX(*res, vid, 1);
+
+            /* The neighbors of 'vid' */
+            while (j < VECTOR(layers)[actlayer + 1] &&
+                   VECTOR(parents)[(long int)VECTOR(vids)[j]] == vid) {
+                igraph_real_t rx, ry;
+                if (actlayer == 1) {
+                    igraph_real_t phi = 2 * M_PI / (VECTOR(layers)[2] - 1) * (j - 1);
+                    rx = cos(phi);
+                    ry = sin(phi);
+                } else {
+                    rx = RNG_UNIF(-1, 1);
+                    ry = RNG_UNIF(-1, 1);
+                }
+                igraph_i_norm2d(&rx, &ry);
+                rx = rx / actlayer * sconst;
+                ry = ry / actlayer * sconst;
+                igraph_2dgrid_add(&grid, (long int) VECTOR(vids)[j], sx + rx, sy + ry);
+                j++;
+            }
+        }
+
+        RNG_END();
+
+        /*-----------------------------------------*/
+        /* Step 2: add the edges of the next layer */
+        /*-----------------------------------------*/
+
+        for (j = (long int) VECTOR(layers)[actlayer];
+             j < VECTOR(layers)[actlayer + 1]; j++) {
+            long int vid = (long int) VECTOR(vids)[j];
+            long int k;
+            IGRAPH_ALLOW_INTERRUPTION();
+            IGRAPH_CHECK(igraph_incident(graph, &eids, (igraph_integer_t) vid,
+                                         IGRAPH_ALL));
+            for (k = 0; k < igraph_vector_size(&eids); k++) {
+                long int eid = (long int) VECTOR(eids)[k];
+                igraph_integer_t from, to;
+                igraph_edge(graph, (igraph_integer_t) eid, &from, &to);
+                if ((from != vid && igraph_2dgrid_in(&grid, from)) ||
+                    (to   != vid && igraph_2dgrid_in(&grid, to))) {
+                    igraph_vector_push_back(&edges, eid);
+                }
+            }
+        }
+
+        /*-----------------------------------------*/
+        /* Step 3: let the springs spring          */
+        /*-----------------------------------------*/
+
+        maxchange = epsilon + 1;
+        while (it < maxit && maxchange > epsilon) {
+            long int jj;
+            igraph_real_t t = maxdelta * pow((maxit - it) / (double)maxit, coolexp);
+            long int vid, nei;
+
+            IGRAPH_PROGRESS("Large graph layout",
+                            100.0 * ((actlayer - 1.0) / (no_of_layers - 1.0) + ((float)it) / (maxit * (no_of_layers - 1.0))),
+                            0);
+
+            /* init */
+            igraph_vector_null(&forcex);
+            igraph_vector_null(&forcey);
+            maxchange = 0;
+
+            /* attractive "forces" along the edges */
+            for (jj = 0; jj < igraph_vector_size(&edges); jj++) {
+                igraph_integer_t from, to;
+                igraph_real_t xd, yd, dist, force;
+                IGRAPH_ALLOW_INTERRUPTION();
+                igraph_edge(graph, (igraph_integer_t) VECTOR(edges)[jj], &from, &to);
+                xd = MATRIX(*res, (long int)from, 0) - MATRIX(*res, (long int)to, 0);
+                yd = MATRIX(*res, (long int)from, 1) - MATRIX(*res, (long int)to, 1);
+                dist = sqrt(xd * xd + yd * yd);
+                if (dist != 0) {
+                    xd /= dist;
+                    yd /= dist;
+                }
+                force = dist * dist / frk;
+                VECTOR(forcex)[(long int)from] -= xd * force;
+                VECTOR(forcex)[(long int)to]   += xd * force;
+                VECTOR(forcey)[(long int)from] -= yd * force;
+                VECTOR(forcey)[(long int)to]   += yd * force;
+            }
+
+            /* repulsive "forces" of the vertices nearby */
+            pairs = 0;
+            igraph_2dgrid_reset(&grid, &vidit);
+            while ( (vid = igraph_2dgrid_next(&grid, &vidit) - 1) != -1) {
+                while ( (nei = igraph_2dgrid_next_nei(&grid, &vidit) - 1) != -1) {
+                    igraph_real_t xd = MATRIX(*res, (long int)vid, 0) -
+                                       MATRIX(*res, (long int)nei, 0);
+                    igraph_real_t yd = MATRIX(*res, (long int)vid, 1) -
+                                       MATRIX(*res, (long int)nei, 1);
+                    igraph_real_t dist = sqrt(xd * xd + yd * yd);
+                    igraph_real_t force;
+                    if (dist < cellsize) {
+                        pairs++;
+                        if (dist == 0) {
+                            dist = epsilon;
+                        };
+                        xd /= dist; yd /= dist;
+                        force = frk * frk * (1.0 / dist - dist * dist / repulserad);
+                        VECTOR(forcex)[(long int)vid] += xd * force;
+                        VECTOR(forcex)[(long int)nei] -= xd * force;
+                        VECTOR(forcey)[(long int)vid] += yd * force;
+                        VECTOR(forcey)[(long int)nei] -= yd * force;
+                    }
+                }
+            }
+
+            /*       printf("verties: %li iterations: %li\n",  */
+            /*       (long int) VECTOR(layers)[actlayer+1], pairs); */
+
+            /* apply the changes */
+            for (jj = 0; jj < VECTOR(layers)[actlayer + 1]; jj++) {
+                long int vvid = (long int) VECTOR(vids)[jj];
+                igraph_real_t fx = VECTOR(forcex)[vvid];
+                igraph_real_t fy = VECTOR(forcey)[vvid];
+                igraph_real_t ded = sqrt(fx * fx + fy * fy);
+                if (ded > t) {
+                    ded = t / ded;
+                    fx *= ded; fy *= ded;
+                }
+                igraph_2dgrid_move(&grid, vvid, fx, fy);
+                if (fx > maxchange) {
+                    maxchange = fx;
+                }
+                if (fy > maxchange) {
+                    maxchange = fy;
+                }
+            }
+            it++;
+            /*       printf("%li iterations, maxchange: %f\n", it, (double)maxchange); */
+        }
+    }
+
+    IGRAPH_PROGRESS("Large graph layout", 100.0, 0);
+    igraph_destroy(&mst);
+    igraph_vector_destroy(&vids);
+    igraph_vector_destroy(&layers);
+    igraph_vector_destroy(&parents);
+    igraph_vector_destroy(&edges);
+    igraph_2dgrid_destroy(&grid);
+    igraph_vector_destroy(&eids);
+    igraph_vector_destroy(&forcex);
+    igraph_vector_destroy(&forcey);
+    IGRAPH_FINALLY_CLEAN(9);
+    return 0;
+
+}
+
+int igraph_i_layout_reingold_tilford_unreachable(
+    const igraph_t *graph,
+    igraph_neimode_t mode,
+    long int real_root,
+    long int no_of_nodes,
+    igraph_vector_t *pnewedges);
+int igraph_i_layout_reingold_tilford_unreachable(
+    const igraph_t *graph,
+    igraph_neimode_t mode,
+    long int real_root,
+    long int no_of_nodes,
+    igraph_vector_t *pnewedges) {
+
+    long int no_of_newedges;
+    igraph_vector_t visited;
+    long int i, j, n;
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    igraph_adjlist_t allneis;
+    igraph_vector_int_t *neis;
+
+    igraph_vector_resize(pnewedges, 0);
+
+    /* traverse from real_root and see what nodes you cannot reach */
+    no_of_newedges = 0;
+    IGRAPH_VECTOR_INIT_FINALLY(&visited, no_of_nodes);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+
+    /* start from real_root and go BFS */
+    IGRAPH_CHECK(igraph_dqueue_push(&q, real_root));
+    while (!igraph_dqueue_empty(&q)) {
+        long int actnode = (long int) igraph_dqueue_pop(&q);
+        neis = igraph_adjlist_get(&allneis, actnode);
+        n = igraph_vector_int_size(neis);
+        VECTOR(visited)[actnode] = 1;
+        for (j = 0; j < n; j++) {
+            long int neighbor = (long int) VECTOR(*neis)[j];
+            if (!(long int)VECTOR(visited)[neighbor]) {
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+            }
+        }
+    }
+
+    for (j = 0; j < no_of_nodes; j++) {
+        no_of_newedges += 1 - VECTOR(visited)[j];
+    }
+
+    /* if any nodes are unreachable, add edges between them and real_root */
+    if (no_of_newedges != 0) {
+
+        igraph_vector_resize(pnewedges, no_of_newedges * 2);
+        j = 0;
+        for (i = 0; i < no_of_nodes; i++) {
+            if (!VECTOR(visited)[i]) {
+                if (mode != IGRAPH_IN) {
+                    VECTOR(*pnewedges)[2 * j] = real_root;
+                    VECTOR(*pnewedges)[2 * j + 1] = i;
+                } else {
+                    VECTOR(*pnewedges)[2 * j] = i;
+                    VECTOR(*pnewedges)[2 * j + 1] = real_root;
+                }
+                j++;
+            }
+        }
+    }
+
+    igraph_dqueue_destroy(&q);
+    igraph_adjlist_destroy(&allneis);
+    igraph_vector_destroy(&visited);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/* Internal structure for Reingold-Tilford layout */
+struct igraph_i_reingold_tilford_vertex {
+    long int parent;        /* Parent node index */
+    long int level;         /* Level of the node */
+    igraph_real_t offset;     /* X offset from parent node */
+    long int left_contour;  /* Next left node of the contour
+              of the subtree rooted at this node */
+    long int right_contour; /* Next right node of the contour
+              of the subtree rooted at this node */
+    igraph_real_t offset_follow_lc;  /* X offset when following the left contour */
+    igraph_real_t offset_follow_rc;  /* X offset when following the right contour */
+};
+
+int igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata,
+        long int node, long int vcount);
+int igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata,
+        igraph_matrix_t *res, long int node,
+        long int vcount, igraph_real_t xpos);
+
+int igraph_i_layout_reingold_tilford(const igraph_t *graph,
+                                     igraph_matrix_t *res,
+                                     igraph_neimode_t mode,
+                                     long int root);
+int igraph_i_layout_reingold_tilford(const igraph_t *graph,
+                                     igraph_matrix_t *res,
+                                     igraph_neimode_t mode,
+                                     long int root) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, n, j;
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    igraph_adjlist_t allneis;
+    igraph_vector_int_t *neis;
+    struct igraph_i_reingold_tilford_vertex *vdata;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+
+    vdata = igraph_Calloc(no_of_nodes, struct igraph_i_reingold_tilford_vertex);
+    if (vdata == 0) {
+        IGRAPH_ERROR("igraph_layout_reingold_tilford failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, vdata);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        vdata[i].parent = -1;
+        vdata[i].level = -1;
+        vdata[i].offset = 0.0;
+        vdata[i].left_contour = -1;
+        vdata[i].right_contour = -1;
+        vdata[i].offset_follow_lc = 0.0;
+        vdata[i].offset_follow_rc = 0.0;
+    }
+    vdata[root].parent = root;
+    vdata[root].level = 0;
+    MATRIX(*res, root, 1) = 0;
+
+    /* Step 1: assign Y coordinates based on BFS and setup parents vector */
+    IGRAPH_CHECK(igraph_dqueue_push(&q, root));
+    IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
+    while (!igraph_dqueue_empty(&q)) {
+        long int actnode = (long int) igraph_dqueue_pop(&q);
+        long int actdist = (long int) igraph_dqueue_pop(&q);
+        neis = igraph_adjlist_get(&allneis, actnode);
+        n = igraph_vector_int_size(neis);
+
+        for (j = 0; j < n; j++) {
+            long int neighbor = (long int) VECTOR(*neis)[j];
+            if (vdata[neighbor].parent >= 0) {
+                continue;
+            }
+            MATRIX(*res, neighbor, 1) = actdist + 1;
+            IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+            IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+            vdata[neighbor].parent = actnode;
+            vdata[neighbor].level = actdist + 1;
+        }
+    }
+
+    /* Step 2: postorder tree traversal, determines the appropriate X
+     * offsets for every node */
+    igraph_i_layout_reingold_tilford_postorder(vdata, root, no_of_nodes);
+
+    /* Step 3: calculate real coordinates based on X offsets */
+    igraph_i_layout_reingold_tilford_calc_coords(vdata, res, root, no_of_nodes, vdata[root].offset);
+
+    igraph_dqueue_destroy(&q);
+    igraph_adjlist_destroy(&allneis);
+    igraph_free(vdata);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    IGRAPH_PROGRESS("Reingold-Tilford tree layout", 100.0, NULL);
+
+    return 0;
+}
+
+int igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata,
+        igraph_matrix_t *res, long int node,
+        long int vcount, igraph_real_t xpos) {
+    long int i;
+    MATRIX(*res, node, 0) = xpos;
+    for (i = 0; i < vcount; i++) {
+        if (i == node) {
+            continue;
+        }
+        if (vdata[i].parent == node) {
+            igraph_i_layout_reingold_tilford_calc_coords(vdata, res, i, vcount,
+                    xpos + vdata[i].offset);
+        }
+    }
+    return 0;
+}
+
+int igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata,
+        long int node, long int vcount) {
+    long int i, j, childcount, leftroot, leftrootidx;
+    igraph_real_t avg;
+
+    /* printf("Starting visiting node %d\n", node); */
+
+    /* Check whether this node is a leaf node */
+    childcount = 0;
+    for (i = 0; i < vcount; i++) {
+        if (i == node) {
+            continue;
+        }
+        if (vdata[i].parent == node) {
+            /* Node i is a child, so visit it recursively */
+            childcount++;
+            igraph_i_layout_reingold_tilford_postorder(vdata, i, vcount);
+        }
+    }
+
+    if (childcount == 0) {
+        return 0;
+    }
+
+    /* Here we can assume that all of the subtrees have been placed and their
+     * left and right contours are calculated. Let's place them next to each
+     * other as close as we can.
+     * We will take each subtree in an arbitrary order. The root of the
+     * first one will be placed at offset 0, the next ones will be placed
+     * as close to each other as possible. leftroot stores the root of the
+     * rightmost subtree of the already placed subtrees - its right contour
+     * will be checked against the left contour of the next subtree */
+    leftroot = leftrootidx = -1;
+    avg = 0.0;
+    /*printf("Visited node %d and arranged its subtrees\n", node);*/
+    for (i = 0, j = 0; i < vcount; i++) {
+        if (i == node) {
+            continue;
+        }
+        if (vdata[i].parent == node) {
+            /*printf("  Placing child %d on level %d\n", i, vdata[i].level);*/
+            if (leftroot >= 0) {
+                /* Now we will follow the right contour of leftroot and the
+                 * left contour of the subtree rooted at i */
+                long lnode, rnode;
+                igraph_real_t loffset, roffset, minsep, rootsep;
+                lnode = leftroot; rnode = i;
+                minsep = 1;
+                rootsep = vdata[leftroot].offset + minsep;
+                loffset = 0; roffset = minsep;
+                /*printf("    Contour: [%d, %d], offsets: [%lf, %lf], rootsep: %lf\n",
+                       lnode, rnode, loffset, roffset, rootsep);*/
+                while ((lnode >= 0) && (rnode >= 0)) {
+                    /* Step to the next level on the right contour of the left subtree */
+                    if (vdata[lnode].right_contour >= 0) {
+                        loffset += vdata[lnode].offset_follow_rc;
+                        lnode = vdata[lnode].right_contour;
+                    } else {
+                        /* Left subtree ended there. The right contour of the left subtree
+                         * will continue to the next step on the right subtree. */
+                        if (vdata[rnode].left_contour >= 0) {
+                            /*printf("      Left subtree ended, continuing left subtree's left and right contour on right subtree (node %ld)\n", vdata[rnode].left_contour);*/
+                            vdata[lnode].left_contour = vdata[rnode].left_contour;
+                            vdata[lnode].right_contour = vdata[rnode].left_contour;
+                            vdata[lnode].offset_follow_lc = vdata[lnode].offset_follow_rc =
+                                                                (roffset - loffset) + vdata[rnode].offset_follow_lc;
+                            /*printf("      vdata[lnode].offset_follow_* = %.4f\n", vdata[lnode].offset_follow_lc);*/
+                        }
+                        lnode = -1;
+                    }
+                    /* Step to the next level on the left contour of the right subtree */
+                    if (vdata[rnode].left_contour >= 0) {
+                        roffset += vdata[rnode].offset_follow_lc;
+                        rnode = vdata[rnode].left_contour;
+                    } else {
+                        /* Right subtree ended here. The left contour of the right
+                         * subtree will continue to the next step on the left subtree.
+                         * Note that lnode has already been advanced here */
+                        if (lnode >= 0) {
+                            /*printf("      Right subtree ended, continuing right subtree's left and right contour on left subtree (node %ld)\n", lnode);*/
+                            vdata[rnode].left_contour = lnode;
+                            vdata[rnode].right_contour = lnode;
+                            vdata[rnode].offset_follow_lc = vdata[rnode].offset_follow_rc =
+                                                                (loffset - roffset); /* loffset has also been increased earlier */
+                            /*printf("      vdata[rnode].offset_follow_* = %.4f\n", vdata[rnode].offset_follow_lc);*/
+                        }
+                        rnode = -1;
+                    }
+                    /*printf("    Contour: [%d, %d], offsets: [%lf, %lf], rootsep: %lf\n",
+                           lnode, rnode, loffset, roffset, rootsep);*/
+
+                    /* Push subtrees away if necessary */
+                    if ((lnode >= 0) && (rnode >= 0) && (roffset - loffset < minsep)) {
+                        /*printf("    Pushing right subtree away by %lf\n", minsep-roffset+loffset);*/
+                        rootsep += minsep - roffset + loffset;
+                        roffset = loffset + minsep;
+                    }
+                }
+
+                /*printf("  Offset of subtree with root node %d will be %lf\n", i, rootsep);*/
+                vdata[i].offset = rootsep;
+                vdata[node].right_contour = i;
+                vdata[node].offset_follow_rc = rootsep;
+                avg = (avg * j) / (j + 1) + rootsep / (j + 1);
+                leftrootidx = j;
+                leftroot = i;
+            } else {
+                leftrootidx = j;
+                leftroot = i;
+                vdata[node].left_contour = i;
+                vdata[node].right_contour = i;
+                vdata[node].offset_follow_lc = 0.0;
+                vdata[node].offset_follow_rc = 0.0;
+                avg = vdata[i].offset;
+            }
+            j++;
+        }
+    }
+    /*printf("Shifting node to be centered above children. Shift amount: %lf\n", avg);*/
+    vdata[node].offset_follow_lc -= avg;
+    vdata[node].offset_follow_rc -= avg;
+    for (i = 0, j = 0; i < vcount; i++) {
+        if (i == node) {
+            continue;
+        }
+        if (vdata[i].parent == node) {
+            vdata[i].offset -= avg;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_layout_reingold_tilford
+ * \brief Reingold-Tilford layout for tree graphs
+ *
+ * </para><para>
+ * Arranges the nodes in a tree where the given node is used as the root.
+ * The tree is directed downwards and the parents are centered above its
+ * children. For the exact algorithm, see:
+ *
+ * </para><para>
+ * Reingold, E and Tilford, J: Tidier drawing of trees.
+ * IEEE Trans. Softw. Eng., SE-7(2):223--228, 1981
+ *
+ * </para><para>
+ * If the given graph is not a tree, a breadth-first search is executed
+ * first to obtain a possible spanning tree.
+ *
+ * \param graph The graph object.
+ * \param res The result, the coordinates in a matrix. The parameter
+ *   should point to an initialized matrix object and will be resized.
+ * \param mode Specifies which edges to consider when building the tree.
+ *   If it is \c IGRAPH_OUT then only the outgoing, if it is \c IGRAPH_IN
+ *   then only the incoming edges of a parent are considered. If it is
+ *   \c IGRAPH_ALL then all edges are used (this was the behavior in
+ *   igraph 0.5 and before). This parameter also influences how the root
+ *   vertices are calculated, if they are not given. See the \p roots parameter.
+ * \param roots The index of the root vertex or root vertices.
+ *   If this is a non-empty vector then the supplied vertex ids are used
+ *   as the roots of the trees (or a single tree if the graph is connected).
+ *   If it is a null pointer of a pointer to an empty vector, then the root
+ *   vertices are automatically calculated based on topological sorting,
+ *   performed with the opposite mode than the \p mode argument.
+ *   After the vertices have been sorted, one is selected from each component.
+ * \param rootlevel This argument can be useful when drawing forests which are
+ *   not trees (i.e. they are unconnected and have tree components). It specifies
+ *   the level of the root vertices for every tree in the forest. It is only
+ *   considered if not a null pointer and the \p roots argument is also given
+ *   (and it is not a null pointer of an empty vector).
+ * \return Error code.
+ *
+ * Added in version 0.2.
+ *
+ * \sa \ref igraph_layout_reingold_tilford_circular().
+ *
+ * \example examples/simple/igraph_layout_reingold_tilford.c
+ */
+
+int igraph_layout_reingold_tilford(const igraph_t *graph,
+                                   igraph_matrix_t *res,
+                                   igraph_neimode_t mode,
+                                   const igraph_vector_t *roots,
+                                   const igraph_vector_t *rootlevel) {
+
+    long int no_of_nodes_orig = igraph_vcount(graph);
+    long int no_of_nodes = no_of_nodes_orig;
+    long int real_root;
+    igraph_t extended;
+    const igraph_t *pextended = graph;
+    igraph_vector_t myroots;
+    const igraph_vector_t *proots = roots;
+    igraph_neimode_t mode2;
+    long int i;
+    igraph_vector_t newedges;
+
+    /* TODO: possible speedup could be achieved if we use a table for storing
+     * the children of each node in the tree. (Now the implementation uses a
+     * single array containing the parent of each node and a node's children
+     * are determined by looking for other nodes that have this node as parent)
+     */
+
+    /* at various steps it might be necessary to add edges to the graph */
+    IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0);
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    if ( (!roots || igraph_vector_size(roots) == 0) &&
+         rootlevel && igraph_vector_size(rootlevel) != 0 ) {
+        IGRAPH_WARNING("Reingold-Tilford layout: 'rootlevel' ignored");
+    }
+
+    /* ----------------------------------------------------------------------- */
+    /* If root vertices are not given, then do a topological sort and take
+       the last element from every component for directed graphs and mode == out,
+       or the first element from every component for directed graphs and mode ==
+       in,or select the vertex with the maximum degree from each component for
+       undirected graphs */
+
+    if (!roots || igraph_vector_size(roots) == 0) {
+
+        igraph_vector_t order, membership;
+        igraph_integer_t no_comps;
+        long int i, noseen = 0;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&myroots, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
+        IGRAPH_VECTOR_INIT_FINALLY(&membership, no_of_nodes);
+
+        if (mode != IGRAPH_ALL) {
+            /* look for roots by swimming against the stream */
+            mode2 = (mode == IGRAPH_IN) ? IGRAPH_OUT : IGRAPH_IN;
+
+            IGRAPH_CHECK(igraph_topological_sorting(graph, &order, mode2));
+            IGRAPH_CHECK(igraph_clusters(graph, &membership, /*csize=*/ 0,
+                                         &no_comps, IGRAPH_WEAK));
+        } else {
+            IGRAPH_CHECK(igraph_sort_vertex_ids_by_degree(graph, &order,
+                         igraph_vss_all(), IGRAPH_ALL, 0, IGRAPH_ASCENDING, 0));
+            IGRAPH_CHECK(igraph_clusters(graph, &membership, /*csize=*/ 0,
+                                         &no_comps, IGRAPH_WEAK));
+        }
+
+        IGRAPH_CHECK(igraph_vector_resize(&myroots, no_comps));
+
+        /* go backwards and fill the roots vector with indices [1, no_of_nodes]
+           The index 0 is used to signal this root has not been found yet:
+           all indices are then decreased by one to [0, no_of_nodes - 1] */
+        igraph_vector_null(&myroots);
+        proots = &myroots;
+        for (i = no_of_nodes - 1; noseen < no_comps && i >= 0; i--) {
+            long int v = (long int) VECTOR(order)[i];
+            long int mem = (long int) VECTOR(membership)[v];
+            if (VECTOR(myroots)[mem] == 0) {
+                noseen += 1;
+                VECTOR(myroots)[mem] = v + 1;
+            }
+        }
+        for (i = 0; i < no_comps; i++) {
+            VECTOR(myroots)[i] -= 1;
+        }
+
+        igraph_vector_destroy(&membership);
+        igraph_vector_destroy(&order);
+        IGRAPH_FINALLY_CLEAN(2);
+
+    } else if (rootlevel && igraph_vector_size(rootlevel) > 0 &&
+               igraph_vector_size(roots) > 1) {
+
+        /* ----------------------------------------------------------------------- */
+        /* Many roots were given to us, check 'rootlevel' */
+
+        long int plus_levels = 0;
+        long int i;
+
+        if (igraph_vector_size(roots) != igraph_vector_size(rootlevel)) {
+            IGRAPH_ERROR("Reingold-Tilford: 'roots' and 'rootlevel' lengths differ",
+                         IGRAPH_EINVAL);
+        }
+
+        /* count the rootlevels that are not zero */
+        for (i = 0; i < igraph_vector_size(roots); i++) {
+            plus_levels += VECTOR(*rootlevel)[i];
+        }
+
+        /* make copy of graph, add vertices/edges */
+        if (plus_levels != 0) {
+            long int edgeptr = 0;
+
+            pextended = &extended;
+            IGRAPH_CHECK(igraph_copy(&extended, graph));
+            IGRAPH_FINALLY(igraph_destroy, &extended);
+            IGRAPH_CHECK(igraph_add_vertices(&extended,
+                                             (igraph_integer_t) plus_levels, 0));
+
+            igraph_vector_resize(&newedges, plus_levels * 2);
+
+            for (i = 0; i < igraph_vector_size(roots); i++) {
+                long int rl = (long int) VECTOR(*rootlevel)[i];
+                long int rn = (long int) VECTOR(*roots)[i];
+                long int j;
+
+                /* zero-level roots don't get anything special */
+                if (rl == 0) {
+                    continue;
+                }
+
+                /* for each nonzero-level root, add vertices
+                   and edges at all levels [1, 2, .., rl]
+                   piercing through the graph. If mode=="in"
+                   they pierce the other way */
+                if (mode != IGRAPH_IN) {
+                    VECTOR(newedges)[edgeptr++] = no_of_nodes;
+                    VECTOR(newedges)[edgeptr++] = rn;
+                    for (j = 0; j < rl - 1; j++) {
+                        VECTOR(newedges)[edgeptr++] = no_of_nodes + 1;
+                        VECTOR(newedges)[edgeptr++] = no_of_nodes;
+                        no_of_nodes++;
+                    }
+                } else {
+                    VECTOR(newedges)[edgeptr++] = rn;
+                    VECTOR(newedges)[edgeptr++] = no_of_nodes;
+                    for (j = 0; j < rl - 1; j++) {
+                        VECTOR(newedges)[edgeptr++] = no_of_nodes;
+                        VECTOR(newedges)[edgeptr++] = no_of_nodes + 1;
+                        no_of_nodes++;
+                    }
+                }
+
+                /* move on to the next root */
+                VECTOR(*roots)[i] = no_of_nodes++;
+            }
+
+            /* actually add the edges to the graph */
+            IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0));
+        }
+    }
+
+    /* We have root vertices now. If one or more nonzero-level roots were
+       chosen by the user, we have copied the graph and added a few vertices
+       and (directed) edges to connect those floating roots to nonfloating,
+       zero-level equivalent roots.
+
+       Below, the function
+
+       igraph_i_layout_reingold_tilford(pextended, res, mode, real_root)
+
+       calculates the actual rt coordinates of the graph. However, for
+       simplicity that function requires a connected graph and a single root.
+       For directed graphs, it needs not be strongly connected, however all
+       nodes must be reachable from the root following the stream (i.e. the
+       root must be a "mother vertex").
+
+       So before we call that function we have to make sure the (copied) graph
+       satisfies that condition. That requires:
+         1. if there is more than one root, defining a single real_root
+         2. if a real_root is defined, adding edges to connect all roots to it
+         3. ensure real_root is mother of the whole graph. If it is not,
+            add shortcut edges from real_root to any disconnected node for now.
+
+      NOTE: 3. could be done better, e.g. by topological sorting of some kind.
+      But for now it's ok like this.
+    */
+    /* if there is only one root, no need for real_root */
+    if (igraph_vector_size(proots) == 1) {
+        real_root = (long int) VECTOR(*proots)[0];
+        if (real_root < 0 || real_root >= no_of_nodes) {
+            IGRAPH_ERROR("invalid vertex id", IGRAPH_EINVVID);
+        }
+
+        /* else, we need to make real_root */
+    } else {
+        long int no_of_newedges;
+
+        /* Make copy of the graph unless it exists already */
+        if (pextended == graph) {
+            pextended = &extended;
+            IGRAPH_CHECK(igraph_copy(&extended, graph));
+            IGRAPH_FINALLY(igraph_destroy, &extended);
+        }
+
+        /* add real_root to the vertices */
+        real_root = no_of_nodes;
+        IGRAPH_CHECK(igraph_add_vertices(&extended, 1, 0));
+        no_of_nodes++;
+
+        /* add edges from the roots to real_root */
+        no_of_newedges = igraph_vector_size(proots);
+        igraph_vector_resize(&newedges, no_of_newedges * 2);
+        for (i = 0; i < no_of_newedges; i++) {
+            VECTOR(newedges)[2 * i] = no_of_nodes - 1;
+            VECTOR(newedges)[2 * i + 1] = VECTOR(*proots)[i];
+        }
+
+        IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0));
+    }
+
+    /* prepare edges to unreachable parts of the graph */
+    IGRAPH_CHECK(igraph_i_layout_reingold_tilford_unreachable(pextended, mode, real_root, no_of_nodes, &newedges));
+
+    if (igraph_vector_size(&newedges) != 0) {
+        /* Make copy of the graph unless it exists already */
+        if (pextended == graph) {
+            pextended = &extended;
+            IGRAPH_CHECK(igraph_copy(&extended, graph));
+            IGRAPH_FINALLY(igraph_destroy, &extended);
+        }
+
+        IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0));
+    }
+    igraph_vector_destroy(&newedges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* ----------------------------------------------------------------------- */
+    /* Layout */
+    IGRAPH_CHECK(igraph_i_layout_reingold_tilford(pextended, res, mode, real_root));
+
+    /* Remove the new vertices from the layout */
+    if (no_of_nodes != no_of_nodes_orig) {
+        if (no_of_nodes - 1 == no_of_nodes_orig) {
+            IGRAPH_CHECK(igraph_matrix_remove_row(res, no_of_nodes_orig));
+        } else {
+            igraph_matrix_t tmp;
+            long int i;
+            IGRAPH_MATRIX_INIT_FINALLY(&tmp, no_of_nodes_orig, 2);
+            for (i = 0; i < no_of_nodes_orig; i++) {
+                MATRIX(tmp, i, 0) = MATRIX(*res, i, 0);
+                MATRIX(tmp, i, 1) = MATRIX(*res, i, 1);
+            }
+            IGRAPH_CHECK(igraph_matrix_update(res, &tmp));
+            igraph_matrix_destroy(&tmp);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    if (pextended != graph) {
+        igraph_destroy(&extended);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* Remove the roots vector if it was created by us */
+    if (proots != roots) {
+        igraph_vector_destroy(&myroots);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_layout_reingold_tilford_circular
+ * \brief Circular Reingold-Tilford layout for trees
+ *
+ * </para><para>
+ * This layout is almost the same as \ref igraph_layout_reingold_tilford(), but
+ * the tree is drawn in a circular way, with the root vertex in the center.
+ *
+ * \param graph The graph object.
+ * \param res The result, the coordinates in a matrix. The parameter
+ *   should point to an initialized matrix object and will be resized.
+ * \param mode Specifies which edges to consider when building the tree.
+ *   If it is \c IGRAPH_OUT then only the outgoing, if it is \c IGRAPH_IN
+ *   then only the incoming edges of a parent are considered. If it is
+ *   \c IGRAPH_ALL then all edges are used (this was the behavior in
+ *   igraph 0.5 and before). This parameter also influences how the root
+ *   vertices are calculated, if they are not given. See the \p roots parameter.
+ * \param roots The index of the root vertex or root vertices.
+ *   If this is a non-empty vector then the supplied vertex ids are used
+ *   as the roots of the trees (or a single tree if the graph is connected).
+ *   If it is a null pointer of a pointer to an empty vector, then the root
+ *   vertices are automatically calculated based on topological sorting,
+ *   performed with the opposite mode than the \p mode argument.
+ *   After the vertices have been sorted, one is selected from each component.
+ * \param rootlevel This argument can be useful when drawing forests which are
+ *   not trees (i.e. they are unconnected and have tree components). It specifies
+ *   the level of the root vertices for every tree in the forest. It is only
+ *   considered if not a null pointer and the \p roots argument is also given
+ *   (and it is not a null pointer of an empty vector). Note that if you supply
+ *   a null pointer here and the graph has multiple components, all of the root
+ *   vertices will be mapped to the origin of the coordinate system, which does
+ *   not really make sense.
+ * \return Error code.
+ *
+ * \sa \ref igraph_layout_reingold_tilford().
+ */
+
+int igraph_layout_reingold_tilford_circular(const igraph_t *graph,
+        igraph_matrix_t *res,
+        igraph_neimode_t mode,
+        const igraph_vector_t *roots,
+        const igraph_vector_t *rootlevel) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+    igraph_real_t ratio = 2 * M_PI * (no_of_nodes - 1.0) / no_of_nodes;
+    igraph_real_t minx, maxx;
+
+    IGRAPH_CHECK(igraph_layout_reingold_tilford(graph, res, mode, roots, rootlevel));
+
+    if (no_of_nodes == 0) {
+        return 0;
+    }
+
+    minx = maxx = MATRIX(*res, 0, 0);
+    for (i = 1; i < no_of_nodes; i++) {
+        if (MATRIX(*res, i, 0) > maxx) {
+            maxx = MATRIX(*res, i, 0);
+        }
+        if (MATRIX(*res, i, 0) < minx) {
+            minx = MATRIX(*res, i, 0);
+        }
+    }
+    if (maxx > minx) {
+        ratio /= (maxx - minx);
+    }
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_real_t phi = (MATRIX(*res, i, 0) - minx) * ratio;
+        igraph_real_t r = MATRIX(*res, i, 1);
+        MATRIX(*res, i, 0) = r * cos(phi);
+        MATRIX(*res, i, 1) = r * sin(phi);
+    }
+
+    return 0;
+}
+
+#define COULOMBS_CONSTANT 8987500000.0
+
+
+igraph_real_t igraph_i_distance_between(const igraph_matrix_t *c, long int a,
+                                        long int b);
+
+int igraph_i_determine_electric_axal_forces(const igraph_matrix_t *pos,
+        igraph_real_t *x,
+        igraph_real_t *y,
+        igraph_real_t directed_force,
+        igraph_real_t distance,
+        long int other_node,
+        long int this_node);
+
+int igraph_i_apply_electrical_force(const igraph_matrix_t *pos,
+                                    igraph_vector_t *pending_forces_x,
+                                    igraph_vector_t *pending_forces_y,
+                                    long int other_node, long int this_node,
+                                    igraph_real_t node_charge,
+                                    igraph_real_t distance);
+
+int igraph_i_determine_spring_axal_forces(const igraph_matrix_t *pos,
+        igraph_real_t *x, igraph_real_t *y,
+        igraph_real_t directed_force,
+        igraph_real_t distance,
+        int spring_length,
+        long int other_node,
+        long int this_node);
+
+int igraph_i_apply_spring_force(const igraph_matrix_t *pos,
+                                igraph_vector_t *pending_forces_x,
+                                igraph_vector_t *pending_forces_y,
+                                long int other_node,
+                                long int this_node, int spring_length,
+                                igraph_real_t spring_constant);
+
+int igraph_i_move_nodes(igraph_matrix_t *pos,
+                        const igraph_vector_t *pending_forces_x,
+                        const igraph_vector_t *pending_forces_y,
+                        igraph_real_t node_mass,
+                        igraph_real_t max_sa_movement);
+
+igraph_real_t igraph_i_distance_between(const igraph_matrix_t *c, long int a,
+                                        long int b) {
+    igraph_real_t diffx = MATRIX(*c, a, 0) - MATRIX(*c, b, 0);
+    igraph_real_t diffy = MATRIX(*c, a, 1) - MATRIX(*c, b, 1);
+    return sqrt( diffx * diffx + diffy * diffy );
+}
+
+int igraph_i_determine_electric_axal_forces(const igraph_matrix_t *pos,
+        igraph_real_t *x,
+        igraph_real_t *y,
+        igraph_real_t directed_force,
+        igraph_real_t distance,
+        long int other_node,
+        long int this_node) {
+
+    // We know what the directed force is.  We now need to translate it
+    // into the appropriate x and y components.
+    // First, assume:
+    //                 other_node
+    //                    /|
+    //  directed_force  /  |
+    //                /    | y
+    //              /______|
+    //    this_node     x
+    //
+    // other_node.x > this_node.x
+    // other_node.y > this_node.y
+    // the force will be on this_node away from other_node
+
+    // the proportion (distance/y_distance) is equal to the proportion
+    // (directed_force/y_force), as the two triangles are similar.
+    // therefore, the magnitude of y_force = (directed_force*y_distance)/distance
+    // the sign of y_force is negative, away from other_node
+
+    igraph_real_t x_distance, y_distance;
+    y_distance = MATRIX(*pos, other_node, 1) - MATRIX(*pos, this_node, 1);
+    if (y_distance < 0) {
+        y_distance = -y_distance;
+    }
+    *y = -1 * ((directed_force * y_distance) / distance);
+
+    // the x component works in exactly the same way.
+    x_distance = MATRIX(*pos, other_node, 0) - MATRIX(*pos, this_node, 0);
+    if (x_distance < 0) {
+        x_distance = -x_distance;
+    }
+    *x = -1 * ((directed_force * x_distance) / distance);
+
+    // Now we need to reverse the polarity of our answers based on the falsness
+    // of our assumptions.
+    if (MATRIX(*pos, other_node, 0) < MATRIX(*pos, this_node, 0)) {
+        *x = *x * -1;
+    }
+    if (MATRIX(*pos, other_node, 1) < MATRIX(*pos, this_node, 1)) {
+        *y = *y * -1;
+    }
+
+    return 0;
+}
+
+int igraph_i_apply_electrical_force(const igraph_matrix_t *pos,
+                                    igraph_vector_t *pending_forces_x,
+                                    igraph_vector_t *pending_forces_y,
+                                    long int other_node, long int this_node,
+                                    igraph_real_t node_charge,
+                                    igraph_real_t distance) {
+
+    igraph_real_t directed_force = COULOMBS_CONSTANT *
+                                   ((node_charge * node_charge) / (distance * distance));
+
+    igraph_real_t x_force, y_force;
+    igraph_i_determine_electric_axal_forces(pos, &x_force, &y_force,
+                                            directed_force, distance,
+                                            other_node, this_node);
+
+    VECTOR(*pending_forces_x)[this_node] += x_force;
+    VECTOR(*pending_forces_y)[this_node] += y_force;
+    VECTOR(*pending_forces_x)[other_node] -= x_force;
+    VECTOR(*pending_forces_y)[other_node] -= y_force;
+
+    return 0;
+}
+
+int igraph_i_determine_spring_axal_forces(const igraph_matrix_t *pos,
+        igraph_real_t *x, igraph_real_t *y,
+        igraph_real_t directed_force,
+        igraph_real_t distance,
+        int spring_length,
+        long int other_node, long int this_node) {
+
+    // if the spring is just the right size, the forces will be 0, so we can
+    // skip the computation.
+    //
+    // if the spring is too long, our forces will be identical to those computed
+    // by determine_electrical_axal_forces() (this_node will be pulled toward
+    // other_node).
+    //
+    // if the spring is too short, our forces will be the opposite of those
+    // computed by determine_electrical_axal_forces() (this_node will be pushed
+    // away from other_node)
+    //
+    // finally, since both nodes are movable, only one-half of the total force
+    // should be applied to each node, so half the forces for our answer.
+
+    if (distance == spring_length) {
+        *x = 0.0;
+        *y = 0.0;
+    } else {
+        igraph_i_determine_electric_axal_forces(pos, x, y, directed_force, distance,
+                                                other_node, this_node);
+        if (distance < spring_length) {
+            *x = -1 * *x;
+            *y = -1 * *y;
+        }
+        *x = 0.5 * *x;
+        *y = 0.5 * *y;
+    }
+
+    return 0;
+}
+
+int igraph_i_apply_spring_force(const igraph_matrix_t *pos,
+                                igraph_vector_t *pending_forces_x,
+                                igraph_vector_t *pending_forces_y,
+                                long int other_node,
+                                long int this_node, int spring_length,
+                                igraph_real_t spring_constant) {
+
+    // determined using Hooke's Law:
+    //   force = -kx
+    // where:
+    //   k = spring constant
+    //   x = displacement from ideal length in meters
+
+    igraph_real_t distance, displacement, directed_force, x_force, y_force;
+    distance = igraph_i_distance_between(pos, other_node, this_node);
+    // let's protect ourselves from division by zero by ignoring two nodes that
+    // happen to be in the same place.  Since we separate all nodes before we
+    // work on any of them, this will only happen in extremely rare circumstances,
+    // and when it does, electrical force will probably push one or both of them
+    // one way or another anyway.
+    if (distance == 0.0) {
+        return 0;
+    }
+
+    displacement = distance - spring_length;
+    if (displacement < 0) {
+        displacement = -displacement;
+    }
+    directed_force = -1 * spring_constant * displacement;
+    // remember, this is force directed away from the spring;
+    // a negative number is back towards the spring (or, in our case, back towards
+    // the other node)
+
+    // get the force that should be applied to >this< node
+    igraph_i_determine_spring_axal_forces(pos, &x_force, &y_force,
+                                          directed_force, distance, spring_length,
+                                          other_node, this_node);
+
+    VECTOR(*pending_forces_x)[this_node] += x_force;
+    VECTOR(*pending_forces_y)[this_node] += y_force;
+    VECTOR(*pending_forces_x)[other_node] -= x_force;
+    VECTOR(*pending_forces_y)[other_node] -= y_force;
+
+    return 0;
+}
+
+int igraph_i_move_nodes(igraph_matrix_t *pos,
+                        const igraph_vector_t *pending_forces_x,
+                        const igraph_vector_t *pending_forces_y,
+                        igraph_real_t node_mass,
+                        igraph_real_t max_sa_movement) {
+
+    // Since each iteration is isolated, time is constant at 1.
+    // Therefore:
+    //   Force effects acceleration.
+    //   acceleration (d(velocity)/time) = velocity
+    //   velocity (d(displacement)/time) = displacement
+    //   displacement = acceleration
+
+    // determined using Newton's second law:
+    //   sum(F) = ma
+    // therefore:
+    //   acceleration = force / mass
+    //   velocity     = force / mass
+    //   displacement = force / mass
+
+    long int this_node, no_of_nodes = igraph_vector_size(pending_forces_x);
+
+    for (this_node = 0; this_node < no_of_nodes; this_node++) {
+
+        igraph_real_t x_movement, y_movement;
+
+        x_movement = VECTOR(*pending_forces_x)[this_node] / node_mass;
+        if (x_movement > max_sa_movement) {
+            x_movement = max_sa_movement;
+        } else if (x_movement < -max_sa_movement) {
+            x_movement = -max_sa_movement;
+        }
+
+        y_movement = VECTOR(*pending_forces_y)[this_node] / node_mass;
+        if (y_movement > max_sa_movement) {
+            y_movement = max_sa_movement;
+        } else if (y_movement < -max_sa_movement) {
+            y_movement = -max_sa_movement;
+        }
+
+        MATRIX(*pos, this_node, 0) += x_movement;
+        MATRIX(*pos, this_node, 1) += y_movement;
+
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_layout_graphopt
+ * \brief Optimizes vertex layout via the graphopt algorithm.
+ *
+ * </para><para>
+ * This is a port of the graphopt layout algorithm by Michael Schmuhl.
+ * graphopt version 0.4.1 was rewritten in C and the support for
+ * layers was removed (might be added later) and a code was a bit
+ * reorganized to avoid some unnecessary steps is the node charge (see below)
+ * is zero.
+ *
+ * </para><para>
+ * graphopt uses physical analogies for defining attracting and repelling
+ * forces among the vertices and then the physical system is simulated
+ * until it reaches an equilibrium. (There is no simulated annealing or
+ * anything like that, so a stable fixed point is not guaranteed.)
+ *
+ * </para><para>
+ * See also http://www.schmuhl.org/graphopt/ for the original graphopt.
+ * \param graph The input graph.
+ * \param res Pointer to an initialized matrix, the result will be stored here
+ *    and its initial contents is used the starting point of the simulation
+ *    if the \p use_seed argument is true. Note that in this case the
+ *    matrix should have the proper size, otherwise a warning is issued and
+ *    the supplied values are ignored. If no starting positions are given
+ *    (or they are invalid) then a random staring position is used.
+ *    The matrix will be resized if needed.
+ * \param niter Integer constant, the number of iterations to perform.
+ *    Should be a couple of hundred in general. If you have a large graph
+ *    then you might want to only do a few iterations and then check the
+ *    result. If it is not good enough you can feed it in again in
+ *    the \p res argument. The original graphopt default if 500.
+ * \param node_charge The charge of the vertices, used to calculate electric
+ *    repulsion. The original graphopt default is 0.001.
+ * \param node_mass The mass of the vertices, used for the spring forces.
+ *    The original graphopt defaults to 30.
+ * \param spring_length The length of the springs, an integer number.
+ *    The original graphopt defaults to zero.
+ * \param spring_constant The spring constant, the original graphopt defaults
+ *    to one.
+ * \param max_sa_movement Real constant, it gives the maximum amount of movement
+ *    allowed in a single step along a single axis. The original graphopt
+ *    default is 5.
+ * \param use_seed Logical scalar, whether to use the positions in \p res as
+ *    a starting configuration. See also \p res above.
+ * \return Error code.
+ *
+ * Time complexity: O(n (|V|^2+|E|) ), n is the number of iterations,
+ * |V| is the number of vertices, |E| the number
+ * of edges. If \p node_charge is zero then it is only O(n|E|).
+ */
+
+int igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *res,
+                           igraph_integer_t niter,
+                           igraph_real_t node_charge, igraph_real_t node_mass,
+                           igraph_real_t spring_length,
+                           igraph_real_t spring_constant,
+                           igraph_real_t max_sa_movement,
+                           igraph_bool_t use_seed) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    int my_spring_length = (int) spring_length;
+    igraph_vector_t pending_forces_x, pending_forces_y;
+    /* Set a flag to calculate (or not) the electrical forces that the nodes */
+    /* apply on each other based on if both node types' charges are zero. */
+    igraph_bool_t apply_electric_charges = (node_charge != 0);
+
+    long int this_node, other_node, edge;
+    igraph_real_t distance;
+    long int i;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_x, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_y, no_of_nodes);
+
+    if (use_seed) {
+        if (igraph_matrix_nrow(res) != no_of_nodes ||
+            igraph_matrix_ncol(res) != 2) {
+            IGRAPH_WARNING("Invalid size for initial matrix, starting from random layout");
+            IGRAPH_CHECK(igraph_layout_random(graph, res));
+        }
+    } else {
+        IGRAPH_CHECK(igraph_layout_random(graph, res));
+    }
+
+    IGRAPH_PROGRESS("Graphopt layout", 0, NULL);
+    for (i = niter; i > 0; i--) {
+        /* Report progress in approx. every 100th step */
+        if (i % 10 == 0) {
+            IGRAPH_PROGRESS("Graphopt layout", 100.0 - 100.0 * i / niter, NULL);
+        }
+
+        /* Clear pending forces on all nodes */
+        igraph_vector_null(&pending_forces_x);
+        igraph_vector_null(&pending_forces_y);
+
+        // Apply electrical force applied by all other nodes
+        if (apply_electric_charges) {
+            // Iterate through all nodes
+            for (this_node = 0; this_node < no_of_nodes; this_node++) {
+                IGRAPH_ALLOW_INTERRUPTION();
+                for (other_node = this_node + 1;
+                     other_node < no_of_nodes;
+                     other_node++) {
+                    distance = igraph_i_distance_between(res, this_node, other_node);
+                    // let's protect ourselves from division by zero by ignoring
+                    // two nodes that happen to be in the same place.  Since we
+                    // separate all nodes before we work on any of them, this
+                    // will only happen in extremely rare circumstances, and when
+                    // it does, springs will probably pull them apart anyway.
+                    // also, if we are more than 50 away, the electric force
+                    // will be negligible.
+                    // ***** may not always be desirable ****
+                    if ((distance != 0.0) && (distance < 500.0)) {
+                        //    if (distance != 0.0) {
+                        // Apply electrical force from node(counter2) on
+                        // node(counter)
+                        igraph_i_apply_electrical_force(res, &pending_forces_x,
+                                                        &pending_forces_y,
+                                                        other_node, this_node,
+                                                        node_charge,
+                                                        distance);
+                    }
+                }
+            }
+        }
+
+        // Apply force from springs
+        for (edge = 0; edge < no_of_edges; edge++) {
+            long int tthis_node = IGRAPH_FROM(graph, edge);
+            long int oother_node = IGRAPH_TO(graph, edge);
+            // Apply spring force on both nodes
+            igraph_i_apply_spring_force(res, &pending_forces_x, &pending_forces_y,
+                                        oother_node, tthis_node, my_spring_length,
+                                        spring_constant);
+        }
+
+        // Effect the movement of the nodes based on all pending forces
+        igraph_i_move_nodes(res, &pending_forces_x, &pending_forces_y, node_mass,
+                            max_sa_movement);
+    }
+    IGRAPH_PROGRESS("Graphopt layout", 100, NULL);
+
+    igraph_vector_destroy(&pending_forces_y);
+    igraph_vector_destroy(&pending_forces_x);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid,
+                              long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r,
+                              igraph_real_t cx, igraph_real_t cy, igraph_real_t startr,
+                              igraph_real_t killr);
+
+int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x,
+                              igraph_real_t *y, igraph_real_t *r);
+int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x,
+                              igraph_real_t *y, igraph_real_t *z,
+                              igraph_real_t *r);
+
+/**
+ * \function igraph_layout_merge_dla
+ * \brief Merge multiple layouts by using a DLA algorithm
+ *
+ * </para><para>
+ * First each layout is covered by a circle. Then the layout of the
+ * largest graph is placed at the origin. Then the other layouts are
+ * placed by the DLA algorithm, larger ones first and smaller ones
+ * last.
+ * \param thegraphs Pointer vector containing the graph object of
+ *        which the layouts will be merged.
+ * \param coords Pointer vector containing matrix objects with the 2d
+ *        layouts of the graphs in \p thegraphs.
+ * \param res Pointer to an initialized matrix object, the result will
+ *        be stored here. It will be resized if needed.
+ * \return Error code.
+ *
+ * Added in version 0.2. This function is experimental.
+ *
+ * </para><para>
+ * Time complexity: TODO.
+ */
+
+int igraph_layout_merge_dla(igraph_vector_ptr_t *thegraphs,
+                            igraph_vector_ptr_t *coords,
+                            igraph_matrix_t *res) {
+    long int graphs = igraph_vector_ptr_size(coords);
+    igraph_vector_t sizes;
+    igraph_vector_t x, y, r;
+    igraph_vector_t nx, ny, nr;
+    long int allnodes = 0;
+    long int i, j;
+    long int actg;
+    igraph_i_layout_mergegrid_t grid;
+    long int jpos = 0;
+    igraph_real_t minx, maxx, miny, maxy;
+    igraph_real_t area = 0;
+    igraph_real_t maxr = 0;
+    long int respos;
+
+    /* Graphs are currently not used, only the coordinates */
+    IGRAPH_UNUSED(thegraphs);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&sizes, graphs);
+    IGRAPH_VECTOR_INIT_FINALLY(&x, graphs);
+    IGRAPH_VECTOR_INIT_FINALLY(&y, graphs);
+    IGRAPH_VECTOR_INIT_FINALLY(&r, graphs);
+    IGRAPH_VECTOR_INIT_FINALLY(&nx, graphs);
+    IGRAPH_VECTOR_INIT_FINALLY(&ny, graphs);
+    IGRAPH_VECTOR_INIT_FINALLY(&nr, graphs);
+
+    RNG_BEGIN();
+
+    for (i = 0; i < igraph_vector_ptr_size(coords); i++) {
+        igraph_matrix_t *mat = VECTOR(*coords)[i];
+        long int size = igraph_matrix_nrow(mat);
+
+        if (igraph_matrix_ncol(mat) != 2) {
+            IGRAPH_ERROR("igraph_layout_merge_dla works for 2D layouts only",
+                         IGRAPH_EINVAL);
+        }
+
+        IGRAPH_ALLOW_INTERRUPTION();
+        allnodes += size;
+        VECTOR(sizes)[i] = size;
+        VECTOR(r)[i] = pow(size, .75);
+        area += VECTOR(r)[i] * VECTOR(r)[i];
+        if (VECTOR(r)[i] > maxr) {
+            maxr = VECTOR(r)[i];
+        }
+
+        igraph_i_layout_sphere_2d(mat,
+                                  igraph_vector_e_ptr(&nx, i),
+                                  igraph_vector_e_ptr(&ny, i),
+                                  igraph_vector_e_ptr(&nr, i));
+
+    }
+    igraph_vector_order2(&sizes); /* largest first */
+
+    /* 0. create grid */
+    minx = miny = -sqrt(5 * area);
+    maxx = maxy = sqrt(5 * area);
+    igraph_i_layout_mergegrid_init(&grid, minx, maxx, 200,
+                                   miny, maxy, 200);
+    IGRAPH_FINALLY(igraph_i_layout_mergegrid_destroy, &grid);
+
+    /*   fprintf(stderr, "Ok, starting DLA\n"); */
+
+    /* 1. place the largest  */
+    actg = (long int) VECTOR(sizes)[jpos++];
+    igraph_i_layout_merge_place_sphere(&grid, 0, 0, VECTOR(r)[actg], actg);
+
+    IGRAPH_PROGRESS("Merging layouts via DLA", 0.0, NULL);
+    while (jpos < graphs) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        /*     fprintf(stderr, "comp: %li", jpos); */
+        IGRAPH_PROGRESS("Merging layouts via DLA", (100.0 * jpos) / graphs, NULL);
+
+        actg = (long int) VECTOR(sizes)[jpos++];
+        /* 2. random walk, TODO: tune parameters */
+        igraph_i_layout_merge_dla(&grid, actg,
+                                  igraph_vector_e_ptr(&x, actg),
+                                  igraph_vector_e_ptr(&y, actg),
+                                  VECTOR(r)[actg], 0, 0,
+                                  maxx, maxx + 5);
+
+        /* 3. place sphere */
+        igraph_i_layout_merge_place_sphere(&grid, VECTOR(x)[actg], VECTOR(y)[actg],
+                                           VECTOR(r)[actg], actg);
+    }
+    IGRAPH_PROGRESS("Merging layouts via DLA", 100.0, NULL);
+
+    /* Create the result */
+    IGRAPH_CHECK(igraph_matrix_resize(res, allnodes, 2));
+    respos = 0;
+    for (i = 0; i < graphs; i++) {
+        long int size = igraph_matrix_nrow(VECTOR(*coords)[i]);
+        igraph_real_t xx = VECTOR(x)[i];
+        igraph_real_t yy = VECTOR(y)[i];
+        igraph_real_t rr = VECTOR(r)[i] / VECTOR(nr)[i];
+        igraph_matrix_t *mat = VECTOR(*coords)[i];
+        IGRAPH_ALLOW_INTERRUPTION();
+        if (VECTOR(nr)[i] == 0) {
+            rr = 1;
+        }
+        for (j = 0; j < size; j++) {
+            MATRIX(*res, respos, 0) = rr * (MATRIX(*mat, j, 0) - VECTOR(nx)[i]);
+            MATRIX(*res, respos, 1) = rr * (MATRIX(*mat, j, 1) - VECTOR(ny)[i]);
+            MATRIX(*res, respos, 0) += xx;
+            MATRIX(*res, respos, 1) += yy;
+            ++respos;
+        }
+    }
+
+    RNG_END();
+
+    igraph_i_layout_mergegrid_destroy(&grid);
+    igraph_vector_destroy(&sizes);
+    igraph_vector_destroy(&x);
+    igraph_vector_destroy(&y);
+    igraph_vector_destroy(&r);
+    igraph_vector_destroy(&nx);
+    igraph_vector_destroy(&ny);
+    igraph_vector_destroy(&nr);
+    IGRAPH_FINALLY_CLEAN(8);
+    return 0;
+}
+
+int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y,
+                              igraph_real_t *r) {
+    long int nodes = igraph_matrix_nrow(coords);
+    long int i;
+    igraph_real_t xmin, xmax, ymin, ymax;
+
+    xmin = xmax = MATRIX(*coords, 0, 0);
+    ymin = ymax = MATRIX(*coords, 0, 1);
+    for (i = 1; i < nodes; i++) {
+
+        if (MATRIX(*coords, i, 0) < xmin) {
+            xmin = MATRIX(*coords, i, 0);
+        } else if (MATRIX(*coords, i, 0) > xmax) {
+            xmax = MATRIX(*coords, i, 0);
+        }
+
+        if (MATRIX(*coords, i, 1) < ymin) {
+            ymin = MATRIX(*coords, i, 1);
+        } else if (MATRIX(*coords, i, 1) > ymax) {
+            ymax = MATRIX(*coords, i, 1);
+        }
+
+    }
+
+    *x = (xmin + xmax) / 2;
+    *y = (ymin + ymax) / 2;
+    *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) ) / 2;
+
+    return 0;
+}
+
+int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y,
+                              igraph_real_t *z, igraph_real_t *r) {
+    long int nodes = igraph_matrix_nrow(coords);
+    long int i;
+    igraph_real_t xmin, xmax, ymin, ymax, zmin, zmax;
+
+    xmin = xmax = MATRIX(*coords, 0, 0);
+    ymin = ymax = MATRIX(*coords, 0, 1);
+    zmin = zmax = MATRIX(*coords, 0, 2);
+    for (i = 1; i < nodes; i++) {
+
+        if (MATRIX(*coords, i, 0) < xmin) {
+            xmin = MATRIX(*coords, i, 0);
+        } else if (MATRIX(*coords, i, 0) > xmax) {
+            xmax = MATRIX(*coords, i, 0);
+        }
+
+        if (MATRIX(*coords, i, 1) < ymin) {
+            ymin = MATRIX(*coords, i, 1);
+        } else if (MATRIX(*coords, i, 1) > ymax) {
+            ymax = MATRIX(*coords, i, 1);
+        }
+
+        if (MATRIX(*coords, i, 2) < zmin) {
+            zmin = MATRIX(*coords, i, 2);
+        } else if (MATRIX(*coords, i, 2) > zmax) {
+            zmax = MATRIX(*coords, i, 2);
+        }
+
+    }
+
+    *x = (xmin + xmax) / 2;
+    *y = (ymin + ymax) / 2;
+    *z = (zmin + zmax) / 2;
+    *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) +
+               (zmax - zmin) * (zmax - zmin) ) / 2;
+
+    return 0;
+}
+
+#define DIST(x,y) (sqrt(pow((x)-cx,2)+pow((y)-cy,2)))
+
+int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid,
+                              long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r,
+                              igraph_real_t cx, igraph_real_t cy, igraph_real_t startr,
+                              igraph_real_t killr) {
+    long int sp = -1;
+    igraph_real_t angle, len;
+    long int steps = 0;
+
+    /* The graph is not used, only its coordinates */
+    IGRAPH_UNUSED(actg);
+
+    while (sp < 0) {
+        /* start particle */
+        do {
+            steps++;
+            angle = RNG_UNIF(0, 2 * M_PI);
+            len = RNG_UNIF(.5 * startr, startr);
+            *x = cx + len * cos(angle);
+            *y = cy + len * sin(angle);
+            sp = igraph_i_layout_mergegrid_get_sphere(grid, *x, *y, r);
+        } while (sp >= 0);
+
+        while (sp < 0 && DIST(*x, *y) < killr) {
+            igraph_real_t nx, ny;
+            steps++;
+            angle = RNG_UNIF(0, 2 * M_PI);
+            len = RNG_UNIF(0, startr / 100);
+            nx = *x + len * cos(angle);
+            ny = *y + len * sin(angle);
+            sp = igraph_i_layout_mergegrid_get_sphere(grid, nx, ny, r);
+            if (sp < 0) {
+                *x = nx; *y = ny;
+            }
+        }
+    }
+
+    /*   fprintf(stderr, "%li ", steps); */
+    return 0;
+}
+
+int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from,
+                             int n, void *extra);
+
+int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res,
+                               igraph_matrix_t *dist, long int dim);
+
+int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from,
+                             int n, void *extra) {
+    igraph_matrix_t* matrix = (igraph_matrix_t*)extra;
+    IGRAPH_UNUSED(n);
+    igraph_blas_dgemv_array(0, 1, matrix, from, 0, to);
+    return 0;
+}
+
+/* MDS layout for a connected graph, with no error checking on the
+ * input parameters. The distance matrix will be modified in-place. */
+int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res,
+                               igraph_matrix_t *dist, long int dim) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int nev = dim;
+    igraph_matrix_t vectors;
+    igraph_vector_t values, row_means;
+    igraph_real_t grand_mean;
+    long int i, j, k;
+    igraph_eigen_which_t which;
+
+    /* Handle the trivial cases */
+    if (no_of_nodes == 1) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, 1, dim));
+        igraph_matrix_fill(res, 0);
+        return IGRAPH_SUCCESS;
+    }
+    if (no_of_nodes == 2) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, 2, dim));
+        igraph_matrix_fill(res, 0);
+        for (j = 0; j < dim; j++) {
+            MATRIX(*res, 1, j) = 1;
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    /* Initialize some stuff */
+    IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes);
+    IGRAPH_CHECK(igraph_matrix_init(&vectors, no_of_nodes, dim));
+    IGRAPH_FINALLY(igraph_matrix_destroy, &vectors);
+
+    /* Take the square of the distance matrix */
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j < no_of_nodes; j++) {
+            MATRIX(*dist, i, j) *= MATRIX(*dist, i, j);
+        }
+    }
+
+    /* Double centering of the distance matrix */
+    IGRAPH_VECTOR_INIT_FINALLY(&row_means, no_of_nodes);
+    igraph_vector_fill(&values, 1.0 / no_of_nodes);
+    igraph_blas_dgemv(0, 1, dist, &values, 0, &row_means);
+    grand_mean = igraph_vector_sum(&row_means) / no_of_nodes;
+    igraph_matrix_add_constant(dist, grand_mean);
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j < no_of_nodes; j++) {
+            MATRIX(*dist, i, j) -= VECTOR(row_means)[i] + VECTOR(row_means)[j];
+            MATRIX(*dist, i, j) *= -0.5;
+        }
+    }
+    igraph_vector_destroy(&row_means);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Calculate the top `dim` eigenvectors. */
+    which.pos = IGRAPH_EIGEN_LA;
+    which.howmany = (int) nev;
+    IGRAPH_CHECK(igraph_eigen_matrix_symmetric(/*A=*/ 0, /*sA=*/ 0,
+                 /*fun=*/ igraph_i_layout_mds_step,
+                 /*n=*/ (int) no_of_nodes, /*extra=*/ dist,
+                 /*algorithm=*/ IGRAPH_EIGEN_LAPACK,
+                 &which, /*options=*/ 0, /*storage=*/ 0,
+                 &values, &vectors));
+
+    /* Calculate and normalize the final coordinates */
+    for (j = 0; j < nev; j++) {
+        VECTOR(values)[j] = sqrt(fabs(VECTOR(values)[j]));
+    }
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, dim));
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0, k = nev - 1; j < nev; j++, k--) {
+            MATRIX(*res, i, k) = VECTOR(values)[j] * MATRIX(vectors, i, j);
+        }
+    }
+
+    igraph_matrix_destroy(&vectors);
+    igraph_vector_destroy(&values);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_layout_mds
+ * \brief Place the vertices on a plane using multidimensional scaling.
+ *
+ * </para><para>
+ * This layout requires a distance matrix, where the intersection of
+ * row i and column j specifies the desired distance between vertex i
+ * and vertex j. The algorithm will try to place the vertices in a
+ * space having a given number of dimensions in a way that approximates
+ * the distance relations prescribed in the distance matrix. igraph
+ * uses the classical multidimensional scaling by Torgerson; for more
+ * details, see Cox &amp; Cox: Multidimensional Scaling (1994), Chapman
+ * and Hall, London.
+ *
+ * </para><para>
+ * If the input graph is disconnected, igraph will decompose it
+ * first into its subgraphs, lay out the subgraphs one by one
+ * using the appropriate submatrices of the distance matrix, and
+ * then merge the layouts using \ref igraph_layout_merge_dla.
+ * Since \ref igraph_layout_merge_dla works for 2D layouts only,
+ * you cannot run the MDS layout on disconnected graphs for
+ * more than two dimensions.
+ *
+ * </para><para>
+ * Warning: if the graph is symmetric to the exchange of two vertices
+ * (as is the case with leaves of a tree connecting to the same parent),
+ * classical multidimensional scaling may assign the same coordinates to
+ * these vertices.
+ *
+ * \param graph A graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized if needed.
+ * \param dist The distance matrix. It must be symmetric and this
+ *        function does not check whether the matrix is indeed
+ *        symmetric. Results are unspecified if you pass a non-symmetric
+ *        matrix here. You can set this parameter to null; in this
+ *        case, the shortest path lengths between vertices will be
+ *        used as distances.
+ * \param dim The number of dimensions in the embedding space. For
+ *        2D layouts, supply 2 here.
+ * \param options This argument is currently ignored, it was used for
+ *        ARPACK, but LAPACK is used now for calculating the eigenvectors.
+ * \return Error code.
+ *
+ * Added in version 0.6.
+ *
+ * </para><para>
+ * Time complexity: usually around O(|V|^2 dim).
+ */
+
+int igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res,
+                      const igraph_matrix_t *dist, long int dim,
+                      igraph_arpack_options_t *options) {
+    long int i, no_of_nodes = igraph_vcount(graph);
+    igraph_matrix_t m;
+    igraph_bool_t conn;
+
+    RNG_BEGIN();
+
+    /* Check the distance matrix */
+    if (dist && (igraph_matrix_nrow(dist) != no_of_nodes ||
+                 igraph_matrix_ncol(dist) != no_of_nodes)) {
+        IGRAPH_ERROR("invalid distance matrix size", IGRAPH_EINVAL);
+    }
+
+    /* Check the number of dimensions */
+    if (dim <= 1) {
+        IGRAPH_ERROR("dim must be positive", IGRAPH_EINVAL);
+    }
+    if (dim > no_of_nodes) {
+        IGRAPH_ERROR("dim must be less than the number of nodes", IGRAPH_EINVAL);
+    }
+
+    /* Copy or obtain the distance matrix */
+    if (dist == 0) {
+        IGRAPH_CHECK(igraph_matrix_init(&m, no_of_nodes, no_of_nodes));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &m);
+        IGRAPH_CHECK(igraph_shortest_paths(graph, &m,
+                                           igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL));
+    } else {
+        IGRAPH_CHECK(igraph_matrix_copy(&m, dist));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &m);
+        /* Make sure that the diagonal contains zeroes only */
+        for (i = 0; i < no_of_nodes; i++) {
+            MATRIX(m, i, i) = 0.0;
+        }
+    }
+
+    /* Check whether the graph is connected */
+    IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
+    if (conn) {
+        /* Yes, it is, just do the MDS */
+        IGRAPH_CHECK(igraph_i_layout_mds_single(graph, res, &m, dim));
+    } else {
+        /* The graph is not connected, lay out the components one by one */
+        igraph_vector_ptr_t layouts;
+        igraph_vector_t comp, vertex_order;
+        igraph_t subgraph;
+        igraph_matrix_t *layout;
+        igraph_matrix_t dist_submatrix;
+        igraph_bool_t *seen_vertices;
+        long int j, n, processed_vertex_count = 0;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&comp, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&vertex_order, no_of_nodes);
+
+        IGRAPH_CHECK(igraph_vector_ptr_init(&layouts, 0));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &layouts);
+        igraph_vector_ptr_set_item_destructor(&layouts, (igraph_finally_func_t*)igraph_matrix_destroy);
+
+        IGRAPH_CHECK(igraph_matrix_init(&dist_submatrix, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &dist_submatrix);
+
+        seen_vertices = igraph_Calloc(no_of_nodes, igraph_bool_t);
+        if (seen_vertices == 0) {
+            IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, seen_vertices);
+
+        for (i = 0; i < no_of_nodes; i++) {
+            if (seen_vertices[i]) {
+                continue;
+            }
+
+            /* This is a vertex whose component we did not lay out so far */
+            IGRAPH_CHECK(igraph_subcomponent(graph, &comp, i, IGRAPH_ALL));
+            /* Take the subgraph */
+            IGRAPH_CHECK(igraph_induced_subgraph(graph, &subgraph, igraph_vss_vector(&comp),
+                                                 IGRAPH_SUBGRAPH_AUTO));
+            IGRAPH_FINALLY(igraph_destroy, &subgraph);
+            /* Calculate the submatrix of the distances */
+            IGRAPH_CHECK(igraph_matrix_select_rows_cols(&m, &dist_submatrix,
+                         &comp, &comp));
+            /* Allocate a new matrix for storing the layout */
+            layout = igraph_Calloc(1, igraph_matrix_t);
+            if (layout == 0) {
+                IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, layout);
+            IGRAPH_CHECK(igraph_matrix_init(layout, 0, 0));
+            IGRAPH_FINALLY(igraph_matrix_destroy, layout);
+            /* Lay out the subgraph */
+            IGRAPH_CHECK(igraph_i_layout_mds_single(&subgraph, layout, &dist_submatrix, dim));
+            /* Store the layout */
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(&layouts, layout));
+            IGRAPH_FINALLY_CLEAN(2);  /* ownership of layout taken by layouts */
+            /* Free the newly created subgraph */
+            igraph_destroy(&subgraph);
+            IGRAPH_FINALLY_CLEAN(1);
+            /* Mark all the vertices in the component as visited */
+            n = igraph_vector_size(&comp);
+            for (j = 0; j < n; j++) {
+                seen_vertices[(long int)VECTOR(comp)[j]] = 1;
+                VECTOR(vertex_order)[(long int)VECTOR(comp)[j]] = processed_vertex_count++;
+            }
+        }
+        /* Merge the layouts - reusing dist_submatrix here */
+        IGRAPH_CHECK(igraph_layout_merge_dla(0, &layouts, &dist_submatrix));
+        /* Reordering the rows of res to match the original graph */
+        IGRAPH_CHECK(igraph_matrix_select_rows(&dist_submatrix, res, &vertex_order));
+
+        igraph_free(seen_vertices);
+        igraph_matrix_destroy(&dist_submatrix);
+        igraph_vector_ptr_destroy_all(&layouts);
+        igraph_vector_destroy(&vertex_order);
+        igraph_vector_destroy(&comp);
+        IGRAPH_FINALLY_CLEAN(5);
+    }
+
+    RNG_END();
+
+    igraph_matrix_destroy(&m);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_layout_bipartite
+ * Simple layout for bipartite graphs
+ *
+ * The layout is created by first placing the vertices in two rows,
+ * according to their types. Then the positions within the rows are
+ * optimized to minimize edge crossings, by calling \ref
+ * igraph_layout_sugiyama().
+ *
+ * \param graph The input graph.
+ * \param types A boolean vector containing ones and zeros, the vertex
+ *     types. Its length must match the number of vertices in the graph.
+ * \param res Pointer to an initialized matrix, the result, the x and
+ *     y coordinates are stored here.
+ * \param hgap The preferred minimum horizontal gap between vertices
+ *     in the same layer (i.e. vertices of the same type).
+ * \param vgap  The distance between layers.
+ * \param maxiter Maximum number of iterations in the crossing
+ *     minimization stage. 100 is a reasonable default; if you feel
+ *     that you have too many edge crossings, increase this.
+ * \return Error code.
+ *
+ * \sa \ref igraph_layout_sugiyama().
+ */
+
+int igraph_layout_bipartite(const igraph_t *graph,
+                            const igraph_vector_bool_t *types,
+                            igraph_matrix_t *res, igraph_real_t hgap,
+                            igraph_real_t vgap, long int maxiter) {
+
+    long int i, no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t layers;
+
+    if (igraph_vector_bool_size(types) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid vertex type vector size", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&layers, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(layers)[i] = 1 - VECTOR(*types)[i];
+    }
+
+    IGRAPH_CHECK(igraph_layout_sugiyama(graph, res, /*extd_graph=*/ 0,
+                                        /*extd_to_orig_eids=*/ 0, &layers, hgap,
+                                        vgap, maxiter, /*weights=*/ 0));
+
+    igraph_vector_destroy(&layers);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
diff --git a/igraph/src/layout_dh.c b/igraph/src/layout_dh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/layout_dh.c
@@ -0,0 +1,457 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph R package.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_layout.h"
+#include "igraph_interface.h"
+#include "igraph_random.h"
+#include "igraph_math.h"
+
+#include <math.h>
+
+igraph_bool_t igraph_i_segments_intersect(float p0_x, float p0_y,
+        float p1_x, float p1_y,
+        float p2_x, float p2_y,
+        float p3_x, float p3_y) {
+    float s1_x = p1_x - p0_x;
+    float s1_y = p1_y - p0_y;
+    float s2_x = p3_x - p2_x;
+    float s2_y = p3_y - p2_y;
+
+    float s1, s2, t1, t2, s, t;
+    s1 = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y));
+    s2 = (-s2_x * s1_y + s1_x * s2_y);
+    if (s2 == 0) {
+        return 0;
+    }
+    t1 = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x));
+    t2 = (-s2_x * s1_y + s1_x * s2_y);
+    s = s1 / s2;
+    t = t1 / t2;
+
+    return s >= 0 && s <= 1 && t >= 0 && t <= 1 ? 1 : 0;
+}
+
+float igraph_i_point_segment_dist2(float v_x, float v_y,
+                                   float u1_x, float u1_y,
+                                   float u2_x, float u2_y) {
+
+    float dx = u2_x - u1_x;
+    float dy = u2_y - u1_y;
+    float l2 = dx * dx + dy * dy;
+    float t, p_x, p_y;
+    if (l2 == 0) {
+        return (v_x - u1_x) * (v_x - u1_x) + (v_y - u1_y) * (v_y - u1_y);
+    }
+    t = ((v_x - u1_x) * dx + (v_y - u1_y) * dy) / l2;
+    if (t < 0.0) {
+        return (v_x - u1_x) * (v_x - u1_x) + (v_y - u1_y) * (v_y - u1_y);
+    } else if (t > 1.0) {
+        return (v_x - u2_x) * (v_x - u2_x) + (v_y - u2_y) * (v_y - u2_y);
+    }
+    p_x = u1_x + t * dx;
+    p_y = u1_y + t * dy;
+    return (v_x - p_x) * (v_x - p_x) + (v_y - p_y) * (v_y - p_y);
+}
+
+/**
+ * \function igraph_layout_davidson_harel
+ * Davidson-Harel layout algorithm
+ *
+ * This function implements the algorithm by Davidson and Harel,
+ * see Ron Davidson, David Harel: Drawing Graphs Nicely Using
+ * Simulated Annealing. ACM Transactions on Graphics 15(4),
+ * pp. 301-331, 1996.
+ *
+ * </para><para>
+ * The algorithm uses simulated annealing and a sophisticated
+ * energy function, which is unfortunately hard to parameterize
+ * for different graphs. The original publication did not disclose any
+ * parameter values, and the ones below were determined by
+ * experimentation.
+ *
+ * </para><para>
+ * The algorithm consists of two phases, an annealing phase, and a
+ * fine-tuning phase. There is no simulated annealing in the second
+ * phase.
+ *
+ * </para><para>
+ * Our implementation tries to follow the original publication, as
+ * much as possible. The only major difference is that coordinates are
+ * explicitly kept within the bounds of the rectangle of the layout.
+ *
+ * \param graph The input graph, edge directions are ignored.
+ * \param res A matrix, the result is stored here. It can be used to
+ *     supply start coordinates, see \p use_seed.
+ * \param use_seed Boolean, whether to use the supplied \p res as
+ *     start coordinates.
+ * \param maxiter The maximum number of annealing iterations. A
+ *     reasonable value for smaller graphs is 10.
+ * \param fineiter The number of fine tuning iterations. A reasonable
+ *     value is max(10, log2(n)) where n is the number of vertices.
+ * \param cool_fact Cooling factor. A reasonable value is 0.75.
+ * \param weight_node_dist Weight for the node-node distances
+ *     component of the energy function. Reasonable value: 1.0.
+ * \param weight_border Weight for the distance from the border
+ *     component of the energy function. It can be set to zero, if
+ *     vertices are allowed to sit on the border.
+ * \param weight_edge_lengths Weight for the edge length component
+ *     of the energy function, a reasonable value is the density of
+ *     the graph divided by 10.
+ * \param weight_edge_crossings Weight for the edge crossing component
+ *     of the energy function, a reasonable default is 1 minus the
+ *     square root of the density of the graph.
+ * \param weight_node_edge_dist Weight for the node-edge distance
+ *     component of the energy function. A reasonable value is
+ *     1 minus the density, divided by 5.
+ * \return Error code.
+ *
+ * Time complexity: one first phase iteration has time complexity
+ * O(n^2+m^2), one fine tuning iteration has time complexity O(mn).
+ * Time complexity might be smaller if some of the weights of the
+ * components of the energy function are set to zero.
+ *
+ */
+
+int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res,
+                                 igraph_bool_t use_seed, igraph_integer_t maxiter,
+                                 igraph_integer_t fineiter, igraph_real_t cool_fact,
+                                 igraph_real_t weight_node_dist, igraph_real_t weight_border,
+                                 igraph_real_t weight_edge_lengths,
+                                 igraph_real_t weight_edge_crossings,
+                                 igraph_real_t weight_node_edge_dist) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+    igraph_integer_t no_edges = igraph_ecount(graph);
+    float width = sqrt(no_nodes) * 10, height = width;
+    igraph_vector_int_t perm;
+    igraph_bool_t fine_tuning = 0;
+    igraph_integer_t round, i;
+    igraph_vector_float_t try_x, try_y;
+    igraph_vector_int_t try_idx;
+    float move_radius = width / 2;
+    float fine_tuning_factor = 0.01;
+    igraph_vector_t neis;
+    float min_x = width / 2, max_x = -width / 2, min_y = height / 2, max_y = -height / 2;
+
+    igraph_integer_t no_tries = 30;
+    float w_node_dist = weight_node_dist ;          /* 1.0 */
+    float w_borderlines = weight_border;            /* 0.0 */
+    float w_edge_lengths = weight_edge_lengths;     /* 0.0001; */
+    float w_edge_crossings = weight_edge_crossings; /* 1.0 */
+    float w_node_edge_dist = weight_node_edge_dist; /* 0.2 */
+
+    if (use_seed && (igraph_matrix_nrow(res) != no_nodes ||
+                     igraph_matrix_ncol(res) != 2)) {
+        IGRAPH_ERROR("Invalid start position matrix size in "
+                     "Davidson-Harel layout", IGRAPH_EINVAL);
+    }
+    if (maxiter < 0) {
+        IGRAPH_ERROR("Number of iterations must be non-negative in "
+                     "Davidson-Harel layout", IGRAPH_EINVAL);
+    }
+    if (fineiter < 0) {
+        IGRAPH_ERROR("Number of fine tuning iterations must be non-negative in "
+                     "Davidson-Harel layout", IGRAPH_EINVAL);
+    }
+    if (cool_fact <= 0 || cool_fact >= 1) {
+        IGRAPH_ERROR("Cooling factor must be in (0,1) in "
+                     "Davidson-Harel layout", IGRAPH_EINVAL);
+    }
+
+    if (no_nodes == 0) {
+        return 0;
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_init_seq(&perm, 0, no_nodes - 1));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &perm);
+    IGRAPH_CHECK(igraph_vector_float_init(&try_x, no_tries));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &try_x);
+    IGRAPH_CHECK(igraph_vector_float_init(&try_y, no_tries));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &try_y);
+    IGRAPH_CHECK(igraph_vector_int_init_seq(&try_idx, 0, no_tries - 1));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &try_idx);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 100);
+
+    RNG_BEGIN();
+
+    if (!use_seed) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2));
+        for (i = 0; i < no_nodes; i++) {
+            float x, y;
+            x = MATRIX(*res, i, 0) = RNG_UNIF(-width / 2, width / 2);
+            y = MATRIX(*res, i, 1) = RNG_UNIF(-height / 2, height / 2);
+            if (x < min_x) {
+                min_x = x;
+            } else if (x > max_x) {
+                max_x = x;
+            }
+            if (y < min_y) {
+                min_y = y;
+            } else if (y > max_y) {
+                max_y = y;
+            }
+        }
+    } else {
+        min_x = IGRAPH_INFINITY; max_x = IGRAPH_NEGINFINITY;
+        min_y = IGRAPH_INFINITY; max_y = IGRAPH_NEGINFINITY;
+        for (i = 0; i < no_nodes; i++) {
+            float x = MATRIX(*res, i, 0);
+            float y = MATRIX(*res, i, 1);
+            if (x < min_x) {
+                min_x = x;
+            } else if (x > max_x) {
+                max_x = x;
+            }
+            if (y < min_y) {
+                min_y = y;
+            } else if (y > max_y) {
+                max_y = y;
+            }
+        }
+    }
+
+    for (i = 0; i < no_tries; i++) {
+        float phi = 2 * M_PI / no_tries * i;
+        VECTOR(try_x)[i] = cos(phi);
+        VECTOR(try_y)[i] = sin(phi);
+    }
+
+    for (round = 0; round < maxiter + fineiter; round++) {
+        igraph_integer_t p;
+        igraph_vector_int_shuffle(&perm);
+
+        fine_tuning = round >= maxiter;
+        if (fine_tuning) {
+            float fx = fine_tuning_factor * (max_x - min_x);
+            float fy = fine_tuning_factor * (max_y - min_y);
+            move_radius = fx < fy ? fx : fy;
+        }
+
+        for (p = 0; p < no_nodes; p++) {
+            igraph_integer_t t;
+            igraph_integer_t v = VECTOR(perm)[p];
+            igraph_vector_int_shuffle(&try_idx);
+
+            for (t = 0; t < no_tries; t++) {
+                float diff_energy = 0.0;
+                int ti = VECTOR(try_idx)[t];
+
+                /* Try moving it */
+                float old_x = MATRIX(*res, v, 0);
+                float old_y = MATRIX(*res, v, 1);
+                float new_x = old_x + move_radius * VECTOR(try_x)[ti];
+                float new_y = old_y + move_radius * VECTOR(try_y)[ti];
+
+                if (new_x < -width / 2) {
+                    new_x = -width / 2 - 1e-6;
+                }
+                if (new_x >  width / 2) {
+                    new_x =  width / 2 - 1e-6;
+                }
+                if (new_y < -height / 2) {
+                    new_y = -height / 2 - 1e-6;
+                }
+                if (new_y >  height / 2) {
+                    new_y =  height / 2 - 1e-6;
+                }
+
+                if (w_node_dist != 0) {
+                    igraph_integer_t u;
+                    for (u = 0; u < no_nodes; u++) {
+                        float odx, ody, odist2, dx, dy, dist2;
+                        if (u == v) {
+                            continue;
+                        }
+                        odx = old_x - MATRIX(*res, u, 0);
+                        ody = old_y - MATRIX(*res, u, 1);
+                        dx = new_x - MATRIX(*res, u, 0);
+                        dy = new_y - MATRIX(*res, u, 1);
+                        odist2 = odx * odx + ody * ody;
+                        dist2 = dx * dx + dy * dy;
+                        diff_energy += w_node_dist / dist2 - w_node_dist / odist2;
+                    }
+                }
+
+                if (w_borderlines != 0) {
+                    float odx1 = width / 2 - old_x, odx2 = old_x + width / 2;
+                    float ody1 = height / 2 - old_y, ody2 = old_y + height / 2;
+                    float dx1 = width / 2 - new_x, dx2 = new_x + width / 2;
+                    float dy1 = height / 2 - new_y, dy2 = new_y + height / 2;
+                    if (odx1 < 0) {
+                        odx1 = 2;
+                    } if (odx2 < 0) {
+                        odx2 = 2;
+                    }
+                    if (ody1 < 0) {
+                        ody1 = 2;
+                    } if (ody2 < 0) {
+                        ody2 = 2;
+                    }
+                    if (dx1 < 0) {
+                        dx1 = 2;
+                    } if (dx2 < 0) {
+                        dx2 = 2;
+                    }
+                    if (dy1 < 0) {
+                        dy1 = 2;
+                    } if (dy2 < 0) {
+                        dy2 = 2;
+                    }
+                    diff_energy -= w_borderlines *
+                                   (1.0 / (odx1 * odx1) + 1.0 / (odx2 * odx2) +
+                                    1.0 / (ody1 * ody1) + 1.0 / (ody2 * ody2));
+                    diff_energy += w_borderlines *
+                                   (1.0 / (dx1 * dx1) + 1.0 / (dx2 * dx2) +
+                                    1.0 / (dy1 * dy1) + 1.0 / (dy2 * dy2));
+                }
+
+                if (w_edge_lengths != 0) {
+                    igraph_integer_t len, j;
+                    igraph_neighbors(graph, &neis, v, IGRAPH_ALL);
+                    len = igraph_vector_size(&neis);
+                    for (j = 0; j < len; j++) {
+                        igraph_integer_t u = VECTOR(neis)[j];
+                        float odx = old_x - MATRIX(*res, u, 0);
+                        float ody = old_y - MATRIX(*res, u, 1);
+                        float odist2 = odx * odx + ody * ody;
+                        float dx = new_x - MATRIX(*res, u, 0);
+                        float dy = new_y - MATRIX(*res, u, 1);
+                        float dist2 = dx * dx + dy * dy;
+                        diff_energy += w_edge_lengths * (dist2 - odist2);
+                    }
+                }
+
+                if (w_edge_crossings != 0) {
+                    igraph_integer_t len, j, no = 0;
+                    igraph_neighbors(graph, &neis, v, IGRAPH_ALL);
+                    len = igraph_vector_size(&neis);
+                    for (j = 0; j < len; j++) {
+                        igraph_integer_t u = VECTOR(neis)[j];
+                        float u_x = MATRIX(*res, u, 0);
+                        float u_y = MATRIX(*res, u, 1);
+                        igraph_integer_t e;
+                        for (e = 0; e < no_edges; e++) {
+                            igraph_integer_t u1 = IGRAPH_FROM(graph, e);
+                            igraph_integer_t u2 = IGRAPH_TO(graph, e);
+                            float u1_x, u1_y, u2_x, u2_y;
+                            if (u1 == v || u2 == v || u1 == u || u2 == u) {
+                                continue;
+                            }
+                            u1_x = MATRIX(*res, u1, 0);
+                            u1_y = MATRIX(*res, u1, 1);
+                            u2_x = MATRIX(*res, u2, 0);
+                            u2_y = MATRIX(*res, u2, 1);
+                            no -= igraph_i_segments_intersect(old_x, old_y, u_x, u_y,
+                                                              u1_x, u1_y, u2_x, u2_y);
+                            no += igraph_i_segments_intersect(new_x, new_y, u_x, u_y,
+                                                              u1_x, u1_y, u2_x, u2_y);
+                        }
+                    }
+                    diff_energy += w_edge_crossings * no;
+                }
+
+                if (w_node_edge_dist != 0 && fine_tuning) {
+                    igraph_integer_t e, no;
+
+                    /* All non-incident edges from the moved 'v' */
+                    for (e = 0; e < no_edges; e++) {
+                        igraph_integer_t u1 = IGRAPH_FROM(graph, e);
+                        igraph_integer_t u2 = IGRAPH_TO(graph, e);
+                        float u1_x, u1_y, u2_x, u2_y, d_ev;
+                        if (u1 == v || u2 == v) {
+                            continue;
+                        }
+                        u1_x = MATRIX(*res, u1, 0);
+                        u1_y = MATRIX(*res, u1, 1);
+                        u2_x = MATRIX(*res, u2, 0);
+                        u2_y = MATRIX(*res, u2, 1);
+                        d_ev = igraph_i_point_segment_dist2(old_x, old_y, u1_x, u1_y,
+                                                            u2_x, u2_y);
+                        diff_energy -= w_node_edge_dist / d_ev;
+                        d_ev = igraph_i_point_segment_dist2(new_x, new_y, u1_x, u1_y,
+                                                            u2_x, u2_y);
+                        diff_energy += w_node_edge_dist / d_ev;
+                    }
+
+                    /* All other nodes from all of v's incident edges */
+                    igraph_incident(graph, &neis, v, IGRAPH_ALL);
+                    no = igraph_vector_size(&neis);
+                    for (e = 0; e < no; e++) {
+                        igraph_integer_t mye = VECTOR(neis)[e];
+                        igraph_integer_t u = IGRAPH_OTHER(graph, mye, v);
+                        float u_x = MATRIX(*res, u, 0);
+                        float u_y = MATRIX(*res, u, 1);
+                        igraph_integer_t w;
+                        for (w = 0; w < no_nodes; w++) {
+                            float w_x, w_y, d_ev;
+                            if (w == v || w == u) {
+                                continue;
+                            }
+                            w_x = MATRIX(*res, w, 0);
+                            w_y = MATRIX(*res, w, 1);
+                            d_ev = igraph_i_point_segment_dist2(w_x, w_y, old_x,
+                                                                old_y, u_x, u_y);
+                            diff_energy -= w_node_edge_dist / d_ev;
+                            d_ev = igraph_i_point_segment_dist2(w_x, w_y, new_x, new_y,
+                                                                u_x, u_y);
+                            diff_energy += w_node_edge_dist / d_ev;
+                        }
+                    }
+                } /* w_node_edge_dist != 0 && fine_tuning */
+
+                if (diff_energy < 0 ||
+                    (!fine_tuning && RNG_UNIF01() < exp(-diff_energy / move_radius))) {
+                    MATRIX(*res, v, 0) = new_x;
+                    MATRIX(*res, v, 1) = new_y;
+                    if (new_x < min_x) {
+                        min_x = new_x;
+                    } else if (new_x > max_x) {
+                        max_x = new_x;
+                    }
+                    if (new_y < min_y) {
+                        min_y = new_y;
+                    } else if (new_y > max_y) {
+                        max_y = new_y;
+                    }
+                }
+
+            } /* t < no_tries */
+
+        } /* p < no_nodes  */
+
+        move_radius *= cool_fact;
+
+    } /* round < maxiter */
+
+    RNG_END();
+
+    igraph_vector_destroy(&neis);
+    igraph_vector_int_destroy(&try_idx);
+    igraph_vector_float_destroy(&try_x);
+    igraph_vector_float_destroy(&try_y);
+    igraph_vector_int_destroy(&perm);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
diff --git a/igraph/src/layout_fr.c b/igraph/src/layout_fr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/layout_fr.c
@@ -0,0 +1,700 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph R package.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_layout.h"
+#include "igraph_random.h"
+#include "igraph_interface.h"
+#include "igraph_components.h"
+#include "igraph_types_internal.h"
+
+int igraph_layout_i_fr(const igraph_t *graph,
+                       igraph_matrix_t *res,
+                       igraph_bool_t use_seed,
+                       igraph_integer_t niter,
+                       igraph_real_t start_temp,
+                       const igraph_vector_t *weight,
+                       const igraph_vector_t *minx,
+                       const igraph_vector_t *maxx,
+                       const igraph_vector_t *miny,
+                       const igraph_vector_t *maxy) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+    igraph_integer_t no_edges = igraph_ecount(graph);
+    igraph_integer_t i;
+    igraph_vector_float_t dispx, dispy;
+    igraph_real_t temp = start_temp;
+    igraph_real_t difftemp = start_temp / niter;
+    float width = sqrtf(no_nodes), height = width;
+    igraph_bool_t conn = 1;
+    float C;
+
+    igraph_is_connected(graph, &conn, IGRAPH_WEAK);
+    if (!conn) {
+        C = no_nodes * sqrtf(no_nodes);
+    }
+
+    RNG_BEGIN();
+
+    if (!use_seed) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2));
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2;
+            igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] :  width / 2;
+            igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2;
+            igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] :  height / 2;
+            if (!igraph_finite(x1)) {
+                x1 = -sqrt(no_nodes) / 2;
+            }
+            if (!igraph_finite(x2)) {
+                x2 =  sqrt(no_nodes) / 2;
+            }
+            if (!igraph_finite(y1)) {
+                y1 = -sqrt(no_nodes) / 2;
+            }
+            if (!igraph_finite(y2)) {
+                y2 =  sqrt(no_nodes) / 2;
+            }
+            MATRIX(*res, i, 0) = RNG_UNIF(x1, x2);
+            MATRIX(*res, i, 1) = RNG_UNIF(y1, y2);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_float_init(&dispx, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &dispx);
+    IGRAPH_CHECK(igraph_vector_float_init(&dispy, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &dispy);
+
+    for (i = 0; i < niter; i++) {
+        igraph_integer_t v, u, e;
+
+        /* calculate repulsive forces, we have a special version
+           for unconnected graphs */
+        igraph_vector_float_null(&dispx);
+        igraph_vector_float_null(&dispy);
+        if (conn) {
+            for (v = 0; v < no_nodes; v++) {
+                for (u = v + 1; u < no_nodes; u++) {
+                    float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+                    float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+                    float dlen = dx * dx + dy * dy;
+
+                    if (dlen == 0) {
+                        dx = RNG_UNIF01() * 1e-9;
+                        dy = RNG_UNIF01() * 1e-9;
+                        dlen = dx * dx + dy * dy;
+                    }
+
+                    VECTOR(dispx)[v] += dx / dlen;
+                    VECTOR(dispy)[v] += dy / dlen;
+                    VECTOR(dispx)[u] -= dx / dlen;
+                    VECTOR(dispy)[u] -= dy / dlen;
+                }
+            }
+        } else {
+            for (v = 0; v < no_nodes; v++) {
+                for (u = v + 1; u < no_nodes; u++) {
+                    float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+                    float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+                    float dlen, rdlen;
+
+                    dlen = dx * dx + dy * dy;
+                    if (dlen == 0) {
+                        dx = RNG_UNIF(0, 1e-6);
+                        dy = RNG_UNIF(0, 1e-6);
+                        dlen = dx * dx + dy * dy;
+                    }
+
+                    rdlen = sqrt(dlen);
+
+                    VECTOR(dispx)[v] += dx * (C - dlen * rdlen) / (dlen * C);
+                    VECTOR(dispy)[v] += dy * (C - dlen * rdlen) / (dlen * C);
+                    VECTOR(dispx)[u] -= dx * (C - dlen * rdlen) / (dlen * C);
+                    VECTOR(dispy)[u] -= dy * (C - dlen * rdlen) / (dlen * C);
+                }
+            }
+        }
+
+        /* calculate attractive forces */
+        for (e = 0; e < no_edges; e++) {
+            /* each edges is an ordered pair of vertices v and u */
+            igraph_integer_t v = IGRAPH_FROM(graph, e);
+            igraph_integer_t u = IGRAPH_TO(graph, e);
+            igraph_real_t dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+            igraph_real_t dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+            igraph_real_t w = weight ? VECTOR(*weight)[e] : 1.0;
+            igraph_real_t dlen = sqrt(dx * dx + dy * dy) * w;
+            VECTOR(dispx)[v] -= (dx * dlen);
+            VECTOR(dispy)[v] -= (dy * dlen);
+            VECTOR(dispx)[u] += (dx * dlen);
+            VECTOR(dispy)[u] += (dy * dlen);
+        }
+
+        /* limit max displacement to temperature t and prevent from
+           displacement outside frame */
+        for (v = 0; v < no_nodes; v++) {
+            igraph_real_t dx = VECTOR(dispx)[v] + RNG_UNIF01() * 1e-9;
+            igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF01() * 1e-9;
+            igraph_real_t displen = sqrt(dx * dx + dy * dy);
+            igraph_real_t mx = fabs(dx) < temp ? dx : temp;
+            igraph_real_t my = fabs(dy) < temp ? dy : temp;
+            if (displen > 0) {
+                MATRIX(*res, v, 0) += (dx / displen) * mx;
+                MATRIX(*res, v, 1) += (dy / displen) * my;
+            }
+            if (minx && MATRIX(*res, v, 0) < VECTOR(*minx)[v]) {
+                MATRIX(*res, v, 0) = VECTOR(*minx)[v];
+            }
+            if (maxx && MATRIX(*res, v, 0) > VECTOR(*maxx)[v]) {
+                MATRIX(*res, v, 0) = VECTOR(*maxx)[v];
+            }
+            if (miny && MATRIX(*res, v, 1) < VECTOR(*miny)[v]) {
+                MATRIX(*res, v, 1) = VECTOR(*miny)[v];
+            }
+            if (maxy && MATRIX(*res, v, 1) > VECTOR(*maxy)[v]) {
+                MATRIX(*res, v, 1) = VECTOR(*maxy)[v];
+            }
+        }
+
+        temp -= difftemp;
+    }
+
+    RNG_END();
+
+    igraph_vector_float_destroy(&dispx);
+    igraph_vector_float_destroy(&dispy);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_layout_i_grid_fr(const igraph_t *graph,
+                            igraph_matrix_t *res, igraph_bool_t use_seed,
+                            igraph_integer_t niter, igraph_real_t start_temp,
+                            const igraph_vector_t *weight, const igraph_vector_t *minx,
+                            const igraph_vector_t *maxx, const igraph_vector_t *miny,
+                            const igraph_vector_t *maxy) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+    igraph_integer_t no_edges = igraph_ecount(graph);
+    float width = sqrtf(no_nodes), height = width;
+    igraph_2dgrid_t grid;
+    igraph_vector_float_t dispx, dispy;
+    igraph_real_t temp = start_temp;
+    igraph_real_t difftemp = start_temp / niter;
+    igraph_2dgrid_iterator_t vidit;
+    igraph_integer_t i;
+    const float cellsize = 2.0;
+
+    RNG_BEGIN();
+
+    if (!use_seed) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2));
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2;
+            igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] :  width / 2;
+            igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2;
+            igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] :  height / 2;
+            if (!igraph_finite(x1)) {
+                x1 = -sqrt(no_nodes) / 2;
+            }
+            if (!igraph_finite(x2)) {
+                x2 =  sqrt(no_nodes) / 2;
+            }
+            if (!igraph_finite(y1)) {
+                y1 = -sqrt(no_nodes) / 2;
+            }
+            if (!igraph_finite(y2)) {
+                y2 =  sqrt(no_nodes) / 2;
+            }
+            MATRIX(*res, i, 0) = RNG_UNIF(x1, x2);
+            MATRIX(*res, i, 1) = RNG_UNIF(y1, y2);
+        }
+    }
+
+    /* make grid */
+    IGRAPH_CHECK(igraph_2dgrid_init(&grid, res, -width / 2, width / 2, cellsize,
+                                    -height / 2, height / 2, cellsize));
+    IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid);
+
+    /* place vertices on grid */
+    for (i = 0; i < no_nodes; i++) {
+        igraph_2dgrid_add2(&grid, i);
+    }
+
+    IGRAPH_CHECK(igraph_vector_float_init(&dispx, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &dispx);
+    IGRAPH_CHECK(igraph_vector_float_init(&dispy, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &dispy);
+
+    for (i = 0; i < niter; i++) {
+        igraph_integer_t v, u, e;
+
+        igraph_vector_float_null(&dispx);
+        igraph_vector_float_null(&dispy);
+
+        /* repulsion */
+        igraph_2dgrid_reset(&grid, &vidit);
+        while ( (v = igraph_2dgrid_next(&grid, &vidit) - 1) != -1) {
+            while ( (u = igraph_2dgrid_next_nei(&grid, &vidit) - 1) != -1) {
+                float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+                float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+                float dlen = dx * dx + dy * dy;
+                if (dlen < cellsize * cellsize) {
+                    VECTOR(dispx)[v] += dx / dlen;
+                    VECTOR(dispy)[v] += dy / dlen;
+                    VECTOR(dispx)[u] -= dx / dlen;
+                    VECTOR(dispy)[u] -= dy / dlen;
+                }
+            }
+        }
+
+        /* attraction */
+        for (e = 0; e < no_edges; e++) {
+            igraph_integer_t v = IGRAPH_FROM(graph, e);
+            igraph_integer_t u = IGRAPH_TO(graph, e);
+            igraph_real_t dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+            igraph_real_t dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+            igraph_real_t w = weight ? VECTOR(*weight)[e] : 1.0;
+            igraph_real_t dlen = sqrt(dx * dx + dy * dy) * w;
+            VECTOR(dispx)[v] -= (dx * dlen);
+            VECTOR(dispy)[v] -= (dy * dlen);
+            VECTOR(dispx)[u] += (dx * dlen);
+            VECTOR(dispy)[u] += (dy * dlen);
+        }
+
+        /* update */
+        for (v = 0; v < no_nodes; v++) {
+            igraph_real_t dx = VECTOR(dispx)[v] + RNG_UNIF01() * 1e-9;
+            igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF01() * 1e-9;
+            igraph_real_t displen = sqrt(dx * dx + dy * dy);
+            igraph_real_t mx = fabs(dx) < temp ? dx : temp;
+            igraph_real_t my = fabs(dy) < temp ? dy : temp;
+            if (displen > 0) {
+                MATRIX(*res, v, 0) += (dx / displen) * mx;
+                MATRIX(*res, v, 1) += (dy / displen) * my;
+            }
+            if (minx && MATRIX(*res, v, 0) < VECTOR(*minx)[v]) {
+                MATRIX(*res, v, 0) = VECTOR(*minx)[v];
+            }
+            if (maxx && MATRIX(*res, v, 0) > VECTOR(*maxx)[v]) {
+                MATRIX(*res, v, 0) = VECTOR(*maxx)[v];
+            }
+            if (miny && MATRIX(*res, v, 1) < VECTOR(*miny)[v]) {
+                MATRIX(*res, v, 1) = VECTOR(*miny)[v];
+            }
+            if (maxy && MATRIX(*res, v, 1) > VECTOR(*maxy)[v]) {
+                MATRIX(*res, v, 1) = VECTOR(*maxy)[v];
+            }
+        }
+
+        temp -= difftemp;
+    }
+
+    igraph_vector_float_destroy(&dispx);
+    igraph_vector_float_destroy(&dispy);
+    igraph_2dgrid_destroy(&grid);
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_fruchterman_reingold
+ * \brief Places the vertices on a plane according to the Fruchterman-Reingold algorithm.
+ *
+ * </para><para>
+ * This is a force-directed layout, see Fruchterman, T.M.J. and
+ * Reingold, E.M.: Graph Drawing by Force-directed Placement.
+ * Software -- Practice and Experience, 21/11, 1129--1164,
+ * 1991.
+ * \param graph Pointer to an initialized graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized as needed.
+ * \param use_seed Logical, if true the supplied values in the
+ *        \p res argument are used as an initial layout, if
+ *        false a random initial layout is used.
+ * \param niter The number of iterations to do. A reasonable
+ *        default value is 500.
+ * \param start_temp Start temperature. This is the maximum amount
+ *        of movement alloved along one axis, within one step, for a
+ *        vertex. Currently it is decreased linearly to zero during
+ *        the iteration.
+ * \param grid Whether to use the (fast but less accurate) grid based
+ *        version of the algorithm. Possible values: \c
+ *        IGRAPH_LAYOUT_GRID, \c IGRAPH_LAYOUT_NOGRID, \c
+ *        IGRAPH_LAYOUT_AUTOGRID. The last one uses the grid based
+ *        version only for large graphs, currently the ones with
+ *        more than 1000 vertices.
+ * \param weight Pointer to a vector containing edge weights,
+ *        the attraction along the edges will be multiplied by these.
+ *        It will be ignored if it is a null-pointer.
+ * \param minx Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote x \endquote coordinate for every vertex.
+ * \param maxx Same as \p minx, but the maximum \quote x \endquote
+ *        coordinates.
+ * \param miny Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote y \endquote coordinate for every vertex.
+ * \param maxy Same as \p miny, but the maximum \quote y \endquote
+ *        coordinates.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^2) in each
+ * iteration, |V| is the number of
+ * vertices in the graph.
+ */
+
+int igraph_layout_fruchterman_reingold(const igraph_t *graph,
+                                       igraph_matrix_t *res,
+                                       igraph_bool_t use_seed,
+                                       igraph_integer_t niter,
+                                       igraph_real_t start_temp,
+                                       igraph_layout_grid_t grid,
+                                       const igraph_vector_t *weight,
+                                       const igraph_vector_t *minx,
+                                       const igraph_vector_t *maxx,
+                                       const igraph_vector_t *miny,
+                                       const igraph_vector_t *maxy) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+
+    if (niter < 0) {
+        IGRAPH_ERROR("Number of iterations must be non-negative in "
+                     "Fruchterman-Reingold layout", IGRAPH_EINVAL);
+    }
+
+    if (use_seed && (igraph_matrix_nrow(res) != no_nodes ||
+                     igraph_matrix_ncol(res) != 2)) {
+        IGRAPH_ERROR("Invalid start position matrix size in "
+                     "Fruchterman-Reingold layout", IGRAPH_EINVAL);
+    }
+
+    if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    if (minx && igraph_vector_size(minx) != no_nodes) {
+        IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL);
+    }
+    if (maxx && igraph_vector_size(maxx) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL);
+    }
+    if (minx && maxx && !igraph_vector_all_le(minx, maxx)) {
+        IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL);
+    }
+    if (miny && igraph_vector_size(miny) != no_nodes) {
+        IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL);
+    }
+    if (maxy && igraph_vector_size(maxy) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL);
+    }
+    if (miny && maxy && !igraph_vector_all_le(miny, maxy)) {
+        IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL);
+    }
+
+    if (grid == IGRAPH_LAYOUT_AUTOGRID) {
+        if (no_nodes > 1000) {
+            grid = IGRAPH_LAYOUT_GRID;
+        } else {
+            grid = IGRAPH_LAYOUT_NOGRID;
+        }
+    }
+
+    if (grid == IGRAPH_LAYOUT_GRID) {
+        return igraph_layout_i_grid_fr(graph, res, use_seed, niter, start_temp,
+                                       weight, minx, maxx, miny, maxy);
+    } else {
+        return igraph_layout_i_fr(graph, res, use_seed, niter, start_temp,
+                                  weight, minx, maxx, miny, maxy);
+    }
+}
+
+/**
+ * \function igraph_layout_fruchterman_reingold_3d
+ * \brief 3D Fruchterman-Reingold algorithm.
+ *
+ * This is the 3D version of the force based
+ * Fruchterman-Reingold layout (see \ref
+ * igraph_layout_fruchterman_reingold for the 2D version
+ *
+ * \param graph Pointer to an initialized graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result and will be resized as needed.
+ * \param use_seed Logical, if true the supplied values in the
+ *        \p res argument are used as an initial layout, if
+ *        false a random initial layout is used.
+ * \param niter The number of iterations to do. A reasonable
+ *        default value is 500.
+ * \param start_temp Start temperature. This is the maximum amount
+ *        of movement alloved along one axis, within one step, for a
+ *        vertex. Currently it is decreased linearly to zero during
+ *        the iteration.
+ * \param weight Pointer to a vector containing edge weights,
+ *        the attraction along the edges will be multiplied by these.
+ *        It will be ignored if it is a null-pointer.
+ * \param minx Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote x \endquote coordinate for every vertex.
+ * \param maxx Same as \p minx, but the maximum \quote x \endquote
+ *        coordinates.
+ * \param miny Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote y \endquote coordinate for every vertex.
+ * \param maxy Same as \p miny, but the maximum \quote y \endquote
+ *        coordinates.
+ * \param minz Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote z \endquote coordinate for every vertex.
+ * \param maxz Same as \p minz, but the maximum \quote z \endquote
+ *        coordinates.
+ * \return Error code.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(|V|^2) in each
+ * iteration, |V| is the number of
+ * vertices in the graph.
+ *
+ */
+
+int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph,
+        igraph_matrix_t *res,
+        igraph_bool_t use_seed,
+        igraph_integer_t niter,
+        igraph_real_t start_temp,
+        const igraph_vector_t *weight,
+        const igraph_vector_t *minx,
+        const igraph_vector_t *maxx,
+        const igraph_vector_t *miny,
+        const igraph_vector_t *maxy,
+        const igraph_vector_t *minz,
+        const igraph_vector_t *maxz) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+    igraph_integer_t no_edges = igraph_ecount(graph);
+    igraph_integer_t i;
+    igraph_vector_float_t dispx, dispy, dispz;
+    igraph_real_t temp = start_temp;
+    igraph_real_t difftemp = start_temp / niter;
+    float width = sqrtf(no_nodes), height = width, depth = width;
+    igraph_bool_t conn = 1;
+    float C;
+
+    if (niter < 0) {
+        IGRAPH_ERROR("Number of iterations must be non-negative in "
+                     "Fruchterman-Reingold layout", IGRAPH_EINVAL);
+    }
+
+    if (use_seed && (igraph_matrix_nrow(res) != no_nodes ||
+                     igraph_matrix_ncol(res) != 3)) {
+        IGRAPH_ERROR("Invalid start position matrix size in "
+                     "Fruchterman-Reingold layout", IGRAPH_EINVAL);
+    }
+
+    if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    if (minx && igraph_vector_size(minx) != no_nodes) {
+        IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL);
+    }
+    if (maxx && igraph_vector_size(maxx) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL);
+    }
+    if (minx && maxx && !igraph_vector_all_le(minx, maxx)) {
+        IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL);
+    }
+    if (miny && igraph_vector_size(miny) != no_nodes) {
+        IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL);
+    }
+    if (maxy && igraph_vector_size(maxy) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL);
+    }
+    if (miny && maxy && !igraph_vector_all_le(miny, maxy)) {
+        IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL);
+    }
+    if (minz && igraph_vector_size(minz) != no_nodes) {
+        IGRAPH_ERROR("Invalid minz vector length", IGRAPH_EINVAL);
+    }
+    if (maxz && igraph_vector_size(maxz) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxz vector length", IGRAPH_EINVAL);
+    }
+    if (minz && maxz && !igraph_vector_all_le(minz, maxz)) {
+        IGRAPH_ERROR("minz must not be greater than maxz", IGRAPH_EINVAL);
+    }
+
+    igraph_is_connected(graph, &conn, IGRAPH_WEAK);
+    if (!conn) {
+        C = no_nodes * sqrtf(no_nodes);
+    }
+
+    RNG_BEGIN();
+
+    if (!use_seed) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 3));
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2;
+            igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] :  width / 2;
+            igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2;
+            igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] :  height / 2;
+            igraph_real_t z1 = minz ? VECTOR(*minz)[i] : -depth / 2;
+            igraph_real_t z2 = maxz ? VECTOR(*maxz)[i] :  depth / 2;
+            MATRIX(*res, i, 0) = RNG_UNIF(x1, x2);
+            MATRIX(*res, i, 1) = RNG_UNIF(y1, y2);
+            MATRIX(*res, i, 2) = RNG_UNIF(z1, z2);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_float_init(&dispx, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &dispx);
+    IGRAPH_CHECK(igraph_vector_float_init(&dispy, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &dispy);
+    IGRAPH_CHECK(igraph_vector_float_init(&dispz, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &dispz);
+
+    for (i = 0; i < niter; i++) {
+        igraph_integer_t v, u, e;
+
+        /* calculate repulsive forces, we have a special version
+           for unconnected graphs */
+        igraph_vector_float_null(&dispx);
+        igraph_vector_float_null(&dispy);
+        igraph_vector_float_null(&dispz);
+        if (conn) {
+            for (v = 0; v < no_nodes; v++) {
+                for (u = v + 1; u < no_nodes; u++) {
+                    float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+                    float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+                    float dz = MATRIX(*res, v, 2) - MATRIX(*res, u, 2);
+                    float dlen = dx * dx + dy * dy + dz * dz;
+
+                    if (dlen == 0) {
+                        dx = RNG_UNIF01() * 1e-9;
+                        dy = RNG_UNIF01() * 1e-9;
+                        dz = RNG_UNIF01() * 1e-9;
+                        dlen = dx * dx + dy * dy + dz * dz;
+                    }
+
+                    VECTOR(dispx)[v] += dx / dlen;
+                    VECTOR(dispy)[v] += dy / dlen;
+                    VECTOR(dispz)[v] += dz / dlen;
+                    VECTOR(dispx)[u] -= dx / dlen;
+                    VECTOR(dispy)[u] -= dy / dlen;
+                    VECTOR(dispz)[u] -= dz / dlen;
+                }
+            }
+        } else {
+            for (v = 0; v < no_nodes; v++) {
+                for (u = v + 1; u < no_nodes; u++) {
+                    float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+                    float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+                    float dz = MATRIX(*res, v, 2) - MATRIX(*res, u, 2);
+                    float dlen, rdlen;
+
+                    dlen = dx * dx + dy * dy + dz * dz;
+                    if (dlen == 0) {
+                        dx = RNG_UNIF01() * 1e-9;
+                        dy = RNG_UNIF01() * 1e-9;
+                        dz = RNG_UNIF01() * 1e-9;
+                        dlen = dx * dx + dy * dy + dz * dz;
+                    }
+
+                    rdlen = sqrt(dlen);
+
+                    VECTOR(dispx)[v] += dx * (C - dlen * rdlen) / (dlen * C);
+                    VECTOR(dispy)[v] += dy * (C - dlen * rdlen) / (dlen * C);
+                    VECTOR(dispy)[v] += dz * (C - dlen * rdlen) / (dlen * C);
+                    VECTOR(dispx)[u] -= dx * (C - dlen * rdlen) / (dlen * C);
+                    VECTOR(dispy)[u] -= dy * (C - dlen * rdlen) / (dlen * C);
+                    VECTOR(dispz)[u] -= dz * (C - dlen * rdlen) / (dlen * C);
+                }
+            }
+        }
+
+        /* calculate attractive forces */
+        for (e = 0; e < no_edges; e++) {
+            /* each edges is an ordered pair of vertices v and u */
+            igraph_integer_t v = IGRAPH_FROM(graph, e);
+            igraph_integer_t u = IGRAPH_TO(graph, e);
+            igraph_real_t dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+            igraph_real_t dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+            igraph_real_t dz = MATRIX(*res, v, 2) - MATRIX(*res, u, 2);
+            igraph_real_t w = weight ? VECTOR(*weight)[e] : 1.0;
+            igraph_real_t dlen = sqrt(dx * dx + dy * dy + dz * dz) * w;
+            VECTOR(dispx)[v] -= (dx * dlen);
+            VECTOR(dispy)[v] -= (dy * dlen);
+            VECTOR(dispz)[v] -= (dz * dlen);
+            VECTOR(dispx)[u] += (dx * dlen);
+            VECTOR(dispy)[u] += (dy * dlen);
+            VECTOR(dispz)[u] += (dz * dlen);
+        }
+
+        /* limit max displacement to temperature t and prevent from
+           displacement outside frame */
+        for (v = 0; v < no_nodes; v++) {
+            igraph_real_t dx = VECTOR(dispx)[v] + RNG_UNIF01() * 1e-9;
+            igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF01() * 1e-9;
+            igraph_real_t dz = VECTOR(dispz)[v] + RNG_UNIF01() * 1e-9;
+            igraph_real_t displen = sqrt(dx * dx + dy * dy + dz * dz);
+            igraph_real_t mx = fabs(dx) < temp ? dx : temp;
+            igraph_real_t my = fabs(dy) < temp ? dy : temp;
+            igraph_real_t mz = fabs(dz) < temp ? dz : temp;
+            if (displen > 0) {
+                MATRIX(*res, v, 0) += (dx / displen) * mx;
+                MATRIX(*res, v, 1) += (dy / displen) * my;
+                MATRIX(*res, v, 2) += (dz / displen) * mz;
+            }
+            if (minx && MATRIX(*res, v, 0) < VECTOR(*minx)[v]) {
+                MATRIX(*res, v, 0) = VECTOR(*minx)[v];
+            }
+            if (maxx && MATRIX(*res, v, 0) > VECTOR(*maxx)[v]) {
+                MATRIX(*res, v, 0) = VECTOR(*maxx)[v];
+            }
+            if (miny && MATRIX(*res, v, 1) < VECTOR(*miny)[v]) {
+                MATRIX(*res, v, 1) = VECTOR(*miny)[v];
+            }
+            if (maxy && MATRIX(*res, v, 1) > VECTOR(*maxy)[v]) {
+                MATRIX(*res, v, 1) = VECTOR(*maxy)[v];
+            }
+            if (minz && MATRIX(*res, v, 2) < VECTOR(*minz)[v]) {
+                MATRIX(*res, v, 2) = VECTOR(*minz)[v];
+            }
+            if (maxz && MATRIX(*res, v, 2) > VECTOR(*maxz)[v]) {
+                MATRIX(*res, v, 2) = VECTOR(*maxz)[v];
+            }
+        }
+
+        temp -= difftemp;
+    }
+
+    RNG_END();
+
+    igraph_vector_float_destroy(&dispx);
+    igraph_vector_float_destroy(&dispy);
+    igraph_vector_float_destroy(&dispz);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
diff --git a/igraph/src/layout_gem.c b/igraph/src/layout_gem.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/layout_gem.c
@@ -0,0 +1,246 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph R package.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_layout.h"
+#include "igraph_interface.h"
+#include "igraph_random.h"
+#include "igraph_math.h"
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_gem
+ *
+ * The GEM layout algorithm, as described in Arne Frick, Andreas Ludwig,
+ * Heiko Mehldau: A Fast Adaptive Layout Algorithm for Undirected Graphs,
+ * Proc. Graph Drawing 1994, LNCS 894, pp. 388-403, 1995.
+ * \param graph The input graph. Edge directions are ignored in
+ *        directed graphs.
+ * \param res The result is stored here. If the \p use_seed argument
+ *        is true (non-zero), then this matrix is also used as the
+ *        starting point of the algorithm.
+ * \param use_seed Boolean, whether to use the supplied coordinates in
+ *        \p res as the starting point. If false (zero), then a
+ *        uniform random starting point is used.
+ * \param maxiter The maximum number of iterations to
+ *        perform. Updating a single vertex counts as an iteration.
+ *        A reasonable default is 40 * n * n, where n is the number of
+ *        vertices. The original paper suggests 4 * n * n, but this
+ *        usually only works if the other parameters are set up carefully.
+ * \param temp_max The maximum allowed local temperature. A reasonable
+ *        default is the number of vertices.
+ * \param temp_min The global temperature at which the algorithm
+ *        terminates (even before reaching \p maxiter iterations). A
+ *        reasonable default is 1/10.
+ * \param temp_init Initial local temperature of all vertices. A
+ *        reasonable default is the square root of the number of
+ *        vertices.
+ * \return Error code.
+ *
+ * Time complexity: O(t * n * (n+e)), where n is the number of vertices,
+ * e is the number of edges and t is the number of time steps
+ * performed.
+ */
+
+int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res,
+                      igraph_bool_t use_seed, igraph_integer_t maxiter,
+                      igraph_real_t temp_max, igraph_real_t temp_min,
+                      igraph_real_t temp_init) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+    igraph_vector_int_t perm;
+    igraph_vector_float_t impulse_x, impulse_y, temp, skew_gauge;
+    igraph_integer_t i;
+    float temp_global;
+    igraph_integer_t perm_pointer = 0;
+    float barycenter_x = 0.0, barycenter_y = 0.0;
+    igraph_vector_t phi;
+    igraph_vector_t neis;
+    const float elen_des2 = 128 * 128;
+    const float gamma = 1 / 16.0;
+    const float alpha_o = M_PI;
+    const float alpha_r = M_PI / 3.0;
+    const float sigma_o = 1.0 / 3.0;
+    const float sigma_r = 1.0 / 2.0 / no_nodes;
+
+    if (maxiter < 0) {
+        IGRAPH_ERROR("Number of iterations must be non-negative in GEM layout",
+                     IGRAPH_EINVAL);
+    }
+    if (use_seed && (igraph_matrix_nrow(res) != no_nodes ||
+                     igraph_matrix_ncol(res) != 2)) {
+        IGRAPH_ERROR("Invalid start position matrix size in GEM layout",
+                     IGRAPH_EINVAL);
+    }
+    if (temp_max <= 0) {
+        IGRAPH_ERROR("Maximum temperature should be positive in GEM layout",
+                     IGRAPH_EINVAL);
+    }
+    if (temp_min <= 0) {
+        IGRAPH_ERROR("Minimum temperature should be positive in GEM layout",
+                     IGRAPH_EINVAL);
+    }
+    if (temp_init <= 0) {
+        IGRAPH_ERROR("Initial temperature should be positive in GEM layout",
+                     IGRAPH_EINVAL);
+    }
+    if (temp_max < temp_init || temp_init < temp_min) {
+        IGRAPH_ERROR("Minimum <= Initial <= Maximum temperature is required "
+                     "in GEM layout", IGRAPH_EINVAL);
+    }
+
+    if (no_nodes == 0) {
+        return 0;
+    }
+
+    IGRAPH_CHECK(igraph_vector_float_init(&impulse_x, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &impulse_x);
+    IGRAPH_CHECK(igraph_vector_float_init(&impulse_y, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &impulse_y);
+    IGRAPH_CHECK(igraph_vector_float_init(&temp, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &temp);
+    IGRAPH_CHECK(igraph_vector_float_init(&skew_gauge, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_float_destroy, &skew_gauge);
+    IGRAPH_CHECK(igraph_vector_int_init_seq(&perm, 0, no_nodes - 1));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &perm);
+    IGRAPH_VECTOR_INIT_FINALLY(&phi, no_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 10);
+
+    RNG_BEGIN();
+
+    /* Initialization */
+    igraph_degree(graph, &phi, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);
+    if (!use_seed) {
+        const igraph_real_t width_half = no_nodes * 100, height_half = width_half;
+        IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2));
+        for (i = 0; i < no_nodes; i++) {
+            MATRIX(*res, i, 0) = RNG_UNIF(-width_half, width_half);
+            MATRIX(*res, i, 1) = RNG_UNIF(-height_half, height_half);
+            barycenter_x += MATRIX(*res, i, 0);
+            barycenter_y += MATRIX(*res, i, 1);
+            VECTOR(phi)[i] *= (VECTOR(phi)[i] / 2.0 + 1.0);
+        }
+    } else {
+        for (i = 0; i < no_nodes; i++) {
+            barycenter_x += MATRIX(*res, i, 0);
+            barycenter_y += MATRIX(*res, i, 1);
+            VECTOR(phi)[i] *= (VECTOR(phi)[i] / 2.0 + 1.0);
+        }
+    }
+    igraph_vector_float_fill(&temp, temp_init);
+    temp_global = temp_init * no_nodes;
+
+    while (temp_global > temp_min * no_nodes && maxiter > 0) {
+
+        /* choose a vertex v to update */
+        igraph_integer_t u, v, nlen, j;
+        float px, py, pvx, pvy;
+        if (!perm_pointer) {
+            igraph_vector_int_shuffle(&perm);
+            perm_pointer = no_nodes - 1;
+        }
+        v = VECTOR(perm)[perm_pointer--];
+
+        /* compute v's impulse */
+        px = (barycenter_x / no_nodes - MATRIX(*res, v, 0)) * gamma * VECTOR(phi)[v];
+        py = (barycenter_y / no_nodes - MATRIX(*res, v, 1)) * gamma * VECTOR(phi)[v];
+        px += RNG_UNIF(-32.0, 32.0);
+        py += RNG_UNIF(-32.0, 32.0);
+
+        for (u = 0; u < no_nodes; u++) {
+            float dx, dy, dist2;
+            if (u == v) {
+                continue;
+            }
+            dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+            dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+            dist2 = dx * dx + dy * dy;
+            if (dist2 != 0) {
+                px += dx * elen_des2 / dist2;
+                py += dy * elen_des2 / dist2;
+            }
+        }
+
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL));
+        nlen = igraph_vector_size(&neis);
+        for (j = 0; j < nlen; j++) {
+            igraph_integer_t u = VECTOR(neis)[j];
+            float dx = MATRIX(*res, v, 0) - MATRIX(*res, u, 0);
+            float dy = MATRIX(*res, v, 1) - MATRIX(*res, u, 1);
+            float dist2 = dx * dx + dy * dy;
+            px -= dx * dist2 / (elen_des2 * VECTOR(phi)[v]);
+            py -= dy * dist2 / (elen_des2 * VECTOR(phi)[v]);
+        }
+
+        /* update v's position and temperature */
+        if (px != 0 || py != 0) {
+            float plen = sqrtf(px * px + py * py);
+            px *= VECTOR(temp)[v] / plen;
+            py *= VECTOR(temp)[v] / plen;
+            MATRIX(*res, v, 0) += px;
+            MATRIX(*res, v, 1) += py;
+            barycenter_x += px;
+            barycenter_y += py;
+        }
+
+        pvx = VECTOR(impulse_x)[v]; pvy = VECTOR(impulse_y)[v];
+        if (pvx != 0 || pvy != 0) {
+            float beta = atan2f(pvy - py, pvx - px);
+            float sin_beta = sinf(beta);
+            float sign_sin_beta = (sin_beta > 0) ? 1 : ((sin_beta < 0) ? -1 : 0);
+            float cos_beta = cosf(beta);
+            float abs_cos_beta = fabsf(cos_beta);
+            float old_temp = VECTOR(temp)[v];
+            if (sin(beta) >= sin(M_PI_2 + alpha_r / 2.0)) {
+                VECTOR(skew_gauge)[v] += sigma_r * sign_sin_beta;
+            }
+            if (abs_cos_beta >= cosf(alpha_o / 2.0)) {
+                VECTOR(temp)[v] *= sigma_o * cos_beta;
+            }
+            VECTOR(temp)[v] *= (1 - fabsf(VECTOR(skew_gauge)[v]));
+            if (VECTOR(temp)[v] > temp_max) {
+                VECTOR(temp)[v] = temp_max;
+            }
+            VECTOR(impulse_x)[v] = px;
+            VECTOR(impulse_y)[v] = py;
+            temp_global += VECTOR(temp)[v] - old_temp;
+        }
+
+        maxiter--;
+
+    } /* while temp && iter */
+
+
+    RNG_END();
+
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&phi);
+    igraph_vector_int_destroy(&perm);
+    igraph_vector_float_destroy(&skew_gauge);
+    igraph_vector_float_destroy(&temp);
+    igraph_vector_float_destroy(&impulse_y);
+    igraph_vector_float_destroy(&impulse_x);
+    IGRAPH_FINALLY_CLEAN(7);
+
+    return 0;
+}
diff --git a/igraph/src/layout_kk.c b/igraph/src/layout_kk.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/layout_kk.c
@@ -0,0 +1,680 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph R package.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_layout.h"
+#include "igraph_interface.h"
+#include "igraph_paths.h"
+#include "igraph_random.h"
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_kamada_kawai
+ * \brief Places the vertices on a plane according the Kamada-Kawai algorithm.
+ *
+ * </para><para>
+ * This is a force directed layout, see  Kamada, T. and Kawai, S.: An
+ * Algorithm for Drawing General Undirected Graphs. Information
+ * Processing Letters, 31/1, 7--15, 1989.
+ * \param graph A graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result (x-positions in column zero and
+ *        y-positions in column one) and will be resized if needed.
+ * \param use_seed Boolean, whether to use the values supplied in the
+ *        \p res argument as the initial configuration. If zero then a
+ *        random initial configuration is used.
+ * \param maxiter The maximum number of iterations to perform. A reasonable
+ *        default value is at least ten (or more) times the number of
+ *        vertices.
+ * \param epsilon Stop the iteration, if the maximum delta value of the
+ *        algorithm is smaller than still. It is safe to leave it at zero,
+ *        and then \p maxiter iterations are performed.
+ * \param kkconst The Kamada-Kawai vertex attraction constant.
+ *        Typical value: number of vertices.
+ * \param weights Edge weights, larger values will result longer edges.
+ * \param minx Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote x \endquote coordinate for every vertex.
+ * \param maxx Same as \p minx, but the maximum \quote x \endquote
+ *        coordinates.
+ * \param miny Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote y \endquote coordinate for every vertex.
+ * \param maxy Same as \p miny, but the maximum \quote y \endquote
+ *        coordinates.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|) for each iteration, after an O(|V|^2
+ * log|V|) initialization step. |V| is the number of vertices in the
+ * graph.
+ */
+
+int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res,
+                               igraph_bool_t use_seed, igraph_integer_t maxiter,
+                               igraph_real_t epsilon, igraph_real_t kkconst,
+                               const igraph_vector_t *weights,
+                               const igraph_vector_t *minx, const igraph_vector_t *maxx,
+                               const igraph_vector_t *miny, const igraph_vector_t *maxy) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+    igraph_integer_t no_edges = igraph_ecount(graph);
+    igraph_real_t L, L0 = sqrt(no_nodes);
+    igraph_matrix_t dij, lij, kij;
+    igraph_real_t max_dij;
+    igraph_vector_t D1, D2;
+    igraph_integer_t i, j, m;
+
+    if (maxiter < 0) {
+        IGRAPH_ERROR("Number of iterations must be non-negatice in "
+                     "Kamada-Kawai layout", IGRAPH_EINVAL);
+    }
+    if (kkconst <= 0) {
+        IGRAPH_ERROR("`K' constant must be positive in Kamada-Kawai layout",
+                     IGRAPH_EINVAL);
+    }
+
+    if (use_seed && (igraph_matrix_nrow(res) != no_nodes ||
+                     igraph_matrix_ncol(res) != 2)) {
+        IGRAPH_ERROR("Invalid start position matrix size in "
+                     "Kamada-Kawai layout", IGRAPH_EINVAL);
+    }
+    if (weights && igraph_vector_size(weights) != no_edges) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    if (minx && igraph_vector_size(minx) != no_nodes) {
+        IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL);
+    }
+    if (maxx && igraph_vector_size(maxx) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL);
+    }
+    if (minx && maxx && !igraph_vector_all_le(minx, maxx)) {
+        IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL);
+    }
+    if (miny && igraph_vector_size(miny) != no_nodes) {
+        IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL);
+    }
+    if (maxy && igraph_vector_size(maxy) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL);
+    }
+    if (miny && maxy && !igraph_vector_all_le(miny, maxy)) {
+        IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL);
+    }
+
+    if (!use_seed) {
+        if (minx || maxx || miny || maxy) {
+            const igraph_real_t width = sqrt(no_nodes), height = width;
+            IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2));
+            RNG_BEGIN();
+            for (i = 0; i < no_nodes; i++) {
+                igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2;
+                igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] :  width / 2;
+                igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2;
+                igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] :  height / 2;
+                if (!igraph_finite(x1)) {
+                    x1 = -width / 2;
+                }
+                if (!igraph_finite(x2)) {
+                    x2 =  width / 2;
+                }
+                if (!igraph_finite(y1)) {
+                    y1 = -height / 2;
+                }
+                if (!igraph_finite(y2)) {
+                    y2 =  height / 2;
+                }
+                MATRIX(*res, i, 0) = RNG_UNIF(x1, x2);
+                MATRIX(*res, i, 1) = RNG_UNIF(y1, y2);
+            }
+            RNG_END();
+        } else {
+            igraph_layout_circle(graph, res, /* order= */ igraph_vss_all());
+        }
+    }
+
+    if (no_nodes <= 1) {
+        return 0;
+    }
+
+    IGRAPH_MATRIX_INIT_FINALLY(&dij, no_nodes, no_nodes);
+    IGRAPH_MATRIX_INIT_FINALLY(&kij, no_nodes, no_nodes);
+    IGRAPH_MATRIX_INIT_FINALLY(&lij, no_nodes, no_nodes);
+
+    if (weights && igraph_vector_min(weights) < 0) {
+        IGRAPH_CHECK(igraph_shortest_paths_bellman_ford(graph, &dij, igraph_vss_all(),
+                     igraph_vss_all(), weights,
+                     IGRAPH_ALL));
+    } else {
+
+        IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, &dij, igraph_vss_all(),
+                     igraph_vss_all(), weights,
+                     IGRAPH_ALL));
+    }
+
+    max_dij = 0.0;
+    for (i = 0; i < no_nodes; i++) {
+        for (j = i + 1; j < no_nodes; j++) {
+            if (!igraph_finite(MATRIX(dij, i, j))) {
+                continue;
+            }
+            if (MATRIX(dij, i, j) > max_dij) {
+                max_dij = MATRIX(dij, i, j);
+            }
+        }
+    }
+    for (i = 0; i < no_nodes; i++) {
+        for (j = 0; j < no_nodes; j++) {
+            if (MATRIX(dij, i, j) > max_dij) {
+                MATRIX(dij, i, j) = max_dij;
+            }
+        }
+    }
+
+    L = L0 / max_dij;
+    for (i = 0; i < no_nodes; i++) {
+        for (j = 0; j < no_nodes; j++) {
+            igraph_real_t tmp = MATRIX(dij, i, j) * MATRIX(dij, i, j);
+            if (i == j) {
+                continue;
+            }
+            MATRIX(kij, i, j) = kkconst / tmp;
+            MATRIX(lij, i, j) = L * MATRIX(dij, i, j);
+        }
+    }
+
+    /* Initialize delta */
+    IGRAPH_VECTOR_INIT_FINALLY(&D1, no_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&D2, no_nodes);
+    for (m = 0; m < no_nodes; m++) {
+        igraph_real_t myD1 = 0.0, myD2 = 0.0;
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t dx, dy, mi_dist;
+            if (i == m) {
+                continue;
+            }
+            dx = MATRIX(*res, m, 0) - MATRIX(*res, i, 0);
+            dy = MATRIX(*res, m, 1) - MATRIX(*res, i, 1);
+            mi_dist = sqrt(dx * dx + dy * dy);
+            myD1 += MATRIX(kij, m, i) * (dx - MATRIX(lij, m, i) * dx / mi_dist);
+            myD2 += MATRIX(kij, m, i) * (dy - MATRIX(lij, m, i) * dy / mi_dist);
+        }
+        VECTOR(D1)[m] = myD1;
+        VECTOR(D2)[m] = myD2;
+    }
+
+    for (j = 0; j < maxiter; j++) {
+        igraph_real_t myD1, myD2, A, B, C;
+        igraph_real_t max_delta, delta_x, delta_y;
+        igraph_real_t old_x, old_y, new_x, new_y;
+
+        myD1 = 0.0, myD2 = 0.0, A = 0.0, B = 0.0, C = 0.0;
+
+        /* Select maximal delta */
+        m = 0; max_delta = -1;
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t delta = (VECTOR(D1)[i] * VECTOR(D1)[i] +
+                                   VECTOR(D2)[i] * VECTOR(D2)[i]);
+            if (delta > max_delta) {
+                m = i; max_delta = delta;
+            }
+        }
+        if (max_delta < epsilon) {
+            break;
+        }
+        old_x = MATRIX(*res, m, 0);
+        old_y = MATRIX(*res, m, 1);
+
+        /* Calculate D1 and D2, A, B, C */
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t dx, dy, dist, den;
+            if (i == m) {
+                continue;
+            }
+            dx = old_x - MATRIX(*res, i, 0);
+            dy = old_y - MATRIX(*res, i, 1);
+            dist = sqrt(dx * dx + dy * dy);
+            den = dist * (dx * dx + dy * dy);
+            A += MATRIX(kij, m, i) * (1 - MATRIX(lij, m, i) * dy * dy / den);
+            B += MATRIX(kij, m, i) * MATRIX(lij, m, i) * dx * dy / den;
+            C += MATRIX(kij, m, i) * (1 - MATRIX(lij, m, i) * dx * dx / den);
+        }
+        myD1 = VECTOR(D1)[m];
+        myD2 = VECTOR(D2)[m];
+
+        /* Need to solve some linear equations */
+        delta_y = (B * myD1 - myD2 * A) / (C * A - B * B);
+        delta_x = - (myD1 + B * delta_y) / A;
+
+        new_x = old_x + delta_x;
+        new_y = old_y + delta_y;
+
+        /* Limits, if given */
+        if (minx && new_x < VECTOR(*minx)[m]) {
+            new_x = VECTOR(*minx)[m];
+        }
+        if (maxx && new_x > VECTOR(*maxx)[m]) {
+            new_x = VECTOR(*maxx)[m];
+        }
+        if (miny && new_y < VECTOR(*miny)[m]) {
+            new_y = VECTOR(*miny)[m];
+        }
+        if (maxy && new_y > VECTOR(*maxy)[m]) {
+            new_y = VECTOR(*maxy)[m];
+        }
+
+        /* Update delta, only with/for the affected node */
+        VECTOR(D1)[m] = VECTOR(D2)[m] = 0.0;
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t old_dx, old_dy, old_mi, new_dx, new_dy, new_mi_dist, old_mi_dist;
+            if (i == m) {
+                continue;
+            }
+            old_dx = old_x - MATRIX(*res, i, 0);
+            old_dy = old_y - MATRIX(*res, i, 1);
+            old_mi_dist = sqrt(old_dx * old_dx + old_dy * old_dy);
+            new_dx = new_x - MATRIX(*res, i, 0);
+            new_dy = new_y - MATRIX(*res, i, 1);
+            new_mi_dist = sqrt(new_dx * new_dx + new_dy * new_dy);
+
+            VECTOR(D1)[i] -= MATRIX(kij, m, i) *
+                             (-old_dx + MATRIX(lij, m, i) * old_dx / old_mi_dist);
+            VECTOR(D2)[i] -= MATRIX(kij, m, i) *
+                             (-old_dy + MATRIX(lij, m, i) * old_dy / old_mi_dist);
+            VECTOR(D1)[i] += MATRIX(kij, m, i) *
+                             (-new_dx + MATRIX(lij, m, i) * new_dx / new_mi_dist);
+            VECTOR(D2)[i] += MATRIX(kij, m, i) *
+                             (-new_dy + MATRIX(lij, m, i) * new_dy / new_mi_dist);
+
+            VECTOR(D1)[m] += MATRIX(kij, m, i) *
+                             (new_dx - MATRIX(lij, m, i) * new_dx / new_mi_dist);
+            VECTOR(D2)[m] += MATRIX(kij, m, i) *
+                             (new_dy - MATRIX(lij, m, i) * new_dy / new_mi_dist);
+        }
+
+        /* Update coordinates*/
+        MATRIX(*res, m, 0) = new_x;
+        MATRIX(*res, m, 1) = new_y;
+    }
+
+    igraph_vector_destroy(&D2);
+    igraph_vector_destroy(&D1);
+    igraph_matrix_destroy(&lij);
+    igraph_matrix_destroy(&kij);
+    igraph_matrix_destroy(&dij);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_kamada_kawai_3d
+ * \brief 3D version of the Kamada-Kawai layout generator
+ *
+ * </para><para>
+ * This is a force directed layout, see  Kamada, T. and Kawai, S.: An
+ * Algorithm for Drawing General Undirected Graphs. Information
+ * Processing Letters, 31/1, 7--15, 1989.
+ * \param graph A graph object.
+ * \param res Pointer to an initialized matrix object. This will
+ *        contain the result (x-positions in column zero and
+ *        y-positions in column one) and will be resized if needed.
+ * \param use_seed Boolean, whether to use the values supplied in the
+ *        \p res argument as the initial configuration. If zero then a
+ *        random initial configuration is used.
+ * \param maxiter The maximum number of iterations to perform. A reasonable
+ *        default value is at least ten (or more) times the number of
+ *        vertices.
+ * \param epsilon Stop the iteration, if the maximum delta value of the
+ *        algorithm is smaller than still. It is safe to leave it at zero,
+ *        and then \p maxiter iterations are performed.
+ * \param kkconst The Kamada-Kawai vertex attraction constant.
+ *        Typical value: number of vertices.
+ * \param weights Edge weights, larger values will result longer edges.
+ * \param minx Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote x \endquote coordinate for every vertex.
+ * \param maxx Same as \p minx, but the maximum \quote x \endquote
+ *        coordinates.
+ * \param miny Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote y \endquote coordinate for every vertex.
+ * \param maxy Same as \p miny, but the maximum \quote y \endquote
+ *        coordinates.
+ * \param minz Pointer to a vector, or a \c NULL pointer. If not a
+ *        \c NULL pointer then the vector gives the minimum
+ *        \quote z \endquote coordinate for every vertex.
+ * \param maxz Same as \p minz, but the maximum \quote z \endquote
+ *        coordinates.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|) for each iteration, after an O(|V|^2
+ * log|V|) initialization step. |V| is the number of vertices in the
+ * graph.
+ */
+
+int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res,
+                                  igraph_bool_t use_seed, igraph_integer_t maxiter,
+                                  igraph_real_t epsilon, igraph_real_t kkconst,
+                                  const igraph_vector_t *weights,
+                                  const igraph_vector_t *minx, const igraph_vector_t *maxx,
+                                  const igraph_vector_t *miny, const igraph_vector_t *maxy,
+                                  const igraph_vector_t *minz, const igraph_vector_t *maxz) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+    igraph_integer_t no_edges = igraph_ecount(graph);
+    igraph_real_t L, L0 = sqrt(no_nodes);
+    igraph_matrix_t dij, lij, kij;
+    igraph_real_t max_dij;
+    igraph_vector_t D1, D2, D3;
+    igraph_integer_t i, j, m;
+
+    if (maxiter < 0) {
+        IGRAPH_ERROR("Number of iterations must be non-negatice in "
+                     "Kamada-Kawai layout", IGRAPH_EINVAL);
+    }
+    if (kkconst <= 0) {
+        IGRAPH_ERROR("`K' constant must be positive in Kamada-Kawai layout",
+                     IGRAPH_EINVAL);
+    }
+
+    if (use_seed && (igraph_matrix_nrow(res) != no_nodes ||
+                     igraph_matrix_ncol(res) != 3)) {
+        IGRAPH_ERROR("Invalid start position matrix size in "
+                     "3d Kamada-Kawai layout", IGRAPH_EINVAL);
+    }
+    if (weights && igraph_vector_size(weights) != no_edges) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    if (minx && igraph_vector_size(minx) != no_nodes) {
+        IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL);
+    }
+    if (maxx && igraph_vector_size(maxx) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL);
+    }
+    if (minx && maxx && !igraph_vector_all_le(minx, maxx)) {
+        IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL);
+    }
+    if (miny && igraph_vector_size(miny) != no_nodes) {
+        IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL);
+    }
+    if (maxy && igraph_vector_size(maxy) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL);
+    }
+    if (miny && maxy && !igraph_vector_all_le(miny, maxy)) {
+        IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL);
+    }
+    if (minz && igraph_vector_size(minz) != no_nodes) {
+        IGRAPH_ERROR("Invalid minz vector length", IGRAPH_EINVAL);
+    }
+    if (maxz && igraph_vector_size(maxz) != no_nodes) {
+        IGRAPH_ERROR("Invalid maxz vector length", IGRAPH_EINVAL);
+    }
+    if (minz && maxz && !igraph_vector_all_le(minz, maxz)) {
+        IGRAPH_ERROR("minz must not be greater than maxz", IGRAPH_EINVAL);
+    }
+
+    if (!use_seed) {
+        if (minx || maxx || miny || maxy || minz || maxz) {
+            const igraph_real_t width = sqrt(no_nodes), height = width, depth = width;
+            IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 3));
+            RNG_BEGIN();
+            for (i = 0; i < no_nodes; i++) {
+                igraph_real_t x1 = minx ? VECTOR(*minx)[i] : -width / 2;
+                igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] :  width / 2;
+                igraph_real_t y1 = miny ? VECTOR(*miny)[i] : -height / 2;
+                igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] :  height / 2;
+                igraph_real_t z1 = minz ? VECTOR(*minz)[i] : -depth / 2;
+                igraph_real_t z2 = maxz ? VECTOR(*maxz)[i] :  depth / 2;
+                if (!igraph_finite(x1)) {
+                    x1 = -width / 2;
+                }
+                if (!igraph_finite(x2)) {
+                    x2 =  width / 2;
+                }
+                if (!igraph_finite(y1)) {
+                    y1 = -height / 2;
+                }
+                if (!igraph_finite(y2)) {
+                    y2 =  height / 2;
+                }
+                if (!igraph_finite(z1)) {
+                    z1 = -depth / 2;
+                }
+                if (!igraph_finite(z2)) {
+                    z2 =  depth / 2;
+                }
+                MATRIX(*res, i, 0) = RNG_UNIF(x1, x2);
+                MATRIX(*res, i, 1) = RNG_UNIF(y1, y2);
+                MATRIX(*res, i, 2) = RNG_UNIF(z1, z2);
+            }
+            RNG_END();
+        } else {
+            igraph_layout_sphere(graph, res);
+        }
+    }
+
+    if (no_nodes <= 1) {
+        return 0;
+    }
+
+    IGRAPH_MATRIX_INIT_FINALLY(&dij, no_nodes, no_nodes);
+    IGRAPH_MATRIX_INIT_FINALLY(&kij, no_nodes, no_nodes);
+    IGRAPH_MATRIX_INIT_FINALLY(&lij, no_nodes, no_nodes);
+    IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, &dij, igraph_vss_all(),
+                 igraph_vss_all(), weights,
+                 IGRAPH_ALL));
+
+    max_dij = 0.0;
+    for (i = 0; i < no_nodes; i++) {
+        for (j = i + 1; j < no_nodes; j++) {
+            if (!igraph_finite(MATRIX(dij, i, j))) {
+                continue;
+            }
+            if (MATRIX(dij, i, j) > max_dij) {
+                max_dij = MATRIX(dij, i, j);
+            }
+        }
+    }
+    for (i = 0; i < no_nodes; i++) {
+        for (j = 0; j < no_nodes; j++) {
+            if (MATRIX(dij, i, j) > max_dij) {
+                MATRIX(dij, i, j) = max_dij;
+            }
+        }
+    }
+
+    L = L0 / max_dij;
+    for (i = 0; i < no_nodes; i++) {
+        for (j = 0; j < no_nodes; j++) {
+            igraph_real_t tmp = MATRIX(dij, i, j) * MATRIX(dij, i, j);
+            if (i == j) {
+                continue;
+            }
+            MATRIX(kij, i, j) = kkconst / tmp;
+            MATRIX(lij, i, j) = L * MATRIX(dij, i, j);
+        }
+    }
+
+    /* Initialize delta */
+    IGRAPH_VECTOR_INIT_FINALLY(&D1, no_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&D2, no_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&D3, no_nodes);
+    for (m = 0; m < no_nodes; m++) {
+        igraph_real_t dx, dy, dz, mi_dist;
+        igraph_real_t myD1 = 0.0, myD2 = 0.0, myD3 = 0.0;
+        for (i = 0; i < no_nodes; i++) {
+            if (i == m) {
+                continue;
+            }
+            dx = MATRIX(*res, m, 0) - MATRIX(*res, i, 0);
+            dy = MATRIX(*res, m, 1) - MATRIX(*res, i, 1);
+            dz = MATRIX(*res, m, 2) - MATRIX(*res, i, 2);
+            mi_dist = sqrt(dx * dx + dy * dy + dz * dz);
+            myD1 += MATRIX(kij, m, i) * (dx - MATRIX(lij, m, i) * dx / mi_dist);
+            myD2 += MATRIX(kij, m, i) * (dy - MATRIX(lij, m, i) * dy / mi_dist);
+            myD3 += MATRIX(kij, m, i) * (dz - MATRIX(lij, m, i) * dz / mi_dist);
+        }
+        VECTOR(D1)[m] = myD1;
+        VECTOR(D2)[m] = myD2;
+        VECTOR(D3)[m] = myD3;
+    }
+
+    for (j = 0; j < maxiter; j++) {
+
+        igraph_real_t Ax = 0.0, Ay = 0.0, Az = 0.0;
+        igraph_real_t Axx = 0.0, Axy = 0.0, Axz = 0.0, Ayy = 0.0, Ayz = 0.0, Azz = 0.0;
+        igraph_real_t max_delta, delta_x, delta_y, delta_z;
+        igraph_real_t old_x, old_y, old_z, new_x, new_y, new_z;
+        igraph_real_t detnum;
+
+        /* Select maximal delta */
+        m = 0; max_delta = -1;
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t delta = (VECTOR(D1)[i] * VECTOR(D1)[i] +
+                                   VECTOR(D2)[i] * VECTOR(D2)[i] +
+                                   VECTOR(D3)[i] * VECTOR(D3)[i]);
+            if (delta > max_delta) {
+                m = i; max_delta = delta;
+            }
+        }
+        if (max_delta < epsilon) {
+            break;
+        }
+        old_x = MATRIX(*res, m, 0);
+        old_y = MATRIX(*res, m, 1);
+        old_z = MATRIX(*res, m, 2);
+
+        /* Calculate D1, D2 and D3, and other coefficients */
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t dx, dy, dz, dist, den, k_mi, l_mi;
+            if (i == m) {
+                continue;
+            }
+            dx = old_x - MATRIX(*res, i, 0);
+            dy = old_y - MATRIX(*res, i, 1);
+            dz = old_z - MATRIX(*res, i, 2);
+            dist = sqrt(dx * dx + dy * dy + dz * dz);
+            den = dist * (dx * dx + dy * dy + dz * dz);
+            k_mi = MATRIX(kij, m, i);
+            l_mi = MATRIX(lij, m, i);
+            Axx += k_mi * (1 - l_mi * (dy * dy + dz * dz) / den);
+            Ayy += k_mi * (1 - l_mi * (dx * dx + dz * dz) / den);
+            Azz += k_mi * (1 - l_mi * (dx * dx + dy * dy) / den);
+            Axy += k_mi * l_mi * dx * dy / den;
+            Axz += k_mi * l_mi * dx * dz / den;
+            Ayz += k_mi * l_mi * dy * dz / den;
+        }
+        Ax = -VECTOR(D1)[m];
+        Ay = -VECTOR(D2)[m];
+        Az = -VECTOR(D3)[m];
+
+        /* Need to solve some linear equations, we just use Cramer's rule */
+#define DET(a,b,c,d,e,f,g,h,i) ((a*e*i+b*f*g+c*d*h)-(c*e*g+b*d*i+a*f*h))
+
+        detnum  = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Axz, Ayz, Azz);
+        delta_x = DET(Ax, Ay, Az, Axy, Ayy, Ayz, Axz, Ayz, Azz) / detnum;
+        delta_y = DET(Axx, Axy, Axz, Ax, Ay, Az, Axz, Ayz, Azz) / detnum;
+        delta_z = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Ax, Ay, Az ) / detnum;
+
+        new_x = old_x + delta_x;
+        new_y = old_y + delta_y;
+        new_z = old_z + delta_z;
+
+        /* Limits, if given */
+        if (minx && new_x < VECTOR(*minx)[m]) {
+            new_x = VECTOR(*minx)[m];
+        }
+        if (maxx && new_x > VECTOR(*maxx)[m]) {
+            new_x = VECTOR(*maxx)[m];
+        }
+        if (miny && new_y < VECTOR(*miny)[m]) {
+            new_y = VECTOR(*miny)[m];
+        }
+        if (maxy && new_y > VECTOR(*maxy)[m]) {
+            new_y = VECTOR(*maxy)[m];
+        }
+        if (minz && new_z < VECTOR(*minz)[m]) {
+            new_z = VECTOR(*minz)[m];
+        }
+        if (maxz && new_z > VECTOR(*maxz)[m]) {
+            new_z = VECTOR(*maxz)[m];
+        }
+
+        /* Update delta, only with/for the affected node */
+        VECTOR(D1)[m] = VECTOR(D2)[m] = VECTOR(D3)[m] = 0.0;
+        for (i = 0; i < no_nodes; i++) {
+            igraph_real_t old_dx, old_dy, old_dz, old_mi_dist, new_dx, new_dy, new_dz, new_mi_dist;
+            if (i == m) {
+                continue;
+            }
+            old_dx = old_x - MATRIX(*res, i, 0);
+            old_dy = old_y - MATRIX(*res, i, 1);
+            old_dz = old_z - MATRIX(*res, i, 2);
+            old_mi_dist = sqrt(old_dx * old_dx + old_dy * old_dy +
+                               old_dz * old_dz);
+            new_dx = new_x - MATRIX(*res, i, 0);
+            new_dy = new_y - MATRIX(*res, i, 1);
+            new_dz = new_z - MATRIX(*res, i, 2);
+            new_mi_dist = sqrt(new_dx * new_dx + new_dy * new_dy +
+                               new_dz * new_dz);
+
+            VECTOR(D1)[i] -= MATRIX(kij, m, i) *
+                             (-old_dx + MATRIX(lij, m, i) * old_dx / old_mi_dist);
+            VECTOR(D2)[i] -= MATRIX(kij, m, i) *
+                             (-old_dy + MATRIX(lij, m, i) * old_dy / old_mi_dist);
+            VECTOR(D3)[i] -= MATRIX(kij, m, i) *
+                             (-old_dz + MATRIX(lij, m, i) * old_dz / old_mi_dist);
+
+            VECTOR(D1)[i] += MATRIX(kij, m, i) *
+                             (-new_dx + MATRIX(lij, m, i) * new_dx / new_mi_dist);
+            VECTOR(D2)[i] += MATRIX(kij, m, i) *
+                             (-new_dy + MATRIX(lij, m, i) * new_dy / new_mi_dist);
+            VECTOR(D3)[i] += MATRIX(kij, m, i) *
+                             (-new_dz + MATRIX(lij, m, i) * new_dz / new_mi_dist);
+
+            VECTOR(D1)[m] += MATRIX(kij, m, i) *
+                             (new_dx - MATRIX(lij, m, i) * new_dx / new_mi_dist);
+            VECTOR(D2)[m] += MATRIX(kij, m, i) *
+                             (new_dy - MATRIX(lij, m, i) * new_dy / new_mi_dist);
+            VECTOR(D3)[m] += MATRIX(kij, m, i) *
+                             (new_dz - MATRIX(lij, m, i) * new_dz / new_mi_dist);
+        }
+
+        /* Update coordinates*/
+        MATRIX(*res, m, 0) = new_x;
+        MATRIX(*res, m, 1) = new_y;
+        MATRIX(*res, m, 2) = new_z;
+    }
+
+    igraph_vector_destroy(&D3);
+    igraph_vector_destroy(&D2);
+    igraph_vector_destroy(&D1);
+    igraph_matrix_destroy(&lij);
+    igraph_matrix_destroy(&kij);
+    igraph_matrix_destroy(&dij);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return 0;
+}
diff --git a/igraph/src/lbfgs.c b/igraph/src/lbfgs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lbfgs.c
@@ -0,0 +1,1378 @@
+/*
+ *      Limited memory BFGS (L-BFGS).
+ *
+ * Copyright (c) 1990, Jorge Nocedal
+ * Copyright (c) 2007-2010 Naoaki Okazaki
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/* $Id: lbfgs.c 65 2010-01-29 12:19:16Z naoaki $ */
+
+/*
+This library is a C port of the FORTRAN implementation of Limited-memory
+Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) method written by Jorge Nocedal.
+The original FORTRAN source code is available at:
+http://www.ece.northwestern.edu/~nocedal/lbfgs.html
+
+The L-BFGS algorithm is described in:
+    - Jorge Nocedal.
+      Updating Quasi-Newton Matrices with Limited Storage.
+      <i>Mathematics of Computation</i>, Vol. 35, No. 151, pp. 773--782, 1980.
+    - Dong C. Liu and Jorge Nocedal.
+      On the limited memory BFGS method for large scale optimization.
+      <i>Mathematical Programming</i> B, Vol. 45, No. 3, pp. 503-528, 1989.
+
+The line search algorithms used in this implementation are described in:
+    - John E. Dennis and Robert B. Schnabel.
+      <i>Numerical Methods for Unconstrained Optimization and Nonlinear
+      Equations</i>, Englewood Cliffs, 1983.
+    - Jorge J. More and David J. Thuente.
+      Line search algorithm with guaranteed sufficient decrease.
+      <i>ACM Transactions on Mathematical Software (TOMS)</i>, Vol. 20, No. 3,
+      pp. 286-307, 1994.
+
+This library also implements Orthant-Wise Limited-memory Quasi-Newton (OWL-QN)
+method presented in:
+    - Galen Andrew and Jianfeng Gao.
+      Scalable training of L1-regularized log-linear models.
+      In <i>Proceedings of the 24th International Conference on Machine
+      Learning (ICML 2007)</i>, pp. 33-40, 2007.
+
+I would like to thank the original author, Jorge Nocedal, who has been
+distributing the effieicnt and explanatory implementation in an open source
+licence.
+*/
+
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif/*HAVE_CONFIG_H*/
+
+#ifndef _MSC_VER
+#include <stdint.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "lbfgs.h"
+
+#ifdef  _MSC_VER
+#define inline  __inline
+typedef unsigned int uint32_t;
+#endif/*_MSC_VER*/
+
+#if     defined(USE_SSE) && defined(__SSE2__) && LBFGS_FLOAT == 64
+/* Use SSE2 optimization for 64bit double precision. */
+#include "arithmetic_sse_double.h"
+
+#elif   defined(USE_SSE) && defined(__SSE__) && LBFGS_FLOAT == 32
+/* Use SSE optimization for 32bit float precision. */
+#include "arithmetic_sse_float.h"
+
+#else
+/* No CPU specific optimization. */
+#include "arithmetic_ansi.h"
+
+#endif
+
+#define min2(a, b)      ((a) <= (b) ? (a) : (b))
+#define max2(a, b)      ((a) >= (b) ? (a) : (b))
+#define max3(a, b, c)   max2(max2((a), (b)), (c));
+
+#define is_aligned(p, bytes) \
+	(((uintptr_t)(const void*)(p)) % (bytes) == 0)
+
+struct tag_callback_data {
+    int n;
+    void *instance;
+    lbfgs_evaluate_t proc_evaluate;
+    lbfgs_progress_t proc_progress;
+};
+typedef struct tag_callback_data callback_data_t;
+
+struct tag_iteration_data {
+    lbfgsfloatval_t alpha;
+    lbfgsfloatval_t *s;     /* [n] */
+    lbfgsfloatval_t *y;     /* [n] */
+    lbfgsfloatval_t ys;     /* vecdot(y, s) */
+};
+typedef struct tag_iteration_data iteration_data_t;
+
+static const lbfgs_parameter_t _defparam = {
+    6, 1e-5, 0, 1e-5,
+    0, LBFGS_LINESEARCH_DEFAULT, 40,
+    1e-20, 1e20, 1e-4, 0.9, 0.9, 1.0e-16,
+    0.0, 0, -1,
+};
+
+/* Forward function declarations. */
+
+typedef int (*line_search_proc)(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *f,
+    lbfgsfloatval_t *g,
+    lbfgsfloatval_t *s,
+    lbfgsfloatval_t *stp,
+    const lbfgsfloatval_t* xp,
+    const lbfgsfloatval_t* gp,
+    lbfgsfloatval_t *wa,
+    callback_data_t *cd,
+    const lbfgs_parameter_t *param
+    );
+    
+static int line_search_backtracking(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *f,
+    lbfgsfloatval_t *g,
+    lbfgsfloatval_t *s,
+    lbfgsfloatval_t *stp,
+    const lbfgsfloatval_t* xp,
+    const lbfgsfloatval_t* gp,
+    lbfgsfloatval_t *wa,
+    callback_data_t *cd,
+    const lbfgs_parameter_t *param
+    );
+
+static int line_search_backtracking_owlqn(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *f,
+    lbfgsfloatval_t *g,
+    lbfgsfloatval_t *s,
+    lbfgsfloatval_t *stp,
+    const lbfgsfloatval_t* xp,
+    const lbfgsfloatval_t* gp,
+    lbfgsfloatval_t *wp,
+    callback_data_t *cd,
+    const lbfgs_parameter_t *param
+    );
+
+static int line_search_morethuente(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *f,
+    lbfgsfloatval_t *g,
+    lbfgsfloatval_t *s,
+    lbfgsfloatval_t *stp,
+    const lbfgsfloatval_t* xp,
+    const lbfgsfloatval_t* gp,
+    lbfgsfloatval_t *wa,
+    callback_data_t *cd,
+    const lbfgs_parameter_t *param
+    );
+
+static int update_trial_interval(
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *fx,
+    lbfgsfloatval_t *dx,
+    lbfgsfloatval_t *y,
+    lbfgsfloatval_t *fy,
+    lbfgsfloatval_t *dy,
+    lbfgsfloatval_t *t,
+    lbfgsfloatval_t *ft,
+    lbfgsfloatval_t *dt,
+    const lbfgsfloatval_t tmin,
+    const lbfgsfloatval_t tmax,
+    int *brackt
+    );
+
+static lbfgsfloatval_t owlqn_x1norm(
+    const lbfgsfloatval_t* x,
+    const int start,
+    const int n
+    );
+
+static void owlqn_pseudo_gradient(
+    lbfgsfloatval_t* pg,
+    const lbfgsfloatval_t* x,
+    const lbfgsfloatval_t* g,
+    const int n,
+    const lbfgsfloatval_t c,
+    const int start,
+    const int end
+    );
+
+static void owlqn_project(
+    lbfgsfloatval_t* d,
+    const lbfgsfloatval_t* sign,
+    const int start,
+    const int end
+    );
+
+
+#if     defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__))
+static int round_out_variables(int n)
+{
+    n += 7;
+    n /= 8;
+    n *= 8;
+    return n;
+}
+#endif/*defined(USE_SSE)*/
+
+lbfgsfloatval_t* lbfgs_malloc(int n)
+{
+#if     defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__))
+    n = round_out_variables(n);
+#endif/*defined(USE_SSE)*/
+    return (lbfgsfloatval_t*)vecalloc(sizeof(lbfgsfloatval_t) * (size_t) n);
+}
+
+void lbfgs_free(lbfgsfloatval_t *x)
+{
+    vecfree(x);
+}
+
+void lbfgs_parameter_init(lbfgs_parameter_t *param)
+{
+    memcpy(param, &_defparam, sizeof(*param));
+}
+
+int lbfgs(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *ptr_fx,
+    lbfgs_evaluate_t proc_evaluate,
+    lbfgs_progress_t proc_progress,
+    void *instance,
+    lbfgs_parameter_t *_param
+    )
+{
+    int ret;
+    int i, j, k, ls, end, bound;
+    lbfgsfloatval_t step;
+
+    /* Constant parameters and their default values. */
+    lbfgs_parameter_t param = (_param != NULL) ? (*_param) : _defparam;
+    const int m = param.m;
+
+    lbfgsfloatval_t *xp = NULL;
+    lbfgsfloatval_t *g = NULL, *gp = NULL, *pg = NULL;
+    lbfgsfloatval_t *d = NULL, *w = NULL, *pf = NULL;
+    iteration_data_t *lm = NULL, *it = NULL;
+    lbfgsfloatval_t ys, yy;
+    lbfgsfloatval_t xnorm, gnorm, beta;
+    lbfgsfloatval_t fx = 0.;
+    lbfgsfloatval_t rate = 0.;
+    line_search_proc linesearch = line_search_morethuente;
+
+    /* Construct a callback data. */
+    callback_data_t cd;
+    cd.n = n;
+    cd.instance = instance;
+    cd.proc_evaluate = proc_evaluate;
+    cd.proc_progress = proc_progress;
+
+#if     defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__))
+    /* Round out the number of variables. */
+    n = round_out_variables(n);
+#endif/*defined(USE_SSE)*/
+
+    /* Check the input parameters for errors. */
+    if (n <= 0) {
+        return LBFGSERR_INVALID_N;
+    }
+#if     defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__))
+    if (n % 8 != 0) {
+        return LBFGSERR_INVALID_N_SSE;
+    }
+    if (!is_aligned(x, 16)) {
+        return LBFGSERR_INVALID_X_SSE;
+    }
+#endif/*defined(USE_SSE)*/
+    if (param.epsilon < 0.) {
+        return LBFGSERR_INVALID_EPSILON;
+    }
+    if (param.past < 0) {
+        return LBFGSERR_INVALID_TESTPERIOD;
+    }
+    if (param.delta < 0.) {
+        return LBFGSERR_INVALID_DELTA;
+    }
+    if (param.min_step < 0.) {
+        return LBFGSERR_INVALID_MINSTEP;
+    }
+    if (param.max_step < param.min_step) {
+        return LBFGSERR_INVALID_MAXSTEP;
+    }
+    if (param.ftol < 0.) {
+        return LBFGSERR_INVALID_FTOL;
+    }
+    if (param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE ||
+        param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE) {
+        if (param.wolfe <= param.ftol || 1. <= param.wolfe) {
+            return LBFGSERR_INVALID_WOLFE;
+        }
+    }
+    if (param.gtol < 0.) {
+        return LBFGSERR_INVALID_GTOL;
+    }
+    if (param.xtol < 0.) {
+        return LBFGSERR_INVALID_XTOL;
+    }
+    if (param.max_linesearch <= 0) {
+        return LBFGSERR_INVALID_MAXLINESEARCH;
+    }
+    if (param.orthantwise_c < 0.) {
+        return LBFGSERR_INVALID_ORTHANTWISE;
+    }
+    if (param.orthantwise_start < 0 || n < param.orthantwise_start) {
+        return LBFGSERR_INVALID_ORTHANTWISE_START;
+    }
+    if (param.orthantwise_end < 0) {
+        param.orthantwise_end = n;
+    }
+    if (n < param.orthantwise_end) {
+        return LBFGSERR_INVALID_ORTHANTWISE_END;
+    }
+    if (param.orthantwise_c != 0.) {
+        switch (param.linesearch) {
+        case LBFGS_LINESEARCH_BACKTRACKING:
+            linesearch = line_search_backtracking_owlqn;
+            break;
+        default:
+            /* Only the backtracking method is available. */
+            return LBFGSERR_INVALID_LINESEARCH;
+        }
+    } else {
+        switch (param.linesearch) {
+        case LBFGS_LINESEARCH_MORETHUENTE:
+            linesearch = line_search_morethuente;
+            break;
+        case LBFGS_LINESEARCH_BACKTRACKING_ARMIJO:
+        case LBFGS_LINESEARCH_BACKTRACKING_WOLFE:
+        case LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE:
+            linesearch = line_search_backtracking;
+            break;
+        default:
+            return LBFGSERR_INVALID_LINESEARCH;
+        }
+    }
+
+    /* Allocate working space. */
+    xp = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t));
+    g = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t));
+    gp = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t));
+    d = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t));
+    w = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t));
+    if (xp == NULL || g == NULL || gp == NULL || d == NULL || w == NULL) {
+        ret = LBFGSERR_OUTOFMEMORY;
+        goto lbfgs_exit;
+    }
+
+    if (param.orthantwise_c != 0.) {
+        /* Allocate working space for OW-LQN. */
+        pg = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t));
+        if (pg == NULL) {
+            ret = LBFGSERR_OUTOFMEMORY;
+            goto lbfgs_exit;
+        }
+    }
+
+    /* Allocate limited memory storage. */
+    lm = (iteration_data_t*)vecalloc((size_t) m * sizeof(iteration_data_t));
+    if (lm == NULL) {
+        ret = LBFGSERR_OUTOFMEMORY;
+        goto lbfgs_exit;
+    }
+
+    /* Initialize the limited memory. */
+    for (i = 0;i < m;++i) {
+        it = &lm[i];
+        it->alpha = 0;
+        it->ys = 0;
+        it->s = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t));
+        it->y = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t));
+        if (it->s == NULL || it->y == NULL) {
+            ret = LBFGSERR_OUTOFMEMORY;
+            goto lbfgs_exit;
+        }
+    }
+
+    /* Allocate an array for storing previous values of the objective function. */
+    if (0 < param.past) {
+        pf = (lbfgsfloatval_t*)vecalloc((size_t) param.past * sizeof(lbfgsfloatval_t));
+    }
+
+    /* Evaluate the function value and its gradient. */
+    fx = cd.proc_evaluate(cd.instance, x, g, cd.n, 0);
+    if (0. != param.orthantwise_c) {
+        /* Compute the L1 norm of the variable and add it to the object value. */
+        xnorm = owlqn_x1norm(x, param.orthantwise_start, param.orthantwise_end);
+        fx += xnorm * param.orthantwise_c;
+        owlqn_pseudo_gradient(
+            pg, x, g, n,
+            param.orthantwise_c, param.orthantwise_start, param.orthantwise_end
+            );
+    }
+
+    /* Store the initial value of the objective function. */
+    if (pf != NULL) {
+        pf[0] = fx;
+    }
+
+    /*
+        Compute the direction;
+        we assume the initial hessian matrix H_0 as the identity matrix.
+     */
+    if (param.orthantwise_c == 0.) {
+        vecncpy(d, g, n);
+    } else {
+        vecncpy(d, pg, n);
+    }
+
+    /*
+       Make sure that the initial variables are not a minimizer.
+     */
+    vec2norm(&xnorm, x, n);
+    if (param.orthantwise_c == 0.) {
+        vec2norm(&gnorm, g, n);
+    } else {
+        vec2norm(&gnorm, pg, n);
+    }
+    if (xnorm < 1.0) xnorm = 1.0;
+    if (gnorm / xnorm <= param.epsilon) {
+        ret = LBFGS_ALREADY_MINIMIZED;
+        goto lbfgs_exit;
+    }
+
+    /* Compute the initial step:
+        step = 1.0 / sqrt(vecdot(d, d, n))
+     */
+    vec2norminv(&step, d, n);
+
+    k = 1;
+    end = 0;
+    for (;;) {
+        /* Store the current position and gradient vectors. */
+        veccpy(xp, x, n);
+        veccpy(gp, g, n);
+
+        /* Search for an optimal step. */
+        if (param.orthantwise_c == 0.) {
+            ls = linesearch(n, x, &fx, g, d, &step, xp, gp, w, &cd, &param);
+        } else {
+            ls = linesearch(n, x, &fx, g, d, &step, xp, pg, w, &cd, &param);
+            owlqn_pseudo_gradient(
+                pg, x, g, n,
+                param.orthantwise_c, param.orthantwise_start, param.orthantwise_end
+                );
+        }
+        if (ls < 0) {
+            /* Revert to the previous point. */
+            veccpy(x, xp, n);
+            veccpy(g, gp, n);
+            ret = ls;
+            goto lbfgs_exit;
+        }
+
+        /* Compute x and g norms. */
+        vec2norm(&xnorm, x, n);
+        if (param.orthantwise_c == 0.) {
+            vec2norm(&gnorm, g, n);
+        } else {
+            vec2norm(&gnorm, pg, n);
+        }
+
+        /* Report the progress. */
+        if (cd.proc_progress) {
+            if ((ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls))) {
+                goto lbfgs_exit;
+            }
+        }
+
+        /*
+            Convergence test.
+            The criterion is given by the following formula:
+                |g(x)| / \max(1, |x|) < \epsilon
+         */
+        if (xnorm < 1.0) xnorm = 1.0;
+        if (gnorm / xnorm <= param.epsilon) {
+            /* Convergence. */
+            ret = LBFGS_SUCCESS;
+            break;
+        }
+
+        /*
+            Test for stopping criterion.
+            The criterion is given by the following formula:
+                (f(past_x) - f(x)) / f(x) < \delta
+         */
+        if (pf != NULL) {
+            /* We don't test the stopping criterion while k < past. */
+            if (param.past <= k) {
+                /* Compute the relative improvement from the past. */
+                rate = (pf[k % param.past] - fx) / fx;
+
+                /* The stopping criterion. */
+                if (rate < param.delta) {
+                    ret = LBFGS_STOP;
+                    break;
+                }
+            }
+
+            /* Store the current value of the objective function. */
+            pf[k % param.past] = fx;
+        }
+
+        if (param.max_iterations != 0 && param.max_iterations < k+1) {
+            /* Maximum number of iterations. */
+            ret = LBFGSERR_MAXIMUMITERATION;
+            break;
+        }
+
+        /*
+            Update vectors s and y:
+                s_{k+1} = x_{k+1} - x_{k} = \step * d_{k}.
+                y_{k+1} = g_{k+1} - g_{k}.
+         */
+        it = &lm[end];
+        vecdiff(it->s, x, xp, n);
+        vecdiff(it->y, g, gp, n);
+
+        /*
+            Compute scalars ys and yy:
+                ys = y^t \cdot s = 1 / \rho.
+                yy = y^t \cdot y.
+            Notice that yy is used for scaling the hessian matrix H_0 (Cholesky factor).
+         */
+        vecdot(&ys, it->y, it->s, n);
+        vecdot(&yy, it->y, it->y, n);
+        it->ys = ys;
+
+        /*
+            Recursive formula to compute dir = -(H \cdot g).
+                This is described in page 779 of:
+                Jorge Nocedal.
+                Updating Quasi-Newton Matrices with Limited Storage.
+                Mathematics of Computation, Vol. 35, No. 151,
+                pp. 773--782, 1980.
+         */
+        bound = (m <= k) ? m : k;
+        ++k;
+        end = (end + 1) % m;
+
+        /* Compute the steepest direction. */
+        if (param.orthantwise_c == 0.) {
+            /* Compute the negative of gradients. */
+            vecncpy(d, g, n);
+        } else {
+            vecncpy(d, pg, n);
+        }
+
+        j = end;
+        for (i = 0;i < bound;++i) {
+            j = (j + m - 1) % m;    /* if (--j == -1) j = m-1; */
+            it = &lm[j];
+            /* \alpha_{j} = \rho_{j} s^{t}_{j} \cdot q_{k+1}. */
+            vecdot(&it->alpha, it->s, d, n);
+            it->alpha /= it->ys;
+            /* q_{i} = q_{i+1} - \alpha_{i} y_{i}. */
+            vecadd(d, it->y, -it->alpha, n);
+        }
+
+        vecscale(d, ys / yy, n);
+
+        for (i = 0;i < bound;++i) {
+            it = &lm[j];
+            /* \beta_{j} = \rho_{j} y^t_{j} \cdot \gamma_{i}. */
+            vecdot(&beta, it->y, d, n);
+            beta /= it->ys;
+            /* \gamma_{i+1} = \gamma_{i} + (\alpha_{j} - \beta_{j}) s_{j}. */
+            vecadd(d, it->s, it->alpha - beta, n);
+            j = (j + 1) % m;        /* if (++j == m) j = 0; */
+        }
+
+        /*
+            Constrain the search direction for orthant-wise updates.
+         */
+        if (param.orthantwise_c != 0.) {
+            for (i = param.orthantwise_start;i < param.orthantwise_end;++i) {
+                if (d[i] * pg[i] >= 0) {
+                    d[i] = 0;
+                }
+            }
+        }
+
+        /*
+            Now the search direction d is ready. We try step = 1 first.
+         */
+        step = 1.0;
+    }
+
+lbfgs_exit:
+    /* Return the final value of the objective function. */
+    if (ptr_fx != NULL) {
+        *ptr_fx = fx;
+    }
+
+    vecfree(pf);
+
+    /* Free memory blocks used by this function. */
+    if (lm != NULL) {
+        for (i = 0;i < m;++i) {
+            vecfree(lm[i].s);
+            vecfree(lm[i].y);
+        }
+        vecfree(lm);
+    }
+    vecfree(pg);
+    vecfree(w);
+    vecfree(d);
+    vecfree(gp);
+    vecfree(g);
+    vecfree(xp);
+
+    return ret;
+}
+
+
+
+static int line_search_backtracking(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *f,
+    lbfgsfloatval_t *g,
+    lbfgsfloatval_t *s,
+    lbfgsfloatval_t *stp,
+    const lbfgsfloatval_t* xp,
+    const lbfgsfloatval_t* gp,
+    lbfgsfloatval_t *wp,
+    callback_data_t *cd,
+    const lbfgs_parameter_t *param
+    )
+{
+    int count = 0;
+    lbfgsfloatval_t width, dg;
+    lbfgsfloatval_t finit, dginit = 0., dgtest;
+    const lbfgsfloatval_t dec = 0.5, inc = 2.1;
+
+    /* Check the input parameters for errors. */
+    if (*stp <= 0.) {
+        return LBFGSERR_INVALIDPARAMETERS;
+    }
+
+    /* Compute the initial gradient in the search direction. */
+    vecdot(&dginit, g, s, n);
+
+    /* Make sure that s points to a descent direction. */
+    if (0 < dginit) {
+        return LBFGSERR_INCREASEGRADIENT;
+    }
+
+    /* The initial value of the objective function. */
+    finit = *f;
+    dgtest = param->ftol * dginit;
+
+    for (;;) {
+        veccpy(x, xp, n);
+        vecadd(x, s, *stp, n);
+
+        /* Evaluate the function and gradient values. */
+        *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp);
+
+        ++count;
+
+        if (*f > finit + *stp * dgtest) {
+            width = dec;
+        } else {
+            /* The sufficient decrease condition (Armijo condition). */
+            if (param->linesearch == LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) {
+                /* Exit with the Armijo condition. */
+                return count;
+	        }
+
+	        /* Check the Wolfe condition. */
+	        vecdot(&dg, g, s, n);
+	        if (dg < param->wolfe * dginit) {
+    		    width = inc;
+	        } else {
+		        if(param->linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE) {
+		            /* Exit with the regular Wolfe condition. */
+		            return count;
+		        }
+
+		        /* Check the strong Wolfe condition. */
+		        if(dg > -param->wolfe * dginit) {
+		            width = dec;
+		        } else {
+		            /* Exit with the strong Wolfe condition. */
+		            return count;
+		        }
+            }
+        }
+
+        if (*stp < param->min_step) {
+            /* The step is the minimum value. */
+            return LBFGSERR_MINIMUMSTEP;
+        }
+        if (*stp > param->max_step) {
+            /* The step is the maximum value. */
+            return LBFGSERR_MAXIMUMSTEP;
+        }
+        if (param->max_linesearch <= count) {
+            /* Maximum number of iteration. */
+            return LBFGSERR_MAXIMUMLINESEARCH;
+        }
+
+        (*stp) *= width;
+    }
+}
+
+
+
+static int line_search_backtracking_owlqn(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *f,
+    lbfgsfloatval_t *g,
+    lbfgsfloatval_t *s,
+    lbfgsfloatval_t *stp,
+    const lbfgsfloatval_t* xp,
+    const lbfgsfloatval_t* gp,
+    lbfgsfloatval_t *wp,
+    callback_data_t *cd,
+    const lbfgs_parameter_t *param
+    )
+{
+    int i, count = 0;
+    lbfgsfloatval_t width = 0.5, norm = 0.;
+    lbfgsfloatval_t finit = *f, dgtest;
+
+    /* Check the input parameters for errors. */
+    if (*stp <= 0.) {
+        return LBFGSERR_INVALIDPARAMETERS;
+    }
+
+    /* Choose the orthant for the new point. */
+    for (i = 0;i < n;++i) {
+        wp[i] = (xp[i] == 0.) ? -gp[i] : xp[i];
+    }
+
+    for (;;) {
+        /* Update the current point. */
+        veccpy(x, xp, n);
+        vecadd(x, s, *stp, n);
+
+        /* The current point is projected onto the orthant. */
+        owlqn_project(x, wp, param->orthantwise_start, param->orthantwise_end);
+
+        /* Evaluate the function and gradient values. */
+        *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp);
+
+        /* Compute the L1 norm of the variables and add it to the object value. */
+        norm = owlqn_x1norm(x, param->orthantwise_start, param->orthantwise_end);
+        *f += norm * param->orthantwise_c;
+
+        ++count;
+
+        dgtest = 0.;
+        for (i = 0;i < n;++i) {
+            dgtest += (x[i] - xp[i]) * gp[i];
+        }
+
+        if (*f <= finit + param->ftol * dgtest) {
+            /* The sufficient decrease condition. */
+            return count;
+        }
+
+        if (*stp < param->min_step) {
+            /* The step is the minimum value. */
+            return LBFGSERR_MINIMUMSTEP;
+        }
+        if (*stp > param->max_step) {
+            /* The step is the maximum value. */
+            return LBFGSERR_MAXIMUMSTEP;
+        }
+        if (param->max_linesearch <= count) {
+            /* Maximum number of iteration. */
+            return LBFGSERR_MAXIMUMLINESEARCH;
+        }
+
+        (*stp) *= width;
+    }
+}
+
+
+
+static int line_search_morethuente(
+    int n,
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *f,
+    lbfgsfloatval_t *g,
+    lbfgsfloatval_t *s,
+    lbfgsfloatval_t *stp,
+    const lbfgsfloatval_t* xp,
+    const lbfgsfloatval_t* gp,
+    lbfgsfloatval_t *wa,
+    callback_data_t *cd,
+    const lbfgs_parameter_t *param
+    )
+{
+    int count = 0;
+    int brackt, stage1, uinfo = 0;
+    lbfgsfloatval_t dg;
+    lbfgsfloatval_t stx, fx, dgx;
+    lbfgsfloatval_t sty, fy, dgy;
+    lbfgsfloatval_t fxm, dgxm, fym, dgym, fm, dgm;
+    lbfgsfloatval_t finit, ftest1, dginit, dgtest;
+    lbfgsfloatval_t width, prev_width;
+    lbfgsfloatval_t stmin, stmax;
+
+    /* Check the input parameters for errors. */
+    if (*stp <= 0.) {
+        return LBFGSERR_INVALIDPARAMETERS;
+    }
+
+    /* Compute the initial gradient in the search direction. */
+    vecdot(&dginit, g, s, n);
+
+    /* Make sure that s points to a descent direction. */
+    if (0 < dginit) {
+        return LBFGSERR_INCREASEGRADIENT;
+    }
+
+    /* Initialize local variables. */
+    brackt = 0;
+    stage1 = 1;
+    finit = *f;
+    dgtest = param->ftol * dginit;
+    width = param->max_step - param->min_step;
+    prev_width = 2.0 * width;
+
+    /*
+        The variables stx, fx, dgx contain the values of the step,
+        function, and directional derivative at the best step.
+        The variables sty, fy, dgy contain the value of the step,
+        function, and derivative at the other endpoint of
+        the interval of uncertainty.
+        The variables stp, f, dg contain the values of the step,
+        function, and derivative at the current step.
+    */
+    stx = sty = 0.;
+    fx = fy = finit;
+    dgx = dgy = dginit;
+
+    for (;;) {
+        /*
+            Set the minimum and maximum steps to correspond to the
+            present interval of uncertainty.
+         */
+        if (brackt) {
+            stmin = min2(stx, sty);
+            stmax = max2(stx, sty);
+        } else {
+            stmin = stx;
+            stmax = *stp + 4.0 * (*stp - stx);
+        }
+
+        /* Clip the step in the range of [stpmin, stpmax]. */
+        if (*stp < param->min_step) *stp = param->min_step;
+        if (param->max_step < *stp) *stp = param->max_step;
+
+        /*
+            If an unusual termination is to occur then let
+            stp be the lowest point obtained so far.
+         */
+        if ((brackt && ((*stp <= stmin || stmax <= *stp) || param->max_linesearch <= count + 1 || uinfo != 0)) || (brackt && (stmax - stmin <= param->xtol * stmax))) {
+            *stp = stx;
+        }
+
+        /*
+            Compute the current value of x:
+                x <- x + (*stp) * s.
+         */
+        veccpy(x, xp, n);
+        vecadd(x, s, *stp, n);
+
+        /* Evaluate the function and gradient values. */
+        *f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp);
+        vecdot(&dg, g, s, n);
+
+        ftest1 = finit + *stp * dgtest;
+        ++count;
+
+        /* Test for errors and convergence. */
+        if (brackt && ((*stp <= stmin || stmax <= *stp) || uinfo != 0)) {
+            /* Rounding errors prevent further progress. */
+            return LBFGSERR_ROUNDING_ERROR;
+        }
+        if (*stp == param->max_step && *f <= ftest1 && dg <= dgtest) {
+            /* The step is the maximum value. */
+            return LBFGSERR_MAXIMUMSTEP;
+        }
+        if (*stp == param->min_step && (ftest1 < *f || dgtest <= dg)) {
+            /* The step is the minimum value. */
+            return LBFGSERR_MINIMUMSTEP;
+        }
+        if (brackt && (stmax - stmin) <= param->xtol * stmax) {
+            /* Relative width of the interval of uncertainty is at most xtol. */
+            return LBFGSERR_WIDTHTOOSMALL;
+        }
+        if (param->max_linesearch <= count) {
+            /* Maximum number of iteration. */
+            return LBFGSERR_MAXIMUMLINESEARCH;
+        }
+        if (*f <= ftest1 && fabs(dg) <= param->gtol * (-dginit)) {
+            /* The sufficient decrease condition and the directional derivative condition hold. */
+            return count;
+        }
+
+        /*
+            In the first stage we seek a step for which the modified
+            function has a nonpositive value and nonnegative derivative.
+         */
+        if (stage1 && *f <= ftest1 && min2(param->ftol, param->gtol) * dginit <= dg) {
+            stage1 = 0;
+        }
+
+        /*
+            A modified function is used to predict the step only if
+            we have not obtained a step for which the modified
+            function has a nonpositive function value and nonnegative
+            derivative, and if a lower function value has been
+            obtained but the decrease is not sufficient.
+         */
+        if (stage1 && ftest1 < *f && *f <= fx) {
+            /* Define the modified function and derivative values. */
+            fm = *f - *stp * dgtest;
+            fxm = fx - stx * dgtest;
+            fym = fy - sty * dgtest;
+            dgm = dg - dgtest;
+            dgxm = dgx - dgtest;
+            dgym = dgy - dgtest;
+
+            /*
+                Call update_trial_interval() to update the interval of
+                uncertainty and to compute the new step.
+             */
+            uinfo = update_trial_interval(
+                &stx, &fxm, &dgxm,
+                &sty, &fym, &dgym,
+                stp, &fm, &dgm,
+                stmin, stmax, &brackt
+                );
+
+            /* Reset the function and gradient values for f. */
+            fx = fxm + stx * dgtest;
+            fy = fym + sty * dgtest;
+            dgx = dgxm + dgtest;
+            dgy = dgym + dgtest;
+        } else {
+            /*
+                Call update_trial_interval() to update the interval of
+                uncertainty and to compute the new step.
+             */
+            uinfo = update_trial_interval(
+                &stx, &fx, &dgx,
+                &sty, &fy, &dgy,
+                stp, f, &dg,
+                stmin, stmax, &brackt
+                );
+        }
+
+        /*
+            Force a sufficient decrease in the interval of uncertainty.
+         */
+        if (brackt) {
+            if (0.66 * prev_width <= fabs(sty - stx)) {
+                *stp = stx + 0.5 * (sty - stx);
+            }
+            prev_width = width;
+            width = fabs(sty - stx);
+        }
+    }
+
+    return LBFGSERR_LOGICERROR;
+}
+
+
+
+/**
+ * Define the local variables for computing minimizers.
+ */
+#define USES_MINIMIZER \
+    lbfgsfloatval_t a, d, gamma, theta, p, q, r, s;
+
+/**
+ * Find a minimizer of an interpolated cubic function.
+ *  @param  cm      The minimizer of the interpolated cubic.
+ *  @param  u       The value of one point, u.
+ *  @param  fu      The value of f(u).
+ *  @param  du      The value of f'(u).
+ *  @param  v       The value of another point, v.
+ *  @param  fv      The value of f(v).
+ *  @param  du      The value of f'(v).
+ */
+#define CUBIC_MINIMIZER(cm, u, fu, du, v, fv, dv) \
+    d = (v) - (u); \
+    theta = ((fu) - (fv)) * 3 / d + (du) + (dv); \
+    p = fabs(theta); \
+    q = fabs(du); \
+    r = fabs(dv); \
+    s = max3(p, q, r); \
+    /* gamma = s*sqrt((theta/s)**2 - (du/s) * (dv/s)) */ \
+    a = theta / s; \
+    gamma = s * sqrt(a * a - ((du) / s) * ((dv) / s)); \
+    if ((v) < (u)) gamma = -gamma; \
+    p = gamma - (du) + theta; \
+    q = gamma - (du) + gamma + (dv); \
+    r = p / q; \
+    (cm) = (u) + r * d;
+
+/**
+ * Find a minimizer of an interpolated cubic function.
+ *  @param  cm      The minimizer of the interpolated cubic.
+ *  @param  u       The value of one point, u.
+ *  @param  fu      The value of f(u).
+ *  @param  du      The value of f'(u).
+ *  @param  v       The value of another point, v.
+ *  @param  fv      The value of f(v).
+ *  @param  du      The value of f'(v).
+ *  @param  xmin    The maximum value.
+ *  @param  xmin    The minimum value.
+ */
+#define CUBIC_MINIMIZER2(cm, u, fu, du, v, fv, dv, xmin, xmax) \
+    d = (v) - (u); \
+    theta = ((fu) - (fv)) * 3 / d + (du) + (dv); \
+    p = fabs(theta); \
+    q = fabs(du); \
+    r = fabs(dv); \
+    s = max3(p, q, r); \
+    /* gamma = s*sqrt((theta/s)**2 - (du/s) * (dv/s)) */ \
+    a = theta / s; \
+    gamma = s * sqrt(max2(0, a * a - ((du) / s) * ((dv) / s))); \
+    if ((u) < (v)) gamma = -gamma; \
+    p = gamma - (dv) + theta; \
+    q = gamma - (dv) + gamma + (du); \
+    r = p / q; \
+    if (r < 0. && gamma != 0.) { \
+        (cm) = (v) - r * d; \
+    } else if (a < 0) { \
+        (cm) = (xmax); \
+    } else { \
+        (cm) = (xmin); \
+    }
+
+/**
+ * Find a minimizer of an interpolated quadratic function.
+ *  @param  qm      The minimizer of the interpolated quadratic.
+ *  @param  u       The value of one point, u.
+ *  @param  fu      The value of f(u).
+ *  @param  du      The value of f'(u).
+ *  @param  v       The value of another point, v.
+ *  @param  fv      The value of f(v).
+ */
+#define QUARD_MINIMIZER(qm, u, fu, du, v, fv) \
+    a = (v) - (u); \
+    (qm) = (u) + (du) / (((fu) - (fv)) / a + (du)) / 2 * a;
+
+/**
+ * Find a minimizer of an interpolated quadratic function.
+ *  @param  qm      The minimizer of the interpolated quadratic.
+ *  @param  u       The value of one point, u.
+ *  @param  du      The value of f'(u).
+ *  @param  v       The value of another point, v.
+ *  @param  dv      The value of f'(v).
+ */
+#define QUARD_MINIMIZER2(qm, u, du, v, dv) \
+    a = (u) - (v); \
+    (qm) = (v) + (dv) / ((dv) - (du)) * a;
+
+/**
+ * Update a safeguarded trial value and interval for line search.
+ *
+ *  The parameter x represents the step with the least function value.
+ *  The parameter t represents the current step. This function assumes
+ *  that the derivative at the point of x in the direction of the step.
+ *  If the bracket is set to true, the minimizer has been bracketed in
+ *  an interval of uncertainty with endpoints between x and y.
+ *
+ *  @param  x       The pointer to the value of one endpoint.
+ *  @param  fx      The pointer to the value of f(x).
+ *  @param  dx      The pointer to the value of f'(x).
+ *  @param  y       The pointer to the value of another endpoint.
+ *  @param  fy      The pointer to the value of f(y).
+ *  @param  dy      The pointer to the value of f'(y).
+ *  @param  t       The pointer to the value of the trial value, t.
+ *  @param  ft      The pointer to the value of f(t).
+ *  @param  dt      The pointer to the value of f'(t).
+ *  @param  tmin    The minimum value for the trial value, t.
+ *  @param  tmax    The maximum value for the trial value, t.
+ *  @param  brackt  The pointer to the predicate if the trial value is
+ *                  bracketed.
+ *  @retval int     Status value. Zero indicates a normal termination.
+ *  
+ *  @see
+ *      Jorge J. More and David J. Thuente. Line search algorithm with
+ *      guaranteed sufficient decrease. ACM Transactions on Mathematical
+ *      Software (TOMS), Vol 20, No 3, pp. 286-307, 1994.
+ */
+static int update_trial_interval(
+    lbfgsfloatval_t *x,
+    lbfgsfloatval_t *fx,
+    lbfgsfloatval_t *dx,
+    lbfgsfloatval_t *y,
+    lbfgsfloatval_t *fy,
+    lbfgsfloatval_t *dy,
+    lbfgsfloatval_t *t,
+    lbfgsfloatval_t *ft,
+    lbfgsfloatval_t *dt,
+    const lbfgsfloatval_t tmin,
+    const lbfgsfloatval_t tmax,
+    int *brackt
+    )
+{
+    int bound;
+    int dsign = fsigndiff(dt, dx);
+    lbfgsfloatval_t mc; /* minimizer of an interpolated cubic. */
+    lbfgsfloatval_t mq; /* minimizer of an interpolated quadratic. */
+    lbfgsfloatval_t newt;   /* new trial value. */
+    USES_MINIMIZER;     /* for CUBIC_MINIMIZER and QUARD_MINIMIZER. */
+
+    /* Check the input parameters for errors. */
+    if (*brackt) {
+        if (*t <= min2(*x, *y) || max2(*x, *y) <= *t) {
+            /* The trival value t is out of the interval. */
+            return LBFGSERR_OUTOFINTERVAL;
+        }
+        if (0. <= *dx * (*t - *x)) {
+            /* The function must decrease from x. */
+            return LBFGSERR_INCREASEGRADIENT;
+        }
+        if (tmax < tmin) {
+            /* Incorrect tmin and tmax specified. */
+            return LBFGSERR_INCORRECT_TMINMAX;
+        }
+    }
+
+    /*
+        Trial value selection.
+     */
+    if (*fx < *ft) {
+        /*
+            Case 1: a higher function value.
+            The minimum is brackt. If the cubic minimizer is closer
+            to x than the quadratic one, the cubic one is taken, else
+            the average of the minimizers is taken.
+         */
+        *brackt = 1;
+        bound = 1;
+        CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt);
+        QUARD_MINIMIZER(mq, *x, *fx, *dx, *t, *ft);
+        if (fabs(mc - *x) < fabs(mq - *x)) {
+            newt = mc;
+        } else {
+            newt = mc + 0.5 * (mq - mc);
+        }
+    } else if (dsign) {
+        /*
+            Case 2: a lower function value and derivatives of
+            opposite sign. The minimum is brackt. If the cubic
+            minimizer is closer to x than the quadratic (secant) one,
+            the cubic one is taken, else the quadratic one is taken.
+         */
+        *brackt = 1;
+        bound = 0;
+        CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt);
+        QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt);
+        if (fabs(mc - *t) > fabs(mq - *t)) {
+            newt = mc;
+        } else {
+            newt = mq;
+        }
+    } else if (fabs(*dt) < fabs(*dx)) {
+        /*
+            Case 3: a lower function value, derivatives of the
+            same sign, and the magnitude of the derivative decreases.
+            The cubic minimizer is only used if the cubic tends to
+            infinity in the direction of the minimizer or if the minimum
+            of the cubic is beyond t. Otherwise the cubic minimizer is
+            defined to be either tmin or tmax. The quadratic (secant)
+            minimizer is also computed and if the minimum is brackt
+            then the the minimizer closest to x is taken, else the one
+            farthest away is taken.
+         */
+        bound = 1;
+        CUBIC_MINIMIZER2(mc, *x, *fx, *dx, *t, *ft, *dt, tmin, tmax);
+        QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt);
+        if (*brackt) {
+            if (fabs(*t - mc) < fabs(*t - mq)) {
+                newt = mc;
+            } else {
+                newt = mq;
+            }
+        } else {
+            if (fabs(*t - mc) > fabs(*t - mq)) {
+                newt = mc;
+            } else {
+                newt = mq;
+            }
+        }
+    } else {
+        /*
+            Case 4: a lower function value, derivatives of the
+            same sign, and the magnitude of the derivative does
+            not decrease. If the minimum is not brackt, the step
+            is either tmin or tmax, else the cubic minimizer is taken.
+         */
+        bound = 0;
+        if (*brackt) {
+            CUBIC_MINIMIZER(newt, *t, *ft, *dt, *y, *fy, *dy);
+        } else if (*x < *t) {
+            newt = tmax;
+        } else {
+            newt = tmin;
+        }
+    }
+
+    /*
+        Update the interval of uncertainty. This update does not
+        depend on the new step or the case analysis above.
+
+        - Case a: if f(x) < f(t),
+            x <- x, y <- t.
+        - Case b: if f(t) <= f(x) && f'(t)*f'(x) > 0,
+            x <- t, y <- y.
+        - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, 
+            x <- t, y <- x.
+     */
+    if (*fx < *ft) {
+        /* Case a */
+        *y = *t;
+        *fy = *ft;
+        *dy = *dt;
+    } else {
+        /* Case c */
+        if (dsign) {
+            *y = *x;
+            *fy = *fx;
+            *dy = *dx;
+        }
+        /* Cases b and c */
+        *x = *t;
+        *fx = *ft;
+        *dx = *dt;
+    }
+
+    /* Clip the new trial value in [tmin, tmax]. */
+    if (tmax < newt) newt = tmax;
+    if (newt < tmin) newt = tmin;
+
+    /*
+        Redefine the new trial value if it is close to the upper bound
+        of the interval.
+     */
+    if (*brackt && bound) {
+        mq = *x + 0.66 * (*y - *x);
+        if (*x < *y) {
+            if (mq < newt) newt = mq;
+        } else {
+            if (newt < mq) newt = mq;
+        }
+    }
+
+    /* Return the new trial value. */
+    *t = newt;
+    return 0;
+}
+
+
+
+
+
+static lbfgsfloatval_t owlqn_x1norm(
+    const lbfgsfloatval_t* x,
+    const int start,
+    const int n
+    )
+{
+    int i;
+    lbfgsfloatval_t norm = 0.;
+
+    for (i = start;i < n;++i) {
+        norm += fabs(x[i]);
+    }
+
+    return norm;
+}
+
+static void owlqn_pseudo_gradient(
+    lbfgsfloatval_t* pg,
+    const lbfgsfloatval_t* x,
+    const lbfgsfloatval_t* g,
+    const int n,
+    const lbfgsfloatval_t c,
+    const int start,
+    const int end
+    )
+{
+    int i;
+
+    /* Compute the negative of gradients. */
+    for (i = 0;i < start;++i) {
+        pg[i] = g[i];
+    }
+
+    /* Compute the psuedo-gradients. */
+    for (i = start;i < end;++i) {
+        if (x[i] < 0.) {
+            /* Differentiable. */
+            pg[i] = g[i] - c;
+        } else if (0. < x[i]) {
+            /* Differentiable. */
+            pg[i] = g[i] + c;
+        } else {
+            if (g[i] < -c) {
+                /* Take the right partial derivative. */
+                pg[i] = g[i] + c;
+            } else if (c < g[i]) {
+                /* Take the left partial derivative. */
+                pg[i] = g[i] - c;
+            } else {
+                pg[i] = 0.;
+            }
+        }
+    }
+
+    for (i = end;i < n;++i) {
+        pg[i] = g[i];
+    }
+}
+
+static void owlqn_project(
+    lbfgsfloatval_t* d,
+    const lbfgsfloatval_t* sign,
+    const int start,
+    const int end
+    )
+{
+    int i;
+
+    for (i = start;i < end;++i) {
+        if (d[i] * sign[i] <= 0) {
+            d[i] = 0;
+        }
+    }
+}
diff --git a/igraph/src/lbitbits.c b/igraph/src/lbitbits.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lbitbits.c
@@ -0,0 +1,68 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LONGBITS
+#define LONGBITS 32
+#endif
+
+ integer
+#ifdef KR_headers
+lbit_bits(a, b, len) integer a, b, len;
+#else
+lbit_bits(integer a, integer b, integer len)
+#endif
+{
+	/* Assume 2's complement arithmetic */
+
+	unsigned long x, y;
+
+	x = (unsigned long) a;
+	y = (unsigned long)-1L;
+	x >>= b;
+	y <<= len;
+	return (integer)(x & ~y);
+	}
+
+ integer
+#ifdef KR_headers
+lbit_cshift(a, b, len) integer a, b, len;
+#else
+lbit_cshift(integer a, integer b, integer len)
+#endif
+{
+	unsigned long x, y, z;
+
+	x = (unsigned long)a;
+	if (len <= 0) {
+		if (len == 0)
+			return 0;
+		goto full_len;
+		}
+	if (len >= LONGBITS) {
+ full_len:
+		if (b >= 0) {
+			b %= LONGBITS;
+			return (integer)(x << b | x >> LONGBITS -b );
+			}
+		b = -b;
+		b %= LONGBITS;
+		return (integer)(x << LONGBITS - b | x >> b);
+		}
+	y = z = (unsigned long)-1;
+	y <<= len;
+	z &= ~y;
+	y &= x;
+	x &= z;
+	if (b >= 0) {
+		b %= len;
+		return (integer)(y | z & (x << b | x >> len - b));
+		}
+	b = -b;
+	b %= len;
+	return (integer)(y | z & (x >> b | x << len - b));
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/lbitshft.c b/igraph/src/lbitshft.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lbitshft.c
@@ -0,0 +1,17 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ integer
+#ifdef KR_headers
+lbit_shift(a, b) integer a; integer b;
+#else
+lbit_shift(integer a, integer b)
+#endif
+{
+	return b >= 0 ? a << b : (integer)((uinteger)a >> -b);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/len_trim.c b/igraph/src/len_trim.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/len_trim.c
@@ -0,0 +1,36 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+
+/*  -- LEN_TRIM is Fortran 95, so we use a replacement here */
+
+integer igraphlen_trim__(char *s, ftnlen s_len)
+{
+    /* System generated locals */
+    integer ret_val;
+
+    /* Builtin functions */
+    integer i_len(char *, ftnlen);
+
+
+
+
+    for (ret_val = i_len(s, s_len); ret_val >= 1; --ret_val) {
+	if (*(unsigned char *)&s[ret_val - 1] != ' ') {
+	    return ret_val;
+	}
+    }
+    return ret_val;
+} /* igraphlen_trim__ */
+
diff --git a/igraph/src/lread.c b/igraph/src/lread.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lread.c
@@ -0,0 +1,806 @@
+#include "f2c.h"
+#include "fio.h"
+
+/* Compile with -DF8X_NML_ELIDE_QUOTES to permit eliding quotation */
+/* marks in namelist input a la the Fortran 8X Draft published in  */
+/* the May 1989 issue of Fortran Forum. */
+
+
+#ifdef Allow_TYQUAD
+static longint f__llx;
+#endif
+
+#ifdef KR_headers
+extern double atof();
+extern char *malloc(), *realloc();
+int (*f__lioproc)(), (*l_getc)(), (*l_ungetc)();
+#else
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#endif
+
+#include "fmt.h"
+#include "lio.h"
+#include "ctype.h"
+#include "fp.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern char *f__fmtbuf;
+#else
+extern const char *f__fmtbuf;
+int (*f__lioproc)(ftnint*, char*, ftnlen, ftnint), (*l_getc)(void),
+	(*l_ungetc)(int,FILE*);
+#endif
+
+int l_eof;
+
+#define isblnk(x) (f__ltab[x+1]&B)
+#define issep(x) (f__ltab[x+1]&SX)
+#define isapos(x) (f__ltab[x+1]&AX)
+#define isexp(x) (f__ltab[x+1]&EX)
+#define issign(x) (f__ltab[x+1]&SG)
+#define iswhit(x) (f__ltab[x+1]&WH)
+#define SX 1
+#define B 2
+#define AX 4
+#define EX 8
+#define SG 16
+#define WH 32
+char f__ltab[128+1] = {	/* offset one for EOF */
+	0,
+	0,0,AX,0,0,0,0,0,0,WH|B,SX|WH,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	SX|B|WH,0,AX,0,0,0,0,AX,0,0,0,SG,SX,SG,0,SX,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,EX,EX,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	AX,0,0,0,EX,EX,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+#ifdef ungetc
+ static int
+#ifdef KR_headers
+un_getc(x,f__cf) int x; FILE *f__cf;
+#else
+un_getc(int x, FILE *f__cf)
+#endif
+{ return ungetc(x,f__cf); }
+#else
+#define un_getc ungetc
+#ifdef KR_headers
+ extern int ungetc();
+#else
+extern int ungetc(int, FILE*);	/* for systems with a buggy stdio.h */
+#endif
+#endif
+
+ int
+t_getc(Void)
+{	int ch;
+	if(f__curunit->uend) return(EOF);
+	if((ch=getc(f__cf))!=EOF) return(ch);
+	if(feof(f__cf))
+		f__curunit->uend = l_eof = 1;
+	return(EOF);
+}
+integer e_rsle(Void)
+{
+	int ch;
+	if(f__curunit->uend) return(0);
+	while((ch=t_getc())!='\n')
+		if (ch == EOF) {
+			if(feof(f__cf))
+				f__curunit->uend = l_eof = 1;
+			return EOF;
+			}
+	return(0);
+}
+
+flag f__lquit;
+int f__lcount,f__ltype,nml_read;
+char *f__lchar;
+double f__lx,f__ly;
+#define ERR(x) if(n=(x)) return(n)
+#define GETC(x) (x=(*l_getc)())
+#define Ungetc(x,y) (*l_ungetc)(x,y)
+
+ static int
+#ifdef KR_headers
+l_R(poststar, reqint) int poststar, reqint;
+#else
+l_R(int poststar, int reqint)
+#endif
+{
+	char s[FMAX+EXPMAXDIGS+4];
+	register int ch;
+	register char *sp, *spe, *sp1;
+	long e, exp;
+	int havenum, havestar, se;
+
+	if (!poststar) {
+		if (f__lcount > 0)
+			return(0);
+		f__lcount = 1;
+		}
+#ifdef Allow_TYQUAD
+	f__llx = 0;
+#endif
+	f__ltype = 0;
+	exp = 0;
+	havestar = 0;
+retry:
+	sp1 = sp = s;
+	spe = sp + FMAX;
+	havenum = 0;
+
+	switch(GETC(ch)) {
+		case '-': *sp++ = ch; sp1++; spe++;
+		case '+':
+			GETC(ch);
+		}
+	while(ch == '0') {
+		++havenum;
+		GETC(ch);
+		}
+	while(isdigit(ch)) {
+		if (sp < spe) *sp++ = ch;
+		else ++exp;
+		GETC(ch);
+		}
+	if (ch == '*' && !poststar) {
+		if (sp == sp1 || exp || *s == '-') {
+			errfl(f__elist->cierr,112,"bad repetition count");
+			}
+		poststar = havestar = 1;
+		*sp = 0;
+		f__lcount = atoi(s);
+		goto retry;
+		}
+	if (ch == '.') {
+#ifndef ALLOW_FLOAT_IN_INTEGER_LIST_INPUT
+		if (reqint)
+			errfl(f__elist->cierr,115,"invalid integer");
+#endif
+		GETC(ch);
+		if (sp == sp1)
+			while(ch == '0') {
+				++havenum;
+				--exp;
+				GETC(ch);
+				}
+		while(isdigit(ch)) {
+			if (sp < spe)
+				{ *sp++ = ch; --exp; }
+			GETC(ch);
+			}
+		}
+	havenum += sp - sp1;
+	se = 0;
+	if (issign(ch))
+		goto signonly;
+	if (havenum && isexp(ch)) {
+#ifndef ALLOW_FLOAT_IN_INTEGER_LIST_INPUT
+		if (reqint)
+			errfl(f__elist->cierr,115,"invalid integer");
+#endif
+		GETC(ch);
+		if (issign(ch)) {
+signonly:
+			if (ch == '-') se = 1;
+			GETC(ch);
+			}
+		if (!isdigit(ch)) {
+bad:
+			errfl(f__elist->cierr,112,"exponent field");
+			}
+
+		e = ch - '0';
+		while(isdigit(GETC(ch))) {
+			e = 10*e + ch - '0';
+			if (e > EXPMAX)
+				goto bad;
+			}
+		if (se)
+			exp -= e;
+		else
+			exp += e;
+		}
+	(void) Ungetc(ch, f__cf);
+	if (sp > sp1) {
+		++havenum;
+		while(*--sp == '0')
+			++exp;
+		if (exp)
+			sprintf(sp+1, "e%ld", exp);
+		else
+			sp[1] = 0;
+		f__lx = atof(s);
+#ifdef Allow_TYQUAD
+		if (reqint&2 && (se = sp - sp1 + exp) > 14 && se < 20) {
+			/* Assuming 64-bit longint and 32-bit long. */
+			if (exp < 0)
+				sp += exp;
+			if (sp1 <= sp) {
+				f__llx = *sp1 - '0';
+				while(++sp1 <= sp)
+					f__llx = 10*f__llx + (*sp1 - '0');
+				}
+			while(--exp >= 0)
+				f__llx *= 10;
+			if (*s == '-')
+				f__llx = -f__llx;
+			}
+#endif
+		}
+	else
+		f__lx = 0.;
+	if (havenum)
+		f__ltype = TYLONG;
+	else
+		switch(ch) {
+			case ',':
+			case '/':
+				break;
+			default:
+				if (havestar && ( ch == ' '
+						||ch == '\t'
+						||ch == '\n'))
+					break;
+				if (nml_read > 1) {
+					f__lquit = 2;
+					return 0;
+					}
+				errfl(f__elist->cierr,112,"invalid number");
+			}
+	return 0;
+	}
+
+ static int
+#ifdef KR_headers
+rd_count(ch) register int ch;
+#else
+rd_count(register int ch)
+#endif
+{
+	if (ch < '0' || ch > '9')
+		return 1;
+	f__lcount = ch - '0';
+	while(GETC(ch) >= '0' && ch <= '9')
+		f__lcount = 10*f__lcount + ch - '0';
+	Ungetc(ch,f__cf);
+	return f__lcount <= 0;
+	}
+
+ static int
+l_C(Void)
+{	int ch, nml_save;
+	double lz;
+	if(f__lcount>0) return(0);
+	f__ltype=0;
+	GETC(ch);
+	if(ch!='(')
+	{
+		if (nml_read > 1 && (ch < '0' || ch > '9')) {
+			Ungetc(ch,f__cf);
+			f__lquit = 2;
+			return 0;
+			}
+		if (rd_count(ch))
+			if(!f__cf || !feof(f__cf))
+				errfl(f__elist->cierr,112,"complex format");
+			else
+				err(f__elist->cierr,(EOF),"lread");
+		if(GETC(ch)!='*')
+		{
+			if(!f__cf || !feof(f__cf))
+				errfl(f__elist->cierr,112,"no star");
+			else
+				err(f__elist->cierr,(EOF),"lread");
+		}
+		if(GETC(ch)!='(')
+		{	Ungetc(ch,f__cf);
+			return(0);
+		}
+	}
+	else
+		f__lcount = 1;
+	while(iswhit(GETC(ch)));
+	Ungetc(ch,f__cf);
+	nml_save = nml_read;
+	nml_read = 0;
+	if (ch = l_R(1,0))
+		return ch;
+	if (!f__ltype)
+		errfl(f__elist->cierr,112,"no real part");
+	lz = f__lx;
+	while(iswhit(GETC(ch)));
+	if(ch!=',')
+	{	(void) Ungetc(ch,f__cf);
+		errfl(f__elist->cierr,112,"no comma");
+	}
+	while(iswhit(GETC(ch)));
+	(void) Ungetc(ch,f__cf);
+	if (ch = l_R(1,0))
+		return ch;
+	if (!f__ltype)
+		errfl(f__elist->cierr,112,"no imaginary part");
+	while(iswhit(GETC(ch)));
+	if(ch!=')') errfl(f__elist->cierr,112,"no )");
+	f__ly = f__lx;
+	f__lx = lz;
+#ifdef Allow_TYQUAD
+	f__llx = 0;
+#endif
+	nml_read = nml_save;
+	return(0);
+}
+
+ static char nmLbuf[256], *nmL_next;
+ static int (*nmL_getc_save)(Void);
+#ifdef KR_headers
+ static int (*nmL_ungetc_save)(/* int, FILE* */);
+#else
+ static int (*nmL_ungetc_save)(int, FILE*);
+#endif
+
+ static int
+nmL_getc(Void)
+{
+	int rv;
+	if (rv = *nmL_next++)
+		return rv;
+	l_getc = nmL_getc_save;
+	l_ungetc = nmL_ungetc_save;
+	return (*l_getc)();
+	}
+
+ static int
+#ifdef KR_headers
+nmL_ungetc(x, f) int x; FILE *f;
+#else
+nmL_ungetc(int x, FILE *f)
+#endif
+{
+	f = f;	/* banish non-use warning */
+	return *--nmL_next = x;
+	}
+
+ static int
+#ifdef KR_headers
+Lfinish(ch, dot, rvp) int ch, dot, *rvp;
+#else
+Lfinish(int ch, int dot, int *rvp)
+#endif
+{
+	char *s, *se;
+	static char what[] = "namelist input";
+
+	s = nmLbuf + 2;
+	se = nmLbuf + sizeof(nmLbuf) - 1;
+	*s++ = ch;
+	while(!issep(GETC(ch)) && ch!=EOF) {
+		if (s >= se) {
+ nmLbuf_ovfl:
+			return *rvp = err__fl(f__elist->cierr,131,what);
+			}
+		*s++ = ch;
+		if (ch != '=')
+			continue;
+		if (dot)
+			return *rvp = err__fl(f__elist->cierr,112,what);
+ got_eq:
+		*s = 0;
+		nmL_getc_save = l_getc;
+		l_getc = nmL_getc;
+		nmL_ungetc_save = l_ungetc;
+		l_ungetc = nmL_ungetc;
+		nmLbuf[1] = *(nmL_next = nmLbuf) = ',';
+		*rvp = f__lcount = 0;
+		return 1;
+		}
+	if (dot)
+		goto done;
+	for(;;) {
+		if (s >= se)
+			goto nmLbuf_ovfl;
+		*s++ = ch;
+		if (!isblnk(ch))
+			break;
+		if (GETC(ch) == EOF)
+			goto done;
+		}
+	if (ch == '=')
+		goto got_eq;
+ done:
+	Ungetc(ch, f__cf);
+	return 0;
+	}
+
+ static int
+l_L(Void)
+{
+	int ch, rv, sawdot;
+
+	if(f__lcount>0)
+		return(0);
+	f__lcount = 1;
+	f__ltype=0;
+	GETC(ch);
+	if(isdigit(ch))
+	{
+		rd_count(ch);
+		if(GETC(ch)!='*')
+			if(!f__cf || !feof(f__cf))
+				errfl(f__elist->cierr,112,"no star");
+			else
+				err(f__elist->cierr,(EOF),"lread");
+		GETC(ch);
+	}
+	sawdot = 0;
+	if(ch == '.') {
+		sawdot = 1;
+		GETC(ch);
+		}
+	switch(ch)
+	{
+	case 't':
+	case 'T':
+		if (nml_read && Lfinish(ch, sawdot, &rv))
+			return rv;
+		f__lx=1;
+		break;
+	case 'f':
+	case 'F':
+		if (nml_read && Lfinish(ch, sawdot, &rv))
+			return rv;
+		f__lx=0;
+		break;
+	default:
+		if(isblnk(ch) || issep(ch) || ch==EOF)
+		{	(void) Ungetc(ch,f__cf);
+			return(0);
+		}
+		if (nml_read > 1) {
+			Ungetc(ch,f__cf);
+			f__lquit = 2;
+			return 0;
+			}
+		errfl(f__elist->cierr,112,"logical");
+	}
+	f__ltype=TYLONG;
+	while(!issep(GETC(ch)) && ch!=EOF);
+	Ungetc(ch, f__cf);
+	return(0);
+}
+
+#define BUFSIZE	128
+
+ static int
+l_CHAR(Void)
+{	int ch,size,i;
+	static char rafail[] = "realloc failure";
+	char quote,*p;
+	if(f__lcount>0) return(0);
+	f__ltype=0;
+	if(f__lchar!=NULL) free(f__lchar);
+	size=BUFSIZE;
+	p=f__lchar = (char *)malloc((unsigned int)size);
+	if(f__lchar == NULL)
+		errfl(f__elist->cierr,113,"no space");
+
+	GETC(ch);
+	if(isdigit(ch)) {
+		/* allow Fortran 8x-style unquoted string...	*/
+		/* either find a repetition count or the string	*/
+		f__lcount = ch - '0';
+		*p++ = ch;
+		for(i = 1;;) {
+			switch(GETC(ch)) {
+				case '*':
+					if (f__lcount == 0) {
+						f__lcount = 1;
+#ifndef F8X_NML_ELIDE_QUOTES
+						if (nml_read)
+							goto no_quote;
+#endif
+						goto noquote;
+						}
+					p = f__lchar;
+					goto have_lcount;
+				case ',':
+				case ' ':
+				case '\t':
+				case '\n':
+				case '/':
+					Ungetc(ch,f__cf);
+					/* no break */
+				case EOF:
+					f__lcount = 1;
+					f__ltype = TYCHAR;
+					return *p = 0;
+				}
+			if (!isdigit(ch)) {
+				f__lcount = 1;
+#ifndef F8X_NML_ELIDE_QUOTES
+				if (nml_read) {
+ no_quote:
+					errfl(f__elist->cierr,112,
+						"undelimited character string");
+					}
+#endif
+				goto noquote;
+				}
+			*p++ = ch;
+			f__lcount = 10*f__lcount + ch - '0';
+			if (++i == size) {
+				f__lchar = (char *)realloc(f__lchar,
+					(unsigned int)(size += BUFSIZE));
+				if(f__lchar == NULL)
+					errfl(f__elist->cierr,113,rafail);
+				p = f__lchar + i;
+				}
+			}
+		}
+	else	(void) Ungetc(ch,f__cf);
+ have_lcount:
+	if(GETC(ch)=='\'' || ch=='"') quote=ch;
+	else if(isblnk(ch) || (issep(ch) && ch != '\n') || ch==EOF) {
+		Ungetc(ch,f__cf);
+		return 0;
+		}
+#ifndef F8X_NML_ELIDE_QUOTES
+	else if (nml_read > 1) {
+		Ungetc(ch,f__cf);
+		f__lquit = 2;
+		return 0;
+		}
+#endif
+	else {
+		/* Fortran 8x-style unquoted string */
+		*p++ = ch;
+		for(i = 1;;) {
+			switch(GETC(ch)) {
+				case ',':
+				case ' ':
+				case '\t':
+				case '\n':
+				case '/':
+					Ungetc(ch,f__cf);
+					/* no break */
+				case EOF:
+					f__ltype = TYCHAR;
+					return *p = 0;
+				}
+ noquote:
+			*p++ = ch;
+			if (++i == size) {
+				f__lchar = (char *)realloc(f__lchar,
+					(unsigned int)(size += BUFSIZE));
+				if(f__lchar == NULL)
+					errfl(f__elist->cierr,113,rafail);
+				p = f__lchar + i;
+				}
+			}
+		}
+	f__ltype=TYCHAR;
+	for(i=0;;)
+	{	while(GETC(ch)!=quote && ch!='\n'
+			&& ch!=EOF && ++i<size) *p++ = ch;
+		if(i==size)
+		{
+		newone:
+			f__lchar= (char *)realloc(f__lchar,
+					(unsigned int)(size += BUFSIZE));
+			if(f__lchar == NULL)
+				errfl(f__elist->cierr,113,rafail);
+			p=f__lchar+i-1;
+			*p++ = ch;
+		}
+		else if(ch==EOF) return(EOF);
+		else if(ch=='\n')
+		{	if(*(p-1) != '\\') continue;
+			i--;
+			p--;
+			if(++i<size) *p++ = ch;
+			else goto newone;
+		}
+		else if(GETC(ch)==quote)
+		{	if(++i<size) *p++ = ch;
+			else goto newone;
+		}
+		else
+		{	(void) Ungetc(ch,f__cf);
+			*p = 0;
+			return(0);
+		}
+	}
+}
+
+ int
+#ifdef KR_headers
+c_le(a) cilist *a;
+#else
+c_le(cilist *a)
+#endif
+{
+	if(!f__init)
+		f_init();
+	f__fmtbuf="list io";
+	f__curunit = &f__units[a->ciunit];
+	if(a->ciunit>=MXUNIT || a->ciunit<0)
+		err(a->cierr,101,"stler");
+	f__scale=f__recpos=0;
+	f__elist=a;
+	if(f__curunit->ufd==NULL && fk_open(SEQ,FMT,a->ciunit))
+		err(a->cierr,102,"lio");
+	f__cf=f__curunit->ufd;
+	if(!f__curunit->ufmt) err(a->cierr,103,"lio")
+	return(0);
+}
+
+ int
+#ifdef KR_headers
+l_read(number,ptr,len,type) ftnint *number,type; char *ptr; ftnlen len;
+#else
+l_read(ftnint *number, char *ptr, ftnlen len, ftnint type)
+#endif
+{
+#define Ptr ((flex *)ptr)
+	int i,n,ch;
+	doublereal *yy;
+	real *xx;
+	for(i=0;i<*number;i++)
+	{
+		if(f__lquit) return(0);
+		if(l_eof)
+			err(f__elist->ciend, EOF, "list in")
+		if(f__lcount == 0) {
+			f__ltype = 0;
+			for(;;)  {
+				GETC(ch);
+				switch(ch) {
+				case EOF:
+					err(f__elist->ciend,(EOF),"list in")
+				case ' ':
+				case '\t':
+				case '\n':
+					continue;
+				case '/':
+					f__lquit = 1;
+					goto loopend;
+				case ',':
+					f__lcount = 1;
+					goto loopend;
+				default:
+					(void) Ungetc(ch, f__cf);
+					goto rddata;
+				}
+			}
+		}
+	rddata:
+		switch((int)type)
+		{
+		case TYINT1:
+		case TYSHORT:
+		case TYLONG:
+#ifndef ALLOW_FLOAT_IN_INTEGER_LIST_INPUT
+			ERR(l_R(0,1));
+			break;
+#endif
+		case TYREAL:
+		case TYDREAL:
+			ERR(l_R(0,0));
+			break;
+#ifdef TYQUAD
+		case TYQUAD:
+			n = l_R(0,2);
+			if (n)
+				return n;
+			break;
+#endif
+		case TYCOMPLEX:
+		case TYDCOMPLEX:
+			ERR(l_C());
+			break;
+		case TYLOGICAL1:
+		case TYLOGICAL2:
+		case TYLOGICAL:
+			ERR(l_L());
+			break;
+		case TYCHAR:
+			ERR(l_CHAR());
+			break;
+		}
+	while (GETC(ch) == ' ' || ch == '\t');
+	if (ch != ',' || f__lcount > 1)
+		Ungetc(ch,f__cf);
+	loopend:
+		if(f__lquit) return(0);
+		if(f__cf && ferror(f__cf)) {
+			clearerr(f__cf);
+			errfl(f__elist->cierr,errno,"list in");
+			}
+		if(f__ltype==0) goto bump;
+		switch((int)type)
+		{
+		case TYINT1:
+		case TYLOGICAL1:
+			Ptr->flchar = (char)f__lx;
+			break;
+		case TYLOGICAL2:
+		case TYSHORT:
+			Ptr->flshort = (short)f__lx;
+			break;
+		case TYLOGICAL:
+		case TYLONG:
+			Ptr->flint = (ftnint)f__lx;
+			break;
+#ifdef Allow_TYQUAD
+		case TYQUAD:
+			if (!(Ptr->fllongint = f__llx))
+				Ptr->fllongint = f__lx;
+			break;
+#endif
+		case TYREAL:
+			Ptr->flreal=f__lx;
+			break;
+		case TYDREAL:
+			Ptr->fldouble=f__lx;
+			break;
+		case TYCOMPLEX:
+			xx=(real *)ptr;
+			*xx++ = f__lx;
+			*xx = f__ly;
+			break;
+		case TYDCOMPLEX:
+			yy=(doublereal *)ptr;
+			*yy++ = f__lx;
+			*yy = f__ly;
+			break;
+		case TYCHAR:
+			b_char(f__lchar,ptr,len);
+			break;
+		}
+	bump:
+		if(f__lcount>0) f__lcount--;
+		ptr += len;
+		if (nml_read)
+			nml_read++;
+	}
+	return(0);
+#undef Ptr
+}
+#ifdef KR_headers
+integer s_rsle(a) cilist *a;
+#else
+integer s_rsle(cilist *a)
+#endif
+{
+	int n;
+
+	f__reading=1;
+	f__external=1;
+	f__formatted=1;
+	if(n=c_le(a)) return(n);
+	f__lioproc = l_read;
+	f__lquit = 0;
+	f__lcount = 0;
+	l_eof = 0;
+	if(f__curunit->uwrt && f__nowreading(f__curunit))
+		err(a->cierr,errno,"read start");
+	if(f__curunit->uend)
+		err(f__elist->ciend,(EOF),"read start");
+	l_getc = t_getc;
+	l_ungetc = un_getc;
+	f__doend = xrd_SL;
+	return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/lsame.c b/igraph/src/lsame.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lsame.c
@@ -0,0 +1,111 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+logical igraphlsame_(char *ca, char *cb)
+{
+    /* System generated locals */
+    logical ret_val;
+
+    /* Local variables */
+    integer inta, intb, zcode;
+
+
+/*  -- LAPACK auxiliary routine (version 3.1) --   
+       Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..   
+       November 2006   
+
+
+    Purpose   
+    =======   
+
+    LSAME returns .TRUE. if CA is the same letter as CB regardless of   
+    case.   
+
+    Arguments   
+    =========   
+
+    CA      (input) CHARACTER*1   
+
+    CB      (input) CHARACTER*1   
+            CA and CB specify the single characters to be compared.   
+
+   =====================================================================   
+
+
+       Test if the characters are equal */
+
+    ret_val = *(unsigned char *)ca == *(unsigned char *)cb;
+    if (ret_val) {
+	return ret_val;
+    }
+
+/*     Now test for equivalence if both characters are alphabetic. */
+
+    zcode = 'Z';
+
+/*     Use 'Z' rather than 'A' so that ASCII can be detected on Prime   
+       machines, on which ICHAR returns a value with bit 8 set.   
+       ICHAR('A') on Prime machines returns 193 which is the same as   
+       ICHAR('A') on an EBCDIC machine. */
+
+    inta = *(unsigned char *)ca;
+    intb = *(unsigned char *)cb;
+
+    if (zcode == 90 || zcode == 122) {
+
+/*        ASCII is assumed - ZCODE is the ASCII code of either lower or   
+          upper case 'Z'. */
+
+	if (inta >= 97 && inta <= 122) {
+	    inta += -32;
+	}
+	if (intb >= 97 && intb <= 122) {
+	    intb += -32;
+	}
+
+    } else if (zcode == 233 || zcode == 169) {
+
+/*        EBCDIC is assumed - ZCODE is the EBCDIC code of either lower or   
+          upper case 'Z'. */
+
+	if (inta >= 129 && inta <= 137 || inta >= 145 && inta <= 153 || inta 
+		>= 162 && inta <= 169) {
+	    inta += 64;
+	}
+	if (intb >= 129 && intb <= 137 || intb >= 145 && intb <= 153 || intb 
+		>= 162 && intb <= 169) {
+	    intb += 64;
+	}
+
+    } else if (zcode == 218 || zcode == 250) {
+
+/*        ASCII is assumed, on Prime machines - ZCODE is the ASCII code   
+          plus 128 of either lower or upper case 'Z'. */
+
+	if (inta >= 225 && inta <= 250) {
+	    inta += -32;
+	}
+	if (intb >= 225 && intb <= 250) {
+	    intb += -32;
+	}
+    }
+    ret_val = inta == intb;
+
+/*     RETURN   
+
+       End of LSAME */
+
+    return ret_val;
+} /* igraphlsame_ */
+
diff --git a/igraph/src/lsap.c b/igraph/src/lsap.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lsap.c
@@ -0,0 +1,632 @@
+
+#include "igraph_lsap.h"
+#include "igraph_error.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <limits.h>     /* INT_MAX */
+#include <float.h>      /* DBL_MAX */
+#include <assert.h>
+#include <time.h>
+
+/* constants used for improving readability of code */
+
+#define COVERED       1
+#define UNCOVERED     0
+#define ASSIGNED      1
+#define UNASSIGNED    0
+#define TRUE          1
+#define FALSE         0
+
+#define MARKED        1
+#define UNMARKED      0
+
+#define REDUCE        1
+#define NOREDUCE      0
+
+typedef struct {
+    int        n;            /* order of problem             */
+    double   **C;            /* cost matrix          */
+    double   **c;            /* reduced cost matrix      */
+    int       *s;            /* assignment                   */
+    int       *f;            /* column i is assigned to f[i] */
+    int       na;            /* number of assigned items;    */
+    int     runs;            /* number of iterations     */
+    double  cost;            /* minimum cost         */
+    time_t rtime;            /* time                         */
+} AP;
+
+/* public interface */
+
+/* constructors and destructor */
+AP     *ap_create_problem(double *t, int n);
+AP     *ap_create_problem_from_matrix(double **t, int n);
+AP     *ap_read_problem(char *file);
+void    ap_free(AP *p);
+
+int     ap_assignment(AP *p, int *res);
+int     ap_costmatrix(AP *p, double **m);
+int     ap_datamatrix(AP *p, double **m);
+int     ap_iterations(AP *p);
+int     ap_hungarian(AP *p);
+double  ap_mincost(AP *p);
+void    ap_print_solution(AP *p);
+void    ap_show_data(AP *p);
+int     ap_size(AP *p);
+int     ap_time(AP *p);
+
+/* error reporting */
+void ap_error(char *message);
+
+/* private functions */
+void    preprocess(AP *p);
+void    preassign(AP *p);
+int     cover(AP *p, int *ri, int *ci);
+void    reduce(AP *p, int *ri, int *ci);
+
+int ap_hungarian(AP *p) {
+    int      n;            /* size of problem */
+    int    *ri;            /* covered rows    */
+    int    *ci;            /* covered columns */
+    time_t start, end;     /* timer           */
+    int i, j, ok;
+
+    start = time(0);
+
+    n = p->n;
+    p->runs = 0;
+
+    /* allocate memory */
+    p->s = calloc(1 + n, sizeof(int));
+    p->f = calloc(1 + n, sizeof(int));
+
+    ri = calloc(1 + n, sizeof(int));
+    ci = calloc(1 + n, sizeof(int));
+
+    if (ri == NULL || ci == NULL || p->s == NULL || p->f == NULL) {
+        IGRAPH_ERROR("ap_hungarian: could not allocate memory", IGRAPH_ENOMEM);
+    }
+
+    preprocess(p);
+    preassign(p);
+
+    while (p->na < n) {
+        if (REDUCE == cover(p, ri, ci)) {
+            reduce(p, ri, ci);
+        }
+        ++p->runs;
+    }
+
+    end = time(0);
+
+    p->rtime = end - start;
+
+    /* check if assignment is a permutation of (1..n) */
+    for (i = 1; i <= n; i++) {
+        ok = 0;
+        for (j = 1; j <= n; j++)
+            if (p->s[j] == i) {
+                ++ok;
+            }
+        if (ok != 1)
+            IGRAPH_ERROR("ap_hungarian: error in assigment, is not a permutation",
+                         IGRAPH_EINVAL);
+    }
+
+    /* calculate cost of assignment */
+    p->cost = 0;
+    for (i = 1; i <= n; i++) {
+        p->cost += p->C[i][p->s[i]];
+    }
+
+    /* reset result back to base-0 indexing */
+    for (i = 1; i <= n; i++) {
+        p->s[i - 1] = p->s[i] - 1;
+    }
+
+    /* free memory */
+
+    free(ri);
+    free(ci);
+
+    return 0;
+}
+
+/* abbreviated interface */
+int ap_assignment(AP *p, int *res) {
+    int i;
+
+    if (p->s == NULL) {
+        ap_hungarian(p);
+    }
+
+    for (i = 0; i < p->n; i++) {
+        res[i] = p->s[i];
+    }
+
+    return p->n;
+}
+
+
+/*******************************************************************/
+/* constructors                                                    */
+/* read data from file                                             */
+/*******************************************************************/
+
+AP *ap_read_problem(char *file) {
+    FILE *f;
+    int i, j, c;
+    int m, n;
+    double x;
+    double **t;
+    int nrow, ncol;
+    AP *p;
+
+    f = fopen(file, "r");
+    if (f == NULL) {
+        return NULL;
+    }
+
+    t = (double **)malloc(sizeof(double*));
+
+    m = 0;
+    n = 0;
+
+    nrow = 0;
+    ncol = 0;
+
+    while (EOF != (i = fscanf(f, "%lf", &x))) {
+        if (i == 1) {
+            if (n == 0) {
+                t = (double **) realloc(t, (m + 1) * sizeof(double *));
+                t[m] = (double *) malloc(sizeof(double));
+            } else {
+                t[m] = (double *) realloc(t[m], (n + 1) * sizeof(double));
+            }
+
+            t[m][n++] = x;
+
+            ncol = (ncol < n) ? n : ncol;
+            c = fgetc(f);
+            if (c == '\n') {
+                n = 0;
+                ++m;
+                nrow = (nrow < m) ? m : nrow;
+            }
+        }
+    }
+    fclose(f);
+
+    /* prepare data */
+
+    if (nrow != ncol) {
+        /*
+          fprintf(stderr,"ap_read_problem: problem not quadratic\nrows =%d, cols = %d\n",nrow,ncol);
+        */
+        igraph_warningf("ap_read_problem: problem not quadratic\nrows = %d, cols = %d\n",
+                        __FILE__, __LINE__, -1, nrow, ncol);
+        return NULL;
+    }
+
+    p = (AP*) malloc(sizeof(AP));
+    p->n = ncol;
+
+    p->C  = (double **) malloc((1 + nrow) * sizeof(double *));
+    p->c  = (double **) malloc((1 + nrow) * sizeof(double *));
+    if (p->C == NULL || p->c == NULL) {
+        return NULL;
+    }
+
+    for (i = 1; i <= nrow; i++) {
+        p->C[i] = (double *) calloc(ncol + 1, sizeof(double));
+        p->c[i] = (double *) calloc(ncol + 1, sizeof(double));
+        if (p->C[i] == NULL || p->c[i] == NULL) {
+            return NULL;
+        }
+    }
+
+    for (i = 1; i <= nrow; i++)
+        for ( j = 1; j <= ncol; j++) {
+            p->C[i][j] = t[i - 1][j - 1];
+            p->c[i][j] = t[i - 1][j - 1];
+        }
+
+    for (i = 0; i < nrow; i++) {
+        free(t[i]);
+    }
+    free(t);
+
+    p->cost = 0;
+    p->s = NULL;
+    p->f = NULL;
+    return p;
+}
+
+AP     *ap_create_problem_from_matrix(double **t, int n) {
+    int i, j;
+    AP *p;
+
+    p = (AP*) malloc(sizeof(AP));
+    if (p == NULL) {
+        return NULL;
+    }
+
+    p->n = n;
+
+    p->C  = (double **) malloc((n + 1) * sizeof(double *));
+    p->c  = (double **) malloc((n + 1) * sizeof(double *));
+    if (p->C == NULL || p->c == NULL) {
+        return NULL;
+    }
+
+    for (i = 1; i <= n; i++) {
+        p->C[i] = (double *) calloc(n + 1, sizeof(double));
+        p->c[i] = (double *) calloc(n + 1, sizeof(double));
+        if (p->C[i] == NULL || p->c[i] == NULL) {
+            return NULL;
+        }
+    }
+
+
+    for (i = 1; i <= n; i++)
+        for ( j = 1; j <= n; j++) {
+            p->C[i][j] = t[i - 1][j - 1];
+            p->c[i][j] = t[i - 1][j - 1];
+        }
+    p->cost = 0;
+    p->s = NULL;
+    p->f = NULL;
+    return p;
+}
+
+/* read data from vector */
+AP *ap_create_problem(double *t, int n) {
+    int i, j;
+    AP *p;
+
+    p = (AP*) malloc(sizeof(AP));
+    if (p == NULL) {
+        return NULL;
+    }
+
+    p->n = n;
+
+    p->C  = (double **) malloc((n + 1) * sizeof(double *));
+    p->c  = (double **) malloc((n + 1) * sizeof(double *));
+    if (p->C == NULL || p->c == NULL) {
+        return NULL;
+    }
+
+    for (i = 1; i <= n; i++) {
+        p->C[i] = (double *) calloc(n + 1, sizeof(double));
+        p->c[i] = (double *) calloc(n + 1, sizeof(double));
+        if (p->C[i] == NULL || p->c[i] == NULL) {
+            return NULL;
+        }
+    }
+
+
+    for (i = 1; i <= n; i++)
+        for ( j = 1; j <= n; j++) {
+            p->C[i][j] = t[n * (j - 1) + i - 1];
+            p->c[i][j] = t[n * (j - 1) + i - 1];
+        }
+    p->cost = 0;
+    p->s = NULL;
+    p->f = NULL;
+    return p;
+}
+
+/* destructor */
+void ap_free(AP *p) {
+    int i;
+
+    free(p->s);
+    free(p->f);
+
+    for (i = 1; i <= p->n; i++) {
+        free(p->C[i]);
+        free(p->c[i]);
+    }
+
+    free(p->C);
+    free(p->c);
+    free(p);
+}
+
+/* set + get functions */
+
+/*
+void ap_show_data(AP *p)
+{
+    int i, j;
+
+    for(i = 1; i <= p->n; i++){
+    for(j = 1; j <= p->n; j++)
+        printf("%6.2f ", p->c[i][j]);
+    printf("\n");
+    }
+}
+*/
+
+double ap_mincost(AP *p) {
+    if (p->s == NULL) {
+        ap_hungarian(p);
+    }
+
+    return p->cost;
+}
+
+int ap_size(AP *p) {
+    return p->n;
+}
+
+int ap_time(AP *p) {
+    return (int) p->rtime;
+}
+
+int ap_iterations(AP *p) {
+    return p->runs;
+}
+
+/*
+void ap_print_solution(AP *p)
+{
+    int i;
+
+    printf("%d itertations, %d secs.\n",p->runs, (int)p->rtime);
+    printf("Min Cost: %10.4f\n",p->cost);
+
+    for(i = 0; i < p->n; i++)
+    printf("%4d",p->s[i]);
+    printf("\n");
+}
+*/
+
+int ap_costmatrix(AP *p, double **m) {
+    int i, j;
+
+    for (i = 0; i < p->n; i++)
+        for (j = 0; j < p->n; j++) {
+            m[i][j] = p->C[i + 1][j + 1];
+        }
+
+    return p->n;
+}
+
+int ap_datamatrix(AP *p, double **m) {
+    int i, j;
+
+    for (i = 0; i < p->n; i++)
+        for (j = 0; j < p->n; j++) {
+            m[i][j] = p->c[i + 1][j + 1];
+        }
+
+    return p->n;
+}
+
+/* error reporting */
+
+/*
+void ap_error(char *message)
+{
+    fprintf(stderr,"%s\n",message);
+    exit(1);
+}
+*/
+
+/*************************************************************/
+/* these functions are used internally                       */
+/* by ap_hungarian                                           */
+/*************************************************************/
+
+int cover(AP *p, int *ri, int *ci) {
+    int *mr, i, r;
+    int n;
+
+    n = p->n;
+    mr = calloc(1 + p->n, sizeof(int));
+
+    /* reset cover indices */
+    for (i = 1; i <= n; i++) {
+        if (p->s[i] == UNASSIGNED) {
+            ri[i] = UNCOVERED;
+            mr[i] = MARKED;
+        } else {
+            ri[i] = COVERED;
+        }
+        ci[i] = UNCOVERED;
+    }
+
+    while (TRUE) {
+        /* find marked row */
+        r = 0;
+        for (i = 1; i <= n; i++)
+            if (mr[i] == MARKED) {
+                r = i;
+                break;
+            }
+
+        if (r == 0) {
+            break;
+        }
+        for (i = 1; i <= n; i++)
+            if (p->c[r][i] == 0 && ci[i] == UNCOVERED) {
+                if (p->f[i]) {
+                    ri[p->f[i]] = UNCOVERED;
+                    mr[p->f[i]] = MARKED;
+                    ci[i] = COVERED;
+                } else {
+                    if (p->s[r] == UNASSIGNED) {
+                        ++p->na;
+                    }
+
+                    p->f[p->s[r]] = 0;
+                    p->f[i] = r;
+                    p->s[r] = i;
+
+                    free(mr);
+                    return NOREDUCE;
+                }
+            }
+        mr[r] = UNMARKED;
+    }
+    free(mr);
+    return REDUCE;
+}
+
+void reduce(AP *p, int *ri, int *ci) {
+    int i, j, n;
+    double min;
+
+    n = p->n;
+
+    /* find minimum in uncovered c-matrix */
+    min = DBL_MAX;
+    for (i = 1; i <= n; i++)
+        for (j = 1; j <= n; j++)
+            if (ri[i] == UNCOVERED && ci[j] == UNCOVERED) {
+                if (p->c[i][j] < min) {
+                    min = p->c[i][j];
+                }
+            }
+
+    /* subtract min from each uncovered element and add it to each element */
+    /* which is covered twice                                              */
+    for (i = 1; i <= n; i++)
+        for (j = 1; j <= n; j++) {
+            if (ri[i] == UNCOVERED && ci[j] == UNCOVERED) {
+                p->c[i][j] -= min;
+            }
+            if (ri[i] == COVERED && ci[j] == COVERED) {
+                p->c[i][j] += min;
+            }
+        }
+}
+
+void preassign(AP *p) {
+    int i, j, min, r, c, n, count;
+    int *ri, *ci, *rz, *cz;
+
+    n = p->n;
+    p->na = 0;
+
+    /* row and column markers */
+    ri = calloc(1 + n, sizeof(int));
+    ci = calloc(1 + n, sizeof(int));
+
+    /* row and column counts of zeroes */
+    rz = calloc(1 + n, sizeof(int));
+    cz = calloc(1 + n, sizeof(int));
+
+    for (i = 1; i <= n; i++) {
+        count = 0;
+        for (j = 1; j <= n; j++)
+            if (p->c[i][j] == 0) {
+                ++count;
+            }
+        rz[i] = count;
+    }
+
+    for (i = 1; i <= n; i++) {
+        count = 0;
+        for (j = 1; j <= n; j++)
+            if (p->c[j][i] == 0) {
+                ++count;
+            }
+        cz[i] = count;
+    }
+
+    while (TRUE) {
+        /* find unassigned row with least number of zeroes > 0 */
+        min = INT_MAX;
+        r = 0;
+        for (i = 1; i <= n; i++)
+            if (rz[i] > 0 && rz[i] < min && ri[i] == UNASSIGNED) {
+                min = rz[i];
+                r = i;
+            }
+        /* check if we are done */
+        if (r == 0) {
+            break;
+        }
+
+        /* find unassigned column in row r with least number of zeroes */
+        c = 0;
+        min = INT_MAX;
+        for (i = 1; i <= n; i++)
+            if (p->c[r][i] == 0 && cz[i] < min && ci[i] == UNASSIGNED) {
+                min = cz[i];
+                c = i;
+            }
+
+        if (c) {
+            ++p->na;
+            p->s[r] = c;
+            p->f[c] = r;
+
+            ri[r] = ASSIGNED;
+            ci[c] = ASSIGNED;
+
+            /* adjust zero counts */
+            cz[c] = 0;
+            for (i = 1; i <= n; i++)
+                if (p->c[i][c] == 0) {
+                    --rz[i];
+                }
+        }
+    }
+
+    /* free memory */
+    free(ri);
+    free(ci);
+    free(rz);
+    free(cz);
+}
+
+void preprocess(AP *p) {
+    int i, j, n;
+    double min;
+
+    n = p->n;
+
+    /* subtract column minima in each row */
+    for (i = 1; i <= n; i++) {
+        min = p->c[i][1];
+        for (j = 2; j <= n; j++)
+            if (p->c[i][j] < min) {
+                min = p->c[i][j];
+            }
+        for (j = 1; j <= n; j++) {
+            p->c[i][j] -= min;
+        }
+    }
+
+    /* subtract row minima in each column */
+    for (i = 1; i <= n; i++) {
+        min = p->c[1][i];
+        for (j = 2; j <= n; j++)
+            if (p->c[j][i] < min) {
+                min = p->c[j][i];
+            }
+        for (j = 1; j <= n; j++) {
+            p->c[j][i] -= min;
+        }
+    }
+}
+
+int igraph_solve_lsap(igraph_matrix_t *c, igraph_integer_t n,
+                      igraph_vector_int_t *p) {
+    AP *ap;
+
+    IGRAPH_CHECK(igraph_vector_int_resize(p, n));
+    igraph_vector_int_null(p);
+
+    ap = ap_create_problem(&MATRIX(*c, 0, 0), n);
+    ap_hungarian(ap);
+    ap_assignment(ap, VECTOR(*p));
+    ap_free(ap);
+
+    return 0;
+}
diff --git a/igraph/src/lwrite.c b/igraph/src/lwrite.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/lwrite.c
@@ -0,0 +1,314 @@
+#include "f2c.h"
+#include "fio.h"
+#include "fmt.h"
+#include "lio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ftnint L_len;
+int f__Aquote;
+
+ static VOID
+donewrec(Void)
+{
+	if (f__recpos)
+		(*f__donewrec)();
+	}
+
+ static VOID
+#ifdef KR_headers
+lwrt_I(n) longint n;
+#else
+lwrt_I(longint n)
+#endif
+{
+	char *p;
+	int ndigit, sign;
+
+	p = f__icvt(n, &ndigit, &sign, 10);
+	if(f__recpos + ndigit >= L_len)
+		donewrec();
+	PUT(' ');
+	if (sign)
+		PUT('-');
+	while(*p)
+		PUT(*p++);
+}
+ static VOID
+#ifdef KR_headers
+lwrt_L(n, len) ftnint n; ftnlen len;
+#else
+lwrt_L(ftnint n, ftnlen len)
+#endif
+{
+	if(f__recpos+LLOGW>=L_len)
+		donewrec();
+	wrt_L((Uint *)&n,LLOGW, len);
+}
+ static VOID
+#ifdef KR_headers
+lwrt_A(p,len) char *p; ftnlen len;
+#else
+lwrt_A(char *p, ftnlen len)
+#endif
+{
+	int a;
+	char *p1, *pe;
+
+	a = 0;
+	pe = p + len;
+	if (f__Aquote) {
+		a = 3;
+		if (len > 1 && p[len-1] == ' ') {
+			while(--len > 1 && p[len-1] == ' ');
+			pe = p + len;
+			}
+		p1 = p;
+		while(p1 < pe)
+			if (*p1++ == '\'')
+				a++;
+		}
+	if(f__recpos+len+a >= L_len)
+		donewrec();
+	if (a
+#ifndef OMIT_BLANK_CC
+		|| !f__recpos
+#endif
+		)
+		PUT(' ');
+	if (a) {
+		PUT('\'');
+		while(p < pe) {
+			if (*p == '\'')
+				PUT('\'');
+			PUT(*p++);
+			}
+		PUT('\'');
+		}
+	else
+		while(p < pe)
+			PUT(*p++);
+}
+
+ static int
+#ifdef KR_headers
+l_g(buf, n) char *buf; double n;
+#else
+l_g(char *buf, double n)
+#endif
+{
+#ifdef Old_list_output
+	doublereal absn;
+	char *fmt;
+
+	absn = n;
+	if (absn < 0)
+		absn = -absn;
+	fmt = LLOW <= absn && absn < LHIGH ? LFFMT : LEFMT;
+#ifdef USE_STRLEN
+	sprintf(buf, fmt, n);
+	return strlen(buf);
+#else
+	return sprintf(buf, fmt, n);
+#endif
+
+#else
+	register char *b, c, c1;
+
+	b = buf;
+	*b++ = ' ';
+	if (n < 0) {
+		*b++ = '-';
+		n = -n;
+		}
+	else
+		*b++ = ' ';
+	if (n == 0) {
+#ifdef SIGNED_ZEROS
+		if (signbit_f2c(&n))
+			*b++ = '-';
+#endif
+		*b++ = '0';
+		*b++ = '.';
+		*b = 0;
+		goto f__ret;
+		}
+	sprintf(b, LGFMT, n);
+	switch(*b) {
+#ifndef WANT_LEAD_0
+		case '0':
+			while(b[0] = b[1])
+				b++;
+			break;
+#endif
+		case 'i':
+		case 'I':
+			/* Infinity */
+		case 'n':
+		case 'N':
+			/* NaN */
+			while(*++b);
+			break;
+
+		default:
+	/* Fortran 77 insists on having a decimal point... */
+		    for(;; b++)
+			switch(*b) {
+			case 0:
+				*b++ = '.';
+				*b = 0;
+				goto f__ret;
+			case '.':
+				while(*++b);
+				goto f__ret;
+			case 'E':
+				for(c1 = '.', c = 'E';  *b = c1;
+					c1 = c, c = *++b);
+				goto f__ret;
+			}
+		}
+ f__ret:
+	return b - buf;
+#endif
+	}
+
+ static VOID
+#ifdef KR_headers
+l_put(s) register char *s;
+#else
+l_put(register char *s)
+#endif
+{
+#ifdef KR_headers
+	register void (*pn)() = f__putn;
+#else
+	register void (*pn)(int) = f__putn;
+#endif
+	register int c;
+
+	while(c = *s++)
+		(*pn)(c);
+	}
+
+ static VOID
+#ifdef KR_headers
+lwrt_F(n) double n;
+#else
+lwrt_F(double n)
+#endif
+{
+	char buf[LEFBL];
+
+	if(f__recpos + l_g(buf,n) >= L_len)
+		donewrec();
+	l_put(buf);
+}
+ static VOID
+#ifdef KR_headers
+lwrt_C(a,b) double a,b;
+#else
+lwrt_C(double a, double b)
+#endif
+{
+	char *ba, *bb, bufa[LEFBL], bufb[LEFBL];
+	int al, bl;
+
+	al = l_g(bufa, a);
+	for(ba = bufa; *ba == ' '; ba++)
+		--al;
+	bl = l_g(bufb, b) + 1;	/* intentionally high by 1 */
+	for(bb = bufb; *bb == ' '; bb++)
+		--bl;
+	if(f__recpos + al + bl + 3 >= L_len)
+		donewrec();
+#ifdef OMIT_BLANK_CC
+	else
+#endif
+	PUT(' ');
+	PUT('(');
+	l_put(ba);
+	PUT(',');
+	if (f__recpos + bl >= L_len) {
+		(*f__donewrec)();
+#ifndef OMIT_BLANK_CC
+		PUT(' ');
+#endif
+		}
+	l_put(bb);
+	PUT(')');
+}
+
+ int
+#ifdef KR_headers
+l_write(number,ptr,len,type) ftnint *number,type; char *ptr; ftnlen len;
+#else
+l_write(ftnint *number, char *ptr, ftnlen len, ftnint type)
+#endif
+{
+#define Ptr ((flex *)ptr)
+	int i;
+	longint x;
+	double y,z;
+	real *xx;
+	doublereal *yy;
+	for(i=0;i< *number; i++)
+	{
+		switch((int)type)
+		{
+		default: f__fatal(117,"unknown type in lio");
+		case TYINT1:
+			x = Ptr->flchar;
+			goto xint;
+		case TYSHORT:
+			x=Ptr->flshort;
+			goto xint;
+#ifdef Allow_TYQUAD
+		case TYQUAD:
+			x = Ptr->fllongint;
+			goto xint;
+#endif
+		case TYLONG:
+			x=Ptr->flint;
+		xint:	lwrt_I(x);
+			break;
+		case TYREAL:
+			y=Ptr->flreal;
+			goto xfloat;
+		case TYDREAL:
+			y=Ptr->fldouble;
+		xfloat: lwrt_F(y);
+			break;
+		case TYCOMPLEX:
+			xx= &Ptr->flreal;
+			y = *xx++;
+			z = *xx;
+			goto xcomplex;
+		case TYDCOMPLEX:
+			yy = &Ptr->fldouble;
+			y= *yy++;
+			z = *yy;
+		xcomplex:
+			lwrt_C(y,z);
+			break;
+		case TYLOGICAL1:
+			x = Ptr->flchar;
+			goto xlog;
+		case TYLOGICAL2:
+			x = Ptr->flshort;
+			goto xlog;
+		case TYLOGICAL:
+			x = Ptr->flint;
+		xlog:	lwrt_L(Ptr->flint, len);
+			break;
+		case TYCHAR:
+			lwrt_A(ptr,len);
+			break;
+		}
+		ptr += len;
+	}
+	return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/matching.c b/igraph/src/matching.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/matching.c
@@ -0,0 +1,1025 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2012  Tamas Nepusz <ntamas@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include <assert.h>
+#include <math.h>
+#include "config.h"
+#include "igraph_adjlist.h"
+#include "igraph_constructors.h"
+#include "igraph_conversion.h"
+#include "igraph_dqueue.h"
+#include "igraph_flow.h"
+#include "igraph_interface.h"
+#include "igraph_matching.h"
+#include "igraph_structural.h"
+
+/* #define MATCHING_DEBUG */
+
+#ifdef _MSC_VER
+/* MSVC does not support variadic macros */
+#include <stdarg.h>
+static void debug(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+#ifdef MATCHING_DEBUG
+    vfprintf(stderr, fmt, args);
+#endif
+    va_end(args);
+}
+#else
+#ifdef MATCHING_DEBUG
+    #define debug(...) fprintf(stderr, __VA_ARGS__)
+#else
+    #define debug(...)
+#endif
+#endif
+
+/**
+ * \function igraph_is_matching
+ * Checks whether the given matching is valid for the given graph.
+ *
+ * This function checks a matching vector and verifies whether its length
+ * matches the number of vertices in the given graph, its values are between
+ * -1 (inclusive) and the number of vertices (exclusive), and whether there
+ * exists a corresponding edge in the graph for every matched vertex pair.
+ * For bipartite graphs, it also verifies whether the matched vertices are
+ * in different parts of the graph.
+ *
+ * \param graph The input graph. It can be directed but the edge directions
+ *              will be ignored.
+ * \param types If the graph is bipartite and you are interested in bipartite
+ *              matchings only, pass the vertex types here. If the graph is
+ *              non-bipartite, simply pass \c NULL.
+ * \param matching The matching itself. It must be a vector where element i
+ *                 contains the ID of the vertex that vertex i is matched to,
+ *                 or -1 if vertex i is unmatched.
+ * \param result Pointer to a boolean variable, the result will be returned
+ *               here.
+ *
+ * \sa \ref igraph_is_maximal_matching() if you are also interested in whether
+ *     the matching is maximal (i.e. non-extendable).
+ *
+ * Time complexity: O(|V|+|E|) where |V| is the number of vertices and
+ * |E| is the number of edges.
+ *
+ * \example examples/simple/igraph_maximum_bipartite_matching.c
+ */
+int igraph_is_matching(const igraph_t* graph,
+                       const igraph_vector_bool_t* types, const igraph_vector_long_t* matching,
+                       igraph_bool_t* result) {
+    long int i, j, no_of_nodes = igraph_vcount(graph);
+    igraph_bool_t conn;
+
+    /* Checking match vector length */
+    if (igraph_vector_long_size(matching) != no_of_nodes) {
+        *result = 0; return IGRAPH_SUCCESS;
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        j = VECTOR(*matching)[i];
+
+        /* Checking range of each element in the match vector */
+        if (j < -1 || j >= no_of_nodes) {
+            *result = 0; return IGRAPH_SUCCESS;
+        }
+        /* When i is unmatched, we're done */
+        if (j == -1) {
+            continue;
+        }
+        /* Matches must be mutual */
+        if (VECTOR(*matching)[j] != i) {
+            *result = 0; return IGRAPH_SUCCESS;
+        }
+        /* Matched vertices must be connected */
+        IGRAPH_CHECK(igraph_are_connected(graph, (igraph_integer_t) i,
+                                          (igraph_integer_t) j, &conn));
+        if (!conn) {
+            /* Try the other direction -- for directed graphs */
+            IGRAPH_CHECK(igraph_are_connected(graph, (igraph_integer_t) j,
+                                              (igraph_integer_t) i, &conn));
+            if (!conn) {
+                *result = 0; return IGRAPH_SUCCESS;
+            }
+        }
+    }
+
+    if (types != 0) {
+        /* Matched vertices must be of different types */
+        for (i = 0; i < no_of_nodes; i++) {
+            j = VECTOR(*matching)[i];
+            if (j == -1) {
+                continue;
+            }
+            if (VECTOR(*types)[i] == VECTOR(*types)[j]) {
+                *result = 0; return IGRAPH_SUCCESS;
+            }
+        }
+    }
+
+    *result = 1;
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_is_maximal_matching
+ * Checks whether a matching in a graph is maximal.
+ *
+ * A matching is maximal if and only if there exists no unmatched vertex in a
+ * graph such that one of its neighbors is also unmatched.
+ *
+ * \param graph The input graph. It can be directed but the edge directions
+ *              will be ignored.
+ * \param types If the graph is bipartite and you are interested in bipartite
+ *              matchings only, pass the vertex types here. If the graph is
+ *              non-bipartite, simply pass \c NULL.
+ * \param matching The matching itself. It must be a vector where element i
+ *                 contains the ID of the vertex that vertex i is matched to,
+ *                 or -1 if vertex i is unmatched.
+ * \param result Pointer to a boolean variable, the result will be returned
+ *               here.
+ *
+ * \sa \ref igraph_is_matching() if you are only interested in whether a
+ *     matching vector is valid for a given graph.
+ *
+ * Time complexity: O(|V|+|E|) where |V| is the number of vertices and
+ * |E| is the number of edges.
+ *
+ * \example examples/simple/igraph_maximum_bipartite_matching.c
+ */
+int igraph_is_maximal_matching(const igraph_t* graph,
+                               const igraph_vector_bool_t* types, const igraph_vector_long_t* matching,
+                               igraph_bool_t* result) {
+    long int i, j, n, no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t neis;
+    igraph_bool_t valid;
+
+    IGRAPH_CHECK(igraph_is_matching(graph, types, matching, &valid));
+    if (!valid) {
+        *result = 0; return IGRAPH_SUCCESS;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    valid = 1;
+    for (i = 0; i < no_of_nodes; i++) {
+        j = VECTOR(*matching)[i];
+        if (j != -1) {
+            continue;
+        }
+
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i,
+                                      IGRAPH_ALL));
+        n = igraph_vector_size(&neis);
+        for (j = 0; j < n; j++) {
+            if (VECTOR(*matching)[(long int)VECTOR(neis)[j]] == -1) {
+                if (types == 0 ||
+                    VECTOR(*types)[i] != VECTOR(*types)[(long int)VECTOR(neis)[j]]) {
+                    valid = 0; break;
+                }
+            }
+        }
+    }
+
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    *result = valid;
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_i_maximum_bipartite_matching_unweighted(const igraph_t* graph,
+        const igraph_vector_bool_t* types, igraph_integer_t* matching_size,
+        igraph_vector_long_t* matching);
+int igraph_i_maximum_bipartite_matching_weighted(const igraph_t* graph,
+        const igraph_vector_bool_t* types, igraph_integer_t* matching_size,
+        igraph_real_t* matching_weight, igraph_vector_long_t* matching,
+        const igraph_vector_t* weights, igraph_real_t eps);
+
+#define MATCHED(v) (VECTOR(match)[v] != -1)
+#define UNMATCHED(v) (!MATCHED(v))
+
+/**
+ * \function igraph_maximum_bipartite_matching
+ * Calculates a maximum matching in a bipartite graph.
+ *
+ * A matching in a bipartite graph is a partial assignment of vertices
+ * of the first kind to vertices of the second kind such that each vertex of
+ * the first kind is matched to at most one vertex of the second kind and
+ * vice versa, and matched vertices must be connected by an edge in the graph.
+ * The size (or cardinality) of a matching is the number of edges.
+ * A matching is a maximum matching if there exists no other matching with
+ * larger cardinality. For weighted graphs, a maximum matching is a matching
+ * whose edges have the largest possible total weight among all possible
+ * matchings.
+ *
+ * </para><para>
+ * Maximum matchings in bipartite graphs are found by the push-relabel algorithm
+ * with greedy initialization and a global relabeling after every n/2 steps where
+ * n is the number of vertices in the graph.
+ *
+ * </para><para>
+ * References: Cherkassky BV, Goldberg AV, Martin P, Setubal JC and Stolfi J:
+ * Augment or push: A computational study of bipartite matching and
+ * unit-capacity flow algorithms. ACM Journal of Experimental Algorithmics 3,
+ * 1998.
+ *
+ * </para><para>
+ * Kaya K, Langguth J, Manne F and Ucar B: Experiments on push-relabel-based
+ * maximum cardinality matching algorithms for bipartite graphs. Technical
+ * Report TR/PA/11/33 of the Centre Europeen de Recherche et de Formation
+ * Avancee en Calcul Scientifique, 2011.
+ *
+ * \param graph The input graph. It can be directed but the edge directions
+ *              will be ignored.
+ * \param types Boolean vector giving the vertex types of the graph.
+ * \param matching_size The size of the matching (i.e. the number of matched
+ *                      vertex pairs will be returned here). It may be \c NULL
+ *                      if you don't need this.
+ * \param matching_weight The weight of the matching if the edges are weighted,
+ *                        or the size of the matching again if the edges are
+ *                        unweighted. It may be \c NULL if you don't need this.
+ * \param matching The matching itself. It must be a vector where element i
+ *                 contains the ID of the vertex that vertex i is matched to,
+ *                 or -1 if vertex i is unmatched.
+ * \param weights A null pointer (=no edge weights), or a vector giving the
+ *                weights of the edges. Note that the algorithm is stable
+ *                only for integer weights.
+ * \param eps A small real number used in equality tests in the weighted
+ *            bipartite matching algorithm. Two real numbers are considered
+ *            equal in the algorithm if their difference is smaller than
+ *            \c eps. This is required to avoid the accumulation of numerical
+ *            errors. It is advised to pass a value derived from the
+ *            \c DBL_EPSILON constant in \c float.h here. If you are
+ *            running the algorithm with no \c weights vector, this argument
+ *            is ignored.
+ * \return Error code.
+ *
+ * Time complexity: O(sqrt(|V|) |E|) for unweighted graphs (according to the
+ * technical report referenced above), O(|V||E|) for weighted graphs.
+ *
+ * \example examples/simple/igraph_maximum_bipartite_matching.c
+ */
+int igraph_maximum_bipartite_matching(const igraph_t* graph,
+                                      const igraph_vector_bool_t* types, igraph_integer_t* matching_size,
+                                      igraph_real_t* matching_weight, igraph_vector_long_t* matching,
+                                      const igraph_vector_t* weights, igraph_real_t eps) {
+
+    /* Sanity checks */
+    if (igraph_vector_bool_size(types) < igraph_vcount(graph)) {
+        IGRAPH_ERROR("types vector too short", IGRAPH_EINVAL);
+    }
+    if (weights && igraph_vector_size(weights) < igraph_ecount(graph)) {
+        IGRAPH_ERROR("weights vector too short", IGRAPH_EINVAL);
+    }
+
+    if (weights == 0) {
+        IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted(graph, types,
+                     matching_size, matching));
+        if (matching_weight != 0) {
+            *matching_weight = *matching_size;
+        }
+        return IGRAPH_SUCCESS;
+    } else {
+        IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_weighted(graph, types,
+                     matching_size, matching_weight, matching, weights, eps));
+        return IGRAPH_SUCCESS;
+    }
+}
+
+int igraph_i_maximum_bipartite_matching_unweighted_relabel(const igraph_t* graph,
+        const igraph_vector_bool_t* types, igraph_vector_t* labels,
+        igraph_vector_long_t* matching, igraph_bool_t smaller_set);
+
+/**
+ * Finding maximum bipartite matchings on bipartite graphs using the
+ * push-relabel algorithm.
+ *
+ * The implementation follows the pseudocode in Algorithm 1 of the
+ * following paper:
+ *
+ * Kaya K, Langguth J, Manne F and Ucar B: Experiments on push-relabel-based
+ * maximum cardinality matching algorithms for bipartite graphs. Technical
+ * Report TR/PA/11/33 of CERFACS (Centre Européen de Recherche et de Formation
+ * Avancée en Calcul Scientifique).
+ * http://www.cerfacs.fr/algor/reports/2011/TR_PA_11_33.pdf
+ */
+int igraph_i_maximum_bipartite_matching_unweighted(const igraph_t* graph,
+        const igraph_vector_bool_t* types, igraph_integer_t* matching_size,
+        igraph_vector_long_t* matching) {
+    long int i, j, k, n, no_of_nodes = igraph_vcount(graph);
+    long int num_matched;             /* number of matched vertex pairs */
+    igraph_vector_long_t match;       /* will store the matching */
+    igraph_vector_t labels;           /* will store the labels */
+    igraph_vector_t neis;             /* used to retrieve the neighbors of a node */
+    igraph_dqueue_long_t q;           /* a FIFO for push ordering */
+    igraph_bool_t smaller_set;        /* denotes which part of the bipartite graph is smaller */
+    long int label_changed = 0;       /* Counter to decide when to run a global relabeling */
+    long int relabeling_freq = no_of_nodes / 2;
+
+    /* We will use:
+     * - FIFO push ordering
+     * - global relabeling frequency: n/2 steps where n is the number of nodes
+     * - simple greedy matching for initialization
+     */
+
+    /* (1) Initialize data structures */
+    IGRAPH_CHECK(igraph_vector_long_init(&match, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &match);
+    IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0));
+    IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q);
+
+    /* (2) Initially, every node is unmatched */
+    igraph_vector_long_fill(&match, -1);
+
+    /* (3) Find an initial matching in a greedy manner.
+     *     At the same time, find which side of the graph is smaller. */
+    num_matched = 0; j = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*types)[i]) {
+            j++;
+        }
+        if (MATCHED(i)) {
+            continue;
+        }
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i,
+                                      IGRAPH_ALL));
+        n = igraph_vector_size(&neis);
+        for (j = 0; j < n; j++) {
+            k = (long int) VECTOR(neis)[j];
+            if (VECTOR(*types)[k] == VECTOR(*types)[i]) {
+                IGRAPH_ERROR("Graph is not bipartite with supplied types vector", IGRAPH_EINVAL);
+            }
+            if (UNMATCHED(k)) {
+                /* We match vertex i to vertex VECTOR(neis)[j] */
+                VECTOR(match)[k] = i;
+                VECTOR(match)[i] = k;
+                num_matched++;
+                break;
+            }
+        }
+    }
+    smaller_set = (j <= no_of_nodes / 2);
+
+    /* (4) Set the initial labeling -- lines 1 and 2 in the tech report */
+    IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted_relabel(
+                     graph, types, &labels, &match, smaller_set));
+
+    /* (5) Fill the push queue with the unmatched nodes from the smaller set. */
+    for (i = 0; i < no_of_nodes; i++) {
+        if (UNMATCHED(i) && VECTOR(*types)[i] == smaller_set) {
+            IGRAPH_CHECK(igraph_dqueue_long_push(&q, i));
+        }
+    }
+
+    /* (6) Main loop from the referenced tech report -- lines 4--13 */
+    label_changed = 0;
+    while (!igraph_dqueue_long_empty(&q)) {
+        long int v = igraph_dqueue_long_pop(&q);             /* Line 13 */
+        long int u = -1, label_u = 2 * no_of_nodes;
+        long int w;
+
+        if (label_changed >= relabeling_freq) {
+            /* Run global relabeling */
+            IGRAPH_CHECK(igraph_i_maximum_bipartite_matching_unweighted_relabel(
+                             graph, types, &labels, &match, smaller_set));
+            label_changed = 0;
+        }
+
+        debug("Considering vertex %ld\n", v);
+
+        /* Line 5: find row u among the neighbors of v s.t. label(u) is minimal */
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v,
+                                      IGRAPH_ALL));
+        n = igraph_vector_size(&neis);
+        for (i = 0; i < n; i++) {
+            if (VECTOR(labels)[(long int)VECTOR(neis)[i]] < label_u) {
+                u = (long int) VECTOR(neis)[i];
+                label_u = (long int) VECTOR(labels)[u];
+                label_changed++;
+            }
+        }
+
+        debug("  Neighbor with smallest label: %ld (label=%ld)\n", u, label_u);
+
+        if (label_u < no_of_nodes) {                         /* Line 6 */
+            VECTOR(labels)[v] = VECTOR(labels)[u] + 1;         /* Line 7 */
+            if (MATCHED(u)) {                                  /* Line 8 */
+                w = VECTOR(match)[u];
+                debug("  Vertex %ld is matched to %ld, performing a double push\n", u, w);
+                if (w != v) {
+                    VECTOR(match)[u] = -1; VECTOR(match)[w] = -1;  /* Line 9 */
+                    IGRAPH_CHECK(igraph_dqueue_long_push(&q, w));  /* Line 10 */
+                    debug("  Unmatching & activating vertex %ld\n", w);
+                    num_matched--;
+                }
+            }
+            VECTOR(match)[u] = v; VECTOR(match)[v] = u;      /* Line 11 */
+            num_matched++;
+            VECTOR(labels)[u] += 2;                          /* Line 12 */
+            label_changed++;
+        }
+    }
+
+    /* Fill the output parameters */
+    if (matching != 0) {
+        IGRAPH_CHECK(igraph_vector_long_update(matching, &match));
+    }
+    if (matching_size != 0) {
+        *matching_size = (igraph_integer_t) num_matched;
+    }
+
+    /* Release everything */
+    igraph_dqueue_long_destroy(&q);
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&labels);
+    igraph_vector_long_destroy(&match);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_i_maximum_bipartite_matching_unweighted_relabel(const igraph_t* graph,
+        const igraph_vector_bool_t* types, igraph_vector_t* labels,
+        igraph_vector_long_t* match, igraph_bool_t smaller_set) {
+    long int i, j, n, no_of_nodes = igraph_vcount(graph), matched_to;
+    igraph_dqueue_long_t q;
+    igraph_vector_t neis;
+
+    debug("Running global relabeling.\n");
+
+    /* Set all the labels to no_of_nodes first */
+    igraph_vector_fill(labels, no_of_nodes);
+
+    /* Allocate vector for neighbors */
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    /* Create a FIFO for the BFS and initialize it with the unmatched rows
+     * (i.e. members of the larger set) */
+    IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0));
+    IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q);
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*types)[i] != smaller_set && VECTOR(*match)[i] == -1) {
+            IGRAPH_CHECK(igraph_dqueue_long_push(&q, i));
+            VECTOR(*labels)[i] = 0;
+        }
+    }
+
+    /* Run the BFS */
+    while (!igraph_dqueue_long_empty(&q)) {
+        long int v = igraph_dqueue_long_pop(&q);
+        long int w;
+
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v,
+                                      IGRAPH_ALL));
+
+        n = igraph_vector_size(&neis);
+        for (j = 0; j < n; j++) {
+            w = (long int) VECTOR(neis)[j];
+            if (VECTOR(*labels)[w] == no_of_nodes) {
+                VECTOR(*labels)[w] = VECTOR(*labels)[v] + 1;
+                matched_to = VECTOR(*match)[w];
+                if (matched_to != -1 && VECTOR(*labels)[matched_to] == no_of_nodes) {
+                    IGRAPH_CHECK(igraph_dqueue_long_push(&q, matched_to));
+                    VECTOR(*labels)[matched_to] = VECTOR(*labels)[w] + 1;
+                }
+            }
+        }
+    }
+
+    igraph_dqueue_long_destroy(&q);
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * Finding maximum bipartite matchings on bipartite graphs using the
+ * Hungarian algorithm (a.k.a. Kuhn-Munkres algorithm).
+ *
+ * The algorithm uses a maximum cardinality matching on a subset of
+ * tight edges as a starting point. This is achieved by
+ * \c igraph_i_maximum_bipartite_matching_unweighted on the restricted
+ * graph.
+ *
+ * The algorithm works reliably only if the weights are integers. The
+ * \c eps parameter should specity a very small number; if the slack on
+ * an edge falls below \c eps, it will be considered tight. If all your
+ * weights are integers, you can safely set \c eps to zero.
+ */
+int igraph_i_maximum_bipartite_matching_weighted(const igraph_t* graph,
+        const igraph_vector_bool_t* types, igraph_integer_t* matching_size,
+        igraph_real_t* matching_weight, igraph_vector_long_t* matching,
+        const igraph_vector_t* weights, igraph_real_t eps) {
+    long int i, j, k, n, no_of_nodes, no_of_edges;
+    igraph_integer_t u, v, w, msize;
+    igraph_t newgraph;
+    igraph_vector_long_t match;       /* will store the matching */
+    igraph_vector_t slack;            /* will store the slack on each edge */
+    igraph_vector_t parent;           /* parent vertices during a BFS */
+    igraph_vector_t vec1, vec2;       /* general temporary vectors */
+    igraph_vector_t labels;           /* will store the labels */
+    igraph_dqueue_long_t q;           /* a FIFO for BST */
+    igraph_bool_t smaller_set_type;   /* denotes which part of the bipartite graph is smaller */
+    igraph_vector_t smaller_set;      /* stores the vertex IDs of the smaller set */
+    igraph_vector_t larger_set;       /* stores the vertex IDs of the larger set */
+    long int smaller_set_size;        /* size of the smaller set */
+    long int larger_set_size;         /* size of the larger set */
+    igraph_real_t dual;               /* solution of the dual problem */
+    igraph_adjlist_t tight_phantom_edges; /* adjacency list to manage tight phantom edges */
+    igraph_integer_t alternating_path_endpoint;
+    igraph_vector_int_t* neis;
+    igraph_vector_int_t *neis2;
+    igraph_inclist_t inclist;         /* incidence list of the original graph */
+
+    /* The Hungarian algorithm is originally for complete bipartite graphs.
+     * For non-complete bipartite graphs, a phantom edge of weight zero must be
+     * added between every pair of non-connected vertices. We don't do this
+     * explicitly of course. See the comments below about how phantom edges
+     * are taken into account. */
+
+    no_of_nodes = igraph_vcount(graph);
+    no_of_edges = igraph_ecount(graph);
+    if (eps < 0) {
+        IGRAPH_WARNING("negative epsilon given, clamping to zero");
+        eps = 0;
+    }
+
+    /* (1) Initialize data structures */
+    IGRAPH_CHECK(igraph_vector_long_init(&match, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &match);
+    IGRAPH_CHECK(igraph_vector_init(&slack, no_of_edges));
+    IGRAPH_FINALLY(igraph_vector_destroy, &slack);
+    IGRAPH_VECTOR_INIT_FINALLY(&vec1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&vec2, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes);
+    IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0));
+    IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q);
+    IGRAPH_VECTOR_INIT_FINALLY(&parent, no_of_nodes);
+    IGRAPH_CHECK(igraph_adjlist_init_empty(&tight_phantom_edges,
+                                           (igraph_integer_t) no_of_nodes));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &tight_phantom_edges);
+    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+    IGRAPH_VECTOR_INIT_FINALLY(&smaller_set, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&larger_set, 0);
+
+    /* (2) Find which set is the smaller one */
+    j = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*types)[i] == 0) {
+            j++;
+        }
+    }
+    smaller_set_type = (j > no_of_nodes / 2);
+    smaller_set_size = smaller_set_type ? (no_of_nodes - j) : j;
+    larger_set_size = no_of_nodes - smaller_set_size;
+    IGRAPH_CHECK(igraph_vector_reserve(&smaller_set, smaller_set_size));
+    IGRAPH_CHECK(igraph_vector_reserve(&larger_set, larger_set_size));
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*types)[i] == smaller_set_type) {
+            IGRAPH_CHECK(igraph_vector_push_back(&smaller_set, i));
+        } else {
+            IGRAPH_CHECK(igraph_vector_push_back(&larger_set, i));
+        }
+    }
+
+    /* (3) Calculate the initial labeling and the set of tight edges. Use the
+     *     smaller set only. Here we can assume that there are no phantom edges
+     *     among the tight ones. */
+    dual = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_real_t max_weight = 0;
+
+        if (VECTOR(*types)[i] != smaller_set_type) {
+            VECTOR(labels)[i] = 0;
+            continue;
+        }
+
+        neis = igraph_inclist_get(&inclist, i);
+        n = igraph_vector_int_size(neis);
+        for (j = 0, k = 0; j < n; j++) {
+            k = (long int) VECTOR(*neis)[j];
+            u = IGRAPH_OTHER(graph, k, i);
+            if (VECTOR(*types)[u] == VECTOR(*types)[i]) {
+                IGRAPH_ERROR("Graph is not bipartite with supplied types vector", IGRAPH_EINVAL);
+            }
+            if (VECTOR(*weights)[k] > max_weight) {
+                max_weight = VECTOR(*weights)[k];
+            }
+        }
+
+        VECTOR(labels)[i] = max_weight;
+        dual += max_weight;
+    }
+
+    igraph_vector_clear(&vec1);
+    IGRAPH_CHECK(igraph_get_edgelist(graph, &vec2, 0));
+#define IS_TIGHT(i) (VECTOR(slack)[i] <= eps)
+    for (i = 0, j = 0; i < no_of_edges; i++, j += 2) {
+        u = (igraph_integer_t) VECTOR(vec2)[j];
+        v = (igraph_integer_t) VECTOR(vec2)[j + 1];
+        VECTOR(slack)[i] = VECTOR(labels)[u] + VECTOR(labels)[v] - VECTOR(*weights)[i];
+        if (IS_TIGHT(i)) {
+            IGRAPH_CHECK(igraph_vector_push_back(&vec1, u));
+            IGRAPH_CHECK(igraph_vector_push_back(&vec1, v));
+        }
+    }
+    igraph_vector_clear(&vec2);
+
+    /* (4) Construct a temporary graph on which the initial maximum matching
+     *     will be calculated (only on the subset of tight edges) */
+    IGRAPH_CHECK(igraph_create(&newgraph, &vec1,
+                               (igraph_integer_t) no_of_nodes, 0));
+    IGRAPH_FINALLY(igraph_destroy, &newgraph);
+    IGRAPH_CHECK(igraph_maximum_bipartite_matching(&newgraph, types, &msize, 0, &match, 0, 0));
+    igraph_destroy(&newgraph);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* (5) Main loop until the matching becomes maximal */
+    while (msize < smaller_set_size) {
+        igraph_real_t min_slack, min_slack_2;
+        igraph_integer_t min_slack_u, min_slack_v;
+
+        /* (7) Fill the push queue with the unmatched nodes from the smaller set. */
+        igraph_vector_clear(&vec1);
+        igraph_vector_clear(&vec2);
+        igraph_vector_fill(&parent, -1);
+        for (j = 0; j < smaller_set_size; j++) {
+            i = VECTOR(smaller_set)[j];
+            if (UNMATCHED(i)) {
+                IGRAPH_CHECK(igraph_dqueue_long_push(&q, i));
+                VECTOR(parent)[i] = i;
+                IGRAPH_CHECK(igraph_vector_push_back(&vec1, i));
+            }
+        }
+
+#ifdef MATCHING_DEBUG
+        debug("Matching:");
+        igraph_vector_long_print(&match);
+        debug("Unmatched vertices are marked by non-negative numbers:\n");
+        igraph_vector_print(&parent);
+        debug("Labeling:");
+        igraph_vector_print(&labels);
+        debug("Slacks:");
+        igraph_vector_print(&slack);
+#endif
+
+        /* (8) Run the BFS */
+        alternating_path_endpoint = -1;
+        while (!igraph_dqueue_long_empty(&q)) {
+            v = (int) igraph_dqueue_long_pop(&q);
+
+            debug("Considering vertex %ld\n", (long int)v);
+
+            /* v is always in the smaller set. Find the neighbors of v, which
+             * are all in the larger set. Find the pairs of these nodes in
+             * the smaller set and push them to the queue. Mark the traversed
+             * nodes as seen.
+             *
+             * Here we have to be careful as there are two types of incident
+             * edges on v: real edges and phantom ones. Real edges are
+             * given by igraph_inclist_get. Phantom edges are not given so we
+             * (ab)use an adjacency list data structure that lists the
+             * vertices connected to v by phantom edges only. */
+            neis = igraph_inclist_get(&inclist, v);
+            n = igraph_vector_int_size(neis);
+            for (i = 0; i < n; i++) {
+                j = (long int) VECTOR(*neis)[i];
+                /* We only care about tight edges */
+                if (!IS_TIGHT(j)) {
+                    continue;
+                }
+                /* Have we seen the other endpoint already? */
+                u = IGRAPH_OTHER(graph, j, v);
+                if (VECTOR(parent)[u] >= 0) {
+                    continue;
+                }
+                debug("  Reached vertex %ld via edge %ld\n", (long)u, (long)j);
+                VECTOR(parent)[u] = v;
+                IGRAPH_CHECK(igraph_vector_push_back(&vec2, u));
+                w = (int) VECTOR(match)[u];
+                if (w == -1) {
+                    /* u is unmatched and it is in the larger set. Therefore, we
+                     * could improve the matching by following the parents back
+                     * from u to the root.
+                     */
+                    alternating_path_endpoint = u;
+                    break;  /* since we don't need any more endpoints that come from v */
+                } else {
+                    IGRAPH_CHECK(igraph_dqueue_long_push(&q, w));
+                    VECTOR(parent)[w] = u;
+                }
+                IGRAPH_CHECK(igraph_vector_push_back(&vec1, w));
+            }
+
+            /* Now do the same with the phantom edges */
+            neis2 = igraph_adjlist_get(&tight_phantom_edges, v);
+            n = igraph_vector_int_size(neis2);
+            for (i = 0; i < n; i++) {
+                u = (igraph_integer_t) VECTOR(*neis2)[i];
+                /* Have we seen u already? */
+                if (VECTOR(parent)[u] >= 0) {
+                    continue;
+                }
+                /* Check if the edge is really tight; it might have happened that the
+                 * edge became non-tight in the meanwhile. We do not remove these from
+                 * tight_phantom_edges at the moment, so we check them once again here.
+                 */
+                if (fabs(VECTOR(labels)[(long int)v] + VECTOR(labels)[(long int)u]) > eps) {
+                    continue;
+                }
+                debug("  Reached vertex %ld via tight phantom edge\n", (long)u);
+                VECTOR(parent)[u] = v;
+                IGRAPH_CHECK(igraph_vector_push_back(&vec2, u));
+                w = (int) VECTOR(match)[u];
+                if (w == -1) {
+                    /* u is unmatched and it is in the larger set. Therefore, we
+                     * could improve the matching by following the parents back
+                     * from u to the root.
+                     */
+                    alternating_path_endpoint = u;
+                    break;  /* since we don't need any more endpoints that come from v */
+                } else {
+                    IGRAPH_CHECK(igraph_dqueue_long_push(&q, w));
+                    VECTOR(parent)[w] = u;
+                }
+                IGRAPH_CHECK(igraph_vector_push_back(&vec1, w));
+            }
+        }
+
+        /* Okay; did we have an alternating path? */
+        if (alternating_path_endpoint != -1) {
+#ifdef MATCHING_DEBUG
+            debug("BFS parent tree:");
+            igraph_vector_print(&parent);
+#endif
+            /* Increase the size of the matching with the alternating path. */
+            v = alternating_path_endpoint;
+            u = (igraph_integer_t) VECTOR(parent)[v];
+            debug("Extending matching with alternating path ending in %ld.\n", (long int)v);
+
+            while (u != v) {
+                w = (int) VECTOR(match)[v];
+                if (w != -1) {
+                    VECTOR(match)[w] = -1;
+                }
+                VECTOR(match)[v] = u;
+
+                VECTOR(match)[v] = u;
+                w = (int) VECTOR(match)[u];
+                if (w != -1) {
+                    VECTOR(match)[w] = -1;
+                }
+                VECTOR(match)[u] = v;
+
+                v = (igraph_integer_t) VECTOR(parent)[u];
+                u = (igraph_integer_t) VECTOR(parent)[v];
+            }
+
+            msize++;
+
+#ifdef MATCHING_DEBUG
+            debug("New matching after update:");
+            igraph_vector_long_print(&match);
+            debug("Matching size is now: %ld\n", (long)msize);
+#endif
+            continue;
+        }
+
+#ifdef MATCHING_DEBUG
+        debug("Vertices reachable from unmatched ones via tight edges:\n");
+        igraph_vector_print(&vec1);
+        igraph_vector_print(&vec2);
+#endif
+
+        /* At this point, vec1 contains the nodes in the smaller set (A)
+         * reachable from unmatched nodes in A via tight edges only, while vec2
+         * contains the nodes in the larger set (B) reachable from unmatched
+         * nodes in A via tight edges only. Also, parent[i] >= 0 if node i
+         * is reachable */
+
+        /* Check the edges between reachable nodes in A and unreachable
+         * nodes in B, and find the minimum slack on them.
+         *
+         * Since the weights are positive, we do no harm if we first
+         * assume that there are no "real" edges between the two sets
+         * mentioned above and determine an upper bound for min_slack
+         * based on this. */
+        min_slack = IGRAPH_INFINITY;
+        min_slack_u = min_slack_v = 0;
+        n = igraph_vector_size(&vec1);
+        for (j = 0; j < larger_set_size; j++) {
+            i = VECTOR(larger_set)[j];
+            if (VECTOR(labels)[i] < min_slack) {
+                min_slack = VECTOR(labels)[i];
+                min_slack_v = (igraph_integer_t) i;
+            }
+        }
+        min_slack_2 = IGRAPH_INFINITY;
+        for (i = 0; i < n; i++) {
+            u = (igraph_integer_t) VECTOR(vec1)[i];
+            /* u is surely from the smaller set, but we are interested in it
+             * only if it is reachable from an unmatched vertex */
+            if (VECTOR(parent)[u] < 0) {
+                continue;
+            }
+            if (VECTOR(labels)[u] < min_slack_2) {
+                min_slack_2 = VECTOR(labels)[u];
+                min_slack_u = u;
+            }
+        }
+        min_slack += min_slack_2;
+        debug("Starting approximation for min_slack = %.4f (based on vertex pair %ld--%ld)\n",
+              min_slack, (long int)min_slack_u, (long int)min_slack_v);
+
+        n = igraph_vector_size(&vec1);
+        for (i = 0; i < n; i++) {
+            u = (igraph_integer_t) VECTOR(vec1)[i];
+            /* u is a reachable node in A; get its incident edges.
+             *
+             * There are two types of incident edges: 1) real edges,
+             * 2) phantom edges. Phantom edges were treated earlier
+             * when we determined the initial value for min_slack. */
+            debug("Trying to expand along vertex %ld\n", (long int)u);
+            neis = igraph_inclist_get(&inclist, u);
+            k = igraph_vector_int_size(neis);
+            for (j = 0; j < k; j++) {
+                /* v is the vertex sitting at the other end of an edge incident
+                 * on u; check whether it was reached */
+                v = IGRAPH_OTHER(graph, VECTOR(*neis)[j], u);
+                debug("  Edge %ld -- %ld (ID=%ld)\n", (long int)u, (long int)v, (long int)VECTOR(*neis)[j]);
+                if (VECTOR(parent)[v] >= 0) {
+                    /* v was reached, so we are not interested in it */
+                    debug("    %ld was reached, so we are not interested in it\n", (long int)v);
+                    continue;
+                }
+                /* v is the ID of the edge from now on */
+                v = (igraph_integer_t) VECTOR(*neis)[j];
+                if (VECTOR(slack)[v] < min_slack) {
+                    min_slack = VECTOR(slack)[v];
+                    min_slack_u = u;
+                    min_slack_v = IGRAPH_OTHER(graph, v, u);
+                }
+                debug("    Slack of this edge: %.4f, min slack is now: %.4f\n",
+                      VECTOR(slack)[v], min_slack);
+            }
+        }
+        debug("Minimum slack: %.4f on edge %d--%d\n", min_slack, (int)min_slack_u, (int)min_slack_v);
+
+        if (min_slack > 0) {
+            /* Decrease the label of reachable nodes in A by min_slack.
+             * Also update the dual solution */
+            n = igraph_vector_size(&vec1);
+            for (i = 0; i < n; i++) {
+                u = (igraph_integer_t) VECTOR(vec1)[i];
+                VECTOR(labels)[u] -= min_slack;
+                neis = igraph_inclist_get(&inclist, u);
+                k = igraph_vector_int_size(neis);
+                for (j = 0; j < k; j++) {
+                    debug("  Decreasing slack of edge %ld (%ld--%ld) by %.4f\n",
+                          (long)VECTOR(*neis)[j], (long)u,
+                          (long)IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack);
+                    VECTOR(slack)[(long int)VECTOR(*neis)[j]] -= min_slack;
+                }
+                dual -= min_slack;
+            }
+
+            /* Increase the label of reachable nodes in B by min_slack.
+             * Also update the dual solution */
+            n = igraph_vector_size(&vec2);
+            for (i = 0; i < n; i++) {
+                u = (igraph_integer_t) VECTOR(vec2)[i];
+                VECTOR(labels)[u] += min_slack;
+                neis = igraph_inclist_get(&inclist, u);
+                k = igraph_vector_int_size(neis);
+                for (j = 0; j < k; j++) {
+                    debug("  Increasing slack of edge %ld (%ld--%ld) by %.4f\n",
+                          (long)VECTOR(*neis)[j], (long)u,
+                          (long)IGRAPH_OTHER(graph, (long)VECTOR(*neis)[j], u), min_slack);
+                    VECTOR(slack)[(long int)VECTOR(*neis)[j]] += min_slack;
+                }
+                dual += min_slack;
+            }
+        }
+
+        /* Update the set of tight phantom edges.
+         * Note that we must do it even if min_slack is zero; the reason is that
+         * it can happen that min_slack is zero in the first step if there are
+         * isolated nodes in the input graph.
+         *
+         * TODO: this is O(n^2) here. Can we do it faster? */
+        for (i = 0; i < smaller_set_size; i++) {
+            u = VECTOR(smaller_set)[i];
+            for (j = 0; j < larger_set_size; j++) {
+                v = VECTOR(larger_set)[j];
+                if (VECTOR(labels)[(long int)u] + VECTOR(labels)[(long int)v] <= eps) {
+                    /* Tight phantom edge found. Note that we don't have to check whether
+                     * u and v are connected; if they were, then the slack of this edge
+                     * would be negative. */
+                    neis2 = igraph_adjlist_get(&tight_phantom_edges, u);
+                    if (!igraph_vector_int_binsearch(neis2, v, &k)) {
+                        debug("New tight phantom edge: %ld -- %ld\n", (long)u, (long)v);
+                        IGRAPH_CHECK(igraph_vector_int_insert(neis2, k, v));
+                    }
+                }
+            }
+        }
+
+#ifdef MATCHING_DEBUG
+        debug("New labels:");
+        igraph_vector_print(&labels);
+        debug("Slacks after updating with min_slack:");
+        igraph_vector_print(&slack);
+#endif
+    }
+
+    /* Cleanup: remove phantom edges from the matching */
+    for (i = 0; i < smaller_set_size; i++) {
+        u = VECTOR(smaller_set)[i];
+        v = VECTOR(match)[u];
+        if (v != -1) {
+            neis2 = igraph_adjlist_get(&tight_phantom_edges, u);
+            if (igraph_vector_int_binsearch(neis2, v, 0)) {
+                VECTOR(match)[u] = VECTOR(match)[v] = -1;
+                msize--;
+            }
+        }
+    }
+
+    /* Fill the output parameters */
+    if (matching != 0) {
+        IGRAPH_CHECK(igraph_vector_long_update(matching, &match));
+    }
+    if (matching_size != 0) {
+        *matching_size = msize;
+    }
+    if (matching_weight != 0) {
+        *matching_weight = 0;
+        for (i = 0; i < no_of_edges; i++) {
+            if (IS_TIGHT(i)) {
+                IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) i, &u, &v));
+                if (VECTOR(match)[u] == v) {
+                    *matching_weight += VECTOR(*weights)[i];
+                }
+            }
+        }
+    }
+
+    /* Release everything */
+#undef IS_TIGHT
+    igraph_vector_destroy(&larger_set);
+    igraph_vector_destroy(&smaller_set);
+    igraph_inclist_destroy(&inclist);
+    igraph_adjlist_destroy(&tight_phantom_edges);
+    igraph_vector_destroy(&parent);
+    igraph_dqueue_long_destroy(&q);
+    igraph_vector_destroy(&labels);
+    igraph_vector_destroy(&vec1);
+    igraph_vector_destroy(&vec2);
+    igraph_vector_destroy(&slack);
+    igraph_vector_long_destroy(&match);
+    IGRAPH_FINALLY_CLEAN(11);
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_maximum_matching(const igraph_t* graph, igraph_integer_t* matching_size,
+                            igraph_real_t* matching_weight, igraph_vector_long_t* matching,
+                            const igraph_vector_t* weights) {
+    IGRAPH_UNUSED(graph);
+    IGRAPH_UNUSED(matching_size);
+    IGRAPH_UNUSED(matching_weight);
+    IGRAPH_UNUSED(matching);
+    IGRAPH_UNUSED(weights);
+    IGRAPH_ERROR("maximum matching on general graphs not implemented yet",
+                 IGRAPH_UNIMPLEMENTED);
+}
+
+#ifdef MATCHING_DEBUG
+    #undef MATCHING_DEBUG
+#endif
+
+
diff --git a/igraph/src/math.c b/igraph/src/math.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/math.c
@@ -0,0 +1,326 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include <math.h>
+#include <float.h>
+#include <stdarg.h>
+#include "config.h"
+#include "igraph_math.h"
+#include "igraph_types.h"
+
+#ifdef _MSC_VER
+    #define isinf(x) (!_finite(x) && !_isnan(x))
+#endif
+
+int igraph_finite(double x) {
+#ifdef isfinite
+    return isfinite(x);
+#elif HAVE_ISFINITE == 1
+    return isfinite(x);
+#elif HAVE_FINITE == 1
+    return finite(x);
+#else
+    /* neither finite nor isfinite work. Do we really need the AIX exception? */
+# ifdef _AIX
+#  include <fp.h>
+    return FINITE(x);
+# else
+    return (!isnan(x) & (x != IGRAPH_POSINFINITY) & (x != IGRAPH_NEGINFINITY));
+# endif
+#endif
+}
+
+double igraph_log2(const double a) {
+    return log(a) / log(2.0);
+}
+
+int igraph_chebyshev_init(const double *dos, int nos, double eta) {
+    int i, ii;
+    double err;
+
+    if (nos < 1) {
+        return 0;
+    }
+
+    err = 0.0;
+    i = 0;          /* just to avoid compiler warnings */
+    for (ii = 1; ii <= nos; ii++) {
+        i = nos - ii;
+        err += fabs(dos[i]);
+        if (err > eta) {
+            return i;
+        }
+    }
+    return i;
+}
+
+double igraph_chebyshev_eval(double x, const double *a, const int n) {
+    double b0, b1, b2, twox;
+    int i;
+
+    if (n < 1 || n > 1000) {
+        IGRAPH_NAN;
+    }
+
+    if (x < -1.1 || x > 1.1) {
+        IGRAPH_NAN;
+    }
+
+    twox = x * 2;
+    b2 = b1 = 0;
+    b0 = 0;
+    for (i = 1; i <= n; i++) {
+        b2 = b1;
+        b1 = b0;
+        b0 = twox * b1 - b2 + a[n - i];
+    }
+    return (b0 - b2) * 0.5;
+}
+
+double igraph_log1p(double x) {
+    /* series for log1p on the interval -.375 to .375
+     *                   with weighted error   6.35e-32
+     *                    log weighted error  31.20
+     *              significant figures required  30.93
+     *               decimal places required  32.01
+     */
+    static const double alnrcs[43] = {
+        +.10378693562743769800686267719098e+1,
+            -.13364301504908918098766041553133e+0,
+            +.19408249135520563357926199374750e-1,
+            -.30107551127535777690376537776592e-2,
+            +.48694614797154850090456366509137e-3,
+            -.81054881893175356066809943008622e-4,
+            +.13778847799559524782938251496059e-4,
+            -.23802210894358970251369992914935e-5,
+            +.41640416213865183476391859901989e-6,
+            -.73595828378075994984266837031998e-7,
+            +.13117611876241674949152294345011e-7,
+            -.23546709317742425136696092330175e-8,
+            +.42522773276034997775638052962567e-9,
+            -.77190894134840796826108107493300e-10,
+            +.14075746481359069909215356472191e-10,
+            -.25769072058024680627537078627584e-11,
+            +.47342406666294421849154395005938e-12,
+            -.87249012674742641745301263292675e-13,
+            +.16124614902740551465739833119115e-13,
+            -.29875652015665773006710792416815e-14,
+            +.55480701209082887983041321697279e-15,
+            -.10324619158271569595141333961932e-15,
+            +.19250239203049851177878503244868e-16,
+            -.35955073465265150011189707844266e-17,
+            +.67264542537876857892194574226773e-18,
+            -.12602624168735219252082425637546e-18,
+            +.23644884408606210044916158955519e-19,
+            -.44419377050807936898878389179733e-20,
+            +.83546594464034259016241293994666e-21,
+            -.15731559416479562574899253521066e-21,
+            +.29653128740247422686154369706666e-22,
+            -.55949583481815947292156013226666e-23,
+            +.10566354268835681048187284138666e-23,
+            -.19972483680670204548314999466666e-24,
+            +.37782977818839361421049855999999e-25,
+            -.71531586889081740345038165333333e-26,
+            +.13552488463674213646502024533333e-26,
+            -.25694673048487567430079829333333e-27,
+            +.48747756066216949076459519999999e-28,
+            -.92542112530849715321132373333333e-29,
+            +.17578597841760239233269760000000e-29,
+            -.33410026677731010351377066666666e-30,
+            +.63533936180236187354180266666666e-31,
+        };
+
+    static IGRAPH_THREAD_LOCAL int nlnrel = 0;
+    static IGRAPH_THREAD_LOCAL double xmin = 0.0;
+
+    if (xmin == 0.0) {
+        xmin = -1 + sqrt(DBL_EPSILON);    /*was sqrt(d1mach(4)); */
+    }
+    if (nlnrel == 0) { /* initialize chebychev coefficients */
+        nlnrel = igraph_chebyshev_init(alnrcs, 43, DBL_EPSILON / 20);    /*was .1*d1mach(3)*/
+    }
+
+    if (x == 0.) {
+        return 0.;    /* speed */
+    }
+    if (x == -1) {
+        return (IGRAPH_NEGINFINITY);
+    }
+    if (x  < -1) {
+        return (IGRAPH_NAN);
+    }
+
+    if (fabs(x) <= .375) {
+        /* Improve on speed (only);
+        again give result accurate to IEEE double precision: */
+        if (fabs(x) < .5 * DBL_EPSILON) {
+            return x;
+        }
+
+        if ( (0 < x && x < 1e-8) || (-1e-9 < x && x < 0)) {
+            return x * (1 - .5 * x);
+        }
+        /* else */
+        return x * (1 - x * igraph_chebyshev_eval(x / .375, alnrcs, nlnrel));
+    }
+    /* else */
+    /*     if (x < xmin) { */
+    /*  /\* answer less than half precision because x too near -1 *\/ */
+    /*         ML_ERROR(ME_PRECISION, "log1p"); */
+    /*     } */
+    return log(1 + x);
+}
+
+long double igraph_fabsl(long double a) {
+    if (a < 0) {
+        return -a;
+    } else {
+        return a;
+    }
+}
+
+double igraph_fmin(double a, double b) {
+    if (b < a) {
+        return b;
+    } else {
+        return a;
+    }
+}
+
+double igraph_i_round(double X) {
+
+    /* NaN */
+    if (X != X) {
+        return X;
+    }
+
+    if (X < 0.0) {
+        return floor(X);
+    }
+
+    return ceil(X);
+}
+
+#ifdef _MSC_VER
+/**
+ * Internal function, replacement for snprintf
+ * Used only in case of the Microsoft Visual C compiler which does not
+ * provide a proper sprintf implementation.
+ *
+ * This implementation differs from the standard in the value returned
+ * when the number of characters needed by the output, excluding the
+ * terminating '\0' is larger than count
+ */
+int igraph_i_snprintf(char *buffer, size_t count, const char *format, ...) {
+    int n;
+    va_list args;
+    if (count > 0) {
+        va_start(args, format);
+        n = _vsnprintf(buffer, count, format, args);
+        buffer[count - 1] = 0;
+        va_end(args);
+    } else {
+        n = 0;
+    }
+    return n;
+}
+
+#endif
+
+int igraph_is_nan(double x) {
+    return isnan(x);
+}
+
+int igraph_is_inf(double x) {
+    return isinf(x) != 0;
+}
+
+int igraph_is_posinf(double x) {
+    return isinf(x) == 1;
+}
+
+int igraph_is_neginf(double x) {
+    return isinf(x) == -1;
+}
+
+/**
+ * \function igraph_almost_equals
+ * Compare two double-precision floats with a tolerance
+ *
+ * Determines whether two double-precision floats are "almost equal"
+ * to each other with a given level of tolerance on the relative error.
+ *
+ * \param  a  the first float
+ * \param  b  the second float
+ * \param  eps  the level of tolerance on the relative error. The relative
+ *         error is defined as \c "abs(a-b) / (abs(a) + abs(b))". The
+ *         two numbers are considered equal if this is less than \c eps.
+ *
+ * \return nonzero if the two floats are nearly equal to each other within
+ *         the given level of tolerance, zero otherwise
+ */
+int igraph_almost_equals(double a, double b, double eps) {
+    return igraph_cmp_epsilon(a, b, eps) == 0 ? 1 : 0;
+}
+
+
+/**
+ * \function igraph_cmp_epsilon
+ * Compare two double-precision floats with a tolerance
+ *
+ * Determines whether two double-precision floats are "almost equal"
+ * to each other with a given level of tolerance on the relative error.
+ *
+ * \param  a  the first float
+ * \param  b  the second float
+ * \param  eps  the level of tolerance on the relative error. The relative
+ *         error is defined as \c "abs(a-b) / (abs(a) + abs(b))". The
+ *         two numbers are considered equal if this is less than \c eps.
+ *
+ * \return zero if the two floats are nearly equal to each other within
+ *         the given level of tolerance, positive number if the first float is
+ *         larger, negative number if the second float is larger
+ */
+int igraph_cmp_epsilon(double a, double b, double eps) {
+    double diff;
+    double abs_diff;
+
+    if (a == b) {
+        /* shortcut, handles infinities */
+        return 0;
+    }
+
+    diff = a - b;
+    abs_diff = fabs(diff);
+
+    if (a == 0 || b == 0 || diff < DBL_MIN) {
+        /* a or b is zero or both are extremely close to it; relative
+         * error is less meaningful here so just compare it with
+         * epsilon */
+        return abs_diff < (eps * DBL_MIN) ? 0 : (diff < 0 ? -1 : 1);
+    } else {
+        /* use relative error */
+        return (abs_diff / (fabs(a) + fabs(b)) < eps) ? 0 : (diff < 0 ? -1 : 1);
+    }
+}
+
diff --git a/igraph/src/matrix.c b/igraph/src/matrix.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/matrix.c
@@ -0,0 +1,158 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_matrix.h"
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "matrix.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "matrix.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "matrix.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "matrix.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "matrix.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_COMPLEX
+#include "igraph_pmt.h"
+#include "matrix.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_COMPLEX
+
+#ifndef USING_R
+int igraph_matrix_complex_print(const igraph_matrix_complex_t *m) {
+
+    long int nr = igraph_matrix_complex_nrow(m);
+    long int nc = igraph_matrix_complex_ncol(m);
+    long int i, j;
+    for (i = 0; i < nr; i++) {
+        for (j = 0; j < nc; j++) {
+            igraph_complex_t z = MATRIX(*m, i, j);
+            if (j != 0) {
+                putchar(' ');
+            }
+            printf("%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z));
+        }
+        printf("\n");
+    }
+
+    return 0;
+}
+#endif
+
+int igraph_matrix_complex_fprint(const igraph_matrix_complex_t *m,
+                                 FILE *file) {
+
+    long int nr = igraph_matrix_complex_nrow(m);
+    long int nc = igraph_matrix_complex_ncol(m);
+    long int i, j;
+    for (i = 0; i < nr; i++) {
+        for (j = 0; j < nc; j++) {
+            igraph_complex_t z = MATRIX(*m, i, j);
+            if (j != 0) {
+                fputc(' ', file);
+            }
+            fprintf(file, "%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z));
+        }
+        fprintf(file, "\n");
+    }
+
+    return 0;
+}
+
+int igraph_matrix_complex_real(const igraph_matrix_complex_t *v,
+                               igraph_matrix_t *real) {
+    long int nrow = igraph_matrix_complex_nrow(v);
+    long int ncol = igraph_matrix_complex_ncol(v);
+    IGRAPH_CHECK(igraph_matrix_resize(real, nrow, ncol));
+    IGRAPH_CHECK(igraph_vector_complex_real(&v->data, &real->data));
+    return 0;
+}
+
+int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v,
+                               igraph_matrix_t *imag) {
+    long int nrow = igraph_matrix_complex_nrow(v);
+    long int ncol = igraph_matrix_complex_ncol(v);
+    IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol));
+    IGRAPH_CHECK(igraph_vector_complex_imag(&v->data, &imag->data));
+    return 0;
+}
+
+int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v,
+                                   igraph_matrix_t *real,
+                                   igraph_matrix_t *imag) {
+    long int nrow = igraph_matrix_complex_nrow(v);
+    long int ncol = igraph_matrix_complex_ncol(v);
+    IGRAPH_CHECK(igraph_matrix_resize(real, nrow, ncol));
+    IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol));
+    IGRAPH_CHECK(igraph_vector_complex_realimag(&v->data, &real->data,
+                 &imag->data));
+    return 0;
+}
+
+int igraph_matrix_complex_create(igraph_matrix_complex_t *v,
+                                 const igraph_matrix_t *real,
+                                 const igraph_matrix_t *imag) {
+    IGRAPH_CHECK(igraph_vector_complex_create(&v->data, &real->data,
+                 &imag->data));
+    return 0;
+}
+
+int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v,
+                                       const igraph_matrix_t *r,
+                                       const igraph_matrix_t *theta) {
+    IGRAPH_CHECK(igraph_vector_complex_create_polar(&v->data, &r->data,
+                 &theta->data));
+    return 0;
+}
+
+igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs,
+                                      const igraph_matrix_t *rhs,
+                                      igraph_real_t tol) {
+    return igraph_vector_e_tol(&lhs->data, &rhs->data, tol);
+}
+
+int igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol) {
+    return igraph_vector_zapsmall(&m->data, tol);
+}
diff --git a/igraph/src/maximal_cliques.c b/igraph/src/maximal_cliques.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/maximal_cliques.c
@@ -0,0 +1,496 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_cliques.h"
+#include "igraph_constants.h"
+#include "igraph_interface.h"
+#include "igraph_community.h"
+#include "igraph_adjlist.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_memory.h"
+#include "igraph_progress.h"
+#include "igraph_math.h"
+
+#define CONCAT2x(a,b) a ## b
+#define CONCAT2(a,b) CONCAT2x(a,b)
+#define FUNCTION(name,sfx) CONCAT2(name,sfx)
+
+int igraph_i_maximal_cliques_reorder_adjlists(
+    const igraph_vector_int_t *PX,
+    int PS, int PE, int XS, int XE,
+    const igraph_vector_int_t *pos,
+    igraph_adjlist_t *adjlist);
+
+int igraph_i_maximal_cliques_select_pivot(const igraph_vector_int_t *PX,
+        int PS, int PE, int XS, int XE,
+        const igraph_vector_int_t *pos,
+        const igraph_adjlist_t *adjlist,
+        int *pivot,
+        igraph_vector_int_t *nextv,
+        int oldPS, int oldXE);
+
+int igraph_i_maximal_cliques_down(igraph_vector_int_t *PX,
+                                  int PS, int PE, int XS, int XE,
+                                  igraph_vector_int_t *pos,
+                                  igraph_adjlist_t *adjlist, int mynextv,
+                                  igraph_vector_int_t *R,
+                                  int *newPS, int *newXE);
+
+int igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, int PS, int *PE,
+                                int *XS, int XE, igraph_vector_int_t *pos,
+                                igraph_adjlist_t *adjlist, int v,
+                                igraph_vector_int_t *H);
+
+int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE,
+                                int XS, int XE, igraph_vector_int_t *pos,
+                                igraph_adjlist_t *adjlist,
+                                igraph_vector_int_t *R,
+                                igraph_vector_int_t *H);
+
+#define PRINT_PX do {                              \
+        int j;                                 \
+        printf("PX=");                             \
+        for (j=0; j<PS; j++) {                         \
+            printf("%i ", VECTOR(*PX)[j]);                       \
+        }                                      \
+        printf("( ");                              \
+        for (; j<=PE; j++) {                           \
+            printf("%i ", VECTOR(*PX)[j]);                       \
+        }                                      \
+        printf("| ");                              \
+        for (; j<=XE; j++) {                           \
+            printf("%i ", VECTOR(*PX)[j]);                       \
+        }                                      \
+        printf(") ");                              \
+        for (; j<igraph_vector_int_size(PX); j++) {                \
+            printf("%i ", VECTOR(*PX)[j]);                       \
+        }                                      \
+        printf("\n");                              \
+    } while (0);
+
+#define PRINT_PX1 do {                             \
+        int j;                                 \
+        printf("PX=");                             \
+        for (j=0; j<PS; j++) {                         \
+            printf("%i ", VECTOR(*PX)[j]);                       \
+        }                                      \
+        printf("( ");                              \
+        for (; j<=*PE; j++) {                          \
+            printf("%i ", VECTOR(*PX)[j]);                       \
+        }                                      \
+        printf("| ");                              \
+        for (; j<=XE; j++) {                           \
+            printf("%i ", VECTOR(*PX)[j]);                       \
+        }                                      \
+        printf(") ");                              \
+        for (; j<igraph_vector_int_size(PX); j++) {                \
+            printf("%i ", VECTOR(*PX)[j]);                       \
+        }                                      \
+        printf("\n");                              \
+    } while (0)
+
+int igraph_i_maximal_cliques_reorder_adjlists(
+    const igraph_vector_int_t *PX,
+    int PS, int PE, int XS, int XE,
+    const igraph_vector_int_t *pos,
+    igraph_adjlist_t *adjlist) {
+    int j;
+    int sPS = PS + 1, sPE = PE + 1;
+
+    for (j = PS; j <= XE; j++) {
+        int av = VECTOR(*PX)[j];
+        igraph_vector_int_t *avneis = igraph_adjlist_get(adjlist, av);
+        int *avp = VECTOR(*avneis);
+        int avlen = igraph_vector_int_size(avneis);
+        int *ave = avp + avlen;
+        int *avnei = avp, *pp = avp;
+
+        for (; avnei < ave; avnei++) {
+            int avneipos = VECTOR(*pos)[(int)(*avnei)];
+            if (avneipos >= sPS && avneipos <= sPE) {
+                if (pp != avnei) {
+                    int tmp = *avnei;
+                    *avnei = *pp;
+                    *pp = tmp;
+                }
+                pp++;
+            }
+        }
+    }
+    return 0;
+}
+
+int igraph_i_maximal_cliques_select_pivot(const igraph_vector_int_t *PX,
+        int PS, int PE, int XS, int XE,
+        const igraph_vector_int_t *pos,
+        const igraph_adjlist_t *adjlist,
+        int *pivot,
+        igraph_vector_int_t *nextv,
+        int oldPS, int oldXE) {
+    igraph_vector_int_t *pivotvectneis;
+    int i, pivotvectlen, j, usize = -1;
+    int soldPS = oldPS + 1, soldXE = oldXE + 1, sPS = PS + 1, sPE = PE + 1;
+
+    /* Choose a pivotvect, and bring up P vertices at the same time */
+    for (i = PS; i <= XE; i++) {
+        int av = VECTOR(*PX)[i];
+        igraph_vector_int_t *avneis = igraph_adjlist_get(adjlist, av);
+        int *avp = VECTOR(*avneis);
+        int avlen = igraph_vector_int_size(avneis);
+        int *ave = avp + avlen;
+        int *avnei = avp, *pp = avp;
+
+        for (; avnei < ave; avnei++) {
+            int avneipos = VECTOR(*pos)[(int)(*avnei)];
+            if (avneipos < soldPS || avneipos > soldXE) {
+                break;
+            }
+            if (avneipos >= sPS && avneipos <= sPE) {
+                if (pp != avnei) {
+                    int tmp = *avnei;
+                    *avnei = *pp;
+                    *pp = tmp;
+                }
+                pp++;
+            }
+        }
+        if ((j = pp - avp) > usize) {
+            *pivot = av;
+            usize = j;
+        }
+    }
+
+    igraph_vector_int_push_back(nextv, -1);
+    pivotvectneis = igraph_adjlist_get(adjlist, *pivot);
+    pivotvectlen = igraph_vector_int_size(pivotvectneis);
+
+    for (j = PS; j <= PE; j++) {
+        int vcand = VECTOR(*PX)[j];
+        igraph_bool_t nei = 0;
+        int k = 0;
+        for (k = 0; k < pivotvectlen; k++) {
+            int unv = VECTOR(*pivotvectneis)[k];
+            int unvpos = VECTOR(*pos)[unv];
+            if (unvpos < sPS || unvpos > sPE) {
+                break;
+            }
+            if (unv == vcand) {
+                nei = 1;
+                break;
+            }
+        }
+        if (!nei) {
+            igraph_vector_int_push_back(nextv, vcand);
+        }
+    }
+
+    return 0;
+}
+
+#define SWAP(p1,p2) do {            \
+        int v1=VECTOR(*PX)[p1];         \
+        int v2=VECTOR(*PX)[p2];         \
+        VECTOR(*PX)[p1] = v2;           \
+        VECTOR(*PX)[p2] = v1;           \
+        VECTOR(*pos)[v1] = (p2)+1;          \
+        VECTOR(*pos)[v2] = (p1)+1;          \
+    } while (0)
+
+int igraph_i_maximal_cliques_down(igraph_vector_int_t *PX,
+                                  int PS, int PE, int XS, int XE,
+                                  igraph_vector_int_t *pos,
+                                  igraph_adjlist_t *adjlist, int mynextv,
+                                  igraph_vector_int_t *R,
+                                  int *newPS, int *newXE) {
+
+    igraph_vector_int_t *vneis = igraph_adjlist_get(adjlist, mynextv);
+    int j, vneislen = igraph_vector_int_size(vneis);
+    int sPS = PS + 1, sPE = PE + 1, sXS = XS + 1, sXE = XE + 1;
+
+    *newPS = PE + 1; *newXE = XS - 1;
+    for (j = 0; j < vneislen; j++) {
+        int vnei = VECTOR(*vneis)[j];
+        int vneipos = VECTOR(*pos)[vnei];
+        if (vneipos >= sPS && vneipos <= sPE) {
+            (*newPS)--;
+            SWAP(vneipos - 1, *newPS);
+        } else if (vneipos >= sXS && vneipos <= sXE) {
+            (*newXE)++;
+            SWAP(vneipos - 1, *newXE);
+        }
+    }
+
+    igraph_vector_int_push_back(R, mynextv);
+
+    return 0;
+}
+
+#undef SWAP
+
+int igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, int PS, int *PE,
+                                int *XS, int XE, igraph_vector_int_t *pos,
+                                igraph_adjlist_t *adjlist, int v,
+                                igraph_vector_int_t *H) {
+
+    int vpos = VECTOR(*pos)[v] - 1;
+    int tmp = VECTOR(*PX)[*PE];
+    VECTOR(*PX)[vpos] = tmp;
+    VECTOR(*PX)[*PE] = v;
+    VECTOR(*pos)[v] = (*PE) + 1;
+    VECTOR(*pos)[tmp] = vpos + 1;
+    (*PE)--; (*XS)--;
+    igraph_vector_int_push_back(H, v);
+
+    return 0;
+}
+
+int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE,
+                                int XS, int XE, igraph_vector_int_t *pos,
+                                igraph_adjlist_t *adjlist,
+                                igraph_vector_int_t *R,
+                                igraph_vector_int_t *H) {
+    int vv;
+    igraph_vector_int_pop_back(R);
+
+    while ((vv = igraph_vector_int_pop_back(H)) != -1) {
+        int vvpos = VECTOR(*pos)[vv];
+        int tmp = VECTOR(*PX)[XS];
+        VECTOR(*PX)[XS] = vv;
+        VECTOR(*PX)[vvpos - 1] = tmp;
+        VECTOR(*pos)[vv] = XS + 1;
+        VECTOR(*pos)[tmp] = vvpos;
+        PE++; XS++;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_maximal_cliques
+ * \brief Find all maximal cliques of a graph
+ *
+ * </para><para>
+ * A maximal clique is a clique which can't be extended any more by
+ * adding a new vertex to it.
+ *
+ * </para><para>
+ * If you are only interested in the size of the largest clique in the
+ * graph, use \ref igraph_clique_number() instead.
+ *
+ * </para><para>
+ * The current implementation uses a modified Bron-Kerbosch
+ * algorithm to find the maximal cliques, see: David Eppstein,
+ * Maarten Löffler, Darren Strash: Listing All Maximal Cliques in
+ * Sparse Graphs in Near-Optimal Time. Algorithms and Computation,
+ * Lecture Notes in Computer Science Volume 6506, 2010, pp 403-414.
+ *
+ * </para><para>The implementation of this function changed between
+ * igraph 0.5 and 0.6 and also between 0.6 and 0.7, so the order of
+ * the cliques and the order of vertices within the cliques will
+ * almost surely be different between these three versions.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a pointer vector, the result will be stored
+ *   here, ie. \c res will contain pointers to \c igraph_vector_t
+ *   objects which contain the indices of vertices involved in a clique.
+ *   The pointer vector will be resized if needed but note that the
+ *   objects in the pointer vector will not be freed. Note that vertices
+ *   of a clique may be returned in arbitrary order.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_maximal_independent_vertex_sets(), \ref
+ * igraph_clique_number()
+ *
+ * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy
+ * of the graph, this is typically small for sparse graphs.
+ *
+ * \example examples/simple/igraph_maximal_cliques.c
+ */
+
+int igraph_maximal_cliques(const igraph_t *graph,
+                           igraph_vector_ptr_t *res,
+                           igraph_integer_t min_size,
+                           igraph_integer_t max_size);
+
+#define IGRAPH_MC_ORIG
+#include "maximal_cliques_template.h"
+#undef IGRAPH_MC_ORIG
+
+/**
+ * \function igraph_maximal_cliques_count
+ * Count the number of maximal cliques in a graph
+ *
+ * </para><para>
+ * The current implementation uses a modified Bron-Kerbosch
+ * algorithm to find the maximal cliques, see: David Eppstein,
+ * Maarten Löffler, Darren Strash: Listing All Maximal Cliques in
+ * Sparse Graphs in Near-Optimal Time. Algorithms and Computation,
+ * Lecture Notes in Computer Science Volume 6506, 2010, pp 403-414.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to an \c igraph_integer_t; the number of maximal
+ *   cliques will be stored here.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_maximal_cliques().
+ *
+ * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy
+ * of the graph, this is typically small for sparse graphs.
+ *
+ * \example examples/simple/igraph_maximal_cliques.c
+ */
+
+int igraph_maximal_cliques_count(const igraph_t *graph,
+                                 igraph_integer_t *res,
+                                 igraph_integer_t min_size,
+                                 igraph_integer_t max_size);
+
+#define IGRAPH_MC_COUNT
+#include "maximal_cliques_template.h"
+#undef IGRAPH_MC_COUNT
+
+/**
+ * \function igraph_maximal_cliques_file
+ * Find maximal cliques and write them to a file
+ *
+ * TODO
+ */
+
+int igraph_maximal_cliques_file(const igraph_t *graph,
+                                FILE *outfile,
+                                igraph_integer_t min_size,
+                                igraph_integer_t max_size);
+
+#define IGRAPH_MC_FILE
+#include "maximal_cliques_template.h"
+#undef IGRAPH_MC_FILE
+
+/**
+ * \function igraph_maximal_cliques_subset
+ * Maximal cliques for a subset of initial vertices
+ *
+ * TODO
+ */
+
+int igraph_maximal_cliques_subset(const igraph_t *graph,
+                                  igraph_vector_int_t *subset,
+                                  igraph_vector_ptr_t *res,
+                                  igraph_integer_t *no,
+                                  FILE *outfile,
+                                  igraph_integer_t min_size,
+                                  igraph_integer_t max_size);
+
+#define IGRAPH_MC_FULL
+#include "maximal_cliques_template.h"
+#undef IGRAPH_MC_FULL
+
+
+/**
+ * \function igraph_maximal_cliques_callback
+ * \brief Finds maximal cliques in a graph and calls a function for each one
+ *
+ * This function enumerates all maximal cliques within the given size range
+ * and calls \p cliquehandler_fn for each of them. The cliques are passed to the
+ * callback function as an <type>igraph_vector_t *</type>.  Destroying and
+ * freeing this vector is left up to the user.  Use \ref igraph_vector_destroy()
+ * to destroy it first, then free it using \ref igraph_free().
+ *
+ * </para><para>
+ *
+ * Edge directions are ignored.
+ *
+ * </para><para>
+ *
+ * \param graph The input graph.
+ * \param cliquehandler_fn Callback function to be called for each clique.
+ * See also \ref igraph_clique_handler_t.
+ * \param arg Extra argument to supply to \p cliquehandler_fn.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_maximal_cliques().
+ *
+ * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy
+ * of the graph, this is typically small for sparse graphs.
+ *
+ */
+
+int igraph_maximal_cliques_callback(const igraph_t *graph,
+                                    igraph_clique_handler_t *cliquehandler_fn, void *arg,
+                                    igraph_integer_t min_size, igraph_integer_t max_size);
+
+#define IGRAPH_MC_CALLBACK
+#include "maximal_cliques_template.h"
+#undef IGRAPH_MC_CALLBACK
+
+
+/**
+ * \function igraph_maximal_cliques_hist
+ * \brief Count the number of maximal cliques of each size in a graph.
+ *
+ * This function counts how many maximal cliques of each size are present in
+ * the graph. Size-1 maximal cliques are simply isolated vertices.
+ *
+ * </para><para>
+ *
+ * Edge directions are ignored.
+ *
+ * </para><para>
+ *
+ * \param graph The input graph.
+ * \param hist Pointer to an initialized vector. The result will be stored
+ * here. The first element will store the number of size-1 maximal cliques,
+ * the second element the number of size-2 maximal cliques, etc.
+ * For cliques smaller than \c min_size, zero counts will be returned.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ *   returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ *   returned. If negative or zero, no upper bound will be used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_maximal_cliques().
+ *
+ * Time complexity: O(d(n-d)3^(d/3)) worst case, d is the degeneracy
+ * of the graph, this is typically small for sparse graphs.
+ *
+ */
+
+int igraph_maximal_cliques_hist(const igraph_t *graph,
+                                igraph_vector_t *hist,
+                                igraph_integer_t min_size,
+                                igraph_integer_t max_size);
+
+#define IGRAPH_MC_HIST
+#include "maximal_cliques_template.h"
+#undef IGRAPH_MC_HIST
diff --git a/igraph/src/memory.c b/igraph/src/memory.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/memory.c
@@ -0,0 +1,99 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_memory.h"
+#include "config.h"
+
+/**
+ * \function igraph_free
+ * Deallocate memory that was allocated by igraph functions
+ *
+ * Some igraph functions return a pointer vector (igraph_vector_ptr_t)
+ * containing pointers to other igraph or other data types. These data
+ * types are dynamically allocated and have to be deallocated
+ * manually, if the user does not need them any more. This can be done
+ * by calling igraph_free on them.
+ *
+ * </para><para>
+ * Here is a complete example on how to use \c igraph_free properly.
+ * <programlisting>
+ * <![CDATA[#include <igraph.h>
+ *
+ * int main(void)
+ * {
+ *    igraph_t graph;
+ *    igraph_vector_ptr_t seps;
+ *    long int i;
+ *
+ *    igraph_famous(&graph, "tutte");
+ *    igraph_vector_ptr_init(&seps, 0);
+ *    igraph_minimum_size_separators(&graph, &seps);
+ *
+ *    for (i=0; i<igraph_vector_ptr_size(&seps); i++) {
+ *      igraph_vector_t *v=VECTOR(seps)[i];
+ *      igraph_vector_print(v);
+ *      igraph_vector_destroy(v);
+ *      igraph_free(v);
+ *    }
+ *
+ *    igraph_vector_ptr_destroy(&seps);
+ *    igraph_destroy(&graph);
+ *    return 0;
+ * }]]>
+ * </programlisting>
+ *
+ *
+ * \param p Pointer to the piece of memory to be deallocated.
+ * \return Error code, currently always zero, meaning success.
+ *
+ * Time complexity: platform dependent, ideally it should be O(1).
+ *
+ * \sa \ref igraph_malloc()
+ */
+
+int igraph_free(void *p) {
+    igraph_Free(p);
+    return 0;
+}
+
+
+/**
+ * \function igraph_malloc
+ * Allocate memory that can be safely deallocated by igraph functions
+ *
+ * Some igraph functions, such as \ref igraph_vector_ptr_free_all() and
+ * \ref igraph_vector_ptr_destroy_all() can free memory that may have been
+ * allocated by the user.  \c igraph_malloc() works exactly like \c malloc()
+ * from the C standard library, but it is guaranteed that it can be safely
+ * paired with the \c free() function used by igraph internally (which is
+ * also user-accessible through \ref igraph_free()).
+ *
+ * \param n Number of bytes to be allocated.
+ * \return Pointer to the piece of allocated memory.
+ *
+ * \sa \ref igraph_free()
+ */
+
+void *igraph_malloc(size_t n) {
+    return malloc(n);
+}
diff --git a/igraph/src/microscopic_update.c b/igraph/src/microscopic_update.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/microscopic_update.c
@@ -0,0 +1,1209 @@
+/* -*- mode: C -*-  */
+/*
+  Microscopic update rules for dealing with agent-level strategy revision.
+  Copyright (C) 2011 Minh Van Nguyen <nguyenminh2@gmail.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301 USA
+*/
+
+#include "igraph_iterators.h"
+#include "igraph_interface.h"
+#include "igraph_microscopic_update.h"
+#include "igraph_nongraph.h"
+#include "igraph_random.h"
+
+#include <assert.h>
+
+/*
+ * Internal use only.
+ * Compute the cumulative proportionate values of a vector. The vector is
+ * assumed to hold values associated with edges.
+ *
+ * \param graph The graph object representing the game network. No error
+ *        checks will be performed on this graph. You are responsible for
+ *        ensuring that this is a valid graph for the particular
+ *        microscopic update rule at hand.
+ * \param U A vector of edge values for which we want to compute cumulative
+ *        proportionate values. So U[i] is the value of the edge with ID i.
+ *        With a local perspective, we would only compute cumulative
+ *        proportionate values for some combination of U. This vector could
+ *        be, for example, a vector of weights for edges in \p graph. It is
+ *        assumed that each value of U is nonnegative; it is your
+ *        responsibility to ensure this. Furthermore, this vector must have a
+ *        length the same as the number of edges in \p graph; you are
+ *        responsible for ensuring this condition holds.
+ * \param V Pointer to an uninitialized vector. The cumulative proportionate
+ *        values will be computed and stored here. No error checks will be
+ *        performed on this parameter.
+ * \param islocal Boolean; this flag controls which perspective to use. If
+ *        true then we use the local perspective; otherwise we use the global
+ *        perspective. In the context of this function, the local perspective
+ *        for a vertex v consists of all edges incident on v. In contrast, the
+ *        global perspective for v consists of all edges in \p graph.
+ * \param vid The vertex to use if we are considering a local perspective,
+ *        i.e. if \p islocal is true. This vertex will be ignored if
+ *        \p islocal is false. That is, if \p islocal is false then it is safe
+ *        pass the value -1 here. On the other hand, if \p islocal is true then
+ *        it is assumed that this is indeed a vertex of \p graph.
+ * \param mode Defines the sort of neighbourhood to consider for \p vid. This
+ *        is only relevant if we are considering the local perspective, i.e. if
+ *        \p islocal is true. If we are considering the global perspective,
+ *        then this parameter would be ignored. In other words, if \p islocal
+ *        is false then it is safe to pass the value \p IGRAPH_ALL here. If
+ *        \p graph is undirected, then we use all the immediate neighbours of
+ *        \p vid. Thus if you know that \p graph is undirected, then it is
+ *        safe to pass the value \p IGRAPH_ALL here. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          Use the out-neighbours of \p vid. This option is only relevant
+ *          when \p graph is a digraph and we are considering the local
+ *          perspective.
+ *        \cli IGRAPH_IN
+ *          Use the in-neighbours of \p vid. Again this option is only relevant
+ *          when \p graph is a directed graph and we are considering the local
+ *          perspective.
+ *        \cli IGRAPH_ALL
+ *          Use both the in- and out-neighbours of \p vid. This option is only
+ *          relevant if \p graph is a digraph and we are considering a local
+ *          perspective. Also use this value if \p graph is undirected or we
+ *          are considering the global perspective.
+ *        \endclist
+ * \return Codes:
+ *         \clist
+ *         \cli IGRAPH_EINVAL
+ *           This error code is returned in the following case: The vector
+ *           \p U, or some combination of its values, sums to zero.
+ *         \cli IGRAPH_SUCCESS
+ *           This signal is returned if the cumulative proportionate values
+ *           were successfully computed.
+ *         \endclist
+ *
+ * Time complexity: O(2n) where n is the number of edges in the perspective
+ * of \p vid.
+ */
+
+int igraph_ecumulative_proportionate_values(const igraph_t *graph,
+        const igraph_vector_t *U,
+        igraph_vector_t *V,
+        igraph_bool_t islocal,
+        igraph_integer_t vid,
+        igraph_neimode_t mode) {
+    igraph_eit_t A;   /* all edges in v's perspective */
+    igraph_es_t es;
+    igraph_integer_t e;
+    igraph_real_t C;  /* cumulative probability */
+    igraph_real_t P;  /* probability */
+    igraph_real_t S;  /* sum of values */
+    long int i;
+
+    /* Set the perspective. Let v be the vertex under consideration. The local */
+    /* perspective for v consists of edges incident on it. In contrast, the */
+    /* global perspective for v are all edges in the given graph. Hence in the */
+    /* global perspective, we will ignore the given vertex and the given */
+    /* neighbourhood type, but instead consider all edges in the given graph. */
+    if (islocal) {
+        IGRAPH_CHECK(igraph_es_incident(&es, vid, mode));
+    } else {
+        IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID));
+    }
+    IGRAPH_FINALLY(igraph_es_destroy, &es);
+
+    /* Sum up all the values of vector U in the perspective for v. This sum */
+    /* will be used in normalizing each value. */
+    /* NOTE: Here we assume that each value to be summed is nonnegative, */
+    /* and at least one of the values is nonzero. The behaviour resulting */
+    /* from all values being zero would be division by zero later on when */
+    /* we normalize each value. We check to see that the values sum to zero. */
+    /* NOTE: In this function, the order in which we iterate through the */
+    /* edges of interest should be the same as the order in which we do so */
+    /* in the caller function. If the caller function doesn't care about the */
+    /* order of values in the resulting vector V, then there's no need to take */
+    /* special notice of that order. But in some cases the order of values in */
+    /* V is taken into account, for example, in the Moran process. */
+    S = 0.0;
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &A));
+    IGRAPH_FINALLY(igraph_eit_destroy, &A);
+    while (!IGRAPH_EIT_END(A)) {
+        e = (igraph_integer_t)IGRAPH_EIT_GET(A);
+        S += (igraph_real_t)VECTOR(*U)[e];
+        IGRAPH_EIT_NEXT(A);
+    }
+    /* avoid division by zero later on */
+    if (S == (igraph_real_t)0.0) {
+        igraph_eit_destroy(&A);
+        igraph_es_destroy(&es);
+        IGRAPH_FINALLY_CLEAN(2);
+        IGRAPH_ERROR("Vector of values sums to zero", IGRAPH_EINVAL);
+    }
+
+    /* Get cumulative probability and relative value for each edge in the */
+    /* perspective of v. The vector V holds the cumulative proportionate */
+    /* values of all edges in v's perspective. The value V[0] is the */
+    /* cumulative proportionate value of the first edge in the edge iterator */
+    /* A. The value V[1] is the cumulative proportionate value of the second */
+    /* edge in the iterator A. And so on. */
+    C = 0.0;
+    i = 0;
+    IGRAPH_EIT_RESET(A);
+    IGRAPH_VECTOR_INIT_FINALLY(V, IGRAPH_EIT_SIZE(A));
+    while (!IGRAPH_EIT_END(A)) {
+        e = (igraph_integer_t)IGRAPH_EIT_GET(A);
+        /* NOTE: Beware of division by zero here. This can happen if the vector */
+        /* of values, or the combination of interest, sums to zero. */
+        P = (igraph_real_t)VECTOR(*U)[e] / S;
+        C += P;
+        VECTOR(*V)[i] = C;
+        i++;
+        IGRAPH_EIT_NEXT(A);
+    }
+
+    igraph_eit_destroy(&A);
+    igraph_es_destroy(&es);
+
+    /* Pop V, A and es from the finally stack -- that's three items */
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+/*
+ * Internal use only.
+ * Compute the cumulative proportionate values of a vector. The vector is
+ * assumed to hold values associated with vertices.
+ *
+ * \param graph The graph object representing the game network. No error
+ *        checks will be performed on this graph. You are responsible for
+ *        ensuring that this is a valid graph for the particular
+ *        microscopic update rule at hand.
+ * \param U A vector of vertex values for which we want to compute cumulative
+ *        proportionate values. The vector could be, for example, a vector of
+ *        fitness for vertices of \p graph. It is assumed that each value of U
+ *        is nonnegative; it is your responsibility to ensure this. Also U, or
+ *        a combination of interest, is assumed to sum to a positive value;
+ *        this condition will be checked.
+ * \param V Pointer to an uninitialized vector. The cumulative proportionate
+ *        values will be computed and stored here. No error checks will be
+ *        performed on this parameter.
+ * \param islocal Boolean; this flag controls which perspective to use. If
+ *        true then we use the local perspective; otherwise we use the global
+ *        perspective. The local perspective for a vertex v is the set of all
+ *        immediate neighbours of v. In contrast, the global perspective
+ *        for v is the vertex set of \p graph.
+ * \param vid The vertex to use if we are considering a local perspective,
+ *        i.e. if \p islocal is true. This vertex will be ignored if
+ *        \p islocal is false. That is, if \p islocal is false then it is safe
+ *        pass the value -1 here. On the other hand, if \p islocal is true then
+ *        it is assumed that this is indeed a vertex of \p graph.
+ * \param mode Defines the sort of neighbourhood to consider for \p vid. This
+ *        is only relevant if we are considering the local perspective, i.e. if
+ *        \p islocal is true. If we are considering the global perspective,
+ *        then this parameter would be ignored. In other words, if \p islocal
+ *        is false then it is safe to pass the value \p IGRAPH_ALL here. If
+ *        \p graph is undirected, then we use all the immediate neighbours of
+ *        \p vid. Thus if you know that \p graph is undirected, then it is
+ *        safe to pass the value \p IGRAPH_ALL here. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          Use the out-neighbours of \p vid. This option is only relevant
+ *          when \p graph is a digraph and we are considering the local
+ *          perspective.
+ *        \cli IGRAPH_IN
+ *          Use the in-neighbours of \p vid. Again this option is only relevant
+ *          when \p graph is a directed graph and we are considering the local
+ *          perspective.
+ *        \cli IGRAPH_ALL
+ *          Use both the in- and out-neighbours of \p vid. This option is only
+ *          relevant if \p graph is a digraph and we are considering a local
+ *          perspective. Also use this value if \p graph is undirected or we
+ *          are considering the global perspective.
+ *        \endclist
+ * \return Codes:
+ *         \clist
+ *         \cli IGRAPH_EINVAL
+ *           This error code is returned in the following case: The vector
+ *           \p U, or some combination of its values, sums to zero.
+ *         \cli IGRAPH_SUCCESS
+ *           This signal is returned if the cumulative proportionate values
+ *           were successfully computed.
+ *         \endclist
+ *
+ * Time complexity: O(2n) where n is the number of vertices in the
+ * perspective of vid.
+ */
+
+int igraph_vcumulative_proportionate_values(const igraph_t *graph,
+        const igraph_vector_t *U,
+        igraph_vector_t *V,
+        igraph_bool_t islocal,
+        igraph_integer_t vid,
+        igraph_neimode_t mode) {
+    igraph_integer_t v;
+    igraph_real_t C;  /* cumulative probability */
+    igraph_real_t P;  /* probability */
+    igraph_real_t S;  /* sum of values */
+    igraph_vit_t A;   /* all vertices in v's perspective */
+    igraph_vs_t vs;
+    long int i;
+
+    /* Set the perspective. Let v be the vertex under consideration; it might */
+    /* be that we want to update v's strategy. The local perspective for v */
+    /* consists of its immediate neighbours. In contrast, the global */
+    /* perspective for v are all the vertices in the given graph. Hence in the */
+    /* global perspective, we will ignore the given vertex and the given */
+    /* neighbourhood type, but instead consider all vertices in the given */
+    /* graph. */
+    if (islocal) {
+        IGRAPH_CHECK(igraph_vs_adj(&vs, vid, mode));
+    } else {
+        IGRAPH_CHECK(igraph_vs_all(&vs));
+    }
+    IGRAPH_FINALLY(igraph_vs_destroy, &vs);
+
+    /* Sum up all the values of vector U in the perspective for v. This */
+    /* sum will be used in normalizing each value. If we are using a local */
+    /* perspective, then we also need to consider the quantity of v in */
+    /* computing the sum. */
+    /* NOTE: Here we assume that each value to be summed is nonnegative, */
+    /* and at least one of the values is nonzero. The behaviour resulting */
+    /* from all values being zero would be division by zero later on when */
+    /* we normalize each value. We check to see that the values sum to zero. */
+    /* NOTE: In this function, the order in which we iterate through the */
+    /* vertices of interest should be the same as the order in which we do so */
+    /* in the caller function. If the caller function doesn't care about the */
+    /* order of values in the resulting vector V, then there's no need to take */
+    /* special notice of that order. But in some cases the order of values in */
+    /* V is taken into account, for example, in roulette wheel selection. */
+    S = 0.0;
+    IGRAPH_CHECK(igraph_vit_create(graph, vs, &A));
+    IGRAPH_FINALLY(igraph_vit_destroy, &A);
+    while (!IGRAPH_VIT_END(A)) {
+        v = (igraph_integer_t)IGRAPH_VIT_GET(A);
+        S += (igraph_real_t)VECTOR(*U)[v];
+        IGRAPH_VIT_NEXT(A);
+    }
+    if (islocal) {
+        S += (igraph_real_t)VECTOR(*U)[vid];
+    }
+    /* avoid division by zero later on */
+    if (S == (igraph_real_t)0.0) {
+        igraph_vit_destroy(&A);
+        igraph_vs_destroy(&vs);
+        IGRAPH_FINALLY_CLEAN(2);
+        IGRAPH_ERROR("Vector of values sums to zero", IGRAPH_EINVAL);
+    }
+
+    /* Get cumulative probability and relative value for each vertex in the */
+    /* perspective of v. The vector V holds the cumulative proportionate */
+    /* values of all vertices in v's perspective. The value V[0] is the */
+    /* cumulative proportionate value of the first vertex in the vertex */
+    /* iterator A. The value V[1] is the cumulative proportionate value of */
+    /* the second vertex in the iterator A. And so on. If we are using the */
+    /* local perspective, then we also need to consider the cumulative */
+    /* proportionate value of v. In the case of the local perspective, we */
+    /* don't need to compute and store v's cumulative proportionate value, */
+    /* but we pretend that such value is appended to the vector V. */
+    C = 0.0;
+    i = 0;
+    IGRAPH_VIT_RESET(A);
+    IGRAPH_VECTOR_INIT_FINALLY(V, IGRAPH_VIT_SIZE(A));
+    while (!IGRAPH_VIT_END(A)) {
+        v = (igraph_integer_t)IGRAPH_VIT_GET(A);
+        /* NOTE: Beware of division by zero here. This can happen if the vector */
+        /* of values, or a combination of interest, sums to zero. */
+        P = (igraph_real_t)VECTOR(*U)[v] / S;
+        C += P;
+        VECTOR(*V)[i] = C;
+        i++;
+        IGRAPH_VIT_NEXT(A);
+    }
+
+    igraph_vit_destroy(&A);
+    igraph_vs_destroy(&vs);
+
+    /* Pop V, A and vs from the finally stack -- that's three items */
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+/*
+ * Internal use only.
+ * A set of standard tests to be performed prior to strategy updates. The
+ * tests contained in this function are common to many strategy revision
+ * functions in this file. This function is meant to be invoked from within
+ * a specific strategy update function in order to perform certain common
+ * tests, including sanity checks and conditions under which no strategy
+ * updates are necessary.
+ *
+ * \param graph The graph object representing the game network. This cannot
+ *        be the empty or trivial graph, but must have at least two vertices
+ *        and one edge. If \p graph has one vertex, then no strategy update
+ *        would take place. Furthermore, if \p graph has at least two vertices
+ *        but zero edges, then strategy update would also not take place.
+ * \param vid The vertex whose strategy is to be updated. It is assumed that
+ *        \p vid represents a vertex in \p graph. No checking is performed and
+ *        it is your responsibility to ensure that \p vid is indeed a vertex
+ *        of \p graph. If an isolated vertex is provided, i.e. the input
+ *        vertex has degree 0, then no strategy update would take place and
+ *        \p vid would retain its current strategy. Strategy update would also
+ *        not take place if the local neighbourhood of \p vid are its
+ *        in-neighbours (respectively out-neighbours), but \p vid has zero
+ *        in-neighbours (respectively out-neighbours). Loops are ignored in
+ *        computing the degree (in, out, all) of \p vid.
+ * \param quantities A vector of quantities providing the quantity of each
+ *        vertex in \p graph. Think of each entry of the vector as being
+ *        generated by a function such as the fitness function for the game.
+ *        So if the vector represents fitness quantities, then each vector
+ *        entry is the fitness of some vertex. The length of this vector must
+ *        be the same as the number of vertices in the vertex set of \p graph.
+ * \param strategies A vector of the current strategies for the vertex
+ *        population. Each strategy is identified with a nonnegative integer,
+ *        whose interpretation depends on the payoff matrix of the game.
+ *        Generally we use the strategy ID as a row or column index of the
+ *        payoff matrix. The length of this vector must be the same as the
+ *        number of vertices in the vertex set of \p graph.
+ * \param mode Defines the sort of neighbourhood to consider for \p vid. If
+ *        \p graph is undirected, then we use all the immediate neighbours of
+ *        \p vid. Thus if you know that \p graph is undirected, then it is safe
+ *        to pass the value \p IGRAPH_ALL here. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          Use the out-neighbours of \p vid. This option is only relevant
+ *          when \p graph is a directed graph.
+ *        \cli IGRAPH_IN
+ *          Use the in-neighbours of \p vid. Again this option is only relevant
+ *          when \p graph is a directed graph.
+ *        \cli IGRAPH_ALL
+ *          Use both the in- and out-neighbours of \p vid. This option is only
+ *          relevant if \p graph is a digraph. Also use this value if
+ *          \p graph is undirected.
+ *        \endclist
+ * \param updates Boolean; at the end of this test suite, this flag
+ *        indicates whether to proceed with strategy revision. If true then
+ *        strategy revision should proceed; otherwise there is no need to
+ *        continue with revising a vertex's strategy. A caller function that
+ *        invokes this function would use the value of \p updates to
+ *        determine whether to proceed with strategy revision.
+ * \param islocal Boolean; this flag controls which perspective to use. If
+ *        true then we use the local perspective; otherwise we use the global
+ *        perspective. The local perspective for \p vid is the set of all
+ *        immediate neighbours of \p vid. In contrast, the global perspective
+ *        for \p vid is the vertex set of \p graph.
+ * \return Codes:
+ *         \clist
+ *         \cli IGRAPH_EINVAL
+ *           This error code is returned in each of the following cases:
+ *           (1) Any of the parameters \p graph, \p quantities, or
+ *           \p strategies is a null pointer. (2) The vector \p quantities
+ *           or \p strategies has a length different from the number of
+ *           vertices in \p graph. (3) The parameter \p graph is the empty
+ *           or null graph, i.e. the graph with zero vertices and edges.
+ *         \cli IGRAPH_SUCCESS
+ *           This signal is returned if no errors were raised. You should use
+ *           the value of the boolean \p updates to decide whether to go
+ *           ahead with updating a vertex's strategy.
+ *         \endclist
+ */
+
+int igraph_microscopic_standard_tests(const igraph_t *graph,
+                                      igraph_integer_t vid,
+                                      const igraph_vector_t *quantities,
+                                      const igraph_vector_t *strategies,
+                                      igraph_neimode_t mode,
+                                      igraph_bool_t *updates,
+                                      igraph_bool_t islocal) {
+
+    igraph_integer_t nvert;
+    igraph_vector_t degv;
+    *updates = 1;
+
+    /* sanity checks */
+    if (graph == NULL) {
+        IGRAPH_ERROR("Graph is a null pointer", IGRAPH_EINVAL);
+    }
+    if (quantities == NULL) {
+        IGRAPH_ERROR("Quantities vector is a null pointer", IGRAPH_EINVAL);
+    }
+    if (strategies == NULL) {
+        IGRAPH_ERROR("Strategies vector is a null pointer", IGRAPH_EINVAL);
+    }
+
+    /* the empty graph */
+    nvert = igraph_vcount(graph);
+    if (nvert < 1) {
+        IGRAPH_ERROR("Graph cannot be the empty graph", IGRAPH_EINVAL);
+    }
+    /* invalid vector length */
+    if (nvert != (igraph_integer_t)igraph_vector_size(quantities)) {
+        IGRAPH_ERROR("Size of quantities vector different from number of vertices",
+                     IGRAPH_EINVAL);
+    }
+    if (nvert != (igraph_integer_t)igraph_vector_size(strategies)) {
+        IGRAPH_ERROR("Size of strategies vector different from number of vertices",
+                     IGRAPH_EINVAL);
+    }
+
+    /* Various conditions under which no strategy updates will take place. That
+     * is, the vertex retains its current strategy.
+     */
+    /* given graph has < 2 vertices */
+    if (nvert < 2) {
+        *updates = 0;
+    }
+    /* graph has >= 2 vertices, but no edges */
+    if (igraph_ecount(graph) < 1) {
+        *updates = 0;
+    }
+
+    /* Test for vertex isolation, depending on the perspective given. For
+     * undirected graphs, a given vertex v is isolated if its degree is zero.
+     * If we are considering in-neighbours (respectively out-neighbours), then
+     * we say that v is isolated if its in-degree (respectively out-degree) is
+     * zero. In general, this vertex isolation test is only relevant if we are
+     * using a local perspective, i.e. if we only consider the immediate
+     * neighbours (local perspective) of v as opposed to all vertices in the
+     * vertex set of the graph (global perspective).
+     */
+    if (islocal) {
+        /* Moving on ahead with vertex isolation test, since local perspective */
+        /* is requested. */
+        IGRAPH_VECTOR_INIT_FINALLY(&degv, 1);
+        IGRAPH_CHECK(igraph_degree(graph, &degv, igraph_vss_1(vid),
+                                   mode, IGRAPH_NO_LOOPS));
+        if (VECTOR(degv)[0] < 1) {
+            *updates = 0;
+        }
+        igraph_vector_destroy(&degv);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup spatialgames
+ * \function igraph_deterministic_optimal_imitation
+ * \brief Adopt a strategy via deterministic optimal imitation.
+ *
+ * A simple deterministic imitation strategy where a vertex revises its
+ * strategy to that which yields a local optimal. Here "local" is with
+ * respect to the immediate neighbours of the vertex. The vertex retains its
+ * current strategy where this strategy yields a locally optimal quantity.
+ * The quantity in this case could be a measure such as fitness.
+ *
+ * \param graph The graph object representing the game network. This cannot
+ *        be the empty or trivial graph, but must have at least two vertices
+ *        and one edge. If \p graph has one vertex, then no strategy update
+ *        would take place. Furthermore, if \p graph has at least two vertices
+ *        but zero edges, then strategy update would also not take place.
+ * \param vid The vertex whose strategy is to be updated. It is assumed that
+ *        \p vid represents a vertex in \p graph. No checking is performed and
+ *        it is your responsibility to ensure that \p vid is indeed a vertex
+ *        of \p graph. If an isolated vertex is provided, i.e. the input
+ *        vertex has degree 0, then no strategy update would take place and
+ *        \p vid would retain its current strategy. Strategy update would also
+ *        not take place if the local neighbourhood of \p vid are its
+ *        in-neighbours (respectively out-neighbours), but \p vid has zero
+ *        in-neighbours (respectively out-neighbours). Loops are ignored in
+ *        computing the degree (in, out, all) of \p vid.
+ * \param optimality Logical; controls the type of optimality to be used.
+ *        Supported values are:
+ *        \clist
+ *        \cli IGRAPH_MAXIMUM
+ *          Use maximum deterministic imitation, where the strategy of the
+ *          vertex with maximum quantity (e.g. fitness) would be adopted. We
+ *          update the strategy of \p vid to that which yields a local
+ *          maximum.
+ *        \cli IGRAPH_MINIMUM
+ *          Use minimum deterministic imitation. That is, the strategy of the
+ *          vertex with minimum quantity would be imitated. In other words,
+ *          update to the strategy that yields a local minimum.
+ *        \endclist
+ * \param quantities A vector of quantities providing the quantity of each
+ *        vertex in \p graph. Think of each entry of the vector as being
+ *        generated by a function such as the fitness function for the game.
+ *        So if the vector represents fitness quantities, then each vector
+ *        entry is the fitness of some vertex. The length of this vector must
+ *        be the same as the number of vertices in the vertex set of \p graph.
+ * \param strategies A vector of the current strategies for the vertex
+ *        population. The updated strategy for \p vid would be stored here.
+ *        Each strategy is identified with a nonnegative integer, whose
+ *        interpretation depends on the payoff matrix of the game. Generally
+ *        we use the strategy ID as a row or column index of the payoff
+ *        matrix. The length of this vector must be the same as the number of
+ *        vertices in the vertex set of \p graph.
+ * \param mode Defines the sort of neighbourhood to consider for \p vid. If
+ *        \p graph is undirected, then we use all the immediate neighbours of
+ *        \p vid. Thus if you know that \p graph is undirected, then it is safe
+ *        to pass the value \p IGRAPH_ALL here. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          Use the out-neighbours of \p vid. This option is only relevant
+ *          when \p graph is a directed graph.
+ *        \cli IGRAPH_IN
+ *          Use the in-neighbours of \p vid. Again this option is only relevant
+ *          when \p graph is a directed graph.
+ *        \cli IGRAPH_ALL
+ *          Use both the in- and out-neighbours of \p vid. This option is only
+ *          relevant if \p graph is a digraph. Also use this value if
+ *          \p graph is undirected.
+ *        \endclist
+ * \return The error code \p IGRAPH_EINVAL is returned in each of the
+ *         following cases: (1) Any of the parameters \p graph, \p quantities,
+ *         or \p strategies is a null pointer. (2) The vector \p quantities
+ *         or \p strategies has a length different from the number of vertices
+ *         in \p graph. (3) The parameter \p graph is the empty or null graph,
+ *         i.e. the graph with zero vertices and edges.
+ *
+ * Time complexity: O(2d), where d is the degree of the vertex \p vid.
+ *
+ * \example examples/simple/igraph_deterministic_optimal_imitation.c
+ */
+
+int igraph_deterministic_optimal_imitation(const igraph_t *graph,
+        igraph_integer_t vid,
+        igraph_optimal_t optimality,
+        const igraph_vector_t *quantities,
+        igraph_vector_t *strategies,
+        igraph_neimode_t mode) {
+    igraph_integer_t i, k, v;
+    igraph_real_t q;
+    igraph_vector_t adj;
+    igraph_bool_t updates;
+
+    IGRAPH_CHECK(igraph_microscopic_standard_tests(graph, vid, quantities,
+                 strategies, mode, &updates,
+                 /*is local?*/ 1));
+    if (!updates) {
+        return IGRAPH_SUCCESS;    /* Nothing to do */
+    }
+
+    /* Choose a locally optimal strategy to imitate. This can be either maximum
+     * or minimum deterministic imitation. By now we know that the given vertex v
+     * has degree >= 1 and at least 1 edge. Then within its immediate
+     * neighbourhood adj(v) and including v itself, there exists a vertex whose
+     * strategy yields a local optimal quantity.
+     */
+    /* Random permutation of adj(v). This ensures that if there are multiple */
+    /* candidates with an optimal strategy, then we choose one such candidate */
+    /* at random. */
+    IGRAPH_VECTOR_INIT_FINALLY(&adj, 0);
+    IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode));
+    IGRAPH_CHECK(igraph_vector_shuffle(&adj));
+    /* maximum deterministic imitation */
+    i = vid;
+    q = (igraph_real_t)VECTOR(*quantities)[vid];
+    if (optimality == IGRAPH_MAXIMUM) {
+        for (k = 0; k < igraph_vector_size(&adj); k++) {
+            v = (igraph_integer_t) VECTOR(adj)[k];
+            if ((igraph_real_t)VECTOR(*quantities)[v] > q) {
+                i = v;
+                q = (igraph_real_t)VECTOR(*quantities)[v];
+            }
+        }
+    } else { /* minimum deterministic imitation */
+        for (k = 0; k < igraph_vector_size(&adj); k++) {
+            v = (igraph_integer_t) VECTOR(adj)[k];
+            if ((igraph_real_t)VECTOR(*quantities)[v] < q) {
+                i = v;
+                q = (igraph_real_t)VECTOR(*quantities)[v];
+            }
+        }
+    }
+    /* Now i is a vertex with a locally optimal quantity, the value of which */
+    /* is q. Update the strategy of vid to that of i. */
+    VECTOR(*strategies)[vid] = VECTOR(*strategies)[i];
+    igraph_vector_destroy(&adj);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup spatialgames
+ * \function igraph_moran_process
+ * \brief The Moran process in a network setting.
+ *
+ * This is an extension of the classic Moran process to a network setting.
+ * The Moran process is a model of haploid (asexual) reproduction within a
+ * population having a fixed size. In the network setting, the Moran process
+ * operates on a weighted graph. At each time step a vertex a is chosen for
+ * reproduction and another vertex b is chosen for death. Vertex a gives birth
+ * to an identical clone c, which replaces b. Vertex c is a clone of a in that
+ * c inherits both the current quantity (e.g. fitness) and current strategy
+ * of a.
+ *
+ * </para><para>
+ * The graph G representing the game network is assumed to be simple,
+ * i.e. free of loops and without multiple edges. If, on the other hand, G has
+ * a loop incident on some vertex v, then it is possible that when v is chosen
+ * for reproduction it would forgo this opportunity. In particular, when v is
+ * chosen for reproduction and v is also chosen for death, the clone of v
+ * would be v itself with its current vertex ID. In effect v forgoes its
+ * chance for reproduction.
+ *
+ * \param graph The graph object representing the game network. This cannot
+ *        be the empty or trivial graph, but must have at least two vertices
+ *        and one edge. The Moran process will not take place in each of the
+ *        following cases: (1) If \p graph has one vertex. (2) If \p graph has
+ *        at least two vertices but zero edges.
+ * \param weights A vector of all edge weights for \p graph. Thus weights[i]
+ *        means the weight of the edge with edge ID i. For the purpose of the
+ *        Moran process, each weight is assumed to be positive; it is your
+ *        responsibility to ensure this condition holds. The length of this
+ *        vector must be the same as the number of edges in \p graph.
+ * \param quantities A vector of quantities providing the quantity of each
+ *        vertex in \p graph. The quantity of the new clone will be stored
+ *        here. Think of each entry of the vector as being generated by a
+ *        function such as the fitness function for the game. So if the vector
+ *        represents fitness quantities, then each vector entry is the fitness
+ *        of some vertex. The length of this vector must be the same as the
+ *        number of vertices in the vertex set of \p graph. For the purpose of
+ *        the Moran process, each vector entry is assumed to be nonnegative;
+ *        no checks will be performed for this. It is your responsibility to
+ *        ensure that at least one entry is positive. Furthermore, this vector
+ *        cannot be a vector of zeros; this condition will be checked.
+ * \param strategies A vector of the current strategies for the vertex
+ *        population. The strategy of the new clone will be stored here. Each
+ *        strategy is identified with a nonnegative integer, whose
+ *        interpretation depends on the payoff matrix of the game. Generally
+ *        we use the strategy ID as a row or column index of the payoff
+ *        matrix. The length of this vector must be the same as the number of
+ *        vertices in the vertex set of \p graph.
+ * \param mode Defines the sort of neighbourhood to consider for the vertex a
+ *        chosen for reproduction. This is only relevant if \p graph is
+ *        directed. If \p graph is undirected, then it is safe to pass the
+ *        value \p IGRAPH_ALL here. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          Use the out-neighbours of a. This option is only relevant when
+ *          \p graph is directed.
+ *        \cli IGRAPH_IN
+ *          Use the in-neighbours of a. Again this option is only relevant
+ *          when \p graph is directed.
+ *        \cli IGRAPH_ALL
+ *          Use both the in- and out-neighbours of a. This option is only
+ *          relevant if \p graph is directed. Also use this value if
+ *          \p graph is undirected.
+ *        \endclist
+ * \return The error code \p IGRAPH_EINVAL is returned in each of the following
+ *         cases: (1) Any of the parameters \p graph, \p weights,
+ *         \p quantities or \p strategies is a null pointer. (2) The vector
+ *         \p quantities or \p strategies has a length different from the
+ *         number of vertices in \p graph. (3) The vector \p weights has a
+ *         length different from the number of edges in \p graph. (4) The
+ *         parameter \p graph is the empty or null graph, i.e. the graph with
+ *         zero vertices and edges. (5) The vector \p weights, or the
+ *         combination of interest, sums to zero. (6) The vector \p quantities,
+ *         or the combination of interest, sums to zero.
+ *
+ * Time complexity: depends on the random number generator, but is usually
+ * O(n) where n is the number of vertices in \p graph.
+ *
+ * </para><para>
+ * References:
+ * \clist
+ * \cli (Lieberman et al. 2005)
+ *   E. Lieberman, C. Hauert, and M. A. Nowak. Evolutionary dynamics on
+ *   graphs. \emb Nature, \eme 433(7023):312--316, 2005.
+ * \cli (Moran 1958)
+ *   P. A. P. Moran. Random processes in genetics. \emb Mathematical
+ *   Proceedings of the Cambridge Philosophical Society, \eme 54(1):60--71,
+ *   1958.
+ * \endclist
+ *
+ * \example examples/simple/igraph_moran_process.c
+ */
+
+int igraph_moran_process(const igraph_t *graph,
+                         const igraph_vector_t *weights,
+                         igraph_vector_t *quantities,
+                         igraph_vector_t *strategies,
+                         igraph_neimode_t mode) {
+    igraph_bool_t updates;
+    igraph_integer_t a = -1;  /* vertex chosen for reproduction */
+    igraph_integer_t b = -1;  /* vertex chosen for death */
+    igraph_integer_t e, nedge, u, v;
+    igraph_real_t r;          /* random number */
+    igraph_vector_t deg;
+    igraph_vector_t V;        /* vector of cumulative proportionate values */
+    igraph_vit_t vA;          /* vertex list */
+    igraph_eit_t eA;          /* edge list */
+    igraph_vs_t vs;
+    igraph_es_t es;
+    long int i;
+
+    /* don't test for vertex isolation, hence vid = -1 and islocal = 0 */
+    IGRAPH_CHECK(igraph_microscopic_standard_tests(graph, /*vid*/ -1,
+                 quantities, strategies, mode,
+                 &updates, /*is local?*/ 0));
+    if (!updates) {
+        return IGRAPH_SUCCESS;    /* nothing more to do */
+    }
+    if (weights == NULL) {
+        IGRAPH_ERROR("Weights vector is a null pointer", IGRAPH_EINVAL);
+    }
+    nedge = igraph_ecount(graph);
+    if (nedge != (igraph_integer_t)igraph_vector_size(weights)) {
+        IGRAPH_ERROR("Size of weights vector different from number of edges",
+                     IGRAPH_EINVAL);
+    }
+
+    /* Cumulative proportionate quantities. We are using the global */
+    /* perspective, hence islocal = 0, vid = -1 and mode = IGRAPH_ALL. */
+    IGRAPH_CHECK(igraph_vcumulative_proportionate_values(graph, quantities, &V,
+                 /*is local?*/ 0,
+                 /*vid*/ -1,
+                 /*mode*/ IGRAPH_ALL));
+
+    /* Choose a vertex for reproduction from among all vertices in the graph. */
+    /* The vertex is chosen proportionate to its quantity and such that its */
+    /* degree is >= 1. In case we are considering in-neighbours (respectively */
+    /* out-neighbours), the chosen vertex must have in-degree (respectively */
+    /* out-degree) >= 1. All loops will be ignored. At this point, we know */
+    /* that the graph has at least one edge, which may be directed or not. */
+    /* Furthermore the quantities of all vertices sum to a positive value. */
+    /* Hence at least one vertex will be chosen for reproduction. */
+    IGRAPH_CHECK(igraph_vs_all(&vs));
+    IGRAPH_FINALLY(igraph_vs_destroy, &vs);
+    IGRAPH_CHECK(igraph_vit_create(graph, vs, &vA));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vA);
+    RNG_BEGIN();
+    r = RNG_UNIF01();
+    RNG_END();
+    i = 0;
+    IGRAPH_VECTOR_INIT_FINALLY(&deg, 1);
+    while (!IGRAPH_VIT_END(vA)) {
+        u = (igraph_integer_t)IGRAPH_VIT_GET(vA);
+        IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_1(u), mode,
+                                   IGRAPH_NO_LOOPS));
+        if (VECTOR(deg)[0] < 1) {
+            i++;
+            IGRAPH_VIT_NEXT(vA);
+            continue;
+        }
+        if (r <= VECTOR(V)[i]) {
+            /* we have found our candidate vertex for reproduction */
+            a = u;
+            break;
+        }
+        i++;
+        IGRAPH_VIT_NEXT(vA);
+    }
+    /* By now we should have chosen a vertex for reproduction. Check this. */
+    assert(a >= 0);
+
+    /* Cumulative proportionate weights. We are using the local perspective */
+    /* with respect to vertex a, which has been chosen for reproduction. */
+    /* The degree of a is deg(a) >= 1 with respect to the mode "mode", which */
+    /* can flag either the in-degree, out-degree or all degree of a. But it */
+    /* still might happen that the edge weights of interest would sum to zero. */
+    /* An error would be raised in that case. */
+    igraph_vector_destroy(&V);
+    IGRAPH_CHECK(igraph_ecumulative_proportionate_values(graph, weights, &V,
+                 /*is local?*/ 1,
+                 /*vertex*/ a, mode));
+
+    /* Choose a vertex for death from among all vertices in a's perspective. */
+    /* Let E be all the edges in the perspective of a. If (u,v) \in E is any */
+    /* such edge, then we have a = u or a = v. That is, any edge in E has a */
+    /* for one of its endpoints. As G is assumed to be a simple graph, then */
+    /* exactly one of u or v is the vertex a. Without loss of generality, we */
+    /* assume that each edge in E has the form (a, v_i). Then the vertex v_j */
+    /* chosen for death is chosen proportionate to the weight of the edge */
+    /* (a, v_j). */
+    IGRAPH_CHECK(igraph_es_incident(&es, a, mode));
+    IGRAPH_FINALLY(igraph_es_destroy, &es);
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eA));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eA);
+    RNG_BEGIN();
+    r = RNG_UNIF01();
+    RNG_END();
+    i = 0;
+    while (!IGRAPH_EIT_END(eA)) {
+        e = (igraph_integer_t)IGRAPH_EIT_GET(eA);
+        if (r <= VECTOR(V)[i]) {
+            /* We have found our candidate vertex for death; call this vertex b. */
+            /* As G is simple, then a =/= b. Check the latter condition. */
+            IGRAPH_CHECK(igraph_edge(graph, /*edge ID*/ e,
+                                     /*tail vertex*/ &u, /*head vertex*/ &v));
+            if (a == u) {
+                b = v;
+            } else {
+                b = u;
+            }
+            assert(a != b);  /* always true if G is simple */
+            break;
+        }
+        i++;
+        IGRAPH_EIT_NEXT(eA);
+    }
+
+    /* By now a vertex a is chosen for reproduction and a vertex b is chosen */
+    /* for death. Check that b has indeed been chosen. Clone vertex a and kill */
+    /* vertex b. Let the clone c have the vertex ID of b, and the strategy and */
+    /* quantity of a. */
+    assert(b >= 0);
+    VECTOR(*quantities)[b] = VECTOR(*quantities)[a];
+    VECTOR(*strategies)[b] = VECTOR(*strategies)[a];
+
+    igraph_vector_destroy(&deg);
+    igraph_vector_destroy(&V);
+    igraph_vit_destroy(&vA);
+    igraph_eit_destroy(&eA);
+    igraph_vs_destroy(&vs);
+    igraph_es_destroy(&es);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup spatialgames
+ * \function igraph_roulette_wheel_imitation
+ * \brief Adopt a strategy via roulette wheel selection.
+ *
+ * A simple stochastic imitation strategy where a vertex revises its
+ * strategy to that of a vertex u chosen proportionate to u's quantity
+ * (e.g. fitness). This is a special case of stochastic imitation, where a
+ * candidate is not chosen uniformly at random but proportionate to its
+ * quantity.
+ *
+ * \param graph The graph object representing the game network. This cannot
+ *        be the empty or trivial graph, but must have at least two vertices
+ *        and one edge. If \p graph has one vertex, then no strategy update
+ *        would take place. Furthermore, if \p graph has at least two vertices
+ *        but zero edges, then strategy update would also not take place.
+ * \param vid The vertex whose strategy is to be updated. It is assumed that
+ *        \p vid represents a vertex in \p graph. No checking is performed and
+ *        it is your responsibility to ensure that \p vid is indeed a vertex
+ *        of \p graph. If an isolated vertex is provided, i.e. the input
+ *        vertex has degree 0, then no strategy update would take place and
+ *        \p vid would retain its current strategy. Strategy update would also
+ *        not take place if the local neighbourhood of \p vid are its
+ *        in-neighbours (respectively out-neighbours), but \p vid has zero
+ *        in-neighbours (respectively out-neighbours). Loops are ignored in
+ *        computing the degree (in, out, all) of \p vid.
+ * \param islocal Boolean; this flag controls which perspective to use in
+ *        computing the relative quantity. If true then we use the local
+ *        perspective; otherwise we use the global perspective. The local
+ *        perspective for \p vid is the set of all immediate neighbours of
+ *        \p vid. In contrast, the global perspective for \p vid is the
+ *        vertex set of \p graph.
+ * \param quantities A vector of quantities providing the quantity of each
+ *        vertex in \p graph. Think of each entry of the vector as being
+ *        generated by a function such as the fitness function for the game.
+ *        So if the vector represents fitness quantities, then each vector
+ *        entry is the fitness of some vertex. The length of this vector must
+ *        be the same as the number of vertices in the vertex set of \p graph.
+ *        For the purpose of roulette wheel selection, each vector entry is
+ *        assumed to be nonnegative; no checks will be performed for this. It
+ *        is your responsibility to ensure that at least one entry is nonzero.
+ *        Furthermore, this vector cannot be a vector of zeros; this condition
+ *        will be checked.
+ * \param strategies A vector of the current strategies for the vertex
+ *        population. The updated strategy for \p vid would be stored here.
+ *        Each strategy is identified with a nonnegative integer, whose
+ *        interpretation depends on the payoff matrix of the game. Generally
+ *        we use the strategy ID as a row or column index of the payoff
+ *        matrix. The length of this vector must be the same as the number of
+ *        vertices in the vertex set of \p graph.
+ * \param mode Defines the sort of neighbourhood to consider for \p vid. This
+ *        is only relevant if we are considering the local perspective, i.e. if
+ *        \p islocal is true. If we are considering the global perspective,
+ *        then it is safe to pass the value \p IGRAPH_ALL here. If \p graph is
+ *        undirected, then we use all the immediate neighbours of \p vid. Thus
+ *        if you know that \p graph is undirected, then it is safe to pass the
+ *        value \p IGRAPH_ALL here. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          Use the out-neighbours of \p vid. This option is only relevant
+ *          when \p graph is a digraph and we are considering the local
+ *          perspective.
+ *        \cli IGRAPH_IN
+ *          Use the in-neighbours of \p vid. Again this option is only relevant
+ *          when \p graph is a directed graph and we are considering the local
+ *          perspective.
+ *        \cli IGRAPH_ALL
+ *          Use both the in- and out-neighbours of \p vid. This option is only
+ *          relevant if \p graph is a digraph. Also use this value if
+ *          \p graph is undirected or we are considering the global
+ *          perspective.
+ *        \endclist
+ * \return The error code \p IGRAPH_EINVAL is returned in each of the following
+ *         cases: (1) Any of the parameters \p graph, \p quantities, or
+ *         \p strategies is a null pointer. (2) The vector \p quantities or
+ *         \p strategies has a length different from the number of vertices
+ *         in \p graph. (3) The parameter \p graph is the empty or null graph,
+ *         i.e. the graph with zero vertices and edges. (4) The vector
+ *         \p quantities sums to zero.
+ *
+ * Time complexity: O(n) where n is the number of vertices in the perspective
+ * to consider. If we consider the global perspective, then n is the number
+ * of vertices in the vertex set of \p graph. On the other hand, for the local
+ * perspective n is the degree of \p vid, excluding loops.
+ *
+ * </para><para>
+ * Reference:
+ * \clist
+ * \cli (Yu &amp; Gen 2010)
+ *   X. Yu and M. Gen. \emb Introduction to Evolutionary Algorithms. \eme
+ *   Springer, 2010, pages 18--20.
+ * \endclist
+ *
+ * \example examples/simple/igraph_roulette_wheel_imitation.c
+ */
+
+int igraph_roulette_wheel_imitation(const igraph_t *graph,
+                                    igraph_integer_t vid,
+                                    igraph_bool_t islocal,
+                                    const igraph_vector_t *quantities,
+                                    igraph_vector_t *strategies,
+                                    igraph_neimode_t mode) {
+    igraph_bool_t updates;
+    igraph_integer_t u;
+    igraph_real_t r;    /* random number */
+    igraph_vector_t V;  /* vector of cumulative proportionate quantities */
+    igraph_vit_t A;     /* all vertices in v's perspective */
+    igraph_vs_t vs;
+    long int i;
+
+    IGRAPH_CHECK(igraph_microscopic_standard_tests(graph, vid, quantities,
+                 strategies, mode, &updates,
+                 islocal));
+    if (!updates) {
+        return IGRAPH_SUCCESS;    /* nothing further to do */
+    }
+
+    /* set the perspective */
+    if (islocal) {
+        IGRAPH_CHECK(igraph_vs_adj(&vs, vid, mode));
+    } else {
+        IGRAPH_CHECK(igraph_vs_all(&vs));
+    }
+    IGRAPH_FINALLY(igraph_vs_destroy, &vs);
+    IGRAPH_CHECK(igraph_vit_create(graph, vs, &A));
+    IGRAPH_FINALLY(igraph_vit_destroy, &A);
+
+    IGRAPH_CHECK(igraph_vcumulative_proportionate_values(graph, quantities, &V,
+                 islocal, vid, mode));
+
+    /* Finally, choose a vertex u to imitate. The vertex u is chosen */
+    /* proportionate to its quantity. In the case of a local perspective, we */
+    /* pretend that v's cumulative proportionate quantity has been appended to */
+    /* the vector V. Let V be of length n so that V[n-1] is the last element */
+    /* of V, and let r be a real number chosen uniformly at random from the */
+    /* unit interval [0,1]. If r > V[i] for all i < n, then v defaults to */
+    /* retaining its current strategy. Similarly in the case of the global */
+    /* perspective, if r > V[i] for all i < n - 1 then v would adopt the */
+    /* strategy of the vertex whose cumulative proportionate quantity is */
+    /* V[n-1]. */
+    /* NOTE: Here we assume that the order in which we iterate through the */
+    /* vertices in A is the same as the order in which we do so in the */
+    /* invoked function igraph_vcumulative_proportionate_values(). */
+    /* Otherwise we would incorrectly associate each V[i] with a vertex in A. */
+    RNG_BEGIN();
+    r = RNG_UNIF01();
+    RNG_END();
+    i = 0;
+    while (!IGRAPH_VIT_END(A)) {
+        if (r <= VECTOR(V)[i]) {
+            /* We have found our candidate vertex for imitation. Update strategy */
+            /* of v to that of u, and exit the selection loop. */
+            u = (igraph_integer_t)IGRAPH_VIT_GET(A);
+            VECTOR(*strategies)[vid] = VECTOR(*strategies)[u];
+            break;
+        }
+        i++;
+        IGRAPH_VIT_NEXT(A);
+    }
+
+    /* By now, vertex v should either retain its current strategy or it has */
+    /* adopted the strategy of a vertex in its perspective. Nothing else to */
+    /* do, but clean up. */
+    igraph_vector_destroy(&V);
+    igraph_vit_destroy(&A);
+    igraph_vs_destroy(&vs);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup spatialgames
+ * \function igraph_stochastic_imitation
+ * \brief Adopt a strategy via stochastic imitation with uniform selection.
+ *
+ * A simple stochastic imitation strategy where a vertex revises its
+ * strategy to that of a vertex chosen uniformly at random from its local
+ * neighbourhood. This is called stochastic imitation via uniform selection,
+ * where the strategy to imitate is chosen via some random process. For the
+ * purposes of this function, we use uniform selection from a pool of
+ * candidates.
+ *
+ * \param graph The graph object representing the game network. This cannot
+ *        be the empty or trivial graph, but must have at least two vertices
+ *        and one edge. If \p graph has one vertex, then no strategy update
+ *        would take place. Furthermore, if \p graph has at least two vertices
+ *        but zero edges, then strategy update would also not take place.
+ * \param vid The vertex whose strategy is to be updated. It is assumed that
+ *        \p vid represents a vertex in \p graph. No checking is performed and
+ *        it is your responsibility to ensure that \p vid is indeed a vertex
+ *        of \p graph. If an isolated vertex is provided, i.e. the input
+ *        vertex has degree 0, then no strategy update would take place and
+ *        \p vid would retain its current strategy. Strategy update would also
+ *        not take place if the local neighbourhood of \p vid are its
+ *        in-neighbours (respectively out-neighbours), but \p vid has zero
+ *        in-neighbours (respectively out-neighbours). Loops are ignored in
+ *        computing the degree (in, out, all) of \p vid.
+ * \param algo This flag controls which algorithm to use in stochastic
+ *        imitation. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_IMITATE_AUGMENTED
+ *          Augmented imitation. Vertex \p vid imitates the strategy of the
+ *          chosen vertex u provided that doing so would increase the
+ *          quantity (e.g. fitness) of \p vid. Augmented imitation can be
+ *          thought of as "imitate if better".
+ *        \cli IGRAPH_IMITATE_BLIND
+ *          Blind imitation. Vertex \p vid blindly imitates the strategy of
+ *          the chosen vertex u, regardless of whether doing so would
+ *          increase or decrease the quantity of \p vid.
+ *        \cli IGRAPH_IMITATE_CONTRACTED
+ *          Contracted imitation. Here vertex \p vid imitates the strategy of
+ *          the chosen vertex u if doing so would decrease the quantity of
+ *          \p vid. Think of contracted imitation as "imitate if worse".
+ *        \endclist
+ * \param quantities A vector of quantities providing the quantity of each
+ *        vertex in \p graph. Think of each entry of the vector as being
+ *        generated by a function such as the fitness function for the game.
+ *        So if the vector represents fitness quantities, then each vector
+ *        entry is the fitness of some vertex. The length of this vector must
+ *        be the same as the number of vertices in the vertex set of \p graph.
+ * \param strategies A vector of the current strategies for the vertex
+ *        population. The updated strategy for \p vid would be stored here.
+ *        Each strategy is identified with a nonnegative integer, whose
+ *        interpretation depends on the payoff matrix of the game. Generally
+ *        we use the strategy ID as a row or column index of the payoff
+ *        matrix. The length of this vector must be the same as the number of
+ *        vertices in the vertex set of \p graph.
+ * \param mode Defines the sort of neighbourhood to consider for \p vid. If
+ *        \p graph is undirected, then we use all the immediate neighbours of
+ *        \p vid. Thus if you know that \p graph is undirected, then it is safe
+ *        to pass the value \p IGRAPH_ALL here. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          Use the out-neighbours of \p vid. This option is only relevant
+ *          when \p graph is a directed graph.
+ *        \cli IGRAPH_IN
+ *          Use the in-neighbours of \p vid. Again this option is only relevant
+ *          when \p graph is a directed graph.
+ *        \cli IGRAPH_ALL
+ *          Use both the in- and out-neighbours of \p vid. This option is only
+ *          relevant if \p graph is a digraph. Also use this value if
+ *          \p graph is undirected.
+ *        \endclist
+ * \return The error code \p IGRAPH_EINVAL is returned in each of the following
+ *         cases: (1) Any of the parameters \p graph, \p quantities, or
+ *         \p strategies is a null pointer. (2) The vector \p quantities or
+ *         \p strategies has a length different from the number of vertices
+ *         in \p graph. (3) The parameter \p graph is the empty or null graph,
+ *         i.e. the graph with zero vertices and edges. (4) The parameter
+ *         \p algo refers to an unsupported stochastic imitation algorithm.
+ *
+ * Time complexity: depends on the uniform random number generator, but should
+ * usually be O(1).
+ *
+ * \example examples/simple/igraph_stochastic_imitation.c
+ */
+
+int igraph_stochastic_imitation(const igraph_t *graph,
+                                igraph_integer_t vid,
+                                igraph_imitate_algorithm_t algo,
+                                const igraph_vector_t *quantities,
+                                igraph_vector_t *strategies,
+                                igraph_neimode_t mode) {
+    igraph_bool_t updates;
+    igraph_integer_t u;
+    igraph_vector_t adj;
+    int i;
+
+    /* sanity checks */
+    if (algo != IGRAPH_IMITATE_AUGMENTED &&
+        algo != IGRAPH_IMITATE_BLIND &&
+        algo != IGRAPH_IMITATE_CONTRACTED) {
+        IGRAPH_ERROR("Unsupported stochastic imitation algorithm",
+                     IGRAPH_EINVAL);
+    }
+    IGRAPH_CHECK(igraph_microscopic_standard_tests(graph, vid, quantities,
+                 strategies, mode, &updates,
+                 /*is local?*/ 1));
+    if (!updates) {
+        return IGRAPH_SUCCESS;    /* nothing more to do */
+    }
+
+    /* immediate neighbours of v */
+    IGRAPH_VECTOR_INIT_FINALLY(&adj, 0);
+    IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode));
+
+    /* Blind imitation. Let v be the vertex whose strategy we want to revise. */
+    /* Choose a vertex u uniformly at random from the immediate neighbours of */
+    /* v, including v itself. Then blindly update the strategy of v to that of */
+    /* u, irrespective of whether doing so would increase or decrease the */
+    /* quantity (e.g. fitness) of v. Here v retains its current strategy if */
+    /* the chosen vertex u is indeed v itself. */
+    if (algo == IGRAPH_IMITATE_BLIND) {
+        IGRAPH_CHECK(igraph_vector_push_back(&adj, vid));
+        RNG_BEGIN();
+        i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1);
+        RNG_END();
+        u = (igraph_integer_t) VECTOR(adj)[i];
+        VECTOR(*strategies)[vid] = VECTOR(*strategies)[u];
+    }
+    /* Augmented imitation. Let v be the vertex whose strategy we want to */
+    /* revise. Let f be the quantity function for the game. Choose a vertex u */
+    /* uniformly at random from the immediate neighbours of v; do not include */
+    /* v. Then v imitates the strategy of u if f(u) > f(v). Otherwise v */
+    /* retains its current strategy. */
+    else if (algo == IGRAPH_IMITATE_AUGMENTED) {
+        RNG_BEGIN();
+        i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1);
+        RNG_END();
+        u = (igraph_integer_t) VECTOR(adj)[i];
+        if (VECTOR(*quantities)[u] > VECTOR(*quantities)[vid]) {
+            VECTOR(*strategies)[vid] = VECTOR(*strategies)[u];
+        }
+    }
+    /* Contracted imitation. Let v be the vertex whose strategy we want to */
+    /* update and let f be the quantity function for the game. Choose a vertex */
+    /* u uniformly at random from the immediate neighbours of v, excluding v */
+    /* itself. Then v imitates the strategy of u provided that f(u) < f(v). */
+    /* Otherwise v retains its current strategy. */
+    else if (algo == IGRAPH_IMITATE_CONTRACTED) {
+        RNG_BEGIN();
+        i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1);
+        RNG_END();
+        u = (igraph_integer_t) VECTOR(adj)[i];
+        if (VECTOR(*quantities)[u] < VECTOR(*quantities)[vid]) {
+            VECTOR(*strategies)[vid] = VECTOR(*strategies)[u];
+        }
+    }
+
+    /* clean up */
+    igraph_vector_destroy(&adj);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/mixing.c b/igraph/src/mixing.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/mixing.c
@@ -0,0 +1,300 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_mixing.h"
+#include "igraph_interface.h"
+
+/**
+ * \function igraph_assortativity_nominal
+ * Assortativity of a graph based on vertex categories
+ *
+ * Assuming the vertices of the input graph belong to different
+ * categories, this function calculates the assortativity coefficient of
+ * the graph. The assortativity coefficient is between minus one and one
+ * and it is one if all connections stay within categories, it is
+ * minus one, if the network is perfectly disassortative. For a
+ * randomly connected network it is (asymptotically) zero.
+ *
+ * </para><para>See equation (2) in M. E. J. Newman: Mixing patterns
+ * in networks, Phys. Rev. E 67, 026126 (2003)
+ * (http://arxiv.org/abs/cond-mat/0209450) for the proper
+ * definition.
+ *
+ * \param graph The input graph, it can be directed or undirected.
+ * \param types Vector giving the vertex types. They are assumed to be
+ *    integer numbers, starting with zero.
+ * \param res Pointer to a real variable, the result is stored here.
+ * \param directed Boolean, it gives whether to consider edge
+ *    directions in a directed graph. It is ignored for undirected
+ *    graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|+t), |E| is the number of edges, t is the
+ * number of vertex types.
+ *
+ * \sa \ref igraph_assortativity if the vertex types are defines by
+ * numeric values (e.g. vertex degree), instead of categories.
+ *
+ * \example examples/simple/assortativity.c
+ */
+
+int igraph_assortativity_nominal(const igraph_t *graph,
+                                 const igraph_vector_t *types,
+                                 igraph_real_t *res,
+                                 igraph_bool_t directed) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int no_of_types;
+    igraph_vector_t ai, bi, eii;
+    long int e, i;
+    igraph_real_t sumaibi = 0.0, sumeii = 0.0;
+
+    if (igraph_vector_size(types) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid `types' vector length", IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_min(types) < 0) {
+        IGRAPH_ERROR("Invalid `types' vector", IGRAPH_EINVAL);
+    }
+
+    directed = directed && igraph_is_directed(graph);
+
+    no_of_types = (long int) igraph_vector_max(types) + 1;
+    IGRAPH_VECTOR_INIT_FINALLY(&ai, no_of_types);
+    IGRAPH_VECTOR_INIT_FINALLY(&bi, no_of_types);
+    IGRAPH_VECTOR_INIT_FINALLY(&eii, no_of_types);
+
+    for (e = 0; e < no_of_edges; e++) {
+        long int from = IGRAPH_FROM(graph, e);
+        long int to = IGRAPH_TO(graph, e);
+        long int from_type = (long int) VECTOR(*types)[from];
+        long int to_type = (long int) VECTOR(*types)[to];
+
+        VECTOR(ai)[from_type] += 1;
+        VECTOR(bi)[to_type] += 1;
+        if (from_type == to_type) {
+            VECTOR(eii)[from_type] += 1;
+        }
+        if (!directed) {
+            if (from_type == to_type) {
+                VECTOR(eii)[from_type] += 1;
+            }
+            VECTOR(ai)[to_type] += 1;
+            VECTOR(bi)[from_type] += 1;
+        }
+    }
+
+    for (i = 0; i < no_of_types; i++) {
+        sumaibi += (VECTOR(ai)[i] / no_of_edges) * (VECTOR(bi)[i] / no_of_edges);
+        sumeii  += (VECTOR(eii)[i] / no_of_edges);
+    }
+
+    if (!directed) {
+        sumaibi /= 4.0;
+        sumeii  /= 2.0;
+    }
+
+    *res = (sumeii - sumaibi) / (1.0 - sumaibi);
+
+    igraph_vector_destroy(&eii);
+    igraph_vector_destroy(&bi);
+    igraph_vector_destroy(&ai);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_assortativity
+ * Assortativity based on numeric properties of vertices
+ *
+ * This function calculates the assortativity coefficient of the input
+ * graph. This coefficient is basically the correlation between the
+ * actual connectivity patterns of the vertices and the pattern
+ * expected from the distribution of the vertex types.
+ *
+ * </para><para>See equation (21) in M. E. J. Newman: Mixing patterns
+ * in networks, Phys. Rev. E 67, 026126 (2003)
+ * (http://arxiv.org/abs/cond-mat/0209450) for the proper
+ * definition. The actual calculation is performed using equation (26)
+ * in the same paper for directed graphs, and equation (4) in
+ * M. E. J. Newman: Assortative mixing in networks,
+ * Phys. Rev. Lett. 89, 208701 (2002)
+ * (http://arxiv.org/abs/cond-mat/0205405/) for undirected graphs.
+ *
+ * \param graph The input graph, it can be directed or undirected.
+ * \param types1 The vertex values, these can be arbitrary numeric
+ *     values.
+ * \param types2 A second value vector to be using for the incoming
+ *     edges when calculating assortativity for a directed graph.
+ *     Supply a null pointer here if you want to use the same values
+ *     for outgoing and incoming edges. This argument is ignored
+ *     (with a warning) if it is not a null pointer and undirected
+ *     assortativity coefficient is being calculated.
+ * \param res Pointer to a real variable, the result is stored here.
+ * \param directed Boolean, whether to consider edge directions for
+ *     directed graphs. It is ignored for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|), linear in the number of edges of the
+ * graph.
+ *
+ * \sa \ref igraph_assortativity_nominal() if you have discrete vertex
+ * categories instead of numeric labels, and \ref
+ * igraph_assortativity_degree() for the special case of assortativity
+ * based on vertex degree.
+ *
+ * \example examples/simple/assortativity.c
+ */
+
+int igraph_assortativity(const igraph_t *graph,
+                         const igraph_vector_t *types1,
+                         const igraph_vector_t *types2,
+                         igraph_real_t *res,
+                         igraph_bool_t directed) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int e;
+
+    directed = directed && igraph_is_directed(graph);
+
+    if (!directed && types2) {
+        IGRAPH_WARNING("Only `types1' is used for undirected case");
+    }
+
+    if (igraph_vector_size(types1) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid `types1' vector length", IGRAPH_EINVAL);
+    }
+
+    if (types2 && igraph_vector_size(types2) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid `types2' vector length", IGRAPH_EINVAL);
+    }
+
+    if (!directed) {
+        igraph_real_t num1 = 0.0, num2 = 0.0, den1 = 0.0;
+
+        for (e = 0; e < no_of_edges; e++) {
+            long int from = IGRAPH_FROM(graph, e);
+            long int to = IGRAPH_TO(graph, e);
+            igraph_real_t from_type = VECTOR(*types1)[from];
+            igraph_real_t to_type = VECTOR(*types1)[to];
+
+            num1 += from_type * to_type;
+            num2 += from_type + to_type;
+            den1 += from_type * from_type + to_type * to_type;
+        }
+
+        num1 /= no_of_edges;
+        den1 /= no_of_edges * 2;
+        num2 /= no_of_edges * 2;
+        num2 = num2 * num2;
+
+        *res = (num1 - num2) / (den1 - num2);
+
+    } else {
+        igraph_real_t num1 = 0.0, num2 = 0.0, num3 = 0.0,
+                      den1 = 0.0, den2 = 0.0;
+        igraph_real_t num, den;
+
+        if (!types2) {
+            types2 = types1;
+        }
+
+        for (e = 0; e < no_of_edges; e++) {
+            long int from = IGRAPH_FROM(graph, e);
+            long int to = IGRAPH_TO(graph, e);
+            igraph_real_t from_type = VECTOR(*types1)[from];
+            igraph_real_t to_type = VECTOR(*types2)[to];
+
+            num1 += from_type * to_type;
+            num2 += from_type;
+            num3 += to_type;
+            den1 += from_type * from_type;
+            den2 += to_type * to_type;
+        }
+
+        num = num1 - num2 * num3 / no_of_edges;
+        den = sqrt(den1 - num2 * num2 / no_of_edges) *
+              sqrt(den2 - num3 * num3 / no_of_edges);
+
+        *res = num / den;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_assortativity_degree
+ * Assortativity of a graph based on vertex degree
+ *
+ * Assortativity based on vertex degree, please see the discussion at
+ * the documentation of \ref igraph_assortativity() for details.
+ *
+ * \param graph The input graph, it can be directed or undirected.
+ * \param res Pointer to a real variable, the result is stored here.
+ * \param directed Boolean, whether to consider edge directions for
+ *     directed graphs. This argument is ignored for undirected
+ *     graphs. Supply 1 (=TRUE) here to do the natural thing, i.e. use
+ *     directed version of the measure for directed graphs and the
+ *     undirected version for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|+|V|), |E| is the number of edges, |V| is
+ * the number of vertices.
+ *
+ * \sa \ref igraph_assortativity() for the general function
+ * calculating assortativity for any kind of numeric vertex values.
+ *
+ * \example examples/simple/assortativity.c
+ */
+
+int igraph_assortativity_degree(const igraph_t *graph,
+                                igraph_real_t *res,
+                                igraph_bool_t directed) {
+
+    directed = directed && igraph_is_directed(graph);
+
+    if (directed) {
+        igraph_vector_t indegree, outdegree;
+        igraph_vector_init(&indegree, 0);
+        igraph_vector_init(&outdegree, 0);
+        igraph_degree(graph, &indegree, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1);
+        igraph_degree(graph, &outdegree, igraph_vss_all(), IGRAPH_OUT, /*loops=*/ 1);
+        igraph_vector_add_constant(&indegree, -1);
+        igraph_vector_add_constant(&outdegree, -1);
+        igraph_assortativity(graph, &outdegree, &indegree, res, /*directed=*/ 1);
+        igraph_vector_destroy(&indegree);
+        igraph_vector_destroy(&outdegree);
+    } else {
+        igraph_vector_t degree;
+        igraph_vector_init(&degree, 0);
+        igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 1);
+        igraph_vector_add_constant(&degree, -1);
+        igraph_assortativity(graph, &degree, 0, res, /*directed=*/ 0);
+        igraph_vector_destroy(&degree);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/motifs.c b/igraph/src/motifs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/motifs.c
@@ -0,0 +1,1126 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_motifs.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_adjlist.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_interface.h"
+#include "igraph_nongraph.h"
+#include "igraph_structural.h"
+#include "igraph_stack.h"
+#include "config.h"
+
+#include <string.h>
+
+extern unsigned int igraph_i_isoclass_3[];
+extern unsigned int igraph_i_isoclass_4[];
+extern unsigned int igraph_i_isoclass_3u[];
+extern unsigned int igraph_i_isoclass_4u[];
+extern unsigned int igraph_i_isoclass2_3[];
+extern unsigned int igraph_i_isoclass2_4[];
+extern unsigned int igraph_i_isoclass2_3u[];
+extern unsigned int igraph_i_isoclass2_4u[];
+extern unsigned int igraph_i_isoclass_3_idx[];
+extern unsigned int igraph_i_isoclass_4_idx[];
+extern unsigned int igraph_i_isoclass_3u_idx[];
+extern unsigned int igraph_i_isoclass_4u_idx[];
+
+/**
+ * Callback function for igraph_motifs_randesu that counts the motifs by
+ * isomorphism class in a histogram.
+ */
+igraph_bool_t igraph_i_motifs_randesu_update_hist(const igraph_t *graph,
+        igraph_vector_t *vids, int isoclass, void* extra) {
+    igraph_vector_t *hist = (igraph_vector_t*)extra;
+    IGRAPH_UNUSED(graph); IGRAPH_UNUSED(vids);
+    VECTOR(*hist)[isoclass]++;
+    return 0;
+}
+
+/**
+ * \function igraph_motifs_randesu
+ * \brief Count the number of motifs in a graph
+ *
+ * </para><para>
+ * Motifs are small connected subgraphs of a given structure in a
+ * graph. It is argued that the motif profile (ie. the number of
+ * different motifs in the graph) is characteristic for different
+ * types of networks and network function is related to the motifs in
+ * the graph.
+ *
+ * </para><para>
+ * This function is able to find the different motifs of size three
+ * and four (ie. the number of different subgraphs with three and four
+ * vertices) in the network.
+ *
+ * </para><para>
+ * In a big network the total number of motifs can be very large, so
+ * it takes a lot of time to find all of them, a sampling method can
+ * be used. This function is capable of doing sampling via the
+ * \c cut_prob argument. This argument gives the probability that
+ * a branch of the motif search tree will not be explored. See
+ * S. Wernicke and F. Rasche: FANMOD: a tool for fast network motif
+ * detection, Bioinformatics 22(9), 1152--1153, 2006 for details.
+ *
+ * </para><para>
+ * Set the \c cut_prob argument to a zero vector for finding all
+ * motifs.
+ *
+ * </para><para>
+ * Directed motifs will be counted in directed graphs and undirected
+ * motifs in undirected graphs.
+ *
+ * \param graph The graph to find the motifs in.
+ * \param hist The result of the computation, it gives the number of
+ *        motifs found for each isomorphism class. See
+ *        \ref igraph_isoclass() for help about isomorphism classes.
+ *        Note that this function does \em not count isomorphism
+ *        classes that are not connected and will report NaN (more
+ *        precisely \c IGRAPH_NAN) for them.
+ * \param size The size of the motifs to search for. Only three and
+ *        four are implemented currently. The limitation is not in the
+ *        motif finding code, but the graph isomorphism code.
+ * \param cut_prob Vector of probabilities for cutting the search tree
+ *        at a given level. The first element is the first level, etc.
+ *        Supply all zeros here (of length \c size) to find all motifs
+ *        in a graph.
+ * \return Error code.
+ * \sa \ref igraph_motifs_randesu_estimate() for estimating the number
+ * of motifs in a graph, this can help to set the \c cut_prob
+ * parameter; \ref igraph_motifs_randesu_no() to calculate the total
+ * number of motifs of a given size in a graph;
+ * \ref igraph_motifs_randesu_callback() for calling a callback function
+ * for every motif found; \ref igraph_subisomorphic_lad() for finding
+ * subgraphs on more than 4 vertices.
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/igraph_motifs_randesu.c
+ */
+int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist,
+                          int size, const igraph_vector_t *cut_prob) {
+    int histlen;
+
+    if (size != 3 && size != 4) {
+        IGRAPH_ERROR("Only 3 and 4 vertex motifs are implemented",
+                     IGRAPH_EINVAL);
+    }
+    if (size == 3) {
+        histlen = igraph_is_directed(graph) ? 16 : 4;
+    } else {
+        histlen = igraph_is_directed(graph) ? 218 : 11;
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(hist, histlen));
+    igraph_vector_null(hist);
+
+    IGRAPH_CHECK(igraph_motifs_randesu_callback(graph, size, cut_prob,
+                 &igraph_i_motifs_randesu_update_hist, hist));
+
+    if (size == 3) {
+        if (igraph_is_directed(graph)) {
+            VECTOR(*hist)[0] = VECTOR(*hist)[1] = VECTOR(*hist)[3] = IGRAPH_NAN;
+        } else {
+            VECTOR(*hist)[0] = VECTOR(*hist)[1] = IGRAPH_NAN;
+        }
+    } else if (size == 4) {
+        if (igraph_is_directed(graph)) {
+            int not_connected[] = { 0, 1, 2, 4, 5, 6, 9, 10, 11, 15, 22, 23, 27,
+                                    28, 33, 34, 39, 62, 120
+                                  };
+            int i, n = sizeof(not_connected) / sizeof(int);
+            for (i = 0; i < n; i++) {
+                VECTOR(*hist)[not_connected[i]] = IGRAPH_NAN;
+            }
+        } else {
+            VECTOR(*hist)[0] = VECTOR(*hist)[1] = VECTOR(*hist)[2] =
+                    VECTOR(*hist)[3] = VECTOR(*hist)[5] = IGRAPH_NAN;
+        }
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_motifs_randesu_callback
+ * \brief Finds motifs in a graph and calls a function for each of them
+ *
+ * </para><para>
+ * Similarly to \ref igraph_motifs_randesu(), this function is able to find the
+ * different motifs of size three and four (ie. the number of different
+ * subgraphs with three and four vertices) in the network. However, instead of
+ * counting them, the function will call a callback function for each motif
+ * found to allow further tests or post-processing.
+ *
+ * </para><para>
+ * The \c cut_prob argument also allows sampling the motifs, just like for
+ * \ref igraph_motifs_randesu(). Set the \c cut_prob argument to a zero vector
+ * for finding all motifs.
+ *
+ * \param graph The graph to find the motifs in.
+ * \param size The size of the motifs to search for. Only three and
+ *        four are implemented currently. The limitation is not in the
+ *        motif finding code, but the graph isomorphism code.
+ * \param cut_prob Vector of probabilities for cutting the search tree
+ *        at a given level. The first element is the first level, etc.
+ *        Supply all zeros here (of length \c size) to find all motifs
+ *        in a graph.
+ * \param callback A pointer to a function of type \ref igraph_motifs_handler_t.
+ *        This function will be called whenever a new motif is found.
+ * \param extra Extra argument to pass to the callback function.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/igraph_motifs_randesu.c
+ */
+
+int igraph_motifs_randesu_callback(const igraph_t *graph, int size,
+                                   const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback,
+                                   void* extra) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_adjlist_t allneis, alloutneis;
+    igraph_vector_int_t *neis;
+    long int father;
+    long int i, j, s;
+    long int motifs = 0;
+
+    igraph_vector_t vids;     /* this is G */
+    igraph_vector_t adjverts; /* this is V_E */
+    igraph_stack_t stack;     /* this is S */
+    long int *added;
+    char *subg;
+
+    unsigned int *arr_idx, *arr_code;
+    int code = 0;
+    unsigned char mul, idx;
+
+    igraph_bool_t terminate = 0;
+
+    if (size != 3 && size != 4) {
+        IGRAPH_ERROR("Only 3 and 4 vertex motifs are implemented",
+                     IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_size(cut_prob) < size) {
+        IGRAPH_ERROR("The size of the cut probability vector must not be smaller than the motif size.",
+                     IGRAPH_EINVAL);
+    }
+
+    if (size == 3) {
+        mul = 3;
+        if (igraph_is_directed(graph)) {
+            arr_idx = igraph_i_isoclass_3_idx;
+            arr_code = igraph_i_isoclass2_3;
+        } else {
+            arr_idx = igraph_i_isoclass_3u_idx;
+            arr_code = igraph_i_isoclass2_3u;
+        }
+    } else {
+        mul = 4;
+        if (igraph_is_directed(graph)) {
+            arr_idx = igraph_i_isoclass_4_idx;
+            arr_code = igraph_i_isoclass2_4;
+        } else {
+            arr_idx = igraph_i_isoclass_4u_idx;
+            arr_code = igraph_i_isoclass2_4u;
+        }
+    }
+
+    added = igraph_Calloc(no_of_nodes, long int);
+    if (added == 0) {
+        IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added);
+
+    subg = igraph_Calloc(no_of_nodes, char);
+    if (subg == 0) {
+        IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, subg);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &alloutneis, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &alloutneis);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&vids, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0);
+    IGRAPH_CHECK(igraph_stack_init(&stack, 0));
+    IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+
+    RNG_BEGIN();
+
+    for (father = 0; father < no_of_nodes; father++) {
+        long int level;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (VECTOR(*cut_prob)[0] == 1 ||
+            RNG_UNIF01() < VECTOR(*cut_prob)[0]) {
+            continue;
+        }
+
+        /* init G */
+        igraph_vector_clear(&vids); level = 0;
+        IGRAPH_CHECK(igraph_vector_push_back(&vids, father));
+        subg[father] = 1; added[father] += 1; level += 1;
+
+        /* init V_E */
+        igraph_vector_clear(&adjverts);
+        neis = igraph_adjlist_get(&allneis, father);
+        s = igraph_vector_int_size(neis);
+        for (i = 0; i < s; i++) {
+            long int nei = (long int) VECTOR(*neis)[i];
+            if (!added[nei] && nei > father) {
+                IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei));
+                IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father));
+            }
+            added[nei] += 1;
+        }
+
+        /* init S */
+        igraph_stack_clear(&stack);
+
+        while (level > 1 || !igraph_vector_empty(&adjverts)) {
+            igraph_real_t cp = VECTOR(*cut_prob)[level];
+
+            if (level == size - 1) {
+                s = igraph_vector_size(&adjverts) / 2;
+                for (i = 0; i < s; i++) {
+                    long int k, s2;
+                    long int last;
+
+                    if (cp != 0 && RNG_UNIF01() < cp) {
+                        continue;
+                    }
+                    motifs += 1;
+
+                    last = (long int) VECTOR(adjverts)[2 * i];
+                    IGRAPH_CHECK(igraph_vector_push_back(&vids, last));
+                    subg[last] = (char) size;
+
+                    code = 0; idx = 0;
+                    for (k = 0; k < size; k++) {
+                        long int from = (long int) VECTOR(vids)[k];
+                        neis = igraph_adjlist_get(&alloutneis, from);
+                        s2 = igraph_vector_int_size(neis);
+                        for (j = 0; j < s2; j++) {
+                            long int nei = (long int) VECTOR(*neis)[j];
+                            if (subg[nei] && k != subg[nei] - 1) {
+                                idx = (unsigned char) (mul * k + (subg[nei] - 1));
+                                code |= arr_idx[idx];
+                            }
+                        }
+                    }
+
+                    if (callback(graph, &vids, (int) arr_code[code], extra)) {
+                        terminate = 1;
+                        break;
+                    }
+                    igraph_vector_pop_back(&vids);
+                    subg[last] = 0;
+                }
+            }
+
+            /* did the callback function asked us to terminate the search? */
+            if (terminate) {
+                break;
+            }
+
+            /* can we step down? */
+            if (level < size - 1 &&
+                !igraph_vector_empty(&adjverts)) {
+                /* we might step down */
+                long int neifather = (long int) igraph_vector_pop_back(&adjverts);
+                long int nei = (long int) igraph_vector_pop_back(&adjverts);
+
+                if (cp == 0 || RNG_UNIF01() > cp) {
+                    /* yes, step down */
+                    IGRAPH_CHECK(igraph_vector_push_back(&vids, nei));
+                    subg[nei] = (char) level + 1; added[nei] += 1; level += 1;
+
+                    IGRAPH_CHECK(igraph_stack_push(&stack, neifather));
+                    IGRAPH_CHECK(igraph_stack_push(&stack, nei));
+                    IGRAPH_CHECK(igraph_stack_push(&stack, level));
+
+                    neis = igraph_adjlist_get(&allneis, nei);
+                    s = igraph_vector_int_size(neis);
+                    for (i = 0; i < s; i++) {
+                        long int nei2 = (long int) VECTOR(*neis)[i];
+                        if (!added[nei2] && nei2 > father) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2));
+                            IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei));
+                        }
+                        added[nei2] += 1;
+                    }
+                }
+            } else {
+                /* no, step back */
+                long int nei, neifather;
+                while (!igraph_stack_empty(&stack) &&
+                       level == igraph_stack_top(&stack) - 1) {
+                    igraph_stack_pop(&stack);
+                    nei = (long int) igraph_stack_pop(&stack);
+                    neifather = (long int) igraph_stack_pop(&stack);
+                    igraph_vector_push_back(&adjverts, nei);
+                    igraph_vector_push_back(&adjverts, neifather);
+                }
+
+                nei = (long int) igraph_vector_pop_back(&vids);
+                subg[nei] = 0; added[nei] -= 1; level -= 1;
+                neis = igraph_adjlist_get(&allneis, nei);
+                s = igraph_vector_int_size(neis);
+                for (i = 0; i < s; i++) {
+                    added[ (long int) VECTOR(*neis)[i] ] -= 1;
+                }
+                while (!igraph_vector_empty(&adjverts) &&
+                       igraph_vector_tail(&adjverts) == nei) {
+                    igraph_vector_pop_back(&adjverts);
+                    igraph_vector_pop_back(&adjverts);
+                }
+            }
+
+        } /* while */
+
+        /* did the callback function asked us to terminate the search? */
+        if (terminate) {
+            break;
+        }
+
+        /* clear the added vector */
+        added[father] -= 1;
+        subg[father] = 0;
+        neis = igraph_adjlist_get(&allneis, father);
+        s = igraph_vector_int_size(neis);
+        for (i = 0; i < s; i++) {
+            added[ (long int) VECTOR(*neis)[i] ] -= 1;
+        }
+
+    } /* for father */
+
+    RNG_END();
+
+    igraph_Free(added);
+    igraph_Free(subg);
+    igraph_vector_destroy(&vids);
+    igraph_vector_destroy(&adjverts);
+    igraph_adjlist_destroy(&alloutneis);
+    igraph_adjlist_destroy(&allneis);
+    igraph_stack_destroy(&stack);
+    IGRAPH_FINALLY_CLEAN(7);
+    return 0;
+}
+
+/**
+ * \function igraph_motifs_randesu_estimate
+ * \brief Estimate the total number of motifs in a graph
+ *
+ * </para><para>
+ * This function is useful for large graphs for which it is not
+ * feasible to count all the different motifs, because there is very
+ * many of them.
+ *
+ * </para><para>
+ * The total number of motifs is estimated by taking a sample of
+ * vertices and counts all motifs in which these vertices are
+ * included. (There is also a \c cut_prob parameter which gives the
+ * probabilities to cut a branch of the search tree.)
+ *
+ * </para><para>
+ * Directed motifs will be counted in directed graphs and undirected
+ * motifs in undirected graphs.
+ *
+ * \param graph The graph object to study.
+ * \param est Pointer to an integer type, the result will be stored
+ *        here.
+ * \param size The size of the motif to look for.
+ * \param cut_prob Vector giving the probabilities to cut a branch of
+ *        the search tree and omit counting the motifs in that branch.
+ *        It contains a probability for each level. Supply \c size
+ *        zeros here to count all the motifs in the sample.
+ * \param sample_size The number of vertices to use as the
+ *        sample. This parameter is only used if the \c parsample
+ *        argument is a null pointer.
+ * \param parsample Either pointer to an initialized vector or a null
+ *        pointer. If a vector then the vertex ids in the vector are
+ *        used as a sample. If a null pointer then the \c sample_size
+ *        argument is used to create a sample of vertices drawn with
+ *        uniform probability.
+ * \return Error code.
+ * \sa \ref igraph_motifs_randesu(), \ref igraph_motifs_randesu_no().
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est,
+                                   int size, const igraph_vector_t *cut_prob,
+                                   igraph_integer_t sample_size,
+                                   const igraph_vector_t *parsample) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t neis;
+
+    igraph_vector_t vids;     /* this is G */
+    igraph_vector_t adjverts; /* this is V_E */
+    igraph_stack_t stack;     /* this is S */
+    long int *added;
+    igraph_vector_t *sample;
+    long int sam;
+    long int i;
+
+    added = igraph_Calloc(no_of_nodes, long int);
+    if (added == 0) {
+        IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&vids, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0);
+    IGRAPH_CHECK(igraph_stack_init(&stack, 0));
+    IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    if (parsample == 0) {
+        sample = igraph_Calloc(1, igraph_vector_t);
+        if (sample == 0) {
+            IGRAPH_ERROR("Cannot estimate motifs", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, sample);
+        IGRAPH_VECTOR_INIT_FINALLY(sample, 0);
+        IGRAPH_CHECK(igraph_random_sample(sample, 0, no_of_nodes - 1, sample_size));
+    } else {
+        sample = (igraph_vector_t*)parsample;
+        sample_size = (igraph_integer_t) igraph_vector_size(sample);
+    }
+
+    *est = 0;
+
+    RNG_BEGIN();
+
+    for (sam = 0; sam < sample_size; sam++) {
+        long int father = (long int) VECTOR(*sample)[sam];
+        long int level, s;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (VECTOR(*cut_prob)[0] == 1 ||
+            RNG_UNIF01() < VECTOR(*cut_prob)[0]) {
+            continue;
+        }
+
+        /* init G */
+        igraph_vector_clear(&vids); level = 0;
+        IGRAPH_CHECK(igraph_vector_push_back(&vids, father));
+        added[father] += 1; level += 1;
+
+        /* init V_E */
+        igraph_vector_clear(&adjverts);
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father,
+                                      IGRAPH_ALL));
+        s = igraph_vector_size(&neis);
+        for (i = 0; i < s; i++) {
+            long int nei = (long int) VECTOR(neis)[i];
+            if (!added[nei] && nei > father) {
+                IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei));
+                IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father));
+            }
+            added[nei] += 1;
+        }
+
+        /* init S */
+        igraph_stack_clear(&stack);
+
+        while (level > 1 || !igraph_vector_empty(&adjverts)) {
+            igraph_real_t cp = VECTOR(*cut_prob)[level];
+
+            if (level == size - 1) {
+                s = igraph_vector_size(&adjverts) / 2;
+                for (i = 0; i < s; i++) {
+                    if (cp != 0 && RNG_UNIF01() < cp) {
+                        continue;
+                    }
+                    (*est) += 1;
+                }
+            }
+
+            if (level < size - 1 &&
+                !igraph_vector_empty(&adjverts)) {
+                /* We might step down */
+                long int neifather = (long int) igraph_vector_pop_back(&adjverts);
+                long int nei = (long int) igraph_vector_pop_back(&adjverts);
+
+                if (cp == 0 || RNG_UNIF01() > cp) {
+                    /* Yes, step down */
+                    IGRAPH_CHECK(igraph_vector_push_back(&vids, nei));
+                    added[nei] += 1; level += 1;
+
+                    IGRAPH_CHECK(igraph_stack_push(&stack, neifather));
+                    IGRAPH_CHECK(igraph_stack_push(&stack, nei));
+                    IGRAPH_CHECK(igraph_stack_push(&stack, level));
+
+                    IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei,
+                                                  IGRAPH_ALL));
+                    s = igraph_vector_size(&neis);
+                    for (i = 0; i < s; i++) {
+                        long int nei2 = (long int) VECTOR(neis)[i];
+                        if (!added[nei2] && nei2 > father) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2));
+                            IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei));
+                        }
+                        added[nei2] += 1;
+                    }
+                }
+            } else {
+                /* no, step back */
+                long int nei, neifather;
+                while (!igraph_stack_empty(&stack) &&
+                       level == igraph_stack_top(&stack) - 1) {
+                    igraph_stack_pop(&stack);
+                    nei = (long int) igraph_stack_pop(&stack);
+                    neifather = (long int) igraph_stack_pop(&stack);
+                    igraph_vector_push_back(&adjverts, nei);
+                    igraph_vector_push_back(&adjverts, neifather);
+                }
+
+                nei = (long int) igraph_vector_pop_back(&vids);
+                added[nei] -= 1; level -= 1;
+                IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei,
+                                              IGRAPH_ALL));
+                s = igraph_vector_size(&neis);
+                for (i = 0; i < s; i++) {
+                    added[ (long int) VECTOR(neis)[i] ] -= 1;
+                }
+                while (!igraph_vector_empty(&adjverts) &&
+                       igraph_vector_tail(&adjverts) == nei) {
+                    igraph_vector_pop_back(&adjverts);
+                    igraph_vector_pop_back(&adjverts);
+                }
+            }
+
+        } /* while */
+
+        /* clear the added vector */
+        added[father] -= 1;
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father,
+                                      IGRAPH_ALL));
+        s = igraph_vector_size(&neis);
+        for (i = 0; i < s; i++) {
+            added[ (long int) VECTOR(neis)[i] ] -= 1;
+        }
+
+    } /* for father */
+
+    RNG_END();
+
+    (*est) *= ((double)no_of_nodes / sample_size);
+
+    if (parsample == 0) {
+        igraph_vector_destroy(sample);
+        igraph_Free(sample);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_Free(added);
+    igraph_vector_destroy(&vids);
+    igraph_vector_destroy(&adjverts);
+    igraph_stack_destroy(&stack);
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(5);
+    return 0;
+}
+
+/**
+ * \function igraph_motifs_randesu_no
+ * \brief Count the total number of motifs in a graph
+ *
+ * </para><para>
+ * This function counts the total number of motifs in a graph without
+ * assigning isomorphism classes to them.
+ *
+ * </para><para>
+ * Directed motifs will be counted in directed graphs and undirected
+ * motifs in undirected graphs.
+ *
+ * \param graph The graph object to study.
+ * \param no Pointer to an integer type, the result will be stored
+ *        here.
+ * \param size The size of the motifs to count.
+ * \param cut_prob Vector giving the probabilities that a branch of
+ *        the search tree will be cut at a given level.
+ * \return Error code.
+ * \sa \ref igraph_motifs_randesu(), \ref
+ *     igraph_motifs_randesu_estimate().
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no,
+                             int size, const igraph_vector_t *cut_prob) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t neis;
+
+    igraph_vector_t vids;     /* this is G */
+    igraph_vector_t adjverts; /* this is V_E */
+    igraph_stack_t stack;     /* this is S */
+    long int *added;
+    long int father;
+    long int i;
+
+    added = igraph_Calloc(no_of_nodes, long int);
+    if (added == 0) {
+        IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&vids, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&adjverts, 0);
+    IGRAPH_CHECK(igraph_stack_init(&stack, 0));
+    IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    *no = 0;
+
+    RNG_BEGIN();
+
+    for (father = 0; father < no_of_nodes; father++) {
+        long int level, s;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (VECTOR(*cut_prob)[0] == 1 ||
+            RNG_UNIF01() < VECTOR(*cut_prob)[0]) {
+            continue;
+        }
+
+        /* init G */
+        igraph_vector_clear(&vids); level = 0;
+        IGRAPH_CHECK(igraph_vector_push_back(&vids, father));
+        added[father] += 1; level += 1;
+
+        /* init V_E */
+        igraph_vector_clear(&adjverts);
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father,
+                                      IGRAPH_ALL));
+        s = igraph_vector_size(&neis);
+        for (i = 0; i < s; i++) {
+            long int nei = (long int) VECTOR(neis)[i];
+            if (!added[nei] && nei > father) {
+                IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei));
+                IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father));
+            }
+            added[nei] += 1;
+        }
+
+        /* init S */
+        igraph_stack_clear(&stack);
+
+        while (level > 1 || !igraph_vector_empty(&adjverts)) {
+            igraph_real_t cp = VECTOR(*cut_prob)[level];
+
+            if (level == size - 1) {
+                s = igraph_vector_size(&adjverts) / 2;
+                for (i = 0; i < s; i++) {
+                    if (cp != 0 && RNG_UNIF01() < cp) {
+                        continue;
+                    }
+                    (*no) += 1;
+                }
+            }
+
+            if (level < size - 1 &&
+                !igraph_vector_empty(&adjverts)) {
+                /* We might step down */
+                long int neifather = (long int) igraph_vector_pop_back(&adjverts);
+                long int nei = (long int) igraph_vector_pop_back(&adjverts);
+
+                if (cp == 0 || RNG_UNIF01() > cp) {
+                    /* Yes, step down */
+                    IGRAPH_CHECK(igraph_vector_push_back(&vids, nei));
+                    added[nei] += 1; level += 1;
+
+                    IGRAPH_CHECK(igraph_stack_push(&stack, neifather));
+                    IGRAPH_CHECK(igraph_stack_push(&stack, nei));
+                    IGRAPH_CHECK(igraph_stack_push(&stack, level));
+
+                    IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei,
+                                                  IGRAPH_ALL));
+                    s = igraph_vector_size(&neis);
+                    for (i = 0; i < s; i++) {
+                        long int nei2 = (long int) VECTOR(neis)[i];
+                        if (!added[nei2] && nei2 > father) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2));
+                            IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei));
+                        }
+                        added[nei2] += 1;
+                    }
+                }
+            } else {
+                /* no, step back */
+                long int nei, neifather;
+                while (!igraph_stack_empty(&stack) &&
+                       level == igraph_stack_top(&stack) - 1) {
+                    igraph_stack_pop(&stack);
+                    nei = (long int) igraph_stack_pop(&stack);
+                    neifather = (long int) igraph_stack_pop(&stack);
+                    igraph_vector_push_back(&adjverts, nei);
+                    igraph_vector_push_back(&adjverts, neifather);
+                }
+
+                nei = (long int) igraph_vector_pop_back(&vids);
+                added[nei] -= 1; level -= 1;
+                IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei,
+                                              IGRAPH_ALL));
+                s = igraph_vector_size(&neis);
+                for (i = 0; i < s; i++) {
+                    added[ (long int) VECTOR(neis)[i] ] -= 1;
+                }
+                while (!igraph_vector_empty(&adjverts) &&
+                       igraph_vector_tail(&adjverts) == nei) {
+                    igraph_vector_pop_back(&adjverts);
+                    igraph_vector_pop_back(&adjverts);
+                }
+            }
+
+        } /* while */
+
+        /* clear the added vector */
+        added[father] -= 1;
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father,
+                                      IGRAPH_ALL));
+        s = igraph_vector_size(&neis);
+        for (i = 0; i < s; i++) {
+            added[ (long int) VECTOR(neis)[i] ] -= 1;
+        }
+
+    } /* for father */
+
+    RNG_END();
+
+    igraph_Free(added);
+    igraph_vector_destroy(&vids);
+    igraph_vector_destroy(&adjverts);
+    igraph_stack_destroy(&stack);
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(5);
+    return 0;
+}
+
+/**
+ * \function igraph_dyad_census
+ * \brief Calculating the dyad census as defined by Holland and Leinhardt
+ *
+ * </para><para>
+ * Dyad census means classifying each pair of vertices of a directed
+ * graph into three categories: mutual, there is an edge from \c a to
+ * \c b and also from \c b to \c a; asymmetric, there is an edge
+ * either from \c a to \c b or from \c b to \c a but not the other way
+ * and null, no edges between \c a and \c b.
+ *
+ * </para><para>
+ * Holland, P.W. and Leinhardt, S.  (1970).  A Method for Detecting
+ * Structure in Sociometric Data.  American Journal of Sociology,
+ * 70, 492-513.
+ * \param graph The input graph, a warning is given if undirected as
+ *    the results are undefined for undirected graphs.
+ * \param mut Pointer to an integer, the number of mutual dyads is
+ *    stored here.
+ * \param asym Pointer to an integer, the number of asymmetric dyads
+ *    is stored here.
+ * \param null Pointer to an integer, the number of null dyads is
+ *    stored here. In case of an integer overflow (i.e. too many
+ *    null dyads), -1 will be returned.
+ * \return Error code.
+ *
+ * \sa \ref igraph_reciprocity(), \ref igraph_triad_census().
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges.
+ */
+
+int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut,
+                       igraph_integer_t *asym, igraph_integer_t *null) {
+
+    igraph_integer_t nonrec = 0, rec = 0;
+    igraph_vector_t inneis, outneis;
+    igraph_integer_t vc = igraph_vcount(graph);
+    long int i;
+
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_WARNING("Dyad census called on undirected graph");
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0);
+
+    for (i = 0; i < vc; i++) {
+        long int ip, op;
+        igraph_neighbors(graph, &inneis, i, IGRAPH_IN);
+        igraph_neighbors(graph, &outneis, i, IGRAPH_OUT);
+
+        ip = op = 0;
+        while (ip < igraph_vector_size(&inneis) &&
+               op < igraph_vector_size(&outneis)) {
+            if (VECTOR(inneis)[ip] < VECTOR(outneis)[op]) {
+                nonrec += 1;
+                ip++;
+            } else if (VECTOR(inneis)[ip] > VECTOR(outneis)[op]) {
+                nonrec += 1;
+                op++;
+            } else {
+                rec += 1;
+                ip++;
+                op++;
+            }
+        }
+        nonrec += (igraph_vector_size(&inneis) - ip) +
+                  (igraph_vector_size(&outneis) - op);
+    }
+
+    igraph_vector_destroy(&inneis);
+    igraph_vector_destroy(&outneis);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    *mut = rec / 2;
+    *asym = nonrec / 2;
+    if (vc % 2) {
+        *null = vc * ((vc - 1) / 2);
+    } else {
+        *null = (vc / 2) * (vc - 1);
+    }
+    if (*null < vc) {
+        IGRAPH_WARNING("Integer overflow, returning -1");
+        *null = -1;
+    } else {
+        *null = *null - (*mut) - (*asym);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_triad_census_24
+ * TODO
+ */
+
+int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2,
+                           igraph_real_t *res4) {
+
+    long int vc = igraph_vcount(graph);
+    igraph_vector_long_t seen;
+    igraph_vector_int_t *neis, *neis2;
+    long int i, j, k, s, neilen, neilen2, ign;
+    igraph_adjlist_t adjlist;
+
+    IGRAPH_CHECK(igraph_vector_long_init(&seen, vc));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &seen);
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+    *res2 = *res4 = 0;
+
+    for (i = 0; i < vc; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        neis = igraph_adjlist_get(&adjlist, i);
+        neilen = igraph_vector_int_size(neis);
+        /* mark neighbors of i & i itself */
+        VECTOR(seen)[i] = i + 1;
+        ign = 0;
+        for (j = 0; j < neilen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            if (VECTOR(seen)[nei] == i + 1 || VECTOR(seen)[nei] == -(i + 1)) {
+                /* multiple edges or loop edge */
+                VECTOR(seen)[nei] = -(i + 1);
+                ign++;
+            } else {
+                VECTOR(seen)[nei] = i + 1;
+            }
+        }
+
+        for (j = 0; j < neilen; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            if (nei <= i || (j > 0 && nei == VECTOR(*neis)[j - 1])) {
+                continue;
+            }
+            neis2 = igraph_adjlist_get(&adjlist, nei);
+            neilen2 = igraph_vector_int_size(neis2);
+            s = 0;
+            for (k = 0; k < neilen2; k++) {
+                long int nei2 = (long int) VECTOR(*neis2)[k];
+                if (k > 0 && nei2 == VECTOR(*neis2)[k - 1]) {
+                    continue;
+                }
+                if (VECTOR(seen)[nei2] != i + 1 && VECTOR(seen)[nei2] != -(i + 1)) {
+                    s++;
+                }
+            }
+            if (VECTOR(seen)[nei] > 0) {
+                *res2 += vc - s - neilen + ign - 1;
+            } else {
+                *res4 += vc - s - neilen + ign - 1;
+            }
+        }
+    }
+
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_long_destroy(&seen);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_triad_census
+ * \brief Triad census, as defined by Davis and Leinhardt
+ *
+ * </para><para>
+ * Calculating the triad census means classifying every triple of
+ * vertices in a directed graph. A triple can be in one of 16 states:
+ * \clist
+ * \cli 003
+ *      A, B, C, the empty graph.
+ * \cli 012
+ *      A->B, C, a graph with a single directed edge.
+ * \cli 102
+ *      A&lt;->B, C, a graph with a mutual connection between two vertices.
+ * \cli 021D
+ *      A&lt;-B->C, the binary out-tree.
+ * \cli 021U
+ *      A->B&lt;-C, the binary in-tree.
+ * \cli 021C
+ *      A->B->C, the directed line.
+ * \cli 111D
+ *      A&lt;->B&lt;-C.
+ * \cli 111U
+ *      A&lt;->B->C.
+ * \cli 030T
+ *      A->B&lt;-C, A->C.
+ * \cli 030C
+ *      A&lt;-B&lt;-C, A->C.
+ * \cli 201
+ *      A&lt;->B&lt;->C.
+ * \cli 120D
+ *      A&lt;-B->C, A&lt;->C.
+ * \cli 120U
+ *      A->B&lt;-C, A&lt;->C.
+ * \cli 120C
+ *      A->B->C, A&lt;->C.
+ * \cli 210
+ *      A->B&lt;->C, A&lt;->C.
+ * \cli 300
+ *      A&lt;->B&lt;->C, A&lt;->C, the complete graph.
+ * \endclist
+ *
+ * </para><para>
+ * See also Davis, J.A. and Leinhardt, S.  (1972).  The Structure of
+ * Positive Interpersonal Relations in Small Groups.  In J. Berger
+ * (Ed.), Sociological Theories in Progress, Volume 2, 218-251.
+ * Boston: Houghton Mifflin.
+ *
+ * </para><para>
+ * This function calls \ref igraph_motifs_randesu() which is an
+ * implementation of the FANMOD motif finder tool, see \ref
+ * igraph_motifs_randesu() for details. Note that the order of the
+ * triads is not the same for \ref igraph_triad_census() and \ref
+ * igraph_motifs_randesu().
+ *
+ * \param graph The input graph. A warning is given for undirected
+ *   graphs, as the result is undefined for those.
+ * \param res Pointer to an initialized vector, the result is stored
+ *   here in the same order as given in the list above. Note that this
+ *   order is different than the one used by \ref igraph_motifs_randesu().
+ * \return Error code.
+ *
+ * \sa \ref igraph_motifs_randesu(), \ref igraph_dyad_census().
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) {
+
+    igraph_vector_t cut_prob;
+    igraph_real_t m2, m4;
+    igraph_vector_t tmp;
+    igraph_integer_t vc = igraph_vcount(graph);
+    igraph_real_t total;
+
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_WARNING("Triad census called on an undirected graph");
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&cut_prob, 3); /* all zeros */
+    IGRAPH_CHECK(igraph_vector_resize(res, 16));
+    igraph_vector_null(res);
+    IGRAPH_CHECK(igraph_motifs_randesu(graph, &tmp, 3, &cut_prob));
+    IGRAPH_CHECK(igraph_triad_census_24(graph, &m2, &m4));
+
+    total = ((igraph_real_t)vc) * (vc - 1);
+    total *= (vc - 2);
+    total /= 6;
+
+    /* Reorder */
+    if (igraph_is_directed(graph)) {
+        VECTOR(tmp)[0] = 0;
+        VECTOR(tmp)[1] = m2;
+        VECTOR(tmp)[3] = m4;
+        VECTOR(tmp)[0] = total - igraph_vector_sum(&tmp);
+
+        VECTOR(*res)[0] = VECTOR(tmp)[0];
+        VECTOR(*res)[1] = VECTOR(tmp)[1];
+        VECTOR(*res)[2] = VECTOR(tmp)[3];
+        VECTOR(*res)[3] = VECTOR(tmp)[6];
+        VECTOR(*res)[4] = VECTOR(tmp)[2];
+        VECTOR(*res)[5] = VECTOR(tmp)[4];
+        VECTOR(*res)[6] = VECTOR(tmp)[5];
+        VECTOR(*res)[7] = VECTOR(tmp)[9];
+        VECTOR(*res)[8] = VECTOR(tmp)[7];
+        VECTOR(*res)[9] = VECTOR(tmp)[11];
+        VECTOR(*res)[10] = VECTOR(tmp)[10];
+        VECTOR(*res)[11] = VECTOR(tmp)[8];
+        VECTOR(*res)[12] = VECTOR(tmp)[13];
+        VECTOR(*res)[13] = VECTOR(tmp)[12];
+        VECTOR(*res)[14] = VECTOR(tmp)[14];
+        VECTOR(*res)[15] = VECTOR(tmp)[15];
+    } else {
+        VECTOR(tmp)[0] = 0;
+        VECTOR(tmp)[1] = m2;
+        VECTOR(tmp)[0] = total - igraph_vector_sum(&tmp);
+
+        VECTOR(*res)[0] = VECTOR(tmp)[0];
+        VECTOR(*res)[2] = VECTOR(tmp)[1];
+        VECTOR(*res)[10] = VECTOR(tmp)[2];
+        VECTOR(*res)[15] = VECTOR(tmp)[3];
+    }
+
+    igraph_vector_destroy(&cut_prob);
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
diff --git a/igraph/src/open.c b/igraph/src/open.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/open.c
@@ -0,0 +1,301 @@
+#include "f2c.h"
+#include "fio.h"
+#include "string.h"
+#ifndef NON_POSIX_STDIO
+#ifdef MSDOS
+#include "io.h"
+#else
+#include "unistd.h"	/* for access */
+#endif
+#endif
+
+#ifdef KR_headers
+extern char *malloc();
+#ifdef NON_ANSI_STDIO
+extern char *mktemp();
+#endif
+extern integer f_clos();
+#define Const /*nothing*/
+#else
+#define Const const
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int f__canseek(FILE*);
+extern integer f_clos(cllist*);
+#endif
+
+#ifdef NON_ANSI_RW_MODES
+Const char *f__r_mode[2] = {"r", "r"};
+Const char *f__w_mode[4] = {"w", "w", "r+w", "r+w"};
+#else
+Const char *f__r_mode[2] = {"rb", "r"};
+Const char *f__w_mode[4] = {"wb", "w", "r+b", "r+"};
+#endif
+
+ static char f__buf0[400], *f__buf = f__buf0;
+ int f__buflen = (int)sizeof(f__buf0);
+
+ static void
+#ifdef KR_headers
+f__bufadj(n, c) int n, c;
+#else
+f__bufadj(int n, int c)
+#endif
+{
+	unsigned int len;
+	char *nbuf, *s, *t, *te;
+
+	if (f__buf == f__buf0)
+		f__buflen = 1024;
+	while(f__buflen <= n)
+		f__buflen <<= 1;
+	len = (unsigned int)f__buflen;
+	if (len != f__buflen || !(nbuf = (char*)malloc(len)))
+		f__fatal(113, "malloc failure");
+	s = nbuf;
+	t = f__buf;
+	te = t + c;
+	while(t < te)
+		*s++ = *t++;
+	if (f__buf != f__buf0)
+		free(f__buf);
+	f__buf = nbuf;
+	}
+
+ int
+#ifdef KR_headers
+f__putbuf(c) int c;
+#else
+f__putbuf(int c)
+#endif
+{
+	char *s, *se;
+	int n;
+
+	if (f__hiwater > f__recpos)
+		f__recpos = f__hiwater;
+	n = f__recpos + 1;
+	if (n >= f__buflen)
+		f__bufadj(n, f__recpos);
+	s = f__buf;
+	se = s + f__recpos;
+	if (c)
+		*se++ = c;
+	*se = 0;
+	for(;;) {
+		fputs(s, f__cf);
+		s += strlen(s);
+		if (s >= se)
+			break;	/* normally happens the first time */
+		putc(*s++, f__cf);
+		}
+	return 0;
+	}
+
+ void
+#ifdef KR_headers
+x_putc(c)
+#else
+x_putc(int c)
+#endif
+{
+	if (f__recpos >= f__buflen)
+		f__bufadj(f__recpos, f__buflen);
+	f__buf[f__recpos++] = c;
+	}
+
+#define opnerr(f,m,s) {if(f) errno= m; else opn_err(m,s,a); return(m);}
+
+ static void
+#ifdef KR_headers
+opn_err(m, s, a) int m; char *s; olist *a;
+#else
+opn_err(int m, const char *s, olist *a)
+#endif
+{
+	if (a->ofnm) {
+		/* supply file name to error message */
+		if (a->ofnmlen >= f__buflen)
+			f__bufadj((int)a->ofnmlen, 0);
+		g_char(a->ofnm, a->ofnmlen, f__curunit->ufnm = f__buf);
+		}
+	f__fatal(m, s);
+	}
+
+#ifdef KR_headers
+integer f_open(a) olist *a;
+#else
+integer f_open(olist *a)
+#endif
+{	unit *b;
+	integer rv;
+	char buf[256], *s;
+	cllist x;
+	int ufmt;
+	FILE *tf;
+#ifndef NON_UNIX_STDIO
+	int n;
+#endif
+	f__external = 1;
+	if(a->ounit>=MXUNIT || a->ounit<0)
+		err(a->oerr,101,"open")
+	if (!f__init)
+		f_init();
+	f__curunit = b = &f__units[a->ounit];
+	if(b->ufd) {
+		if(a->ofnm==0)
+		{
+		same:	if (a->oblnk)
+				b->ublnk = *a->oblnk == 'z' || *a->oblnk == 'Z';
+			return(0);
+		}
+#ifdef NON_UNIX_STDIO
+		if (b->ufnm
+		 && strlen(b->ufnm) == a->ofnmlen
+		 && !strncmp(b->ufnm, a->ofnm, (unsigned)a->ofnmlen))
+			goto same;
+#else
+		g_char(a->ofnm,a->ofnmlen,buf);
+		if (f__inode(buf,&n) == b->uinode && n == b->udev)
+			goto same;
+#endif
+		x.cunit=a->ounit;
+		x.csta=0;
+		x.cerr=a->oerr;
+		if ((rv = f_clos(&x)) != 0)
+			return rv;
+		}
+	b->url = (int)a->orl;
+	b->ublnk = a->oblnk && (*a->oblnk == 'z' || *a->oblnk == 'Z');
+	if(a->ofm==0)
+	{	if(b->url>0) b->ufmt=0;
+		else b->ufmt=1;
+	}
+	else if(*a->ofm=='f' || *a->ofm == 'F') b->ufmt=1;
+	else b->ufmt=0;
+	ufmt = b->ufmt;
+#ifdef url_Adjust
+	if (b->url && !ufmt)
+		url_Adjust(b->url);
+#endif
+	if (a->ofnm) {
+		g_char(a->ofnm,a->ofnmlen,buf);
+		if (!buf[0])
+			opnerr(a->oerr,107,"open")
+		}
+	else
+		sprintf(buf, "fort.%ld", (long)a->ounit);
+	b->uscrtch = 0;
+	b->uend=0;
+	b->uwrt = 0;
+	b->ufd = 0;
+	b->urw = 3;
+	switch(a->osta ? *a->osta : 'u')
+	{
+	case 'o':
+	case 'O':
+#ifdef NON_POSIX_STDIO
+		if (!(tf = FOPEN(buf,"r")))
+			opnerr(a->oerr,errno,"open")
+		fclose(tf);
+#else
+		if (access(buf,0))
+			opnerr(a->oerr,errno,"open")
+#endif
+		break;
+	 case 's':
+	 case 'S':
+		b->uscrtch=1;
+#ifdef NON_ANSI_STDIO
+		(void) strcpy(buf,"tmp.FXXXXXX");
+		(void) mktemp(buf);
+		goto replace;
+#else
+		if (!(b->ufd = tmpfile()))
+			opnerr(a->oerr,errno,"open")
+		b->ufnm = 0;
+#ifndef NON_UNIX_STDIO
+		b->uinode = b->udev = -1;
+#endif
+		b->useek = 1;
+		return 0;
+#endif
+
+	case 'n':
+	case 'N':
+#ifdef NON_POSIX_STDIO
+		if ((tf = FOPEN(buf,"r")) || (tf = FOPEN(buf,"a"))) {
+			fclose(tf);
+			opnerr(a->oerr,128,"open")
+			}
+#else
+		if (!access(buf,0))
+			opnerr(a->oerr,128,"open")
+#endif
+		/* no break */
+	case 'r':	/* Fortran 90 replace option */
+	case 'R':
+#ifdef NON_ANSI_STDIO
+ replace:
+#endif
+		if (tf = FOPEN(buf,f__w_mode[0]))
+			fclose(tf);
+	}
+
+	b->ufnm=(char *) malloc((unsigned int)(strlen(buf)+1));
+	if(b->ufnm==NULL) opnerr(a->oerr,113,"no space");
+	(void) strcpy(b->ufnm,buf);
+	if ((s = a->oacc) && b->url)
+		ufmt = 0;
+	if(!(tf = FOPEN(buf, f__w_mode[ufmt|2]))) {
+		if (tf = FOPEN(buf, f__r_mode[ufmt]))
+			b->urw = 1;
+		else if (tf = FOPEN(buf, f__w_mode[ufmt])) {
+			b->uwrt = 1;
+			b->urw = 2;
+			}
+		else
+			err(a->oerr, errno, "open");
+		}
+	b->useek = f__canseek(b->ufd = tf);
+#ifndef NON_UNIX_STDIO
+	if((b->uinode = f__inode(buf,&b->udev)) == -1)
+		opnerr(a->oerr,108,"open")
+#endif
+	if(b->useek)
+		if (a->orl)
+			rewind(b->ufd);
+		else if ((s = a->oacc) && (*s == 'a' || *s == 'A')
+			&& FSEEK(b->ufd, 0L, SEEK_END))
+				opnerr(a->oerr,129,"open");
+	return(0);
+}
+
+ int
+#ifdef KR_headers
+fk_open(seq,fmt,n) ftnint n;
+#else
+fk_open(int seq, int fmt, ftnint n)
+#endif
+{	char nbuf[10];
+	olist a;
+	(void) sprintf(nbuf,"fort.%ld",(long)n);
+	a.oerr=1;
+	a.ounit=n;
+	a.ofnm=nbuf;
+	a.ofnmlen=strlen(nbuf);
+	a.osta=NULL;
+	a.oacc= (char*)(seq==SEQ?"s":"d");
+	a.ofm = (char*)(fmt==FMT?"f":"u");
+	a.orl = seq==DIR?1:0;
+	a.oblnk=NULL;
+	return(f_open(&a));
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/operators.c b/igraph/src/operators.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/operators.c
@@ -0,0 +1,1243 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_operators.h"
+#include "igraph_error.h"
+#include "igraph_memory.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_interface.h"
+#include "igraph_constructors.h"
+#include "igraph_adjlist.h"
+#include "igraph_attributes.h"
+#include "igraph_conversion.h"
+#include "igraph_qsort.h"
+#include <limits.h>
+#include "config.h"
+
+/**
+ * \function igraph_disjoint_union
+ * \brief Creates the union of two disjoint graphs
+ *
+ * </para><para>
+ * First the vertices of the second graph will be relabeled with new
+ * vertex ids to have two disjoint sets of vertex ids, then the union
+ * of the two graphs will be formed.
+ * If the two graphs have |V1| and |V2| vertices and |E1| and |E2|
+ * edges respectively then the new graph will have |V1|+|V2| vertices
+ * and |E1|+|E2| edges.
+ *
+ * </para><para>
+ * Both graphs need to have the same directedness, ie. either both
+ * directed or both undirected.
+ *
+ * </para><para>
+ * The current version of this function cannot handle graph, vertex
+ * and edge attributes, they will be lost.
+ *
+ * \param res  Pointer to an uninitialized graph object, the result
+ *        will stored here.
+ * \param left The first graph.
+ * \param right The second graph.
+ * \return Error code.
+ * \sa \ref igraph_disjoint_union_many() for creating the disjoint union
+ * of more than two graphs, \ref igraph_union() for non-disjoint
+ * union.
+ *
+ * Time complexity: O(|V1|+|V2|+|E1|+|E2|).
+ *
+ * \example examples/simple/igraph_disjoint_union.c
+ */
+
+int igraph_disjoint_union(igraph_t *res, const igraph_t *left,
+                          const igraph_t *right) {
+
+    long int no_of_nodes_left = igraph_vcount(left);
+    long int no_of_nodes_right = igraph_vcount(right);
+    long int no_of_edges_left = igraph_ecount(left);
+    long int no_of_edges_right = igraph_ecount(right);
+    igraph_vector_t edges;
+    igraph_bool_t directed_left = igraph_is_directed(left);
+    igraph_integer_t from, to;
+    long int i;
+
+    if (directed_left != igraph_is_directed(right)) {
+        IGRAPH_ERROR("Cannot union directed and undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges,
+                                       2 * (no_of_edges_left + no_of_edges_right)));
+    for (i = 0; i < no_of_edges_left; i++) {
+        igraph_edge(left, (igraph_integer_t) i, &from, &to);
+        igraph_vector_push_back(&edges, from);
+        igraph_vector_push_back(&edges, to);
+    }
+    for (i = 0; i < no_of_edges_right; i++) {
+        igraph_edge(right, (igraph_integer_t) i, &from, &to);
+        igraph_vector_push_back(&edges, from + no_of_nodes_left);
+        igraph_vector_push_back(&edges, to + no_of_nodes_left);
+    }
+
+    IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t)
+                               (no_of_nodes_left + no_of_nodes_right),
+                               directed_left));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_disjoint_union_many
+ * \brief The disjint union of many graphs.
+ *
+ * </para><para>
+ * First the vertices in the graphs will be relabeled with new vertex
+ * ids to have pairwise disjoint vertex id sets and then the union of
+ * the graphs is formed.
+ * The number of vertices and edges in the result is the total number
+ * of vertices and edges in the graphs.
+ *
+ * </para><para>
+ * Both graphs need to have the same directedness, ie. either both
+ * directed or both undirected.
+ *
+ * </para><para>
+ * The current version of this function cannot handle graph, vertex
+ * and edge attributes, they will be lost.
+ *
+ * \param res Pointer to an uninitialized graph object, the result of
+ *        the operation will be stored here.
+ * \param graphs Pointer vector, contains pointers to initialized
+ *        graph objects.
+ * \return Error code.
+ * \sa \ref igraph_disjoint_union() for an easier syntax if you have
+ * only two graphs, \ref igraph_union_many() for non-disjoint union.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges in the result.
+ */
+
+int igraph_disjoint_union_many(igraph_t *res,
+                               const igraph_vector_ptr_t *graphs) {
+    long int no_of_graphs = igraph_vector_ptr_size(graphs);
+    igraph_bool_t directed = 1;
+    igraph_vector_t edges;
+    long int no_of_edges = 0;
+    long int shift = 0;
+    igraph_t *graph;
+    long int i, j;
+    igraph_integer_t from, to;
+
+    if (no_of_graphs != 0) {
+        graph = VECTOR(*graphs)[0];
+        directed = igraph_is_directed(graph);
+        for (i = 0; i < no_of_graphs; i++) {
+            graph = VECTOR(*graphs)[i];
+            no_of_edges += igraph_ecount(graph);
+            if (directed != igraph_is_directed(graph)) {
+                IGRAPH_ERROR("Cannot union directed and undirected graphs",
+                             IGRAPH_EINVAL);
+            }
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * no_of_edges));
+
+    for (i = 0; i < no_of_graphs; i++) {
+        long int ec;
+        graph = VECTOR(*graphs)[i];
+        ec = igraph_ecount(graph);
+        for (j = 0; j < ec; j++) {
+            igraph_edge(graph, (igraph_integer_t) j, &from, &to);
+            igraph_vector_push_back(&edges, from + shift);
+            igraph_vector_push_back(&edges, to + shift);
+        }
+        shift += igraph_vcount(graph);
+    }
+
+    IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) shift, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_i_order_edgelist_cmp(void *edges, const void *e1,
+                                const void *e2) {
+    igraph_vector_t *edgelist = edges;
+    long int edge1 = (*(const long int*) e1) * 2;
+    long int edge2 = (*(const long int*) e2) * 2;
+    long int from1 = VECTOR(*edgelist)[edge1];
+    long int from2 = VECTOR(*edgelist)[edge2];
+    if (from1 < from2) {
+        return -1;
+    } else if (from1 > from2) {
+        return 1;
+    } else {
+        long int to1 = VECTOR(*edgelist)[edge1 + 1];
+        long int to2 = VECTOR(*edgelist)[edge2 + 1];
+        if (to1 < to2) {
+            return -1;
+        } else if (to1 > to2) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+}
+
+#define IGRAPH_MODE_UNION        1
+#define IGRAPH_MODE_INTERSECTION 2
+
+int igraph_i_merge(igraph_t *res, int mode,
+                   const igraph_t *left, const igraph_t *right,
+                   igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) {
+
+    long int no_of_nodes_left = igraph_vcount(left);
+    long int no_of_nodes_right = igraph_vcount(right);
+    long int no_of_nodes;
+    long int no_edges_left = igraph_ecount(left);
+    long int no_edges_right = igraph_ecount(right);
+    igraph_bool_t directed = igraph_is_directed(left);
+    igraph_vector_t edges;
+    igraph_vector_t edges1, edges2;
+    igraph_vector_long_t order1, order2;
+    long int i, j, eptr = 0;
+    long int idx1, idx2, edge1 = -1, edge2 = -1, from1 = -1, from2 = -1, to1 = -1, to2 = -1;
+    igraph_bool_t l;
+
+    if (directed != igraph_is_directed(right)) {
+        IGRAPH_ERROR("Cannot make union or intersection of directed "
+                     "and undirected graph", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges1, no_edges_left * 2);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges2, no_edges_right * 2);
+    IGRAPH_CHECK(igraph_vector_long_init(&order1, no_edges_left));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &order1);
+    IGRAPH_CHECK(igraph_vector_long_init(&order2, no_edges_right));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &order2);
+
+    if (edge_map1) {
+        switch (mode) {
+        case IGRAPH_MODE_UNION:
+            IGRAPH_CHECK(igraph_vector_resize(edge_map1, no_edges_left));
+            break;
+        case IGRAPH_MODE_INTERSECTION:
+            igraph_vector_clear(edge_map1);
+            break;
+        }
+    }
+    if (edge_map2) {
+        switch (mode) {
+        case IGRAPH_MODE_UNION:
+            IGRAPH_CHECK(igraph_vector_resize(edge_map2, no_edges_right));
+            break;
+        case IGRAPH_MODE_INTERSECTION:
+            igraph_vector_clear(edge_map2);
+            break;
+        }
+    }
+
+    no_of_nodes = no_of_nodes_left > no_of_nodes_right ?
+                  no_of_nodes_left : no_of_nodes_right;
+
+    /* We merge the two edge lists. We need to sort them first.
+       For undirected graphs, we also need to make sure that
+       for every edge, that larger (non-smaller) vertex id is in the
+       second column. */
+
+    IGRAPH_CHECK(igraph_get_edgelist(left, &edges1, /*bycol=*/ 0));
+    IGRAPH_CHECK(igraph_get_edgelist(right, &edges2, /*bycol=*/ 0));
+    if (!directed) {
+        for (i = 0, j = 0; i < no_edges_left; i++, j += 2) {
+            if (VECTOR(edges1)[j] > VECTOR(edges1)[j + 1]) {
+                long int tmp = VECTOR(edges1)[j];
+                VECTOR(edges1)[j] = VECTOR(edges1)[j + 1];
+                VECTOR(edges1)[j + 1] = tmp;
+            }
+        }
+        for (i = 0, j = 0; i < no_edges_right; i++, j += 2) {
+            if (VECTOR(edges2)[j] > VECTOR(edges2)[j + 1]) {
+                long int tmp = VECTOR(edges2)[j];
+                VECTOR(edges2)[j] = VECTOR(edges2)[j + 1];
+                VECTOR(edges2)[j + 1] = tmp;
+            }
+        }
+    }
+
+    for (i = 0; i < no_edges_left; i++) {
+        VECTOR(order1)[i] = i;
+    }
+    for (i = 0; i < no_edges_right; i++) {
+        VECTOR(order2)[i] = i;
+    }
+
+    igraph_qsort_r(VECTOR(order1), no_edges_left, sizeof(VECTOR(order1)[0]),
+                   &edges1, igraph_i_order_edgelist_cmp);
+    igraph_qsort_r(VECTOR(order2), no_edges_right, sizeof(VECTOR(order2)[0]),
+                   &edges2, igraph_i_order_edgelist_cmp);
+
+#define INC1() if ( (++idx1) < no_edges_left) {          \
+        edge1 = VECTOR(order1)[idx1];                \
+        from1 = VECTOR(edges1)[2*edge1];                 \
+        to1 = VECTOR(edges1)[2*edge1+1];                 \
+    }
+#define INC2() if ( (++idx2) < no_edges_right) {         \
+        edge2 = VECTOR(order2)[idx2];                \
+        from2 = VECTOR(edges2)[2*edge2];                 \
+        to2 = VECTOR(edges2)[2*edge2+1];                 \
+    }
+
+    idx1 = idx2 = -1;
+    INC1();
+    INC2();
+
+#define CONT() switch (mode) {              \
+    case IGRAPH_MODE_UNION:                \
+        l = idx1 < no_edges_left || idx2 < no_edges_right;   \
+        break;                       \
+    case IGRAPH_MODE_INTERSECTION:             \
+        l = idx1 < no_edges_left && idx2 < no_edges_right;   \
+        break;                       \
+    }
+
+    CONT();
+    while (l) {
+        if (idx2 >= no_edges_right ||
+            (idx1 < no_edges_left && from1 < from2) ||
+            (idx1 < no_edges_left && from1 == from2 && to1 < to2)) {
+            /* Edge from first graph */
+            if (mode == IGRAPH_MODE_UNION) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, from1));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, to1));
+                if (edge_map1) {
+                    VECTOR(*edge_map1)[edge1] = eptr;
+                }
+                eptr++;
+            }
+            INC1();
+        } else if (idx1 >= no_edges_left ||
+                   (idx2 < no_edges_right && from2 < from1) ||
+                   (idx2 < no_edges_right && from1 == from2 && to2 < to1)) {
+            /* Edge from second graph */
+            if (mode == IGRAPH_MODE_UNION) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, from2));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, to2));
+                if (edge_map2) {
+                    VECTOR(*edge_map2)[edge2] = eptr;
+                }
+                eptr++;
+            }
+            INC2();
+        } else {
+            /* Edge from both */
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, from1));
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, to1));
+            if (mode == IGRAPH_MODE_UNION) {
+                if (edge_map1) {
+                    VECTOR(*edge_map1)[edge1] = eptr;
+                }
+                if (edge_map2) {
+                    VECTOR(*edge_map2)[edge2] = eptr;
+                }
+            } else if (mode == IGRAPH_MODE_INTERSECTION) {
+                if (edge_map1) {
+                    IGRAPH_CHECK(igraph_vector_push_back(edge_map1, edge1));
+                }
+                if (edge_map2) {
+                    IGRAPH_CHECK(igraph_vector_push_back(edge_map2, edge2));
+                }
+            }
+            eptr++;
+            INC1();
+            INC2();
+        }
+        CONT();
+    }
+
+#undef INC1
+#undef INC2
+
+    igraph_vector_long_destroy(&order2);
+    igraph_vector_long_destroy(&order1);
+    igraph_vector_destroy(&edges2);
+    igraph_vector_destroy(&edges1);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_intersection
+ * \brief Collect the common edges from two graphs.
+ *
+ * </para><para>
+ * The result graph contains only edges present both in the first and
+ * the second graph. The number of vertices in the result graph is the
+ * same as the larger from the two arguments.
+ *
+ * \param res Pointer to an uninitialized graph object. This will
+ * contain the result of the operation.
+ * \param left The first operand, a graph object.
+ * \param right The second operand, a graph object.
+ * \param edge_map1 Null pointer, or an initialized \type igraph_vector_t.
+ *    If the latter, then a mapping from the edges of the result graph, to
+ *    the edges of the \p left input graph is stored here.
+ * \param edge_map2 Null pointer, or an \type igraph_vector_t. The same
+ *    as \p edge_map1, but for the \p right input graph.
+ * \return Error code.
+ * \sa \ref igraph_intersection_many() to calculate the intersection
+ * of many graphs at once, \ref igraph_union(), \ref
+ * igraph_difference() for other operators.
+ *
+ * Time complexity: O(|V|+|E|), |V| is the number of nodes, |E|
+ * is the number of edges in the smaller graph of the two. (The one
+ * containing less vertices is considered smaller.)
+ *
+ * \example examples/simple/igraph_intersection.c
+ */
+
+int igraph_intersection(igraph_t *res,
+                        const igraph_t *left, const igraph_t *right,
+                        igraph_vector_t *edge_map1,
+                        igraph_vector_t *edge_map2) {
+    return igraph_i_merge(res, IGRAPH_MODE_INTERSECTION, left, right,
+                          edge_map1, edge_map2);
+}
+
+void igraph_i_union_many_free(igraph_vector_ptr_t *v) {
+    long int i, n = igraph_vector_ptr_size(v);
+    for (i = 0; i < n; i++) {
+        if (VECTOR(*v)[i] != 0) {
+            igraph_vector_destroy(VECTOR(*v)[i]);
+            igraph_Free(VECTOR(*v)[i]);
+        }
+    }
+    igraph_vector_ptr_destroy(v);
+}
+
+void igraph_i_union_many_free2(igraph_vector_ptr_t *v) {
+    long int i, n = igraph_vector_ptr_size(v);
+    for (i = 0; i < n; i++) {
+        if (VECTOR(*v)[i] != 0) {
+            igraph_vector_long_destroy(VECTOR(*v)[i]);
+            igraph_Free(VECTOR(*v)[i]);
+        }
+    }
+    igraph_vector_ptr_destroy(v);
+}
+
+void igraph_i_union_many_free3(igraph_vector_ptr_t *v) {
+    long int i, n = igraph_vector_ptr_size(v);
+    for (i = 0; i < n; i++) {
+        if (VECTOR(*v)[i] != 0) {
+            igraph_vector_destroy(VECTOR(*v)[i]);
+            igraph_Free(VECTOR(*v)[i]);
+        }
+    }
+}
+
+/**
+ * \function igraph_intersection_many
+ * \brief The intersection of more than two graphs.
+ *
+ * </para><para>
+ * This function calculates the intersection of the graphs stored in
+ * the \c graphs argument. Only those edges will be included in the
+ * result graph which are part of every graph in \c graphs.
+ *
+ * </para><para>
+ * The number of vertices in the result graph will be the maximum
+ * number of vertices in the argument graphs.
+ *
+ * \param res Pointer to an uninitialized graph object, the result of
+ *        the operation will be stored here.
+ * \param graphs Pointer vector, contains pointers to graphs objects,
+ *        the operands of the intersection operator.
+ * \param edgemaps If not a null pointer, then it must be an initialized
+ *        pointer vector and the mappings of edges from the graphs to the
+ *        result graph will be stored here, in the same order as
+ *        \p graphs. Each mapping is stored in a separate
+ *        \type igraph_vector_t object. For the edges that are not in
+ *        the intersection, -1 is stored.
+ * \return Error code.
+ * \sa \ref igraph_intersection() for the intersection of two graphs,
+ * \ref igraph_union_many(), \ref igraph_union() and \ref
+ * igraph_difference() for other operators.
+ *
+ * Time complexity: O(|V|+|E|), |V| is the number of vertices,
+ * |E| is the number of edges in the smallest graph (ie. the graph having
+ * the less vertices).
+ */
+
+int igraph_intersection_many(igraph_t *res,
+                             const igraph_vector_ptr_t *graphs,
+                             igraph_vector_ptr_t *edgemaps) {
+
+    long int no_of_graphs = igraph_vector_ptr_size(graphs);
+    long int no_of_nodes = 0;
+    igraph_bool_t directed = 1;
+    igraph_vector_t edges;
+    igraph_vector_ptr_t edge_vects, order_vects;
+    long int i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1;
+    igraph_vector_long_t no_edges;
+    igraph_bool_t allne = no_of_graphs == 0 ? 0 : 1, allsame = 0;
+    long int idx = 0;
+
+    /* Check directedness */
+    if (no_of_graphs != 0) {
+        directed = igraph_is_directed(VECTOR(*graphs)[0]);
+    }
+    for (i = 1; i < no_of_graphs; i++) {
+        if (directed != igraph_is_directed(VECTOR(*graphs)[i])) {
+            IGRAPH_ERROR("Cannot intersect directed and undirected graphs",
+                         IGRAPH_EINVAL);
+        }
+    }
+
+    if (edgemaps) {
+        IGRAPH_CHECK(igraph_vector_ptr_resize(edgemaps, no_of_graphs));
+        igraph_vector_ptr_null(edgemaps);
+        IGRAPH_FINALLY(igraph_i_union_many_free3, edgemaps);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_long_init(&no_edges, no_of_graphs));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &no_edges);
+
+    /* Calculate number of nodes, query number of edges */
+    for (i = 0; i < no_of_graphs; i++) {
+        long int n = igraph_vcount(VECTOR(*graphs)[i]);
+        if (n > no_of_nodes) {
+            no_of_nodes = n;
+        }
+        VECTOR(no_edges)[i] = igraph_ecount(VECTOR(*graphs)[i]);
+        allne = allne && VECTOR(no_edges)[i] > 0;
+    }
+
+    if (edgemaps) {
+        for (i = 0; i < no_of_graphs; i++) {
+            VECTOR(*edgemaps)[i] = igraph_Calloc(1, igraph_vector_t);
+            if (!VECTOR(*edgemaps)[i]) {
+                IGRAPH_ERROR("Cannot intersect graphs", IGRAPH_ENOMEM);
+            }
+            IGRAPH_CHECK(igraph_vector_init(VECTOR(*edgemaps)[i],
+                                            VECTOR(no_edges)[i]));
+            igraph_vector_fill(VECTOR(*edgemaps)[i], -1);
+        }
+    }
+
+    /* Allocate memory for the edge lists and their index vectors */
+    if (no_of_graphs != 0) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&edge_vects, no_of_graphs));
+        IGRAPH_FINALLY(igraph_i_union_many_free, &edge_vects);
+        IGRAPH_CHECK(igraph_vector_ptr_init(&order_vects, no_of_graphs));
+        IGRAPH_FINALLY(igraph_i_union_many_free2, &order_vects);
+    }
+    for (i = 0; i < no_of_graphs; i++) {
+        VECTOR(edge_vects)[i] = igraph_Calloc(1, igraph_vector_t);
+        VECTOR(order_vects)[i] = igraph_Calloc(1, igraph_vector_long_t);
+        if (! VECTOR(edge_vects)[i] || ! VECTOR(order_vects)[i]) {
+            IGRAPH_ERROR("Cannot intersect graphs", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_init(VECTOR(edge_vects)[i],
+                                        2 * VECTOR(no_edges)[i]));
+        IGRAPH_CHECK(igraph_vector_long_init(VECTOR(order_vects)[i],
+                                             VECTOR(no_edges)[i]));
+    }
+
+    /* Query and sort the edge lists */
+    for (i = 0; i < no_of_graphs; i++) {
+        long int k, j, n = VECTOR(no_edges)[i];
+        igraph_vector_t *edges = VECTOR(edge_vects)[i];
+        igraph_vector_long_t *order = VECTOR(order_vects)[i];
+        IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], edges, /*bycol=*/0));
+        if (!directed) {
+            for (k = 0, j = 0; k < n; k++, j += 2) {
+                if (VECTOR(*edges)[j] > VECTOR(*edges)[j + 1]) {
+                    long int tmp = VECTOR(*edges)[j];
+                    VECTOR(*edges)[j] = VECTOR(*edges)[j + 1];
+                    VECTOR(*edges)[j + 1] = tmp;
+                }
+            }
+        }
+        for (k = 0; k < n; k++) {
+            VECTOR(*order)[k] = k;
+        }
+        igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), edges,
+                       igraph_i_order_edgelist_cmp);
+    }
+
+    /* Do the merge. We work from the end of the edge lists,
+       because then we don't have to keep track of where we are right
+       now in the edge and order lists. We find the "largest" edge,
+       and if it is present in all graphs, then we copy it to the
+       result. We remove all instances of this edge.  */
+
+    while (allne) {
+
+        /* Look for the smallest tail element */
+        for (j = 0, tailfrom = LONG_MAX, tailto = LONG_MAX; j < no_of_graphs; j++) {
+            long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]);
+            igraph_vector_t *ev = VECTOR(edge_vects)[j];
+            long int from = VECTOR(*ev)[2 * edge];
+            long int to = VECTOR(*ev)[2 * edge + 1];
+            if (from < tailfrom || (from == tailfrom && to < tailto)) {
+                tailfrom = from; tailto = to;
+            }
+        }
+
+        /* OK, now remove all elements from the tail(s) that are bigger
+           than the smallest tail element. */
+        for (j = 0, allsame = 1; j < no_of_graphs; j++) {
+            long int from = -1, to = -1;
+            while (1) {
+                long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]);
+                igraph_vector_t *ev = VECTOR(edge_vects)[j];
+                from = VECTOR(*ev)[2 * edge];
+                to = VECTOR(*ev)[2 * edge + 1];
+                if (from > tailfrom || (from == tailfrom && to > tailto)) {
+                    igraph_vector_long_pop_back(VECTOR(order_vects)[j]);
+                    if (igraph_vector_long_empty(VECTOR(order_vects)[j])) {
+                        allne = 0;
+                        break;
+                    }
+                } else {
+                    break;
+                }
+            }
+            if (from != tailfrom || to != tailto) {
+                allsame = 0;
+            }
+        }
+
+        /* Add the edge, if the smallest tail element was present
+           in all graphs. */
+        if (allsame) {
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, tailfrom));
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, tailto));
+        }
+
+        /* Drop edges matching the smalles tail elements
+           from the order vectors, build edge maps */
+        if (allne) {
+            for (j = 0; j < no_of_graphs; j++) {
+                long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]);
+                igraph_vector_t *ev = VECTOR(edge_vects)[j];
+                long int from = VECTOR(*ev)[2 * edge];
+                long int to = VECTOR(*ev)[2 * edge + 1];
+                if (from == tailfrom && to == tailto) {
+                    igraph_vector_long_pop_back(VECTOR(order_vects)[j]);
+                    if (igraph_vector_long_empty(VECTOR(order_vects)[j])) {
+                        allne = 0;
+                    }
+                    if (edgemaps && allsame) {
+                        igraph_vector_t *map = VECTOR(*edgemaps)[j];
+                        VECTOR(*map)[edge] = idx;
+                    }
+                }
+            }
+            if (allsame) {
+                idx++;
+            }
+        }
+
+    } /* while allne */
+
+    if (no_of_graphs > 0) {
+        igraph_i_union_many_free2(&order_vects);
+        igraph_i_union_many_free(&edge_vects);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_vector_long_destroy(&no_edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    if (edgemaps) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_union
+ * \brief Calculates the union of two graphs.
+ *
+ * </para><para>
+ * The number of vertices in the result is that of the larger graph
+ * from the two arguments. The result graph contains edges which are
+ * present in at least one of the operand graphs.
+ *
+ * \param res Pointer to an uninitialized graph object, the result
+ *        will be stored here.
+ * \param left The first graph.
+ * \param right The second graph.
+ * \param edge_map1 Pointer to an initialized vector or a null pointer.
+ *     If not a null pointer, it will contain a mapping from the edges
+ *     of the first argument graph (\p left) to the edges of the
+ *     result graph.
+ * \param edge_map2 The same as \p edge_map1, but for the second
+ *     graph, \p right.
+ * \return Error code.
+ * \sa \ref igraph_union_many() for the union of many graphs,
+ * \ref igraph_intersection() and \ref igraph_difference() for other
+ * operators.
+ *
+ * Time complexity: O(|V|+|E|), |V| is the number of
+ * vertices, |E| the number of edges in the result graph.
+ *
+ * \example examples/simple/igraph_union.c
+ */
+
+int igraph_union(igraph_t *res,
+                 const igraph_t *left, const igraph_t *right,
+                 igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) {
+    return igraph_i_merge(res, IGRAPH_MODE_UNION, left, right,
+                          edge_map1, edge_map2);
+}
+
+/**
+ * \function igraph_union_many
+ * \brief Creates the union of many graphs.
+ *
+ * </para><para>
+ * The result graph will contain as many vertices as the largest graph
+ * among the arguments does, and an edge will be included in it if it
+ * is part of at least one operand graph.
+ *
+ * </para><para>
+ * The directedness of the operand graphs must be the same.
+ *
+ * \param res Pointer to an uninitialized graph object, this will
+ *        contain the result.
+ * \param graphs Pointer vector, contains pointers to the operands of
+ *        the union operator, graph objects of course.
+ * \param edgemaps If not a null pointer, then it must be an initialized
+ *        pointer vector and the mappings of edges from the graphs to the
+ *        result graph will be stored here, in the same order as
+ *        \p graphs. Each mapping is stored in a separate
+ *        \type igraph_vector_t object.
+ * \return Error code.
+ * \sa \ref igraph_union() for the union of two graphs, \ref
+ * igraph_intersection_many(), \ref igraph_intersection() and \ref
+ * igraph_difference for other operators.
+ *
+ *
+ * Time complexity: O(|V|+|E|), |V| is the number of vertices
+ * in largest graph and |E| is the number of edges in the result graph.
+ *
+ * \example examples/simple/igraph_union.c
+ */
+
+int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs,
+                      igraph_vector_ptr_t *edgemaps) {
+
+    long int no_of_graphs = igraph_vector_ptr_size(graphs);
+    long int no_of_nodes = 0;
+    igraph_bool_t directed = 1;
+    igraph_vector_t edges;
+    igraph_vector_ptr_t edge_vects, order_vects;
+    igraph_vector_long_t no_edges;
+    long int i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1;
+    long int idx = 0;
+
+    /* Check directedness */
+    if (no_of_graphs != 0) {
+        directed = igraph_is_directed(VECTOR(*graphs)[0]);
+        no_of_nodes = igraph_vcount(VECTOR(*graphs)[0]);
+    }
+    for (i = 1; i < no_of_graphs; i++) {
+        if (directed != igraph_is_directed(VECTOR(*graphs)[i])) {
+            IGRAPH_ERROR("Cannot union directed and undirected graphs",
+                         IGRAPH_EINVAL);
+        }
+    }
+
+    if (edgemaps) {
+        IGRAPH_CHECK(igraph_vector_ptr_resize(edgemaps, no_of_graphs));
+        igraph_vector_ptr_null(edgemaps);
+        IGRAPH_FINALLY(igraph_i_union_many_free3, edgemaps);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_long_init(&no_edges, no_of_graphs));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &no_edges);
+
+    /* Calculate number of nodes, query number of edges */
+    for (i = 0; i < no_of_graphs; i++) {
+        long int n = igraph_vcount(VECTOR(*graphs)[i]);
+        if (n > no_of_nodes) {
+            no_of_nodes = n;
+        }
+        VECTOR(no_edges)[i] = igraph_ecount(VECTOR(*graphs)[i]);
+    }
+
+    if (edgemaps) {
+        for (i = 0; i < no_of_graphs; i++) {
+            VECTOR(*edgemaps)[i] = igraph_Calloc(1, igraph_vector_t);
+            if (!VECTOR(*edgemaps)[i]) {
+                IGRAPH_ERROR("Cannot union graphs", IGRAPH_ENOMEM);
+            }
+            IGRAPH_CHECK(igraph_vector_init(VECTOR(*edgemaps)[i],
+                                            VECTOR(no_edges)[i]));
+        }
+    }
+
+    /* Allocate memory for the edge lists and their index vectors */
+    if (no_of_graphs != 0) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&edge_vects, no_of_graphs));
+        IGRAPH_FINALLY(igraph_i_union_many_free, &edge_vects);
+        IGRAPH_CHECK(igraph_vector_ptr_init(&order_vects, no_of_graphs));
+        IGRAPH_FINALLY(igraph_i_union_many_free2, &order_vects);
+    }
+    for (i = 0; i < no_of_graphs; i++) {
+        VECTOR(edge_vects)[i] = igraph_Calloc(1, igraph_vector_t);
+        VECTOR(order_vects)[i] = igraph_Calloc(1, igraph_vector_long_t);
+        if (! VECTOR(edge_vects)[i] || ! VECTOR(order_vects)[i]) {
+            IGRAPH_ERROR("Cannot union graphs", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_init(VECTOR(edge_vects)[i],
+                                        2 * VECTOR(no_edges)[i]));
+        IGRAPH_CHECK(igraph_vector_long_init(VECTOR(order_vects)[i],
+                                             VECTOR(no_edges)[i]));
+    }
+
+    /* Query and sort the edge lists */
+    for (i = 0; i < no_of_graphs; i++) {
+        long int k, j, n = VECTOR(no_edges)[i];
+        igraph_vector_t *edges = VECTOR(edge_vects)[i];
+        igraph_vector_long_t *order = VECTOR(order_vects)[i];
+        IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], edges, /*bycol=*/0));
+        if (!directed) {
+            for (k = 0, j = 0; k < n; k++, j += 2) {
+                if (VECTOR(*edges)[j] > VECTOR(*edges)[j + 1]) {
+                    long int tmp = VECTOR(*edges)[j];
+                    VECTOR(*edges)[j] = VECTOR(*edges)[j + 1];
+                    VECTOR(*edges)[j + 1] = tmp;
+                }
+            }
+        }
+        for (k = 0; k < n; k++) {
+            VECTOR(*order)[k] = k;
+        }
+        igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), edges,
+                       igraph_i_order_edgelist_cmp);
+    }
+
+    while (tailfrom >= 0) {
+
+        /* Get the largest tail element */
+        tailfrom = tailto = -1;
+        for (j = 0; j < no_of_graphs; j++) {
+            if (!igraph_vector_long_empty(VECTOR(order_vects)[j])) {
+                long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]);
+                igraph_vector_t *ev = VECTOR(edge_vects)[j];
+                long int from = VECTOR(*ev)[2 * edge];
+                long int to = VECTOR(*ev)[2 * edge + 1];
+                if (from > tailfrom || (from == tailfrom && to > tailto)) {
+                    tailfrom = from; tailto = to;
+                }
+            }
+        }
+        if (tailfrom < 0) {
+            continue;
+        }
+
+        /* add the edge */
+        IGRAPH_CHECK(igraph_vector_push_back(&edges, tailfrom));
+        IGRAPH_CHECK(igraph_vector_push_back(&edges, tailto));
+
+        /* update edge lists, we just modify the 'order' vectors */
+        for (j = 0; j < no_of_graphs; j++) {
+            if (!igraph_vector_long_empty(VECTOR(order_vects)[j])) {
+                long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]);
+                igraph_vector_t *ev = VECTOR(edge_vects)[j];
+                long int from = VECTOR(*ev)[2 * edge];
+                long int to = VECTOR(*ev)[2 * edge + 1];
+                if (from == tailfrom && to == tailto) {
+                    igraph_vector_long_pop_back(VECTOR(order_vects)[j]);
+                    if (edgemaps) {
+                        igraph_vector_t *map = VECTOR(*edgemaps)[j];
+                        VECTOR(*map)[edge] = idx;
+                    }
+                }
+            }
+        }
+        idx++;
+
+    }
+
+    if (no_of_graphs > 0) {
+        igraph_i_union_many_free2(&order_vects);
+        igraph_i_union_many_free(&edge_vects);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_vector_long_destroy(&no_edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    if (edgemaps) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_difference
+ * \brief Calculate the difference of two graphs
+ *
+ * </para><para>
+ * The number of vertices in the result is the number of vertices in
+ * the original graph, ie. the left, first operand. In the results
+ * graph only edges will be included from \c orig which are not
+ * present in \c sub.
+ *
+ * \param res Pointer to an uninitialized graph object, the result
+ * will be stored here.
+ * \param orig The left operand of the operator, a graph object.
+ * \param sub The right operand of the operator, a graph object.
+ * \return Error code.
+ * \sa \ref igraph_intersection() and \ref igraph_union() for other
+ * operators.
+ *
+ * Time complexity: O(|V|+|E|), |V| is the number vertices in
+ * the smaller graph, |E| is the
+ * number of edges in the result graph.
+ *
+ * \example examples/simple/igraph_difference.c
+ */
+
+int igraph_difference(igraph_t *res,
+                      const igraph_t *orig, const igraph_t *sub) {
+
+    /* Quite nasty, but we will use that an edge adjacency list
+       contains the vertices according to the order of the
+       vertex ids at the "other" end of the edge. */
+
+    long int no_of_nodes_orig = igraph_vcount(orig);
+    long int no_of_nodes_sub = igraph_vcount(sub);
+    long int no_of_nodes = no_of_nodes_orig;
+    long int smaller_nodes;
+    igraph_bool_t directed = igraph_is_directed(orig);
+    igraph_vector_t edges;
+    igraph_vector_t edge_ids;
+    igraph_vector_int_t *nei1, *nei2;
+    igraph_inclist_t inc_orig, inc_sub;
+    long int i;
+    igraph_integer_t v1, v2;
+
+    if (directed != igraph_is_directed(sub)) {
+        IGRAPH_ERROR("Cannot subtract directed and undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edge_ids, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_inclist_init(orig, &inc_orig, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &inc_orig);
+    IGRAPH_CHECK(igraph_inclist_init(sub, &inc_sub, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &inc_sub);
+
+    smaller_nodes = no_of_nodes_orig > no_of_nodes_sub ?
+                    no_of_nodes_sub : no_of_nodes_orig;
+
+    for (i = 0; i < smaller_nodes; i++) {
+        long int n1, n2, e1, e2;
+        IGRAPH_ALLOW_INTERRUPTION();
+        nei1 = igraph_inclist_get(&inc_orig, i);
+        nei2 = igraph_inclist_get(&inc_sub, i);
+        n1 = igraph_vector_int_size(nei1) - 1;
+        n2 = igraph_vector_int_size(nei2) - 1;
+        while (n1 >= 0 && n2 >= 0) {
+            e1 = (long int) VECTOR(*nei1)[n1];
+            e2 = (long int) VECTOR(*nei2)[n2];
+            v1 = IGRAPH_OTHER(orig, e1, i);
+            v2 = IGRAPH_OTHER(sub, e2, i);
+
+            if (!directed && v1 < i) {
+                n1--;
+            } else if (!directed && v2 < i) {
+                n2--;
+            } else if (v1 > v2) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, v1));
+                n1--;
+            } else if (v2 > v1) {
+                n2--;
+            } else {
+                n1--;
+                n2--;
+            }
+        }
+
+        /* Copy remaining edges */
+        while (n1 >= 0) {
+            e1 = (long int) VECTOR(*nei1)[n1];
+            v1 = IGRAPH_OTHER(orig, e1, i);
+            if (directed || v1 >= i) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, v1));
+            }
+            n1--;
+        }
+    }
+
+    /* copy remaining edges, use the previous value of 'i' */
+    for (; i < no_of_nodes_orig; i++) {
+        long int n1, e1;
+        nei1 = igraph_inclist_get(&inc_orig, i);
+        n1 = igraph_vector_int_size(nei1) - 1;
+        while (n1 >= 0) {
+            e1 = (long int) VECTOR(*nei1)[n1];
+            v1 = IGRAPH_OTHER(orig, e1, i);
+            if (directed || v1 >= i) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, v1));
+            }
+            n1--;
+        }
+    }
+
+    igraph_inclist_destroy(&inc_sub);
+    igraph_inclist_destroy(&inc_orig);
+    IGRAPH_FINALLY_CLEAN(2);
+    IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Attributes */
+    if (orig->attr) {
+        IGRAPH_I_ATTRIBUTE_DESTROY(res);
+        IGRAPH_I_ATTRIBUTE_COPY(res, orig, /*graph=*/1, /*vertex=*/1, /*edge=*/0);
+        IGRAPH_CHECK(igraph_i_attribute_permute_edges(orig, res, &edge_ids));
+    }
+
+    igraph_vector_destroy(&edge_ids);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_complementer
+ * \brief Create the complementer of a graph
+ *
+ * </para><para>The complementer graph means that all edges which are
+ * not part of the original graph will be included in the result.
+ *
+ * \param res Pointer to an uninitialized graph object.
+ * \param graph The original graph.
+ * \param loops Whether to add loop edges to the complementer graph.
+ * \return Error code.
+ * \sa \ref igraph_union(), \ref igraph_intersection() and \ref
+ * igraph_difference().
+ *
+ * Time complexity: O(|V|+|E1|+|E2|), |V| is the number of
+ * vertices in the graph, |E1| is the number of edges in the original
+ * and |E2| in the complementer graph.
+ *
+ * \example examples/simple/igraph_complementer.c
+ */
+
+int igraph_complementer(igraph_t *res, const igraph_t *graph,
+                        igraph_bool_t loops) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t edges;
+    igraph_vector_t neis;
+    long int i, j;
+    long int zero = 0, *limit;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    if (igraph_is_directed(graph)) {
+        limit = &zero;
+    } else {
+        limit = &i;
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i,
+                                      IGRAPH_OUT));
+        if (loops) {
+            for (j = no_of_nodes - 1; j >= *limit; j--) {
+                if (igraph_vector_empty(&neis) || j > igraph_vector_tail(&neis)) {
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                    IGRAPH_CHECK(igraph_vector_push_back(&edges, j));
+                } else {
+                    igraph_vector_pop_back(&neis);
+                }
+            }
+        } else {
+            for (j = no_of_nodes - 1; j >= *limit; j--) {
+                if (igraph_vector_empty(&neis) || j > igraph_vector_tail(&neis)) {
+                    if (i != j) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edges, j));
+                    }
+                } else {
+                    igraph_vector_pop_back(&neis);
+                }
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes,
+                               igraph_is_directed(graph)));
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&neis);
+    IGRAPH_I_ATTRIBUTE_DESTROY(res);
+    IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/1, /*vertex=*/1, /*edge=*/0);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_compose
+ * \brief Calculates the composition of two graphs
+ *
+ * The composition of graphs contains the same number of vertices as
+ * the bigger graph of the two operands. It contains an (i,j) edge if
+ * and only if there is a k vertex, such that the first graphs
+ * contains an (i,k) edge and the second graph a (k,j) edge.
+ *
+ * </para><para>This is of course exactly the composition of two
+ * binary relations.
+ *
+ * </para><para>Two two graphs must have the same directedness,
+ * otherwise the function returns with an error message.
+ * Note that for undirected graphs the two relations are by definition
+ * symmetric.
+ *
+ * \param res Pointer to an uninitialized graph object, the result
+ *        will be stored here.
+ * \param g1 The firs operand, a graph object.
+ * \param g2 The second operand, another graph object.
+ * \param edge_map1 If not a null pointer, then it must be a pointer
+ *        to an initialized vector, and a mapping from the edges of
+ *        the result graph to the edges of the first graph is stored
+ *        here.
+ * \param edge_map1 If not a null pointer, then it must be a pointer
+ *        to an initialized vector, and a mapping from the edges of
+ *        the result graph to the edges of the second graph is stored
+ *        here.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|*d1*d2), |V| is the number of vertices in the
+ * first graph, d1 and d2 the average degree in the first and second
+ * graphs.
+ *
+ * \example examples/simple/igraph_compose.c
+ */
+
+int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2,
+                   igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) {
+
+    long int no_of_nodes_left = igraph_vcount(g1);
+    long int no_of_nodes_right = igraph_vcount(g2);
+    long int no_of_nodes;
+    igraph_bool_t directed = igraph_is_directed(g1);
+    igraph_vector_t edges;
+    igraph_vector_t neis1, neis2;
+    long int i;
+
+    if (directed != igraph_is_directed(g2)) {
+        IGRAPH_ERROR("Cannot compose directed and undirected graph",
+                     IGRAPH_EINVAL);
+    }
+
+    no_of_nodes = no_of_nodes_left > no_of_nodes_right ?
+                  no_of_nodes_left : no_of_nodes_right;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis2, 0);
+
+    if (edge_map1) {
+        igraph_vector_clear(edge_map1);
+    }
+    if (edge_map2) {
+        igraph_vector_clear(edge_map2);
+    }
+
+    for (i = 0; i < no_of_nodes_left; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        IGRAPH_CHECK(igraph_incident(g1, &neis1, (igraph_integer_t) i,
+                                     IGRAPH_OUT));
+        while (!igraph_vector_empty(&neis1)) {
+            long int con = (long int) igraph_vector_pop_back(&neis1);
+            long int v1 = IGRAPH_OTHER(g1, con, i);
+            if (v1 < no_of_nodes_right) {
+                IGRAPH_CHECK(igraph_incident(g2, &neis2, (igraph_integer_t) v1,
+                                             IGRAPH_OUT));
+            } else {
+                continue;
+            }
+            while (!igraph_vector_empty(&neis2)) {
+                long int con2 = igraph_vector_pop_back(&neis2);
+                long int v2 = IGRAPH_OTHER(g2, con2, v1);
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, v2));
+                if (edge_map1) {
+                    IGRAPH_CHECK(igraph_vector_push_back(edge_map1, con));
+                }
+                if (edge_map2) {
+                    IGRAPH_CHECK(igraph_vector_push_back(edge_map2, con2));
+                }
+            }
+        }
+    }
+
+    igraph_vector_destroy(&neis1);
+    igraph_vector_destroy(&neis2);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
diff --git a/igraph/src/optimal_modularity.c b/igraph/src/optimal_modularity.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/optimal_modularity.c
@@ -0,0 +1,260 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_interface.h"
+#include "igraph_structural.h"
+#include "igraph_community.h"
+#include "igraph_error.h"
+#include "igraph_glpk_support.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_centrality.h"
+#include "config.h"
+
+#ifdef HAVE_GLPK
+    #include <glpk.h>
+#endif
+
+/**
+ * \function igraph_community_optimal_modularity
+ * Calculate the community structure with the highest modularity value
+ *
+ * This function calculates the optimal community structure for a
+ * graph, in terms of maximal modularity score.
+ *
+ * </para><para>
+ * The calculation is done by transforming the modularity maximization
+ * into an integer programming problem, and then calling the GLPK
+ * library to solve that. Please see Ulrik Brandes et al.: On
+ * Modularity Clustering, IEEE Transactions on Knowledge and Data
+ * Engineering 20(2):172-188, 2008.
+ *
+ * </para><para>
+ * Note that modularity optimization is an NP-complete problem, and
+ * all known algorithms for it have exponential time complexity. This
+ * means that you probably don't want to run this function on larger
+ * graphs. Graphs with up to fifty vertices should be fine, graphs
+ * with a couple of hundred vertices might be possible.
+ *
+ * \param graph The input graph. It is always treated as undirected.
+ * \param modularity Pointer to a real number, or a null pointer.
+ *        If it is not a null pointer, then a optimal modularity value
+ *        is returned here.
+ * \param membership Pointer to a vector, or a null pointer. If not a
+ *        null pointer, then the membership vector of the optimal
+ *        community structure is stored here.
+ * \param weights Vector giving the weights of the edges. If it is
+ *        \c NULL then each edge is supposed to have the same weight.
+ * \return Error code.
+ *
+ * \sa \ref igraph_modularity(), \ref igraph_community_fastgreedy()
+ * for an algorithm that finds a local optimum in a greedy way.
+ *
+ * Time complexity: exponential in the number of vertices.
+ *
+ * \example examples/simple/igraph_community_optimal_modularity.c
+ */
+
+int igraph_community_optimal_modularity(const igraph_t *graph,
+                                        igraph_real_t *modularity,
+                                        igraph_vector_t *membership,
+                                        const igraph_vector_t *weights) {
+
+#ifndef HAVE_GLPK
+    IGRAPH_ERROR("GLPK is not available",
+                 IGRAPH_UNIMPLEMENTED);
+#else
+
+    igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph);
+    igraph_integer_t no_of_edges = (igraph_integer_t) igraph_ecount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+    int no_of_variables = no_of_nodes * (no_of_nodes + 1) / 2;
+    int i, j, k, l, st;
+    int idx[] = { 0, 0, 0, 0 };
+    double coef[] = { 0.0, 1.0, 1.0, -2.0 };
+    igraph_real_t total_weight;
+    igraph_vector_t indegree;
+    igraph_vector_t outdegree;
+
+    glp_prob *ip;
+    glp_iocp parm;
+
+    if (weights != 0) {
+        if (igraph_vector_size(weights) != no_of_edges) {
+            IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL);
+        }
+        if (igraph_vector_min(weights) < 0) {
+            IGRAPH_ERROR("Negative weights are not allowed in weight vector", IGRAPH_EINVAL);
+        }
+    }
+
+    if (weights) {
+        total_weight = igraph_vector_sum(weights);
+    } else {
+        total_weight = no_of_edges;
+    }
+    if (!directed) {
+        total_weight *= 2;
+    }
+
+    /* Special case */
+    if (no_of_edges == 0 || total_weight == 0) {
+        if (modularity) {
+            *modularity = IGRAPH_NAN;
+        }
+        if (membership) {
+            IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+            igraph_vector_null(membership);
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&outdegree, no_of_nodes);
+    IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(),
+                                 IGRAPH_IN, IGRAPH_LOOPS, weights));
+    IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(),
+                                 IGRAPH_OUT, IGRAPH_LOOPS, weights));
+
+    glp_term_out(GLP_OFF);
+    ip = glp_create_prob();
+    IGRAPH_FINALLY(glp_delete_prob, ip);
+
+    glp_set_obj_dir(ip, GLP_MAX);
+    st = glp_add_cols(ip, no_of_variables);
+
+    /* variables are binary */
+    for (i = 0; i < no_of_variables; i++) {
+        glp_set_col_kind(ip, (st + i), GLP_BV);
+    }
+
+#define IDX(a,b) ((b)*((b)+1)/2+(a))
+
+    /* reflexivity */
+    for (i = 0; i < no_of_nodes; i++) {
+        glp_set_col_bnds(ip, (st + IDX(i, i)), GLP_FX, 1.0, 1.0);
+    }
+
+    /* transitivity */
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = i + 1; j < no_of_nodes; j++) {
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            for (k = j + 1; k < no_of_nodes; k++) {
+                int newrow = glp_add_rows(ip, 3);
+
+                glp_set_row_bnds(ip, newrow, GLP_UP, 0.0, 1.0);
+                idx[1] = (st + IDX(i, j)); idx[2] = (st + IDX(j, k));
+                idx[3] = (st + IDX(i, k));
+                glp_set_mat_row(ip, newrow, 3, idx, coef);
+
+                glp_set_row_bnds(ip, newrow + 1, GLP_UP, 0.0, 1.0);
+                idx[1] = st + IDX(i, j); idx[2] = st + IDX(i, k); idx[3] = st + IDX(j, k);
+                glp_set_mat_row(ip, newrow + 1, 3, idx, coef);
+
+                glp_set_row_bnds(ip, newrow + 2, GLP_UP, 0.0, 1.0);
+                idx[1] = st + IDX(i, k); idx[2] = st + IDX(j, k); idx[3] = st + IDX(i, j);
+                glp_set_mat_row(ip, newrow + 2, 3, idx, coef);
+
+            }
+        }
+    }
+
+    /* objective function */
+    {
+        igraph_real_t c;
+
+        /* first part: -strength(i)*strength(j)/total_weight for every node pair */
+        for (i = 0; i < no_of_nodes; i++) {
+            for (j = i + 1; j < no_of_nodes; j++) {
+                c = -VECTOR(indegree)[i] * VECTOR(outdegree)[j] / total_weight \
+                    -VECTOR(outdegree)[i] * VECTOR(indegree)[j] / total_weight;
+                glp_set_obj_coef(ip, st + IDX(i, j), c);
+            }
+            /* special case for (i,i) */
+            c = -VECTOR(indegree)[i] * VECTOR(outdegree)[i] / total_weight;
+            glp_set_obj_coef(ip, st + IDX(i, i), c);
+        }
+
+        /* second part: add the weighted adjacency matrix to the coefficient matrix */
+        for (k = 0; k < no_of_edges; k++) {
+            i = IGRAPH_FROM(graph, k);
+            j = IGRAPH_TO(graph, k);
+            if (i > j) {
+                l = i; i = j; j = l;
+            }
+            c = weights ? VECTOR(*weights)[k] : 1.0;
+            if (!directed || i == j) {
+                c *= 2.0;
+            }
+            glp_set_obj_coef(ip, st + IDX(i, j), c + glp_get_obj_coef(ip, st + IDX(i, j)));
+        }
+    }
+
+    /* solve it */
+    glp_init_iocp(&parm);
+    parm.br_tech = GLP_BR_DTH;
+    parm.bt_tech = GLP_BT_BLB;
+    parm.presolve = GLP_ON;
+    parm.binarize = GLP_ON;
+    parm.cb_func = igraph_i_glpk_interruption_hook;
+    IGRAPH_GLPK_CHECK(glp_intopt(ip, &parm), "Modularity optimization failed");
+
+    /* store the results */
+    if (modularity) {
+        *modularity = glp_mip_obj_val(ip) / total_weight;
+    }
+
+    if (membership) {
+        long int comm = 0;   /* id of the last community that was found */
+        IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+        for (i = 0; i < no_of_nodes; i++) {
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            for (j = 0; j < i; j++) {
+                int val = (int) glp_mip_col_val(ip, st + IDX(j, i));
+                if (val == 1) {
+                    VECTOR(*membership)[i] = VECTOR(*membership)[j];
+                    break;
+                }
+            }
+            if (j == i) {     /* new community */
+                VECTOR(*membership)[i] = comm++;
+            }
+        }
+    }
+
+#undef IDX
+
+    igraph_vector_destroy(&indegree);
+    igraph_vector_destroy(&outdegree);
+    glp_delete_prob(ip);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+
+#endif
+
+}
+
diff --git a/igraph/src/options.c b/igraph/src/options.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/options.c
@@ -0,0 +1,47 @@
+/* options.c
+ *
+ * Copyright (C) 2012 Tamas Nepusz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "error.h"
+#include "plfit.h"
+
+const plfit_continuous_options_t plfit_continuous_default_options = {
+  /* .finite_size_correction = */ 0,
+  /* .xmin_method = */ PLFIT_GSS_OR_LINEAR
+};
+
+const plfit_discrete_options_t plfit_discrete_default_options = {
+  /* .finite_size_correction = */ 0,
+  /* .alpha_method = */ PLFIT_LBFGS,
+  /* .alpha = */ {
+    /* .min = */ 1.01,
+    /* .max = */ 5,
+    /* .step = */ 0.01
+  }
+};
+
+int plfit_continuous_options_init(plfit_continuous_options_t* options) {
+	*options = plfit_continuous_default_options;
+	return PLFIT_SUCCESS;
+}
+
+int plfit_discrete_options_init(plfit_discrete_options_t* options) {
+	*options = plfit_discrete_default_options;
+	return PLFIT_SUCCESS;
+}
+
diff --git a/igraph/src/orbit.cc b/igraph/src/orbit.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/orbit.cc
@@ -0,0 +1,144 @@
+#include <stdlib.h>
+#include <assert.h>
+#include "defs.hh"
+#include "orbit.hh"
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+Orbit::Orbit()
+{
+  orbits = 0;
+  in_orbit = 0;
+  nof_elements = 0;
+}
+
+
+Orbit::~Orbit()
+{
+  if(orbits)
+    {
+      free(orbits);
+      orbits = 0;
+    }
+  if(in_orbit)
+    {
+      free(in_orbit);
+      in_orbit = 0;
+    }
+  nof_elements = 0;
+}
+
+
+void Orbit::init(const unsigned int n)
+{
+  assert(n > 0);
+  if(orbits) free(orbits);
+  orbits = (OrbitEntry*)malloc(n * sizeof(OrbitEntry));
+  if(in_orbit) free(in_orbit);
+  in_orbit = (OrbitEntry**)malloc(n * sizeof(OrbitEntry*));
+  nof_elements = n;
+
+  reset();
+}
+
+
+void Orbit::reset()
+{
+  assert(orbits);
+  assert(in_orbit);
+
+  for(unsigned int i = 0; i < nof_elements; i++)
+    {
+      orbits[i].element = i;
+      orbits[i].next = 0;
+      orbits[i].size = 1;
+      in_orbit[i] = &orbits[i];
+    }
+  _nof_orbits = nof_elements;
+}
+
+
+void Orbit::merge_orbits(OrbitEntry *orbit1, OrbitEntry *orbit2)
+{
+
+  if(orbit1 != orbit2)
+    {
+      _nof_orbits--;
+      /* Only update the elements in the smaller orbit */
+      if(orbit1->size > orbit2->size)
+	{
+	  OrbitEntry * const temp = orbit2;
+	  orbit2 = orbit1;
+	  orbit1 = temp;
+	}
+      /* Link the elements of orbit1 to the almost beginning of orbit2 */
+      OrbitEntry *e = orbit1;
+      while(e->next)
+	{
+	  in_orbit[e->element] = orbit2;
+	  e = e->next;
+	}
+      in_orbit[e->element] = orbit2;
+      e->next = orbit2->next;
+      orbit2->next = orbit1;
+      /* Keep the minimal orbit representative in the beginning */
+      if(orbit1->element < orbit2->element)
+	{
+	  const unsigned int temp = orbit1->element;
+	  orbit1->element = orbit2->element;
+	  orbit2->element = temp;
+	}
+      orbit2->size += orbit1->size;
+    }
+}
+
+
+void Orbit::merge_orbits(unsigned int e1, unsigned int e2)
+{
+
+  merge_orbits(in_orbit[e1], in_orbit[e2]);
+}
+
+
+bool Orbit::is_minimal_representative(unsigned int element) const
+{
+  return(get_minimal_representative(element) == element);
+}
+
+
+unsigned int Orbit::get_minimal_representative(unsigned int element) const
+{
+
+  OrbitEntry * const orbit = in_orbit[element];
+
+  return(orbit->element);
+}
+
+
+unsigned int Orbit::orbit_size(unsigned int element) const
+{
+  
+  return(in_orbit[element]->size);
+}
+
+
+} // namespace bliss
diff --git a/igraph/src/other.c b/igraph/src/other.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/other.c
@@ -0,0 +1,427 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_nongraph.h"
+#include "igraph_types.h"
+#include "igraph_memory.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_types_internal.h"
+#include "config.h"
+#include "plfit/error.h"
+#include "plfit/plfit.h"
+#include <math.h>
+#include <stdarg.h>
+#include <string.h>
+
+/**
+ * \ingroup nongraph
+ * \function igraph_running_mean
+ * \brief Calculates the running mean of a vector.
+ *
+ * </para><para>
+ * The running mean is defined by the mean of the
+ * previous \p binwidth values.
+ * \param data The vector containing the data.
+ * \param res The vector containing the result. This should be
+ *        initialized before calling this function and will be
+ *        resized.
+ * \param binwidth Integer giving the width of the bin for the running
+ *        mean calculation.
+ * \return Error code.
+ *
+ * Time complexity: O(n),
+ * n is the length of
+ * the data vector.
+ */
+
+int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res,
+                        igraph_integer_t binwidth) {
+
+    double sum = 0;
+    long int i;
+
+    /* Check */
+    if (igraph_vector_size(data) < binwidth) {
+        IGRAPH_ERROR("Vector too short for this binwidth", IGRAPH_EINVAL);
+    }
+
+    /* Memory for result */
+
+    IGRAPH_CHECK(igraph_vector_resize(res, (long int)(igraph_vector_size(data) - binwidth + 1)));
+
+    /* Initial bin */
+    for (i = 0; i < binwidth; i++) {
+        sum += VECTOR(*data)[i];
+    }
+
+    VECTOR(*res)[0] = sum / binwidth;
+
+    for (i = 1; i < igraph_vector_size(data) - binwidth + 1; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        sum -= VECTOR(*data)[i - 1];
+        sum += VECTOR(*data)[ (long int)(i + binwidth - 1)];
+        VECTOR(*res)[i] = sum / binwidth;
+    }
+
+    return 0;
+}
+
+
+/**
+ * \ingroup nongraph
+ * \function igraph_convex_hull
+ * \brief Determines the convex hull of a given set of points in the 2D plane
+ *
+ * </para><para>
+ * The convex hull is determined by the Graham scan algorithm.
+ * See the following reference for details:
+ *
+ * </para><para>
+ * Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford
+ * Stein. Introduction to Algorithms, Second Edition. MIT Press and
+ * McGraw-Hill, 2001. ISBN 0262032937. Pages 949-955 of section 33.3:
+ * Finding the convex hull.
+ *
+ * \param data vector containing the coordinates. The length of the
+ *        vector must be even, since it contains X-Y coordinate pairs.
+ * \param resverts the vector containing the result, e.g. the vector of
+ *        vertex indices used as the corners of the convex hull. Supply
+ *        \c NULL here if you are only interested in the coordinates of
+ *        the convex hull corners.
+ * \param rescoords the matrix containing the coordinates of the selected
+ *        corner vertices. Supply \c NULL here if you are only interested in
+ *        the vertex indices.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM: not enough memory
+ *
+ * Time complexity: O(n log(n)) where n is the number of vertices
+ *
+ * \example examples/simple/igraph_convex_hull.c
+ */
+int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts,
+                       igraph_matrix_t *rescoords) {
+    igraph_integer_t no_of_nodes;
+    long int i, pivot_idx = 0, last_idx, before_last_idx, next_idx, j;
+    igraph_vector_t angles, stack, order;
+    igraph_real_t px, py, cp;
+
+    no_of_nodes = (igraph_integer_t) igraph_matrix_nrow(data);
+    if (igraph_matrix_ncol(data) != 2) {
+        IGRAPH_ERROR("matrix must have 2 columns", IGRAPH_EINVAL);
+    }
+    if (no_of_nodes == 0) {
+        if (resverts != 0) {
+            IGRAPH_CHECK(igraph_vector_resize(resverts, 0));
+        }
+        if (rescoords != 0) {
+            IGRAPH_CHECK(igraph_matrix_resize(rescoords, 0, 2));
+        }
+        /**************************** this is an exit here *********/
+        return 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&angles, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&stack, 0);
+
+    /* Search for the pivot vertex */
+    for (i = 1; i < no_of_nodes; i++) {
+        if (MATRIX(*data, i, 1) < MATRIX(*data, pivot_idx, 1)) {
+            pivot_idx = i;
+        } else if (MATRIX(*data, i, 1) == MATRIX(*data, pivot_idx, 1) &&
+                   MATRIX(*data, i, 0) < MATRIX(*data, pivot_idx, 0)) {
+            pivot_idx = i;
+        }
+    }
+    px = MATRIX(*data, pivot_idx, 0);
+    py = MATRIX(*data, pivot_idx, 1);
+
+    /* Create angle array */
+    for (i = 0; i < no_of_nodes; i++) {
+        if (i == pivot_idx) {
+            /* We can't calculate the angle of the pivot point with itself,
+             * so we use 10 here. This way, after sorting the angle vector,
+             * the pivot point will always be the first one, since the range
+             * of atan2 is -3.14..3.14 */
+            VECTOR(angles)[i] = 10;
+        } else {
+            VECTOR(angles)[i] = atan2(MATRIX(*data, i, 1) - py, MATRIX(*data, i, 0) - px);
+        }
+    }
+
+    /* Sort points by angles */
+    IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
+    IGRAPH_CHECK(igraph_vector_qsort_ind(&angles, &order, 0));
+
+    /* Check if two points have the same angle. If so, keep only the point that
+     * is farthest from the pivot */
+    j = 0;
+    last_idx = (long int) VECTOR(order)[0];
+    pivot_idx = (long int) VECTOR(order)[no_of_nodes - 1];
+    for (i = 1; i < no_of_nodes; i++) {
+        next_idx = (long int) VECTOR(order)[i];
+        if (VECTOR(angles)[last_idx] == VECTOR(angles)[next_idx]) {
+            /* Keep the vertex that is farther from the pivot, drop the one that is
+             * closer */
+            px = pow(MATRIX(*data, last_idx, 0) - MATRIX(*data, pivot_idx, 0), 2) +
+                 pow(MATRIX(*data, last_idx, 1) - MATRIX(*data, pivot_idx, 1), 2);
+            py = pow(MATRIX(*data, next_idx, 0) - MATRIX(*data, pivot_idx, 0), 2) +
+                 pow(MATRIX(*data, next_idx, 1) - MATRIX(*data, pivot_idx, 1), 2);
+            if (px > py) {
+                VECTOR(order)[i] = -1;
+            } else {
+                VECTOR(order)[j] = -1;
+                last_idx = next_idx;
+                j = i;
+            }
+        } else {
+            last_idx = next_idx;
+            j = i;
+        }
+    }
+
+    j = 0;
+    last_idx = -1;
+    before_last_idx = -1;
+    while (!igraph_vector_empty(&order)) {
+        next_idx = (long int)VECTOR(order)[igraph_vector_size(&order) - 1];
+        if (next_idx < 0) {
+            /* This vertex should be skipped; was excluded in an earlier step */
+            igraph_vector_pop_back(&order);
+            continue;
+        }
+        /* Determine whether we are at a left or right turn */
+        if (j < 2) {
+            /* Pretend that we are turning into the right direction if we have less
+             * than two items in the stack */
+            cp = -1;
+        } else {
+            cp = (MATRIX(*data, last_idx, 0) - MATRIX(*data, before_last_idx, 0)) *
+                 (MATRIX(*data, next_idx, 1) - MATRIX(*data, before_last_idx, 1)) -
+                 (MATRIX(*data, next_idx, 0) - MATRIX(*data, before_last_idx, 0)) *
+                 (MATRIX(*data, last_idx, 1) - MATRIX(*data, before_last_idx, 1));
+        }
+        /*
+        printf("B L N cp: %ld, %ld, %ld, %f [", before_last_idx, last_idx, next_idx, (float)cp);
+        for (int k=0; k<j; k++) printf("%ld ", (long)VECTOR(stack)[k]);
+        printf("]\n");
+        */
+        if (cp < 0) {
+            /* We are turning into the right direction */
+            igraph_vector_pop_back(&order);
+            IGRAPH_CHECK(igraph_vector_push_back(&stack, next_idx));
+            before_last_idx = last_idx;
+            last_idx = next_idx;
+            j++;
+        } else {
+            /* No, skip back and try again in the next iteration */
+            igraph_vector_pop_back(&stack);
+            j--;
+            last_idx = before_last_idx;
+            before_last_idx = (j >= 2) ? (long int) VECTOR(stack)[j - 2] : -1;
+        }
+    }
+
+    /* Create result vector */
+    if (resverts != 0) {
+        igraph_vector_clear(resverts);
+        IGRAPH_CHECK(igraph_vector_append(resverts, &stack));
+    }
+    if (rescoords != 0) {
+        igraph_matrix_select_rows(data, rescoords, &stack);
+    }
+
+    /* Free everything */
+    igraph_vector_destroy(&order);
+    igraph_vector_destroy(&stack);
+    igraph_vector_destroy(&angles);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+
+static const char* igraph_i_plfit_error_message = 0;
+
+static void igraph_i_plfit_error_handler_store(const char *reason, const char *file,
+        int line, int plfit_errno) {
+    igraph_i_plfit_error_message = reason;
+}
+
+/**
+ * \ingroup nongraph
+ * \function igraph_power_law_fit
+ * \brief Fits a power-law distribution to a vector of numbers
+ *
+ * This function fits a power-law distribution to a vector containing samples
+ * from a distribution (that is assumed to follow a power-law of course). In
+ * a power-law distribution, it is generally assumed that P(X=x) is
+ * proportional to x<superscript>-alpha</superscript>, where x is a positive number and alpha
+ * is greater than 1. In many real-world cases, the power-law behaviour kicks
+ * in only above a threshold value \em xmin. The goal of this functions is to
+ * determine \em alpha if \em xmin is given, or to determine \em xmin and the
+ * corresponding value of \em alpha.
+ *
+ * </para><para>
+ * The function uses the maximum likelihood principle to determine \em alpha
+ * for a given \em xmin; in other words, the function will return the \em alpha
+ * value for which the probability of drawing the given sample is the highest.
+ * When \em xmin is not given in advance, the algorithm will attempt to find
+ * the optimal \em xmin value for which the p-value of a Kolmogorov-Smirnov
+ * test between the fitted distribution and the original sample is the largest.
+ * The function uses the method of Clauset, Shalizi and Newman to calculate the
+ * parameters of the fitted distribution. See the following reference for
+ * details:
+ *
+ * </para><para>
+ * Aaron Clauset, Cosma R .Shalizi and Mark E.J. Newman: Power-law
+ * distributions in empirical data. SIAM Review 51(4):661-703, 2009.
+ *
+ * \param data vector containing the samples for which a power-law distribution
+ *             is to be fitted. Note that you have to provide the \em samples,
+ *             not the probability density function or the cumulative
+ *             distribution function. For example, if you wish to fit
+ *             a power-law to the degrees of a graph, you can use the output of
+ *             \ref igraph_degree directly as an input argument to
+ *             \ref igraph_power_law_fit
+ * \param result the result of the fitting algorithm. See \ref igraph_plfit_result_t
+ *             for more details.
+ * \param xmin the minimum value in the sample vector where the power-law
+ *             behaviour is expected to kick in. Samples smaller than \c xmin
+ *             will be ignored by the algoritm. Pass zero here if you want to
+ *             include all the samples. If \c xmin is negative, the algorithm
+ *             will attempt to determine its best value automatically.
+ * \param force_continuous assume that the samples in the \c data argument come
+ *             from a continuous distribution even if the sample vector
+ *             contains integer values only (by chance). If this argument is
+ *             false, igraph will assume a continuous distribution if at least
+ *             one sample is non-integer and assume a discrete distribution
+ *             otherwise.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM: not enough memory
+ *         \c IGRAPH_EINVAL: one of the arguments is invalid
+ *         \c IGRAPH_EOVERFLOW: overflow during the fitting process
+ *         \c IGRAPH_EUNDERFLOW: underflow during the fitting process
+ *         \c IGRAPH_FAILURE: the underlying algorithm signaled a failure
+ *         without returning a more specific error code
+ *
+ * Time complexity: in the continuous case, O(n log(n)) if \c xmin is given.
+ * In the discrete case, the time complexity is dominated by the complexity of
+ * the underlying L-BFGS algorithm that is used to optimize alpha. If \c xmin
+ * is not given, the time complexity is multiplied by the number of unique
+ * samples in the input vector (although it should be faster in practice).
+ *
+ * \example examples/simple/igraph_power_law_fit.c
+ */
+int igraph_power_law_fit(const igraph_vector_t* data, igraph_plfit_result_t* result,
+                         igraph_real_t xmin, igraph_bool_t force_continuous) {
+    plfit_error_handler_t* plfit_stored_error_handler;
+    plfit_result_t plfit_result;
+    plfit_continuous_options_t cont_options;
+    plfit_discrete_options_t disc_options;
+    igraph_bool_t discrete = force_continuous ? 0 : 1;
+    igraph_bool_t finite_size_correction;
+    int retval;
+    size_t i, n;
+
+    n = (size_t) igraph_vector_size(data);
+    finite_size_correction = (n < 50);
+
+    if (discrete) {
+        /* Does the vector contain discrete values only? */
+        for (i = 0; i < n; i++) {
+            if ((long int)(VECTOR(*data)[i]) != VECTOR(*data)[i]) {
+                discrete = 0;
+                break;
+            }
+        }
+    }
+
+    plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store);
+    if (discrete) {
+        plfit_discrete_options_init(&disc_options);
+        disc_options.finite_size_correction = (plfit_bool_t) finite_size_correction;
+
+        if (xmin >= 0) {
+            retval = plfit_estimate_alpha_discrete(VECTOR(*data), n, xmin,
+                                                   &disc_options, &plfit_result);
+        } else {
+            retval = plfit_discrete(VECTOR(*data), n, &disc_options, &plfit_result);
+        }
+    } else {
+        plfit_continuous_options_init(&cont_options);
+        cont_options.finite_size_correction = (plfit_bool_t) finite_size_correction;
+
+        if (xmin >= 0) {
+            retval = plfit_estimate_alpha_continuous(VECTOR(*data), n, xmin,
+                     &cont_options, &plfit_result);
+        } else {
+            retval = plfit_continuous(VECTOR(*data), n, &cont_options, &plfit_result);
+        }
+    }
+    plfit_set_error_handler(plfit_stored_error_handler);
+
+    switch (retval) {
+    case PLFIT_FAILURE:
+        IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_FAILURE);
+        break;
+
+    case PLFIT_EINVAL:
+        IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EINVAL);
+        break;
+
+    case PLFIT_UNDRFLOW:
+        IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EUNDERFLOW);
+        break;
+
+    case PLFIT_OVERFLOW:
+        IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EOVERFLOW);
+        break;
+
+    case PLFIT_ENOMEM:
+        IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_ENOMEM);
+        break;
+
+    default:
+        break;
+    }
+
+    if (result) {
+        result->continuous = !discrete;
+        result->alpha = plfit_result.alpha;
+        result->xmin = plfit_result.xmin;
+        result->L = plfit_result.L;
+        result->D = plfit_result.D;
+        result->p = plfit_result.p;
+    }
+
+    return 0;
+}
+
+/**
+ * Internal function, floating point division
+ * Used only in compilers not supporting INFINITY and HUGE_VAL to create
+ * infinity values
+ */
+double igraph_i_fdiv(const double a, const double b) {
+    return a / b;
+}
diff --git a/igraph/src/partition.cc b/igraph/src/partition.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/partition.cc
@@ -0,0 +1,1143 @@
+#include <assert.h>
+#include <vector>
+#include <list>
+#include "graph.hh"
+#include "partition.hh"
+
+/* use 'and' instead of '&&' */
+#if _MSC_VER
+#include <ciso646>
+#endif
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+Partition::Partition()
+{
+  N = 0;
+  elements = 0;
+  in_pos = 0;
+  invariant_values = 0;
+  cells = 0;
+  free_cells = 0;
+  element_to_cell_map = 0;
+  graph = 0;
+  discrete_cell_count = 0;
+  /* Initialize a distribution count sorting array. */
+  for(unsigned int i = 0; i < 256; i++)
+    dcs_count[i] = 0;
+
+  cr_enabled = false;
+  cr_cells = 0;
+  cr_levels = 0;
+}
+
+
+
+Partition::~Partition()
+{
+  if(elements)            {free(elements); elements = 0; }
+  if(cells)               {free(cells); cells = 0; }
+  if(element_to_cell_map) {free(element_to_cell_map); element_to_cell_map = 0; }
+  if(in_pos)              {free(in_pos); in_pos = 0; }
+  if(invariant_values)    {free(invariant_values); invariant_values = 0; }
+  N = 0;
+}
+
+
+
+void Partition::init(const unsigned int M)
+{
+  assert(M > 0);
+  N = M;
+
+  if(elements)
+    free(elements);
+  elements = (unsigned int*)malloc(N * sizeof(unsigned int));
+  for(unsigned int i = 0; i < N; i++)
+    elements[i] = i;
+
+  if(in_pos)
+    free(in_pos);
+  in_pos = (unsigned int**)malloc(N * sizeof(unsigned int*));
+  for(unsigned int i = 0; i < N; i++)
+    in_pos[i] = elements + i;
+
+  if(invariant_values)
+    free(invariant_values);
+  invariant_values = (unsigned int*)malloc(N * sizeof(unsigned int));
+  for(unsigned int i = 0; i < N; i++)
+    invariant_values[i] = 0;
+
+  if(cells)
+    free(cells);
+  cells = (Cell*)malloc(N * sizeof(Cell));
+
+  cells[0].first = 0;
+  cells[0].length = N;
+  cells[0].max_ival = 0;
+  cells[0].max_ival_count = 0;
+  cells[0].in_splitting_queue = false;
+  cells[0].in_neighbour_heap = false;
+  cells[0].prev = 0;
+  cells[0].next = 0;
+  cells[0].next_nonsingleton = 0;
+  cells[0].prev_nonsingleton = 0;
+  cells[0].split_level = 0;
+  first_cell = &cells[0];
+  if(N == 1)
+    {
+      first_nonsingleton_cell = 0;
+      discrete_cell_count = 1;
+    }
+  else
+    {
+      first_nonsingleton_cell = &cells[0];
+      discrete_cell_count = 0;
+    }
+
+  for(unsigned int i = 1; i < N; i++)
+    {
+      cells[i].first = 0;
+      cells[i].length = 0;
+      cells[i].max_ival = 0;
+      cells[i].max_ival_count = 0;
+      cells[i].in_splitting_queue = false;
+      cells[i].in_neighbour_heap = false;
+      cells[i].prev = 0;
+      cells[i].next = (i < N-1)?&cells[i+1]:0;
+      cells[i].next_nonsingleton = 0;
+      cells[i].prev_nonsingleton = 0;
+    }
+  if(N > 1)
+    free_cells = &cells[1];
+  else
+    free_cells = 0;
+
+  if(element_to_cell_map)
+    free(element_to_cell_map);
+  element_to_cell_map = (Cell **)malloc(N * sizeof(Cell *));
+  for(unsigned int i = 0; i < N; i++)
+    element_to_cell_map[i] = first_cell;
+
+  splitting_queue.init(N);
+  refinement_stack.init(N);
+
+  /* Reset the main backtracking stack */
+  bt_stack.clear();
+}
+
+
+
+
+
+
+Partition::BacktrackPoint
+Partition::set_backtrack_point()
+{
+  BacktrackInfo info;
+  info.refinement_stack_size = refinement_stack.size();
+  if(cr_enabled)
+    info.cr_backtrack_point = cr_get_backtrack_point();
+  BacktrackPoint p = bt_stack.size();
+  bt_stack.push_back(info);
+  return p;
+}
+
+
+
+void
+Partition::goto_backtrack_point(BacktrackPoint p)
+{
+  BacktrackInfo info = bt_stack[p];
+  bt_stack.resize(p);
+
+  if(cr_enabled)
+    cr_goto_backtrack_point(info.cr_backtrack_point);
+
+  const unsigned int dest_refinement_stack_size = info.refinement_stack_size;
+  
+  assert(refinement_stack.size() >= dest_refinement_stack_size);
+  while(refinement_stack.size() > dest_refinement_stack_size)
+    {
+      RefInfo i = refinement_stack.pop();
+      const unsigned int first = i.split_cell_first;
+      Cell* cell = get_cell(elements[first]);
+      
+      if(cell->first != first)
+	{
+	  assert(cell->first < first);
+	  assert(cell->split_level <= dest_refinement_stack_size);
+	  goto done;
+	}
+      assert(cell->split_level > dest_refinement_stack_size);
+
+      while(cell->split_level > dest_refinement_stack_size)
+	{
+	  assert(cell->prev);
+	  cell = cell->prev;
+	}
+      while(cell->next and
+	    cell->next->split_level > dest_refinement_stack_size)
+	{
+	  /* Merge next cell */
+	  Cell* const next_cell = cell->next;
+	  if(cell->length == 1)
+	    discrete_cell_count--;
+	  if(next_cell->length == 1)
+	    discrete_cell_count--;
+	  /* Update element_to_cell_map values of elements added in cell */
+	  unsigned int* ep = elements + next_cell->first;
+	  unsigned int* const lp = ep + next_cell->length;
+	  for( ; ep < lp; ep++)
+	    element_to_cell_map[*ep] = cell;
+	  /* Update cell parameters */
+	  cell->length += next_cell->length;
+	  if(next_cell->next)
+	    next_cell->next->prev = cell;
+	  cell->next = next_cell->next;
+	  /* (Pseudo)free next_cell */
+	  next_cell->first = 0;
+	  next_cell->length = 0;
+	  next_cell->prev = 0;
+	  next_cell->next = free_cells;
+	  free_cells = next_cell;
+	}
+
+    done:
+      if(i.prev_nonsingleton_first >= 0)
+	{
+	  Cell* const prev_cell = get_cell(elements[i.prev_nonsingleton_first]);
+	  cell->prev_nonsingleton = prev_cell;
+	  prev_cell->next_nonsingleton = cell;
+	}
+      else
+	{
+	  //assert(cell->prev_nonsingleton == 0);
+	  cell->prev_nonsingleton = 0;
+	  first_nonsingleton_cell = cell;
+	}
+
+      if(i.next_nonsingleton_first >= 0)
+	{
+	  Cell* const next_cell = get_cell(elements[i.next_nonsingleton_first]);
+	  cell->next_nonsingleton = next_cell;
+	  next_cell->prev_nonsingleton = cell;
+	}
+      else
+	{
+	  //assert(cell->next_nonsingleton == 0);
+	  cell->next_nonsingleton = 0;
+	}
+    }
+
+}
+
+
+
+Partition::Cell*
+Partition::individualize(Partition::Cell * const cell,
+			 const unsigned int element)
+{
+
+  unsigned int * const pos = in_pos[element];
+
+  const unsigned int last = cell->first + cell->length - 1;
+  *pos = elements[last];
+  in_pos[*pos] = pos;
+  elements[last] = element;
+  in_pos[element] = elements + last;
+  
+  Partition::Cell * const new_cell = aux_split_in_two(cell, cell->length-1);
+  element_to_cell_map[element] = new_cell;
+
+  return new_cell;
+} 
+
+
+
+Partition::Cell*
+Partition::aux_split_in_two(Partition::Cell* const cell,
+			    const unsigned int first_half_size)
+{
+  RefInfo i;
+
+
+  /* (Pseudo)allocate new cell */
+  Cell * const new_cell = free_cells;
+  free_cells = new_cell->next;
+  /* Update new cell parameters */
+  new_cell->first = cell->first + first_half_size;
+  new_cell->length = cell->length - first_half_size;
+  new_cell->next = cell->next;
+  if(new_cell->next)
+    new_cell->next->prev = new_cell;
+  new_cell->prev = cell;
+  new_cell->split_level = refinement_stack.size()+1;
+  /* Update old, splitted cell parameters */
+  cell->length = first_half_size;
+  cell->next = new_cell;
+  /* CR */
+  if(cr_enabled)
+    cr_create_at_level_trailed(new_cell->first, cr_get_level(cell->first));
+
+  /* Add cell in refinement_stack for backtracking */
+  i.split_cell_first = new_cell->first;
+  if(cell->prev_nonsingleton)
+    i.prev_nonsingleton_first = cell->prev_nonsingleton->first;
+  else
+    i.prev_nonsingleton_first = -1;
+  if(cell->next_nonsingleton)
+    i.next_nonsingleton_first = cell->next_nonsingleton->first;
+  else
+    i.next_nonsingleton_first = -1;
+  refinement_stack.push(i);
+
+  /* Modify nonsingleton cell list */
+  if(new_cell->length > 1)
+    {
+      new_cell->prev_nonsingleton = cell;
+      new_cell->next_nonsingleton = cell->next_nonsingleton;
+      if(new_cell->next_nonsingleton)
+	new_cell->next_nonsingleton->prev_nonsingleton = new_cell;
+      cell->next_nonsingleton = new_cell;
+    }
+  else
+    {
+      new_cell->next_nonsingleton = 0;
+      new_cell->prev_nonsingleton = 0;
+      discrete_cell_count++;
+    }
+
+  if(cell->is_unit())
+    {
+      if(cell->prev_nonsingleton)
+	cell->prev_nonsingleton->next_nonsingleton = cell->next_nonsingleton;
+      else
+	first_nonsingleton_cell = cell->next_nonsingleton;
+      if(cell->next_nonsingleton)
+	cell->next_nonsingleton->prev_nonsingleton = cell->prev_nonsingleton;
+      cell->next_nonsingleton = 0;
+      cell->prev_nonsingleton = 0;
+      discrete_cell_count++;
+    }
+
+  return new_cell;
+} 
+
+
+
+size_t
+Partition::print(FILE* const fp, const bool add_newline) const
+{
+  size_t r = 0;
+  const char* cell_sep = "";
+  r += fprintf(fp, "[");
+  for(Cell* cell = first_cell; cell; cell = cell->next)
+    {
+      /* Print cell */
+      r += fprintf(fp, "%s{", cell_sep);
+      cell_sep = ",";
+      const char* elem_sep = "";
+      for(unsigned int i = 0; i < cell->length; i++)
+	{
+	  r += fprintf(fp, "%s%u", elem_sep, elements[cell->first + i]);
+	  elem_sep = ",";
+	}
+      r += fprintf(fp, "}");
+    }
+  r += fprintf(fp, "]");
+  if(add_newline) r += fprintf(fp, "\n");
+  return r;
+}
+
+
+
+size_t
+Partition::print_signature(FILE* const fp, const bool add_newline) const
+{
+  size_t r = 0;
+  const char* cell_sep = "";
+  r += fprintf(fp, "[");
+  for(Cell* cell = first_cell; cell; cell = cell->next)
+    {
+      if(cell->is_unit()) continue;
+      //fprintf(fp, "%s%u", cell_sep, cr_cells[cell->first].level);
+      r += fprintf(fp, "%s%u", cell_sep, cell->length);
+      cell_sep = ",";
+    }
+  r += fprintf(fp, "]");
+  if(add_newline) r += fprintf(fp, "\n");
+  return r;
+}
+
+
+
+void
+Partition::splitting_queue_add(Cell* const cell)
+{
+  static const unsigned int smallish_cell_threshold = 1;
+  cell->in_splitting_queue = true;
+  if(cell->length <= smallish_cell_threshold)
+    splitting_queue.push_front(cell);
+  else
+    splitting_queue.push_back(cell);    
+}
+
+
+
+void
+Partition::splitting_queue_clear()
+{
+  while(!splitting_queue_is_empty())
+    splitting_queue_pop();
+}
+
+
+
+
+
+/*
+ * Assumes that the invariant values are NOT the same
+ * and that the cell contains more than one element
+ */
+Partition::Cell*
+Partition::sort_and_split_cell1(Partition::Cell* const cell)
+{
+#if defined(BLISS_EXPENSIVE_CONSISTENCY_CHECKS)
+  assert(cell->length > 1);
+  assert(cell->first + cell->length <= N);
+  unsigned int nof_0_found = 0;
+  unsigned int nof_1_found = 0;
+  for(unsigned int i = cell->first; i < cell->first + cell->length; i++)
+    {
+      const unsigned int ival = invariant_values[elements[i]];
+      assert(ival == 0 or ival == 1);
+      if(ival == 0) nof_0_found++;
+      else nof_1_found++;
+    }
+  assert(nof_0_found > 0);
+  assert(nof_1_found > 0);
+  assert(nof_1_found == cell->max_ival_count);
+  assert(nof_0_found + nof_1_found == cell->length);
+  assert(cell->max_ival == 1);
+#endif
+
+
+  /* (Pseudo)allocate new cell */
+  Cell* const new_cell = free_cells;
+  free_cells = new_cell->next;
+
+#define NEW_SORT1
+#ifdef NEW_SORT1
+      unsigned int *ep0 = elements + cell->first;
+      unsigned int *ep1 = ep0 + cell->length - cell->max_ival_count;
+      if(cell->max_ival_count > cell->length / 2)
+	{
+	  /* There are more ones than zeros, only move zeros */
+	  unsigned int * const end = ep0 + cell->length;
+	  while(ep1 < end)
+	    {
+	      while(invariant_values[*ep1] == 0)
+		{
+		  const unsigned int tmp = *ep1;
+		  *ep1 = *ep0;
+		  *ep0 = tmp;
+		  in_pos[tmp] = ep0;
+		  in_pos[*ep1] = ep1;
+		  ep0++;
+		}
+	      element_to_cell_map[*ep1] = new_cell;
+	      invariant_values[*ep1] = 0;
+	      ep1++;
+	    }
+	}
+      else
+	{
+	  /* There are more zeros than ones, only move ones */
+	  unsigned int * const end = ep1;
+	  while(ep0 < end)
+	    {
+	      while(invariant_values[*ep0] != 0)
+		{
+		  const unsigned int tmp = *ep0;
+		  *ep0 = *ep1;
+		  *ep1 = tmp;
+		  in_pos[tmp] = ep1;
+		  in_pos[*ep0] = ep0;
+		  ep1++;
+		}
+	      ep0++;
+	    }
+	  ep1 = end;
+	  while(ep1 < elements + cell->first + cell->length)
+	    {
+	      element_to_cell_map[*ep1] = new_cell;
+	      invariant_values[*ep1] = 0;
+	      ep1++;
+	    }
+	}
+  /* Update new cell parameters */
+  new_cell->first = cell->first + cell->length - cell->max_ival_count;
+  new_cell->length = cell->length - (new_cell->first - cell->first);
+  new_cell->next = cell->next;
+  if(new_cell->next)
+    new_cell->next->prev = new_cell;
+  new_cell->prev = cell;
+  new_cell->split_level = refinement_stack.size()+1;
+  /* Update old, splitted cell parameters */
+  cell->length = new_cell->first - cell->first;
+  cell->next = new_cell;
+  /* CR */
+  if(cr_enabled)
+    cr_create_at_level_trailed(new_cell->first, cr_get_level(cell->first));
+
+#else
+  /* Sort vertices in the cell according to the invariant values */
+  unsigned int *ep0 = elements + cell->first;
+  unsigned int *ep1 = ep0 + cell->length;
+  while(ep1 > ep0)
+    {
+      const unsigned int element = *ep0;
+      const unsigned int ival = invariant_values[element];
+      invariant_values[element] = 0;
+      if(ival == 0)
+	{
+	  ep0++;
+	}
+      else
+	{
+	  ep1--;
+	  *ep0 = *ep1;
+	  *ep1 = element;
+	  element_to_cell_map[element] = new_cell;
+	  in_pos[element] = ep1;
+	  in_pos[*ep0] = ep0;
+	}
+    }
+
+
+  /* Update new cell parameters */
+  new_cell->first = ep1 - elements;
+  new_cell->length = cell->length - (new_cell->first - cell->first);
+  new_cell->next = cell->next;
+  if(new_cell->next)
+    new_cell->next->prev = new_cell;
+  new_cell->prev = cell;
+  new_cell->split_level = cell->split_level;
+  /* Update old, splitted cell parameters */
+  cell->length = new_cell->first - cell->first;
+  cell->next = new_cell;
+  cell->split_level = refinement_stack.size()+1;
+  /* CR */
+  if(cr_enabled)
+    cr_create_at_level_trailed(new_cell->first, cr_get_level(cell->first));
+
+#endif /* ifdef NEW_SORT1*/
+
+  /* Add cell in refinement stack for backtracking */
+  {
+    RefInfo i;
+    i.split_cell_first = new_cell->first;
+    if(cell->prev_nonsingleton)
+      i.prev_nonsingleton_first = cell->prev_nonsingleton->first;
+    else
+      i.prev_nonsingleton_first = -1;
+    if(cell->next_nonsingleton)
+      i.next_nonsingleton_first = cell->next_nonsingleton->first;
+    else
+      i.next_nonsingleton_first = -1;
+    /* Modify nonsingleton cell list */
+    if(new_cell->length > 1)
+      {
+	new_cell->prev_nonsingleton = cell;
+	new_cell->next_nonsingleton = cell->next_nonsingleton;
+	if(new_cell->next_nonsingleton)
+	  new_cell->next_nonsingleton->prev_nonsingleton = new_cell;
+	cell->next_nonsingleton = new_cell;
+      }
+    else
+      {
+	new_cell->next_nonsingleton = 0;
+	new_cell->prev_nonsingleton = 0;
+	discrete_cell_count++;
+      }
+    if(cell->is_unit())
+      {
+	if(cell->prev_nonsingleton)
+	  cell->prev_nonsingleton->next_nonsingleton = cell->next_nonsingleton;
+	else
+	  first_nonsingleton_cell = cell->next_nonsingleton;
+	if(cell->next_nonsingleton)
+	  cell->next_nonsingleton->prev_nonsingleton = cell->prev_nonsingleton;
+	cell->next_nonsingleton = 0;
+	cell->prev_nonsingleton = 0;
+	discrete_cell_count++;
+      }
+    refinement_stack.push(i);
+  }
+
+
+  /* Add cells in splitting queue */
+  if(cell->in_splitting_queue) {
+    /* Both cells must be included in splitting_queue in order to have
+       refinement to equitable partition */
+    splitting_queue_add(new_cell);
+  } else {
+    Cell *min_cell, *max_cell;
+    if(cell->length <= new_cell->length) {
+      min_cell = cell;
+      max_cell = new_cell;
+    } else {
+      min_cell = new_cell;
+      max_cell = cell;
+    }
+    /* Put the smaller cell in splitting_queue */
+    splitting_queue_add(min_cell);
+    if(max_cell->is_unit()) {
+      /* Put the "larger" cell also in splitting_queue */
+      splitting_queue_add(max_cell);
+    }
+  }
+
+
+  return new_cell;
+}
+
+
+
+
+
+/**
+ * An auxiliary function for distribution count sorting.
+ * Build start array so that
+ * dcs_start[0] = 0 and dcs_start[i+1] = dcs_start[i] + dcs_count[i].
+ */
+void
+Partition::dcs_cumulate_count(const unsigned int max) 
+{
+  unsigned int* count_p = dcs_count;
+  unsigned int* start_p = dcs_start;
+  unsigned int sum = 0;
+  for(unsigned int i = max+1; i > 0; i--)
+    {
+      *start_p = sum;
+      start_p++;
+      sum += *count_p;
+      count_p++;
+    }
+}
+
+
+/**
+ * Distribution count sorting of cells with invariant values less than 256.
+ */
+Partition::Cell*
+Partition::sort_and_split_cell255(Partition::Cell* const cell,
+				  const unsigned int max_ival)
+{
+
+  if(cell->is_unit())
+    {
+      /* Reset invariant value */
+      invariant_values[elements[cell->first]] = 0;
+      return cell;
+    }
+  
+#ifdef BLISS_CONSISTENCY_CHECKS
+  for(unsigned int i = 0; i < 256; i++)
+    assert(dcs_count[i] == 0);
+#endif
+
+  /*
+   * Compute the distribution of invariant values to the count array
+   */
+  {
+    const unsigned int *ep = elements + cell->first;
+    const unsigned int ival = invariant_values[*ep];
+    dcs_count[ival]++;
+    ep++;
+#if defined(BLISS_CONSISTENCY_CHECKS)
+    bool equal_invariant_values = true;
+#endif
+    for(unsigned int i = cell->length - 1; i != 0; i--)
+      {
+	const unsigned int ival2 = invariant_values[*ep];
+	dcs_count[ival2]++;
+#if defined(BLISS_CONSISTENCY_CHECKS)
+	if(ival2 != ival) {
+	  equal_invariant_values = false;
+	}
+#endif
+	ep++;
+      }
+#if defined(BLISS_CONSISTENCY_CHECKS)
+    assert(!equal_invariant_values);
+    if(equal_invariant_values) {
+      assert(dcs_count[ival] == cell->length);
+      dcs_count[ival] = 0;
+      clear_ivs(cell);
+      return cell;
+    }
+#endif
+  }
+
+  /* Build start array */
+  dcs_cumulate_count(max_ival);
+
+
+  /* Do the sorting */
+  for(unsigned int i = 0; i <= max_ival; i++)
+    {
+      unsigned int *ep = elements + cell->first + dcs_start[i];
+      for(unsigned int j = dcs_count[i]; j > 0; j--)
+	{
+	  while(true)
+	    {
+	      const unsigned int element = *ep;
+	      const unsigned int ival = invariant_values[element];
+	      if(ival == i)
+		break;
+	      *ep = elements[cell->first + dcs_start[ival]];
+	      elements[cell->first + dcs_start[ival]] = element;
+	      dcs_start[ival]++;
+	      dcs_count[ival]--;
+	    }
+	  ep++;
+	}
+      dcs_count[i] = 0;
+    }
+
+#if defined(BLISS_CONSISTENCY_CHECKS)
+  for(unsigned int i = 0; i < 256; i++)
+    assert(dcs_count[i] == 0);
+#endif
+
+  /* split cell */
+  Cell* const new_cell = split_cell(cell);
+  return new_cell;
+}
+
+
+
+/*
+ * Sort the elements in a cell according to their invariant values.
+ * The invariant values are not cleared.
+ * Warning: the in_pos array is left in incorrect state.
+ */
+bool
+Partition::shellsort_cell(Partition::Cell* const cell)
+{
+  unsigned int h;
+  unsigned int* ep;
+
+
+  if(cell->is_unit())
+    return false;
+
+  /* Check whether all the elements have the same invariant value */
+  bool equal_invariant_values = true;
+  {
+    ep = elements + cell->first;
+    const unsigned int ival = invariant_values[*ep];
+    ep++;
+    for(unsigned int i = cell->length - 1; i > 0; i--)
+      {
+	if(invariant_values[*ep] != ival) {
+	  equal_invariant_values = false;
+	  break;
+	}
+	ep++;
+      }
+  }
+  if(equal_invariant_values)
+    return false;
+
+  ep = elements + cell->first;
+
+  for(h = 1; h <= cell->length/9; h = 3*h + 1)
+    ;
+  for( ; h > 0; h = h/3) {
+    for(unsigned int i = h; i < cell->length; i++) {
+      const unsigned int element = ep[i];
+      const unsigned int ival = invariant_values[element];
+      unsigned int j = i;
+      while(j >= h and invariant_values[ep[j-h]] > ival) {
+        ep[j] = ep[j-h];
+        j -= h;
+      }
+      ep[j] = element;
+    }
+  }
+  return true;
+}
+
+
+
+void
+Partition::clear_ivs(Cell* const cell)
+{
+  unsigned int* ep = elements + cell->first;
+  for(unsigned int i = cell->length; i > 0; i--, ep++)
+    invariant_values[*ep] = 0;
+}
+
+
+/*
+ * Assumes that the elements in the cell are sorted according to their
+ * invariant values.
+ */
+Partition::Cell*
+Partition::split_cell(Partition::Cell* const original_cell)
+{
+  Cell* cell = original_cell;
+  const bool original_cell_was_in_splitting_queue =
+    original_cell->in_splitting_queue;
+  Cell* largest_new_cell = 0;
+
+  while(true) 
+    {
+      unsigned int* ep = elements + cell->first;
+      const unsigned int* const lp = ep + cell->length;
+      const unsigned int ival = invariant_values[*ep];
+      invariant_values[*ep] = 0;
+      element_to_cell_map[*ep] = cell;
+      in_pos[*ep] = ep;
+      ep++;
+      while(ep < lp)
+	{
+	  const unsigned int e = *ep;
+	  if(invariant_values[e] != ival)
+	    break;
+	  invariant_values[e] = 0;
+	  in_pos[e] = ep;
+	  ep++;
+	  element_to_cell_map[e] = cell;
+	}
+      if(ep == lp)
+	break;
+      
+      Cell* const new_cell = aux_split_in_two(cell,
+					      (ep - elements) - cell->first);
+      
+      if(graph and graph->compute_eqref_hash)
+	{
+	  graph->eqref_hash.update(new_cell->first);
+	  graph->eqref_hash.update(new_cell->length);
+	  graph->eqref_hash.update(ival);
+	}
+      
+      /* Add cells in splitting_queue */
+      assert(!new_cell->is_in_splitting_queue());
+      if(original_cell_was_in_splitting_queue)
+	{
+	  /* In this case, all new cells are inserted in splitting_queue */
+	  assert(cell->is_in_splitting_queue());
+	  splitting_queue_add(new_cell);
+	}
+      else
+	{
+	  /* Otherwise, we can omit one new cell from splitting_queue */
+	  assert(!cell->is_in_splitting_queue());
+	  if(largest_new_cell == 0) {
+	    largest_new_cell = cell;
+	  } else {
+	    assert(!largest_new_cell->is_in_splitting_queue());
+	    if(cell->length > largest_new_cell->length) {
+	      splitting_queue_add(largest_new_cell);
+	      largest_new_cell = cell;
+	    } else {
+	      splitting_queue_add(cell);
+	    }
+	  }
+	}
+      /* Process the rest of the cell */
+      cell = new_cell;
+    }
+
+  
+  if(original_cell == cell) {
+    /* All the elements in cell had the same invariant value */
+    return cell;
+  }
+
+  /* Add cells in splitting_queue */
+  if(!original_cell_was_in_splitting_queue)
+    {
+      /* Also consider the last new cell */
+      assert(largest_new_cell);
+      if(cell->length > largest_new_cell->length)
+	{
+	  splitting_queue_add(largest_new_cell);
+	  largest_new_cell = cell;
+	}
+      else
+	{
+	  splitting_queue_add(cell);
+	}
+      if(largest_new_cell->is_unit())
+	{
+	  /* Needed in certificate computation */
+	  splitting_queue_add(largest_new_cell);
+	}
+    }
+
+  return cell;
+}
+
+
+Partition::Cell*
+Partition::zplit_cell(Partition::Cell* const cell,
+		      const bool max_ival_info_ok)
+{
+
+  Cell* last_new_cell = cell;
+
+  if(!max_ival_info_ok)
+    {
+      /* Compute max_ival info */
+      assert(cell->max_ival == 0);
+      assert(cell->max_ival_count == 0);
+      unsigned int *ep = elements + cell->first;
+      for(unsigned int i = cell->length; i > 0; i--, ep++)
+	{
+	  const unsigned int ival = invariant_values[*ep];
+	  if(ival > cell->max_ival)
+	    {
+	      cell->max_ival = ival;
+	      cell->max_ival_count = 1;
+	    }
+	  else if(ival == cell->max_ival)
+	    {
+	      cell->max_ival_count++;
+	    }
+	}
+    }
+
+#ifdef BLISS_CONSISTENCY_CHECKS
+  /* Verify max_ival info */
+  {
+    unsigned int nof_zeros = 0;
+    unsigned int max_ival = 0;
+    unsigned int max_ival_count = 0;
+    unsigned int *ep = elements + cell->first;
+    for(unsigned int i = cell->length; i > 0; i--, ep++)
+      {
+	const unsigned int ival = invariant_values[*ep];
+	if(ival == 0)
+	  nof_zeros++;
+	if(ival > max_ival)
+	  {
+	    max_ival = ival;
+	    max_ival_count = 1;
+	  }
+	else if(ival == max_ival)
+	  max_ival_count++;
+      }
+    assert(max_ival == cell->max_ival);
+    assert(max_ival_count == cell->max_ival_count);
+  }
+#endif
+
+  /* max_ival info has been computed */
+
+  if(cell->max_ival_count == cell->length)
+    {
+      /* All invariant values are the same, clear 'em */
+      if(cell->max_ival > 0)
+	clear_ivs(cell);
+    }
+  else
+    {
+      /* All invariant values are not the same */
+      if(cell->max_ival == 1)
+	{
+	  /* Specialized splitting for cells with binary invariant values */
+	  last_new_cell = sort_and_split_cell1(cell);
+	}
+      else if(cell->max_ival < 256)
+	{
+	  /* Specialized splitting for cells with invariant values < 256 */
+	  last_new_cell = sort_and_split_cell255(cell, cell->max_ival);
+	}
+      else
+	{
+	  /* Generic sorting and splitting */
+	  const bool sorted = shellsort_cell(cell);
+	  assert(sorted);
+	  last_new_cell = split_cell(cell);
+	}
+    }
+  cell->max_ival = 0;
+  cell->max_ival_count = 0;
+  return last_new_cell;
+}
+
+
+
+/*
+ *
+ * Component recursion specific code
+ *
+ */
+void
+Partition::cr_init()
+{
+  assert(bt_stack.empty());
+
+  cr_enabled = true;
+
+  if(cr_cells) free(cr_cells);
+  cr_cells = (CRCell*)malloc(N * sizeof(CRCell));
+  if(!cr_cells) {assert(false && "Mem out"); }
+
+  if(cr_levels) free(cr_levels);
+  cr_levels = (CRCell**)malloc(N * sizeof(CRCell*));
+  if(!cr_levels) {assert(false && "Mem out"); }
+
+  for(unsigned int i = 0; i < N; i++) {
+    cr_levels[i] = 0;
+    cr_cells[i].level = UINT_MAX;
+    cr_cells[i].next = 0;
+    cr_cells[i].prev_next_ptr = 0;
+  }
+
+  for(const Cell *cell = first_cell; cell; cell = cell->next)
+    cr_create_at_level_trailed(cell->first, 0);
+
+  cr_max_level = 0;
+}
+
+
+void
+Partition::cr_free()
+{
+  if(cr_cells) {free(cr_cells); cr_cells = 0; }
+  if(cr_levels) {free(cr_levels); cr_levels = 0; }
+
+  cr_created_trail.clear();
+  cr_splitted_level_trail.clear();
+  cr_bt_info.clear();
+  cr_max_level = 0;
+
+  cr_enabled = false;
+}
+
+
+unsigned int
+Partition::cr_split_level(const unsigned int level,
+			  const std::vector<unsigned int>& splitted_cells)
+{
+  assert(cr_enabled);
+  assert(level <= cr_max_level);
+  cr_levels[++cr_max_level] = 0;
+  cr_splitted_level_trail.push_back(level);
+
+  for(unsigned int i = 0; i < splitted_cells.size(); i++)
+    {
+      const unsigned int cell_index = splitted_cells[i];
+      assert(cell_index < N);
+      CRCell& cr_cell = cr_cells[cell_index];
+      assert(cr_cell.level == level);
+      cr_cell.detach();
+      cr_create_at_level(cell_index, cr_max_level);
+    }
+
+  return cr_max_level;
+}
+
+
+unsigned int
+Partition::cr_get_backtrack_point()
+{
+  assert(cr_enabled);
+  CR_BTInfo info;
+  info.created_trail_index = cr_created_trail.size();
+  info.splitted_level_trail_index = cr_splitted_level_trail.size();
+  cr_bt_info.push_back(info);
+  return cr_bt_info.size()-1;
+}
+
+
+void
+Partition::cr_goto_backtrack_point(const unsigned int btpoint)
+{
+  assert(cr_enabled);
+  assert(btpoint < cr_bt_info.size());
+  while(cr_created_trail.size() > cr_bt_info[btpoint].created_trail_index)
+    {
+      const unsigned int cell_index = cr_created_trail.back();
+      cr_created_trail.pop_back();
+      CRCell& cr_cell = cr_cells[cell_index];
+      assert(cr_cell.level != UINT_MAX);
+      assert(cr_cell.prev_next_ptr);
+      cr_cell.detach();
+    }
+
+  while(cr_splitted_level_trail.size() >
+	cr_bt_info[btpoint].splitted_level_trail_index)
+    {
+      const unsigned int dest_level = cr_splitted_level_trail.back();
+      cr_splitted_level_trail.pop_back();
+      assert(cr_max_level > 0);
+      assert(dest_level < cr_max_level);
+      while(cr_levels[cr_max_level]) {
+	CRCell *cr_cell = cr_levels[cr_max_level];
+	cr_cell->detach();
+	cr_create_at_level(cr_cell - cr_cells, dest_level);
+      }
+      cr_max_level--;
+    }
+  cr_bt_info.resize(btpoint);
+}
+
+
+void
+Partition::cr_create_at_level(const unsigned int cell_index,
+			      const unsigned int level)
+{
+  assert(cr_enabled);
+  assert(cell_index < N);
+  assert(level < N);
+  CRCell& cr_cell = cr_cells[cell_index];
+  assert(cr_cell.level == UINT_MAX);
+  assert(cr_cell.next == 0);
+  assert(cr_cell.prev_next_ptr == 0);
+  if(cr_levels[level])
+    cr_levels[level]->prev_next_ptr = &(cr_cell.next);
+  cr_cell.next = cr_levels[level];
+  cr_levels[level] = &cr_cell;
+  cr_cell.prev_next_ptr = &cr_levels[level];
+  cr_cell.level = level;
+}
+
+
+void
+Partition::cr_create_at_level_trailed(const unsigned int cell_index,
+				      const unsigned int level)
+{
+  assert(cr_enabled);
+  cr_create_at_level(cell_index, level);
+  cr_created_trail.push_back(cell_index);
+}
+
+
+} // namespace bliss
diff --git a/igraph/src/paths.c b/igraph/src/paths.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/paths.c
@@ -0,0 +1,175 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_interface.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_iterators.h"
+#include "igraph_adjlist.h"
+#include "igraph_stack.h"
+
+/**
+ * \function igraph_get_all_simple_paths
+ * List all simple paths from one source
+ *
+ * A path is simple, if its vertices are unique, no vertex
+ * is visited more than once.
+ *
+ * </para><para>
+ * Note that potentially there are exponentially many
+ * paths between two vertices of a graph, and you may
+ * run out of memory when using this function, if your
+ * graph is lattice-like.
+ *
+ * </para><para>
+ * This function currently ignored multiple and loop edges.
+ * \param graph The input graph.
+ * \param res Initialized integer vector, all paths are
+ *        returned here, separated by -1 markers. The paths
+ *        are included in arbitrary order, as they are found.
+ * \param from The start vertex.
+ * \param to The target vertices.
+ * \param cutoff Maximum length of path that is considered. If
+ *        negative, paths of all lengths are considered.
+ * \param mode The type of the paths to consider, it is ignored
+ *        for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(n!) in the worst case, n is the number of
+ * vertices.
+ */
+
+int igraph_get_all_simple_paths(const igraph_t *graph,
+                                igraph_vector_int_t *res,
+                                igraph_integer_t from,
+                                const igraph_vs_t to,
+                                igraph_integer_t cutoff,
+                                igraph_neimode_t mode) {
+
+    igraph_integer_t no_nodes = igraph_vcount(graph);
+    igraph_vit_t vit;
+    igraph_bool_t toall = igraph_vs_is_all(&to);
+    igraph_vector_char_t markto;
+    igraph_lazy_adjlist_t adjlist;
+    igraph_vector_int_t stack, dist;
+    igraph_vector_char_t added;
+    igraph_vector_int_t nptr;
+    int iteration;
+
+    if (from < 0 || from >= no_nodes) {
+        IGRAPH_ERROR("Invalid starting vertex", IGRAPH_EINVAL);
+    }
+
+    if (!toall) {
+        igraph_vector_char_init(&markto, no_nodes);
+        IGRAPH_FINALLY(igraph_vector_char_destroy, &markto);
+        IGRAPH_CHECK(igraph_vit_create(graph, to, &vit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+        for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+            VECTOR(markto)[ IGRAPH_VIT_GET(vit) ] = 1;
+        }
+        igraph_vit_destroy(&vit);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    IGRAPH_CHECK(igraph_vector_char_init(&added, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &added);
+    IGRAPH_CHECK(igraph_vector_int_init(&stack, 100));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &stack);
+    IGRAPH_CHECK(igraph_vector_int_init(&dist, 100));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &dist);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode,
+                                          /*simplify=*/ 1));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
+    IGRAPH_CHECK(igraph_vector_int_init(&nptr, no_nodes));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &nptr);
+
+    igraph_vector_int_clear(res);
+
+    igraph_vector_int_clear(&stack);
+    igraph_vector_int_clear(&dist);
+    igraph_vector_int_push_back(&stack, from);
+    igraph_vector_int_push_back(&dist, 0);
+    VECTOR(added)[from] = 1;
+    while (!igraph_vector_int_empty(&stack)) {
+        int act = igraph_vector_int_tail(&stack);
+        int curdist = igraph_vector_int_tail(&dist);
+        igraph_vector_t *neis = igraph_lazy_adjlist_get(&adjlist, act);
+        int n = igraph_vector_size(neis);
+        int *ptr = igraph_vector_int_e_ptr(&nptr, act);
+        igraph_bool_t any;
+        igraph_bool_t within_dist;
+        int nei;
+
+        if (iteration == 0) {
+            IGRAPH_ALLOW_INTERRUPTION();
+        }
+
+        within_dist = (curdist < cutoff || cutoff < 0);
+        if (within_dist) {
+            /* Search for a neighbor that was not yet visited */
+            any = 0;
+            while (!any && (*ptr) < n) {
+                nei = (int) VECTOR(*neis)[(*ptr)];
+                any = !VECTOR(added)[nei];
+                (*ptr) ++;
+            }
+        }
+        if (within_dist && any) {
+            /* There is such a neighbor, add it */
+            IGRAPH_CHECK(igraph_vector_int_push_back(&stack, nei));
+            IGRAPH_CHECK(igraph_vector_int_push_back(&dist, curdist + 1));
+            VECTOR(added)[nei] = 1;
+            /* Add to results */
+            if (toall || VECTOR(markto)[nei]) {
+                IGRAPH_CHECK(igraph_vector_int_append(res, &stack));
+                IGRAPH_CHECK(igraph_vector_int_push_back(res, -1));
+            }
+        } else {
+            /* There is no such neighbor, finished with the subtree */
+            int up = igraph_vector_int_pop_back(&stack);
+            igraph_vector_int_pop_back(&dist);
+            VECTOR(added)[up] = 0;
+            VECTOR(nptr)[up] = 0;
+        }
+
+        iteration++;
+        if (iteration >= 10000) {
+            iteration = 0;
+        }
+    }
+
+    igraph_vector_int_destroy(&nptr);
+    igraph_lazy_adjlist_destroy(&adjlist);
+    igraph_vector_int_destroy(&dist);
+    igraph_vector_int_destroy(&stack);
+    igraph_vector_char_destroy(&added);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    if (!toall) {
+        igraph_vector_char_destroy(&markto);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/plfit.c b/igraph/src/plfit.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/plfit.c
@@ -0,0 +1,778 @@
+/* plfit.c
+ *
+ * Copyright (C) 2010-2011 Tamas Nepusz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+#include "gss.h"
+#include "lbfgs.h"
+#include "platform.h"
+#include "plfit.h"
+#include "kolmogorov.h"
+#include "zeta.h"
+
+/* #define PLFIT_DEBUG */
+
+#define DATA_POINTS_CHECK \
+    if (n <= 0) { \
+        PLFIT_ERROR("no data points", PLFIT_EINVAL); \
+    }
+
+#define XMIN_CHECK_ZERO \
+    if (xmin <= 0) { \
+        PLFIT_ERROR("xmin must be greater than zero", PLFIT_EINVAL); \
+    }
+#define XMIN_CHECK_ONE \
+    if (xmin < 1) { \
+        PLFIT_ERROR("xmin must be at least 1", PLFIT_EINVAL); \
+    }
+
+static int double_comparator(const void *a, const void *b) {
+    const double *da = (const double*)a;
+    const double *db = (const double*)b;
+    return (*da > *db) - (*da < *db);
+}
+
+/**
+ * Given a sorted array of doubles, return another array that contains pointers
+ * into the array for the start of each block of identical elements.
+ *
+ * \param  begin          pointer to the beginning of the array
+ * \param  end            pointer to the first element after the end of the array
+ * \param  result_length  if not \c NULL, the number of unique elements in the
+ *                        given array is returned here
+ */
+static double** unique_element_pointers(double* begin, double* end, size_t* result_length) {
+    double* ptr = begin;
+    double** result;
+    double prev_x;
+    size_t num_elts = 15;
+    size_t used_elts = 0;
+
+    /* Special case: empty array */
+    if (begin == end) {
+        result = calloc(1, sizeof(double*));
+        if (result != 0) {
+            result[0] = 0;
+        }
+        return result;
+    }
+
+    /* Allocate initial result array, including the guard element */
+    result = calloc(num_elts+1, sizeof(double*));
+    if (result == 0)
+        return 0;
+
+    prev_x = *begin;
+    result[used_elts++] = begin;
+
+    /* Process the input array */
+    for (ptr = begin+1; ptr < end; ptr++) {
+        if (*ptr == prev_x)
+            continue;
+
+        /* New block found */
+        if (used_elts >= num_elts) {
+            /* Array full; allocate a new chunk */
+            num_elts = num_elts*2 + 1;
+            result = realloc(result, sizeof(double*) * (num_elts+1));
+            if (result == 0)
+                return 0;
+        }
+
+        /* Store the new element */
+        result[used_elts++] = ptr;
+        prev_x = *ptr;
+    }
+
+    /* Calculate the result length */
+    if (result_length != 0) {
+        *result_length = used_elts;
+    }
+
+    /* Add the guard entry to the end of the result */
+    result[used_elts++] = 0;
+
+    return result;
+}
+
+static void plfit_i_perform_finite_size_correction(plfit_result_t* result, size_t n) {
+    result->alpha = result->alpha * (n-1) / n + 1.0 / n;
+}
+
+/********** Continuous power law distribution fitting **********/
+
+void plfit_i_logsum_less_than_continuous(double* begin, double* end,
+        double xmin, double* result, size_t* m) {
+    double logsum = 0.0;
+    size_t count = 0;
+
+    for (; begin != end; begin++) {
+        if (*begin >= xmin) {
+            count++;
+            logsum += log(*begin / xmin);
+        }
+    }
+
+    *m = count;
+    *result = logsum;
+}
+
+double plfit_i_logsum_continuous(double* begin, double* end, double xmin) {
+    double logsum = 0.0;
+    for (; begin != end; begin++)
+        logsum += log(*begin / xmin);
+    return logsum;
+}
+
+int plfit_i_estimate_alpha_continuous(double* xs, size_t n,
+        double xmin, double* alpha) {
+    double result;
+    size_t m;
+
+    XMIN_CHECK_ZERO;
+
+    plfit_i_logsum_less_than_continuous(xs, xs+n, xmin, &result, &m);
+
+    if (m == 0) {
+        PLFIT_ERROR("no data point was larger than xmin", PLFIT_EINVAL);
+    }
+
+    *alpha = 1 + m / result;
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_i_estimate_alpha_continuous_sorted(double* xs, size_t n,
+        double xmin, double* alpha) {
+	double* end = xs+n;
+
+    XMIN_CHECK_ZERO;
+
+    for (; xs != end && *xs < xmin; xs++);
+    if (xs == end) {
+        PLFIT_ERROR("no data point was larger than xmin", PLFIT_EINVAL);
+    }
+
+    *alpha = 1 + (end-xs) / plfit_i_logsum_continuous(xs, end, xmin);
+
+    return PLFIT_SUCCESS;
+}
+
+static int plfit_i_ks_test_continuous(double* xs, double* xs_end,
+        const double alpha, const double xmin, double* D) {
+    /* Assumption: xs is sorted and cut off at xmin so the first element is
+     * always larger than or equal to xmin. */
+    double result = 0, n;
+    int m = 0;
+
+    n = xs_end - xs;
+
+    while (xs < xs_end) {
+        double d = fabs(1-pow(xmin / *xs, alpha-1) - m / n);
+
+        if (d > result)
+            result = d;
+
+        xs++; m++;
+    }
+
+    *D = result;
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_log_likelihood_continuous(double* xs, size_t n, double alpha,
+        double xmin, double* L) {
+    double logsum, c;
+    size_t m;
+
+    if (alpha <= 1) {
+        PLFIT_ERROR("alpha must be greater than one", PLFIT_EINVAL);
+    }
+    XMIN_CHECK_ZERO;
+
+    c = (alpha - 1) / xmin;
+    plfit_i_logsum_less_than_continuous(xs, xs+n, xmin, &logsum, &m);
+    *L = -alpha * logsum + log(c) * m;
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_estimate_alpha_continuous(double* xs, size_t n, double xmin,
+        const plfit_continuous_options_t* options, plfit_result_t *result) {
+    double *xs_copy;
+
+	if (!options)
+		options = &plfit_continuous_default_options;
+
+    /* Make a copy of xs and sort it */
+    xs_copy = (double*)malloc(sizeof(double) * n);
+    memcpy(xs_copy, xs, sizeof(double) * n);
+    qsort(xs_copy, n, sizeof(double), double_comparator);
+
+    PLFIT_CHECK(plfit_estimate_alpha_continuous_sorted(xs_copy, n, xmin,
+				options, result));
+
+    free(xs_copy);
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_estimate_alpha_continuous_sorted(double* xs, size_t n, double xmin,
+        const plfit_continuous_options_t* options, plfit_result_t *result) {
+    double* end;
+
+	if (!options)
+		options = &plfit_continuous_default_options;
+
+	end = xs + n;
+    while (xs < end && *xs < xmin)
+        xs++;
+    n = (size_t) (end - xs);
+
+    PLFIT_CHECK(plfit_i_estimate_alpha_continuous_sorted(xs, n,
+				xmin, &result->alpha));
+    PLFIT_CHECK(plfit_i_ks_test_continuous(xs, end, result->alpha,
+				xmin, &result->D));
+
+    if (options->finite_size_correction)
+        plfit_i_perform_finite_size_correction(result, n);
+    result->xmin = xmin;
+    result->p = plfit_ks_test_one_sample_p(result->D, n);
+    plfit_log_likelihood_continuous(xs, n, result->alpha, result->xmin, &result->L);
+
+    return PLFIT_SUCCESS;
+}
+
+typedef struct {
+	double *begin;        /**< Pointer to the beginning of the array holding the data */
+	double *end;          /**< Pointer to after the end of the array holding the data */
+	double **uniques;     /**< Pointers to unique elements of the input array */
+	plfit_result_t last;  /**< Result of the last evaluation */
+} plfit_continuous_xmin_opt_data_t;
+
+double plfit_i_continuous_xmin_opt_evaluate(void* instance, double x) {
+	plfit_continuous_xmin_opt_data_t* data = (plfit_continuous_xmin_opt_data_t*)instance;
+	double* begin = data->uniques[(int)x];
+
+	data->last.xmin = *begin;
+
+#ifdef PLFIT_DEBUG
+	printf("Trying with xmin = %.4f\n", *begin);
+#endif
+
+	plfit_i_estimate_alpha_continuous_sorted(begin, (size_t) (data->end-begin), *begin,
+			&data->last.alpha);
+	plfit_i_ks_test_continuous(begin, data->end, data->last.alpha, *begin,
+			&data->last.D);
+
+	return data->last.D;
+}
+
+int plfit_i_continuous_xmin_opt_progress(void* instance, double x, double fx,
+		double min, double fmin, double left, double right, int k) {
+#ifdef PLFIT_DEBUG
+    printf("Iteration #%d: [%.4f; %.4f), x=%.4f, fx=%.4f, min=%.4f, fmin=%.4f\n",
+            k, left, right, x, fx, min, fmin);
+#endif
+
+	/* Continue only if `left' and `right' point to different integers */
+	return (int)left == (int)right;
+}
+
+int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* options,
+        plfit_result_t* result) {
+	gss_parameter_t gss_param;
+	plfit_continuous_xmin_opt_data_t opt_data;
+	plfit_result_t best_result;
+	int success;
+	size_t i, best_n, num_uniques;
+    double x, *px;
+
+    DATA_POINTS_CHECK;
+
+	if (!options)
+		options = &plfit_continuous_default_options;
+
+    /* Make a copy of xs and sort it */
+    opt_data.begin = (double*)malloc(sizeof(double) * n);
+    memcpy(opt_data.begin, xs, sizeof(double) * n);
+    qsort(opt_data.begin, n, sizeof(double), double_comparator);
+    opt_data.end = opt_data.begin + n;
+
+    /* Create an array containing pointers to the unique elements of the input. From
+     * each block of unique elements, we add the pointer to the first one. */
+    opt_data.uniques = unique_element_pointers(opt_data.begin, opt_data.end,
+			&num_uniques);
+    if (opt_data.uniques == 0)
+        return PLFIT_ENOMEM;
+
+    /* We will now determine the best xmin that yields the lowest D-score.
+	 * First we try a golden section search if needed. If that fails, we try
+	 * a linear search.
+     */
+	if (options->xmin_method == PLFIT_GSS_OR_LINEAR && num_uniques > 5) {
+		gss_parameter_init(&gss_param);
+		success = (gss(0, num_uniques-5, &x, 0,
+				plfit_i_continuous_xmin_opt_evaluate,
+				plfit_i_continuous_xmin_opt_progress, &opt_data, &gss_param) == 0);
+		best_result = opt_data.last;
+		/* plfit_i_continuous_xmin_opt_evaluate will set opt_data.last to
+		 * indicate the location of the optimum and the value of D */
+	} else {
+		success = 0;
+	}
+
+	if (success) {
+		/* calculate best_n because we'll need it later. Luckily x indicates
+		 * the index in opt_data.uniques that we have to look up in order to
+		 * find the first element in the array that is included */
+		px = opt_data.uniques[(int)x];
+		best_n = (size_t) (opt_data.end-px+1);
+	} else {
+		/* GSS failed or skipped; try linear search */
+
+		/* Prepare some variables */
+		best_n = 0;
+		best_result.D = DBL_MAX;
+		best_result.xmin = 0;
+		best_result.alpha = 0;
+		
+		for (i = 0; i < num_uniques-1; i++) {
+			plfit_i_continuous_xmin_opt_evaluate(&opt_data, i);
+			if (opt_data.last.D < best_result.D) {
+				best_result = opt_data.last;
+				best_n = (size_t) (opt_data.end - 
+						   opt_data.uniques[i] + 1);
+			}
+		}
+	}
+
+    /* Get rid of the uniques array, we don't need it any more */
+    free(opt_data.uniques);
+
+    /* Sort out the result */
+    *result = best_result;
+    if (options->finite_size_correction)
+        plfit_i_perform_finite_size_correction(result, best_n);
+    result->p = plfit_ks_test_one_sample_p(result->D, best_n);
+    plfit_log_likelihood_continuous(opt_data.begin + n - best_n, best_n,
+			result->alpha, result->xmin, &result->L);
+
+    /* Get rid of the copied data as well */
+    free(opt_data.begin);
+
+    return PLFIT_SUCCESS;
+}
+
+/********** Discrete power law distribution fitting **********/
+
+typedef struct {
+    size_t m;
+    double logsum;
+    double xmin;
+} plfit_i_estimate_alpha_discrete_data_t;
+
+double plfit_i_logsum_discrete(double* begin, double* end, double xmin) {
+    double logsum = 0.0;
+    for (; begin != end; begin++)
+        logsum += log(*begin);
+    return logsum;
+}
+
+void plfit_i_logsum_less_than_discrete(double* begin, double* end, double xmin,
+        double* logsum, size_t* m) {
+    double result = 0.0;
+    size_t count = 0;
+
+    for (; begin != end; begin++) {
+        if (*begin < xmin)
+            continue;
+
+        result += log(*begin);
+        count++;
+    }
+
+    *logsum = result;
+    *m = count;
+}
+
+lbfgsfloatval_t plfit_i_estimate_alpha_discrete_lbfgs_evaluate(
+        void* instance, const lbfgsfloatval_t* x,
+        lbfgsfloatval_t* g, const int n,
+        const lbfgsfloatval_t step) {
+    plfit_i_estimate_alpha_discrete_data_t* data;
+    lbfgsfloatval_t result;
+    double dx = step;
+    double huge = 1e10;     /* pseudo-infinity; apparently DBL_MAX does not work */
+
+    data = (plfit_i_estimate_alpha_discrete_data_t*)instance;
+
+#ifdef PLFIT_DEBUG
+    printf("- Evaluating at %.4f (step = %.4f, xmin = %.4f)\n", *x, step, data->xmin);
+#endif
+
+	if (isnan(*x)) {
+		g[0] = huge;
+		return huge;
+	}
+
+    /* Find the delta X value to estimate the gradient */
+    if (dx > 0.001 || dx == 0)
+        dx = 0.001;
+    else if (dx < -0.001)
+        dx = -0.001;
+
+	/* Is x[0] in its valid range? */
+	if (x[0] <= 1.0) {
+		/* The Hurwitz zeta function is infinite in this case */
+        g[0] = (dx > 0) ? -huge : huge;
+		return huge;
+	}
+	if (x[0] + dx <= 1.0)
+		g[0] = huge;
+	else
+		g[0] = data->logsum + data->m *
+			(log(gsl_sf_hzeta(x[0] + dx, data->xmin)) - log(gsl_sf_hzeta(x[0], data->xmin))) / dx;
+
+    result = x[0] * data->logsum + data->m * log(gsl_sf_hzeta(x[0], data->xmin));
+
+#ifdef PLFIT_DEBUG
+    printf("  - Gradient: %.4f\n", g[0]);
+    printf("  - Result: %.4f\n", result);
+#endif
+
+    return result;
+}
+
+int plfit_i_estimate_alpha_discrete_lbfgs_progress(void* instance,
+        const lbfgsfloatval_t* x, const lbfgsfloatval_t* g,
+        const lbfgsfloatval_t fx, const lbfgsfloatval_t xnorm,
+        const lbfgsfloatval_t gnorm, const lbfgsfloatval_t step,
+        int n, int k, int ls) {
+    return 0;
+}
+
+int plfit_i_estimate_alpha_discrete_linear_scan(double* xs, size_t n, double xmin,
+        double* alpha, const plfit_discrete_options_t* options,
+		plfit_bool_t sorted) {
+    double curr_alpha, best_alpha, L, L_max;
+    double logsum;
+    size_t m;
+
+    XMIN_CHECK_ONE;
+	if (options->alpha.min <= 1.0) {
+		PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL);
+	}
+	if (options->alpha.max < options->alpha.min) {
+		PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL);
+	}
+	if (options->alpha.step <= 0) {
+		PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL);
+	}
+
+    if (sorted) {
+        logsum = plfit_i_logsum_discrete(xs, xs+n, xmin);
+        m = n;
+    } else {
+        plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &logsum, &m);
+    }
+
+    best_alpha = options->alpha.min; L_max = -DBL_MAX;
+    for (curr_alpha = options->alpha.min; curr_alpha <= options->alpha.max;
+			curr_alpha += options->alpha.step) {
+        L = -curr_alpha * logsum - m * log(gsl_sf_hzeta(curr_alpha, xmin));
+        if (L > L_max) {
+            L_max = L;
+            best_alpha = curr_alpha;
+        }
+    }
+
+    *alpha = best_alpha;
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_i_estimate_alpha_discrete_lbfgs(double* xs, size_t n, double xmin,
+		double* alpha, const plfit_discrete_options_t* options, plfit_bool_t sorted) {
+    lbfgs_parameter_t param;
+    lbfgsfloatval_t* variables;
+    plfit_i_estimate_alpha_discrete_data_t data;
+    int ret;
+
+    XMIN_CHECK_ONE;
+
+    /* Initialize algorithm parameters */
+    lbfgs_parameter_init(&param);
+    param.max_iterations = 0;   /* proceed until infinity */
+
+    /* Set up context for optimization */
+    data.xmin = xmin;
+    if (sorted) {
+        data.logsum = plfit_i_logsum_discrete(xs, xs+n, xmin);
+        data.m = n;
+    } else {
+        plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &data.logsum, &data.m);
+    }
+
+    /* Allocate space for the single alpha variable */
+    variables = lbfgs_malloc(1);
+    variables[0] = 3.0;       /* initial guess */
+
+    /* Optimization */
+    ret = lbfgs(1, variables, /* ptr_fx = */ 0,
+            plfit_i_estimate_alpha_discrete_lbfgs_evaluate,
+            plfit_i_estimate_alpha_discrete_lbfgs_progress,
+            &data, &param);
+
+    if (ret < 0 &&
+        ret != LBFGSERR_ROUNDING_ERROR &&
+        ret != LBFGSERR_MAXIMUMLINESEARCH &&
+        ret != LBFGSERR_CANCELED) {
+        char buf[4096];
+        snprintf(buf, 4096, "L-BFGS optimization signaled an error (error code = %d)", ret);
+        lbfgs_free(variables);
+        PLFIT_ERROR(buf, PLFIT_FAILURE);
+    }
+    *alpha = variables[0];
+    
+    /* Deallocate the variable array */
+    lbfgs_free(variables);
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_i_estimate_alpha_discrete_fast(double* xs, size_t n, double xmin,
+        double* alpha, const plfit_discrete_options_t* options, plfit_bool_t sorted) {
+	plfit_continuous_options_t cont_options;
+
+	if (!options)
+		options = &plfit_discrete_default_options;
+
+	plfit_continuous_options_init(&cont_options);
+	cont_options.finite_size_correction = options->finite_size_correction;
+
+    XMIN_CHECK_ONE;
+
+	if (sorted) {
+		return plfit_i_estimate_alpha_continuous_sorted(xs, n, xmin-0.5, alpha);
+	} else {
+		return plfit_i_estimate_alpha_continuous(xs, n, xmin-0.5, alpha);
+	}
+}
+
+int plfit_i_estimate_alpha_discrete(double* xs, size_t n, double xmin,
+		double* alpha, const plfit_discrete_options_t* options,
+		plfit_bool_t sorted) {
+	switch (options->alpha_method) {
+		case PLFIT_LBFGS:
+			PLFIT_CHECK(plfit_i_estimate_alpha_discrete_lbfgs(xs, n, xmin, alpha,
+						options, sorted));
+			break;
+
+		case PLFIT_LINEAR_SCAN:
+			PLFIT_CHECK(plfit_i_estimate_alpha_discrete_linear_scan(xs, n, xmin,
+						alpha, options, sorted));
+			break;
+
+		case PLFIT_PRETEND_CONTINUOUS:
+			PLFIT_CHECK(plfit_i_estimate_alpha_discrete_fast(xs, n, xmin,
+						alpha, options, sorted));
+			break;
+
+		default:
+			PLFIT_ERROR("unknown optimization method specified", PLFIT_EINVAL);
+	}
+
+	return PLFIT_SUCCESS;
+}
+
+static int plfit_i_ks_test_discrete(double* xs, double* xs_end, const double alpha,
+        const double xmin, double* D) {
+    /* Assumption: xs is sorted and cut off at xmin so the first element is
+     * always larger than or equal to xmin. */
+    double result = 0, n, hzeta, x;
+    int m = 0;
+
+    n = xs_end - xs;
+    hzeta = gsl_sf_hzeta(alpha, xmin);
+
+    while (xs < xs_end) {
+        double d;
+
+        x = *xs;
+        d = fabs(1-(gsl_sf_hzeta(alpha, x) / hzeta) - m / n);
+
+        if (d > result)
+            result = d;
+
+        do {
+            xs++; m++;
+        } while (xs < xs_end && *xs == x);
+    }
+
+    *D = result;
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_log_likelihood_discrete(double* xs, size_t n, double alpha, double xmin, double* L) {
+    double result;
+    size_t m;
+
+    if (alpha <= 1) {
+        PLFIT_ERROR("alpha must be greater than one", PLFIT_EINVAL);
+    }
+    XMIN_CHECK_ONE;
+
+    plfit_i_logsum_less_than_discrete(xs, xs+n, xmin, &result, &m);
+    result = - alpha * result - m * log(gsl_sf_hzeta(alpha, xmin));
+
+    *L = result;
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_estimate_alpha_discrete(double* xs, size_t n, double xmin,
+        const plfit_discrete_options_t* options, plfit_result_t *result) {
+    double *xs_copy, *end;
+
+	if (!options)
+		options = &plfit_discrete_default_options;
+
+	/* Check the validity of the input parameters */
+    DATA_POINTS_CHECK;
+	if (options->alpha_method == PLFIT_LINEAR_SCAN) {
+		if (options->alpha.min <= 1.0) {
+			PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL);
+		}
+		if (options->alpha.max < options->alpha.min) {
+			PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL);
+		}
+		if (options->alpha.step <= 0) {
+			PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL);
+		}
+	}
+
+    /* Make a copy of xs and sort it */
+    xs_copy = (double*)malloc(sizeof(double) * n);
+    memcpy(xs_copy, xs, sizeof(double) * n);
+    qsort(xs_copy, n, sizeof(double), double_comparator);
+
+    xs = xs_copy; end = xs_copy + n;
+    while (xs < end && *xs < xmin)
+        xs++;
+    n = (size_t) (end - xs);
+
+    PLFIT_CHECK(plfit_i_estimate_alpha_discrete(xs, n, xmin, &result->alpha,
+				options, /* sorted = */ 1));
+    PLFIT_CHECK(plfit_i_ks_test_discrete(xs, end, result->alpha, xmin, &result->D));
+
+    result->xmin = xmin;
+    if (options->finite_size_correction)
+        plfit_i_perform_finite_size_correction(result, n);
+    result->p = plfit_ks_test_one_sample_p(result->D, n);
+    plfit_log_likelihood_discrete(xs, n, result->alpha, result->xmin, &result->L);
+
+    free(xs_copy);
+
+    return PLFIT_SUCCESS;
+}
+
+int plfit_discrete(double* xs, size_t n, const plfit_discrete_options_t* options,
+        plfit_result_t* result) {
+    double curr_D, curr_alpha;
+    plfit_result_t best_result;
+    double *xs_copy, *px, *end, *end_xmin, prev_x;
+	size_t best_n;
+    size_t m;
+
+	if (!options)
+		options = &plfit_discrete_default_options;
+
+	/* Check the validity of the input parameters */
+    DATA_POINTS_CHECK;
+	if (options->alpha_method == PLFIT_LINEAR_SCAN) {
+		if (options->alpha.min <= 1.0) {
+			PLFIT_ERROR("alpha.min must be greater than 1.0", PLFIT_EINVAL);
+		}
+		if (options->alpha.max < options->alpha.min) {
+			PLFIT_ERROR("alpha.max must be greater than alpha.min", PLFIT_EINVAL);
+		}
+		if (options->alpha.step <= 0) {
+			PLFIT_ERROR("alpha.step must be positive", PLFIT_EINVAL);
+		}
+	}
+
+    /* Make a copy of xs and sort it */
+    xs_copy = (double*)malloc(sizeof(double) * n);
+    memcpy(xs_copy, xs, sizeof(double) * n);
+    qsort(xs_copy, n, sizeof(double), double_comparator);
+
+    best_result.D = DBL_MAX;
+    best_result.xmin = 1;
+    best_result.alpha = 1;
+	best_n = 0;
+
+    /* Make sure there are at least three distinct values if possible */
+    px = xs_copy; end = px + n; end_xmin = end - 1; m = 0;
+    prev_x = *end_xmin;
+    while (*end_xmin == prev_x && end_xmin > px)
+        end_xmin--;
+    prev_x = *end_xmin;
+    while (*end_xmin == prev_x && end_xmin > px)
+        end_xmin--;
+
+    prev_x = 0;
+    while (px < end_xmin) {
+        while (px < end_xmin && *px == prev_x) {
+            px++; m++;
+        }
+
+	plfit_i_estimate_alpha_discrete(px, n - m, *px,
+					&curr_alpha, options, /* sorted = */ 1);
+        plfit_i_ks_test_discrete(px, end, curr_alpha, *px, &curr_D);
+
+        if (curr_D < best_result.D) {
+            best_result.alpha = curr_alpha;
+            best_result.xmin = *px;
+            best_result.D = curr_D;
+	    best_n = n - m;
+        }
+
+        prev_x = *px;
+        px++; m++;
+    }
+
+    *result = best_result;
+    if (options->finite_size_correction)
+        plfit_i_perform_finite_size_correction(result, best_n);
+    result->p = plfit_ks_test_one_sample_p(result->D, best_n);
+    plfit_log_likelihood_discrete(xs_copy+(n-best_n), best_n,
+			result->alpha, result->xmin, &result->L);
+
+    free(xs_copy);
+
+    return PLFIT_SUCCESS;
+}
+
diff --git a/igraph/src/pottsmodel_2.cpp b/igraph/src/pottsmodel_2.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/pottsmodel_2.cpp
@@ -0,0 +1,2225 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Jörg Reichardt
+   This file was modified by Vincent Traag
+   The original copyright notice follows here */
+
+/***************************************************************************
+                          pottsmodel.cpp  -  description
+                             -------------------
+    begin                : Fri May 28 2004
+    copyright            : (C) 2004 by
+    email                :
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <cmath>
+#include "pottsmodel_2.h"
+#include "NetRoutines.h"
+
+using namespace std;
+
+#include "igraph_random.h"
+#include "igraph_interrupt_internal.h"
+#include "config.h"
+
+//#################################################################################################
+PottsModel::PottsModel(network *n, unsigned int qvalue, int m) : acceptance(0) {
+    DLList_Iter<NNode*> iter;
+    NNode *n_cur;
+    unsigned int *i_ptr;
+    net = n;
+    q = qvalue;
+    operation_mode = m;
+    k_max = 0;
+    //needed in calculating modularity
+    Qa     = new double[q + 1];
+    //weights for each spin state needed in Monte Carlo process
+    weights = new double[q + 1];
+    //bookkeeping of occupation numbers of spin states or the number of links in community
+    color_field = new double[q + 1];
+    neighbours = new double[q + 1];
+
+    num_of_nodes = net->node_list->Size();
+    num_of_links = net->link_list->Size();
+
+    n_cur = iter.First(net->node_list);
+    //these lists are needed to keep track of spin states for parallel update mode
+    new_spins = new DL_Indexed_List<unsigned int*>();
+    previous_spins = new DL_Indexed_List<unsigned int*>();
+    while (!iter.End()) {
+        if (k_max < n_cur->Get_Degree()) {
+            k_max = n_cur->Get_Degree();
+        }
+        i_ptr = new unsigned int;
+        *i_ptr = 0;
+        new_spins->Push(i_ptr);
+        i_ptr = new unsigned int;
+        *i_ptr = 0;
+        previous_spins->Push(i_ptr);
+        n_cur = iter.Next();
+    }
+    return;
+}
+//#######################################################
+//Destructor of PottsModel
+//########################################################
+PottsModel::~PottsModel() {
+    /* The DLItem destructor does not delete its item currently,
+       because of some bad design. As a workaround, we delete them here
+       by hand */
+    new_spins->delete_items();
+    previous_spins->delete_items();
+    delete new_spins;
+    delete previous_spins;
+    delete [] Qa;
+    delete [] weights;
+    delete [] color_field;
+    delete [] neighbours;
+    return;
+}
+//#####################################################
+//Assing an initial random configuration of spins to nodes
+//if called with negative argument or the spin used as argument
+//when called with positve one.
+//This may be handy, if you want to warm up the network.
+//####################################################
+unsigned long PottsModel::assign_initial_conf(int spin) {
+    int s;
+    DLList_Iter<NNode*> iter;
+    DLList_Iter<NLink*> l_iter;
+    NNode *n_cur;
+    NLink *l_cur;
+    double sum_weight;
+    double av_k_squared = 0.0;
+    double av_k = 0.0;
+//   printf("Assigning initial configuration...\n");
+    // initialize colorfield
+    for (unsigned int i = 0; i <= q; i++) {
+        color_field[i] = 0.0;
+    }
+    //
+    total_degree_sum = 0.0;
+    n_cur = iter.First(net->node_list);
+    while (!iter.End()) {
+        if (spin < 0) {
+            s = RNG_INTEGER(1, q);
+        } else {
+            s = spin;
+        }
+        n_cur->Set_ClusterIndex(s);
+        l_cur = l_iter.First(n_cur->Get_Links());
+        sum_weight = 0;
+        while (!l_iter.End()) {
+            sum_weight += l_cur->Get_Weight(); //weight should be one, in case we are not using it.
+            l_cur = l_iter.Next();
+        }
+        // we set the sum of the weights or the degree as the weight of the node, this way
+        // we do not have to calculate it again.
+        n_cur->Set_Weight(sum_weight);
+        av_k_squared += sum_weight * sum_weight;
+        av_k += sum_weight;
+
+        // in case we want all links to be contribute equally - parameter gamm=fixed
+        if (operation_mode == 0) {
+            color_field[s]++;
+        } else {
+            color_field[s] += sum_weight;
+        }
+        // or in case we want to use a weight of each link that is proportional to k_i\times k_j
+        total_degree_sum += sum_weight;
+        n_cur = iter.Next();
+    }
+    av_k_squared /= double(net->node_list->Size());
+    av_k /= double(net->node_list->Size());
+    // total_degree_sum-=av_k_squared/av_k;
+//   printf("Total Degree Sum=2M=%f\n",total_degree_sum);
+    return net->node_list->Size();
+}
+//#####################################################################
+//If I ever manage to write a decent LookUp function, it will be here
+//#####################################################################
+unsigned long PottsModel::initialize_lookup(double kT, double gamma) {
+    IGRAPH_UNUSED(kT);
+    IGRAPH_UNUSED(gamma);
+    /*
+    double beta;
+    // the look-up table contains all entries of exp(-beta(-neighbours+gamma*h))
+    // as needed in the HeatBath algorithm
+    beta=1.0/kT;
+    for (long w=0; w<=k_max+num_of_nodes; w++)
+    {
+       neg_lookup[w]=exp(-beta*-w
+    }
+    delta_ij[0]=1.0;
+    for (long w=-num_of_nodes-k_max; w<=k_max+num_of_nodes; w++)
+    {
+
+    }
+
+    // wenn wir spaeter exp(-1/kT*gamma*(nk+1-nj) fuer eine spin-flip von j nach k benoetigen schauen wir nur noch hier nach
+    for (unsigned long n=1; n<=num_of_nodes; n++)
+    {
+      gamma_term[n]=exp(-double(n)/kT*gamma);
+    }
+    gamma_term[0]=1.0;
+    */
+    return 1;
+}
+//#####################################################################
+// Q denotes the modulary of the network
+// This function calculates it initially
+// In the event of a spin changing its state, it only needs updating
+// Note that Qmatrix and Qa are only counting! The normalization
+// by num_of_links is done later
+//####################################################################
+double PottsModel::initialize_Qmatrix(void) {
+    DLList_Iter<NLink*> l_iter;
+    NLink *l_cur;
+    unsigned int i, j;
+    //initialize with zeros
+    num_of_links = net->link_list->Size();
+    for (i = 0; i <= q; i++) {
+        Qa[i] = 0.0;
+        for (j = i; j <= q; j++) {
+            Qmatrix[i][j] = 0.0;
+            Qmatrix[j][i] = 0.0;
+        }
+    }
+    //go over all links and make corresponding entries in Q matrix
+    //An edge connecting state i wiht state j will get an entry in Qij and Qji
+    l_cur = l_iter.First(net->link_list);
+    while (!l_iter.End()) {
+        i = l_cur->Get_Start()->Get_ClusterIndex();
+        j = l_cur->Get_End()->Get_ClusterIndex();
+        //printf("%d %d\n",i,j);
+        Qmatrix[i][j] += l_cur->Get_Weight();
+        Qmatrix[j][i] += l_cur->Get_Weight();
+
+        l_cur = l_iter.Next();
+    }
+    //Finally, calculate sum over rows and keep in Qa
+    for (i = 0; i <= q; i++) {
+        for (j = 0; j <= q; j++) {
+            Qa[i] += Qmatrix[i][j];
+        }
+    }
+    return calculate_Q();
+}
+//####################################################################
+// This function does the actual calculation of Q from the matrix
+// The normalization by num_of_links is done here
+//####################################################################
+double PottsModel::calculate_Q() {
+    double Q = 0.0;
+    for (unsigned int i = 0; i <= q; i++) {
+        Q += Qmatrix[i][i] - Qa[i] * Qa[i] / double(2.0 * net->sum_weights);
+        if ((Qa[i] < 0.0) || Qmatrix[i][i] < 0.0) {
+//         printf("Negatives Qa oder Qii\n\n\n");
+            //printf("Press any key to continue\n\n");
+            //cin >> Q;
+        }
+    }
+    Q /= double(2.0 * net->sum_weights);
+    return Q;
+}
+double PottsModel::calculate_genQ(double gamma) {
+    double Q = 0.0;
+    for (unsigned int i = 0; i <= q; i++) {
+        Q += Qmatrix[i][i] - gamma * Qa[i] * Qa[i] / double(2.0 * net->sum_weights);
+        if ((Qa[i] < 0.0) || Qmatrix[i][i] < 0.0) {
+//         printf("Negatives Qa oder Qii\n\n\n");
+            //printf("Press any key to continue\n\n");
+            //cin >> Q;
+        }
+    }
+    Q /= double(2.0 * net->sum_weights);
+    return Q;
+}
+//#######################################################################
+// This function calculates the Energy for the standard Hamiltonian
+// given a particular value of gamma and the current spin states
+// #####################################################################
+double PottsModel::calculate_energy(double gamma) {
+    double e = 0.0;
+    DLList_Iter<NLink*> l_iter;
+    NLink *l_cur;
+    l_cur = l_iter.First(net->link_list);
+    //every in-cluster edge contributes -1
+    while (!l_iter.End()) {
+        if (l_cur->Get_Start()->Get_ClusterIndex() == l_cur->Get_End()->Get_ClusterIndex()) {
+            e--;
+        };
+        l_cur = l_iter.Next();
+    }
+    //and the penalty term contributes according to cluster sizes
+    for (unsigned int i = 1; i <= q; i++) {
+        e += gamma * 0.5 * double(color_field[i]) * double((color_field[i] - 1));
+    }
+    energy = e;
+    return e;
+}
+//##########################################################################
+// We would like to start from a temperature with at least 95 of all proposed
+// spin changes accepted in 50 sweeps over the network
+// The function returns the Temperature found
+//#########################################################################
+double PottsModel::FindStartTemp(double gamma, double prob, double ts) {
+    double kT;
+    kT = ts;
+    //assing random initial condition
+    assign_initial_conf(-1);
+    //initialize Modularity matrix, from now on, it will be updated at every spin change
+    initialize_Qmatrix();
+    // the factor 1-1/q is important, since even, at infinite temperature,
+    // only 1-1/q of all spins do change their state, since a randomly chooses new
+    // state is with prob. 1/q the old state.
+    while (acceptance < (1.0 - 1.0 / double(q)) * 0.95) { //want 95% acceptance
+        kT = kT * 1.1;
+        // if I ever have a lookup table, it will need initialization for every kT
+        //initialize_lookup(kT,k_max,net->node_list->Size());
+        HeatBathParallelLookup(gamma, prob, kT, 50);
+//        printf("kT=%f acceptance=%f\n", kT, acceptance);
+    }
+    kT *= 1.1; // just to be sure...
+//   printf("Starting with acceptance ratio: %1.6f bei kT=%2.4f\n",acceptance,kT);
+    return kT;
+}
+
+//##############################################################
+//This function does a parallel update at zero T
+//Hence, it is really fast on easy problems
+//max sweeps is the maximum number of sweeps it should perform,
+//if it does not converge earlier
+//##############################################################
+long PottsModel::HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps) {
+    DLList_Iter<NNode*> iter, net_iter;
+    DLList_Iter<NLink*> l_iter;
+    DLList_Iter<unsigned int*> i_iter, i_iter2;
+    NNode *node, *n_cur;
+    NLink *l_cur;
+    unsigned int *SPIN, *P_SPIN, new_spin, spin_opt, old_spin, spin, sweep;
+    // long h; // degree;
+    unsigned long changes;
+    double h, delta = 0, deltaE, deltaEmin, w, degree;
+    //HugeArray<double> neighbours;
+    bool cyclic = 0;
+
+    sweep = 0;
+    changes = 1;
+    while (sweep < max_sweeps && changes) {
+        cyclic = true;
+        sweep++;
+        changes = 0;
+        //Loop over all nodes
+        node = net_iter.First(net->node_list);
+        SPIN = i_iter.First(new_spins);
+        while (!net_iter.End()) {
+            // How many neigbors of each type?
+            // set them all zero
+            for (unsigned int i = 0; i <= q; i++) {
+                neighbours[i] = 0;
+            }
+            degree = node->Get_Weight();
+            //Loop over all links (=neighbours)
+            l_cur = l_iter.First(node->Get_Links());
+            while (!l_iter.End()) {
+                //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name());
+                w = l_cur->Get_Weight();
+                if (node == l_cur->Get_Start()) {
+                    n_cur = l_cur->Get_End();
+                } else {
+                    n_cur = l_cur->Get_Start();
+                }
+                neighbours[n_cur->Get_ClusterIndex()] += w;
+                l_cur = l_iter.Next();
+            }
+            //Search optimal Spin
+            old_spin = node->Get_ClusterIndex();
+            //degree=node->Get_Degree();
+            switch (operation_mode) {
+            case 0: {
+                delta = 1.0;
+                break;
+            }
+            case 1: { //newman modularity
+                prob = degree / total_degree_sum;
+                delta = degree;
+                break;
+            }
+            }
+
+
+            spin_opt = old_spin;
+            deltaEmin = 0.0;
+            for (spin = 1; spin <= q; spin++) { // all possible spin states
+                if (spin != old_spin) {
+                    h = color_field[spin] + delta - color_field[old_spin];
+                    deltaE = double(neighbours[old_spin] - neighbours[spin]) + gamma * prob * double(h);
+                    if (deltaE < deltaEmin) {
+                        spin_opt = spin;
+                        deltaEmin = deltaE;
+                    }
+                }
+            } // for spin
+
+            //Put optimal spin on list for later update
+            *SPIN = spin_opt;
+            node = net_iter.Next();
+            SPIN = i_iter.Next();
+        } // while !net_iter.End()
+
+        //-------------------------------
+        //Now set all spins to new values
+        node = net_iter.First(net->node_list);
+        SPIN = i_iter.First(new_spins);
+        P_SPIN = i_iter2.First(previous_spins);
+        while (!net_iter.End()) {
+            old_spin = node->Get_ClusterIndex();
+            new_spin = *SPIN;
+            if (new_spin != old_spin) { // Do we really have a change??
+                changes++;
+                node->Set_ClusterIndex(new_spin);
+                //this is important!!
+                //In Parallel update, there occur cyclic attractors of size two
+                //which then make the program run for ever
+                if (new_spin != *P_SPIN) {
+                    cyclic = false;
+                }
+                *P_SPIN = old_spin;
+                color_field[old_spin]--;
+                color_field[new_spin]++;
+
+                //Qmatrix update
+                //iteration over all neighbours
+                l_cur = l_iter.First(node->Get_Links());
+                while (!l_iter.End()) {
+                    w = l_cur->Get_Weight();
+                    if (node == l_cur->Get_Start()) {
+                        n_cur = l_cur->Get_End();
+                    } else {
+                        n_cur = l_cur->Get_Start();
+                    }
+                    Qmatrix[old_spin][n_cur->Get_ClusterIndex()] -= w;
+                    Qmatrix[new_spin][n_cur->Get_ClusterIndex()] += w;
+                    Qmatrix[n_cur->Get_ClusterIndex()][old_spin] -= w;
+                    Qmatrix[n_cur->Get_ClusterIndex()][new_spin] += w;
+                    Qa[old_spin] -= w;
+                    Qa[new_spin] += w;
+                    l_cur = l_iter.Next();
+                }  // while l_iter
+            }
+            node = net_iter.Next();
+            SPIN = i_iter.Next();
+            P_SPIN = i_iter2.Next();
+        } // while (!net_iter.End())
+    }  // while markov
+
+    // In case of a cyclic attractor, we want to interrupt
+    if (cyclic)  {
+//       printf("Cyclic attractor!\n");
+        acceptance = 0.0;
+        return 0;
+    } else {
+        acceptance = double(changes) / double(num_of_nodes);
+        return changes;
+    }
+}
+//###################################################################################
+//The same function as before, but rather than parallel update, it pics the nodes to update
+//randomly
+//###################################################################################
+double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps) {
+    DLList_Iter<NNode*> iter;
+    DLList_Iter<NLink*> l_iter;
+    DLList_Iter<unsigned int*> i_iter, i_iter2;
+    NNode *node, *n_cur;
+    NLink *l_cur;
+    unsigned int new_spin, spin_opt, old_spin, spin, sweep;
+    long r;// degree;
+    unsigned long changes;
+    double delta = 0, h, deltaE, deltaEmin, w, degree;
+    //HugeArray<int> neighbours;
+
+    sweep = 0;
+    changes = 0;
+    while (sweep < max_sweeps) {
+        sweep++;
+        //ueber alle Knoten im Netz
+        for (unsigned long n = 0; n < num_of_nodes; n++) {
+            r = -1;
+            while ((r < 0) || (r > (long)num_of_nodes - 1)) {
+                r = RNG_INTEGER(0, num_of_nodes - 1);
+            }
+            /* r=long(double(num_of_nodes*double(rand())/double(RAND_MAX+1.0)));*/
+            node = net->node_list->Get(r);
+            // Wir zaehlen, wieviele Nachbarn von jedem spin vorhanden sind
+            // erst mal alles Null setzen
+            for (unsigned int i = 0; i <= q; i++) {
+                neighbours[i] = 0;
+            }
+            degree = node->Get_Weight();
+            //Loop over all links (=neighbours)
+            l_cur = l_iter.First(node->Get_Links());
+            while (!l_iter.End()) {
+                //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name());
+                w = l_cur->Get_Weight();
+                if (node == l_cur->Get_Start()) {
+                    n_cur = l_cur->Get_End();
+                } else {
+                    n_cur = l_cur->Get_Start();
+                }
+                neighbours[n_cur->Get_ClusterIndex()] += w;
+                l_cur = l_iter.Next();
+            }
+            //Search optimal Spin
+            old_spin = node->Get_ClusterIndex();
+            //degree=node->Get_Degree();
+            switch (operation_mode) {
+            case 0: {
+                delta = 1.0;
+                break;
+            }
+            case 1: { //newman modularity
+                prob = degree / total_degree_sum;
+                delta = degree;
+                break;
+            }
+            }
+
+
+            spin_opt = old_spin;
+            deltaEmin = 0.0;
+            for (spin = 1; spin <= q; spin++) { // alle moeglichen Spins
+                if (spin != old_spin) {
+                    h = color_field[spin] + delta - color_field[old_spin];
+                    deltaE = double(neighbours[old_spin] - neighbours[spin]) + gamma * prob * double(h);
+                    if (deltaE < deltaEmin) {
+                        spin_opt = spin;
+                        deltaEmin = deltaE;
+                    }
+                }
+            } // for spin
+
+            //-------------------------------
+            //Now update the spins
+            new_spin = spin_opt;
+            if (new_spin != old_spin) { // Did we really change something??
+                changes++;
+                node->Set_ClusterIndex(new_spin);
+                color_field[old_spin] -= delta;
+                color_field[new_spin] += delta;
+
+                //Qmatrix update
+                //iteration over all neighbours
+                l_cur = l_iter.First(node->Get_Links());
+                while (!l_iter.End()) {
+                    w = l_cur->Get_Weight();
+                    if (node == l_cur->Get_Start()) {
+                        n_cur = l_cur->Get_End();
+                    } else {
+                        n_cur = l_cur->Get_Start();
+                    }
+                    Qmatrix[old_spin][n_cur->Get_ClusterIndex()] -= w;
+                    Qmatrix[new_spin][n_cur->Get_ClusterIndex()] += w;
+                    Qmatrix[n_cur->Get_ClusterIndex()][old_spin] -= w;
+                    Qmatrix[n_cur->Get_ClusterIndex()][new_spin] += w;
+                    Qa[old_spin] -= w;
+                    Qa[new_spin] += w;
+                    l_cur = l_iter.Next();
+                }  // while l_iter
+            }
+        } // for n
+    }  // while markov
+
+    acceptance = double(changes) / double(num_of_nodes) / double(sweep);
+    return acceptance;
+}
+//#####################################################################################
+//This function performs a parallel update at Terperature T
+//#####################################################################################
+long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps) {
+    DLList_Iter<NNode*> iter, net_iter;
+    DLList_Iter<NLink*> l_iter;
+    DLList_Iter<unsigned int*> i_iter, i_iter2;
+    NNode *node, *n_cur;
+    NLink *l_cur;
+    unsigned int new_spin, spin_opt, old_spin;
+    unsigned int *SPIN, *P_SPIN;
+    unsigned int sweep;
+    long max_q;
+    unsigned long changes, /*degree,*/ problemcount;
+    //HugeArray<int> neighbours;
+    double h, delta = 0, norm, r, beta, minweight, prefac = 0, w, degree;
+    bool cyclic = 0, found;
+    unsigned long num_of_nodes;
+
+    sweep = 0;
+    changes = 1;
+    num_of_nodes = net->node_list->Size();
+    while (sweep < max_sweeps && changes) {
+        cyclic = true;
+        sweep++;
+        changes = 0;
+        //Loop over all nodes
+        node = net_iter.First(net->node_list);
+        SPIN = i_iter.First(new_spins);
+        while (!net_iter.End()) {
+            // Initialize neighbours and weights
+            problemcount = 0;
+            for (unsigned int i = 0; i <= q; i++) {
+                neighbours[i] = 0;
+                weights[i] = 0;
+            }
+            norm = 0.0;
+            degree = node->Get_Weight();
+            //Loop over all links (=neighbours)
+            l_cur = l_iter.First(node->Get_Links());
+            while (!l_iter.End()) {
+                //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name());
+                w = l_cur->Get_Weight();
+                if (node == l_cur->Get_Start()) {
+                    n_cur = l_cur->Get_End();
+                } else {
+                    n_cur = l_cur->Get_Start();
+                }
+                neighbours[n_cur->Get_ClusterIndex()] += w;
+                l_cur = l_iter.Next();
+            }
+            //Search optimal Spin
+            old_spin = node->Get_ClusterIndex();
+            //degree=node->Get_Degree();
+            switch (operation_mode) {
+            case 0: {
+                prefac = 1.0;
+                delta = 1.0;
+                break;
+            }
+            case 1: { //newman modularity
+                prefac = 1.0;
+                prob = degree / total_degree_sum;
+                delta = degree;
+                break;
+            }
+            }
+            spin_opt = old_spin;
+            beta = 1.0 / kT * prefac;
+            minweight = 0.0;
+            weights[old_spin] = 0.0;
+            for (unsigned spin = 1; spin <= q; spin++) { // loop over all possible new spins
+                if (spin != old_spin) { // only if we have a different than old spin!
+                    h = color_field[spin] + delta - color_field[old_spin];
+                    weights[spin] = double(neighbours[old_spin] - neighbours[spin]) + gamma * prob * double(h);
+                    if (weights[spin] < minweight) {
+                        minweight = weights[spin];
+                    }
+                }
+            }   // for spin
+            for (unsigned spin = 1; spin <= q; spin++) { // loop over all possibe spins
+                weights[spin] -= minweight;       // subtract minweight
+                // to avoid numerical problems with large exponents
+                weights[spin] = exp(-beta * weights[spin]);
+                norm += weights[spin];
+            }   // for spin
+
+            //now choose a new spin
+            r = RNG_UNIF(0, norm);
+            /* norm*double(rand())/double(RAND_MAX + 1.0); */
+            new_spin = 1;
+            found = false;
+            while (!found && new_spin <= q) {
+                if (r <= weights[new_spin]) {
+                    spin_opt = new_spin;
+                    found = true;
+                    break;
+                } else {
+                    r -= weights[new_spin];
+                }
+                new_spin++;
+            }
+            if (!found) {
+//         printf(".");
+                problemcount++;
+            }
+            //Put new spin on list
+            *SPIN = spin_opt;
+
+            node = net_iter.Next();
+            SPIN = i_iter.Next();
+        } // while !net_iter.End()
+
+        //-------------------------------
+        //now update all spins
+        node = net_iter.First(net->node_list);
+        SPIN = i_iter.First(new_spins);
+        P_SPIN = i_iter2.First(previous_spins);
+        while (!net_iter.End()) {
+            old_spin = node->Get_ClusterIndex();
+            new_spin = *SPIN;
+            if (new_spin != old_spin) { // Did we really change something??
+                changes++;
+                node->Set_ClusterIndex(new_spin);
+                if (new_spin != *P_SPIN) {
+                    cyclic = false;
+                }
+                *P_SPIN = old_spin;
+                color_field[old_spin] -= delta;
+                color_field[new_spin] += delta;
+
+                //Qmatrix update
+                //iteration over all neighbours
+                l_cur = l_iter.First(node->Get_Links());
+                while (!l_iter.End()) {
+                    w = l_cur->Get_Weight();
+                    if (node == l_cur->Get_Start()) {
+                        n_cur = l_cur->Get_End();
+                    } else {
+                        n_cur = l_cur->Get_Start();
+                    }
+                    Qmatrix[old_spin][n_cur->Get_ClusterIndex()] -= w;
+                    Qmatrix[new_spin][n_cur->Get_ClusterIndex()] += w;
+                    Qmatrix[n_cur->Get_ClusterIndex()][old_spin] -= w;
+                    Qmatrix[n_cur->Get_ClusterIndex()][new_spin] += w;
+                    Qa[old_spin] -= w;
+                    Qa[new_spin] += w;
+                    l_cur = l_iter.Next();
+                }  // while l_iter
+            }
+            node = net_iter.Next();
+            SPIN = i_iter.Next();
+            P_SPIN = i_iter2.Next();
+        } // while (!net_iter.End())
+
+    }  // while markov
+    max_q = 0;
+    for (unsigned int i = 1; i <= q; i++) if (color_field[i] > max_q) {
+            max_q = long(color_field[i]);
+        }
+
+    //again, we would not like to end up in cyclic attractors
+    if (cyclic && changes)  {
+//       printf("Cyclic attractor!\n");
+        acceptance = double(changes) / double(num_of_nodes);
+        return 0;
+    } else {
+        acceptance = double(changes) / double(num_of_nodes);
+        return changes;
+    }
+}
+//##############################################################
+// This is the function generally used for optimisation,
+// as the parallel update has its flaws, due to the cyclic attractors
+//##############################################################
+double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps) {
+    DLList_Iter<NNode*> iter;
+    DLList_Iter<NLink*> l_iter;
+    DLList_Iter<unsigned int*> i_iter, i_iter2;
+    NNode *node, *n_cur;
+    NLink *l_cur;
+    unsigned int new_spin, spin_opt, old_spin;
+    unsigned int sweep;
+    long max_q, rn;
+    unsigned long changes, /*degree,*/ problemcount;
+    double degree, w, delta = 0, h;
+    //HugeArray<int> neighbours;
+    double norm, r, beta, minweight, prefac = 0;
+    bool found;
+    long int num_of_nodes;
+    sweep = 0;
+    changes = 0;
+    num_of_nodes = net->node_list->Size();
+    while (sweep < max_sweeps) {
+        sweep++;
+        //loop over all nodes in network
+        for (int n = 0; n < num_of_nodes; n++) {
+            rn = -1;
+            while ((rn < 0) || (rn > num_of_nodes - 1)) {
+                rn = RNG_INTEGER(0, num_of_nodes - 1);
+            }
+            /* rn=long(double(num_of_nodes*double(rand())/double(RAND_MAX+1.0))); */
+
+            node = net->node_list->Get(rn);
+            // initialize the neighbours and the weights
+            problemcount = 0;
+            for (unsigned int i = 0; i <= q; i++) {
+                neighbours[i] = 0.0;
+                weights[i] = 0.0;
+            }
+            norm = 0.0;
+            degree = node->Get_Weight();
+            //Loop over all links (=neighbours)
+            l_cur = l_iter.First(node->Get_Links());
+            while (!l_iter.End()) {
+                //printf("%s %s\n",node->Get_Name(),n_cur->Get_Name());
+                w = l_cur->Get_Weight();
+                if (node == l_cur->Get_Start()) {
+                    n_cur = l_cur->Get_End();
+                } else {
+                    n_cur = l_cur->Get_Start();
+                }
+                neighbours[n_cur->Get_ClusterIndex()] += w;
+                l_cur = l_iter.Next();
+            }
+
+            //Look for optimal spin
+
+            old_spin = node->Get_ClusterIndex();
+            //degree=node->Get_Degree();
+            switch (operation_mode) {
+            case 0: {
+                prefac = 1.0;
+                delta = 1.0;
+                break;
+            }
+            case 1:  {//newman modularity
+                prefac = 1.0;
+                prob = degree / total_degree_sum;
+                delta = degree;
+                break;
+            }
+            }
+            spin_opt = old_spin;
+            beta = 1.0 / kT * prefac;
+            minweight = 0.0;
+            weights[old_spin] = 0.0;
+            for (unsigned spin = 1; spin <= q; spin++) { // all possible new spins
+                if (spin != old_spin) { // except the old one!
+                    h = color_field[spin] - (color_field[old_spin] - delta);
+                    weights[spin] = neighbours[old_spin] - neighbours[spin] + gamma * prob * h;
+                    if (weights[spin] < minweight) {
+                        minweight = weights[spin];
+                    }
+                }
+            }   // for spin
+            for (unsigned spin = 1; spin <= q; spin++) { // all possible new spins
+                weights[spin] -= minweight;       // subtract minweigt
+                // for numerical stability
+                weights[spin] = exp(-beta * weights[spin]);
+                norm += weights[spin];
+            }   // for spin
+
+
+            //choose a new spin
+            /*      r = norm*double(rand())/double(RAND_MAX + 1.0); */
+            r = RNG_UNIF(0, norm);
+            new_spin = 1;
+            found = false;
+            while (!found && new_spin <= q) {
+                if (r <= weights[new_spin]) {
+                    spin_opt = new_spin;
+                    found = true;
+                    break;
+                } else {
+                    r -= weights[new_spin];
+                }
+                new_spin++;
+            }
+            if (!found) {
+//         printf(".");
+                problemcount++;
+            }
+            //-------------------------------
+            //now set the new spin
+            new_spin = spin_opt;
+            if (new_spin != old_spin) { // Did we really change something??
+                changes++;
+                node->Set_ClusterIndex(new_spin);
+                color_field[old_spin] -= delta;
+                color_field[new_spin] += delta;
+
+                //Qmatrix update
+                //iteration over all neighbours
+                l_cur = l_iter.First(node->Get_Links());
+                while (!l_iter.End()) {
+                    w = l_cur->Get_Weight();
+                    if (node == l_cur->Get_Start()) {
+                        n_cur = l_cur->Get_End();
+                    } else {
+                        n_cur = l_cur->Get_Start();
+                    }
+                    Qmatrix[old_spin][n_cur->Get_ClusterIndex()] -= w;
+                    Qmatrix[new_spin][n_cur->Get_ClusterIndex()] += w;
+                    Qmatrix[n_cur->Get_ClusterIndex()][old_spin] -= w;
+                    Qmatrix[n_cur->Get_ClusterIndex()][new_spin] += w;
+                    Qa[old_spin] -= w;
+                    Qa[new_spin] += w;
+                    l_cur = l_iter.Next();
+                }  // while l_iter
+            }
+        } // for n
+    }  // while markov
+    max_q = 0;
+
+    for (unsigned int i = 1; i <= q; i++) if (color_field[i] > max_q) {
+            max_q = long(color_field[i] + 0.5);
+        }
+
+    acceptance = double(changes) / double(num_of_nodes) / double(sweep);
+    return acceptance;
+}
+
+//###############################################################################################
+//# Here we try to minimize the affinity to the rest of the network
+//###############################################################################################
+double PottsModel::FindCommunityFromStart(double gamma, double prob,
+        char *nodename,
+        igraph_vector_t *result,
+        igraph_real_t *cohesion,
+        igraph_real_t *adhesion,
+        igraph_integer_t *my_inner_links,
+        igraph_integer_t *my_outer_links) {
+    DLList_Iter<NNode*> iter, iter2;
+    DLList_Iter<NLink*> l_iter;
+    DLList<NNode*>* to_do;
+    DLList<NNode*>* community;
+    NNode *start_node = 0, *n_cur, *neighbor, *max_aff_node, *node;
+    NLink *l_cur;
+    bool found = false, add = false, remove = false;
+    double degree, delta_aff_add, delta_aff_rem, max_delta_aff, Ks = 0.0, Kr = 0, kis, kir, w;
+    long community_marker = 5;
+    long to_do_marker = 10;
+    double inner_links = 0, outer_links = 0, aff_r, aff_s;
+
+    IGRAPH_UNUSED(prob);
+
+    to_do = new DLList<NNode*>;
+    community = new DLList<NNode*>;
+
+    // find the node in the network
+    n_cur = iter.First(net->node_list);
+    while (!found && !iter.End()) {
+        if (0 == strcmp(n_cur->Get_Name(), nodename)) {
+            start_node = n_cur;
+            found = true;
+            start_node->Set_Affinity(0.0);
+            community->Push(start_node);
+            start_node->Set_Marker(community_marker);
+            Ks = start_node->Get_Weight();
+            Kr = total_degree_sum - start_node->Get_Weight();
+        }
+        n_cur = iter.Next();
+    }
+    if (!found) {
+//      printf("%s not found found. Aborting.\n",nodename);
+//      fprintf(file,"%s not found found. Aborting.\n",nodename);
+        delete to_do;
+        delete community;
+        return -1;
+    }
+    //#############################
+    // initialize the to_do list and community with the neighbours of start node
+    //#############################
+    neighbor = iter.First(start_node->Get_Neighbours());
+    while (!iter.End()) {
+//     printf("Adding node %s to comunity.\n",neighbor->Get_Name());
+        community->Push(neighbor);
+        neighbor->Set_Marker(community_marker);
+        Ks += neighbor->Get_Weight();
+        Kr -= neighbor->Get_Weight();
+        neighbor = iter.Next();
+    }
+    node = iter.First(community);
+    while (!iter.End()) {
+        //now add at the second neighbors to the to_do list
+        neighbor = iter2.First(node->Get_Neighbours());
+        while (!iter2.End()) {
+            if ((long)neighbor->Get_Marker() != community_marker && (long)neighbor->Get_Marker() != to_do_marker) {
+                to_do->Push(neighbor);
+                neighbor->Set_Marker(to_do_marker);
+//  printf("Adding node %s to to_do list.\n",neighbor->Get_Name());
+            }
+            neighbor = iter2.Next();
+        }
+        node = iter.Next();
+    }
+
+    //#############
+    //repeat, as long as we are still adding nodes to the communtiy
+    //#############
+    add = true;
+    remove = true;
+    while (add || remove) {
+        //#############################
+        //calculate the affinity changes of all nodes for adding every node in the to_do list to the community
+        //##############################
+
+        IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */
+
+        max_delta_aff = 0.0;
+        max_aff_node = NULL;
+        add = false;
+        node = iter.First(to_do);
+        while (!iter.End()) {
+            //printf("Checking Links of %s\n",node->Get_Name());
+            degree = node->Get_Weight();
+            kis = 0.0;
+            kir = 0.0;
+            // For every of the neighbors, check, count the links to the community
+            l_cur = l_iter.First(node->Get_Links());
+            while (!l_iter.End()) {
+                w = l_cur->Get_Weight();
+                if (node == l_cur->Get_Start()) {
+                    n_cur = l_cur->Get_End();
+                } else {
+                    n_cur = l_cur->Get_Start();
+                }
+                if ((long)n_cur->Get_Marker() == community_marker) {
+                    kis += w; //the weight/number of links to the community
+                } else {
+                    kir += w; //the weight/number of links to the rest of the network
+                }
+                l_cur = l_iter.Next();
+            }
+            aff_r = kir - gamma / total_degree_sum * (Kr - degree) * degree;
+            aff_s = kis - gamma / total_degree_sum * Ks * degree;
+            delta_aff_add = aff_r - aff_s;
+            //  if (aff_s>=aff_r && delta_aff_add<=max_delta_aff) {
+            if (delta_aff_add <= max_delta_aff) {
+                node->Set_Affinity(aff_s);
+                max_delta_aff = delta_aff_add;
+                max_aff_node = node;
+                add = true;
+            }
+            //printf("%s in to_do list with affinity %f\n",node->Get_Name(),node->Get_Affinity());
+            node = iter.Next();
+        }
+        //################
+        //calculate the affinity changes for removing every single node from the community
+        //################
+        inner_links = 0;
+        outer_links = 0;
+        remove = false;
+        node = iter.First(community);
+        while (!iter.End()) {
+            //printf("Checking Links of %s\n",node->Get_Name());
+            degree = node->Get_Weight();
+            kis = 0.0;
+            kir = 0.0;
+            // For every of the neighbors, check, count the links to the community
+            l_cur = l_iter.First(node->Get_Links());
+            while (!l_iter.End()) {
+                w = l_cur->Get_Weight();
+                if (node == l_cur->Get_Start()) {
+                    n_cur = l_cur->Get_End();
+                } else {
+                    n_cur = l_cur->Get_Start();
+                }
+                if ((long)n_cur->Get_Marker() == community_marker) {
+                    kis += w;
+                    inner_links += w; //summing all w gives twice the number of inner links(weights)
+                } else {
+                    kir += w;
+                    outer_links += w;
+                }
+                l_cur = l_iter.Next();
+            }
+//  if (kir+kis!=degree) {  printf("error kir=%f\tkis=%f\tk=%f\n",kir,kis,degree); }
+            aff_r = kir - gamma / total_degree_sum * Kr * degree;
+            aff_s = kis - gamma / total_degree_sum * (Ks - degree) * degree;
+            delta_aff_rem = aff_s - aff_r;
+            node->Set_Affinity(aff_s);
+            // we should not remove the nodes, we have just added
+            if (delta_aff_rem < max_delta_aff) {
+                max_delta_aff = delta_aff_rem ;
+                max_aff_node = node;
+                remove = true;
+                add = false;
+            }
+            //printf("%s in to_do list with affinity %f\n",node->Get_Name(),node->Get_Affinity());
+            node = iter.Next();
+        }
+        inner_links = inner_links * 0.5;
+        //################
+        // Now check, whether we want to remove or add a node
+        //################
+        if (add) {
+            //################
+            //add the node of maximum affinity to the community
+            //###############
+            community->Push(max_aff_node);
+            max_aff_node->Set_Marker(community_marker);
+            //delete node from to_do
+            to_do->fDelete(max_aff_node);
+            //update the sum of degrees in the community
+            Ks += max_aff_node->Get_Weight();
+            Kr -= max_aff_node->Get_Weight();
+//  printf("Adding node %s to community with affinity of %f delta_aff: %f.\n",max_aff_node->Get_Name(), max_aff_node->Get_Affinity(),max_delta_aff);
+            //now add all neighbors of this node, that are not already
+            //in the to_do list or in the community
+            neighbor = iter.First(max_aff_node->Get_Neighbours());
+            while (!iter.End()) {
+                if ((long)neighbor->Get_Marker() != community_marker && (long)neighbor->Get_Marker() != to_do_marker) {
+                    to_do->Push(neighbor);
+                    neighbor->Set_Marker(to_do_marker);
+                    //printf("Adding node %s to to_do list.\n",neighbor->Get_Name());
+                }
+                neighbor = iter.Next();
+            }
+        }
+        if (remove) {
+            //################
+            //remove those with negative affinities
+            //################
+            community->fDelete(max_aff_node);
+            max_aff_node->Set_Marker(to_do_marker);
+            //update the sum of degrees in the community
+            Ks -= max_aff_node->Get_Weight();
+            Kr += max_aff_node->Get_Weight();
+            //add the node to to_do again
+            to_do->Push(max_aff_node);
+//  printf("Removing node %s from community with affinity of %f delta_aff: %f.\n",max_aff_node->Get_Name(), max_aff_node->Get_Affinity(),max_delta_aff);
+        }
+        IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */
+    }
+    //###################
+    //write the node in the community to a file
+    //###################
+    // TODO return this instead of writing it
+//   fprintf(file,"Number_of_nodes:\t%d\n",community->Size());
+//   fprintf(file,"Inner_Links:\t%f\n",inner_links);
+//   fprintf(file,"Outer_Links:\t%f\n",Ks-2*inner_links);
+//   fprintf(file,"Cohesion:\t%f\n",inner_links-gamma/total_degree_sum*Ks*Ks*0.5);
+//   fprintf(file,"Adhesion:\t%f\n",outer_links-gamma/total_degree_sum*Ks*Kr);
+//   fprintf(file,"\n");
+    if (cohesion) {
+        *cohesion = inner_links - gamma / total_degree_sum * Ks * Ks * 0.5;
+    }
+    if (adhesion) {
+        *adhesion = outer_links - gamma / total_degree_sum * Ks * Kr;
+    }
+    if (my_inner_links) {
+        *my_inner_links = inner_links;
+    }
+    if (my_outer_links) {
+        *my_outer_links = outer_links;
+    }
+    if (result) {
+        node = iter.First(community);
+        igraph_vector_resize(result, 0);
+        while (!iter.End()) {
+            // printf("%s in community.\n",node->Get_Name());
+            // fprintf(file,"%s\t%f\n",node->Get_Name(),node->Get_Affinity());
+            IGRAPH_CHECK(igraph_vector_push_back(result, node->Get_Index()));
+            node = iter.Next();
+        }
+    }
+//   printf("%d nodes in community around %s\n",community->Size(),start_node->Get_Name());
+//   fclose(file);
+    unsigned int size = community->Size();
+    delete to_do;
+    delete community;
+    return size;
+}
+
+//################################################################################################
+// this Function writes the clusters to disk
+//################################################################################################
+long PottsModel::WriteClusters(igraph_real_t *modularity,
+                               igraph_real_t *temperature,
+                               igraph_vector_t *csize,
+                               igraph_vector_t *membership,
+                               double kT, double gamma) {
+    NNode *n_cur, *n_cur2;
+    /*
+    double a1,a2,a3,p,p1,p2;
+    long n,N,lin,lout;
+    */
+    DLList_Iter<NNode*> iter, iter2;
+    HugeArray<int> inner_links;
+    HugeArray<int> outer_links;
+    HugeArray<int> nodes;
+
+    //den Header schreiben
+//   p=2.0*double(num_of_links)/double(num_of_nodes)/double(num_of_nodes-1);
+//   fprintf(file,"      Nodes=\t%lu\n",num_of_nodes);
+//   fprintf(file,"      Links=\t%lu\n",num_of_links);
+//   fprintf(file,"          q=\t%d\n",q);
+//   fprintf(file,"          p=\t%f\n",p);
+//   fprintf(file," Modularity=\t%f\n",calculate_Q());
+//   fprintf(file,"Temperature=\t%f\n", kT);
+//   fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\t<Ln(#comm.)>\n");
+
+    if (temperature) {
+        *temperature = kT;
+    }
+
+    if (csize || membership || modularity) {
+        // TODO: count the number of clusters
+        for (unsigned int spin = 1; spin <= q; spin++) {
+            inner_links[spin] = 0;
+            outer_links[spin] = 0;
+            nodes[spin] = 0;
+            n_cur = iter.First(net->node_list);
+            while (!iter.End()) {
+                if (n_cur->Get_ClusterIndex() == spin) {
+                    nodes[spin]++;
+                    n_cur2 = iter2.First(n_cur->Get_Neighbours());
+                    while (!iter2.End()) {
+                        if (n_cur2->Get_ClusterIndex() == spin) {
+                            inner_links[spin]++;
+                        } else {
+                            outer_links[spin]++;
+                        }
+                        n_cur2 = iter2.Next();
+                    }
+                }
+                n_cur = iter.Next();
+            }
+        }
+    }
+    if (modularity) {
+        *modularity = 0.0;
+        for (unsigned int spin = 1; spin <= q; spin++) {
+            if (nodes[spin] > 0) {
+                double t1 = inner_links[spin] / net->sum_weights / 2.0;
+                double t2 = (inner_links[spin] + outer_links[spin]) /
+                            net->sum_weights / 2.0;
+                *modularity += t1;
+                *modularity -= gamma * t2 * t2;
+            }
+        }
+    }
+    if (csize) {
+        igraph_vector_resize(csize, 0);
+        for (unsigned int spin = 1; spin <= q; spin++) {
+            if (nodes[spin] > 0) {
+                inner_links[spin] /= 2;
+                //    fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\n");
+                /*
+                N=num_of_nodes;
+                n=nodes[spin];
+                lin=inner_links[spin];
+                lout=outer_links[spin];
+                a1=N*log((double)N)-n*log((double)n)*(N-n)*log((double)N-n);
+                if ((lin==long(n*(n-1)*0.5+0.5)) || (n==1)) a2=0.0;
+                else a2=(n*(n-1)*0.5    )*log((double)n*(n-1)*0.5    )-(n*(n-1)*0.5    )-
+                   (n*(n-1)*0.5-lin)*log((double)n*(n-1)*0.5-lin)+(n*(n-1)*0.5-lin)-
+                   lin*log((double)lin            )+lin;
+                */
+
+                /*
+                if ((lout==n*(N-n)) || n==N) a3=0.0;
+                else a3=(n*(N-n)     )*log((double)n*(N-n)     )-(n*(N-n))-
+                   (n*(N-n)-lout)*log((double)n*(N-n)-lout)+(n*(N-n)-lout)-
+                   lout*log((double)lout        )+lout;
+                */
+
+                /*
+                p1=(lin+lout)*log((double)p);
+                p2=(0.5*n*(n-1)-lin + n*(N-n)-lout)*log((double)1.0-p);
+                */
+                //       fprintf(file,"%d\t%d\t%d\t%d\t%f\t%f\t%f\n",spin,nodes[spin], inner_links[spin], outer_links[spin], p_in, p_out,log_num_exp);
+                IGRAPH_CHECK(igraph_vector_push_back(csize, nodes[spin]));
+            }
+        }
+        //   fprintf(file,"\n");
+    }
+
+    //die Elemente der Cluster
+    if (membership) {
+        long int no = -1;
+        IGRAPH_CHECK(igraph_vector_resize(membership, num_of_nodes));
+        for (unsigned int spin = 1; spin <= q; spin++) {
+            if (nodes[spin] > 0) {
+                no++;
+            }
+            n_cur = iter.First(net->node_list);
+            while (!iter.End()) {
+                if (n_cur->Get_ClusterIndex() == spin) {
+                    //         fprintf(file,"%d\t%s\n",spin,n_cur->Get_Name());
+                    VECTOR(*membership)[ n_cur->Get_Index() ] = no;
+                }
+                n_cur = iter.Next();
+            }
+        }
+    }
+
+    return num_of_nodes;
+}
+//################################################################################################
+//This function writes the soft clusters after a gamma sweep
+//that is, it groups every node together that was found in
+// more than threshold percent together with the other node
+// in the same cluster
+//################################################################################################
+// Does not work at the moment !!!
+//################################################################################################
+// long PottsModel::WriteSoftClusters(char *filename, double threshold)
+// {
+//   FILE *file;
+//   NNode *n_cur, *n_cur2;
+//   DLList_Iter<NNode*> iter, iter2;
+//   DL_Indexed_List<ClusterList<NNode*>*> *cl_list, *old_clusterlist;
+//   ClusterList<NNode*> *cl_cur;
+
+//   double max;
+
+//   file=fopen(filename,"w");
+//   if (!file) {
+//     printf("Could not open %s for writing.\n",filename);
+//     return -1;
+//   }
+
+//   max=correlation[0]->Get(0);
+//   //printf("max=%f\n",max);
+//   cl_list=new DL_Indexed_List<ClusterList<NNode*>*>();
+
+//   n_cur=iter.First(net->node_list);
+//   while (!iter.End())
+//   {
+//     cl_cur=new ClusterList<NNode*>();
+//     cl_list->Push(cl_cur);
+//     n_cur2=iter2.First(net->node_list);
+//     while (!iter2.End())
+//     {
+//       if (double(correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index()))/max>threshold)
+//         cl_cur->Push(n_cur2);
+//       n_cur2=iter2.Next();
+//     }
+//     n_cur=iter.Next();
+//   }
+//   old_clusterlist=net->cluster_list;
+//   net->cluster_list=cl_list;
+//   clear_all_markers(net);
+//   //printf("Es gibt %d Cluster\n",cl_list->Size());
+//   reduce_cliques2(net, false, 15);
+//   //printf("Davon bleiben %d Cluster uebrig\n",cl_list->Size());
+//   clear_all_markers(net);
+//   while (net->cluster_list->Size()){
+//     cl_cur=net->cluster_list->Pop();
+//     while (cl_cur->Size())
+//     {
+//       n_cur=cl_cur->Pop();
+//       fprintf(file,"%s\n",n_cur->Get_Name());
+//       //printf("%s\n",n_cur->Get_Name());
+//     }
+//     fprintf(file,"\n");
+//   }
+//   net->cluster_list=old_clusterlist;
+//   fclose(file);
+
+//   return 1;
+// }
+//#############################################################################
+// Performs a gamma sweep
+//#############################################################################
+double PottsModel::GammaSweep(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel, int repetitions) {
+    double stepsize;
+    double kT, kT_start;
+    long changes;
+    double gamma, acc;
+    NNode *n_cur, *n_cur2;
+    DLList_Iter<NNode*> iter, iter2;
+
+    stepsize = (gamma_stop - gamma_start) / double(steps);
+
+    n_cur = iter.First(net->node_list);
+    while (!iter.End()) {
+        correlation[n_cur->Get_Index()] = new HugeArray<double>();
+        n_cur2 = iter2.First(net->node_list);
+        while (!iter2.End()) {
+            correlation[n_cur->Get_Index()]->Set(n_cur->Get_Index()) = 0.0;
+            n_cur2 = iter2.Next();
+        }
+        n_cur = iter.Next();
+    }
+
+    for (unsigned int n = 0; n <= steps; n++) {
+        assign_initial_conf(-1);
+        initialize_Qmatrix();
+        gamma = gamma_start + stepsize * n;
+        kT = 0.5;
+        acceptance = 0.5;
+        while (acceptance < (1.0 - 1.0 / double(q)) * 0.95) { //wollen 95% Acceptance
+            kT *= 1.1;
+            //initialize_lookup(kT,kmax,net->node_list->Size());
+            if (!non_parallel) {
+                HeatBathParallelLookup(gamma, prob, kT, 25);
+            } else {
+                HeatBathLookup(gamma, prob, kT, 25);
+            }
+            // printf("kT=%f acceptance=%f\n", kT, acceptance);
+        }
+        // printf("Starting with gamma=%f\n", gamma);
+        kT_start = kT;
+
+        for (int i = 0; i < repetitions; i++) {
+            changes = 1;
+            kT = kT_start;
+            assign_initial_conf(-1);
+            initialize_Qmatrix();
+            while ((changes > 0) && (kT > 0.01)) {
+                kT = kT * 0.99;
+                //initialize_lookup(kT,kmax,net->node_list->Size());
+                if (!non_parallel) {
+                    changes = HeatBathParallelLookup(gamma, prob, kT, 50);
+                    // printf("kT: %f   \t Changes %li\n",kT, changes);
+                } else {
+                    acc = HeatBathLookup(gamma, prob, kT, 50);
+                    if (acc > (1.0 - 1.0 / double(q)) * 0.01) {
+                        changes = 1;
+                    } else {
+                        changes = 0;
+                    }
+                    // printf("kT: %f   Acceptance: %f\n",kT, acc);
+                }
+            }
+            // printf("Finisched with acceptance: %1.6f bei kT=%2.4f und gamma=%2.4f\n",acceptance,kT, gamma);
+//      fprintf(file,"%f\t%f\n",gamma_,acceptance);
+//      fprintf(file2,"%f\t%f\n",gamma_,kT);
+            //   fprintf(file3,"%f\t%d\n",gamma_,count_clusters(5));
+
+            //Die Correlation berechnen
+            n_cur = iter.First(net->node_list);
+            while (!iter.End()) {
+                n_cur2 = iter2.First(net->node_list);
+                while (!iter2.End()) {
+                    if (n_cur->Get_ClusterIndex() == n_cur2->Get_ClusterIndex()) {
+                        correlation[n_cur->Get_Index()]->Set(n_cur2->Get_Index()) += 0.5;
+                    }
+                    n_cur2 = iter2.Next();
+                }
+                n_cur = iter.Next();
+            }
+        } // for i
+    } //for n
+    return kT;
+}
+//#############################################################################
+//Performs a Gamma sweep at zero T
+//#############################################################################
+double PottsModel::GammaSweepZeroTemp(double gamma_start, double gamma_stop, double prob, unsigned int steps, bool non_parallel, int repetitions) {
+    double stepsize;
+    long changes;
+    double gamma, acc;
+    long runs;
+    NNode *n_cur, *n_cur2;
+    DLList_Iter<NNode*> iter, iter2;
+
+    stepsize = (gamma_stop - gamma_start) / double(steps);
+
+    n_cur = iter.First(net->node_list);
+    while (!iter.End()) {
+        correlation[n_cur->Get_Index()] = new HugeArray<double>();
+        n_cur2 = iter2.First(net->node_list);
+        while (!iter2.End()) {
+            correlation[n_cur->Get_Index()]->Set(n_cur->Get_Index()) = 0.0;
+            n_cur2 = iter2.Next();
+        }
+        n_cur = iter.Next();
+    }
+
+    for (unsigned int n = 0; n <= steps; n++) {
+        assign_initial_conf(-1);
+        initialize_Qmatrix();
+        gamma = gamma_start + stepsize * n;
+        // printf("Starting with gamma=%f\n", gamma);
+        for (int i = 0; i < repetitions; i++) {
+            changes = 1;
+            assign_initial_conf(-1);
+            initialize_Qmatrix();
+            runs = 0;
+            while (changes > 0 && runs < 250) {
+                //initialize_lookup(kT,kmax,net->node_list->Size());
+                if (!non_parallel) {
+                    changes = HeatBathParallelLookupZeroTemp(gamma, prob, 1);
+                    // printf("Changes %li\n", changes);
+                } else {
+                    acc = HeatBathLookupZeroTemp(gamma, prob, 1);
+                    if (acc > (1.0 - 1.0 / double(q)) * 0.01) {
+                        changes = 1;
+                    } else {
+                        changes = 0;
+                    }
+                    // printf("Acceptance: %f\n", acc);
+                }
+                runs++;
+            }
+            // printf("Finisched with Modularity: %1.6f bei Gamma=%1.6f\n",calculate_Q(), gamma);
+//      fprintf(file,"%f\t%f\n",gamma_,acceptance);
+//      fprintf(file2,"%f\t%f\n",gamma_,kT);
+            //   fprintf(file3,"%f\t%d\n",gamma_,count_clusters(5));
+
+            //Die Correlation berechnen
+            n_cur = iter.First(net->node_list);
+            while (!iter.End()) {
+                n_cur2 = iter2.First(net->node_list);
+                while (!iter2.End()) {
+                    if (n_cur->Get_ClusterIndex() == n_cur2->Get_ClusterIndex()) {
+                        correlation[n_cur->Get_Index()]->Set(n_cur2->Get_Index()) += 0.5;
+                        correlation[n_cur2->Get_Index()]->Set(n_cur->Get_Index()) += 0.5;
+                    }
+                    n_cur2 = iter2.Next();
+                }
+                n_cur = iter.Next();
+            }
+        } // for i
+    } //for n
+    return gamma;
+}
+//#######################################################################
+//-----------------------------------------------------------------------
+//#######################################################################
+// This function writes the Correlation Matrix that results from a
+// Gamma-Sweep, this matrix is used to make ps files of it.
+// ######################################################################
+// long PottsModel::WriteCorrelationMatrix(char *filename)
+// {
+//   FILE *file, *file2;
+//   char filename2[255];
+//   NNode *n_cur, *n_cur2;
+//   DLList_Iter<NNode*> iter, iter2;
+
+//   sprintf(filename2,"%s.mat",filename);
+//   file=fopen(filename,"w");
+//   if (!file) {
+//     printf("Could not open %s for writing.\n",filename);
+//     return -1;
+//   }
+//   file2=fopen(filename2,"w");
+//   if (!file2) {
+//     printf("Could not open %s for writing.\n",filename2);
+//     return -1;
+//   }
+//   //write the header in one line
+//   n_cur=iter.First(net->node_list);
+//   while (!iter.End())
+//   {
+//       fprintf(file, "\t%s",n_cur->Get_Name());
+//       n_cur=iter.Next();
+//   }
+//   fprintf(file, "\n");
+
+//   //fprintf(file, "%d\t%d\n",net->node_list->Size(),net->node_list->Size());
+
+//   long r=0,c=0;
+//   n_cur=iter.First(net->node_list);
+//   while (!iter.End())
+//   {
+//     fprintf(file, "%s",n_cur->Get_Name());
+//     r++;
+//     n_cur2=iter2.First(net->node_list);
+//     while (!iter2.End())
+//     {
+//       c++;
+//       fprintf(file,"\t%f",correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index()));
+//       fprintf(file2,"%li\t%li\t%f\n",r,c,correlation[n_cur->Get_Index()]->Get(n_cur2->Get_Index()));
+//       n_cur2=iter2.Next();
+//     }
+//     fprintf(file,"\n");
+//     n_cur=iter.Next();
+//   }
+//   fclose(file);
+//   fclose(file2);
+//   return 1;
+// }
+//##############################################################################
+
+//#################################################################################################
+PottsModelN::PottsModelN(network *n, unsigned int num_communities, bool directed) {
+    //Set internal variable
+    net = n;
+    q   = num_communities;
+
+    is_directed = directed;
+
+    is_init = false;
+
+    num_nodes   = net->node_list->Size();
+}
+//#######################################################
+//Destructor of PottsModel
+//########################################################
+PottsModelN::~PottsModelN() {
+    delete degree_pos_in;
+    delete degree_neg_in;
+    delete degree_pos_out;
+    delete degree_neg_out;
+
+    delete degree_community_pos_in;
+    delete degree_community_neg_in;
+    delete degree_community_pos_out;
+    delete degree_community_neg_out;
+
+    delete weights;
+    delete neighbours;
+    delete csize;
+
+    delete spin;
+
+    return;
+}
+
+void PottsModelN::assign_initial_conf(bool init_spins) {
+#ifdef DEBUG
+    printf("Start assigning.\n");
+#endif
+    int s;
+    DLList_Iter<NNode*> iter;
+    DLList_Iter<NLink*> l_iter;
+    NNode *n_cur;
+    NLink *l_cur;
+
+
+    if (init_spins) {
+#ifdef DEBUG
+        printf("Initializing spin.\n");
+#endif
+        //Bookkeeping of the various degrees (positive/negative) and (in/out)
+        degree_pos_in   = new double[num_nodes]; //Postive indegree of the nodes (or sum of weights)
+        degree_neg_in   = new double[num_nodes]; //Negative indegree of the nodes (or sum of weights)
+        degree_pos_out  = new double[num_nodes]; //Postive outdegree of the nodes (or sum of weights)
+        degree_neg_out  = new double[num_nodes]; //Negative outdegree of the nodes (or sum of weights)
+
+        spin            = new unsigned int[num_nodes]; //The spin state of each node
+    }
+
+    if (is_init) {
+        delete degree_community_pos_in;
+        delete degree_community_neg_in;
+        delete degree_community_pos_out;
+        delete degree_community_neg_out;
+
+        delete weights;
+        delete neighbours;
+        delete csize;
+    }
+
+    is_init = true;
+
+    //Bookkeep of occupation numbers of spin states or the number of links in community...
+    degree_community_pos_in     = new double[q + 1]; //Positive sum of indegree for communities
+    degree_community_neg_in     = new double[q + 1]; //Negative sum of indegree for communities
+    degree_community_pos_out    = new double[q + 1]; //Positive sum of outegree for communities
+    degree_community_neg_out    = new double[q + 1]; //Negative sum of outdegree for communities
+
+    //...and of weights and neighbours for in the HeathBathLookup
+    weights                     = new double[q + 1]; //The weights for changing to another spin state
+    neighbours                  = new double[q + 1]; //The number of neighbours (or weights) in different spin states
+    csize                       = new unsigned int[q + 1]; //The number of nodes in each community
+
+
+    //Initialize communities
+    for (unsigned int i = 0; i <= q; i++) {
+        degree_community_pos_in[i]  = 0.0;
+        degree_community_neg_in[i]  = 0.0;
+        degree_community_pos_out[i] = 0.0;
+        degree_community_neg_out[i] = 0.0;
+
+        csize[i]                    = 0;
+    }
+
+    //Initialize vectors
+    if (init_spins) {
+        for (unsigned int i = 0; i < num_nodes; i++) {
+            degree_pos_in[i]    = 0.0;
+            degree_neg_in[i]    = 0.0;
+            degree_pos_out[i]   = 0.0;
+            degree_neg_out[i]   = 0.0;
+
+#ifdef DEBUG
+            printf("Initializing spin %d", i);
+#endif
+            spin[i] = 0;
+        }
+    }
+    m_p = 0.0;
+    m_n = 0.0;
+    //Set community for each node, and
+    //correctly store it in the bookkeeping
+
+    double sum_weight_pos_in, sum_weight_pos_out, sum_weight_neg_in, sum_weight_neg_out;
+    //double av_w = 0.0, av_k=0.0;
+    //int l = 0;
+#ifdef DEBUG
+    printf("Visiting each node.\n");
+#endif
+    for (unsigned int v = 0; v < num_nodes; v++) {
+        if (init_spins) {
+            s = RNG_INTEGER(1, q);  //The new spin s
+            spin[v] = (unsigned int)s;
+        } else {
+            s = spin[v];
+        }
+
+#ifdef DEBUG
+        printf("Spin %d assigned to node %d.\n", s, v);
+#endif
+
+        n_cur               =  net->node_list->Get(v);
+
+        l_cur               = l_iter.First(n_cur->Get_Links());
+
+        sum_weight_pos_in   = 0.0;
+        sum_weight_pos_out  = 0.0;
+        sum_weight_neg_in   = 0.0;
+        sum_weight_neg_out  = 0.0;
+
+        while (!l_iter.End()) {
+            double w = l_cur->Get_Weight();
+            //av_w = (av_w*l + w)/(l+1); //Average weight
+            //l++;
+            if (l_cur->Get_Start() == n_cur) //From this to other, so outgoing link
+                if (w > 0) {
+                    sum_weight_pos_out += w;    //Increase positive outgoing weight
+                } else {
+                    sum_weight_neg_out -= w;    //Increase negative outgoing weight
+                } else if (w > 0) {
+                sum_weight_pos_in += w;    //Increase positive incoming weight
+            } else {
+                sum_weight_neg_in -= w;    //Increase negative incoming weight
+            }
+
+            l_cur = l_iter.Next();
+        }
+
+        if (!is_directed) {
+            double sum_weight_pos       = sum_weight_pos_out + sum_weight_pos_in;
+            sum_weight_pos_out   = sum_weight_pos;
+            sum_weight_pos_in    = sum_weight_pos;
+            double sum_weight_neg = sum_weight_neg_out + sum_weight_neg_in;
+            sum_weight_neg_out   = sum_weight_neg;
+            sum_weight_neg_in    = sum_weight_neg;
+        }
+
+        //av_k = (av_k*l + sum_weight_pos_in)/(l+1); //Average k
+
+        if (init_spins) {
+            //Set the degrees correctly
+            degree_pos_in[v]    = sum_weight_pos_in;
+            degree_neg_in[v]    = sum_weight_neg_in;
+            degree_pos_out[v]   = sum_weight_pos_out;
+            degree_neg_out[v]   = sum_weight_neg_out;
+        }
+
+        //Correct the community bookkeeping
+        degree_community_pos_in[s]  += sum_weight_pos_in;
+        degree_community_neg_in[s]  += sum_weight_neg_in;
+        degree_community_pos_out[s] += sum_weight_pos_out;
+        degree_community_neg_out[s] += sum_weight_neg_out;
+
+        //Community just increased
+        csize[s]++;
+
+        //Sum the weights (notice that sum of indegrees equals sum of outdegrees)
+        m_p += sum_weight_pos_in;
+        m_n += sum_weight_neg_in;
+    }
+
+#ifdef DEBUG
+    printf("Done assigning.\n");
+#endif
+
+    return;
+}
+//##############################################################
+// This is the function generally used for optimisation,
+// as the parallel update has its flaws, due to the cyclic attractors
+//##############################################################
+double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsigned int max_sweeps) {
+#ifdef DEBUG
+    printf("Starting sweep at temperature %f.\n", t);
+#endif
+    DLList_Iter<NNode*> iter;
+    DLList_Iter<NLink*> l_iter;
+    DLList_Iter<unsigned int*> i_iter, i_iter2;
+    NNode *node, *n_cur;
+    NLink *l_cur;
+    /* The new_spin contains the spin to which we will update,
+     * the spin_opt is the optional spin we will consider and
+     * the old_spin is the spin of the node we are currently
+     * changing.
+     */
+    unsigned int new_spin, spin_opt, old_spin;
+    unsigned int sweep; //current sweep
+    unsigned long changes, problemcount; //Number of changes and number of problems encountered
+
+    double exp_old_spin; //The expectation value for the old spin
+    double exp_spin; //The expectation value for the other spin(s)
+    int v; //The node we will be investigating
+
+    //The variables required for the calculations
+    double delta_pos_out, delta_pos_in, delta_neg_out, delta_neg_in;
+    double k_v_pos_out, k_v_pos_in, k_v_neg_out, k_v_neg_in;
+
+    //weight of edge
+    double w;
+
+    double beta = 1 / t; //Weight for probabilities
+    double r = 0.0; //random number used for assigning new spin
+
+    double maxweight = 0.0;
+    double sum_weights = 0.0; //sum_weights for normalizing the probabilities
+
+    sweep = 0;
+    changes = 0;
+    double m_pt = m_p;
+    double m_nt = m_n;
+
+    if (m_pt < 0.001) {
+        m_pt = 1;
+    }
+
+    if (m_nt < 0.001) {
+        m_nt = 1;
+    }
+
+    while (sweep < max_sweeps) {
+        sweep++;
+        //loop over all nodes in network
+        for (unsigned int n = 0; n < num_nodes; n++) {
+            //Look for a random node
+            v = RNG_INTEGER(0, num_nodes - 1);
+            //We will be investigating node v
+
+            node = net->node_list->Get(v);
+
+            /*******************************************/
+            // initialize the neighbours and the weights
+            problemcount = 0;
+            for (unsigned int i = 0; i <= q; i++) {
+                neighbours[i] = 0.0;
+                weights[i] = 0.0;
+            }
+
+            //Loop over all links (=neighbours)
+            l_cur = l_iter.First(node->Get_Links());
+            while (!l_iter.End()) {
+                w = l_cur->Get_Weight();
+                if (node == l_cur->Get_Start()) {
+                    n_cur = l_cur->Get_End();
+                } else {
+                    n_cur = l_cur->Get_Start();
+                }
+                //Add the link to the correct cluster
+                neighbours[spin[n_cur->Get_Index()]] += w;
+                l_cur = l_iter.Next();
+            }
+            //We now have the weight of the (in and out) neighbours
+            //in each cluster available to us.
+            /*******************************************/
+            old_spin = spin[v];
+
+            //Look for optimal spin
+
+            //Set the appropriate variable
+            delta_pos_out   = degree_pos_out[v];
+            delta_pos_in    = degree_pos_in[v];
+            delta_neg_out   = degree_neg_out[v];
+            delta_neg_in    = degree_neg_in[v];
+
+            k_v_pos_out     = gamma * delta_pos_out / m_pt;
+            k_v_pos_in      = gamma * delta_pos_in / m_pt;
+            k_v_neg_out     = lambda * delta_neg_out / m_nt;
+            k_v_neg_in      = lambda * delta_neg_in / m_nt;
+
+            //The expectation value for the old spin
+            if (is_directed)
+                exp_old_spin = (k_v_pos_out * (degree_community_pos_in[old_spin] - delta_pos_in) -
+                                k_v_neg_out * (degree_community_neg_in[old_spin] - delta_neg_in)) +
+                               (k_v_pos_in * (degree_community_pos_out[old_spin] - delta_pos_out) -
+                                k_v_neg_in * (degree_community_neg_out[old_spin] - delta_neg_out));
+            else
+                exp_old_spin = (k_v_pos_out * (degree_community_pos_in[old_spin] - delta_pos_in) -
+                                k_v_neg_out * (degree_community_neg_in[old_spin] - delta_neg_in));
+
+            /*******************************************/
+            //Calculating probabilities for each transition to another
+            //community.
+
+            maxweight = 0.0;
+            weights[old_spin] = 0.0;
+
+            for (spin_opt = 1; spin_opt <= q; spin_opt++) { // all possible new spins
+                if (spin_opt != old_spin) { // except the old one!
+                    if (is_directed)
+                        exp_spin = (k_v_pos_out * degree_community_pos_in[spin_opt] - k_v_neg_out * degree_community_neg_in[spin_opt]) +
+                                   (k_v_pos_in * degree_community_pos_out[spin_opt] - k_v_neg_in * degree_community_neg_out[spin_opt]);
+                    else {
+                        exp_spin = (k_v_pos_out * degree_community_pos_in[spin_opt] - k_v_neg_out * degree_community_neg_in[spin_opt]);
+                    }
+
+                    weights[spin_opt] = (neighbours[spin_opt] - exp_spin) - (neighbours[old_spin] - exp_old_spin);
+
+                    if (weights[spin_opt] > maxweight) {
+                        maxweight = weights[spin_opt];
+                    }
+                }
+            }   // for spin
+
+            //Calculate exp. prob. an
+            sum_weights = 0.0;
+            for (spin_opt = 1; spin_opt <= q; spin_opt++) { // all possible new spins
+                weights[spin_opt] -= maxweight;  //subtract maxweight for numerical stability (otherwise overflow).
+                weights[spin_opt]  = exp((double)(beta * weights[spin_opt]));
+                sum_weights   += weights[spin_opt];
+            }   // for spin
+            /*******************************************/
+
+
+            /*******************************************/
+            //Choose a new spin dependent on the calculated probabilities
+            r = RNG_UNIF(0, sum_weights);
+            new_spin = 1;
+
+            bool found = false;
+            while (!found && new_spin <= q) {
+                if (r <= weights[new_spin]) {
+                    spin_opt = new_spin; //We have found are new spin
+                    found = true;
+                    break;
+                } else {
+                    r -= weights[new_spin];    //Perhaps the next spin is the one we want
+                }
+
+                new_spin++;
+            }
+
+            //Some weird thing happened. We haven't found a new spin
+            //while that shouldn't be the case. Numerical problems?
+            if (!found) {
+                problemcount++;
+            }
+
+            new_spin = spin_opt;
+            //If there wasn't a problem we should have found
+            //our new spin.
+            /*******************************************/
+
+
+            /*******************************************/
+            //The new spin is available to us, so change
+            //all the appropriate counters.
+            if (new_spin != old_spin) { // Did we really change something??
+                changes++;
+                spin[v] = new_spin;
+
+                //The new spin increase by one, and the old spin decreases by one
+                csize[new_spin]++; csize[old_spin]--;
+
+                //Change the sums of degree for the old spin...
+                degree_community_pos_in[old_spin]   -= delta_pos_in;
+                degree_community_neg_in[old_spin]   -= delta_neg_in;
+                degree_community_pos_out[old_spin]  -= delta_pos_out;
+                degree_community_neg_out[old_spin]  -= delta_neg_out;
+
+                //...and for the new spin
+                degree_community_pos_in[new_spin]   += delta_pos_in;
+                degree_community_neg_in[new_spin]   += delta_neg_in;
+                degree_community_pos_out[new_spin]  += delta_pos_out;
+                degree_community_neg_out[new_spin]  += delta_neg_out;
+            }
+
+            //We have no change a node from old_spin to new_spin
+            /*******************************************/
+
+        } // for n
+    }  // while sweep
+#ifdef DEBUG
+    printf("Done %d sweeps.\n", max_sweeps);
+    printf("%d changes made for %d nodes.\n", changes, num_nodes);
+    printf("Last node is %d and last random number is %f with sum of weights %f with spin %d.\n", v, r, sum_weights, old_spin);
+#endif
+
+    return (double(changes) / double(num_nodes) / double(sweep));
+}
+
+//We need to begin at a suitable temperature. That is, a temperature at which
+//enough nodes may change their initially assigned communties
+double PottsModelN::FindStartTemp(double gamma, double lambda, double ts) {
+    double kT;
+    kT = ts;
+    //assing random initial condition
+    assign_initial_conf(true);
+    // the factor 1-1/q is important, since even, at infinite temperature,
+    // only 1-1/q of all spins do change their state, since a randomly chooses new
+    // state is with prob. 1/q the old state.
+    double acceptance = 0.0;
+    while (acceptance < (1.0 - 1.0 / double(q)) * 0.95) { //want 95% acceptance
+        kT = kT * 1.1;
+        acceptance = HeatBathLookup(gamma, lambda, kT, 50);
+    }
+    kT *= 1.1; // just to be sure...
+    return kT;
+}
+
+long PottsModelN::WriteClusters(igraph_real_t *modularity,
+                                igraph_real_t *temperature,
+                                igraph_vector_t *community_size,
+                                igraph_vector_t *membership,
+                                igraph_matrix_t *adhesion,
+                                igraph_matrix_t *normalised_adhesion,
+                                igraph_real_t *polarization,
+                                double t,
+                                double d_p,
+                                double d_n,
+                                double gamma,
+                                double lambda) {
+    IGRAPH_UNUSED(gamma);
+    IGRAPH_UNUSED(lambda);
+#ifdef DEBUG
+    printf("Start writing clusters.\n");
+#endif
+    //Reassign each community so that we retrieve a community assignment 1 through num_communities
+    unsigned int *cluster_assign = new unsigned int[q + 1];
+    for (unsigned int i = 0; i <= q; i++) {
+        cluster_assign[i] = 0;
+    }
+
+    int num_clusters = 0;
+
+    //Find out what the new communities will be
+    for (unsigned int i = 0; i < num_nodes; i++) {
+        int s = spin[i];
+        if (cluster_assign[s] == 0) {
+            num_clusters++;
+            cluster_assign[s] = num_clusters;
+#ifdef DEBUG
+            printf("Setting cluster %d to %d.\n", s, num_clusters);
+#endif
+        }
+    }
+
+
+    /*
+    DLList_Iter<NNode*> iter;
+    NNode *n_cur=iter.First(net->node_list);
+    n_cur = iter.First(net->node_list);
+    */
+
+    //And now assign each node to its new community
+    q = num_clusters;
+    for (unsigned int i = 0; i < num_nodes; i++) {
+#ifdef DEBUG
+        printf("Setting node %d to %d.\n", i, cluster_assign[spin[i]]);
+#endif
+        unsigned int s = cluster_assign[spin[i]];
+        spin[i] = s;
+#ifdef DEBUG
+        printf("Have set node %d to %d.\n", i, s);
+#endif
+    }
+    assign_initial_conf(false);
+
+    delete[] cluster_assign;
+
+    if (temperature) {
+        *temperature = t;
+    }
+
+    if (community_size) {
+        //Initialize the vector
+        IGRAPH_CHECK(igraph_vector_resize(community_size, q));
+        for (unsigned int spin_opt = 1; spin_opt <= q; spin_opt++) {
+            //Set the community size
+            VECTOR(*community_size)[spin_opt - 1] = csize[spin_opt];
+        }
+    }
+
+    //Set the membership
+    if (membership) {
+        IGRAPH_CHECK(igraph_vector_resize(membership, num_nodes));
+        for (unsigned int i = 0; i < num_nodes; i++) {
+            VECTOR(*membership)[ i ] = spin[i] - 1;
+        }
+    }
+
+    double Q = 0.0; //Modularity
+    if (adhesion) {
+        IGRAPH_CHECK(igraph_matrix_resize(adhesion, q, q));
+        IGRAPH_CHECK(igraph_matrix_resize(normalised_adhesion, q, q));
+
+        double **num_links_pos = 0;
+        double **num_links_neg = 0;
+        //memory allocated for elements of rows.
+        num_links_pos = new double *[q + 1] ;
+        num_links_neg = new double *[q + 1] ;
+
+        //memory allocated for  elements of each column.
+        for ( unsigned int i = 0 ; i < q + 1 ; i++) {
+            num_links_pos[i] = new double[q + 1];
+            num_links_neg[i] = new double[q + 1];
+        }
+
+
+
+        //Init num_links
+        for (unsigned int i = 0; i <= q; i++) {
+            for (unsigned int j = 0; j <= q; j++) {
+                num_links_pos[i][j] = 0.0;
+                num_links_neg[i][j] = 0.0;
+            }
+        }
+
+        DLList_Iter<NLink*> iter_l;
+        NLink *l_cur = iter_l.First(net->link_list);
+
+        double w = 0.0;
+
+        while (!iter_l.End()) {
+            w = l_cur->Get_Weight();
+            unsigned int a = spin[l_cur->Get_Start()->Get_Index()];
+            unsigned int b =  spin[l_cur->Get_End()->Get_Index()];
+            if (w > 0) {
+                num_links_pos[a][b] += w;
+                if (!is_directed && a != b) { //Only one edge is defined in case it is undirected
+                    num_links_pos[b][a] += w;
+                }
+            } else {
+                num_links_neg[a][b] -= w;
+                if (!is_directed && a != b) { //Only one edge is defined in case it is undirected
+                    num_links_neg[b][a] -= w;
+                }
+            }
+
+            l_cur = iter_l.Next();
+        } //while links
+
+#ifdef DEBUG
+        printf("d_p: %f\n", d_p);
+        printf("d_n: %f\n", d_n);
+#endif
+
+        double expected = 0.0;
+        double a = 0.0;
+        double normal_a = 0.0;
+
+        double delta, u_p, u_n;
+        double max_expected, max_a;
+
+        //We don't take into account the lambda or gamma for
+        //computing the modularity and adhesion, since they
+        //are then incomparable to other definitions.
+        for (unsigned int i = 1; i <= q; i++) {
+            for (unsigned int j = 1; j <= q; j++) {
+                if (!is_directed && i == j)
+                    expected    = degree_community_pos_out[i] * degree_community_pos_in[j] / (m_p == 0 ? 1 : 2 * m_p)
+                                  - degree_community_neg_out[i] * degree_community_neg_in[j] / (m_n == 0 ? 1 : 2 * m_n);
+                else
+                    expected    = degree_community_pos_out[i] * degree_community_pos_in[j] / (m_p == 0 ? 1 : m_p)
+                                  - degree_community_neg_out[i] * degree_community_neg_in[j] / (m_n == 0 ? 1 : m_n);
+
+                a           = (num_links_pos[i][j] - num_links_neg[i][j]) - expected;
+
+                if (i == j) { //cohesion
+                    if (is_directed) {
+                        delta = d_p * csize[i] * (csize[i] - 1);    //Maximum amount
+                    } else {
+                        delta = d_p * csize[i] * (csize[i] - 1) / 2;    //Maximum amount
+                    }
+
+                    u_p     = delta - num_links_pos[i][i]; //Add as many positive links we can
+                    u_n     = -num_links_neg[i][i]; //Delete as many negative links we can
+                    Q      += a;
+                } else { //adhesion
+                    if (is_directed) {
+                        delta = d_n * csize[i] * csize[j] * 2;    //Maximum amount
+                    } else {
+                        delta = d_n * csize[i] * csize[j];    //Maximum amount
+                    }
+
+                    u_p     = -num_links_pos[i][j]; //Delete as many positive links we can
+                    u_n     = delta - num_links_neg[i][j]; //Add as many negative links we can
+                }
+
+                if (!is_directed && i == j)
+                    max_expected    = (degree_community_pos_out[i] + u_p) * (degree_community_pos_in[j] + u_p) / ((m_p + u_p) == 0 ? 1 : 2 * (m_p + u_p))
+                                      - (degree_community_neg_out[i] - u_n) * (degree_community_neg_in[j] + u_n) / ((m_n + u_n) == 0 ? 1 : 2 * (m_n + u_n));
+                else
+                    max_expected    = (degree_community_pos_out[i] + u_p) * (degree_community_pos_in[j] + u_p) / ((m_p + u_p) == 0 ? 1 : m_p + u_p)
+                                      - (degree_community_neg_out[i] - u_n) * (degree_community_neg_in[j] + u_n) / ((m_n + u_n) == 0 ? 1 : m_n + u_n);
+                //printf("%f/%f %d/%d\t", num_links_pos[i][j], num_links_neg[i][j], csize[i], csize[j]);
+                //printf("%f/%f - %f(%f)\t", u_p, u_n, expected, max_expected);
+                max_a           = ((num_links_pos[i][j] + u_p) - (num_links_neg[i][j] + u_n)) - max_expected;
+
+
+                //In cases where we haven't actually found a ground state
+                //the adhesion/cohesion *might* not be negative/positive,
+                //hence the maximum adhesion and cohesion might behave quite
+                //strangely. In order to prevent that, we limit them to 1 in
+                //absolute value, and prevent from dividing by zero (even if
+                //chuck norris would).
+                if (i == j) {
+                    normal_a = a / (max_a == 0 ? a : max_a);
+                } else {
+                    normal_a = -a / (max_a == 0 ? a : max_a);
+                }
+
+                if (normal_a > 1) {
+                    normal_a = 1;
+                } else if (normal_a < -1) {
+                    normal_a = -1;
+                }
+
+                MATRIX(*adhesion, i - 1, j - 1) = a;
+                MATRIX(*normalised_adhesion, i - 1, j - 1) = normal_a;
+            } //for j
+            //printf("\n");
+        } //for i
+
+        //free the allocated memory
+        for ( unsigned int i = 0 ; i < q + 1 ; i++ ) {
+            delete [] num_links_pos[i] ;
+            delete [] num_links_neg[i];
+        }
+        delete [] num_links_pos ;
+        delete [] num_links_neg ;
+
+    } //adhesion
+
+    if (modularity) {
+        if (is_directed) {
+            *modularity = Q / (m_p + m_n);
+        } else {
+            *modularity = 2 * Q / (m_p + m_n);    //Correction for the way m_p and m_n are counted. Modularity is 1/m, not 1/2m
+        }
+    }
+
+    if (polarization) {
+        double sum_ad = 0.0;
+        for (unsigned int i = 0; i < q; i++) {
+            for (unsigned int j = 0; j < q; j++) {
+                if (i != j) {
+                    sum_ad -= MATRIX(*normalised_adhesion, i, j);
+                }
+            }
+        }
+        *polarization = sum_ad / (q * q - q);
+    }
+#ifdef DEBUG
+    printf("Finished writing cluster.\n");
+#endif
+    return num_nodes;
+}
diff --git a/igraph/src/pow_ci.c b/igraph/src/pow_ci.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/pow_ci.c
@@ -0,0 +1,26 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+VOID pow_ci(p, a, b) 	/* p = a**b  */
+ f2c_complex *p, *a; integer *b;
+#else
+extern void pow_zi(doublecomplex*, doublecomplex*, integer*);
+void pow_ci(f2c_complex *p, f2c_complex *a, integer *b) 	/* p = a**b  */
+#endif
+{
+doublecomplex p1, a1;
+
+a1.r = a->r;
+a1.i = a->i;
+
+pow_zi(&p1, &a1, b);
+
+p->r = p1.r;
+p->i = p1.i;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/pow_dd.c b/igraph/src/pow_dd.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/pow_dd.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double pow();
+double pow_dd(ap, bp) doublereal *ap, *bp;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double pow_dd(doublereal *ap, doublereal *bp)
+#endif
+{
+return(pow(*ap, *bp) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/pow_di.c b/igraph/src/pow_di.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/pow_di.c
@@ -0,0 +1,41 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double pow_di(ap, bp) doublereal *ap; integer *bp;
+#else
+double pow_di(doublereal *ap, integer *bp)
+#endif
+{
+double pow, x;
+integer n;
+unsigned long u;
+
+pow = 1;
+x = *ap;
+n = *bp;
+
+if(n != 0)
+	{
+	if(n < 0)
+		{
+		n = -n;
+		x = 1/x;
+		}
+	for(u = n; ; )
+		{
+		if(u & 01)
+			pow *= x;
+		if(u >>= 1)
+			x *= x;
+		else
+			break;
+		}
+	}
+return(pow);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/pow_hh.c b/igraph/src/pow_hh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/pow_hh.c
@@ -0,0 +1,39 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+shortint pow_hh(ap, bp) shortint *ap, *bp;
+#else
+shortint pow_hh(shortint *ap, shortint *bp)
+#endif
+{
+	shortint pow, x, n;
+	unsigned u;
+
+	x = *ap;
+	n = *bp;
+
+	if (n <= 0) {
+		if (n == 0 || x == 1)
+			return 1;
+		if (x != -1)
+			return x == 0 ? 1/x : 0;
+		n = -n;
+		}
+	u = n;
+	for(pow = 1; ; )
+		{
+		if(u & 01)
+			pow *= x;
+		if(u >>= 1)
+			x *= x;
+		else
+			break;
+		}
+	return(pow);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/pow_ii.c b/igraph/src/pow_ii.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/pow_ii.c
@@ -0,0 +1,39 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+integer pow_ii(ap, bp) integer *ap, *bp;
+#else
+integer pow_ii(integer *ap, integer *bp)
+#endif
+{
+	integer pow, x, n;
+	unsigned long u;
+
+	x = *ap;
+	n = *bp;
+
+	if (n <= 0) {
+		if (n == 0 || x == 1)
+			return 1;
+		if (x != -1)
+			return x == 0 ? 1/x : 0;
+		n = -n;
+		}
+	u = n;
+	for(pow = 1; ; )
+		{
+		if(u & 01)
+			pow *= x;
+		if(u >>= 1)
+			x *= x;
+		else
+			break;
+		}
+	return(pow);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/pow_ri.c b/igraph/src/pow_ri.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/pow_ri.c
@@ -0,0 +1,41 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double pow_ri(ap, bp) real *ap; integer *bp;
+#else
+double pow_ri(real *ap, integer *bp)
+#endif
+{
+double pow, x;
+integer n;
+unsigned long u;
+
+pow = 1;
+x = *ap;
+n = *bp;
+
+if(n != 0)
+	{
+	if(n < 0)
+		{
+		n = -n;
+		x = 1/x;
+		}
+	for(u = n; ; )
+		{
+		if(u & 01)
+			pow *= x;
+		if(u >>= 1)
+			x *= x;
+		else
+			break;
+		}
+	}
+return(pow);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/pow_zi.c b/igraph/src/pow_zi.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/pow_zi.c
@@ -0,0 +1,60 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+VOID pow_zi(p, a, b) 	/* p = a**b  */
+ doublecomplex *p, *a; integer *b;
+#else
+extern void z_div(doublecomplex*, doublecomplex*, doublecomplex*);
+void pow_zi(doublecomplex *p, doublecomplex *a, integer *b) 	/* p = a**b  */
+#endif
+{
+	integer n;
+	unsigned long u;
+	double t;
+	doublecomplex q, x;
+	static doublecomplex one = {1.0, 0.0};
+
+	n = *b;
+	q.r = 1;
+	q.i = 0;
+
+	if(n == 0)
+		goto done;
+	if(n < 0)
+		{
+		n = -n;
+		z_div(&x, &one, a);
+		}
+	else
+		{
+		x.r = a->r;
+		x.i = a->i;
+		}
+
+	for(u = n; ; )
+		{
+		if(u & 01)
+			{
+			t = q.r * x.r - q.i * x.i;
+			q.i = q.r * x.i + q.i * x.r;
+			q.r = t;
+			}
+		if(u >>= 1)
+			{
+			t = x.r * x.r - x.i * x.i;
+			x.i = 2 * x.r * x.i;
+			x.r = t;
+			}
+		else
+			break;
+		}
+ done:
+	p->i = q.i;
+	p->r = q.r;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/pow_zz.c b/igraph/src/pow_zz.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/pow_zz.c
@@ -0,0 +1,29 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double log(), exp(), cos(), sin(), atan2(), f__cabs();
+VOID pow_zz(r,a,b) doublecomplex *r, *a, *b;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern double f__cabs(double,double);
+void pow_zz(doublecomplex *r, doublecomplex *a, doublecomplex *b)
+#endif
+{
+double logr, logi, x, y;
+
+logr = log( f__cabs(a->r, a->i) );
+logi = atan2(a->i, a->r);
+
+x = exp( logr * b->r - logi * b->i );
+y = logr * b->i + logi * b->r;
+
+r->r = x * cos(y);
+r->i = x * sin(y);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/progress.c b/igraph/src/progress.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/progress.c
@@ -0,0 +1,153 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_progress.h"
+#include "config.h"
+
+static IGRAPH_THREAD_LOCAL igraph_progress_handler_t *igraph_i_progress_handler = 0;
+static IGRAPH_THREAD_LOCAL char igraph_i_progressmsg_buffer[1000];
+
+/**
+ * \function igraph_progress
+ * Report progress
+ *
+ * Note that the usual way to report progress is the \ref IGRAPH_PROGRESS
+ * macro, as that takes care of the return value of the progress
+ * handler.
+ * \param message A string describing the function or algorithm
+ *     that is reporting the progress. Current igraph functions
+ *     always use the name \p message argument if reporting from the
+ *     same function.
+ * \param percent Numeric, the percentage that was completed by the
+ *     algorithm or function.
+ * \param data User-defined data. Current igraph functions that
+ *     report progress pass a null pointer here. Users can
+ *     write their own progress handlers and functions with progress
+ *     reporting, and then pass some meaningfull context here.
+ * \return If there is a progress handler installed and
+ *     it does not return \c IGRAPH_SUCCESS, then \c IGRAPH_INTERRUPTED
+ *     is returned.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_progress(const char *message, igraph_real_t percent, void *data) {
+    if (igraph_i_progress_handler) {
+        if (igraph_i_progress_handler(message, percent, data) != IGRAPH_SUCCESS) {
+            return IGRAPH_INTERRUPTED;
+        }
+    }
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_progressf
+ * Report progress, printf-like version
+ *
+ * This is a more flexible version of \ref igraph_progress(), with
+ * a printf-like template string. First the template string
+ * is filled with the additional arguments and then \ref
+ * igraph_progress() is called.
+ *
+ * </para><para>Note that there is an upper limit for the length of
+ * the \p message string, currently 1000 characters.
+ * \param message A string describing the function or algorithm
+ *     that is reporting the progress. For this function this is a
+ *     template string, using the same syntax as the standard
+ *     \c libc \c printf function.
+ * \param percent Numeric, the percentage that was completed by the
+ *     algorithm or function.
+ * \param data User-defined data. Current igraph functions that
+ *     report progress pass a null pointer here. Users can
+ *     write their own progress handlers and functions with progress
+ *     reporting, and then pass some meaningfull context here.
+ * \param ... Additional argument that were specified in the
+ *     \p message argument.
+ * \return If there is a progress handler installed and
+ *     it does not return \c IGRAPH_SUCCESS, then \c IGRAPH_INTERRUPTED
+ *     is returned.
+ * \return
+ */
+
+int igraph_progressf(const char *message, igraph_real_t percent, void *data,
+                     ...) {
+    va_list ap;
+    va_start(ap, data);
+    vsnprintf(igraph_i_progressmsg_buffer,
+              sizeof(igraph_i_progressmsg_buffer) / sizeof(char), message, ap);
+    return igraph_progress(igraph_i_progressmsg_buffer, percent, data);
+}
+
+#ifndef USING_R
+
+/**
+ * \function igraph_progress_handler_stderr
+ * A simple predefined progress handler
+ *
+ * This simple progress handler first prints \p message, and then
+ * the percentage complete value in a short message to standard error.
+ * \param message A string describing the function or algorithm
+ *     that is reporting the progress. Current igraph functions
+ *     always use the name \p message argument if reporting from the
+ *     same function.
+ * \param percent Numeric, the percentage that was completed by the
+ *     algorithm or function.
+ * \param data User-defined data. Current igraph functions that
+ *     report progress pass a null pointer here. Users can
+ *     write their own progress handlers and functions with progress
+ *     reporting, and then pass some meaningfull context here.
+ * \return This function always returns with \c IGRAPH_SUCCESS.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_progress_handler_stderr(const char *message, igraph_real_t percent,
+                                   void* data) {
+    IGRAPH_UNUSED(data);
+    fputs(message, stderr);
+    fprintf(stderr, "%.1f percent ready\n", (double)percent);
+    return 0;
+}
+#endif
+
+/**
+ * \function igraph_set_progress_handler
+ * Install a progress handler, or remove the current handler
+ *
+ * There is a single simple predefined progress handler:
+ * \ref igraph_progress_handler_stderr().
+ * \param new_handler Pointer to a function of type
+ *     \ref igraph_progress_handler_t, the progress handler function to
+ *     install. To uninstall the current progress handler, this argument
+ *     can be a null pointer.
+ * \return Pointer to the previously installed progress handler function.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_progress_handler_t *
+igraph_set_progress_handler(igraph_progress_handler_t new_handler) {
+    igraph_progress_handler_t *previous_handler = igraph_i_progress_handler;
+    igraph_i_progress_handler = new_handler;
+    return previous_handler;
+}
diff --git a/igraph/src/prpack.cpp b/igraph/src/prpack.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack.cpp
@@ -0,0 +1,103 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "prpack.h"
+#include "prpack/prpack_igraph_graph.h"
+#include "prpack/prpack_solver.h"
+#include "igraph_error.h"
+
+using namespace prpack;
+using namespace std;
+
+/*
+ * PRPACK-based implementation of \c igraph_personalized_pagerank.
+ *
+ * See \c igraph_personalized_pagerank for the documentation of the parameters.
+ */
+int igraph_personalized_pagerank_prpack(const igraph_t *graph, 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) {
+    long int i, no_of_nodes = igraph_vcount(graph), nodes_to_calc;
+    igraph_vit_t vit;
+    double* u = 0;
+    double* v = 0;
+    const prpack_result* res;
+
+    if (reset) {
+        /* Normalize reset vector so the sum is 1 */
+        double reset_sum = igraph_vector_sum(reset);
+        if (igraph_vector_min(reset) < 0) {
+            IGRAPH_ERROR("the reset vector must not contain negative elements", IGRAPH_EINVAL);
+        }
+        if (reset_sum == 0) {
+            IGRAPH_ERROR("the sum of the elements in the reset vector must not be zero", IGRAPH_EINVAL);
+        }
+
+        // Construct the personalization vector
+        v = new double[no_of_nodes];
+        for (i = 0; i < no_of_nodes; i++) {
+            v[i] = VECTOR(*reset)[i] / reset_sum;
+        }
+    }
+
+    // Construct and run the solver
+    prpack_igraph_graph prpack_graph(graph, weights, directed);
+    prpack_solver solver(&prpack_graph, false);
+    res = solver.solve(damping, 1e-10, u, v, "");
+
+    // Delete the personalization vector
+    if (v) {
+        delete[] v;
+    }
+
+    // Check whether the solver converged
+    // TODO: this is commented out because some of the solvers do not implement it yet
+    /*
+    if (!res->converged) {
+        IGRAPH_WARNING("PRPACK solver failed to converge. Results may be inaccurate.");
+    }
+    */
+
+    // Fill the result vector
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+    IGRAPH_CHECK(igraph_vector_resize(vector, nodes_to_calc));
+    for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit);
+         IGRAPH_VIT_NEXT(vit), i++) {
+        VECTOR(*vector)[i] = res->x[(long int)IGRAPH_VIT_GET(vit)];
+    }
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    // TODO: can we get the eigenvalue? We'll just fake it until we can.
+    if (value) {
+        *value = 1.0;
+    }
+    delete res;
+
+    return IGRAPH_SUCCESS;
+}
+
diff --git a/igraph/src/prpack_base_graph.cpp b/igraph/src/prpack_base_graph.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_base_graph.cpp
@@ -0,0 +1,333 @@
+#include "prpack_base_graph.h"
+#include "prpack_utils.h"
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <vector>
+#include <limits>
+using namespace prpack;
+using namespace std;
+
+void prpack_base_graph::initialize() {
+    heads = NULL;
+    tails = NULL;
+    vals = NULL;
+}
+
+prpack_base_graph::prpack_base_graph() {
+	initialize();
+	num_vs = num_es = 0;
+}
+
+prpack_base_graph::prpack_base_graph(const prpack_csc* g) {
+    initialize();
+    num_vs = g->num_vs;
+    num_es = g->num_es;
+    // fill in heads and tails
+    num_self_es = 0;
+    int* hs = g->heads;
+    int* ts = g->tails;
+    tails = new int[num_vs];
+    memset(tails, 0, num_vs*sizeof(tails[0]));
+    for (int h = 0; h < num_vs; ++h) {
+        const int start_ti = hs[h];
+        const int end_ti = (h + 1 != num_vs) ? hs[h + 1] : num_es;
+        for (int ti = start_ti; ti < end_ti; ++ti) {
+            const int t = ts[ti];
+            ++tails[t];
+            if (h == t)
+                ++num_self_es;
+        }
+    }
+    for (int i = 0, sum = 0; i < num_vs; ++i) {
+        const int temp = sum;
+        sum += tails[i];
+        tails[i] = temp;
+    }
+    heads = new int[num_es];
+    int* osets = new int[num_vs];
+    memset(osets, 0, num_vs*sizeof(osets[0]));
+    for (int h = 0; h < num_vs; ++h) {
+        const int start_ti = hs[h];
+        const int end_ti = (h + 1 != num_vs) ? hs[h + 1] : num_es;
+        for (int ti = start_ti; ti < end_ti; ++ti) {
+            const int t = ts[ti];
+            heads[tails[t] + osets[t]++] = h;
+        }
+    }
+    // clean up
+    delete[] osets;
+}
+
+prpack_base_graph::prpack_base_graph(const prpack_int64_csc* g) {
+    initialize();
+    // TODO remove the assert and add better behavior
+    assert(num_vs <= std::numeric_limits<int>::max());
+    num_vs = (int)g->num_vs;
+    num_es = (int)g->num_es;
+    // fill in heads and tails
+    num_self_es = 0;
+    int64_t* hs = g->heads;
+    int64_t* ts = g->tails;
+    tails = new int[num_vs];
+    memset(tails, 0, num_vs*sizeof(tails[0]));
+    for (int h = 0; h < num_vs; ++h) {
+        const int start_ti = (int)hs[h];
+        const int end_ti = (h + 1 != num_vs) ? (int)hs[h + 1] : num_es;
+        for (int ti = start_ti; ti < end_ti; ++ti) {
+            const int t = (int)ts[ti];
+            ++tails[t];
+            if (h == t)
+                ++num_self_es;
+        }
+    }
+    for (int i = 0, sum = 0; i < num_vs; ++i) {
+        const int temp = sum;
+        sum += tails[i];
+        tails[i] = temp;
+    }
+    heads = new int[num_es];
+    int* osets = new int[num_vs];
+    memset(osets, 0, num_vs*sizeof(osets[0]));
+    for (int h = 0; h < num_vs; ++h) {
+        const int start_ti = (int)hs[h];
+        const int end_ti = (h + 1 != num_vs) ? (int)hs[h + 1] : num_es;
+        for (int ti = start_ti; ti < end_ti; ++ti) {
+            const int t = (int)ts[ti];
+            heads[tails[t] + osets[t]++] = h;
+        }
+    }
+    // clean up
+    delete[] osets;
+}
+
+prpack_base_graph::prpack_base_graph(const prpack_csr* g) {
+    initialize();
+    assert(false);
+    // TODO
+}
+
+prpack_base_graph::prpack_base_graph(const prpack_edge_list* g) {
+    initialize();
+    num_vs = g->num_vs;
+    num_es = g->num_es;
+    // fill in heads and tails
+    num_self_es = 0;
+    int* hs = g->heads;
+    int* ts = g->tails;
+    tails = new int[num_vs];
+    memset(tails, 0, num_vs*sizeof(tails[0]));
+    for (int i = 0; i < num_es; ++i) {
+        ++tails[ts[i]];
+        if (hs[i] == ts[i])
+            ++num_self_es;
+    }
+    for (int i = 0, sum = 0; i < num_vs; ++i) {
+        const int temp = sum;
+        sum += tails[i];
+        tails[i] = temp;
+    }
+    heads = new int[num_es];
+    int* osets = new int[num_vs];
+    memset(osets, 0, num_vs*sizeof(osets[0]));
+    for (int i = 0; i < num_es; ++i)
+        heads[tails[ts[i]] + osets[ts[i]]++] = hs[i];
+    // clean up
+    delete[] osets;
+}
+
+prpack_base_graph::prpack_base_graph(const char* filename, const char* format, const bool weighted) {
+    initialize();
+    FILE* f = fopen(filename, "r");
+    const string s(filename);
+    const string t(format);
+    const string ext = (t == "") ? s.substr(s.rfind('.') + 1) : t;
+    if (ext == "smat") {
+        read_smat(f, weighted);
+    } else {
+        prpack_utils::validate(!weighted, 
+            "Error: graph format is not compatible with weighted option.");
+        if (ext == "edges" || ext == "eg2") {
+            read_edges(f);
+        } else if (ext == "graph-txt") {
+            read_ascii(f);
+        } else {
+            prpack_utils::validate(false, "Error: invalid graph format.");
+        }
+    }
+    fclose(f);
+}
+
+prpack_base_graph::~prpack_base_graph() {
+    delete[] heads;
+    delete[] tails;
+    delete[] vals;
+}
+
+void prpack_base_graph::read_smat(FILE* f, const bool weighted) {
+    // read in header
+    double ignore = 0.0;
+    assert(fscanf(f, "%d %lf %d", &num_vs, &ignore, &num_es) == 3);
+    // fill in heads and tails
+    num_self_es = 0;
+    int* hs = new int[num_es];
+    int* ts = new int[num_es];
+    heads = new int[num_es];
+    tails = new int[num_vs];
+    double* vs = NULL;
+    if (weighted) {
+        vs = new double[num_es];
+        vals = new double[num_es];
+    }
+    memset(tails, 0, num_vs*sizeof(tails[0]));
+    for (int i = 0; i < num_es; ++i) {
+        assert(fscanf(f, "%d %d %lf", 
+            &hs[i], &ts[i], &((weighted) ? vs[i] : ignore)) == 3);
+        ++tails[ts[i]];
+        if (hs[i] == ts[i])
+            ++num_self_es;
+    }
+    for (int i = 0, sum = 0; i < num_vs; ++i) {
+        const int temp = sum;
+        sum += tails[i];
+        tails[i] = temp;
+    }
+    int* osets = new int[num_vs];
+    memset(osets, 0, num_vs*sizeof(osets[0]));
+    for (int i = 0; i < num_es; ++i) {
+        const int idx = tails[ts[i]] + osets[ts[i]]++;
+        heads[idx] = hs[i];
+        if (weighted)
+            vals[idx] = vs[i];
+    }
+    // clean up
+    delete[] hs;
+    delete[] ts;
+    delete[] vs;
+    delete[] osets;
+}
+
+void prpack_base_graph::read_edges(FILE* f) {
+    vector<vector<int> > al;
+    int h, t;
+    num_es = num_self_es = 0;
+    while (fscanf(f, "%d %d", &h, &t) == 2) {
+        const int m = (h < t) ? t : h;
+        if ((int) al.size() < m + 1)
+            al.resize(m + 1);
+        al[t].push_back(h);
+        ++num_es;
+        if (h == t)
+            ++num_self_es;
+    }
+    num_vs = al.size();
+    heads = new int[num_es];
+    tails = new int[num_vs];
+    for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) {
+        tails[tails_i] = heads_i;
+        for (int j = 0; j < (int) al[tails_i].size(); ++j)
+            heads[heads_i++] = al[tails_i][j];
+    }
+}
+
+void prpack_base_graph::read_ascii(FILE* f) {
+    assert(fscanf(f, "%d", &num_vs) == 1);
+    while (getc(f) != '\n');
+    vector<int>* al = new vector<int>[num_vs];
+    num_es = num_self_es = 0;
+    char s[32];
+    for (int h = 0; h < num_vs; ++h) {
+        bool line_ended = false;
+        while (!line_ended) {
+            for (int i = 0; ; ++i) {
+                s[i] = getc(f);
+                if ('9' < s[i] || s[i] < '0') {
+                    line_ended = s[i] == '\n';
+                    if (i != 0) {
+                        s[i] = '\0';
+                        const int t = atoi(s);
+                        al[t].push_back(h);
+                        ++num_es;
+                        if (h == t)
+                            ++num_self_es;
+                    }
+                    break;
+                }
+            }
+        }
+    }
+    heads = new int[num_es];
+    tails = new int[num_vs];
+    for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) {
+        tails[tails_i] = heads_i;
+        for (int j = 0; j < (int) al[tails_i].size(); ++j)
+            heads[heads_i++] = al[tails_i][j];
+    }
+    delete[] al;
+}
+
+prpack_base_graph::prpack_base_graph(int nverts, int nedges, 
+        std::pair<int,int>* edges) {
+    initialize();
+    num_vs = nverts;
+    num_es = nedges;
+
+    // fill in heads and tails
+    num_self_es = 0;
+    int* hs = new int[num_es];
+    int* ts = new int[num_es];
+    tails = new int[num_vs];
+    memset(tails, 0, num_vs*sizeof(tails[0]));
+    for (int i = 0; i < num_es; ++i) {
+        assert(edges[i].first >= 0 && edges[i].first < num_vs);
+        assert(edges[i].second >= 0 && edges[i].second < num_vs);
+        hs[i] = edges[i].first;
+        ts[i] = edges[i].second;
+        ++tails[ts[i]];
+        if (hs[i] == ts[i])
+            ++num_self_es;
+    }
+    for (int i = 0, sum = 0; i < num_vs; ++i) {
+        int temp = sum;
+        sum += tails[i];
+        tails[i] = temp;
+    }
+    heads = new int[num_es];
+    int* osets = new int[num_vs];
+    memset(osets, 0, num_vs*sizeof(osets[0]));
+    for (int i = 0; i < num_es; ++i)
+        heads[tails[ts[i]] + osets[ts[i]]++] = hs[i];
+    // clean up
+    delete[] hs;
+    delete[] ts;
+    delete[] osets;
+}
+
+/** Normalize the edge weights to sum to one.  
+ */
+void prpack_base_graph::normalize_weights() {
+    if (!vals) { 
+        // skip normalizing weights if not using values
+        return;
+    }
+    std::vector<double> rowsums(num_vs,0.);
+    // the graph is in a compressed in-edge list.
+    for (int i=0; i<num_vs; ++i) {
+        int end_ei = (i + 1 != num_vs) ? tails[i + 1] : num_es;
+        for (int ei=tails[i]; ei < end_ei; ++ei) {
+            int head = heads[ei];
+            rowsums[head] += vals[ei];
+        }
+    }
+    for (int i=0; i<num_vs; ++i) {
+        rowsums[i] = 1./rowsums[i];
+    }
+    for (int i=0; i<num_vs; ++i) {
+        int end_ei = (i + 1 != num_vs) ? tails[i + 1] : num_es;
+        for (int ei=tails[i]; ei < end_ei; ++ei) {
+            vals[ei] *= rowsums[heads[ei]];
+        }
+    }
+}
+
diff --git a/igraph/src/prpack_igraph_graph.cpp b/igraph/src/prpack_igraph_graph.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_igraph_graph.cpp
@@ -0,0 +1,146 @@
+#include "prpack_igraph_graph.h"
+#include <cstdlib>
+#include <cstring>
+
+using namespace prpack;
+using namespace std;
+
+#ifdef PRPACK_IGRAPH_SUPPORT
+
+prpack_igraph_graph::prpack_igraph_graph(const igraph_t* g, const igraph_vector_t* weights,
+        igraph_bool_t directed) {
+    const igraph_bool_t treat_as_directed = igraph_is_directed(g) && directed;
+    igraph_es_t es;
+    igraph_eit_t eit;
+    igraph_vector_t neis;
+    long int i, j, eid, sum, temp, num_ignored_es;
+    int *p_head, *p_head_copy;
+    double* p_weight;
+
+    // Get the number of vertices and edges. For undirected graphs, we add
+    // an edge in both directions.
+    num_vs = igraph_vcount(g);
+    num_es = igraph_ecount(g);
+    num_self_es = 0;
+    if (!treat_as_directed) {
+        num_es *= 2;
+    }
+
+    // Allocate memory for heads and tails
+    p_head = heads = new int[num_es];
+    tails = new int[num_vs];
+    memset(tails, 0, num_vs * sizeof(tails[0]));
+
+    // Allocate memory for weights if needed
+    if (weights != 0) {
+        p_weight = vals = new double[num_es];
+    }
+
+    // Count the number of ignored edges (those with negative or zero weight)
+    num_ignored_es = 0;
+
+    if (treat_as_directed) {
+        // Select all the edges and iterate over them by the source vertices
+        es = igraph_ess_all(IGRAPH_EDGEORDER_TO);
+
+        // Add the edges
+        igraph_eit_create(g, es, &eit);
+        while (!IGRAPH_EIT_END(eit)) {
+            eid = IGRAPH_EIT_GET(eit);
+            IGRAPH_EIT_NEXT(eit);
+
+            // Handle the weight
+            if (weights != 0) {
+                // Does this edge have zero or negative weight?
+                if (VECTOR(*weights)[eid] <= 0) {
+                    // Ignore it.
+                    num_ignored_es++;
+                    continue;
+                }
+
+                *p_weight = VECTOR(*weights)[eid];
+                ++p_weight;
+            }
+
+            *p_head = IGRAPH_FROM(g, eid);
+            ++p_head;
+            ++tails[IGRAPH_TO(g, eid)];
+
+            if (IGRAPH_FROM(g, eid) == IGRAPH_TO(g, eid)) {
+                ++num_self_es;
+            }
+        }
+        igraph_eit_destroy(&eit);
+    } else {
+        // Select all the edges and iterate over them by the target vertices
+        igraph_vector_init(&neis, 0);
+
+        for (i = 0; i < num_vs; i++) {
+            igraph_incident(g, &neis, i, IGRAPH_ALL);
+            temp = igraph_vector_size(&neis);
+
+            // TODO: should loop edges be added in both directions?
+            p_head_copy = p_head;
+            for (j = 0; j < temp; j++) {
+                if (weights != 0) {
+                    if (VECTOR(*weights)[(long int)VECTOR(neis)[j]] <= 0) {
+                        // Ignore
+                        num_ignored_es++;
+                        continue;
+                    }
+
+                    *p_weight = VECTOR(*weights)[(long int)VECTOR(neis)[j]];
+                    ++p_weight;
+                }
+
+                *p_head = IGRAPH_OTHER(g, VECTOR(neis)[j], i);
+                if (i == *p_head) {
+                    num_self_es++;
+                }
+                ++p_head;
+            }
+            tails[i] = p_head - p_head_copy;
+        }
+
+        igraph_vector_destroy(&neis);
+    }
+
+    // Decrease num_es by the number of ignored edges
+    num_es -= num_ignored_es;
+
+    // Finalize the tails vector
+    for (i = 0, sum = 0; i < num_vs; ++i) {
+        temp = sum;
+        sum += tails[i];
+        tails[i] = temp;
+    }
+
+    // Normalize the weights
+    normalize_weights();
+
+    // Debug
+    /*
+    printf("Heads:");
+    for (i = 0; i < num_es; ++i) {
+        printf(" %d", heads[i]);
+    }
+    printf("\n");
+    printf("Tails:");
+    for (i = 0; i < num_vs; ++i) {
+        printf(" %d", tails[i]);
+    }
+    printf("\n");
+    if (vals) {
+        printf("Vals:");
+        for (i = 0; i < num_es; ++i) {
+            printf(" %.4f", vals[i]);
+        }
+        printf("\n");
+    }
+    printf("===========================\n");
+    */
+}
+
+// PRPACK_IGRAPH_SUPPORT 
+#endif 
+
diff --git a/igraph/src/prpack_preprocessed_ge_graph.cpp b/igraph/src/prpack_preprocessed_ge_graph.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_preprocessed_ge_graph.cpp
@@ -0,0 +1,64 @@
+#include "prpack_preprocessed_ge_graph.h"
+#include <algorithm>
+using namespace prpack;
+using namespace std;
+
+void prpack_preprocessed_ge_graph::initialize() {
+    matrix = NULL;
+    d = NULL;
+}
+
+void prpack_preprocessed_ge_graph::initialize_weighted(const prpack_base_graph* bg) {
+    // initialize d
+    fill(d, d + num_vs, 1);
+    // fill in the matrix
+    for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs) {
+        const int start_j = bg->tails[i];
+        const int end_j = (i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es;
+        for (int j = start_j; j < end_j; ++j)
+            d[bg->heads[j]] -= matrix[inum_vs + bg->heads[j]] = bg->vals[j];
+    }
+}
+
+void prpack_preprocessed_ge_graph::initialize_unweighted(const prpack_base_graph* bg) {
+    // fill in the matrix
+    for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs) {
+        const int start_j = bg->tails[i];
+        const int end_j = (i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es;
+        for (int j = start_j; j < end_j; ++j)
+            ++matrix[inum_vs + bg->heads[j]];
+    }
+    // normalize the columns
+    for (int j = 0; j < num_vs; ++j) {
+        double sum = 0;
+        for (int inum_vs = 0; inum_vs < num_vs*num_vs; inum_vs += num_vs)
+            sum += matrix[inum_vs + j];
+        if (sum > 0) {
+            d[j] = 0;
+            const double coeff = 1/sum;
+            for (int inum_vs = 0; inum_vs < num_vs*num_vs; inum_vs += num_vs)
+                matrix[inum_vs + j] *= coeff;
+        } else {
+            d[j] = 1;
+        }
+    }
+}
+
+prpack_preprocessed_ge_graph::prpack_preprocessed_ge_graph(const prpack_base_graph* bg) {
+    initialize();
+    num_vs = bg->num_vs;
+    num_es = bg->num_es;
+    matrix = new double[num_vs*num_vs];
+    d = new double[num_vs];
+    fill(matrix, matrix + num_vs*num_vs, 0);
+    if (bg->vals != NULL)
+        initialize_weighted(bg);
+    else
+        initialize_unweighted(bg);
+}
+
+prpack_preprocessed_ge_graph::~prpack_preprocessed_ge_graph() {
+    delete[] matrix;
+    delete[] d;
+}
+
diff --git a/igraph/src/prpack_preprocessed_gs_graph.cpp b/igraph/src/prpack_preprocessed_gs_graph.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_preprocessed_gs_graph.cpp
@@ -0,0 +1,81 @@
+#include "prpack_preprocessed_gs_graph.h"
+#include <algorithm>
+using namespace prpack;
+using namespace std;
+
+void prpack_preprocessed_gs_graph::initialize() {
+    heads = NULL;
+    tails = NULL;
+    vals = NULL;
+    ii = NULL;
+    d = NULL;
+    num_outlinks = NULL;
+}
+
+void prpack_preprocessed_gs_graph::initialize_weighted(const prpack_base_graph* bg) {
+    vals = new double[num_es];
+    d = new double[num_vs];
+    fill(d, d + num_vs, 1);
+    for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) {
+        tails[tails_i] = heads_i;
+        ii[tails_i] = 0;
+        const int start_j = bg->tails[tails_i];
+        const int end_j = (tails_i + 1 != num_vs) ? bg->tails[tails_i + 1]: bg->num_es;
+        for (int j = start_j; j < end_j; ++j) {
+            if (tails_i == bg->heads[j])
+                ii[tails_i] += bg->vals[j];
+            else {
+                heads[heads_i] = bg->heads[j];
+                vals[heads_i] = bg->vals[j];
+                ++heads_i;
+            }
+            d[bg->heads[j]] -= bg->vals[j];
+        }
+    }
+}
+
+void prpack_preprocessed_gs_graph::initialize_unweighted(const prpack_base_graph* bg) {
+    num_outlinks = new double[num_vs];
+    fill(num_outlinks, num_outlinks + num_vs, 0);
+    for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) {
+        tails[tails_i] = heads_i;
+        ii[tails_i] = 0;
+        const int start_j = bg->tails[tails_i];
+        const int end_j = (tails_i + 1 != num_vs) ? bg->tails[tails_i + 1]: bg->num_es;
+        for (int j = start_j; j < end_j; ++j) {
+            if (tails_i == bg->heads[j])
+                ++ii[tails_i];
+            else
+                heads[heads_i++] = bg->heads[j];
+            ++num_outlinks[bg->heads[j]];
+        }
+    }
+    for (int i = 0; i < num_vs; ++i) {
+        if (num_outlinks[i] == 0)
+            num_outlinks[i] = -1;
+        ii[i] /= num_outlinks[i];
+    }
+}
+
+prpack_preprocessed_gs_graph::prpack_preprocessed_gs_graph(const prpack_base_graph* bg) {
+    initialize();
+    num_vs = bg->num_vs;
+    num_es = bg->num_es - bg->num_self_es;
+    heads = new int[num_es];
+    tails = new int[num_vs];
+    ii = new double[num_vs];
+    if (bg->vals != NULL)
+        initialize_weighted(bg);
+    else
+        initialize_unweighted(bg);
+}
+
+prpack_preprocessed_gs_graph::~prpack_preprocessed_gs_graph() {
+    delete[] heads;
+    delete[] tails;
+    delete[] vals;
+    delete[] ii;
+    delete[] d;
+    delete[] num_outlinks;
+}
+
diff --git a/igraph/src/prpack_preprocessed_scc_graph.cpp b/igraph/src/prpack_preprocessed_scc_graph.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_preprocessed_scc_graph.cpp
@@ -0,0 +1,202 @@
+#include "prpack_preprocessed_scc_graph.h"
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+using namespace prpack;
+using namespace std;
+
+void prpack_preprocessed_scc_graph::initialize() {
+    heads_inside = NULL;
+    tails_inside = NULL;
+    vals_inside = NULL;
+    heads_outside = NULL;
+    tails_outside = NULL;
+    vals_outside = NULL;
+    ii = NULL;
+    d = NULL;
+    num_outlinks = NULL;
+    divisions = NULL;
+    encoding = NULL;
+    decoding = NULL;
+}
+
+void prpack_preprocessed_scc_graph::initialize_weighted(const prpack_base_graph* bg) {
+    vals_inside = new double[num_es];
+    vals_outside = new double[num_es];
+    d = new double[num_vs];
+    fill(d, d + num_vs, 1);
+    for (int comp_i = 0; comp_i < num_comps; ++comp_i) {
+        const int start_i = divisions[comp_i];
+        const int end_i = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs;
+        for (int i = start_i; i < end_i; ++i) {
+            ii[i] = 0;
+            const int decoded = decoding[i];
+            const int start_j = bg->tails[decoded];
+            const int end_j = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es;
+            tails_inside[i] = num_es_inside;
+            tails_outside[i] = num_es_outside;
+            for (int j = start_j; j < end_j; ++j) {
+                const int h = encoding[bg->heads[j]];
+                if (h == i) {
+                    ii[i] += bg->vals[j];
+                } else {
+                    if (start_i <= h && h < end_i) {
+                        heads_inside[num_es_inside] = h;
+                        vals_inside[num_es_inside] = bg->vals[j];
+                        ++num_es_inside;
+                    } else {
+                        heads_outside[num_es_outside] = h;
+                        vals_outside[num_es_outside] = bg->vals[j];
+                        ++num_es_outside;
+                    }
+                }
+                d[h] -= bg->vals[j];
+            }
+        }
+    }
+}
+
+void prpack_preprocessed_scc_graph::initialize_unweighted(const prpack_base_graph* bg) {
+    num_outlinks = new double[num_vs];
+    fill(num_outlinks, num_outlinks + num_vs, 0);
+    for (int comp_i = 0; comp_i < num_comps; ++comp_i) {
+        const int start_i = divisions[comp_i];
+        const int end_i = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs;
+        for (int i = start_i; i < end_i; ++i) {
+            ii[i] = 0;
+            const int decoded = decoding[i];
+            const int start_j = bg->tails[decoded];
+            const int end_j = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es;
+            tails_inside[i] = num_es_inside;
+            tails_outside[i] = num_es_outside;
+            for (int j = start_j; j < end_j; ++j) {
+                const int h = encoding[bg->heads[j]];
+                if (h == i) {
+                    ++ii[i];
+                } else {
+                    if (start_i <= h && h < end_i)
+                        heads_inside[num_es_inside++] = h;
+                    else
+                        heads_outside[num_es_outside++] = h;
+                }
+                ++num_outlinks[h];
+            }
+        }
+    }
+    for (int i = 0; i < num_vs; ++i) {
+        if (num_outlinks[i] == 0)
+            num_outlinks[i] = -1;
+        ii[i] /= num_outlinks[i];
+    }
+}
+
+prpack_preprocessed_scc_graph::prpack_preprocessed_scc_graph(const prpack_base_graph* bg) {
+    initialize();
+    // initialize instance variables
+    num_vs = bg->num_vs;
+    num_es = bg->num_es - bg->num_self_es;
+    // initialize Tarjan's algorithm variables
+    num_comps = 0;
+    int mn = 0;                 // the number of vertices seen so far
+    int sz = 0;                 // size of st
+    int decoding_i = 0;         // size of decoding currently filled in
+    decoding = new int[num_vs];
+    int* scc = new int[num_vs]; // the strongly connected component this vertex is in
+    int* low = new int[num_vs]; // the lowest index this vertex can reach
+    int* num = new int[num_vs]; // the index of this vertex in the dfs traversal
+    int* st = new int[num_vs];  // a stack for the dfs
+    memset(num, -1, num_vs*sizeof(num[0]));
+    memset(scc, -1, num_vs*sizeof(scc[0]));
+    int* cs1 = new int[num_vs]; // call stack variable for dfs
+    int* cs2 = new int[num_vs]; // call stack variable for dfs
+    // run iterative Tarjan's algorithm
+    for (int root = 0; root < num_vs; ++root) {
+        if (num[root] != -1)
+            continue;
+        int csz = 1;
+        cs1[0] = root;
+        cs2[0] = bg->tails[root];
+        // dfs
+        while (csz) {
+            const int p = cs1[csz - 1]; // node we're dfs-ing on
+            int& it = cs2[csz - 1]; // iteration of the for loop
+            if (it == bg->tails[p]) {
+                low[p] = num[p] = mn++;
+                st[sz++] = p;
+            } else {
+                low[p] = min(low[p], low[bg->heads[it - 1]]);
+            }
+            bool done = false;
+            int end_it = (p + 1 != num_vs) ? bg->tails[p + 1] : bg->num_es;
+            for (; it < end_it; ++it) {
+                int h = bg->heads[it];
+                if (scc[h] == -1) {
+                    if (num[h] == -1) {
+                        // dfs(h, p);
+                        cs1[csz] = h;
+                        cs2[csz++] = bg->tails[h];
+                        ++it;
+                        done = true;
+                        break;
+                    }
+                    low[p] = min(low[p], low[h]);
+                }
+            }
+            if (done)
+                continue;
+            // if p is the first explored vertex of a scc
+            if (low[p] == num[p]) {
+                cs1[num_vs - 1 - num_comps] = decoding_i;
+                while (scc[p] != num_comps) {
+                    scc[st[--sz]] = num_comps;
+                    decoding[decoding_i++] = st[sz];
+                }
+                ++num_comps;
+            }
+            --csz;
+        }
+    }
+    // set up other instance variables
+    divisions = new int[num_comps];
+    divisions[0] = 0;
+    for (int i = 1; i < num_comps; ++i)
+        divisions[i] = cs1[num_vs - 1 - i];
+    encoding = num;
+    for (int i = 0; i < num_vs; ++i)
+        encoding[decoding[i]] = i;
+    // fill in inside and outside instance variables
+    ii = new double[num_vs];
+    tails_inside = cs1;
+    heads_inside = new int[num_es];
+    tails_outside = cs2;
+    heads_outside = new int[num_es];
+    num_es_inside = num_es_outside = 0;
+    // continue initialization based off of weightedness
+    if (bg->vals != NULL)
+        initialize_weighted(bg);
+    else
+        initialize_unweighted(bg);
+    // free memory
+    // do not free num <==> encoding
+    // do not free cs1 <==> tails_inside
+    // do not free cs2 <==> tails_outside
+    delete[] scc;
+    delete[] low;
+    delete[] st;
+}
+
+prpack_preprocessed_scc_graph::~prpack_preprocessed_scc_graph() {
+    delete[] heads_inside;
+    delete[] tails_inside;
+    delete[] vals_inside;
+    delete[] heads_outside;
+    delete[] tails_outside;
+    delete[] vals_outside;
+    delete[] ii;
+    delete[] d;
+    delete[] num_outlinks;
+    delete[] divisions;
+    delete[] encoding;
+    delete[] decoding;
+}
+
diff --git a/igraph/src/prpack_preprocessed_schur_graph.cpp b/igraph/src/prpack_preprocessed_schur_graph.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_preprocessed_schur_graph.cpp
@@ -0,0 +1,121 @@
+#include "prpack_preprocessed_schur_graph.h"
+#include <algorithm>
+#include <cstring>
+using namespace prpack;
+using namespace std;
+
+void prpack_preprocessed_schur_graph::initialize() {
+    heads = NULL;
+    tails = NULL;
+    vals = NULL;
+    ii = NULL;
+    d = NULL;
+    num_outlinks = NULL;
+    encoding = NULL;
+    decoding = NULL;
+}
+
+void prpack_preprocessed_schur_graph::initialize_weighted(const prpack_base_graph* bg) {
+    // permute d
+    ii = d;
+    d = new double[num_vs];
+    for (int i = 0; i < num_vs; ++i)
+        d[encoding[i]] = ii[i];
+    // convert bg to head/tail format
+    for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) {
+        ii[tails_i] = 0;
+        tails[tails_i] = heads_i;
+        const int decoded = decoding[tails_i];
+        const int start_i = bg->tails[decoded];
+        const int end_i = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es;
+        for (int i = start_i; i < end_i; ++i) {
+            if (decoded == bg->heads[i])
+                ii[tails_i] += bg->vals[i];
+            else {
+                heads[heads_i] = encoding[bg->heads[i]];
+                vals[heads_i] = bg->vals[i];
+                ++heads_i;
+            }
+        }
+    }
+}
+
+void prpack_preprocessed_schur_graph::initialize_unweighted(const prpack_base_graph* bg) {
+    // permute num_outlinks
+    ii = num_outlinks;
+    num_outlinks = new double[num_vs];
+    for (int i = 0; i < num_vs; ++i)
+        num_outlinks[encoding[i]] = (ii[i] == 0) ? -1 : ii[i];
+    // convert bg to head/tail format
+    for (int tails_i = 0, heads_i = 0; tails_i < num_vs; ++tails_i) {
+        ii[tails_i] = 0;
+        tails[tails_i] = heads_i;
+        const int decoded = decoding[tails_i];
+        const int start_i = bg->tails[decoded];
+        const int end_i = (decoded + 1 != num_vs) ? bg->tails[decoded + 1] : bg->num_es;
+        for (int i = start_i; i < end_i; ++i) {
+            if (decoded == bg->heads[i])
+                ++ii[tails_i];
+            else
+                heads[heads_i++] = encoding[bg->heads[i]];
+        }
+        if (ii[tails_i] > 0)
+            ii[tails_i] /= num_outlinks[tails_i];
+    }
+}
+
+prpack_preprocessed_schur_graph::prpack_preprocessed_schur_graph(const prpack_base_graph* bg) {
+    initialize();
+    // initialize instance variables
+    num_vs = bg->num_vs;
+    num_es = bg->num_es - bg->num_self_es;
+    tails = new int[num_vs];
+    heads = new int[num_es];
+    const bool weighted = bg->vals != NULL;
+    if (weighted) {
+        vals = new double[num_vs];
+        d = new double[num_vs];
+        fill(d, d + num_vs, 1);
+        for (int i = 0; i < bg->num_es; ++i)
+            d[bg->heads[i]] -= bg->vals[i];
+    } else {
+        num_outlinks = new double[num_vs];
+        fill(num_outlinks, num_outlinks + num_vs, 0);
+        for (int i = 0; i < bg->num_es; ++i)
+            ++num_outlinks[bg->heads[i]];
+    }
+    // permute no-inlink vertices to the beginning, and no-outlink vertices to the end
+    encoding = new int[num_vs];
+    decoding = new int[num_vs];
+    num_no_in_vs = num_no_out_vs = 0;
+    for (int i = 0; i < num_vs; ++i) {
+        if (bg->tails[i] == ((i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es)) {
+            decoding[encoding[i] = num_no_in_vs] = i;
+            ++num_no_in_vs;
+        } else if ((weighted) ? (d[i] == 1) : (num_outlinks[i] == 0)) {
+            decoding[encoding[i] = num_vs - 1 - num_no_out_vs] = i;
+            ++num_no_out_vs;
+        }
+    }
+    // permute everything else
+    for (int i = 0, p = num_no_in_vs; i < num_vs; ++i)
+        if (bg->tails[i] < ((i + 1 != num_vs) ? bg->tails[i + 1] : bg->num_es) && ((weighted) ? (d[i] < 1) : (num_outlinks[i] > 0)))
+            decoding[encoding[i] = p++] = i;
+    // continue initialization based off of weightedness
+    if (weighted)
+        initialize_weighted(bg);
+    else
+        initialize_unweighted(bg);
+}
+
+prpack_preprocessed_schur_graph::~prpack_preprocessed_schur_graph() {
+    delete[] heads;
+    delete[] tails;
+    delete[] vals;
+    delete[] ii;
+    delete[] d;
+    delete[] num_outlinks;
+    delete[] encoding;
+    delete[] decoding;
+}
+
diff --git a/igraph/src/prpack_result.cpp b/igraph/src/prpack_result.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_result.cpp
@@ -0,0 +1,12 @@
+#include "prpack_result.h"
+#include <cstdlib>
+using namespace prpack;
+
+prpack_result::prpack_result() {
+    x = NULL;
+}
+
+prpack_result::~prpack_result() {
+    delete[] x;
+}
+
diff --git a/igraph/src/prpack_solver.cpp b/igraph/src/prpack_solver.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_solver.cpp
@@ -0,0 +1,878 @@
+#include "prpack_solver.h"
+#include "prpack_utils.h"
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <algorithm>
+using namespace prpack;
+using namespace std;
+
+void prpack_solver::initialize() {
+    geg = NULL;
+    gsg = NULL;
+    sg = NULL;
+    sccg = NULL;
+	owns_bg = true;
+}
+
+prpack_solver::prpack_solver(const prpack_csc* g) {
+    initialize();
+    TIME(read_time, bg = new prpack_base_graph(g));
+}
+
+prpack_solver::prpack_solver(const prpack_int64_csc* g) {
+    initialize();
+    TIME(read_time, bg = new prpack_base_graph(g));
+}
+
+prpack_solver::prpack_solver(const prpack_csr* g) {
+    initialize();
+    TIME(read_time, bg = new prpack_base_graph(g));
+}
+
+prpack_solver::prpack_solver(const prpack_edge_list* g) {
+    initialize();
+    TIME(read_time, bg = new prpack_base_graph(g));
+}
+
+prpack_solver::prpack_solver(prpack_base_graph* g, bool owns_bg) {
+    initialize();
+	this->owns_bg = owns_bg;
+    TIME(read_time, bg = g);
+}
+
+prpack_solver::prpack_solver(const char* filename, const char* format, const bool weighted) {
+    initialize();
+    TIME(read_time, bg = new prpack_base_graph(filename, format, weighted));
+}
+
+prpack_solver::~prpack_solver() {
+	if (owns_bg) {
+		delete bg;
+	}
+    delete geg;
+    delete gsg;
+    delete sg;
+    delete sccg;
+}
+
+int prpack_solver::get_num_vs() {
+    return bg->num_vs;
+}
+
+prpack_result* prpack_solver::solve(const double alpha, const double tol, const char* method) {
+    return solve(alpha, tol, NULL, NULL, method);
+}
+
+prpack_result* prpack_solver::solve(
+        const double alpha,
+        const double tol,
+        const double* u,
+        const double* v,
+        const char* method) {
+    double preprocess_time = 0;
+    double compute_time = 0;
+    prpack_result* ret = NULL;
+    // decide which method to run
+    string m;
+    if (strcmp(method, "") != 0)
+        m = string(method);
+    else {
+        if (bg->num_vs < 128)
+            m = "ge";
+        else if (sccg != NULL)
+            m = "sccgs";
+        else if (sg != NULL)
+            m = "sg";
+        else
+            m = "sccgs";
+        if (u != v)
+            m += "_uv";
+    }
+    // run the appropriate method
+    if (m == "ge") {
+        if (geg == NULL) {
+            TIME(preprocess_time, geg = new prpack_preprocessed_ge_graph(bg));
+        }
+        TIME(compute_time, ret = solve_via_ge(
+                alpha,
+                tol,
+                geg->num_vs,
+                geg->matrix,
+                u));
+    } else if (m == "ge_uv") {
+        if (geg == NULL) {
+            TIME(preprocess_time, geg = new prpack_preprocessed_ge_graph(bg));
+        }
+        TIME(compute_time, ret = solve_via_ge_uv(
+                alpha,
+                tol,
+                geg->num_vs,
+                geg->matrix,
+                geg->d,
+                u,
+                v));
+    } else if (m == "gs") {
+        if (gsg == NULL) {
+            TIME(preprocess_time, gsg = new prpack_preprocessed_gs_graph(bg));
+        }
+        TIME(compute_time, ret = solve_via_gs(
+                alpha,
+                tol,
+                gsg->num_vs,
+                gsg->num_es,
+                gsg->heads,
+                gsg->tails,
+                gsg->vals,
+                gsg->ii,
+                gsg->d,
+                gsg->num_outlinks,
+                u,
+                v));
+    } else if (m == "gserr") {
+        if (gsg == NULL) {
+            TIME(preprocess_time, gsg = new prpack_preprocessed_gs_graph(bg));
+        }
+        TIME(compute_time, ret = solve_via_gs_err(
+                alpha,
+                tol,
+                gsg->num_vs,
+                gsg->num_es,
+                gsg->heads,
+                gsg->tails,
+                gsg->ii,
+                gsg->num_outlinks,
+                u,
+                v));
+    } else if (m == "sgs") {
+        if (sg == NULL) {
+            TIME(preprocess_time, sg = new prpack_preprocessed_schur_graph(bg));
+        }
+        TIME(compute_time, ret = solve_via_schur_gs(
+                alpha,
+                tol,
+                sg->num_vs,
+                sg->num_no_in_vs,
+                sg->num_no_out_vs,
+                sg->num_es,
+                sg->heads,
+                sg->tails,
+                sg->vals,
+                sg->ii,
+                sg->d,
+                sg->num_outlinks,
+                u,
+                sg->encoding,
+                sg->decoding));
+    } else if (m == "sgs_uv") {
+        if (sg == NULL) {
+            TIME(preprocess_time, sg = new prpack_preprocessed_schur_graph(bg));
+        }
+        TIME(compute_time, ret = solve_via_schur_gs_uv(
+                alpha,
+                tol,
+                sg->num_vs,
+                sg->num_no_in_vs,
+                sg->num_no_out_vs,
+                sg->num_es,
+                sg->heads,
+                sg->tails,
+                sg->vals,
+                sg->ii,
+                sg->d,
+                sg->num_outlinks,
+                u,
+                v,
+                sg->encoding,
+                sg->decoding));
+    } else if (m == "sccgs") {
+        if (sccg == NULL) {
+            TIME(preprocess_time, sccg = new prpack_preprocessed_scc_graph(bg));
+        }
+        TIME(compute_time, ret = solve_via_scc_gs(
+                alpha,
+                tol,
+                sccg->num_vs,
+                sccg->num_es_inside,
+                sccg->heads_inside,
+                sccg->tails_inside,
+                sccg->vals_inside,
+                sccg->num_es_outside,
+                sccg->heads_outside,
+                sccg->tails_outside,
+                sccg->vals_outside,
+                sccg->ii,
+                sccg->d,
+                sccg->num_outlinks,
+                u,
+                sccg->num_comps,
+                sccg->divisions,
+                sccg->encoding,
+                sccg->decoding));
+    } else if (m == "sccgs_uv") {
+        if (sccg == NULL) {
+            TIME(preprocess_time, sccg = new prpack_preprocessed_scc_graph(bg));
+        }
+        TIME(compute_time, ret = solve_via_scc_gs_uv(
+                alpha,
+                tol,
+                sccg->num_vs,
+                sccg->num_es_inside,
+                sccg->heads_inside,
+                sccg->tails_inside,
+                sccg->vals_inside,
+                sccg->num_es_outside,
+                sccg->heads_outside,
+                sccg->tails_outside,
+                sccg->vals_outside,
+                sccg->ii,
+                sccg->d,
+                sccg->num_outlinks,
+                u,
+                v,
+                sccg->num_comps,
+                sccg->divisions,
+                sccg->encoding,
+                sccg->decoding));
+    } else {
+        // TODO: throw exception
+    }
+    ret->method = m.c_str();
+    ret->read_time = read_time;
+    ret->preprocess_time = preprocess_time;
+    ret->compute_time = compute_time;
+    ret->num_vs = bg->num_vs;
+    ret->num_es = bg->num_es;
+    return ret;
+}
+
+// VARIOUS SOLVING METHODS ////////////////////////////////////////////////////////////////////////
+
+prpack_result* prpack_solver::solve_via_ge(
+        const double alpha,
+        const double tol,
+        const int num_vs,
+        const double* matrix,
+        const double* uv) {
+    prpack_result* ret = new prpack_result();
+    // initialize uv values
+    const double uv_const = 1.0/num_vs;
+    const int uv_exists = (uv) ? 1 : 0;
+    uv = (uv) ? uv : &uv_const;
+    // create matrix A
+    double* A = new double[num_vs*num_vs];
+    for (int i = 0; i < num_vs*num_vs; ++i)
+        A[i] = -alpha*matrix[i];
+    for (int i = 0; i < num_vs*num_vs; i += num_vs + 1)
+        ++A[i];
+    // create vector b
+    double* b = new double[num_vs];
+    for (int i = 0; i < num_vs; ++i)
+        b[i] = uv[uv_exists*i];
+    // solve and normalize
+    ge(num_vs, A, b);
+    normalize(num_vs, b);
+    // clean up and return
+    delete[] A;
+    ret->num_es_touched = -1;
+    ret->x = b;
+    return ret;
+}
+
+prpack_result* prpack_solver::solve_via_ge_uv(
+        const double alpha,
+        const double tol,
+        const int num_vs,
+        const double* matrix,
+        const double* d,
+        const double* u,
+        const double* v) {
+    prpack_result* ret = new prpack_result();
+    // initialize u and v values
+    const double u_const = 1.0/num_vs;
+    const double v_const = 1.0/num_vs;
+    const int u_exists = (u) ? 1 : 0;
+    const int v_exists = (v) ? 1 : 0;
+    u = (u) ? u : &u_const;
+    v = (v) ? v : &v_const;
+    // create matrix A
+    double* A = new double[num_vs*num_vs];
+    for (int i = 0; i < num_vs*num_vs; ++i)
+        A[i] = -alpha*matrix[i];
+    for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs)
+        for (int j = 0; j < num_vs; ++j)
+            A[inum_vs + j] -= alpha*u[u_exists*i]*d[j];
+    for (int i = 0; i < num_vs*num_vs; i += num_vs + 1)
+        ++A[i];
+    // create vector b
+    double* b = new double[num_vs];
+    for (int i = 0; i < num_vs; ++i)
+        b[i] = (1 - alpha)*v[v_exists*i];
+    // solve
+    ge(num_vs, A, b);
+    // clean up and return
+    delete[] A;
+    ret->num_es_touched = -1;
+    ret->x = b;
+    return ret;
+}
+
+// Vanilla Gauss-Seidel.
+prpack_result* prpack_solver::solve_via_gs(
+        const double alpha,
+        const double tol,
+        const int num_vs,
+        const int num_es,
+        const int* heads,
+        const int* tails,
+        const double* vals,
+        const double* ii,
+        const double* d,
+        const double* num_outlinks,
+        const double* u,
+        const double* v) {
+    prpack_result* ret = new prpack_result();
+    const bool weighted = vals != NULL;
+    // initialize u and v values
+    const double u_const = 1.0/num_vs;
+    const double v_const = 1.0/num_vs;
+    const int u_exists = (u) ? 1 : 0;
+    const int v_exists = (v) ? 1 : 0;
+    u = (u) ? u : &u_const;
+    v = (v) ? v : &v_const;
+    // initialize the eigenvector (and use personalization vector)
+    double* x = new double[num_vs];
+    for (int i = 0; i < num_vs; ++i)
+        x[i] = 0;
+    // initialize delta
+    double delta = 0;
+    // run Gauss-Seidel
+    ret->num_es_touched = 0;
+    double err = 1, c = 0;
+    do {
+        if (weighted) {
+            for (int i = 0; i < num_vs; ++i) {
+                double new_val = 0;
+                const int start_j = tails[i];
+                const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
+                for (int j = start_j; j < end_j; ++j)
+                    // TODO: might want to use compensation summation for large: end_j - start_j
+                    new_val += x[heads[j]]*vals[j];
+                new_val = alpha*new_val + (1 - alpha)*v[v_exists*i];
+                delta -= alpha*x[i]*d[i];
+                new_val += delta*u[u_exists*i];
+                new_val /= 1 - alpha*(d[i]*u[u_exists*i] + (1 - d[i])*ii[i]);
+                delta += alpha*new_val*d[i];
+                COMPENSATED_SUM(err, x[i] - new_val, c);
+                x[i] = new_val;
+            }
+        } else {
+            for (int i = 0; i < num_vs; ++i) {
+                const double old_val = x[i]*num_outlinks[i];
+                double new_val = 0;
+                const int start_j = tails[i];
+                const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
+                for (int j = start_j; j < end_j; ++j)
+                    // TODO: might want to use compensation summation for large: end_j - start_j
+                    new_val += x[heads[j]];
+                new_val = alpha*new_val + (1 - alpha)*v[v_exists*i];
+                if (num_outlinks[i] < 0) {
+                    delta -= alpha*old_val;
+                    new_val += delta*u[u_exists*i];
+                    new_val /= 1 - alpha*u[u_exists*i];
+                    delta += alpha*new_val;
+                } else {
+                    new_val += delta*u[u_exists*i];
+                    new_val /= 1 - alpha*ii[i];
+                }
+                COMPENSATED_SUM(err, old_val - new_val, c);
+                x[i] = new_val/num_outlinks[i];
+            }
+        }
+        // update iteration index
+        ret->num_es_touched += num_es;
+    } while (err >= tol);
+    // undo num_outlinks transformation
+    if (!weighted)
+        for (int i = 0; i < num_vs; ++i)
+            x[i] *= num_outlinks[i];
+    // return results
+    ret->x = x;
+    return ret;
+}
+
+// Implement a gauss-seidel-like process with a strict error bound
+// we return a solution with 1-norm error less than tol.
+prpack_result* prpack_solver::solve_via_gs_err(
+        const double alpha,
+        const double tol,
+        const int num_vs,
+        const int num_es,
+        const int* heads,
+        const int* tails,
+        const double* ii,
+        const double* num_outlinks,
+        const double* u,
+        const double* v) {
+    prpack_result* ret = new prpack_result();
+    // initialize u and v values
+    const double u_const = 1.0/num_vs;
+    const double v_const = 1.0/num_vs;
+    const int u_exists = (u) ? 1 : 0;
+    const int v_exists = (v) ? 1 : 0;
+    u = (u) ? u : &u_const;
+    v = (v) ? v : &v_const;
+    // Note to Dave, we can't rescale v because we could be running this
+    // same routine from multiple threads.
+    // initialize the eigenvector (and use personalization vector)
+    double* x = new double[num_vs];
+    for (int i = 0; i < num_vs; ++i) {
+        x[i] = 0.;
+    }
+    // initialize delta
+    double delta = 0.;
+    // run Gauss-Seidel, note that we store x/deg[i] throughout this 
+    // iteration.
+    int64_t maxedges = (int64_t)((double)num_es*std::min(
+                            log(tol)/log(alpha),
+                            (double)PRPACK_SOLVER_MAX_ITERS));
+    ret->num_es_touched = 0;
+    double err=1., c = 0.;
+    do {
+        // iterate through vertices
+        for (int i = 0; i < num_vs; ++i) {
+            double old_val = x[i]*num_outlinks[i]; // adjust back to the "true" value.
+            double new_val = 0.;
+            int start_j = tails[i], end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
+            for (int j = start_j; j < end_j; ++j) {
+                // TODO: might want to use compensation summation for large: end_j - start_j
+                new_val += x[heads[j]];
+            }
+            new_val = alpha*new_val + alpha*ii[i]*old_val + (1.0-alpha)*v[v_exists*i];
+            new_val += delta*u[u_exists*i]; // add the dangling node adjustment
+            if (num_outlinks[i] < 0) {
+                delta += alpha*(new_val - old_val);
+            } 
+            // note that new_val > old_val, but the fabs is just for 
+            COMPENSATED_SUM(err, -(new_val - old_val), c);
+            x[i] = new_val/num_outlinks[i];
+        }
+        // update iteration index
+        ret->num_es_touched += num_es;
+    } while (err >= tol && ret->num_es_touched < maxedges);
+    if (err >= tol) {
+        ret->converged = 0;
+    } else {
+        ret->converged = 1;
+    }
+    // undo num_outlinks transformation
+    for (int i = 0; i < num_vs; ++i)
+        x[i] *= num_outlinks[i];
+    // return results
+    ret->x = x;
+    return ret;
+}
+
+// Gauss-Seidel using the Schur complement to separate dangling nodes.
+prpack_result* prpack_solver::solve_via_schur_gs(
+        const double alpha,
+        const double tol,
+        const int num_vs,
+        const int num_no_in_vs,
+        const int num_no_out_vs,
+        const int num_es,
+        const int* heads,
+        const int* tails,
+        const double* vals,
+        const double* ii,
+        const double* d,
+        const double* num_outlinks,
+        const double* uv,
+        const int* encoding,
+        const int* decoding,
+        const bool should_normalize) {
+    prpack_result* ret = new prpack_result();
+    const bool weighted = vals != NULL;
+    // initialize uv values
+    const double uv_const = 1.0/num_vs;
+    const int uv_exists = (uv) ? 1 : 0;
+    uv = (uv) ? prpack_utils::permute(num_vs, uv, encoding) : &uv_const;
+    // initialize the eigenvector (and use personalization vector)
+    double* x = new double[num_vs];
+    for (int i = 0; i < num_vs - num_no_out_vs; ++i)
+        x[i] = uv[uv_exists*i]/(1 - alpha*ii[i])/((weighted) ? 1 : num_outlinks[i]);
+    // run Gauss-Seidel for the top left part of (I - alpha*P)*x = uv
+    ret->num_es_touched = 0;
+    double err, c;
+    do {
+        // iterate through vertices
+        int num_es_touched = 0;
+        err = c = 0;
+        #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64)
+        for (int i = num_no_in_vs; i < num_vs - num_no_out_vs; ++i) {
+            double new_val = 0;
+            const int start_j = tails[i];
+            const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
+            if (weighted) {
+                for (int j = start_j; j < end_j; ++j)
+                    // TODO: might want to use compensation summation for large: end_j - start_j
+                    new_val += x[heads[j]]*vals[j];
+                COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c);
+                new_val = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]);
+                x[i] = new_val;
+            } else {
+                for (int j = start_j; j < end_j; ++j)
+                    // TODO: might want to use compensation summation for large: end_j - start_j
+                    new_val += x[heads[j]];
+                COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c);
+                new_val = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]);
+                x[i] = new_val/num_outlinks[i];
+            }
+            num_es_touched += end_j - start_j;
+        }
+        // update iteration index
+        ret->num_es_touched += num_es_touched;
+    } while (err/(1 - alpha) >= tol);
+    // solve for the dangling nodes
+    int num_es_touched = 0;
+    #pragma omp parallel for reduction(+:num_es_touched) schedule(dynamic, 64)
+    for (int i = num_vs - num_no_out_vs; i < num_vs; ++i) {
+        x[i] = 0;
+        const int start_j = tails[i];
+        const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
+        for (int j = start_j; j < end_j; ++j)
+            x[i] += x[heads[j]]*((weighted) ? vals[j] : 1);
+        x[i] = (alpha*x[i] + uv[uv_exists*i])/(1 - alpha*ii[i]);
+        num_es_touched += end_j - start_j;
+    }
+    ret->num_es_touched += num_es_touched;
+    // undo num_outlinks transformation
+    if (!weighted)
+        for (int i = 0; i < num_vs - num_no_out_vs; ++i)
+            x[i] *= num_outlinks[i];
+    // normalize x to get the solution for: (I - alpha*P - alpha*u*d')*x = (1 - alpha)*v
+    if (should_normalize)
+        normalize(num_vs, x);
+    // return results
+    ret->x = prpack_utils::permute(num_vs, x, decoding);
+    delete[] x;
+    if (uv_exists)
+        delete[] uv;
+    return ret;
+}
+
+prpack_result* prpack_solver::solve_via_schur_gs_uv(
+        const double alpha,
+        const double tol,
+        const int num_vs,
+        const int num_no_in_vs,
+        const int num_no_out_vs,
+        const int num_es,
+        const int* heads,
+        const int* tails,
+        const double* vals,
+        const double* ii,
+        const double* d,
+        const double* num_outlinks,
+        const double* u,
+        const double* v,
+        const int* encoding,
+        const int* decoding) {
+    // solve uv = u
+    prpack_result* ret_u = solve_via_schur_gs(
+            alpha,
+            tol,
+            num_vs,
+            num_no_in_vs,
+            num_no_out_vs,
+            num_es,
+            heads,
+            tails,
+            vals,
+            ii,
+            d,
+            num_outlinks,
+            u,
+            encoding,
+            decoding,
+            false);
+    // solve uv = v
+    prpack_result* ret_v = solve_via_schur_gs(
+            alpha,
+            tol,
+            num_vs,
+            num_no_in_vs,
+            num_no_out_vs,
+            num_es,
+            heads,
+            tails,
+            vals,
+            ii,
+            d,
+            num_outlinks,
+            v,
+            encoding,
+            decoding,
+            false);
+    // combine the u and v cases
+    return combine_uv(num_vs, d, num_outlinks, encoding, alpha, ret_u, ret_v);
+}
+
+/** Gauss-Seidel using strongly connected components.
+ * Notes:
+ *   If not weighted, then we store x[i] = "x[i]/outdegree" to 
+ *   avoid additional arithmetic.  We don't do this for the weighted
+ *   case because the adjustment may not be constant.
+ */
+prpack_result* prpack_solver::solve_via_scc_gs(
+        const double alpha,
+        const double tol,
+        const int num_vs,
+        const int num_es_inside,
+        const int* heads_inside,
+        const int* tails_inside,
+        const double* vals_inside,
+        const int num_es_outside,
+        const int* heads_outside,
+        const int* tails_outside,
+        const double* vals_outside,
+        const double* ii,
+        const double* d,
+        const double* num_outlinks,
+        const double* uv,
+        const int num_comps,
+        const int* divisions,
+        const int* encoding,
+        const int* decoding,
+        const bool should_normalize) {
+    prpack_result* ret = new prpack_result();
+    const bool weighted = vals_inside != NULL;
+    // initialize uv values
+    const double uv_const = 1.0/num_vs;
+    const int uv_exists = (uv) ? 1 : 0;
+    uv = (uv) ? prpack_utils::permute(num_vs, uv, encoding) : &uv_const;
+    // CHECK initialize the solution with one iteration of GS from x=0.
+    double* x = new double[num_vs];
+    for (int i = 0; i < num_vs; ++i)
+        x[i] = uv[uv_exists*i]/(1 - alpha*ii[i])/((weighted) ? 1 : num_outlinks[i]);
+    // create x_outside
+    double* x_outside = new double[num_vs];
+    // run Gauss-Seidel for (I - alpha*P)*x = uv
+    ret->num_es_touched = 0;
+    for (int comp_i = 0; comp_i < num_comps; ++comp_i) {
+        const int start_comp = divisions[comp_i];
+        const int end_comp = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs;
+        const bool parallelize = end_comp - start_comp > 512;
+        // initialize relevant x_outside values
+        for (int i = start_comp; i < end_comp; ++i) {
+            x_outside[i] = 0;
+            const int start_j = tails_outside[i];
+            const int end_j = (i + 1 != num_vs) ? tails_outside[i + 1] : num_es_outside;
+            for (int j = start_j; j < end_j; ++j)
+                x_outside[i] += x[heads_outside[j]]*((weighted) ? vals_outside[j] : 1.);
+            ret->num_es_touched += end_j - start_j;
+        }
+        double err, c;
+        do {
+            int num_es_touched = 0;
+            err = c = 0;
+            if (parallelize) {
+                // iterate through vertices
+                #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64)
+                for (int i = start_comp; i < end_comp; ++i) {
+                    double new_val = x_outside[i];
+                    const int start_j = tails_inside[i];
+                    const int end_j = (i + 1 != num_vs) ? tails_inside[i + 1] : num_es_inside;
+                    if (weighted) {
+                        for (int j = start_j; j < end_j; ++j) {
+                            // TODO: might want to use compensation summation for large: end_j - start_j
+                            new_val += x[heads_inside[j]]*vals_inside[j];
+                        }
+                        COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c);
+                        x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]);
+                    } else {
+                        for (int j = start_j; j < end_j; ++j) {
+                            // TODO: might want to use compensation summation for large: end_j - start_j
+                            new_val += x[heads_inside[j]];
+                        }
+                        COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c);
+                        x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i])/num_outlinks[i];
+                    }
+                    num_es_touched += end_j - start_j;
+                }
+            } else {
+                for (int i = start_comp; i < end_comp; ++i) {
+                    double new_val = x_outside[i];
+                    const int start_j = tails_inside[i];
+                    const int end_j = (i + 1 != num_vs) ? tails_inside[i + 1] : num_es_inside;
+                    if (weighted) {
+                        for (int j = start_j; j < end_j; ++j) {
+                            // TODO: might want to use compensation summation for large: end_j - start_j
+                            new_val += x[heads_inside[j]]*vals_inside[j];
+                        }
+                        COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c);
+                        x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]);
+                    } else {
+                        for (int j = start_j; j < end_j; ++j) {
+                            // TODO: might want to use compensation summation for large: end_j - start_j
+                            new_val += x[heads_inside[j]];
+                        }
+                        COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c);
+                        x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i])/num_outlinks[i];
+                    }
+                    num_es_touched += end_j - start_j;
+                }
+            }
+            // update iteration index
+            ret->num_es_touched += num_es_touched;
+        } while (err/(1 - alpha) >= tol*(end_comp - start_comp)/num_vs);
+    }
+    // undo num_outlinks transformation
+    if (!weighted)
+        for (int i = 0; i < num_vs; ++i)
+            x[i] *= num_outlinks[i];
+    // normalize x to get the solution for: (I - alpha*P - alpha*u*d')*x = (1 - alpha)*v
+    if (should_normalize)
+        normalize(num_vs, x);
+    // return results
+    ret->x = prpack_utils::permute(num_vs, x, decoding);
+    delete[] x;
+    delete[] x_outside;
+    if (uv_exists)
+        delete[] uv;
+    return ret;
+}
+
+prpack_result* prpack_solver::solve_via_scc_gs_uv(
+        const double alpha,
+        const double tol,
+        const int num_vs,
+        const int num_es_inside,
+        const int* heads_inside,
+        const int* tails_inside,
+        const double* vals_inside,
+        const int num_es_outside,
+        const int* heads_outside,
+        const int* tails_outside,
+        const double* vals_outside,
+        const double* ii,
+        const double* d,
+        const double* num_outlinks,
+        const double* u,
+        const double* v,
+        const int num_comps,
+        const int* divisions,
+        const int* encoding,
+        const int* decoding) {
+    // solve uv = u
+    prpack_result* ret_u = solve_via_scc_gs(
+            alpha,
+            tol,
+            num_vs,
+            num_es_inside,
+            heads_inside,
+            tails_inside,
+            vals_inside,
+            num_es_outside,
+            heads_outside,
+            tails_outside,
+            vals_outside,
+            ii,
+            d,
+            num_outlinks,
+            u,
+            num_comps,
+            divisions,
+            encoding,
+            decoding,
+            false);
+    // solve uv = v
+    prpack_result* ret_v = solve_via_scc_gs(
+            alpha,
+            tol,
+            num_vs,
+            num_es_inside,
+            heads_inside,
+            tails_inside,
+            vals_inside,
+            num_es_outside,
+            heads_outside,
+            tails_outside,
+            vals_outside,
+            ii,
+            d,
+            num_outlinks,
+            v,
+            num_comps,
+            divisions,
+            encoding,
+            decoding,
+            false);
+    // combine u and v
+    return combine_uv(num_vs, d, num_outlinks, encoding, alpha, ret_u, ret_v);
+}
+
+// VARIOUS HELPER METHODS /////////////////////////////////////////////////////////////////////////
+
+// Run Gaussian-Elimination (note: this changes A and returns the solution in b)
+void prpack_solver::ge(const int sz, double* A, double* b) {
+    // put into triangular form
+    for (int i = 0, isz = 0; i < sz; ++i, isz += sz)
+        for (int k = 0, ksz = 0; k < i; ++k, ksz += sz)
+            if (A[isz + k] != 0) {
+                const double coeff = A[isz + k]/A[ksz + k];
+                A[isz + k] = 0;
+                for (int j = k + 1; j < sz; ++j)
+                    A[isz + j] -= coeff*A[ksz + j];
+                b[i] -= coeff*b[k];
+            }
+    // backwards substitution
+    for (int i = sz - 1, isz = (sz - 1)*sz; i >= 0; --i, isz -= sz) {
+        for (int j = i + 1; j < sz; ++j)
+            b[i] -= A[isz + j]*b[j];
+        b[i] /= A[isz + i];
+    }
+}
+
+// Normalize a vector to sum to 1.
+void prpack_solver::normalize(const int length, double* x) {
+    double norm = 0, c = 0;
+    for (int i = 0; i < length; ++i) {
+        COMPENSATED_SUM(norm, x[i], c);
+    }
+    norm = 1/norm;
+    for (int i = 0; i < length; ++i)
+        x[i] *= norm;
+}
+
+// Combine u and v results.
+prpack_result* prpack_solver::combine_uv(
+        const int num_vs,
+        const double* d,
+        const double* num_outlinks,
+        const int* encoding,
+        const double alpha,
+        const prpack_result* ret_u,
+        const prpack_result* ret_v) {
+    prpack_result* ret = new prpack_result();
+    const bool weighted = d != NULL;
+    double delta_u = 0;
+    double delta_v = 0;
+    for (int i = 0; i < num_vs; ++i) {
+        if ((weighted) ? (d[encoding[i]] == 1) : (num_outlinks[encoding[i]] < 0)) {
+            delta_u += ret_u->x[i];
+            delta_v += ret_v->x[i];
+        }
+    }
+    const double s = ((1 - alpha)*alpha*delta_v)/(1 - alpha*delta_u);
+    const double t = 1 - alpha;
+    ret->x = new double[num_vs];
+    for (int i = 0; i < num_vs; ++i)
+        ret->x[i] = s*ret_u->x[i] + t*ret_v->x[i];
+    ret->num_es_touched = ret_u->num_es_touched + ret_v->num_es_touched;
+    // clean up and return
+    delete ret_u;
+    delete ret_v;
+    return ret;
+}
+
diff --git a/igraph/src/prpack_utils.cpp b/igraph/src/prpack_utils.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/prpack_utils.cpp
@@ -0,0 +1,60 @@
+/**
+ * @file prpack_utils.cpp
+ * An assortment of utility functions for reporting errors, checking time,
+ * and working with vectors.
+ */
+
+#include <stdlib.h>
+#include "prpack_utils.h"
+#include <cassert>
+#include <iostream>
+#include <string>
+using namespace prpack;
+using namespace std;
+
+#ifdef PRPACK_IGRAPH_SUPPORT
+#include "igraph_error.h"
+#endif
+
+#if defined(_WIN32) || defined(_WIN64)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+double prpack_utils::get_time() {
+    LARGE_INTEGER t, freq;
+    QueryPerformanceCounter(&t);
+    QueryPerformanceFrequency(&freq);
+    return double(t.QuadPart)/double(freq.QuadPart);
+}
+#else
+#include <sys/types.h>
+#include <sys/time.h>
+double prpack_utils::get_time() {
+    struct timeval t;
+    gettimeofday(&t, 0);
+    return (t.tv_sec*1.0 + t.tv_usec/1000000.0);
+}
+#endif
+
+// Fails and outputs 'msg' if 'condition' is false.
+void prpack_utils::validate(const bool condition, const string& msg) {
+    if (!condition) {
+#ifdef PRPACK_IGRAPH_SUPPORT
+        igraph_error("Internal error in PRPACK", __FILE__, __LINE__, 
+	             IGRAPH_EINTERNAL);
+#else
+        cerr << msg << endl;
+        exit(-1);
+#endif
+    }
+}
+
+// Permute a vector.
+double* prpack_utils::permute(const int length, const double* a, const int* coding) {
+    double* ret = new double[length];
+    for (int i = 0; i < length; ++i)
+        ret[coding[i]] = a[i];
+    return ret;
+}
+
diff --git a/igraph/src/qsort.c b/igraph/src/qsort.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/qsort.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *  This product includes software developed by the University of
+ *  California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef _MSC_VER
+    /* MSVC does not have inline when compiling C source files */
+    #define inline __inline
+    #define __unused
+#endif
+
+#ifndef __unused
+    #define __unused    __attribute__ ((unused))
+#endif
+
+#if defined(LIBC_SCCS) && !defined(lint)
+    static char sccsid[] = "@(#)qsort.c	8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+/*#include <sys/cdefs.h> */
+
+#include <stdlib.h>
+
+#ifdef I_AM_QSORT_R
+    typedef int      cmp_t(void *, const void *, const void *);
+#else
+    typedef int      cmp_t(const void *, const void *);
+#endif
+static inline char  *med3(char *, char *, char *, cmp_t *, void *);
+static inline void   swapfunc(char *, char *, int, int);
+
+#define igraph_min(a, b)    (a) < (b) ? a : b
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define swapcode(TYPE, parmi, parmj, n) {       \
+        long i = (n) / sizeof (TYPE);           \
+        TYPE *pi = (TYPE *) (parmi);        \
+        TYPE *pj = (TYPE *) (parmj);        \
+        do {                        \
+            TYPE    t = *pi;        \
+            *pi++ = *pj;                \
+            *pj++ = t;              \
+        } while (--i > 0);              \
+    }
+
+#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+                                   es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static inline void
+swapfunc(a, b, n, swaptype)
+char *a, *b;
+int n, swaptype;
+{
+    if (swaptype <= 1)
+        swapcode(long, a, b, n)
+        else
+            swapcode(char, a, b, n)
+        }
+
+#define swap(a, b)                  \
+    if (swaptype == 0) {                \
+        long t = *(long *)(a);          \
+        *(long *)(a) = *(long *)(b);        \
+        *(long *)(b) = t;           \
+    } else                      \
+        swapfunc(a, b, es, swaptype)
+
+#define vecswap(a, b, n)    if ((n) > 0) swapfunc(a, b, n, swaptype)
+
+#ifdef I_AM_QSORT_R
+    #define CMP(t, x, y) (cmp((t), (x), (y)))
+#else
+    #define CMP(t, x, y) (cmp((x), (y)))
+#endif
+
+static inline char *
+med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk
+#ifndef I_AM_QSORT_R
+    __unused
+#endif
+    ) {
+    return CMP(thunk, a, b) < 0 ?
+           (CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a ))
+           : (CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c ));
+}
+
+#ifdef I_AM_QSORT_R
+    void
+    igraph_qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp)
+#else
+    #define thunk NULL
+    void
+    igraph_qsort(void *a, size_t n, size_t es, cmp_t *cmp)
+#endif
+{
+    char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+    int d, r, swaptype, swap_cnt;
+
+loop:   SWAPINIT(a, es);
+    swap_cnt = 0;
+    if (n < 7) {
+        for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
+            for (pl = pm;
+                 pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
+                 pl -= es) {
+                swap(pl, pl - es);
+            }
+        return;
+    }
+    pm = (char *)a + (n / 2) * es;
+    if (n > 7) {
+        pl = a;
+        pn = (char *)a + (n - 1) * es;
+        if (n > 40) {
+            d = (n / 8) * es;
+            pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk);
+            pm = med3(pm - d, pm, pm + d, cmp, thunk);
+            pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk);
+        }
+        pm = med3(pl, pm, pn, cmp, thunk);
+    }
+    swap(a, pm);
+    pa = pb = (char *)a + es;
+
+    pc = pd = (char *)a + (n - 1) * es;
+    for (;;) {
+        while (pb <= pc && (r = CMP(thunk, pb, a)) <= 0) {
+            if (r == 0) {
+                swap_cnt = 1;
+                swap(pa, pb);
+                pa += es;
+            }
+            pb += es;
+        }
+        while (pb <= pc && (r = CMP(thunk, pc, a)) >= 0) {
+            if (r == 0) {
+                swap_cnt = 1;
+                swap(pc, pd);
+                pd -= es;
+            }
+            pc -= es;
+        }
+        if (pb > pc) {
+            break;
+        }
+        swap(pb, pc);
+        swap_cnt = 1;
+        pb += es;
+        pc -= es;
+    }
+    if (swap_cnt == 0) {  /* Switch to insertion sort */
+        for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
+            for (pl = pm;
+                 pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
+                 pl -= es) {
+                swap(pl, pl - es);
+            }
+        return;
+    }
+
+    pn = (char *)a + n * es;
+    r = igraph_min(pa - (char *)a, pb - pa);
+    vecswap(a, pb - r, r);
+    r = igraph_min((size_t)(pd - pc), (size_t)(pn - pd - es));
+    vecswap(pb, pn - r, r);
+    if ((size_t)(r = pb - pa) > es)
+#ifdef I_AM_QSORT_R
+        igraph_qsort_r(a, r / es, es, thunk, cmp);
+#else
+        igraph_qsort(a, r / es, es, cmp);
+#endif
+    if ((size_t)(r = pd - pc) > es) {
+        /* Iterate rather than recurse to save stack space */
+        a = pn - r;
+        n = r / es;
+        goto loop;
+    }
+    /*      qsort(pn - r, r / es, es, cmp);*/
+}
+
diff --git a/igraph/src/qsort_r.c b/igraph/src/qsort_r.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/qsort_r.c
@@ -0,0 +1,8 @@
+/*
+ * This file is in the public domain.  Originally written by Garrett
+ * A. Wollman.
+ *
+ * $FreeBSD: src/lib/libc/stdlib/qsort_r.c,v 1.1 2002/09/10 02:04:49 wollman Exp $
+ */
+#define I_AM_QSORT_R
+#include "qsort.c"
diff --git a/igraph/src/r_abs.c b/igraph/src/r_abs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_abs.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double r_abs(x) real *x;
+#else
+double r_abs(real *x)
+#endif
+{
+if(*x >= 0)
+	return(*x);
+return(- *x);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_acos.c b/igraph/src/r_acos.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_acos.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double acos();
+double r_acos(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_acos(real *x)
+#endif
+{
+return( acos(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_asin.c b/igraph/src/r_asin.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_asin.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double asin();
+double r_asin(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_asin(real *x)
+#endif
+{
+return( asin(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_atan.c b/igraph/src/r_atan.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_atan.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double atan();
+double r_atan(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_atan(real *x)
+#endif
+{
+return( atan(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_atn2.c b/igraph/src/r_atn2.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_atn2.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double atan2();
+double r_atn2(x,y) real *x, *y;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_atn2(real *x, real *y)
+#endif
+{
+return( atan2(*x,*y) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_cnjg.c b/igraph/src/r_cnjg.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_cnjg.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+VOID r_cnjg(r, z) f2c_complex *r, *z;
+#else
+VOID r_cnjg(f2c_complex *r, f2c_complex *z)
+#endif
+{
+	real zi = z->i;
+	r->r = z->r;
+	r->i = -zi;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_cos.c b/igraph/src/r_cos.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_cos.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double cos();
+double r_cos(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_cos(real *x)
+#endif
+{
+return( cos(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_cosh.c b/igraph/src/r_cosh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_cosh.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double cosh();
+double r_cosh(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_cosh(real *x)
+#endif
+{
+return( cosh(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_dim.c b/igraph/src/r_dim.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_dim.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double r_dim(a,b) real *a, *b;
+#else
+double r_dim(real *a, real *b)
+#endif
+{
+return( *a > *b ? *a - *b : 0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_exp.c b/igraph/src/r_exp.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_exp.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double exp();
+double r_exp(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_exp(real *x)
+#endif
+{
+return( exp(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_imag.c b/igraph/src/r_imag.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_imag.c
@@ -0,0 +1,16 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double r_imag(z) f2c_complex *z;
+#else
+double r_imag(f2c_complex *z)
+#endif
+{
+return(z->i);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_int.c b/igraph/src/r_int.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_int.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double floor();
+double r_int(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_int(real *x)
+#endif
+{
+return( (*x>0) ? floor(*x) : -floor(- *x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_lg10.c b/igraph/src/r_lg10.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_lg10.c
@@ -0,0 +1,21 @@
+#include "f2c.h"
+
+#define log10e 0.43429448190325182765
+
+#ifdef KR_headers
+double log();
+double r_lg10(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_lg10(real *x)
+#endif
+{
+return( log10e * log(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_log.c b/igraph/src/r_log.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_log.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double log();
+double r_log(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_log(real *x)
+#endif
+{
+return( log(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_mod.c b/igraph/src/r_mod.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_mod.c
@@ -0,0 +1,46 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+#ifdef IEEE_drem
+double drem();
+#else
+double floor();
+#endif
+double r_mod(x,y) real *x, *y;
+#else
+#ifdef IEEE_drem
+double drem(double, double);
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#endif
+double r_mod(real *x, real *y)
+#endif
+{
+#ifdef IEEE_drem
+	double xa, ya, z;
+	if ((ya = *y) < 0.)
+		ya = -ya;
+	z = drem(xa = *x, ya);
+	if (xa > 0) {
+		if (z < 0)
+			z += ya;
+		}
+	else if (z > 0)
+		z -= ya;
+	return z;
+#else
+	double quotient;
+	if( (quotient = (double)*x / *y) >= 0)
+		quotient = floor(quotient);
+	else
+		quotient = -floor(-quotient);
+	return(*x - (*y) * quotient );
+#endif
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_nint.c b/igraph/src/r_nint.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_nint.c
@@ -0,0 +1,20 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double floor();
+double r_nint(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_nint(real *x)
+#endif
+{
+return( (*x)>=0 ?
+	floor(*x + .5) : -floor(.5 - *x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_sign.c b/igraph/src/r_sign.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_sign.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double r_sign(a,b) real *a, *b;
+#else
+double r_sign(real *a, real *b)
+#endif
+{
+double x;
+x = (*a >= 0 ? *a : - *a);
+return( *b >= 0 ? x : -x);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_sin.c b/igraph/src/r_sin.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_sin.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sin();
+double r_sin(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_sin(real *x)
+#endif
+{
+return( sin(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_sinh.c b/igraph/src/r_sinh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_sinh.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sinh();
+double r_sinh(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_sinh(real *x)
+#endif
+{
+return( sinh(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_sqrt.c b/igraph/src/r_sqrt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_sqrt.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sqrt();
+double r_sqrt(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_sqrt(real *x)
+#endif
+{
+return( sqrt(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_tan.c b/igraph/src/r_tan.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_tan.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double tan();
+double r_tan(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_tan(real *x)
+#endif
+{
+return( tan(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/r_tanh.c b/igraph/src/r_tanh.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/r_tanh.c
@@ -0,0 +1,19 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double tanh();
+double r_tanh(x) real *x;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+double r_tanh(real *x)
+#endif
+{
+return( tanh(*x) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/random.c b/igraph/src/random.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/random.c
@@ -0,0 +1,2500 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_random.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <math.h>
+#include <limits.h>
+#include <string.h>
+#include "igraph_math.h"
+#include "igraph_types.h"
+#include "igraph_vector.h"
+#include "igraph_memory.h"
+#include "igraph_matrix.h"
+
+/**
+ * \section about_rngs
+ *
+ * <section>
+ * <title>About random numbers in igraph, use cases</title>
+ *
+ * <para>
+ * Some algorithms in igraph, e.g. the generation of random graphs,
+ * require random number generators (RNGs). Prior to version 0.6
+ * igraph did not have a sophisticated way to deal with random number
+ * generators at the C level, but this has changed. From version 0.6
+ * different and multiple random number generators are supported.
+ * </para>
+ * </section>
+ *
+ */
+
+/**
+ * \section rng_use_cases
+ *
+ * <section><title>Use cases</title>
+ *
+ * <section><title>Normal (default) use</title>
+ * <para>
+ * If the user does not use any of the RNG functions explicitly, but calls
+ * some of the randomized igraph functions, then a default RNG is set
+ * up the first time an igraph function needs random numbers. The
+ * seed of this RNG is the output of the <code>time(0)</code> function
+ * call, using the <code>time</code> function from the standard C
+ * library. This ensures that igraph creates a different random graph,
+ * each time the C program is called.
+ * </para>
+ *
+ * <para>
+ * The created default generator is stored internally and can be
+ * queried with the \ref igraph_rng_default() function.
+ * </para>
+ * </section>
+ *
+ * <section><title>Reproducible simulations</title>
+ * <para>
+ * If reproducible results are needed, then the user should set the
+ * seed of the default random number generator explicitly, using the
+ * \ref igraph_rng_seed() function on the default generator, \ref
+ * igraph_rng_default(). When setting the seed to the same number,
+ * igraph generates exactly the same random graph (or series of random
+ * graphs).
+ * </para>
+ * </section>
+ *
+ * <section><title>Changing the default generator</title>
+ * <para>
+ * By default igraph uses the \ref igraph_rng_default() random number
+ * generator. This can be changed any time by calling \ref
+ * igraph_rng_set_default(), with an already initialized random number
+ * generator. Note that the old (replaced) generator is not
+ * destroyed, so no memory is deallocated.
+ * </para>
+ * </section>
+ *
+ * <section><title>Using multiple generators</title>
+ * <para>
+ * igraph also provides functions to set up multiple random number
+ * generators, using the \ref igraph_rng_init() function, and then
+ * generating random numbers from them, e.g. with \ref igraph_rng_get_integer()
+ * and/or \ref igraph_rng_get_unif() calls.
+ * </para>
+ *
+ * <para>
+ * Note that initializing a new random number generator is
+ * independent of the generator that the igraph functions themselves
+ * use. If you want to replace that, then please use \ref
+ * igraph_rng_set_default().
+ * </para>
+ * </section>
+ *
+ * <section><title>Example</title>
+ * <para>
+ * \example examples/simple/random_seed.c
+ * </para>
+ * </section>
+ *
+ * </section>
+ */
+
+/* ------------------------------------ */
+
+typedef struct {
+    int i, j;
+    long int x[31];
+} igraph_i_rng_glibc2_state_t;
+
+unsigned long int igraph_i_rng_glibc2_get(int *i, int *j, int n,
+        long int *x) {
+    unsigned long int k;
+
+    x[*i] += x[*j];
+    k = (x[*i] >> 1) & 0x7FFFFFFF;
+
+    (*i)++;
+    if (*i == n) {
+        *i = 0;
+    }
+
+    (*j)++ ;
+    if (*j == n) {
+        *j = 0;
+    }
+
+    return k;
+}
+
+unsigned long int igraph_rng_glibc2_get(void *vstate) {
+    igraph_i_rng_glibc2_state_t *state =
+        (igraph_i_rng_glibc2_state_t*) vstate;
+    return igraph_i_rng_glibc2_get(&state->i, &state->j, 31, state->x);
+}
+
+igraph_real_t igraph_rng_glibc2_get_real(void *state) {
+    return igraph_rng_glibc2_get(state) / 2147483648.0;
+}
+
+/* this function is independent of the bit size */
+
+void igraph_i_rng_glibc2_init(long int *x, int n,
+                              unsigned long int s) {
+    int i;
+
+    if (s == 0) {
+        s = 1;
+    }
+
+    x[0] = (long) s;
+    for (i = 1 ; i < n ; i++) {
+        const long int h = s / 127773;
+        const long int t = 16807 * ((long) s - h * 127773) - h * 2836;
+        if (t < 0) {
+            s = (unsigned long) t + 2147483647 ;
+        } else {
+            s = (unsigned long) t ;
+        }
+
+        x[i] = (long int) s ;
+    }
+}
+
+int igraph_rng_glibc2_seed(void *vstate, unsigned long int seed) {
+    igraph_i_rng_glibc2_state_t *state =
+        (igraph_i_rng_glibc2_state_t*) vstate;
+    int i;
+
+    igraph_i_rng_glibc2_init(state->x, 31, seed);
+
+    state->i = 3;
+    state->j = 0;
+
+    for (i = 0; i < 10 * 31; i++) {
+        igraph_rng_glibc2_get(state);
+    }
+
+    return 0;
+}
+
+int igraph_rng_glibc2_init(void **state) {
+    igraph_i_rng_glibc2_state_t *st;
+
+    st = igraph_Calloc(1, igraph_i_rng_glibc2_state_t);
+    if (!st) {
+        IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM);
+    }
+    (*state) = st;
+
+    igraph_rng_glibc2_seed(st, 0);
+
+    return 0;
+}
+
+void igraph_rng_glibc2_destroy(void *vstate) {
+    igraph_i_rng_glibc2_state_t *state =
+        (igraph_i_rng_glibc2_state_t*) vstate;
+    igraph_Free(state);
+}
+
+/**
+ * \var igraph_rngtype_glibc2
+ * \brief The random number generator type introduced in GNU libc 2
+ *
+ * It is a linear feedback shift register generator with a 128-byte
+ * buffer. This generator was the default prior to igraph version 0.6,
+ * at least on systems relying on GNU libc.
+ *
+ * This generator was ported from the GNU Scientific Library.
+ */
+
+const igraph_rng_type_t igraph_rngtype_glibc2 = {
+    /* name= */      "LIBC",
+    /* min=  */      0,
+    /* max=  */      RAND_MAX,
+    /* init= */      igraph_rng_glibc2_init,
+    /* destroy= */   igraph_rng_glibc2_destroy,
+    /* seed= */      igraph_rng_glibc2_seed,
+    /* get= */       igraph_rng_glibc2_get,
+    /* get_real= */  igraph_rng_glibc2_get_real,
+    /* get_norm= */  0,
+    /* get_geom= */  0,
+    /* get_binom= */ 0,
+    /* get_exp= */   0,
+    /* get_gamma= */ 0
+};
+
+/* ------------------------------------ */
+
+typedef struct {
+    unsigned long int x;
+} igraph_i_rng_rand_state_t;
+
+unsigned long int igraph_rng_rand_get(void *vstate) {
+    igraph_i_rng_rand_state_t *state = vstate;
+    state->x = (1103515245 * state->x + 12345) & 0x7fffffffUL;
+    return state->x;
+}
+
+igraph_real_t igraph_rng_rand_get_real(void *vstate) {
+    return igraph_rng_rand_get (vstate) / 2147483648.0 ;
+}
+
+int igraph_rng_rand_seed(void *vstate, unsigned long int seed) {
+    igraph_i_rng_rand_state_t *state = vstate;
+    state->x = seed;
+    return 0;
+}
+
+int igraph_rng_rand_init(void **state) {
+    igraph_i_rng_rand_state_t *st;
+
+    st = igraph_Calloc(1, igraph_i_rng_rand_state_t);
+    if (!st) {
+        IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM);
+    }
+    (*state) = st;
+
+    igraph_rng_rand_seed(st, 0);
+
+    return 0;
+}
+
+void igraph_rng_rand_destroy(void *vstate) {
+    igraph_i_rng_rand_state_t *state =
+        (igraph_i_rng_rand_state_t*) vstate;
+    igraph_Free(state);
+}
+
+/**
+ * \var igraph_rngtype_rand
+ * \brief The old BSD rand/stand random number generator
+ *
+ * The sequence is
+ *     x_{n+1} = (a x_n + c) mod m
+ * with a = 1103515245, c = 12345 and m = 2^31 = 2147483648. The seed
+ * specifies the initial value, x_1.
+ *
+ * The theoretical value of x_{10001} is 1910041713.
+ *
+ *  The period of this generator is 2^31.
+ *
+ * This generator is not very good -- the low bits of successive
+ * numbers are correlated.
+ *
+ * This generator was ported from the GNU Scientific Library.
+ */
+
+const igraph_rng_type_t igraph_rngtype_rand = {
+    /* name= */      "RAND",
+    /* min=  */      0,
+    /* max=  */      0x7fffffffUL,
+    /* init= */      igraph_rng_rand_init,
+    /* destroy= */   igraph_rng_rand_destroy,
+    /* seed= */      igraph_rng_rand_seed,
+    /* get= */       igraph_rng_rand_get,
+    /* get_real= */  igraph_rng_rand_get_real,
+    /* get_norm= */  0,
+    /* get_geom= */  0,
+    /* get_binom= */ 0,
+    /* get_exp= */   0,
+    /* get_gamma= */ 0
+};
+
+/* ------------------------------------ */
+
+#define N 624   /* Period parameters */
+#define M 397
+
+/* most significant w-r bits */
+static const unsigned long UPPER_MASK = 0x80000000UL;
+
+/* least significant r bits */
+static const unsigned long LOWER_MASK = 0x7fffffffUL;
+
+typedef struct {
+    unsigned long mt[N];
+    int mti;
+} igraph_i_rng_mt19937_state_t;
+
+unsigned long int igraph_rng_mt19937_get(void *vstate) {
+    igraph_i_rng_mt19937_state_t *state = vstate;
+
+    unsigned long k ;
+    unsigned long int *const mt = state->mt;
+
+#define MAGIC(y) (((y)&0x1) ? 0x9908b0dfUL : 0)
+
+    if (state->mti >= N) {
+        /* generate N words at one time */
+        int kk;
+
+        for (kk = 0; kk < N - M; kk++) {
+            unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
+            mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y);
+        }
+        for (; kk < N - 1; kk++) {
+            unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
+            mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y);
+        }
+
+        {
+            unsigned long y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
+            mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ MAGIC(y);
+        }
+
+        state->mti = 0;
+    }
+
+#undef MAGIC
+
+    /* Tempering */
+
+    k = mt[state->mti];
+    k ^= (k >> 11);
+    k ^= (k << 7) & 0x9d2c5680UL;
+    k ^= (k << 15) & 0xefc60000UL;
+    k ^= (k >> 18);
+
+    state->mti++;
+
+    return k;
+}
+
+igraph_real_t igraph_rng_mt19937_get_real(void *vstate) {
+    return igraph_rng_mt19937_get (vstate) / 4294967296.0 ;
+}
+
+int igraph_rng_mt19937_seed(void *vstate, unsigned long int seed) {
+    igraph_i_rng_mt19937_state_t *state = vstate;
+    int i;
+
+    memset(state, 0, sizeof(igraph_i_rng_mt19937_state_t));
+
+    if (seed == 0) {
+        seed = 4357;   /* the default seed is 4357 */
+    }
+    state->mt[0] = seed & 0xffffffffUL;
+
+    for (i = 1; i < N; i++) {
+        /* See Knuth's "Art of Computer Programming" Vol. 2, 3rd
+           Ed. p.106 for multiplier. */
+        state->mt[i] =
+            (1812433253UL * (state->mt[i - 1] ^ (state->mt[i - 1] >> 30)) +
+             (unsigned long) i);
+        state->mt[i] &= 0xffffffffUL;
+    }
+
+    state->mti = i;
+    return 0;
+}
+
+int igraph_rng_mt19937_init(void **state) {
+    igraph_i_rng_mt19937_state_t *st;
+
+    st = igraph_Calloc(1, igraph_i_rng_mt19937_state_t);
+    if (!st) {
+        IGRAPH_ERROR("Cannot initialize RNG", IGRAPH_ENOMEM);
+    }
+    (*state) = st;
+
+    igraph_rng_mt19937_seed(st, 0);
+
+    return 0;
+}
+
+void igraph_rng_mt19937_destroy(void *vstate) {
+    igraph_i_rng_mt19937_state_t *state =
+        (igraph_i_rng_mt19937_state_t*) vstate;
+    igraph_Free(state);
+}
+
+/**
+ * \var igraph_rngtype_mt19937
+ * \brief The MT19937 random number generator
+ *
+ * The MT19937 generator of Makoto Matsumoto and Takuji Nishimura is a
+ * variant of the twisted generalized feedback shift-register
+ * algorithm, and is known as the “Mersenne Twister” generator. It has
+ * a Mersenne prime period of 2^19937 - 1 (about 10^6000) and is
+ * equi-distributed in 623 dimensions. It has passed the diehard
+ * statistical tests. It uses 624 words of state per generator and is
+ * comparable in speed to the other generators. The original generator
+ * used a default seed of 4357 and choosing s equal to zero in
+ * gsl_rng_set reproduces this. Later versions switched to 5489 as the
+ * default seed, you can choose this explicitly via igraph_rng_seed
+ * instead if you require it.
+ *
+ * For more information see,
+ * Makoto Matsumoto and Takuji Nishimura, “Mersenne Twister: A
+ * 623-dimensionally equidistributed uniform pseudorandom number
+ * generator”. ACM Transactions on Modeling and Computer Simulation,
+ * Vol. 8, No. 1 (Jan. 1998), Pages 3–30
+ *
+ * The generator igraph_rngtype_mt19937 uses the second revision of the
+ * seeding procedure published by the two authors above in 2002. The
+ * original seeding procedures could cause spurious artifacts for some
+ * seed values.
+ *
+ * This generator was ported from the GNU Scientific Library.
+ */
+
+const igraph_rng_type_t igraph_rngtype_mt19937 = {
+    /* name= */      "MT19937",
+    /* min=  */      0,
+    /* max=  */      0xffffffffUL,
+    /* init= */      igraph_rng_mt19937_init,
+    /* destroy= */   igraph_rng_mt19937_destroy,
+    /* seed= */      igraph_rng_mt19937_seed,
+    /* get= */       igraph_rng_mt19937_get,
+    /* get_real= */  igraph_rng_mt19937_get_real,
+    /* get_norm= */  0,
+    /* get_geom= */  0,
+    /* get_binom= */ 0,
+    /* get_exp= */   0,
+    /* get_gamma= */ 0
+};
+
+#undef N
+#undef M
+
+/* ------------------------------------ */
+
+#ifndef USING_R
+
+igraph_i_rng_mt19937_state_t igraph_i_rng_default_state;
+
+#define addr(a) (&a)
+
+/**
+ * \var igraph_i_rng_default
+ * The default igraph random number generator
+ *
+ * This generator is used by all builtin igraph functions that need to
+ * generate random numbers; e.g. all random graph generators.
+ *
+ * You can use \ref igraph_i_rng_default with \ref igraph_rng_seed()
+ * to set its seed.
+ *
+ * You can change the default generator using the \ref
+ * igraph_rng_set_default() function.
+ */
+
+IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = {
+    addr(igraph_rngtype_mt19937),
+    addr(igraph_i_rng_default_state),
+    /* def= */ 1
+};
+
+#undef addr
+
+/**
+ * \function igraph_rng_set_default
+ * Set the default igraph random number generator
+ *
+ * \param rng The random number generator to use as default from now
+ *    on. Calling \ref igraph_rng_destroy() on it, while it is still
+ *    being used as the default will result craches and/or
+ *    unpredictable results.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_rng_set_default(igraph_rng_t *rng) {
+    igraph_i_rng_default = (*rng);
+}
+
+#endif
+
+
+/* ------------------------------------ */
+
+#ifdef USING_R
+
+double  unif_rand(void);
+double  norm_rand(void);
+double  exp_rand(void);
+double  Rf_rgeom(double);
+double  Rf_rbinom(double, double);
+double  Rf_rgamma(double, double);
+
+int igraph_rng_R_init(void **state) {
+    IGRAPH_ERROR("R RNG error, unsupported function called",
+                 IGRAPH_EINTERNAL);
+    return 0;
+}
+
+void igraph_rng_R_destroy(void *state) {
+    igraph_error("R RNG error, unsupported function called",
+                 __FILE__, __LINE__, IGRAPH_EINTERNAL);
+}
+
+int igraph_rng_R_seed(void *state, unsigned long int seed) {
+    IGRAPH_ERROR("R RNG error, unsupported function called",
+                 IGRAPH_EINTERNAL);
+    return 0;
+}
+
+unsigned long int igraph_rng_R_get(void *state) {
+    return (unsigned long) (unif_rand() * 0x7FFFFFFFUL);
+}
+
+igraph_real_t igraph_rng_R_get_real(void *state) {
+    return unif_rand();
+}
+
+igraph_real_t igraph_rng_R_get_norm(void *state) {
+    return norm_rand();
+}
+
+igraph_real_t igraph_rng_R_get_geom(void *state, igraph_real_t p) {
+    return Rf_rgeom(p);
+}
+
+igraph_real_t igraph_rng_R_get_binom(void *state, long int n,
+                                     igraph_real_t p) {
+    return Rf_rbinom(n, p);
+}
+
+igraph_real_t igraph_rng_R_get_gamma(void *state, igraph_real_t shape,
+                                     igraph_real_t scale) {
+    return Rf_rgamma(shape, scale);
+}
+
+igraph_real_t igraph_rng_R_get_exp(void *state, igraph_real_t rate) {
+    igraph_real_t scale = 1.0 / rate;
+    if (!IGRAPH_FINITE(scale) || scale <= 0.0) {
+        if (scale == 0.0) {
+            return 0.0;
+        }
+        return IGRAPH_NAN;
+    }
+    return scale * exp_rand();
+}
+
+igraph_rng_type_t igraph_rngtype_R = {
+    /* name= */      "GNU R",
+    /* min=  */      0,
+    /* max=  */      0x7FFFFFFFUL,
+    /* init= */      igraph_rng_R_init,
+    /* destroy= */   igraph_rng_R_destroy,
+    /* seed= */      igraph_rng_R_seed,
+    /* get= */       igraph_rng_R_get,
+    /* get_real= */  igraph_rng_R_get_real,
+    /* get_norm= */  igraph_rng_R_get_norm,
+    /* get_geom= */  igraph_rng_R_get_geom,
+    /* get_binom= */ igraph_rng_R_get_binom,
+    /* get_exp= */   igraph_rng_R_get_exp
+};
+
+IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = {
+    &igraph_rngtype_R,
+    0,
+    /* def= */ 1
+};
+
+#endif
+
+/* ------------------------------------ */
+
+/**
+ * \function igraph_rng_default
+ * Query the default random number generator.
+ *
+ * \return A pointer to the default random number generator.
+ *
+ * \sa igraph_rng_set_default()
+ */
+
+igraph_rng_t *igraph_rng_default() {
+    return &igraph_i_rng_default;
+}
+
+/* ------------------------------------ */
+
+double igraph_norm_rand(igraph_rng_t *rng);
+double igraph_rgeom(igraph_rng_t *rng, double p);
+double igraph_rbinom(igraph_rng_t *rng, double nin, double pp);
+double igraph_rexp(igraph_rng_t *rng, double rate);
+double igraph_rgamma(igraph_rng_t *rng, double shape, double scale);
+
+/**
+ * \function igraph_rng_init
+ * Initialize a random number generator
+ *
+ * This function allocates memory for a random number generator, with
+ * the given type, and sets its seed to the default.
+ *
+ * \param rng Pointer to an uninitialized RNG.
+ * \param type The type of the RNG, please see the documentation for
+ *    the supported types.
+ * \return Error code.
+ *
+ * Time complexity: depends on the type of the generator, but usually
+ * it should be O(1).
+ */
+
+int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type) {
+    rng->type = type;
+    IGRAPH_CHECK(rng->type->init(&rng->state));
+    return 0;
+}
+
+/**
+ * \function igraph_rng_destroy
+ * Deallocate memory associated with a random number generator
+ *
+ * \param rng The RNG to destroy. Do not destroy an RNG that is used
+ *    as the default igraph RNG.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_rng_destroy(igraph_rng_t *rng) {
+    rng->type->destroy(rng->state);
+}
+
+/**
+ * \function igraph_rng_seed
+ * Set the seed of a random number generator
+ *
+ * \param rng The RNG.
+ * \param seed The new seed.
+ * \return Error code.
+ *
+ * Time complexity: usually O(1), but may depend on the type of the
+ * RNG.
+ */
+int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed) {
+    const igraph_rng_type_t *type = rng->type;
+    rng->def = 0;
+    IGRAPH_CHECK(type->seed(rng->state, seed));
+    return 0;
+}
+
+/**
+ * \function igraph_rng_max
+ * Query the maximum possible integer for a random number generator
+ *
+ * \param rng The RNG.
+ * \return The largest possible integer that can be generated by
+ *         calling \ref igraph_rng_get_integer() on the RNG.
+ *
+ * Time complexity: O(1).
+ */
+
+unsigned long int igraph_rng_max(igraph_rng_t *rng) {
+    const igraph_rng_type_t *type = rng->type;
+    return type->max;
+}
+
+/**
+ * \function igraph_rng_min
+ * Query the minimum possible integer for a random number generator
+ *
+ * \param rng The RNG.
+ * \return The smallest possible integer that can be generated by
+ *         calling \ref igraph_rng_get_integer() on the RNG.
+ *
+ * Time complexity: O(1).
+ */
+
+unsigned long int igraph_rng_min(igraph_rng_t *rng) {
+    const igraph_rng_type_t *type = rng->type;
+    return type->min;
+}
+
+/**
+ * \function igraph_rng_name
+ * Query the type of a random number generator
+ *
+ * \param rng The RNG.
+ * \return The name of the type of the generator. Do not deallocate or
+ *         change the returned string pointer.
+ *
+ * Time complexity: O(1).
+ */
+
+const char *igraph_rng_name(igraph_rng_t *rng) {
+    const igraph_rng_type_t *type = rng->type;
+    return type->name;
+}
+
+/**
+ * \function igraph_rng_get_integer
+ * Generate an integer random number from an interval
+ *
+ * \param rng Pointer to the RNG to use for the generation. Use \ref
+ *        igraph_rng_default() here to use the default igraph RNG.
+ * \param l Lower limit, inclusive, it can be negative as well.
+ * \param h Upper limit, inclusive, it can be negative as well, but it
+ *        should be at least <code>l</code>.
+ * \return The generated random integer.
+ *
+ * Time complexity: depends on the generator, but should be usually
+ * O(1).
+ */
+
+long int igraph_rng_get_integer(igraph_rng_t *rng,
+                                long int l, long int h) {
+    const igraph_rng_type_t *type = rng->type;
+    if (type->get_real) {
+        return (long int)(type->get_real(rng->state) * (h - l + 1) + l);
+    } else if (type->get) {
+        unsigned long int max = type->max;
+        return (long int)(type->get(rng->state) / ((double)max + 1) * (h - l + 1) + l);
+    }
+    IGRAPH_ERROR("Internal random generator error", IGRAPH_EINTERNAL);
+    return 0;
+}
+
+/**
+ * \function igraph_rng_get_normal
+ * Normally distributed random numbers
+ *
+ * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default()
+ *        here to use the default igraph RNG.
+ * \param m The mean.
+ * \param s Standard deviation.
+ * \return The generated normally distributed random number.
+ *
+ * Time complexity: depends on the type of the RNG.
+ */
+
+igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng,
+                                    igraph_real_t m, igraph_real_t s) {
+    const igraph_rng_type_t *type = rng->type;
+    if (type->get_norm) {
+        return type->get_norm(rng->state) * s + m;
+    } else {
+        return igraph_norm_rand(rng) * s + m;
+    }
+}
+
+/**
+ * \function igraph_rng_get_unif
+ * Generate real, uniform random numbers from an interval
+ *
+ * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default()
+ *        here to use the default igraph RNG.
+ * \param l The lower bound, it can be negative.
+ * \param h The upper bound, it can be negative, but it has to be
+ *        larger than the lower bound.
+ * \return The generated uniformly distributed random number.
+ *
+ * Time complexity: depends on the type of the RNG.
+ */
+
+igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng,
+                                  igraph_real_t l, igraph_real_t h) {
+    const igraph_rng_type_t *type = rng->type;
+    if (type->get_real) {
+        return type->get_real(rng->state) * (h - l) + l;
+    } else if (type->get) {
+        unsigned long int max = type->max;
+        return type->get(rng->state) / ((double)max + 1) * (double)(h - l) + l;
+    }
+    IGRAPH_ERROR("Internal random generator error", IGRAPH_EINTERNAL);
+    return 0;
+}
+
+/**
+ * \function igraph_rng_get_unif01
+ * Generate real, uniform random number from the unit interval
+ *
+ * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default()
+ *        here to use the default igraph RNG.
+ * \return The generated uniformly distributed random number.
+ *
+ * Time complexity: depends on the type of the RNG.
+ */
+
+igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng) {
+    const igraph_rng_type_t *type = rng->type;
+    if (type->get_real) {
+        return type->get_real(rng->state);
+    } else if (type->get) {
+        unsigned long int max = type->max;
+        return type->get(rng->state) / ((double)max + 1);
+    }
+    IGRAPH_ERROR("Internal random generator error", IGRAPH_EINTERNAL);
+    return 0;
+}
+
+/**
+ * \function igraph_rng_get_geom
+ * Generate geometrically distributed random numbers
+ *
+ * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default()
+ *        here to use the default igraph RNG.
+ * \param p The probability of success in each trial. Must be larger
+ *        than zero and smaller or equal to 1.
+ * \return The generated geometrically distributed random number.
+ *
+ * Time complexity: depends on the type of the RNG.
+ */
+
+igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p) {
+    const igraph_rng_type_t *type = rng->type;
+    if (type->get_geom) {
+        return type->get_geom(rng->state, p);
+    } else {
+        return igraph_rgeom(rng, p);
+    }
+}
+
+/**
+ * \function igraph_rng_get_binom
+ * Generate binomially distributed random numbers
+ *
+ * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default()
+ *        here to use the default igraph RNG.
+ * \param n Number of observations.
+ * \param p Probability of an event.
+ * \return The generated binomially distributed random number.
+ *
+ * Time complexity: depends on the type of the RNG.
+ */
+
+igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n,
+                                   igraph_real_t p) {
+    const igraph_rng_type_t *type = rng->type;
+    if (type->get_binom) {
+        return type->get_binom(rng->state, n, p);
+    } else {
+        return igraph_rbinom(rng, n, p);
+    }
+}
+
+/**
+ * \function igraph_rng_get_gamma
+ * Generate sample from a Gamma distribution
+ *
+ * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default()
+ *        here to use the default igraph RNG.
+ * \param shape Shape parameter.
+ * \param scale Scale parameter.
+ * \return The generated sample
+ *
+ * Time complexity: depends on RNG.
+ */
+
+igraph_real_t igraph_rng_get_gamma(igraph_rng_t *rng, igraph_real_t shape,
+                                   igraph_real_t scale) {
+    const igraph_rng_type_t *type = rng->type;
+    if (type->get_gamma) {
+        return type->get_gamma(rng->state, shape, scale);
+    } else {
+        return igraph_rgamma(rng, shape, scale);
+    }
+}
+
+unsigned long int igraph_rng_get_int31(igraph_rng_t *rng) {
+    const igraph_rng_type_t *type = rng->type;
+    unsigned long int max = type->max;
+    if (type->get && max == 0x7FFFFFFFUL) {
+        return type->get(rng->state);
+    } else if (type->get_real) {
+        return (unsigned long int) (type->get_real(rng->state) * 0x7FFFFFFFUL);
+    } else {
+        return (unsigned long int) (igraph_rng_get_unif01(rng) * 0x7FFFFFFFUL);
+    }
+}
+
+igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate) {
+    const igraph_rng_type_t *type = rng->type;
+    if (type->get_exp) {
+        return type->get_exp(rng->state, rate);
+    } else {
+        return igraph_rexp(rng, rate);
+    }
+}
+
+
+#ifndef HAVE_EXPM1
+#ifndef USING_R         /* R provides a replacement */
+/* expm1 replacement */
+double expm1 (double x) {
+    if (fabs(x) < M_LN2) {
+        /* Compute the Taylor series S = x + (1/2!) x^2 + (1/3!) x^3 + ... */
+
+        double i = 1.0;
+        double sum = x;
+        double term = x / 1.0;
+
+        do {
+            term *= x / ++i;
+            sum += term;
+        } while (fabs(term) > fabs(sum) * 2.22e-16);
+
+        return sum;
+    }
+
+    return expl(x) - 1.0L;
+}
+#endif
+#endif
+
+#ifndef HAVE_RINT
+#ifndef USING_R         /* R provides a replacement */
+/* rint replacement */
+double rint (double x) {
+    return ( (x < 0.) ? -floor(-x + .5) : floor(x + .5) );
+}
+#endif
+#endif
+
+#ifndef HAVE_RINTF
+float rintf (float x) {
+    return ( (x < (float)0.) ? -(float)floor(-x + .5) : (float)floor(x + .5) );
+}
+#endif
+
+/*
+ * \ingroup internal
+ *
+ * This function appends the rest of the needed random number to the
+ * result vector.
+ */
+
+int igraph_i_random_sample_alga(igraph_vector_t *res, igraph_integer_t l, igraph_integer_t h,
+                                igraph_integer_t length) {
+    igraph_real_t N = h - l + 1;
+    igraph_real_t n = length;
+
+    igraph_real_t top = N - n;
+    igraph_real_t Nreal = N;
+    igraph_real_t S = 0;
+    igraph_real_t V, quot;
+
+    l = l - 1;
+
+    while (n >= 2) {
+        V = RNG_UNIF01();
+        S = 1;
+        quot = top / Nreal;
+        while (quot > V) {
+            S += 1;
+            top = -1.0 + top;
+            Nreal = -1.0 + Nreal;
+            quot = (quot * top) / Nreal;
+        }
+        l += S;
+        igraph_vector_push_back(res, l);    /* allocated */
+        Nreal = -1.0 + Nreal; n = -1 + n;
+    }
+
+    S = floor(round(Nreal) * RNG_UNIF01());
+    l += S + 1;
+    igraph_vector_push_back(res, l);  /* allocated */
+
+    return 0;
+}
+
+/**
+ * \ingroup nongraph
+ * \function igraph_random_sample
+ * \brief Generates an increasing random sequence of integers.
+ *
+ * </para><para>
+ * This function generates an increasing sequence of random integer
+ * numbers from a given interval. The algorithm is taken literally
+ * from (Vitter 1987). This method can be used for generating numbers from a
+ * \em very large interval. It is primarily created for randomly
+ * selecting some edges from the sometimes huge set of possible edges
+ * in a large graph.
+ * </para><para>
+ * Note that the type of the lower and the upper limit is \c igraph_real_t,
+ * not \c igraph_integer_t. This does not mean that you can pass fractional
+ * numbers there; these values must still be integral, but we need the
+ * longer range of \c igraph_real_t in several places in the library
+ * (for instance, when generating Erdos-Renyi graphs).
+ * \param res Pointer to an initialized vector. This will hold the
+ *        result. It will be resized to the proper size.
+ * \param l The lower limit of the generation interval (inclusive). This must
+ *        be less than or equal to the upper limit, and it must be integral.
+ *        Passing a fractional number here results in undefined behaviour.
+ * \param h The upper limit of the generation interval (inclusive). This must
+ *        be greater than or equal to the lower limit, and it must be integral.
+ *        Passing a fractional number here results in undefined behaviour.
+ * \param length The number of random integers to generate.
+ * \return The error code \c IGRAPH_EINVAL is returned in each of the
+ *         following cases: (1) The given lower limit is greater than the
+ *         given upper limit, i.e. \c l &gt; \c h. (2) Assuming that
+ *         \c l &lt; \c h and N is the sample size, the above error code is
+ *         returned if N &gt; |\c h - \c l|, i.e. the sample size exceeds the
+ *         size of the candidate pool.
+ *
+ * Time complexity: according to (Vitter 1987), the expected
+ * running time is O(length).
+ *
+ * </para><para>
+ * Reference:
+ * \clist
+ * \cli (Vitter 1987)
+ *   J. S. Vitter. An efficient algorithm for sequential random sampling.
+ *   \emb ACM Transactions on Mathematical Software, \eme 13(1):58--67, 1987.
+ * \endclist
+ *
+ * \example examples/simple/igraph_random_sample.c
+ */
+
+int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h,
+                         igraph_integer_t length) {
+    igraph_real_t N = h - l + 1;
+    igraph_real_t n = length;
+    int retval;
+
+    igraph_real_t nreal = length;
+    igraph_real_t ninv = (nreal != 0) ? 1.0 / nreal : 0.0;
+    igraph_real_t Nreal = N;
+    igraph_real_t Vprime;
+    igraph_real_t qu1 = -n + 1 + N;
+    igraph_real_t qu1real = -nreal + 1.0 + Nreal;
+    igraph_real_t negalphainv = -13;
+    igraph_real_t threshold = -negalphainv * n;
+    igraph_real_t S;
+
+    /* getting back some sense of sanity */
+    if (l > h) {
+        IGRAPH_ERROR("Lower limit is greater than upper limit", IGRAPH_EINVAL);
+    }
+    /* now we know that l <= h */
+    if (length > N) {
+        IGRAPH_ERROR("Sample size exceeds size of candidate pool", IGRAPH_EINVAL);
+    }
+
+    /* treat rare cases quickly */
+    if (l == h) {
+        IGRAPH_CHECK(igraph_vector_resize(res, 1));
+        VECTOR(*res)[0] = l;
+        return 0;
+    }
+    if (length == 0) {
+        igraph_vector_clear(res);
+        return 0;
+    }
+    if (length == N) {
+        long int i = 0;
+        IGRAPH_CHECK(igraph_vector_resize(res, length));
+        for (i = 0; i < length; i++) {
+            VECTOR(*res)[i] = l++;
+        }
+        return 0;
+    }
+
+    igraph_vector_clear(res);
+    IGRAPH_CHECK(igraph_vector_reserve(res, length));
+
+    RNG_BEGIN();
+
+    Vprime = exp(log(RNG_UNIF01()) * ninv);
+    l = l - 1;
+
+    while (n > 1 && threshold < N) {
+        igraph_real_t X, U;
+        igraph_real_t limit, t;
+        igraph_real_t negSreal, y1, y2, top, bottom;
+        igraph_real_t nmin1inv = 1.0 / (-1.0 + nreal);
+        while (1) {
+            while (1) {
+                X = Nreal * (-Vprime + 1.0);
+                S = floor(X);
+                // if (S==0) { S=1; }
+                if (S < qu1) {
+                    break;
+                }
+                Vprime = exp(log(RNG_UNIF01()) * ninv);
+            }
+            U = RNG_UNIF01();
+            negSreal = -S;
+
+            y1 = exp(log(U * Nreal / qu1real) * nmin1inv);
+            Vprime = y1 * (-X / Nreal + 1.0) * (qu1real / (negSreal + qu1real));
+            if (Vprime <= 1.0) {
+                break;
+            }
+
+            y2 = 1.0;
+            top = -1.0 + Nreal;
+            if (-1 + n > S) {
+                bottom = -nreal + Nreal;
+                limit = -S + N;
+            } else {
+                bottom = -1.0 + negSreal + Nreal;
+                limit = qu1;
+            }
+            for (t = -1 + N; t >= limit; t--) {
+                y2 = (y2 * top) / bottom;
+                top = -1.0 + top;
+                bottom = -1.0 + bottom;
+            }
+            if (Nreal / (-X + Nreal) >= y1 * exp(log(y2)*nmin1inv)) {
+                Vprime = exp(log(RNG_UNIF01()) * nmin1inv);
+                break;
+            }
+            Vprime = exp(log(RNG_UNIF01()) * ninv);
+        }
+
+        l += S + 1;
+        igraph_vector_push_back(res, l);    /* allocated */
+        N = -S + (-1 + N);   Nreal = negSreal + (-1.0 + Nreal);
+        n = -1 + n;   nreal = -1.0 + nreal; ninv = nmin1inv;
+        qu1 = -S + qu1; qu1real = negSreal + qu1real;
+        threshold = threshold + negalphainv;
+    }
+
+    if (n > 1) {
+        retval = igraph_i_random_sample_alga(res, (igraph_integer_t) l + 1,
+                                             (igraph_integer_t) h,
+                                             (igraph_integer_t) n);
+    } else {
+        retval = 0;
+        S = floor(N * Vprime);
+        l += S + 1;
+        igraph_vector_push_back(res, l);    /* allocated */
+    }
+
+    RNG_END();
+
+    return retval;
+}
+
+#ifdef USING_R
+
+/* These are never called. But they are correct, nevertheless */
+
+double igraph_norm_rand(igraph_rng_t *rng) {
+    return norm_rand();
+}
+
+double igraph_rgeom(igraph_rng_t *rng, double p) {
+    return Rf_rgeom(p);
+}
+
+double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) {
+    return Rf_rbinom(nin, pp);
+}
+
+double igraph_rexp(igraph_rng_t *rng, double rate) {
+    igraph_real_t scale = 1.0 / rate;
+    if (!IGRAPH_FINITE(scale) || scale <= 0.0) {
+        if (scale == 0.0) {
+            return 0.0;
+        }
+        return IGRAPH_NAN;
+    }
+    return scale * exp_rand();
+}
+
+double igraph_rgamma(igraph_rng_t *rng, double shape, double scale) {
+    return Rf_rgamma(shape, scale);
+}
+
+#else
+
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *  Copyright (C) 2000 The R Development Core Team
+ *  based on AS 111 (C) 1977 Royal Statistical Society
+ *  and   on AS 241 (C) 1988 Royal Statistical Society
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ *
+ *  SYNOPSIS
+ *
+ *  double qnorm5(double p, double mu, double sigma,
+ *            int lower_tail, int log_p)
+ *            {qnorm (..) is synonymous and preferred inside R}
+ *
+ *  DESCRIPTION
+ *
+ *  Compute the quantile function for the normal distribution.
+ *
+ *  For small to moderate probabilities, algorithm referenced
+ *  below is used to obtain an initial approximation which is
+ *  polished with a final Newton step.
+ *
+ *  For very large arguments, an algorithm of Wichura is used.
+ *
+ *  REFERENCE
+ *
+ *  Beasley, J. D. and S. G. Springer (1977).
+ *  Algorithm AS 111: The percentage points of the normal distribution,
+ *  Applied Statistics, 26, 118-121.
+ *
+ *      Wichura, M.J. (1988).
+ *      Algorithm AS 241: The Percentage Points of the Normal Distribution.
+ *      Applied Statistics, 37, 477-484.
+ */
+
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998-2004  The R Development Core Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/* Private header file for use during compilation of Mathlib */
+#ifndef MATHLIB_PRIVATE_H
+#define MATHLIB_PRIVATE_H
+
+#define ML_POSINF IGRAPH_INFINITY
+#define ML_NEGINF -IGRAPH_INFINITY
+#define ML_NAN    IGRAPH_NAN
+
+#define ML_ERROR(x) /* nothing */
+#define ML_UNDERFLOW    (DBL_MIN * DBL_MIN)
+#define ML_VALID(x) (!ISNAN(x))
+
+#define ME_NONE     0
+/*  no error */
+#define ME_DOMAIN   1
+/*  argument out of domain */
+#define ME_RANGE    2
+/*  value out of range */
+#define ME_NOCONV   4
+/*  process did not converge */
+#define ME_PRECISION    8
+/*  does not have "full" precision */
+#define ME_UNDERFLOW    16
+/*  and underflow occurred (important for IEEE)*/
+
+#define ML_ERR_return_NAN { ML_ERROR(ME_DOMAIN); return ML_NAN; }
+
+/* Wilcoxon Rank Sum Distribution */
+
+#define WILCOX_MAX 50
+
+/* Wilcoxon Signed Rank Distribution */
+
+#define SIGNRANK_MAX 50
+
+/* Formerly private part of Mathlib.h */
+
+/* always remap internal functions */
+#define bd0         Rf_bd0
+#define chebyshev_eval  Rf_chebyshev_eval
+#define chebyshev_init  Rf_chebyshev_init
+#define i1mach      Rf_i1mach
+#define gammalims   Rf_gammalims
+#define lfastchoose Rf_lfastchoose
+#define lgammacor   Rf_lgammacor
+#define stirlerr        Rf_stirlerr
+
+/* Chebyshev Series */
+
+int chebyshev_init(double*, int, double);
+double  chebyshev_eval(double, const double *, const int);
+
+/* Gamma and Related Functions */
+
+void    gammalims(double*, double*);
+double  lgammacor(double); /* log(gamma) correction */
+double  stirlerr(double);  /* Stirling expansion "error" */
+
+double  lfastchoose(double, double);
+
+double  bd0(double, double);
+
+/* Consider adding these two to the API (Rmath.h): */
+double  dbinom_raw(double, double, double, double, int);
+double  dpois_raw (double, double, int);
+double  pnchisq_raw(double, double, double, double, double, int);
+
+int i1mach(int);
+
+/* From toms708.c */
+void bratio(double a, double b, double x, double y,
+            double *w, double *w1, int *ierr);
+
+
+#endif /* MATHLIB_PRIVATE_H */
+
+
+/* Utilities for `dpq' handling (density/probability/quantile) */
+
+/* give_log in "d";  log_p in "p" & "q" : */
+#define give_log log_p
+/* "DEFAULT" */
+/* --------- */
+#define R_D__0  (log_p ? ML_NEGINF : 0.)        /* 0 */
+#define R_D__1  (log_p ? 0. : 1.)           /* 1 */
+#define R_DT_0  (lower_tail ? R_D__0 : R_D__1)      /* 0 */
+#define R_DT_1  (lower_tail ? R_D__1 : R_D__0)      /* 1 */
+
+#define R_D_Lval(p) (lower_tail ? (p) : (1 - (p)))  /*  p  */
+#define R_D_Cval(p) (lower_tail ? (1 - (p)) : (p))  /*  1 - p */
+
+#define R_D_val(x)  (log_p  ? log(x) : (x))     /*  x  in pF(x,..) */
+#define R_D_qIv(p)  (log_p  ? exp(p) : (p))     /*  p  in qF(p,..) */
+#define R_D_exp(x)  (log_p  ?  (x)   : exp(x))  /* exp(x) */
+#define R_D_log(p)  (log_p  ?  (p)   : log(p))  /* log(p) */
+#define R_D_Clog(p) (log_p  ? log1p(-(p)) : (1 - (p)))/* [log](1-p) */
+
+/* log(1-exp(x)):  R_D_LExp(x) == (log1p(- R_D_qIv(x))) but even more stable:*/
+#define R_D_LExp(x)     (log_p ? R_Log1_Exp(x) : log1p(-x))
+
+/*till 1.8.x:
+ * #define R_DT_val(x)  R_D_val(R_D_Lval(x))
+ * #define R_DT_Cval(x) R_D_val(R_D_Cval(x)) */
+#define R_DT_val(x) (lower_tail ? R_D_val(x)  : R_D_Clog(x))
+#define R_DT_Cval(x)    (lower_tail ? R_D_Clog(x) : R_D_val(x))
+
+/*#define R_DT_qIv(p)   R_D_Lval(R_D_qIv(p))         *  p  in qF ! */
+#define R_DT_qIv(p) (log_p ? (lower_tail ? exp(p) : - expm1(p)) \
+                     : R_D_Lval(p))
+
+/*#define R_DT_CIv(p)   R_D_Cval(R_D_qIv(p))         *  1 - p in qF */
+#define R_DT_CIv(p) (log_p ? (lower_tail ? -expm1(p) : exp(p)) \
+                     : R_D_Cval(p))
+
+#define R_DT_exp(x) R_D_exp(R_D_Lval(x))        /* exp(x) */
+#define R_DT_Cexp(x)    R_D_exp(R_D_Cval(x))        /* exp(1 - x) */
+
+#define R_DT_log(p) (lower_tail? R_D_log(p) : R_D_LExp(p))/* log(p) in qF */
+#define R_DT_Clog(p)    (lower_tail? R_D_LExp(p): R_D_log(p))/* log(1-p) in qF*/
+#define R_DT_Log(p) (lower_tail? (p) : R_Log1_Exp(p))
+/* ==   R_DT_log when we already "know" log_p == TRUE :*/
+
+#define R_Q_P01_check(p)            \
+    if ((log_p  && p > 0) ||            \
+        (!log_p && (p < 0 || p > 1)) )      \
+        ML_ERR_return_NAN
+
+/* additions for density functions (C.Loader) */
+#define R_D_fexp(f,x)     (give_log ? -0.5*log(f)+(x) : exp(x)/sqrt(f))
+#define R_D_forceint(x)   floor((x) + 0.5)
+#define R_D_nonint(x)     (fabs((x) - floor((x)+0.5)) > 1e-7)
+/* [neg]ative or [non int]eger : */
+#define R_D_negInonint(x) (x < 0. || R_D_nonint(x))
+
+#define R_D_nonint_check(x)                 \
+    if(R_D_nonint(x)) {                  \
+        MATHLIB_WARNING("non-integer x = %f", x);   \
+        return R_D__0;                  \
+    }
+
+double igraph_qnorm5(double p, double mu, double sigma, int lower_tail, int log_p) {
+    double p_, q, r, val;
+
+#ifdef IEEE_754
+    if (ISNAN(p) || ISNAN(mu) || ISNAN(sigma)) {
+        return p + mu + sigma;
+    }
+#endif
+    if (p == R_DT_0) {
+        return ML_NEGINF;
+    }
+    if (p == R_DT_1) {
+        return ML_POSINF;
+    }
+    R_Q_P01_check(p);
+
+    if (sigma  < 0) {
+        ML_ERR_return_NAN;
+    }
+    if (sigma == 0) {
+        return mu;
+    }
+
+    p_ = R_DT_qIv(p);/* real lower_tail prob. p */
+    q = p_ - 0.5;
+
+    /*-- use AS 241 --- */
+    /* double ppnd16_(double *p, long *ifault)*/
+    /*      ALGORITHM AS241  APPL. STATIST. (1988) VOL. 37, NO. 3
+
+            Produces the normal deviate Z corresponding to a given lower
+            tail area of P; Z is accurate to about 1 part in 10**16.
+
+            (original fortran code used PARAMETER(..) for the coefficients
+             and provided hash codes for checking them...)
+    */
+    if (fabs(q) <= .425) {/* 0.075 <= p <= 0.925 */
+        r = .180625 - q * q;
+        val =
+            q * (((((((r * 2509.0809287301226727 +
+                       33430.575583588128105) * r + 67265.770927008700853) * r +
+                     45921.953931549871457) * r + 13731.693765509461125) * r +
+                   1971.5909503065514427) * r + 133.14166789178437745) * r +
+                 3.387132872796366608)
+            / (((((((r * 5226.495278852854561 +
+                     28729.085735721942674) * r + 39307.89580009271061) * r +
+                   21213.794301586595867) * r + 5394.1960214247511077) * r +
+                 687.1870074920579083) * r + 42.313330701600911252) * r + 1.);
+    } else { /* closer than 0.075 from {0,1} boundary */
+
+        /* r = min(p, 1-p) < 0.075 */
+        if (q > 0) {
+            r = R_DT_CIv(p);    /* 1-p */
+        } else {
+            r = p_;    /* = R_DT_Iv(p) ^=  p */
+        }
+
+        r = sqrt(- ((log_p &&
+                     ((lower_tail && q <= 0) || (!lower_tail && q > 0))) ?
+                    p : /* else */ log(r)));
+        /* r = sqrt(-log(r))  <==>  min(p, 1-p) = exp( - r^2 ) */
+
+        if (r <= 5.) { /* <==> min(p,1-p) >= exp(-25) ~= 1.3888e-11 */
+            r += -1.6;
+            val = (((((((r * 7.7454501427834140764e-4 +
+                         .0227238449892691845833) * r + .24178072517745061177) *
+                       r + 1.27045825245236838258) * r +
+                      3.64784832476320460504) * r + 5.7694972214606914055) *
+                    r + 4.6303378461565452959) * r +
+                   1.42343711074968357734)
+                  / (((((((r *
+                           1.05075007164441684324e-9 + 5.475938084995344946e-4) *
+                          r + .0151986665636164571966) * r +
+                         .14810397642748007459) * r + .68976733498510000455) *
+                       r + 1.6763848301838038494) * r +
+                      2.05319162663775882187) * r + 1.);
+        } else { /* very close to  0 or 1 */
+            r += -5.;
+            val = (((((((r * 2.01033439929228813265e-7 +
+                         2.71155556874348757815e-5) * r +
+                        .0012426609473880784386) * r + .026532189526576123093) *
+                      r + .29656057182850489123) * r +
+                     1.7848265399172913358) * r + 5.4637849111641143699) *
+                   r + 6.6579046435011037772)
+                  / (((((((r *
+                           2.04426310338993978564e-15 + 1.4215117583164458887e-7) *
+                          r + 1.8463183175100546818e-5) * r +
+                         7.868691311456132591e-4) * r + .0148753612908506148525)
+                       * r + .13692988092273580531) * r +
+                      .59983220655588793769) * r + 1.);
+        }
+
+        if (q < 0.0) {
+            val = -val;
+        }
+        /* return (q >= 0.)? r : -r ;*/
+    }
+    return mu + sigma * val;
+}
+
+double fsign(double x, double y) {
+#ifdef IEEE_754
+    if (ISNAN(x) || ISNAN(y)) {
+        return x + y;
+    }
+#endif
+    return ((y >= 0) ? fabs(x) : -fabs(x));
+}
+
+int imax2(int x, int y) {
+    return (x < y) ? y : x;
+}
+
+int imin2(int x, int y) {
+    return (x < y) ? x : y;
+}
+
+#if HAVE_WORKING_ISFINITE || HAVE_ISFINITE
+    /* isfinite is defined in <math.h> according to C99 */
+    #define R_FINITE(x)    isfinite(x)
+#elif HAVE_WORKING_FINITE || HAVE_FINITE
+    /* include header needed to define finite() */
+    #ifdef HAVE_IEEE754_H
+        #include <ieee754.h>         /* newer Linuxen */
+    #else
+        #ifdef HAVE_IEEEFP_H
+            #include <ieeefp.h>         /* others [Solaris], .. */
+        #endif
+    #endif
+    #define R_FINITE(x)    finite(x)
+#else
+    #define R_FINITE(x)    R_finite(x)
+#endif
+
+int R_finite(double x) {
+#if HAVE_WORKING_ISFINITE || HAVE_ISFINITE
+    return isfinite(x);
+#elif HAVE_WORKING_FINITE || HAVE_FINITE
+    return finite(x);
+#else
+    /* neither finite nor isfinite work. Do we really need the AIX exception? */
+# ifdef _AIX
+#  include <fp.h>
+    return FINITE(x);
+# elif defined(_MSC_VER)
+    return _finite(x);
+#else
+    return (!isnan(x) & (x != 1 / 0.0) & (x != -1.0 / 0.0));
+# endif
+#endif
+}
+
+int R_isnancpp(double x) {
+    return (isnan(x) != 0);
+}
+
+#ifdef __cplusplus
+    int R_isnancpp(double); /* in arithmetic.c */
+    #define ISNAN(x)     R_isnancpp(x)
+#else
+    #define ISNAN(x)     (isnan(x)!=0)
+#endif
+
+double igraph_norm_rand(igraph_rng_t *rng) {
+
+    double u1;
+
+#define BIG 134217728 /* 2^27 */
+    /* unif_rand() alone is not of high enough precision */
+    u1 = igraph_rng_get_unif01(rng);
+    u1 = (int)(BIG * u1) + igraph_rng_get_unif01(rng);
+    return igraph_qnorm5(u1 / BIG, 0.0, 1.0, 1, 0);
+}
+
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *  Copyright (C) 2000-2002 the R Development Core Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ *
+ *  SYNOPSIS
+ *
+ *    #include <Rmath.h>
+ *    double exp_rand(void);
+ *
+ *  DESCRIPTION
+ *
+ *    Random variates from the standard exponential distribution.
+ *
+ *  REFERENCE
+ *
+ *    Ahrens, J.H. and Dieter, U. (1972).
+ *    Computer methods for sampling from the exponential and
+ *    normal distributions.
+ *    Comm. ACM, 15, 873-882.
+ */
+
+double igraph_exp_rand(igraph_rng_t *rng) {
+    /* q[k-1] = sum(log(2)^k / k!)  k=1,..,n, */
+    /* The highest n (here 8) is determined by q[n-1] = 1.0 */
+    /* within standard precision */
+    const double q[] = {
+        0.6931471805599453,
+        0.9333736875190459,
+        0.9888777961838675,
+        0.9984959252914960,
+        0.9998292811061389,
+        0.9999833164100727,
+        0.9999985691438767,
+        0.9999998906925558,
+        0.9999999924734159,
+        0.9999999995283275,
+        0.9999999999728814,
+        0.9999999999985598,
+        0.9999999999999289,
+        0.9999999999999968,
+        0.9999999999999999,
+        1.0000000000000000
+    };
+    double a, u, ustar, umin;
+    int i;
+
+    a = 0.;
+    /* precaution if u = 0 is ever returned */
+    u = igraph_rng_get_unif01(rng);
+    while (u <= 0.0 || u >= 1.0) {
+        u = igraph_rng_get_unif01(rng);
+    }
+    for (;;) {
+        u += u;
+        if (u > 1.0) {
+            break;
+        }
+        a += q[0];
+    }
+    u -= 1.;
+
+    if (u <= q[0]) {
+        return a + u;
+    }
+
+    i = 0;
+    ustar = igraph_rng_get_unif01(rng);
+    umin = ustar;
+    do {
+        ustar = igraph_rng_get_unif01(rng);
+        if (ustar < umin) {
+            umin = ustar;
+        }
+        i++;
+    } while (u > q[i]);
+    return a + umin * q[0];
+}
+
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *  Copyright (C) 2000-2001 The R Development Core Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ *
+ *  SYNOPSIS
+ *
+ *    #include <Rmath.h>
+ *    double rpois(double lambda)
+ *
+ *  DESCRIPTION
+ *
+ *    Random variates from the Poisson distribution.
+ *
+ *  REFERENCE
+ *
+ *    Ahrens, J.H. and Dieter, U. (1982).
+ *    Computer generation of Poisson deviates
+ *    from modified normal distributions.
+ *    ACM Trans. Math. Software 8, 163-179.
+ */
+
+#define a0  -0.5
+#define a1   0.3333333
+#define a2  -0.2500068
+#define a3   0.2000118
+#define a4  -0.1661269
+#define a5   0.1421878
+#define a6  -0.1384794
+#define a7   0.1250060
+
+#define one_7   0.1428571428571428571
+#define one_12  0.0833333333333333333
+#define one_24  0.0416666666666666667
+
+#define repeat for(;;)
+
+#define FALSE 0
+#define TRUE  1
+#define M_1_SQRT_2PI    0.398942280401432677939946059934     /* 1/sqrt(2pi) */
+
+double igraph_rpois(igraph_rng_t *rng, double mu) {
+    /* Factorial Table (0:9)! */
+    const double fact[10] = {
+        1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880.
+    };
+
+    /* These are static --- persistent between calls for same mu : */
+    static IGRAPH_THREAD_LOCAL int l, m;
+
+    static IGRAPH_THREAD_LOCAL double b1, b2, c, c0, c1, c2, c3;
+    static IGRAPH_THREAD_LOCAL double pp[36], p0, p, q, s, d, omega;
+    static IGRAPH_THREAD_LOCAL double big_l;/* integer "w/o overflow" */
+    static IGRAPH_THREAD_LOCAL double muprev = 0., muprev2 = 0.;/*, muold    = 0.*/
+
+    /* Local Vars  [initialize some for -Wall]: */
+    double del, difmuk = 0., E = 0., fk = 0., fx, fy, g, px, py, t, u = 0., v, x;
+    double pois = -1.;
+    int k, kflag, big_mu, new_big_mu = FALSE;
+
+    if (!R_FINITE(mu)) {
+        ML_ERR_return_NAN;
+    }
+
+    if (mu <= 0.) {
+        return 0.;
+    }
+
+    big_mu = mu >= 10.;
+    if (big_mu) {
+        new_big_mu = FALSE;
+    }
+
+    if (!(big_mu && mu == muprev)) {/* maybe compute new persistent par.s */
+
+        if (big_mu) {
+            new_big_mu = TRUE;
+            /* Case A. (recalculation of s,d,l  because mu has changed):
+             * The Poisson probabilities pk exceed the discrete normal
+             * probabilities fk whenever k >= m(mu).
+             */
+            muprev = mu;
+            s = sqrt(mu);
+            d = 6. * mu * mu;
+            big_l = floor(mu - 1.1484);
+            /* = an upper bound to m(mu) for all mu >= 10.*/
+        } else { /* Small mu ( < 10) -- not using normal approx. */
+
+            /* Case B. (start new table and calculate p0 if necessary) */
+
+            /*muprev = 0.;-* such that next time, mu != muprev ..*/
+            if (mu != muprev) {
+                muprev = mu;
+                m = imax2(1, (int) mu);
+                l = 0; /* pp[] is already ok up to pp[l] */
+                q = p0 = p = exp(-mu);
+            }
+
+            repeat {
+                /* Step U. uniform sample for inversion method */
+                u = igraph_rng_get_unif01(rng);
+                if (u <= p0) {
+                    return 0.;
+                }
+
+                /* Step T. table comparison until the end pp[l] of the
+                   pp-table of cumulative Poisson probabilities
+                   (0.458 > ~= pp[9](= 0.45792971447) for mu=10 ) */
+                if (l != 0) {
+                    for (k = (u <= 0.458) ? 1 : imin2(l, m);  k <= l; k++)
+                        if (u <= pp[k]) {
+                            return (double)k;
+                        }
+                    if (l == 35) { /* u > pp[35] */
+                        continue;
+                    }
+                }
+                /* Step C. creation of new Poisson
+                   probabilities p[l..] and their cumulatives q =: pp[k] */
+                l++;
+                for (k = l; k <= 35; k++) {
+                    p *= mu / k;
+                    q += p;
+                    pp[k] = q;
+                    if (u <= q) {
+                        l = k;
+                        return (double)k;
+                    }
+                }
+                l = 35;
+            } /* end(repeat) */
+        }/* mu < 10 */
+
+    } /* end {initialize persistent vars} */
+
+    /* Only if mu >= 10 : ----------------------- */
+
+    /* Step N. normal sample */
+    g = mu + s * igraph_norm_rand(rng);/* norm_rand() ~ N(0,1), standard normal */
+
+    if (g >= 0.) {
+        pois = floor(g);
+        /* Step I. immediate acceptance if pois is large enough */
+        if (pois >= big_l) {
+            return pois;
+        }
+        /* Step S. squeeze acceptance */
+        fk = pois;
+        difmuk = mu - fk;
+        u = igraph_rng_get_unif01(rng); /* ~ U(0,1) - sample */
+        if (d * u >= difmuk * difmuk * difmuk) {
+            return pois;
+        }
+    }
+
+    /* Step P. preparations for steps Q and H.
+       (recalculations of parameters if necessary) */
+
+    if (new_big_mu || mu != muprev2) {
+        /* Careful! muprev2 is not always == muprev
+        because one might have exited in step I or S
+        */
+        muprev2 = mu;
+        omega = M_1_SQRT_2PI / s;
+        /* The quantities b1, b2, c3, c2, c1, c0 are for the Hermite
+         * approximations to the discrete normal probabilities fk. */
+
+        b1 = one_24 / mu;
+        b2 = 0.3 * b1 * b1;
+        c3 = one_7 * b1 * b2;
+        c2 = b2 - 15. * c3;
+        c1 = b1 - 6. * b2 + 45. * c3;
+        c0 = 1. - b1 + 3. * b2 - 15. * c3;
+        c = 0.1069 / mu; /* guarantees majorization by the 'hat'-function. */
+    }
+
+    if (g >= 0.) {
+        /* 'Subroutine' F is called (kflag=0 for correct return) */
+        kflag = 0;
+        goto Step_F;
+    }
+
+
+    repeat {
+        /* Step E. Exponential Sample */
+
+        E = igraph_exp_rand(rng);/* ~ Exp(1) (standard exponential) */
+
+        /*  sample t from the laplace 'hat'
+            (if t <= -0.6744 then pk < fk for all mu >= 10.) */
+        u = 2 * igraph_rng_get_unif01(rng) - 1.;
+        t = 1.8 + fsign(E, u);
+        if (t > -0.6744) {
+            pois = floor(mu + s * t);
+            fk = pois;
+            difmuk = mu - fk;
+
+            /* 'subroutine' F is called (kflag=1 for correct return) */
+            kflag = 1;
+
+Step_F: /* 'subroutine' F : calculation of px,py,fx,fy. */
+
+            if (pois < 10) { /* use factorials from table fact[] */
+                px = -mu;
+                py = pow(mu, pois) / fact[(int)pois];
+            } else {
+                /* Case pois >= 10 uses polynomial approximation
+                   a0-a7 for accuracy when advisable */
+                del = one_12 / fk;
+                del = del * (1. - 4.8 * del * del);
+                v = difmuk / fk;
+                if (fabs(v) <= 0.25)
+                    px = fk * v * v * (((((((a7 * v + a6) * v + a5) * v + a4) *
+                                          v + a3) * v + a2) * v + a1) * v + a0)
+                    - del;
+                else { /* |v| > 1/4 */
+                    px = fk * log(1. + v) - difmuk - del;
+                }
+                py = M_1_SQRT_2PI / sqrt(fk);
+            }
+            x = (0.5 - difmuk) / s;
+            x *= x;/* x^2 */
+            fx = -0.5 * x;
+            fy = omega * (((c3 * x + c2) * x + c1) * x + c0);
+            if (kflag > 0) {
+                /* Step H. Hat acceptance (E is repeated on rejection) */
+                if (c * fabs(u) <= py * exp(px + E) - fy * exp(fx + E)) {
+                    break;
+                }
+            } else
+                /* Step Q. Quotient acceptance (rare case) */
+                if (fy - u * fy <= py * exp(px - fx)) {
+                    break;
+                }
+        }/* t > -.67.. */
+    }
+    return pois;
+}
+
+#undef a1
+#undef a2
+#undef a3
+#undef a4
+#undef a5
+#undef a6
+#undef a7
+
+double igraph_rgeom(igraph_rng_t *rng, double p) {
+    if (ISNAN(p) || p <= 0 || p > 1) {
+        ML_ERR_return_NAN;
+    }
+
+    return igraph_rpois(rng, igraph_exp_rand(rng) * ((1 - p) / p));
+}
+
+/* This is from nmath/rbinom.c */
+
+#define repeat for(;;)
+
+double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) {
+    /* FIXME: These should become THREAD_specific globals : */
+
+    static IGRAPH_THREAD_LOCAL double c, fm, npq, p1, p2, p3, p4, qn;
+    static IGRAPH_THREAD_LOCAL double xl, xll, xlr, xm, xr;
+
+    static IGRAPH_THREAD_LOCAL double psave = -1.0;
+    static IGRAPH_THREAD_LOCAL int nsave = -1;
+    static IGRAPH_THREAD_LOCAL int m;
+
+    double f, f1, f2, u, v, w, w2, x, x1, x2, z, z2;
+    double p, q, np, g, r, al, alv, amaxp, ffm, ynorm;
+    int i, ix, k, n;
+
+    if (!R_FINITE(nin)) {
+        ML_ERR_return_NAN;
+    }
+    n = floor(nin + 0.5);
+    if (n != nin) {
+        ML_ERR_return_NAN;
+    }
+
+    if (!R_FINITE(pp) ||
+        /* n=0, p=0, p=1 are not errors <TSL>*/
+        n < 0 || pp < 0. || pp > 1.) {
+        ML_ERR_return_NAN;
+    }
+
+    if (n == 0 || pp == 0.) {
+        return 0;
+    }
+    if (pp == 1.) {
+        return n;
+    }
+
+    p = fmin(pp, 1. - pp);
+    q = 1. - p;
+    np = n * p;
+    r = p / q;
+    g = r * (n + 1);
+
+    /* Setup, perform only when parameters change [using static (globals): */
+
+    /* FIXING: Want this thread safe
+       -- use as little (thread globals) as possible
+    */
+    if (pp != psave || n != nsave) {
+        psave = pp;
+        nsave = n;
+        if (np < 30.0) {
+            /* inverse cdf logic for mean less than 30 */
+            qn = pow(q, (double) n);
+            goto L_np_small;
+        } else {
+            ffm = np + p;
+            m = ffm;
+            fm = m;
+            npq = np * q;
+            p1 = (int)(2.195 * sqrt(npq) - 4.6 * q) + 0.5;
+            xm = fm + 0.5;
+            xl = xm - p1;
+            xr = xm + p1;
+            c = 0.134 + 20.5 / (15.3 + fm);
+            al = (ffm - xl) / (ffm - xl * p);
+            xll = al * (1.0 + 0.5 * al);
+            al = (xr - ffm) / (xr * q);
+            xlr = al * (1.0 + 0.5 * al);
+            p2 = p1 * (1.0 + c + c);
+            p3 = p2 + c / xll;
+            p4 = p3 + c / xlr;
+        }
+    } else if (n == nsave) {
+        if (np < 30.0) {
+            goto L_np_small;
+        }
+    }
+
+    /*-------------------------- np = n*p >= 30 : ------------------- */
+    repeat {
+        u = igraph_rng_get_unif01(rng) * p4;
+        v = igraph_rng_get_unif01(rng);
+        /* triangular region */
+        if (u <= p1) {
+            ix = xm - p1 * v + u;
+            goto finis;
+        }
+        /* parallelogram region */
+        if (u <= p2) {
+            x = xl + (u - p1) / c;
+            v = v * c + 1.0 - fabs(xm - x) / p1;
+            if (v > 1.0 || v <= 0.) {
+                continue;
+            }
+            ix = x;
+        } else {
+            if (u > p3) { /* right tail */
+                ix = xr - log(v) / xlr;
+                if (ix > n) {
+                    continue;
+                }
+                v = v * (u - p3) * xlr;
+            } else {/* left tail */
+                ix = xl + log(v) / xll;
+                if (ix < 0) {
+                    continue;
+                }
+                v = v * (u - p2) * xll;
+            }
+        }
+        /* determine appropriate way to perform accept/reject test */
+        k = abs(ix - m);
+        if (k <= 20 || k >= npq / 2 - 1) {
+            /* explicit evaluation */
+            f = 1.0;
+            if (m < ix) {
+                for (i = m + 1; i <= ix; i++) {
+                    f *= (g / i - r);
+                }
+            } else if (m != ix) {
+                for (i = ix + 1; i <= m; i++) {
+                    f /= (g / i - r);
+                }
+            }
+            if (v <= f) {
+                goto finis;
+            }
+        } else {
+            /* squeezing using upper and lower bounds on log(f(x)) */
+            amaxp = (k / npq) * ((k * (k / 3. + 0.625) + 0.1666666666666) / npq + 0.5);
+            ynorm = -k * k / (2.0 * npq);
+            alv = log(v);
+            if (alv < ynorm - amaxp) {
+                goto finis;
+            }
+            if (alv <= ynorm + amaxp) {
+                /* Stirling's formula to machine accuracy */
+                /* for the final acceptance/rejection test */
+                x1 = ix + 1;
+                f1 = fm + 1.0;
+                z = n + 1 - fm;
+                w = n - ix + 1.0;
+                z2 = z * z;
+                x2 = x1 * x1;
+                f2 = f1 * f1;
+                w2 = w * w;
+                if (alv <= xm * log(f1 / x1) + (n - m + 0.5) * log(z / w) + (ix - m) * log(w * p / (x1 * q)) + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / f2) / f2) / f2) / f2) / f1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / z2) / z2) / z2) / z2) / z / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / x2) / x2) / x2) / x2) / x1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / w2) / w2) / w2) / w2) / w / 166320.) {
+                    goto finis;
+                }
+            }
+        }
+    }
+
+L_np_small:
+    /*---------------------- np = n*p < 30 : ------------------------- */
+
+    repeat {
+        ix = 0;
+        f = qn;
+        u = igraph_rng_get_unif01(rng);
+        repeat {
+            if (u < f) {
+                goto finis;
+            }
+            if (ix > 110) {
+                break;
+            }
+            u -= f;
+            ix++;
+            f *= (g / ix - r);
+        }
+    }
+finis:
+    if (psave > 0.5) {
+        ix = n - ix;
+    }
+    return (double)ix;
+}
+
+igraph_real_t igraph_rexp(igraph_rng_t *rng, double rate) {
+    igraph_real_t scale = 1.0 / rate;
+    if (!IGRAPH_FINITE(scale) || scale <= 0.0) {
+        if (scale == 0.0) {
+            return 0.0;
+        }
+        return IGRAPH_NAN;
+    }
+    return scale * igraph_exp_rand(rng);
+}
+
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *  Copyright (C) 2000      The R Core Team
+ *  Copyright (C) 2003      The R Foundation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, a copy is available at
+ *  http://www.r-project.org/Licenses/
+ *
+ *  SYNOPSIS
+ *
+ *      double dnorm4(double x, double mu, double sigma, int give_log)
+ *            {dnorm (..) is synonymous and preferred inside R}
+ *
+ *  DESCRIPTION
+ *
+ *      Compute the density of the normal distribution.
+ */
+
+double igraph_dnorm(double x, double mu, double sigma, int give_log) {
+#ifdef IEEE_754
+    if (ISNAN(x) || ISNAN(mu) || ISNAN(sigma)) {
+        return x + mu + sigma;
+    }
+#endif
+    if (!R_FINITE(sigma)) {
+        return R_D__0;
+    }
+    if (!R_FINITE(x) && mu == x) {
+        return ML_NAN;    /* x-mu is NaN */
+    }
+    if (sigma <= 0) {
+        if (sigma < 0) {
+            ML_ERR_return_NAN;
+        }
+        /* sigma == 0 */
+        return (x == mu) ? ML_POSINF : R_D__0;
+    }
+    x = (x - mu) / sigma;
+
+    if (!R_FINITE(x)) {
+        return R_D__0;
+    }
+    return (give_log ?
+            -(M_LN_SQRT_2PI  +  0.5 * x * x + log(sigma)) :
+            M_1_SQRT_2PI * exp(-0.5 * x * x)  /   sigma);
+    /* M_1_SQRT_2PI = 1 / sqrt(2 * pi) */
+}
+
+/* This is from nmath/rgamma.c */
+
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *  Copyright (C) 2000--2008 The R Core Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, a copy is available at
+ *  http://www.r-project.org/Licenses/
+ *
+ *  SYNOPSIS
+ *
+ *    #include <Rmath.h>
+ *    double rgamma(double a, double scale);
+ *
+ *  DESCRIPTION
+ *
+ *    Random variates from the gamma distribution.
+ *
+ *  REFERENCES
+ *
+ *    [1] Shape parameter a >= 1.  Algorithm GD in:
+ *
+ *    Ahrens, J.H. and Dieter, U. (1982).
+ *    Generating gamma variates by a modified
+ *    rejection technique.
+ *    Comm. ACM, 25, 47-54.
+ *
+ *
+ *    [2] Shape parameter 0 < a < 1. Algorithm GS in:
+ *
+ *    Ahrens, J.H. and Dieter, U. (1974).
+ *    Computer methods for sampling from gamma, beta,
+ *    poisson and binomial distributions.
+ *    Computing, 12, 223-246.
+ *
+ *    Input: a = parameter (mean) of the standard gamma distribution.
+ *    Output: a variate from the gamma(a)-distribution
+ */
+
+double igraph_rgamma(igraph_rng_t *rng, double a, double scale) {
+    /* Constants : */
+    const static double sqrt32 = 5.656854;
+    const static double exp_m1 = 0.36787944117144232159;/* exp(-1) = 1/e */
+
+    /* Coefficients q[k] - for q0 = sum(q[k]*a^(-k))
+     * Coefficients a[k] - for q = q0+(t*t/2)*sum(a[k]*v^k)
+     * Coefficients e[k] - for exp(q)-1 = sum(e[k]*q^k)
+     */
+    const static double q1 = 0.04166669;
+    const static double q2 = 0.02083148;
+    const static double q3 = 0.00801191;
+    const static double q4 = 0.00144121;
+    const static double q5 = -7.388e-5;
+    const static double q6 = 2.4511e-4;
+    const static double q7 = 2.424e-4;
+
+    const static double a1 = 0.3333333;
+    const static double a2 = -0.250003;
+    const static double a3 = 0.2000062;
+    const static double a4 = -0.1662921;
+    const static double a5 = 0.1423657;
+    const static double a6 = -0.1367177;
+    const static double a7 = 0.1233795;
+
+    /* State variables [FIXME for threading!] :*/
+    static double aa = 0.;
+    static double aaa = 0.;
+    static double s, s2, d;    /* no. 1 (step 1) */
+    static double q0, b, si, c;/* no. 2 (step 4) */
+
+    double e, p, q, r, t, u, v, w, x, ret_val;
+
+    if (!R_FINITE(a) || !R_FINITE(scale) || a < 0.0 || scale <= 0.0) {
+        if (scale == 0.) {
+            return 0.;
+        }
+        ML_ERR_return_NAN;
+    }
+
+    if (a < 1.) { /* GS algorithm for parameters a < 1 */
+        if (a == 0) {
+            return 0.;
+        }
+        e = 1.0 + exp_m1 * a;
+        repeat {
+            p = e * igraph_rng_get_unif01(rng);
+            if (p >= 1.0) {
+                x = -log((e - p) / a);
+                if (igraph_exp_rand(rng) >= (1.0 - a) * log(x)) {
+                    break;
+                }
+            } else {
+                x = exp(log(p) / a);
+                if (igraph_exp_rand(rng) >= x) {
+                    break;
+                }
+            }
+        }
+        return scale * x;
+    }
+
+    /* --- a >= 1 : GD algorithm --- */
+
+    /* Step 1: Recalculations of s2, s, d if a has changed */
+    if (a != aa) {
+        aa = a;
+        s2 = a - 0.5;
+        s = sqrt(s2);
+        d = sqrt32 - s * 12.0;
+    }
+    /* Step 2: t = standard normal deviate,
+               x = (s,1/2) -normal deviate. */
+
+    /* immediate acceptance (i) */
+    t = igraph_norm_rand(rng);
+    x = s + 0.5 * t;
+    ret_val = x * x;
+    if (t >= 0.0) {
+        return scale * ret_val;
+    }
+
+    /* Step 3: u = 0,1 - uniform sample. squeeze acceptance (s) */
+    u = igraph_rng_get_unif01(rng);
+    if (d * u <= t * t * t) {
+        return scale * ret_val;
+    }
+
+    /* Step 4: recalculations of q0, b, si, c if necessary */
+
+    if (a != aaa) {
+        aaa = a;
+        r = 1.0 / a;
+        q0 = ((((((q7 * r + q6) * r + q5) * r + q4) * r + q3) * r
+               + q2) * r + q1) * r;
+
+        /* Approximation depending on size of parameter a */
+        /* The constants in the expressions for b, si and c */
+        /* were established by numerical experiments */
+
+        if (a <= 3.686) {
+            b = 0.463 + s + 0.178 * s2;
+            si = 1.235;
+            c = 0.195 / s - 0.079 + 0.16 * s;
+        } else if (a <= 13.022) {
+            b = 1.654 + 0.0076 * s2;
+            si = 1.68 / s + 0.275;
+            c = 0.062 / s + 0.024;
+        } else {
+            b = 1.77;
+            si = 0.75;
+            c = 0.1515 / s;
+        }
+    }
+    /* Step 5: no quotient test if x not positive */
+
+    if (x > 0.0) {
+        /* Step 6: calculation of v and quotient q */
+        v = t / (s + s);
+        if (fabs(v) <= 0.25)
+            q = q0 + 0.5 * t * t * ((((((a7 * v + a6) * v + a5) * v + a4) * v
+                                      + a3) * v + a2) * v + a1) * v;
+        else {
+            q = q0 - s * t + 0.25 * t * t + (s2 + s2) * log(1.0 + v);
+        }
+
+
+        /* Step 7: quotient acceptance (q) */
+        if (log(1.0 - u) <= q) {
+            return scale * ret_val;
+        }
+    }
+
+    repeat {
+        /* Step 8: e = standard exponential deviate
+         *  u =  0,1 -uniform deviate
+         *  t = (b,si)-double exponential (laplace) sample */
+        e = igraph_exp_rand(rng);
+        u = igraph_rng_get_unif01(rng);
+        u = u + u - 1.0;
+        if (u < 0.0) {
+            t = b - si * e;
+        } else {
+            t = b + si * e;
+        }
+        /* Step  9:  rejection if t < tau(1) = -0.71874483771719 */
+        if (t >= -0.71874483771719) {
+            /* Step 10:  calculation of v and quotient q */
+            v = t / (s + s);
+            if (fabs(v) <= 0.25)
+                q = q0 + 0.5 * t * t *
+                ((((((a7 * v + a6) * v + a5) * v + a4) * v + a3) * v
+                  + a2) * v + a1) * v;
+            else {
+                q = q0 - s * t + 0.25 * t * t + (s2 + s2) * log(1.0 + v);
+            }
+            /* Step 11:  hat acceptance (h) */
+            /* (if q not positive go to step 8) */
+            if (q > 0.0) {
+                w = expm1(q);
+                /*  ^^^^^ original code had approximation with rel.err < 2e-7 */
+                /* if t is rejected sample again at step 8 */
+                if (c * fabs(u) <= w * exp(e - 0.5 * t * t)) {
+                    break;
+                }
+            }
+        }
+    } /* repeat .. until  `t' is accepted */
+    x = s + 0.5 * t;
+    return scale * x * x;
+}
+
+#endif
+
+int igraph_rng_get_dirichlet(igraph_rng_t *rng,
+                             const igraph_vector_t *alpha,
+                             igraph_vector_t *result) {
+
+    igraph_integer_t len = igraph_vector_size(alpha);
+    igraph_integer_t j;
+    igraph_real_t sum = 0.0;
+
+    if (len < 2) {
+        IGRAPH_ERROR("Dirichlet parameter vector too short, must "
+                     "have at least two entries", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_min(alpha) <= 0) {
+        IGRAPH_ERROR("Dirichlet concentration parameters must be positive",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(result, len));
+
+    RNG_BEGIN();
+
+    for (j = 0; j < len; j++) {
+        VECTOR(*result)[j] = igraph_rng_get_gamma(rng, VECTOR(*alpha)[j], 1.0);
+        sum += VECTOR(*result)[j];
+    }
+    for (j = 0; j < len; j++) {
+        VECTOR(*result)[j] /= sum;
+    }
+
+    RNG_END();
+
+    return 0;
+}
+
+/**********************************************************
+ * Testing purposes                                       *
+ *********************************************************/
+
+/* int main() { */
+
+/*   int i; */
+
+/*   RNG_BEGIN(); */
+
+/*   for (i=0; i<1000; i++) { */
+/*     printf("%li ", RNG_INTEGER(1,10)); */
+/*   } */
+/*   printf("\n"); */
+
+/*   for (i=0; i<1000; i++) { */
+/*     printf("%f ", RNG_UNIF(0,1)); */
+/*   } */
+/*   printf("\n"); */
+
+/*   for (i=0; i<1000; i++) { */
+/*     printf("%f ", RNG_NORMAL(0,5)); */
+/*   } */
+/*   printf("\n"); */
+
+/*   RNG_END(); */
+
+/*   return 0; */
+/* } */
diff --git a/igraph/src/random_walk.c b/igraph/src/random_walk.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/random_walk.c
@@ -0,0 +1,287 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_paths.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+#include "igraph_random.h"
+#include "igraph_memory.h"
+#include "igraph_interrupt_internal.h"
+
+/**
+ * \function igraph_random_walk
+ * Perform a random walk on a graph
+ *
+ * Performs a random walk with a given length on a graph, from the given
+ * start vertex. Edge directions are (potentially) considered, depending on
+ * the \p mode argument.
+ *
+ * \param graph The input graph, it can be directed or undirected.
+ *   Multiple edges are respected, so are loop edges.
+ * \param walk An allocated vector, the result is stored here.
+ *   It will be resized as needed.
+ * \param start The start vertex for the walk.
+ * \param steps The number of steps to take. If the random walk gets
+ *   stuck, then the \p stuck argument specifies what happens.
+ * \param mode How to walk along the edges in direted graphs.
+ *   \c IGRAPH_OUT means following edge directions, \c IGRAPH_IN means
+ *   going opposite the edge directions, \c IGRAPH_ALL means ignoring
+ *   edge directions. This argument is ignored for undirected graphs.
+ * \param stuck What to do if the random walk gets stuck.
+ *   \c IGRAPH_RANDOM_WALK_STUCK_RETURN means that the function returns
+ *   with a shorter walk; \c IGRAPH_RANDOM_WALK_STUCK_ERROR means
+ *   that an error is reported. In both cases \p walk is truncated
+ *   to contain the actual interrupted walk.
+ * \return Error code.
+ *
+ * Time complexity: O(l + d), where \c l is the length of the
+ * walk, and \c d is the total degree of the visited nodes.
+ */
+
+
+int igraph_random_walk(const igraph_t *graph, igraph_vector_t *walk,
+                       igraph_integer_t start, igraph_neimode_t mode,
+                       igraph_integer_t steps,
+                       igraph_random_walk_stuck_t stuck) {
+
+    /* TODO:
+       - multiple walks potentially from multiple start vertices
+       - weights
+    */
+
+    igraph_lazy_adjlist_t adj;
+    igraph_integer_t vc = igraph_vcount(graph);
+    igraph_integer_t i;
+
+    if (start < 0 || start >= vc) {
+        IGRAPH_ERROR("Invalid start vertex", IGRAPH_EINVAL);
+    }
+    if (steps < 0) {
+        IGRAPH_ERROR("Invalid number of steps", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adj, mode,
+                                          IGRAPH_DONT_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adj);
+
+    IGRAPH_CHECK(igraph_vector_resize(walk, steps));
+
+    RNG_BEGIN();
+
+    VECTOR(*walk)[0] = start;
+    for (i = 1; i < steps; i++) {
+        igraph_vector_t *neis;
+        igraph_integer_t nn;
+        neis = igraph_lazy_adjlist_get(&adj, start);
+        nn = igraph_vector_size(neis);
+
+        if (IGRAPH_UNLIKELY(nn == 0)) {
+            igraph_vector_resize(walk, i);
+            if (stuck == IGRAPH_RANDOM_WALK_STUCK_RETURN) {
+                break;
+            } else {
+                IGRAPH_ERROR("Random walk got stuck", IGRAPH_ERWSTUCK);
+            }
+        }
+        start = VECTOR(*walk)[i] = VECTOR(*neis)[ RNG_INTEGER(0, nn - 1) ];
+    }
+
+    RNG_END();
+
+    igraph_lazy_adjlist_destroy(&adj);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+
+/* Used as item destructor for 'cdfs' in igraph_random_edge_walk(). */
+static void vec_destr(igraph_vector_t *vec) {
+    if (vec != NULL) {
+        igraph_vector_destroy(vec);
+    }
+}
+
+
+/**
+ * \function igraph_random_edge_walk
+ * \brief Perform a random walk on a graph and return the traversed edges
+ *
+ * Performs a random walk with a given length on a graph, from the given
+ * start vertex. Edge directions are (potentially) considered, depending on
+ * the \p mode argument.
+ *
+ * \param graph The input graph, it can be directed or undirected.
+ *   Multiple edges are respected, so are loop edges.
+ * \param weights A vector of non-negative edge weights.
+ *   It is assumed that at least one strictly positive weight is found among the
+ *   outgoing edges of each vertex.  If it is a NULL pointer, all edges are considered
+ *   to have equal weight.
+ * \param edgewalk An initialized vector; the indices of traversed edges are stored here.
+ *   It will be resized as needed.
+ * \param start The start vertex for the walk.
+ * \param steps The number of steps to take. If the random walk gets
+ *   stuck, then the \p stuck argument specifies what happens.
+ * \param mode How to walk along the edges in direted graphs.
+ *   \c IGRAPH_OUT means following edge directions, \c IGRAPH_IN means
+ *   going opposite the edge directions, \c IGRAPH_ALL means ignoring
+ *   edge directions. This argument is ignored for undirected graphs.
+ * \param stuck What to do if the random walk gets stuck.
+ *   \c IGRAPH_RANDOM_WALK_STUCK_RETURN means that the function returns
+ *   with a shorter walk; \c IGRAPH_RANDOM_WALK_STUCK_ERROR means
+ *   that an error is reported. In both cases, \p edgewalk is truncated
+ *   to contain the actual interrupted walk.
+ *
+ * \return Error code.
+ *
+ */
+int igraph_random_edge_walk(const igraph_t *graph,
+                            const igraph_vector_t *weights,
+                            igraph_vector_t *edgewalk,
+                            igraph_integer_t start, igraph_neimode_t mode,
+                            igraph_integer_t steps,
+                            igraph_random_walk_stuck_t stuck) {
+    igraph_integer_t vc = igraph_vcount(graph);
+    igraph_integer_t ec = igraph_ecount(graph);
+    igraph_integer_t i;
+    igraph_inclist_t il;
+    igraph_vector_t weight_temp;
+    igraph_vector_ptr_t cdfs; /* cumulative distribution vectors for each node, used for weighted choice */
+
+    /* the fourth igraph_neimode_t value, IGRAPH_TOTAL, is disallowed */
+    if (! (mode == IGRAPH_ALL || mode == IGRAPH_IN || mode == IGRAPH_OUT)) {
+        IGRAPH_ERROR("Invalid mode parameter", IGRAPH_EINVMODE);
+    }
+
+    /* ref switch statement at end of main loop */
+    if (! igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    if (start < 0 || start >= vc) {
+        IGRAPH_ERROR("Invalid start vertex", IGRAPH_EINVAL);
+    }
+
+    if (steps < 0) {
+        IGRAPH_ERROR("Invalid number of steps", IGRAPH_EINVAL);
+    }
+
+    if (weights) {
+        if (igraph_vector_size(weights) != ec) {
+            IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+        }
+        if (igraph_vector_min(weights) < 0) {
+            IGRAPH_ERROR("Weights must be non-negative", IGRAPH_EINVAL);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(edgewalk, steps));
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &il, mode));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &il);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&weight_temp, 0);
+
+    /* cdf vectors will be computed lazily */
+    IGRAPH_CHECK(igraph_vector_ptr_init(&cdfs, vc));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &cdfs);
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&cdfs, vec_destr);
+    for (i = 0; i < vc; ++i) {
+        VECTOR(cdfs)[i] = NULL;
+    }
+
+    RNG_BEGIN();
+
+    for (i = 0; i < steps; ++i) {
+        long degree, edge, idx;
+        igraph_vector_int_t *edges = igraph_inclist_get(&il, start);
+
+        degree = igraph_vector_int_size(edges);
+
+        /* are we stuck? */
+        if (IGRAPH_UNLIKELY(degree == 0)) {
+            igraph_vector_resize(edgewalk, i); /* can't fail since size is reduced, skip IGRAPH_CHECK */
+            if (stuck == IGRAPH_RANDOM_WALK_STUCK_RETURN) {
+                break;
+            } else {
+                IGRAPH_ERROR("Random walk got stuck", IGRAPH_ERWSTUCK);
+            }
+        }
+
+        if (weights) { /* weighted: choose an out-edge with probability proportional to its weight */
+            igraph_real_t r;
+            igraph_vector_t **cd = (igraph_vector_t **) & (VECTOR(cdfs)[start]);
+
+            /* compute out-edge cdf for this node if not already done */
+            if (IGRAPH_UNLIKELY(! *cd)) {
+                long j;
+
+                *cd = igraph_malloc(sizeof(igraph_vector_t));
+                if (*cd == NULL) {
+                    IGRAPH_ERROR("random edge walk failed", IGRAPH_ENOMEM);
+                }
+                IGRAPH_CHECK(igraph_vector_init(*cd, degree));
+
+                IGRAPH_CHECK(igraph_vector_resize(&weight_temp, degree));
+                for (j = 0; j < degree; ++j) {
+                    VECTOR(weight_temp)[j] = VECTOR(*weights)[ VECTOR(*edges)[j] ];
+                }
+
+                IGRAPH_CHECK(igraph_vector_cumsum(*cd, &weight_temp));
+            }
+
+            r = RNG_UNIF(0, VECTOR( **cd )[degree - 1]);
+            igraph_vector_binsearch(*cd, r, &idx);
+        } else { /* unweighted: choose an out-edge at random */
+            idx = RNG_INTEGER(0, degree - 1);
+        }
+
+        edge = VECTOR(*edges)[idx];
+        VECTOR(*edgewalk)[i] = edge;
+
+        /* travel along edge in a direction specified by 'mode' */
+        /* note: 'mode' is always set to IGRAPH_ALL for undirected graphs */
+        switch (mode) {
+        case IGRAPH_OUT:
+            start = IGRAPH_TO(graph, edge);
+            break;
+        case IGRAPH_IN:
+            start = IGRAPH_FROM(graph, edge);
+            break;
+        case IGRAPH_ALL:
+            start = IGRAPH_OTHER(graph, edge, start);
+            break;
+        }
+
+        IGRAPH_ALLOW_INTERRUPTION();
+    }
+
+    RNG_END();
+
+    igraph_vector_ptr_destroy_all(&cdfs);
+    igraph_vector_destroy(&weight_temp);
+    igraph_inclist_destroy(&il);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/rdfmt.c b/igraph/src/rdfmt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/rdfmt.c
@@ -0,0 +1,553 @@
+#include "f2c.h"
+#include "fio.h"
+
+#ifdef KR_headers
+extern double atof();
+#define Const /*nothing*/
+#else
+#define Const const
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#endif
+
+#include "fmt.h"
+#include "fp.h"
+#include "ctype.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ static int
+#ifdef KR_headers
+rd_Z(n,w,len) Uint *n; ftnlen len;
+#else
+rd_Z(Uint *n, int w, ftnlen len)
+#endif
+{
+	long x[9];
+	char *s, *s0, *s1, *se, *t;
+	Const char *sc;
+	int ch, i, w1, w2;
+	static char hex[256];
+	static int one = 1;
+	int bad = 0;
+
+	if (!hex['0']) {
+		sc = "0123456789";
+		while(ch = *sc++)
+			hex[ch] = ch - '0' + 1;
+		sc = "ABCDEF";
+		while(ch = *sc++)
+			hex[ch] = hex[ch + 'a' - 'A'] = ch - 'A' + 11;
+		}
+	s = s0 = (char *)x;
+	s1 = (char *)&x[4];
+	se = (char *)&x[8];
+	if (len > 4*sizeof(long))
+		return errno = 117;
+	while (w) {
+		GET(ch);
+		if (ch==',' || ch=='\n')
+			break;
+		w--;
+		if (ch > ' ') {
+			if (!hex[ch & 0xff])
+				bad++;
+			*s++ = ch;
+			if (s == se) {
+				/* discard excess characters */
+				for(t = s0, s = s1; t < s1;)
+					*t++ = *s++;
+				s = s1;
+				}
+			}
+		}
+	if (bad)
+		return errno = 115;
+	w = (int)len;
+	w1 = s - s0;
+	w2 = w1+1 >> 1;
+	t = (char *)n;
+	if (*(char *)&one) {
+		/* little endian */
+		t += w - 1;
+		i = -1;
+		}
+	else
+		i = 1;
+	for(; w > w2; t += i, --w)
+		*t = 0;
+	if (!w)
+		return 0;
+	if (w < w2)
+		s0 = s - (w << 1);
+	else if (w1 & 1) {
+		*t = hex[*s0++ & 0xff] - 1;
+		if (!--w)
+			return 0;
+		t += i;
+		}
+	do {
+		*t = hex[*s0 & 0xff]-1 << 4 | hex[s0[1] & 0xff]-1;
+		t += i;
+		s0 += 2;
+		}
+		while(--w);
+	return 0;
+	}
+
+ static int
+#ifdef KR_headers
+rd_I(n,w,len, base) Uint *n; int w; ftnlen len; register int base;
+#else
+rd_I(Uint *n, int w, ftnlen len, register int base)
+#endif
+{
+	int ch, sign;
+	longint x = 0;
+
+	if (w <= 0)
+		goto have_x;
+	for(;;) {
+		GET(ch);
+		if (ch != ' ')
+			break;
+		if (!--w)
+			goto have_x;
+		}
+	sign = 0;
+	switch(ch) {
+	  case ',':
+	  case '\n':
+		w = 0;
+		goto have_x;
+	  case '-':
+		sign = 1;
+	  case '+':
+		break;
+	  default:
+		if (ch >= '0' && ch <= '9') {
+			x = ch - '0';
+			break;
+			}
+		goto have_x;
+		}
+	while(--w) {
+		GET(ch);
+		if (ch >= '0' && ch <= '9') {
+			x = x*base + ch - '0';
+			continue;
+			}
+		if (ch != ' ') {
+			if (ch == '\n' || ch == ',')
+				w = 0;
+			break;
+			}
+		if (f__cblank)
+			x *= base;
+		}
+	if (sign)
+		x = -x;
+ have_x:
+	if(len == sizeof(integer))
+		n->il=x;
+	else if(len == sizeof(char))
+		n->ic = (char)x;
+#ifdef Allow_TYQUAD
+	else if (len == sizeof(longint))
+		n->ili = x;
+#endif
+	else
+		n->is = (short)x;
+	if (w) {
+		while(--w)
+			GET(ch);
+		return errno = 115;
+		}
+	return 0;
+}
+
+ static int
+#ifdef KR_headers
+rd_L(n,w,len) ftnint *n; ftnlen len;
+#else
+rd_L(ftnint *n, int w, ftnlen len)
+#endif
+{	int ch, dot, lv;
+
+	if (w <= 0)
+		goto bad;
+	for(;;) {
+		GET(ch);
+		--w;
+		if (ch != ' ')
+			break;
+		if (!w)
+			goto bad;
+		}
+	dot = 0;
+ retry:
+	switch(ch) {
+	  case '.':
+		if (dot++ || !w)
+			goto bad;
+		GET(ch);
+		--w;
+		goto retry;
+	  case 't':
+	  case 'T':
+		lv = 1;
+		break;
+	  case 'f':
+	  case 'F':
+		lv = 0;
+		break;
+	  default:
+ bad:
+		for(; w > 0; --w)
+			GET(ch);
+		/* no break */
+	  case ',':
+	  case '\n':
+		return errno = 116;
+		}
+	switch(len) {
+		case sizeof(char):	*(char *)n = (char)lv;	 break;
+		case sizeof(short):	*(short *)n = (short)lv; break;
+		default:		*n = lv;
+		}
+	while(w-- > 0) {
+		GET(ch);
+		if (ch == ',' || ch == '\n')
+			break;
+		}
+	return 0;
+}
+
+ static int
+#ifdef KR_headers
+rd_F(p, w, d, len) ufloat *p; ftnlen len;
+#else
+rd_F(ufloat *p, int w, int d, ftnlen len)
+#endif
+{
+	char s[FMAX+EXPMAXDIGS+4];
+	register int ch;
+	register char *sp, *spe, *sp1;
+	double x;
+	int scale1, se;
+	long e, exp;
+
+	sp1 = sp = s;
+	spe = sp + FMAX;
+	exp = -d;
+	x = 0.;
+
+	do {
+		GET(ch);
+		w--;
+		} while (ch == ' ' && w);
+	switch(ch) {
+		case '-': *sp++ = ch; sp1++; spe++;
+		case '+':
+			if (!w) goto zero;
+			--w;
+			GET(ch);
+		}
+	while(ch == ' ') {
+blankdrop:
+		if (!w--) goto zero; GET(ch); }
+	while(ch == '0')
+		{ if (!w--) goto zero; GET(ch); }
+	if (ch == ' ' && f__cblank)
+		goto blankdrop;
+	scale1 = f__scale;
+	while(isdigit(ch)) {
+digloop1:
+		if (sp < spe) *sp++ = ch;
+		else ++exp;
+digloop1e:
+		if (!w--) goto done;
+		GET(ch);
+		}
+	if (ch == ' ') {
+		if (f__cblank)
+			{ ch = '0'; goto digloop1; }
+		goto digloop1e;
+		}
+	if (ch == '.') {
+		exp += d;
+		if (!w--) goto done;
+		GET(ch);
+		if (sp == sp1) { /* no digits yet */
+			while(ch == '0') {
+skip01:
+				--exp;
+skip0:
+				if (!w--) goto done;
+				GET(ch);
+				}
+			if (ch == ' ') {
+				if (f__cblank) goto skip01;
+				goto skip0;
+				}
+			}
+		while(isdigit(ch)) {
+digloop2:
+			if (sp < spe)
+				{ *sp++ = ch; --exp; }
+digloop2e:
+			if (!w--) goto done;
+			GET(ch);
+			}
+		if (ch == ' ') {
+			if (f__cblank)
+				{ ch = '0'; goto digloop2; }
+			goto digloop2e;
+			}
+		}
+	switch(ch) {
+	  default:
+		break;
+	  case '-': se = 1; goto signonly;
+	  case '+': se = 0; goto signonly;
+	  case 'e':
+	  case 'E':
+	  case 'd':
+	  case 'D':
+		if (!w--)
+			goto bad;
+		GET(ch);
+		while(ch == ' ') {
+			if (!w--)
+				goto bad;
+			GET(ch);
+			}
+		se = 0;
+	  	switch(ch) {
+		  case '-': se = 1;
+		  case '+':
+signonly:
+			if (!w--)
+				goto bad;
+			GET(ch);
+			}
+		while(ch == ' ') {
+			if (!w--)
+				goto bad;
+			GET(ch);
+			}
+		if (!isdigit(ch))
+			goto bad;
+
+		e = ch - '0';
+		for(;;) {
+			if (!w--)
+				{ ch = '\n'; break; }
+			GET(ch);
+			if (!isdigit(ch)) {
+				if (ch == ' ') {
+					if (f__cblank)
+						ch = '0';
+					else continue;
+					}
+				else
+					break;
+				}
+			e = 10*e + ch - '0';
+			if (e > EXPMAX && sp > sp1)
+				goto bad;
+			}
+		if (se)
+			exp -= e;
+		else
+			exp += e;
+		scale1 = 0;
+		}
+	switch(ch) {
+	  case '\n':
+	  case ',':
+		break;
+	  default:
+bad:
+		return (errno = 115);
+		}
+done:
+	if (sp > sp1) {
+		while(*--sp == '0')
+			++exp;
+		if (exp -= scale1)
+			sprintf(sp+1, "e%ld", exp);
+		else
+			sp[1] = 0;
+		x = atof(s);
+		}
+zero:
+	if (len == sizeof(real))
+		p->pf = x;
+	else
+		p->pd = x;
+	return(0);
+	}
+
+
+ static int
+#ifdef KR_headers
+rd_A(p,len) char *p; ftnlen len;
+#else
+rd_A(char *p, ftnlen len)
+#endif
+{	int i,ch;
+	for(i=0;i<len;i++)
+	{	GET(ch);
+		*p++=VAL(ch);
+	}
+	return(0);
+}
+ static int
+#ifdef KR_headers
+rd_AW(p,w,len) char *p; ftnlen len;
+#else
+rd_AW(char *p, int w, ftnlen len)
+#endif
+{	int i,ch;
+	if(w>=len)
+	{	for(i=0;i<w-len;i++)
+			GET(ch);
+		for(i=0;i<len;i++)
+		{	GET(ch);
+			*p++=VAL(ch);
+		}
+		return(0);
+	}
+	for(i=0;i<w;i++)
+	{	GET(ch);
+		*p++=VAL(ch);
+	}
+	for(i=0;i<len-w;i++) *p++=' ';
+	return(0);
+}
+ static int
+#ifdef KR_headers
+rd_H(n,s) char *s;
+#else
+rd_H(int n, char *s)
+#endif
+{	int i,ch;
+	for(i=0;i<n;i++)
+		if((ch=(*f__getn)())<0) return(ch);
+		else *s++ = ch=='\n'?' ':ch;
+	return(1);
+}
+ static int
+#ifdef KR_headers
+rd_POS(s) char *s;
+#else
+rd_POS(char *s)
+#endif
+{	char quote;
+	int ch;
+	quote= *s++;
+	for(;*s;s++)
+		if(*s==quote && *(s+1)!=quote) break;
+		else if((ch=(*f__getn)())<0) return(ch);
+		else *s = ch=='\n'?' ':ch;
+	return(1);
+}
+
+ int
+#ifdef KR_headers
+rd_ed(p,ptr,len) struct syl *p; char *ptr; ftnlen len;
+#else
+rd_ed(struct syl *p, char *ptr, ftnlen len)
+#endif
+{	int ch;
+	for(;f__cursor>0;f__cursor--) if((ch=(*f__getn)())<0) return(ch);
+	if(f__cursor<0)
+	{	if(f__recpos+f__cursor < 0) /*err(elist->cierr,110,"fmt")*/
+			f__cursor = -f__recpos;	/* is this in the standard? */
+		if(f__external == 0) {
+			extern char *f__icptr;
+			f__icptr += f__cursor;
+		}
+		else if(f__curunit && f__curunit->useek)
+			(void) FSEEK(f__cf, f__cursor,SEEK_CUR);
+		else
+			err(f__elist->cierr,106,"fmt");
+		f__recpos += f__cursor;
+		f__cursor=0;
+	}
+	switch(p->op)
+	{
+	default: fprintf(stderr,"rd_ed, unexpected code: %d\n", p->op);
+		sig_die(f__fmtbuf, 1);
+	case IM:
+	case I: ch = rd_I((Uint *)ptr,p->p1,len, 10);
+		break;
+
+		/* O and OM don't work right for character, double, complex, */
+		/* or doublecomplex, and they differ from Fortran 90 in */
+		/* showing a minus sign for negative values. */
+
+	case OM:
+	case O: ch = rd_I((Uint *)ptr, p->p1, len, 8);
+		break;
+	case L: ch = rd_L((ftnint *)ptr,p->p1,len);
+		break;
+	case A:	ch = rd_A(ptr,len);
+		break;
+	case AW:
+		ch = rd_AW(ptr,p->p1,len);
+		break;
+	case E: case EE:
+	case D:
+	case G:
+	case GE:
+	case F:	ch = rd_F((ufloat *)ptr,p->p1,p->p2.i[0],len);
+		break;
+
+		/* Z and ZM assume 8-bit bytes. */
+
+	case ZM:
+	case Z:
+		ch = rd_Z((Uint *)ptr, p->p1, len);
+		break;
+	}
+	if(ch == 0) return(ch);
+	else if(ch == EOF) return(EOF);
+	if (f__cf)
+		clearerr(f__cf);
+	return(errno);
+}
+
+ int
+#ifdef KR_headers
+rd_ned(p) struct syl *p;
+#else
+rd_ned(struct syl *p)
+#endif
+{
+	switch(p->op)
+	{
+	default: fprintf(stderr,"rd_ned, unexpected code: %d\n", p->op);
+		sig_die(f__fmtbuf, 1);
+	case APOS:
+		return(rd_POS(p->p2.s));
+	case H:	return(rd_H(p->p1,p->p2.s));
+	case SLASH: return((*f__donewrec)());
+	case TR:
+	case X:	f__cursor += p->p1;
+		return(1);
+	case T: f__cursor=p->p1-f__recpos - 1;
+		return(1);
+	case TL: f__cursor -= p->p1;
+		if(f__cursor < -f__recpos)	/* TL1000, 1X */
+			f__cursor = -f__recpos;
+		return(1);
+	}
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/reorder.c b/igraph/src/reorder.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/reorder.c
@@ -0,0 +1,425 @@
+
+/*
+ * This file contains the vertex reordering routines.
+ *
+ * Copyright (C) 2002 Sampo Niskanen, Patric Östergård.
+ * Licensed under the GNU GPL, read the file LICENSE for details.
+ */
+
+#include "reorder.h"
+
+#include <stdlib.h>
+
+#include <limits.h>
+
+#include <igraph_random.h>
+
+
+/*
+ * reorder_set()
+ *
+ * Reorders the set s with a function  i -> order[i].
+ *
+ * Note: Assumes that order is the same size as SET_MAX_SIZE(s).
+ */
+void reorder_set(set_t s,int *order) {
+        set_t tmp;
+        int i,j;
+        setelement e;
+
+        ASSERT(reorder_is_bijection(order,SET_MAX_SIZE(s)));
+
+        tmp=set_new(SET_MAX_SIZE(s));
+
+        for (i=0; i<(SET_MAX_SIZE(s)/ELEMENTSIZE); i++) {
+                e=s[i];
+                if (e==0)
+                        continue;
+                for (j=0; j<ELEMENTSIZE; j++) {
+                        if (e&1) {
+                                SET_ADD_ELEMENT(tmp,order[i*ELEMENTSIZE+j]);
+                        }
+                        e = e>>1;
+                }
+        }
+        if (SET_MAX_SIZE(s)%ELEMENTSIZE) {
+                e=s[i];
+                for (j=0; j<(SET_MAX_SIZE(s)%ELEMENTSIZE); j++) {
+                        if (e&1) {
+                                SET_ADD_ELEMENT(tmp,order[i*ELEMENTSIZE+j]);
+                        }
+                        e = e>>1;
+                }
+        }
+        set_copy(s,tmp);
+        set_free(tmp);
+        return;
+}
+
+
+/*
+ * reorder_graph()
+ *
+ * Reorders the vertices in the graph with function  i -> order[i].
+ *
+ * Note: Assumes that order is of size g->n.
+ */
+void reorder_graph(graph_t *g, int *order) {
+        int i;
+        set_t *tmp_e;
+        int *tmp_w;
+
+        ASSERT(reorder_is_bijection(order,g->n));
+
+        tmp_e=malloc(g->n * sizeof(set_t));
+        tmp_w=malloc(g->n * sizeof(int));
+        for (i=0; i<g->n; i++) {
+                reorder_set(g->edges[i],order);
+                tmp_e[order[i]]=g->edges[i];
+                tmp_w[order[i]]=g->weights[i];
+        }
+        for (i=0; i<g->n; i++) {
+                g->edges[i]=tmp_e[i];
+                g->weights[i]=tmp_w[i];
+        }
+        free(tmp_e);
+        free(tmp_w);
+        return;
+}
+
+
+
+/*
+ * reorder_duplicate()
+ *
+ * Returns a newly allocated duplicate of the given ordering.
+ */
+int *reorder_duplicate(int *order,int n) {
+	int *new;
+
+	new=malloc(n*sizeof(int));
+	memcpy(new,order,n*sizeof(int));
+	return new;
+}
+
+/*
+ * reorder_invert()
+ *
+ * Inverts the given ordering so that new[old[i]]==i.
+ *
+ * Note: Asserts that order is a bijection.
+ */
+void reorder_invert(int *order,int n) {
+	int *new;
+	int i;
+
+	ASSERT(reorder_is_bijection(order,n));
+
+	new=malloc(n*sizeof(int));
+	for (i=0; i<n; i++)
+		new[order[i]]=i;
+	for (i=0; i<n; i++)
+		order[i]=new[i];
+	free(new);
+	return;
+}
+
+/*
+ * reorder_reverse()
+ *
+ * Reverses the given ordering so that  new[i] == n-1 - old[i].
+ */
+void reorder_reverse(int *order,int n) {
+	int i;
+
+	for (i=0; i<n; i++)
+		order[i] = n-1 - order[i];
+	return;
+}
+
+/*
+ * reorder_is_bijection
+ *
+ * Checks that an ordering is a bijection {0,...,n-1} -> {0,...,n-1}.
+ *
+ * Returns TRUE if it is a bijection, FALSE otherwise.
+ */
+boolean reorder_is_bijection(int *order,int n) {
+	boolean *used;
+	int i;
+
+	used=calloc(n,sizeof(boolean));
+	for (i=0; i<n; i++) {
+		if (order[i]<0 || order[i]>=n) {
+			free(used);
+			return FALSE;
+		}
+		if (used[order[i]]) {
+			free(used);
+			return FALSE;
+		}
+		used[order[i]]=TRUE;
+	}
+	for (i=0; i<n; i++) {
+		if (!used[i]) {
+			free(used);
+			return FALSE;
+		}
+	}
+	free(used);
+	return TRUE;
+}
+
+/*
+ * reorder_ident()
+ *
+ * Returns a newly allocated identity ordering of size n, ie. order[i]==i.
+ */
+int *reorder_ident(int n) {
+	int i;
+	int *order;
+
+	order=malloc(n*sizeof(int));
+	for (i=0; i<n; i++)
+		order[i]=i;
+	return order;
+}
+
+
+
+/*** Reordering functions for use in clique_options ***/
+
+/*
+ * reorder_by_ident()
+ *
+ * Returns an identity ordering.
+ */
+int *reorder_by_ident(graph_t *g,boolean weighted) {
+	return reorder_ident(g->n);
+}
+
+/*
+ * reorder_by_reverse()
+ *
+ * Returns a reverse identity ordering.
+ */
+int *reorder_by_reverse(graph_t *g,boolean weighted) {
+	int i;
+	int *order;
+
+	order=malloc(g->n * sizeof(int));
+	for (i=0; i < g->n; i++)
+		order[i]=g->n-i-1;
+	return order;
+}
+
+/*
+ * reorder_by_greedy_coloring()
+ *
+ * Equivalent to reorder_by_weighted_greedy_coloring or
+ * reorder_by_unweighted_greedy_coloring according to the value of weighted.
+ */
+int *reorder_by_greedy_coloring(graph_t *g,boolean weighted) {
+	if (weighted)
+		return reorder_by_weighted_greedy_coloring(g,weighted);
+	else
+		return reorder_by_unweighted_greedy_coloring(g,weighted);
+}
+
+
+/*
+ * reorder_by_unweighted_greedy_coloring()
+ *
+ * Returns an ordering for the graph g by coloring the clique one
+ * color at a time, always adding the vertex of largest degree within
+ * the uncolored graph, and numbering these vertices 0, 1, ...
+ *
+ * Experimentally efficient for use with unweighted graphs.
+ */
+int *reorder_by_unweighted_greedy_coloring(graph_t *g,boolean weighted) {
+	int i,j,v;
+	boolean *tmp_used;
+	int *degree;   /* -1 for used vertices */
+	int *order;
+	int maxdegree,maxvertex=0;
+	boolean samecolor;
+
+	tmp_used=calloc(g->n,sizeof(boolean));
+	degree=calloc(g->n,sizeof(int));
+	order=calloc(g->n,sizeof(int));
+
+	for (i=0; i < g->n; i++) {
+		for (j=0; j < g->n; j++) {
+			ASSERT(!((i==j) && GRAPH_IS_EDGE(g,i,j)));
+			if (GRAPH_IS_EDGE(g,i,j))
+				degree[i]++;
+		}
+	}
+
+	v=0;
+	while (v < g->n) {
+		/* Reset tmp_used. */
+		memset(tmp_used,0,g->n * sizeof(boolean));
+
+		do {
+			/* Find vertex to be colored. */
+			maxdegree=0;
+			samecolor=FALSE;
+			for (i=0; i < g->n; i++) {
+				if (!tmp_used[i] && degree[i] >= maxdegree) {
+					maxvertex=i;
+					maxdegree=degree[i];
+					samecolor=TRUE;
+				}
+			}
+			if (samecolor) {
+				order[v]=maxvertex;
+				degree[maxvertex]=-1;
+				v++;
+
+				/* Mark neighbors not to color with same
+				 * color and update neighbor degrees. */
+				for (i=0; i < g->n; i++) {
+					if (GRAPH_IS_EDGE(g,maxvertex,i)) {
+						tmp_used[i]=TRUE;
+						degree[i]--;
+					}
+				}
+			}
+		} while (samecolor);
+	}
+
+	free(tmp_used);
+	free(degree);
+	return order;
+}
+
+/*
+ * reorder_by_weighted_greedy_coloring()
+ *
+ * Returns an ordering for the graph g by coloring the clique one
+ * color at a time, always adding the vertex that (in order of importance):
+ *  1. has the minimum weight in the remaining graph
+ *  2. has the largest sum of weights surrounding the vertex
+ *
+ * Experimentally efficient for use with weighted graphs.
+ */
+int *reorder_by_weighted_greedy_coloring(graph_t *g, boolean weighted) {
+	int i,j,p=0;
+	int cnt;
+	int *nwt;    /* Sum of surrounding vertices' weights */
+	int min_wt,max_nwt;
+	boolean *used;
+	int *order;
+	
+	nwt=malloc(g->n * sizeof(int));
+	order=malloc(g->n * sizeof(int));
+	used=calloc(g->n,sizeof(boolean));
+	
+	for (i=0; i < g->n; i++) {
+		nwt[i]=0;
+		for (j=0; j < g->n; j++)
+			if (GRAPH_IS_EDGE(g, i, j))
+				nwt[i] += g->weights[j];
+	}
+
+	for (cnt=0; cnt < g->n; cnt++) {
+		min_wt=INT_MAX;
+		max_nwt=-1;
+		for (i=g->n-1; i>=0; i--)
+			if ((!used[i]) && (g->weights[i] < min_wt))
+				min_wt=g->weights[i];
+		for (i=g->n-1; i>=0; i--) {
+			if (used[i] || (g->weights[i] > min_wt))
+				continue;
+			if (nwt[i] > max_nwt) {
+				max_nwt=nwt[i];
+				p=i;
+			}
+		}
+		order[cnt]=p;
+		used[p]=TRUE;
+		for (j=0; j < g->n; j++)
+			if ((!used[j]) && (GRAPH_IS_EDGE(g, p, j)))
+				nwt[j] -= g->weights[p];
+	}
+
+	free(nwt);
+	free(used);
+
+	ASSERT(reorder_is_bijection(order,g->n));
+
+	return order;
+}
+
+/*
+ * reorder_by_degree()
+ *
+ * Returns a reordering of the graph g so that the vertices with largest
+ * degrees (most neighbors) are first.
+ */
+int *reorder_by_degree(graph_t *g, boolean weighted) {
+	int i,j,v;
+	int *degree;
+	int *order;
+	int maxdegree,maxvertex=0;
+
+	degree=calloc(g->n,sizeof(int));
+	order=calloc(g->n,sizeof(int));
+
+	for (i=0; i < g->n; i++) {
+		for (j=0; j < g->n; j++) {
+			ASSERT(!((i==j) && GRAPH_IS_EDGE(g,i,j)));
+			if (GRAPH_IS_EDGE(g,i,j))
+				degree[i]++;
+		}
+	}
+
+	for (v=0; v < g->n; v++) {
+		maxdegree=0;
+		for (i=0; i < g->n; i++) {
+			if (degree[i] >= maxdegree) {
+				maxvertex=i;
+				maxdegree=degree[i];
+			}
+		}
+		order[v]=maxvertex;
+		degree[maxvertex]=-1;  /* used */
+/*** Max. degree withing unselected graph:
+		for (i=0; i < g->n; i++) {
+			if (GRAPH_IS_EDGE(g,maxvertex,i))
+				degree[i]--;
+		}
+***/
+	}
+
+	free(degree);
+	return order;
+}
+
+/*
+ * reorder_by_random()
+ *
+ * Returns a random reordering for graph g.
+ * Note: Used the functions rand() and srand() to generate the random
+ *       numbers.  srand() is re-initialized every time reorder_by_random()
+ *       is called using the system time.
+ */
+int *reorder_by_random(graph_t *g, boolean weighted) {
+	int i,r;
+	int *new;
+	boolean *used;
+
+	new=calloc(g->n, sizeof(int));
+	used=calloc(g->n, sizeof(boolean));
+	for (i=0; i < g->n; i++) {
+		do {
+            r = igraph_rng_get_integer(igraph_rng_default(), 0, g->n - 1);
+		} while (used[r]);
+		new[i]=r;
+		used[r]=TRUE;
+	}
+	free(used);
+	return new;
+}
+
diff --git a/igraph/src/rewind.c b/igraph/src/rewind.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/rewind.c
@@ -0,0 +1,30 @@
+#include "f2c.h"
+#include "fio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef KR_headers
+integer f_rew(a) alist *a;
+#else
+integer f_rew(alist *a)
+#endif
+{
+	unit *b;
+	if(a->aunit>=MXUNIT || a->aunit<0)
+		err(a->aerr,101,"rewind");
+	b = &f__units[a->aunit];
+	if(b->ufd == NULL || b->uwrt == 3)
+		return(0);
+	if(!b->useek)
+		err(a->aerr,106,"rewind")
+	if(b->uwrt) {
+		(void) t_runc(a);
+		b->uwrt = 3;
+		}
+	rewind(b->ufd);
+	b->uend=0;
+	return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/rsfe.c b/igraph/src/rsfe.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/rsfe.c
@@ -0,0 +1,91 @@
+/* read sequential formatted external */
+#include "f2c.h"
+#include "fio.h"
+#include "fmt.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ int
+xrd_SL(Void)
+{	int ch;
+	if(!f__curunit->uend)
+		while((ch=getc(f__cf))!='\n')
+			if (ch == EOF) {
+				f__curunit->uend = 1;
+				break;
+				}
+	f__cursor=f__recpos=0;
+	return(1);
+}
+
+ int
+x_getc(Void)
+{	int ch;
+	if(f__curunit->uend) return(EOF);
+	ch = getc(f__cf);
+	if(ch!=EOF && ch!='\n')
+	{	f__recpos++;
+		return(ch);
+	}
+	if(ch=='\n')
+	{	(void) ungetc(ch,f__cf);
+		return(ch);
+	}
+	if(f__curunit->uend || feof(f__cf))
+	{	errno=0;
+		f__curunit->uend=1;
+		return(-1);
+	}
+	return(-1);
+}
+
+ int
+x_endp(Void)
+{
+	xrd_SL();
+	return f__curunit->uend == 1 ? EOF : 0;
+}
+
+ int
+x_rev(Void)
+{
+	(void) xrd_SL();
+	return(0);
+}
+#ifdef KR_headers
+integer s_rsfe(a) cilist *a; /* start */
+#else
+integer s_rsfe(cilist *a) /* start */
+#endif
+{	int n;
+	if(!f__init) f_init();
+	f__reading=1;
+	f__sequential=1;
+	f__formatted=1;
+	f__external=1;
+	if(n=c_sfe(a)) return(n);
+	f__elist=a;
+	f__cursor=f__recpos=0;
+	f__scale=0;
+	f__fmtbuf=a->cifmt;
+	f__cf=f__curunit->ufd;
+	if(pars_f(f__fmtbuf)<0) err(a->cierr,100,"startio");
+	f__getn= x_getc;
+	f__doed= rd_ed;
+	f__doned= rd_ned;
+	fmt_bg();
+	f__doend=x_endp;
+	f__donewrec=xrd_SL;
+	f__dorevert=x_rev;
+	f__cblank=f__curunit->ublnk;
+	f__cplus=0;
+	if(f__curunit->uwrt && f__nowreading(f__curunit))
+		err(a->cierr,errno,"read start");
+	if(f__curunit->uend)
+		err(f__elist->ciend,(EOF),"read start");
+	return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/rsli.c b/igraph/src/rsli.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/rsli.c
@@ -0,0 +1,109 @@
+#include "f2c.h"
+#include "fio.h"
+#include "lio.h"
+#include "fmt.h" /* for f__doend */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern flag f__lquit;
+extern int f__lcount;
+extern char *f__icptr;
+extern char *f__icend;
+extern icilist *f__svic;
+extern int f__icnum, f__recpos;
+
+static int i_getc(Void)
+{
+	if(f__recpos >= f__svic->icirlen) {
+		if (f__recpos++ == f__svic->icirlen)
+			return '\n';
+		z_rnew();
+		}
+	f__recpos++;
+	if(f__icptr >= f__icend)
+		return EOF;
+	return(*f__icptr++);
+	}
+
+ static
+#ifdef KR_headers
+int i_ungetc(ch, f) int ch; FILE *f;
+#else
+int i_ungetc(int ch, FILE *f)
+#endif
+{
+	if (--f__recpos == f__svic->icirlen)
+		return '\n';
+	if (f__recpos < -1)
+		err(f__svic->icierr,110,"recend");
+	/* *--icptr == ch, and icptr may point to read-only memory */
+	return *--f__icptr /* = ch */;
+	}
+
+ static void
+#ifdef KR_headers
+c_lir(a) icilist *a;
+#else
+c_lir(icilist *a)
+#endif
+{
+	extern int l_eof;
+	f__reading = 1;
+	f__external = 0;
+	f__formatted = 1;
+	f__svic = a;
+	L_len = a->icirlen;
+	f__recpos = -1;
+	f__icnum = f__recpos = 0;
+	f__cursor = 0;
+	l_getc = i_getc;
+	l_ungetc = i_ungetc;
+	l_eof = 0;
+	f__icptr = a->iciunit;
+	f__icend = f__icptr + a->icirlen*a->icirnum;
+	f__cf = 0;
+	f__curunit = 0;
+	f__elist = (cilist *)a;
+	}
+
+
+#ifdef KR_headers
+integer s_rsli(a) icilist *a;
+#else
+integer s_rsli(icilist *a)
+#endif
+{
+	f__lioproc = l_read;
+	f__lquit = 0;
+	f__lcount = 0;
+	c_lir(a);
+	f__doend = 0;
+	return(0);
+	}
+
+integer e_rsli(Void)
+{ return 0; }
+
+#ifdef KR_headers
+integer s_rsni(a) icilist *a;
+#else
+extern int x_rsne(cilist*);
+
+integer s_rsni(icilist *a)
+#endif
+{
+	extern int nml_read;
+	integer rv;
+	cilist ca;
+	ca.ciend = a->iciend;
+	ca.cierr = a->icierr;
+	ca.cifmt = a->icifmt;
+	c_lir(a);
+	rv = x_rsne(&ca);
+	nml_read = 0;
+	return rv;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/rsne.c b/igraph/src/rsne.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/rsne.c
@@ -0,0 +1,618 @@
+#include "f2c.h"
+#include "fio.h"
+#include "lio.h"
+
+#define MAX_NL_CACHE 3	/* maximum number of namelist hash tables to cache */
+#define MAXDIM 20	/* maximum number of subscripts */
+
+ struct dimen {
+	ftnlen extent;
+	ftnlen curval;
+	ftnlen delta;
+	ftnlen stride;
+	};
+ typedef struct dimen dimen;
+
+ struct hashentry {
+	struct hashentry *next;
+	char *name;
+	Vardesc *vd;
+	};
+ typedef struct hashentry hashentry;
+
+ struct hashtab {
+	struct hashtab *next;
+	Namelist *nl;
+	int htsize;
+	hashentry *tab[1];
+	};
+ typedef struct hashtab hashtab;
+
+ static hashtab *nl_cache;
+ static int n_nlcache;
+ static hashentry **zot;
+ static int colonseen;
+ extern ftnlen f__typesize[];
+
+ extern flag f__lquit;
+ extern int f__lcount, nml_read;
+ extern int t_getc(Void);
+
+#ifdef KR_headers
+ extern char *malloc(), *memset();
+#define Const /*nothing*/
+
+#ifdef ungetc
+ static int
+un_getc(x,f__cf) int x; FILE *f__cf;
+{ return ungetc(x,f__cf); }
+#else
+#define un_getc ungetc
+ extern int ungetc();
+#endif
+
+#else
+#define Const const
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#include "string.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef ungetc
+ static int
+un_getc(int x, FILE *f__cf)
+{ return ungetc(x,f__cf); }
+#else
+#define un_getc ungetc
+extern int ungetc(int, FILE*);	/* for systems with a buggy stdio.h */
+#endif
+#endif
+
+ static Vardesc *
+#ifdef KR_headers
+hash(ht, s) hashtab *ht; register char *s;
+#else
+hash(hashtab *ht, register char *s)
+#endif
+{
+	register int c, x;
+	register hashentry *h;
+	char *s0 = s;
+
+	for(x = 0; c = *s++; x = x & 0x4000 ? ((x << 1) & 0x7fff) + 1 : x << 1)
+		x += c;
+	for(h = *(zot = ht->tab + x % ht->htsize); h; h = h->next)
+		if (!strcmp(s0, h->name))
+			return h->vd;
+	return 0;
+	}
+
+ hashtab *
+#ifdef KR_headers
+mk_hashtab(nl) Namelist *nl;
+#else
+mk_hashtab(Namelist *nl)
+#endif
+{
+	int nht, nv;
+	hashtab *ht;
+	Vardesc *v, **vd, **vde;
+	hashentry *he;
+
+	hashtab **x, **x0, *y;
+	for(x = &nl_cache; y = *x; x0 = x, x = &y->next)
+		if (nl == y->nl)
+			return y;
+	if (n_nlcache >= MAX_NL_CACHE) {
+		/* discard least recently used namelist hash table */
+		y = *x0;
+		free((char *)y->next);
+		y->next = 0;
+		}
+	else
+		n_nlcache++;
+	nv = nl->nvars;
+	if (nv >= 0x4000)
+		nht = 0x7fff;
+	else {
+		for(nht = 1; nht < nv; nht <<= 1);
+		nht += nht - 1;
+		}
+	ht = (hashtab *)malloc(sizeof(hashtab) + (nht-1)*sizeof(hashentry *)
+				+ nv*sizeof(hashentry));
+	if (!ht)
+		return 0;
+	he = (hashentry *)&ht->tab[nht];
+	ht->nl = nl;
+	ht->htsize = nht;
+	ht->next = nl_cache;
+	nl_cache = ht;
+	memset((char *)ht->tab, 0, nht*sizeof(hashentry *));
+	vd = nl->vars;
+	vde = vd + nv;
+	while(vd < vde) {
+		v = *vd++;
+		if (!hash(ht, v->name)) {
+			he->next = *zot;
+			*zot = he;
+			he->name = v->name;
+			he->vd = v;
+			he++;
+			}
+		}
+	return ht;
+	}
+
+static char Alpha[256], Alphanum[256];
+
+ static VOID
+nl_init(Void) {
+	Const char *s;
+	int c;
+
+	if(!f__init)
+		f_init();
+	for(s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; c = *s++; )
+		Alpha[c]
+		= Alphanum[c]
+		= Alpha[c + 'a' - 'A']
+		= Alphanum[c + 'a' - 'A']
+		= c;
+	for(s = "0123456789_"; c = *s++; )
+		Alphanum[c] = c;
+	}
+
+#define GETC(x) (x=(*l_getc)())
+#define Ungetc(x,y) (*l_ungetc)(x,y)
+
+ static int
+#ifdef KR_headers
+getname(s, slen) register char *s; int slen;
+#else
+getname(register char *s, int slen)
+#endif
+{
+	register char *se = s + slen - 1;
+	register int ch;
+
+	GETC(ch);
+	if (!(*s++ = Alpha[ch & 0xff])) {
+		if (ch != EOF)
+			ch = 115;
+		errfl(f__elist->cierr, ch, "namelist read");
+		}
+	while(*s = Alphanum[GETC(ch) & 0xff])
+		if (s < se)
+			s++;
+	if (ch == EOF)
+		err(f__elist->cierr, EOF, "namelist read");
+	if (ch > ' ')
+		Ungetc(ch,f__cf);
+	return *s = 0;
+	}
+
+ static int
+#ifdef KR_headers
+getnum(chp, val) int *chp; ftnlen *val;
+#else
+getnum(int *chp, ftnlen *val)
+#endif
+{
+	register int ch, sign;
+	register ftnlen x;
+
+	while(GETC(ch) <= ' ' && ch >= 0);
+	if (ch == '-') {
+		sign = 1;
+		GETC(ch);
+		}
+	else {
+		sign = 0;
+		if (ch == '+')
+			GETC(ch);
+		}
+	x = ch - '0';
+	if (x < 0 || x > 9)
+		return 115;
+	while(GETC(ch) >= '0' && ch <= '9')
+		x = 10*x + ch - '0';
+	while(ch <= ' ' && ch >= 0)
+		GETC(ch);
+	if (ch == EOF)
+		return EOF;
+	*val = sign ? -x : x;
+	*chp = ch;
+	return 0;
+	}
+
+ static int
+#ifdef KR_headers
+getdimen(chp, d, delta, extent, x1)
+ int *chp; dimen *d; ftnlen delta, extent, *x1;
+#else
+getdimen(int *chp, dimen *d, ftnlen delta, ftnlen extent, ftnlen *x1)
+#endif
+{
+	register int k;
+	ftnlen x2, x3;
+
+	if (k = getnum(chp, x1))
+		return k;
+	x3 = 1;
+	if (*chp == ':') {
+		if (k = getnum(chp, &x2))
+			return k;
+		x2 -= *x1;
+		if (*chp == ':') {
+			if (k = getnum(chp, &x3))
+				return k;
+			if (!x3)
+				return 123;
+			x2 /= x3;
+			colonseen = 1;
+			}
+		if (x2 < 0 || x2 >= extent)
+			return 123;
+		d->extent = x2 + 1;
+		}
+	else
+		d->extent = 1;
+	d->curval = 0;
+	d->delta = delta;
+	d->stride = x3;
+	return 0;
+	}
+
+#ifndef No_Namelist_Questions
+ static Void
+#ifdef KR_headers
+print_ne(a) cilist *a;
+#else
+print_ne(cilist *a)
+#endif
+{
+	flag intext = f__external;
+	int rpsave = f__recpos;
+	FILE *cfsave = f__cf;
+	unit *usave = f__curunit;
+	cilist t;
+	t = *a;
+	t.ciunit = 6;
+	s_wsne(&t);
+	fflush(f__cf);
+	f__external = intext;
+	f__reading = 1;
+	f__recpos = rpsave;
+	f__cf = cfsave;
+	f__curunit = usave;
+	f__elist = a;
+	}
+#endif
+
+ static char where0[] = "namelist read start ";
+
+ int
+#ifdef KR_headers
+x_rsne(a) cilist *a;
+#else
+x_rsne(cilist *a)
+#endif
+{
+	int ch, got1, k, n, nd, quote, readall;
+	Namelist *nl;
+	static char where[] = "namelist read";
+	char buf[64];
+	hashtab *ht;
+	Vardesc *v;
+	dimen *dn, *dn0, *dn1;
+	ftnlen *dims, *dims1;
+	ftnlen b, b0, b1, ex, no, nomax, size, span;
+	ftnint no1, no2, type;
+	char *vaddr;
+	long iva, ivae;
+	dimen dimens[MAXDIM], substr;
+
+	if (!Alpha['a'])
+		nl_init();
+	f__reading=1;
+	f__formatted=1;
+	got1 = 0;
+ top:
+	for(;;) switch(GETC(ch)) {
+		case EOF:
+ eof:
+			err(a->ciend,(EOF),where0);
+		case '&':
+		case '$':
+			goto have_amp;
+#ifndef No_Namelist_Questions
+		case '?':
+			print_ne(a);
+			continue;
+#endif
+		default:
+			if (ch <= ' ' && ch >= 0)
+				continue;
+#ifndef No_Namelist_Comments
+			while(GETC(ch) != '\n')
+				if (ch == EOF)
+					goto eof;
+#else
+			errfl(a->cierr, 115, where0);
+#endif
+		}
+ have_amp:
+	if (ch = getname(buf,sizeof(buf)))
+		return ch;
+	nl = (Namelist *)a->cifmt;
+	if (strcmp(buf, nl->name))
+#ifdef No_Bad_Namelist_Skip
+		errfl(a->cierr, 118, where0);
+#else
+	{
+		fprintf(stderr,
+			"Skipping namelist \"%s\": seeking namelist \"%s\".\n",
+			buf, nl->name);
+		fflush(stderr);
+		for(;;) switch(GETC(ch)) {
+			case EOF:
+				err(a->ciend, EOF, where0);
+			case '/':
+			case '&':
+			case '$':
+				if (f__external)
+					e_rsle();
+				else
+					z_rnew();
+				goto top;
+			case '"':
+			case '\'':
+				quote = ch;
+ more_quoted:
+				while(GETC(ch) != quote)
+					if (ch == EOF)
+						err(a->ciend, EOF, where0);
+				if (GETC(ch) == quote)
+					goto more_quoted;
+				Ungetc(ch,f__cf);
+			default:
+				continue;
+			}
+		}
+#endif
+	ht = mk_hashtab(nl);
+	if (!ht)
+		errfl(f__elist->cierr, 113, where0);
+	for(;;) {
+		for(;;) switch(GETC(ch)) {
+			case EOF:
+				if (got1)
+					return 0;
+				err(a->ciend, EOF, where0);
+			case '/':
+			case '$':
+			case '&':
+				return 0;
+			default:
+				if (ch <= ' ' && ch >= 0 || ch == ',')
+					continue;
+				Ungetc(ch,f__cf);
+				if (ch = getname(buf,sizeof(buf)))
+					return ch;
+				goto havename;
+			}
+ havename:
+		v = hash(ht,buf);
+		if (!v)
+			errfl(a->cierr, 119, where);
+		while(GETC(ch) <= ' ' && ch >= 0);
+		vaddr = v->addr;
+		type = v->type;
+		if (type < 0) {
+			size = -type;
+			type = TYCHAR;
+			}
+		else
+			size = f__typesize[type];
+		ivae = size;
+		iva = readall = 0;
+		if (ch == '(' /*)*/ ) {
+			dn = dimens;
+			if (!(dims = v->dims)) {
+				if (type != TYCHAR)
+					errfl(a->cierr, 122, where);
+				if (k = getdimen(&ch, dn, (ftnlen)size,
+						(ftnlen)size, &b))
+					errfl(a->cierr, k, where);
+				if (ch != ')')
+					errfl(a->cierr, 115, where);
+				b1 = dn->extent;
+				if (--b < 0 || b + b1 > size)
+					return 124;
+				iva += b;
+				size = b1;
+				while(GETC(ch) <= ' ' && ch >= 0);
+				goto scalar;
+				}
+			nd = (int)dims[0];
+			nomax = span = dims[1];
+			ivae = iva + size*nomax;
+			colonseen = 0;
+			if (k = getdimen(&ch, dn, size, nomax, &b))
+				errfl(a->cierr, k, where);
+			no = dn->extent;
+			b0 = dims[2];
+			dims1 = dims += 3;
+			ex = 1;
+			for(n = 1; n++ < nd; dims++) {
+				if (ch != ',')
+					errfl(a->cierr, 115, where);
+				dn1 = dn + 1;
+				span /= *dims;
+				if (k = getdimen(&ch, dn1, dn->delta**dims,
+						span, &b1))
+					errfl(a->cierr, k, where);
+				ex *= *dims;
+				b += b1*ex;
+				no *= dn1->extent;
+				dn = dn1;
+				}
+			if (ch != ')')
+				errfl(a->cierr, 115, where);
+			readall = 1 - colonseen;
+			b -= b0;
+			if (b < 0 || b >= nomax)
+				errfl(a->cierr, 125, where);
+			iva += size * b;
+			dims = dims1;
+			while(GETC(ch) <= ' ' && ch >= 0);
+			no1 = 1;
+			dn0 = dimens;
+			if (type == TYCHAR && ch == '(' /*)*/) {
+				if (k = getdimen(&ch, &substr, size, size, &b))
+					errfl(a->cierr, k, where);
+				if (ch != ')')
+					errfl(a->cierr, 115, where);
+				b1 = substr.extent;
+				if (--b < 0 || b + b1 > size)
+					return 124;
+				iva += b;
+				b0 = size;
+				size = b1;
+				while(GETC(ch) <= ' ' && ch >= 0);
+				if (b1 < b0)
+					goto delta_adj;
+				}
+			if (readall)
+				goto delta_adj;
+			for(; dn0 < dn; dn0++) {
+				if (dn0->extent != *dims++ || dn0->stride != 1)
+					break;
+				no1 *= dn0->extent;
+				}
+			if (dn0 == dimens && dimens[0].stride == 1) {
+				no1 = dimens[0].extent;
+				dn0++;
+				}
+ delta_adj:
+			ex = 0;
+			for(dn1 = dn0; dn1 <= dn; dn1++)
+				ex += (dn1->extent-1)
+					* (dn1->delta *= dn1->stride);
+			for(dn1 = dn; dn1 > dn0; dn1--) {
+				ex -= (dn1->extent - 1) * dn1->delta;
+				dn1->delta -= ex;
+				}
+			}
+		else if (dims = v->dims) {
+			no = no1 = dims[1];
+			ivae = iva + no*size;
+			}
+		else
+ scalar:
+			no = no1 = 1;
+		if (ch != '=')
+			errfl(a->cierr, 115, where);
+		got1 = nml_read = 1;
+		f__lcount = 0;
+	 readloop:
+		for(;;) {
+			if (iva >= ivae || iva < 0) {
+				f__lquit = 1;
+				goto mustend;
+				}
+			else if (iva + no1*size > ivae)
+				no1 = (ivae - iva)/size;
+			f__lquit = 0;
+			if (k = l_read(&no1, vaddr + iva, size, type))
+				return k;
+			if (f__lquit == 1)
+				return 0;
+			if (readall) {
+				iva += dn0->delta;
+				if (f__lcount > 0) {
+					no2 = (ivae - iva)/size;
+					if (no2 > f__lcount)
+						no2 = f__lcount;
+					if (k = l_read(&no2, vaddr + iva,
+							size, type))
+						return k;
+					iva += no2 * dn0->delta;
+					}
+				}
+ mustend:
+			GETC(ch);
+			if (readall)
+				if (iva >= ivae)
+					readall = 0;
+				else for(;;) {
+					switch(ch) {
+						case ' ':
+						case '\t':
+						case '\n':
+							GETC(ch);
+							continue;
+						}
+					break;
+					}
+			if (ch == '/' || ch == '$' || ch == '&') {
+				f__lquit = 1;
+				return 0;
+				}
+			else if (f__lquit) {
+				while(ch <= ' ' && ch >= 0)
+					GETC(ch);
+				Ungetc(ch,f__cf);
+				if (!Alpha[ch & 0xff] && ch >= 0)
+					errfl(a->cierr, 125, where);
+				break;
+				}
+			Ungetc(ch,f__cf);
+			if (readall && !Alpha[ch & 0xff])
+				goto readloop;
+			if ((no -= no1) <= 0)
+				break;
+			for(dn1 = dn0; dn1 <= dn; dn1++) {
+				if (++dn1->curval < dn1->extent) {
+					iva += dn1->delta;
+					goto readloop;
+					}
+				dn1->curval = 0;
+				}
+			break;
+			}
+		}
+	}
+
+ integer
+#ifdef KR_headers
+s_rsne(a) cilist *a;
+#else
+s_rsne(cilist *a)
+#endif
+{
+	extern int l_eof;
+	int n;
+
+	f__external=1;
+	l_eof = 0;
+	if(n = c_le(a))
+		return n;
+	if(f__curunit->uwrt && f__nowreading(f__curunit))
+		err(a->cierr,errno,where0);
+	l_getc = t_getc;
+	l_ungetc = un_getc;
+	f__doend = xrd_SL;
+	n = x_rsne(a);
+	nml_read = 0;
+	if (n)
+		return n;
+	return e_rsle();
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/s_cat.c b/igraph/src/s_cat.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/s_cat.c
@@ -0,0 +1,86 @@
+/* Unless compiled with -DNO_OVERWRITE, this variant of s_cat allows the
+ * target of a concatenation to appear on its right-hand side (contrary
+ * to the Fortran 77 Standard, but in accordance with Fortran 90).
+ */
+
+#include "f2c.h"
+#ifndef NO_OVERWRITE
+#include "stdio.h"
+#undef abs
+#ifdef KR_headers
+ extern char *F77_aloc();
+ extern void free();
+ extern void exit_();
+#else
+#undef min
+#undef max
+#include "stdlib.h"
+extern
+#ifdef __cplusplus
+	"C"
+#endif
+	char *F77_aloc(ftnlen, const char*);
+#endif
+#include "string.h"
+#endif /* NO_OVERWRITE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ VOID
+#ifdef KR_headers
+s_cat(lp, rpp, rnp, np, ll) char *lp, *rpp[]; ftnint rnp[], *np; ftnlen ll;
+#else
+s_cat(char *lp, char *rpp[], ftnint rnp[], ftnint *np, ftnlen ll)
+#endif
+{
+	ftnlen i, nc;
+	char *rp;
+	ftnlen n = *np;
+#ifndef NO_OVERWRITE
+	ftnlen L, m;
+	char *lp0, *lp1;
+
+	lp0 = 0;
+	lp1 = lp;
+	L = ll;
+	i = 0;
+	while(i < n) {
+		rp = rpp[i];
+		m = rnp[i++];
+		if (rp >= lp1 || rp + m <= lp) {
+			if ((L -= m) <= 0) {
+				n = i;
+				break;
+				}
+			lp1 += m;
+			continue;
+			}
+		lp0 = lp;
+		lp = lp1 = F77_aloc(L = ll, "s_cat");
+		break;
+		}
+	lp1 = lp;
+#endif /* NO_OVERWRITE */
+	for(i = 0 ; i < n ; ++i) {
+		nc = ll;
+		if(rnp[i] < nc)
+			nc = rnp[i];
+		ll -= nc;
+		rp = rpp[i];
+		while(--nc >= 0)
+			*lp++ = *rp++;
+		}
+	while(--ll >= 0)
+		*lp++ = ' ';
+#ifndef NO_OVERWRITE
+	if (lp0) {
+		memcpy(lp0, lp1, L);
+		free(lp1);
+		}
+#endif
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/s_cmp.c b/igraph/src/s_cmp.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/s_cmp.c
@@ -0,0 +1,50 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* compare two strings */
+
+#ifdef KR_headers
+integer s_cmp(a0, b0, la, lb) char *a0, *b0; ftnlen la, lb;
+#else
+integer s_cmp(char *a0, char *b0, ftnlen la, ftnlen lb)
+#endif
+{
+register unsigned char *a, *aend, *b, *bend;
+a = (unsigned char *)a0;
+b = (unsigned char *)b0;
+aend = a + la;
+bend = b + lb;
+
+if(la <= lb)
+	{
+	while(a < aend)
+		if(*a != *b)
+			return( *a - *b );
+		else
+			{ ++a; ++b; }
+
+	while(b < bend)
+		if(*b != ' ')
+			return( ' ' - *b );
+		else	++b;
+	}
+
+else
+	{
+	while(b < bend)
+		if(*a == *b)
+			{ ++a; ++b; }
+		else
+			return( *a - *b );
+	while(a < aend)
+		if(*a != ' ')
+			return(*a - ' ');
+		else	++a;
+	}
+return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/s_copy.c b/igraph/src/s_copy.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/s_copy.c
@@ -0,0 +1,57 @@
+/* Unless compiled with -DNO_OVERWRITE, this variant of s_copy allows the
+ * target of an assignment to appear on its right-hand side (contrary
+ * to the Fortran 77 Standard, but in accordance with Fortran 90),
+ * as in  a(2:5) = a(4:7) .
+ */
+
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* assign strings:  a = b */
+
+#ifdef KR_headers
+VOID s_copy(a, b, la, lb) register char *a, *b; ftnlen la, lb;
+#else
+void s_copy(register char *a, register char *b, ftnlen la, ftnlen lb)
+#endif
+{
+	register char *aend, *bend;
+
+	aend = a + la;
+
+	if(la <= lb)
+#ifndef NO_OVERWRITE
+		if (a <= b || a >= b + la)
+#endif
+			while(a < aend)
+				*a++ = *b++;
+#ifndef NO_OVERWRITE
+		else
+			for(b += la; a < aend; )
+				*--aend = *--b;
+#endif
+
+	else {
+		bend = b + lb;
+#ifndef NO_OVERWRITE
+		if (a <= b || a >= bend)
+#endif
+			while(b < bend)
+				*a++ = *b++;
+#ifndef NO_OVERWRITE
+		else {
+			a += lb;
+			while(b < bend)
+				*--a = *--bend;
+			a += lb;
+			}
+#endif
+		while(a < aend)
+			*a++ = ' ';
+		}
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/s_paus.c b/igraph/src/s_paus.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/s_paus.c
@@ -0,0 +1,96 @@
+#include "stdio.h"
+#include "f2c.h"
+#define PAUSESIG 15
+
+#include "signal1.h"
+#ifdef KR_headers
+#define Void /* void */
+#define Int /* int */
+#else
+#define Void void
+#define Int int
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int getpid(void), isatty(int), pause(void);
+#endif
+
+extern VOID f_exit(Void);
+
+#ifndef MSDOS
+ static VOID
+waitpause(Sigarg)
+{	Use_Sigarg;
+	return;
+	}
+#endif
+
+ static VOID
+#ifdef KR_headers
+s_1paus(fin) FILE *fin;
+#else
+s_1paus(FILE *fin)
+#endif
+{
+	fprintf(stderr,
+	"To resume execution, type go.  Other input will terminate the job.\n");
+	fflush(stderr);
+	if( getc(fin)!='g' || getc(fin)!='o' || getc(fin)!='\n' ) {
+		fprintf(stderr, "STOP\n");
+#ifdef NO_ONEXIT
+		f_exit();
+#endif
+		exit(0);
+		}
+	}
+
+ int
+#ifdef KR_headers
+s_paus(s, n) char *s; ftnlen n;
+#else
+s_paus(char *s, ftnlen n)
+#endif
+{
+	fprintf(stderr, "PAUSE ");
+	if(n > 0)
+		fprintf(stderr, " %.*s", (int)n, s);
+	fprintf(stderr, " statement executed\n");
+	if( isatty(fileno(stdin)) )
+		s_1paus(stdin);
+	else {
+#ifdef MSDOS
+		FILE *fin;
+		fin = fopen("con", "r");
+		if (!fin) {
+			fprintf(stderr, "s_paus: can't open con!\n");
+			fflush(stderr);
+			exit(1);
+			}
+		s_1paus(fin);
+		fclose(fin);
+#else
+		fprintf(stderr,
+		"To resume execution, execute a   kill -%d %d   command\n",
+			PAUSESIG, getpid() );
+		signal1(PAUSESIG, waitpause);
+		fflush(stderr);
+		pause();
+#endif
+		}
+	fprintf(stderr, "Execution resumes after PAUSE.\n");
+	fflush(stderr);
+	return 0; /* NOT REACHED */
+#ifdef __cplusplus
+	}
+#endif
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/s_rnge.c b/igraph/src/s_rnge.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/s_rnge.c
@@ -0,0 +1,32 @@
+#include "stdio.h"
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* called when a subscript is out of range */
+
+#ifdef KR_headers
+extern VOID sig_die();
+integer s_rnge(varn, offset, procn, line) char *varn, *procn; ftnint offset, line;
+#else
+extern VOID sig_die(const char*,int);
+integer s_rnge(char *varn, ftnint offset, char *procn, ftnint line)
+#endif
+{
+register int i;
+
+fprintf(stderr, "Subscript out of range on file line %ld, procedure ",
+	(long)line);
+while((i = *procn) && i != '_' && i != ' ')
+	putc(*procn++, stderr);
+fprintf(stderr, ".\nAttempt to access the %ld-th element of variable ",
+	(long)offset+1);
+while((i = *varn) && i != ' ')
+	putc(*varn++, stderr);
+sig_die(".", 1);
+return 0;	/* not reached */
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/s_stop.c b/igraph/src/s_stop.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/s_stop.c
@@ -0,0 +1,48 @@
+#include "stdio.h"
+#include "f2c.h"
+
+#ifdef KR_headers
+extern void f_exit();
+int s_stop(s, n) char *s; ftnlen n;
+#else
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+void f_exit(void);
+
+int s_stop(char *s, ftnlen n)
+#endif
+{
+int i;
+
+if(n > 0)
+	{
+	fprintf(stderr, "STOP ");
+	for(i = 0; i<n ; ++i)
+		putc(*s++, stderr);
+	fprintf(stderr, " statement executed\n");
+	}
+#ifdef NO_ONEXIT
+f_exit();
+#endif
+exit(0);
+
+/* We cannot avoid (useless) compiler diagnostics here:		*/
+/* some compilers complain if there is no return statement,	*/
+/* and others complain that this one cannot be reached.		*/
+
+return 0; /* NOT REACHED */
+}
+#ifdef __cplusplus
+}
+#endif
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/sbm.c b/igraph/src/sbm.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/sbm.c
@@ -0,0 +1,607 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph R library.
+   Copyright (C) 2003-2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_interface.h"
+#include "igraph_vector.h"
+#include "igraph_matrix.h"
+#include "igraph_random.h"
+#include "igraph_constructors.h"
+#include "igraph_games.h"
+
+#include <float.h>      /* for DBL_EPSILON */
+#include <math.h>       /* for sqrt */
+
+/**
+ * \function igraph_sbm_game
+ * Sample from a stochastic block model
+ *
+ * This function samples graphs from a stochastic block
+ * model by (doing the equivalent of) Bernoulli
+ * trials for each potential edge with the probabilities
+ * given by the Bernoulli rate matrix, \p pref_matrix.
+ * See Faust, K., &amp; Wasserman, S. (1992a). Blockmodels:
+ * Interpretation and evaluation. Social Networks, 14, 5-–61.
+ *
+ * </para><para>
+ * The order of the vertex ids in the generated graph corresponds to
+ * the \p block_sizes argument.
+ *
+ * \param graph The output graph.
+ * \param n Number of vertices.
+ * \param pref_matrix The matrix giving the Bernoulli rates.
+ *     This is a KxK matrix, where K is the number of groups.
+ *     The probability of creating an edge between vertices from
+ *     groups i and j is given by element (i,j).
+ * \param block_sizes An integer vector giving the number of
+ *     vertices in each group.
+ * \param directed Boolean, whether to create a directed graph. If
+ *     this argument is false, then \p pref_matrix must be symmetric.
+ * \param loops Boolean, whether to create self-loops.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|+K^2), where |V| is the number of
+ * vertices, |E| is the number of edges, and K is the number of
+ * groups.
+ *
+ * \sa \ref igraph_erdos_renyi_game() for a simple Bernoulli graph.
+ *
+ */
+
+int igraph_sbm_game(igraph_t *graph, igraph_integer_t n,
+                    const igraph_matrix_t *pref_matrix,
+                    const igraph_vector_int_t *block_sizes,
+                    igraph_bool_t directed, igraph_bool_t loops) {
+
+    int no_blocks = igraph_matrix_nrow(pref_matrix);
+    int from, to, fromoff = 0;
+    igraph_real_t minp, maxp;
+    igraph_vector_t edges;
+
+    /* ------------------------------------------------------------ */
+    /* Check arguments                                              */
+    /* ------------------------------------------------------------ */
+
+    if (igraph_matrix_ncol(pref_matrix) != no_blocks) {
+        IGRAPH_ERROR("Preference matrix is not square",
+                     IGRAPH_NONSQUARE);
+    }
+
+    igraph_matrix_minmax(pref_matrix, &minp, &maxp);
+    if (minp < 0 || maxp > 1) {
+        IGRAPH_ERROR("Connection probabilities must in [0,1]", IGRAPH_EINVAL);
+    }
+
+    if (n < 0) {
+        IGRAPH_ERROR("Number of vertices must be non-negative", IGRAPH_EINVAL);
+    }
+
+    if (!directed && !igraph_matrix_is_symmetric(pref_matrix)) {
+        IGRAPH_ERROR("Preference matrix must be symmetric for undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_int_size(block_sizes) != no_blocks) {
+        IGRAPH_ERROR("Invalid block size vector length", IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_int_min(block_sizes) < 0) {
+        IGRAPH_ERROR("Block size must be non-negative", IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_int_sum(block_sizes) != n) {
+        IGRAPH_ERROR("Block sizes must sum up to number of vertices",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    RNG_BEGIN();
+
+    for (from = 0; from < no_blocks; from++) {
+        double fromsize = VECTOR(*block_sizes)[from];
+        int start = directed ? 0 : from;
+        int i, tooff = 0;
+        for (i = 0; i < start; i++) {
+            tooff += VECTOR(*block_sizes)[i];
+        }
+        for (to = start; to < no_blocks; to++) {
+            double tosize = VECTOR(*block_sizes)[to];
+            igraph_real_t prob = MATRIX(*pref_matrix, from, to);
+            double maxedges, last = RNG_GEOM(prob);
+            if (directed && loops) {
+                maxedges = fromsize * tosize;
+                while (last < maxedges) {
+                    int vto = floor(last / fromsize);
+                    int vfrom = last - (igraph_real_t)vto * fromsize;
+                    igraph_vector_push_back(&edges, fromoff + vfrom);
+                    igraph_vector_push_back(&edges, tooff + vto);
+                    last += RNG_GEOM(prob);
+                    last += 1;
+                }
+            } else if (directed && !loops && from != to) {
+                maxedges = fromsize * tosize;
+                while (last < maxedges) {
+                    int vto = floor(last / fromsize);
+                    int vfrom = last - (igraph_real_t)vto * fromsize;
+                    igraph_vector_push_back(&edges, fromoff + vfrom);
+                    igraph_vector_push_back(&edges, tooff + vto);
+                    last += RNG_GEOM(prob);
+                    last += 1;
+                }
+            } else if (directed && !loops && from == to) {
+                maxedges = fromsize * (fromsize - 1);
+                while (last < maxedges) {
+                    int vto = floor(last / fromsize);
+                    int vfrom = last - (igraph_real_t)vto * fromsize;
+                    if (vfrom == vto) {
+                        vto = fromsize - 1;
+                    }
+                    igraph_vector_push_back(&edges, fromoff + vfrom);
+                    igraph_vector_push_back(&edges, tooff + vto);
+                    last += RNG_GEOM(prob);
+                    last += 1;
+                }
+            } else if (!directed && loops && from != to) {
+                maxedges = fromsize * tosize;
+                while (last < maxedges) {
+                    int vto = floor(last / fromsize);
+                    int vfrom = last - (igraph_real_t)vto * fromsize;
+                    igraph_vector_push_back(&edges, fromoff + vfrom);
+                    igraph_vector_push_back(&edges, tooff + vto);
+                    last += RNG_GEOM(prob);
+                    last += 1;
+                }
+            } else if (!directed && loops && from == to) {
+                maxedges = fromsize * (fromsize + 1) / 2.0;
+                while (last < maxedges) {
+                    long int vto = floor((sqrt(8 * last + 1) - 1) / 2);
+                    long int vfrom = last - (((igraph_real_t)vto) * (vto + 1)) / 2;
+                    igraph_vector_push_back(&edges, fromoff + vfrom);
+                    igraph_vector_push_back(&edges, tooff + vto);
+                    last += RNG_GEOM(prob);
+                    last += 1;
+                }
+            } else if (!directed && !loops && from != to) {
+                maxedges = fromsize * tosize;
+                while (last < maxedges) {
+                    int vto = floor(last / fromsize);
+                    int vfrom = last - (igraph_real_t)vto * fromsize;
+                    igraph_vector_push_back(&edges, fromoff + vfrom);
+                    igraph_vector_push_back(&edges, tooff + vto);
+                    last += RNG_GEOM(prob);
+                    last += 1;
+                }
+            } else { /*!directed && !loops && from==to */
+                maxedges = fromsize * (fromsize - 1) / 2.0;
+                while (last < maxedges) {
+                    int vto = floor((sqrt(8 * last + 1) + 1) / 2);
+                    int vfrom = last - (((igraph_real_t)vto) * (vto - 1)) / 2;
+                    igraph_vector_push_back(&edges, fromoff + vfrom);
+                    igraph_vector_push_back(&edges, tooff + vto);
+                    last += RNG_GEOM(prob);
+                    last += 1;
+                }
+            }
+
+            tooff += tosize;
+        }
+        fromoff += fromsize;
+    }
+
+    RNG_END();
+
+    igraph_create(graph, &edges, n, directed);
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_hsbm_game
+ * Hierarchical stochastic block model
+ *
+ * The function generates a random graph according to the hierarchical
+ * stochastic block model.
+ *
+ * \param graph The generated graph is stored here.
+ * \param n The number of vertices in the graph.
+ * \param m The number of vertices per block. n/m must be integer.
+ * \param rho The fraction of vertices per cluster,
+ *        within a block. Must sum up to 1, and rho * m must be integer
+ *        for all elements of rho.
+ * \param C A square, symmetric numeric matrix, the Bernoulli rates for
+ *        the clusters within a block. Its size must mach the size of the
+ *        \code{rho} vector.
+ * \param p The Bernoulli rate of connections between
+ *        vertices in different blocks.
+ * \return Error code.
+ *
+ * \sa \ref igraph_sbm_game() for the classic stochastic block model,
+ * \ref igraph_hsbm_list_game() for a more general version.
+ */
+
+int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n,
+                     igraph_integer_t m, const igraph_vector_t *rho,
+                     const igraph_matrix_t *C, igraph_real_t p) {
+
+    int b, i, k = igraph_vector_size(rho);
+    igraph_vector_t csizes;
+    igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON);
+    int no_blocks = n / m;
+    igraph_vector_t edges;
+    int offset = 0;
+
+    if (n < 1) {
+        IGRAPH_ERROR("`n' must be positive for HSBM", IGRAPH_EINVAL);
+    }
+    if (m < 1) {
+        IGRAPH_ERROR("`m' must be positive for HSBM", IGRAPH_EINVAL);
+    }
+    if ((long) n  % (long) m) {
+        IGRAPH_ERROR("`n' must be a multiple of `m' for HSBM", IGRAPH_EINVAL);
+    }
+    if (!igraph_vector_isininterval(rho, 0, 1)) {
+        IGRAPH_ERROR("`rho' must be between zero and one for HSBM",
+                     IGRAPH_EINVAL);
+    }
+    if (igraph_matrix_min(C) < 0 || igraph_matrix_max(C) > 1) {
+        IGRAPH_ERROR("`C' must be between zero and one for HSBM", IGRAPH_EINVAL);
+    }
+    if (fabs(igraph_vector_sum(rho) - 1.0) > sq_dbl_epsilon) {
+        IGRAPH_ERROR("`rho' must sum up to 1 for HSBM", IGRAPH_EINVAL);
+    }
+    if (igraph_matrix_nrow(C) != k || igraph_matrix_ncol(C) != k) {
+        IGRAPH_ERROR("`C' dimensions must match `rho' dimensions in HSBM",
+                     IGRAPH_EINVAL);
+    }
+    if (!igraph_matrix_is_symmetric(C)) {
+        IGRAPH_ERROR("`C' must be a symmetric matrix", IGRAPH_EINVAL);
+    }
+    if (p < 0 || p > 1) {
+        IGRAPH_ERROR("`p' must be a probability for HSBM", IGRAPH_EINVAL);
+    }
+    for (i = 0; i < k; i++) {
+        igraph_real_t s = VECTOR(*rho)[i] * m;
+        if (fabs(round(s) - s) > sq_dbl_epsilon) {
+            IGRAPH_ERROR("`rho' * `m' is not integer in HSBM", IGRAPH_EINVAL);
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&csizes, k);
+    for (i = 0; i < k; i++) {
+        VECTOR(csizes)[i] = round(VECTOR(*rho)[i] * m);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    RNG_BEGIN();
+
+    /* Block models first */
+
+    for (b = 0; b < no_blocks; b++) {
+        int from, to, fromoff = 0;
+
+        for (from = 0; from < k; from++) {
+            int fromsize = VECTOR(csizes)[from];
+            int i, tooff = 0;
+            for (i = 0; i < from; i++) {
+                tooff += VECTOR(csizes)[i];
+            }
+            for (to = from; to < k; to++) {
+                int tosize = VECTOR(csizes)[to];
+                igraph_real_t prob = MATRIX(*C, from, to);
+                igraph_real_t maxedges;
+                igraph_real_t last = RNG_GEOM(prob);
+                if (from != to) {
+                    maxedges = fromsize * tosize;
+                    while (last < maxedges) {
+                        int vto = floor(last / fromsize);
+                        int vfrom = last - (igraph_real_t)vto * fromsize;
+                        igraph_vector_push_back(&edges, offset + fromoff + vfrom);
+                        igraph_vector_push_back(&edges, offset + tooff + vto);
+                        last += RNG_GEOM(prob);
+                        last += 1;
+                    }
+                } else { /* from==to */
+                    maxedges = fromsize * (fromsize - 1) / 2.0;
+                    while (last < maxedges) {
+                        int vto = floor((sqrt(8 * last + 1) + 1) / 2);
+                        int vfrom = last - (((igraph_real_t)vto) * (vto - 1)) / 2;
+                        igraph_vector_push_back(&edges, offset + fromoff + vfrom);
+                        igraph_vector_push_back(&edges, offset + tooff + vto);
+                        last += RNG_GEOM(prob);
+                        last += 1;
+                    }
+                }
+
+                tooff += tosize;
+            }
+            fromoff += fromsize;
+        }
+
+        offset += m;
+    }
+
+    /* And now the rest, if not a special case */
+
+    if (p == 1) {
+        int fromoff = 0, tooff = m;
+        for (b = 0; b < no_blocks; b++) {
+            igraph_real_t fromsize = m;
+            igraph_real_t tosize = n - tooff;
+            int from, to;
+            for (from = 0; from < fromsize; from++) {
+                for (to = 0; to < tosize; to++) {
+                    igraph_vector_push_back(&edges, fromoff + from);
+                    igraph_vector_push_back(&edges, tooff + to);
+                }
+            }
+            fromoff += m;
+            tooff += m;
+        }
+    } else if (p > 0) {
+        int fromoff = 0, tooff = m;
+        for (b = 0; b < no_blocks; b++) {
+            igraph_real_t fromsize = m;
+            igraph_real_t tosize = n - tooff;
+            igraph_real_t maxedges = fromsize * tosize;
+            igraph_real_t last = RNG_GEOM(p);
+            while (last < maxedges) {
+                int vto = floor(last / fromsize);
+                int vfrom = last - (igraph_real_t) vto * fromsize;
+                igraph_vector_push_back(&edges, fromoff + vfrom);
+                igraph_vector_push_back(&edges, tooff + vto);
+                last += RNG_GEOM(p);
+                last += 1;
+            }
+
+            fromoff += m;
+            tooff += m;
+        }
+    }
+
+    RNG_END();
+
+    igraph_create(graph, &edges, n, /*directed=*/ 0);
+
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&csizes);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_hsbm_list_game
+ * Hierarchical stochastic block model, more general version
+ *
+ * The function generates a random graph according to the hierarchical
+ * stochastic block model.
+ *
+ * \param graph The generated graph is stored here.
+ * \param n The number of vertices in the graph.
+ * \param mlist An integer vector of block sizes.
+ * \param rholist A list of rho vectors (\c igraph_vector_t objects), one
+ *        for each block.
+ * \param Clist A list of square matrices (\c igraph_matrix_t objects),
+ *        one for each block, giving the Bernoulli rates of connections
+ *        within the block.
+ * \param p The Bernoulli rate of connections between
+ *        vertices in different blocks.
+ * \return Error code.
+ *
+ * \sa \ref igraph_sbm_game() for the classic stochastic block model,
+ * \ref igraph_hsbm_game() for a simpler general version.
+ */
+
+int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n,
+                          const igraph_vector_int_t *mlist,
+                          const igraph_vector_ptr_t *rholist,
+                          const igraph_vector_ptr_t *Clist,
+                          igraph_real_t p) {
+
+    int i, no_blocks = igraph_vector_ptr_size(rholist);
+    igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON);
+    igraph_vector_t csizes, edges;
+    int b, offset = 0;
+
+    if (n < 1) {
+        IGRAPH_ERROR("`n' must be positive for HSBM", IGRAPH_EINVAL);
+    }
+    if (no_blocks == 0) {
+        IGRAPH_ERROR("`rholist' empty for HSBM", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_ptr_size(Clist) != no_blocks &&
+        igraph_vector_int_size(mlist) != no_blocks) {
+        IGRAPH_ERROR("`rholist' must have same length as `Clist' and `m' "
+                     "for HSBM", IGRAPH_EINVAL);
+    }
+    if (p < 0 || p > 1) {
+        IGRAPH_ERROR("`p' must be a probability for HSBM", IGRAPH_EINVAL);
+    }
+    /* Checks for m's */
+    if (igraph_vector_int_sum(mlist) != n) {
+        IGRAPH_ERROR("`m' must sum up to `n' for HSBM", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_int_min(mlist) < 1) {
+        IGRAPH_ERROR("`m' must be positive for HSBM", IGRAPH_EINVAL);
+    }
+    /* Checks for the rhos */
+    for (i = 0; i < no_blocks; i++) {
+        const igraph_vector_t *rho = VECTOR(*rholist)[i];
+        if (!igraph_vector_isininterval(rho, 0, 1)) {
+            IGRAPH_ERROR("`rho' must be between zero and one for HSBM",
+                         IGRAPH_EINVAL);
+        }
+        if (fabs(igraph_vector_sum(rho) - 1.0) > sq_dbl_epsilon) {
+            IGRAPH_ERROR("`rho' must sum up to 1 for HSBM", IGRAPH_EINVAL);
+        }
+    }
+    /* Checks for the Cs */
+    for (i = 0; i < no_blocks; i++) {
+        const igraph_matrix_t *C = VECTOR(*Clist)[i];
+        if (igraph_matrix_min(C) < 0 || igraph_matrix_max(C) > 1) {
+            IGRAPH_ERROR("`C' must be between zero and one for HSBM",
+                         IGRAPH_EINVAL);
+        }
+        if (!igraph_matrix_is_symmetric(C)) {
+            IGRAPH_ERROR("`C' must be a symmetric matrix", IGRAPH_EINVAL);
+        }
+    }
+    /* Check that C and rho sizes match */
+    for (i = 0; i < no_blocks; i++) {
+        const igraph_vector_t *rho = VECTOR(*rholist)[i];
+        const igraph_matrix_t *C = VECTOR(*Clist)[i];
+        int k = igraph_vector_size(rho);
+        if (igraph_matrix_nrow(C) != k || igraph_matrix_ncol(C) != k) {
+            IGRAPH_ERROR("`C' dimensions must match `rho' dimensions in HSBM",
+                         IGRAPH_EINVAL);
+        }
+    }
+    /* Check that rho * m is integer */
+    for (i = 0; i < no_blocks; i++) {
+        const igraph_vector_t *rho = VECTOR(*rholist)[i];
+        igraph_real_t m = VECTOR(*mlist)[i];
+        int j, k = igraph_vector_size(rho);
+        for (j = 0; j < k; j++) {
+            igraph_real_t s = VECTOR(*rho)[j] * m;
+            if (fabs(round(s) - s) > sq_dbl_epsilon) {
+                IGRAPH_ERROR("`rho' * `m' is not integer in HSBM", IGRAPH_EINVAL);
+            }
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&csizes, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    RNG_BEGIN();
+
+    /* Block models first */
+
+    for (b = 0; b < no_blocks; b++) {
+        int from, to, fromoff = 0;
+        const igraph_vector_t *rho = VECTOR(*rholist)[b];
+        const igraph_matrix_t *C = VECTOR(*Clist)[b];
+        igraph_real_t m = VECTOR(*mlist)[b];
+        int k = igraph_vector_size(rho);
+
+        igraph_vector_resize(&csizes, k);
+        for (i = 0; i < k; i++) {
+            VECTOR(csizes)[i] = round(VECTOR(*rho)[i] * m);
+        }
+
+        for (from = 0; from < k; from++) {
+            int fromsize = VECTOR(csizes)[from];
+            int i, tooff = 0;
+            for (i = 0; i < from; i++) {
+                tooff += VECTOR(csizes)[i];
+            }
+            for (to = from; to < k; to++) {
+                int tosize = VECTOR(csizes)[to];
+                igraph_real_t prob = MATRIX(*C, from, to);
+                igraph_real_t maxedges;
+                igraph_real_t last = RNG_GEOM(prob);
+                if (from != to) {
+                    maxedges = fromsize * tosize;
+                    while (last < maxedges) {
+                        int vto = floor(last / fromsize);
+                        int vfrom = last - (igraph_real_t)vto * fromsize;
+                        igraph_vector_push_back(&edges, offset + fromoff + vfrom);
+                        igraph_vector_push_back(&edges, offset + tooff + vto);
+                        last += RNG_GEOM(prob);
+                        last += 1;
+                    }
+                } else { /* from==to */
+                    maxedges = fromsize * (fromsize - 1) / 2.0;
+                    while (last < maxedges) {
+                        int vto = floor((sqrt(8 * last + 1) + 1) / 2);
+                        int vfrom = last - (((igraph_real_t)vto) * (vto - 1)) / 2;
+                        igraph_vector_push_back(&edges, offset + fromoff + vfrom);
+                        igraph_vector_push_back(&edges, offset + tooff + vto);
+                        last += RNG_GEOM(prob);
+                        last += 1;
+                    }
+                }
+
+                tooff += tosize;
+            }
+            fromoff += fromsize;
+        }
+
+        offset += m;
+    }
+
+    /* And now the rest, if not a special case */
+
+    if (p == 1) {
+        int fromoff = 0, tooff = VECTOR(*mlist)[0];
+        for (b = 0; b < no_blocks; b++) {
+            igraph_real_t fromsize = VECTOR(*mlist)[b];
+            igraph_real_t tosize = n - tooff;
+            int from, to;
+            for (from = 0; from < fromsize; from++) {
+                for (to = 0; to < tosize; to++) {
+                    igraph_vector_push_back(&edges, fromoff + from);
+                    igraph_vector_push_back(&edges, tooff + to);
+                }
+            }
+            fromoff += fromsize;
+            if (b + 1 < no_blocks) {
+                tooff += VECTOR(*mlist)[b + 1];
+            }
+        }
+    } else if (p > 0) {
+        int fromoff = 0, tooff = VECTOR(*mlist)[0];
+        for (b = 0; b < no_blocks; b++) {
+            igraph_real_t fromsize = VECTOR(*mlist)[b];
+            igraph_real_t tosize = n - tooff;
+            igraph_real_t maxedges = fromsize * tosize;
+            igraph_real_t last = RNG_GEOM(p);
+            while (last < maxedges) {
+                int vto = floor(last / fromsize);
+                int vfrom = last - (igraph_real_t) vto * fromsize;
+                igraph_vector_push_back(&edges, fromoff + vfrom);
+                igraph_vector_push_back(&edges, tooff + vto);
+                last += RNG_GEOM(p);
+                last += 1;
+            }
+
+            fromoff += fromsize;
+            if (b + 1 < no_blocks) {
+                tooff += VECTOR(*mlist)[b + 1];
+            }
+        }
+    }
+
+    RNG_END();
+
+    igraph_create(graph, &edges, n, /*directed=*/ 0);
+
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&csizes);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
diff --git a/igraph/src/scan.c b/igraph/src/scan.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/scan.c
@@ -0,0 +1,880 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2013  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_scan.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_memory.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_arpack.h"
+#include "igraph_eigen.h"
+#include "igraph_centrality.h"
+#include "igraph_operators.h"
+#include "igraph_dqueue.h"
+#include "igraph_stack.h"
+
+/**
+ * \section about_local_scan
+ *
+ * <para>
+ * The scan statistic is a summary of the locality statistics that is computed
+ * from the local neighborhood of each vertex. For details, see
+ * Priebe, C. E., Conroy, J. M., Marchette, D. J., Park, Y. (2005).
+ * Scan Statistics on Enron Graphs. Computational and Mathematical Organization Theory.
+ * </para>
+ */
+
+/**
+ * \function igraph_local_scan_0
+ * Local scan-statistics, k=0
+ *
+ * K=0 scan-statistics is arbitrarily defined as the vertex degree for
+ * unweighted, and the vertex strength for weighted graphs. See \ref
+ * igraph_degree() and \ref igraph_strength().
+ *
+ * \param graph The input graph
+ * \param res An initialized vector, the results are stored here.
+ * \param weights Weight vector for weighted graphs, null pointer for
+ *        unweighted graphs.
+ * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing,
+ *        \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges.
+ * \return Error code.
+ *
+ */
+
+int igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res,
+                        const igraph_vector_t *weights,
+                        igraph_neimode_t mode) {
+    if (weights) {
+        igraph_strength(graph, res, igraph_vss_all(), mode, /*loops=*/ 1,
+                        weights);
+    } else {
+        igraph_degree(graph, res, igraph_vss_all(), mode, /*loops=*/ 1);
+    }
+    return 0;
+}
+
+/* From triangles.c */
+
+int igraph_i_trans4_al_simplify(igraph_adjlist_t *al,
+                                const igraph_vector_int_t *rank);
+
+/* This removes loop, multiple edges and edges that point
+   "backwards" according to the rank vector. It works on
+   edge lists */
+
+int igraph_i_trans4_il_simplify(const igraph_t *graph, igraph_inclist_t *il,
+                                const igraph_vector_int_t *rank) {
+
+    long int i;
+    long int n = il->length;
+    igraph_vector_int_t mark;
+    igraph_vector_int_init(&mark, n);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &mark);
+
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &il->incs[i];
+        int j, l = igraph_vector_int_size(v);
+        int irank = VECTOR(*rank)[i];
+        VECTOR(mark)[i] = i + 1;
+        for (j = 0; j < l; /* nothing */) {
+            long int edge = (long int) VECTOR(*v)[j];
+            long int e = IGRAPH_OTHER(graph, edge, i);
+            if (VECTOR(*rank)[e] > irank && VECTOR(mark)[e] != i + 1) {
+                VECTOR(mark)[e] = i + 1;
+                j++;
+            } else {
+                VECTOR(*v)[j] = igraph_vector_int_tail(v);
+                igraph_vector_int_pop_back(v);
+                l--;
+            }
+        }
+    }
+
+    igraph_vector_int_destroy(&mark);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+
+}
+
+/* This one handles both weighted and unweighted cases */
+
+int igraph_i_local_scan_1_directed(const igraph_t *graph,
+                                   igraph_vector_t *res,
+                                   const igraph_vector_t *weights,
+                                   igraph_neimode_t mode) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    igraph_inclist_t incs;
+    int i, node;
+
+    igraph_vector_int_t neis;
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &incs, mode));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &incs);
+
+    igraph_vector_int_init(&neis, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &neis);
+
+    igraph_vector_resize(res, no_of_nodes);
+    igraph_vector_null(res);
+
+    for (node = 0; node < no_of_nodes; node++) {
+        igraph_vector_int_t *edges1 = igraph_inclist_get(&incs, node);
+        int edgeslen1 = igraph_vector_int_size(edges1);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /* Mark neighbors and self*/
+        VECTOR(neis)[node] = node + 1;
+        for (i = 0; i < edgeslen1; i++) {
+            int e = VECTOR(*edges1)[i];
+            int nei = IGRAPH_OTHER(graph, e, node);
+            igraph_real_t w = weights ? VECTOR(*weights)[e] : 1;
+            VECTOR(neis)[nei] = node + 1;
+            VECTOR(*res)[node] += w;
+        }
+
+        /* Crawl neighbors */
+        for (i = 0; i < edgeslen1; i++) {
+            int e2 = VECTOR(*edges1)[i];
+            int nei = IGRAPH_OTHER(graph, e2, node);
+            igraph_vector_int_t *edges2 = igraph_inclist_get(&incs, nei);
+            int j, edgeslen2 = igraph_vector_int_size(edges2);
+            for (j = 0; j < edgeslen2; j++) {
+                int e2 = VECTOR(*edges2)[j];
+                int nei2 = IGRAPH_OTHER(graph, e2, nei);
+                igraph_real_t w2 = weights ? VECTOR(*weights)[e2] : 1;
+                if (VECTOR(neis)[nei2] == node + 1) {
+                    VECTOR(*res)[node] += w2;
+                }
+            }
+        }
+
+    } /* node < no_of_nodes */
+
+    igraph_vector_int_destroy(&neis);
+    igraph_inclist_destroy(&incs);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_local_scan_1_directed_all(const igraph_t *graph,
+                                       igraph_vector_t *res,
+                                       const igraph_vector_t *weights) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    igraph_inclist_t incs;
+    int i, node;
+
+    igraph_vector_int_t neis;
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &incs);
+
+    igraph_vector_int_init(&neis, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &neis);
+
+    igraph_vector_resize(res, no_of_nodes);
+    igraph_vector_null(res);
+
+    for (node = 0; node < no_of_nodes; node++) {
+        igraph_vector_int_t *edges1 = igraph_inclist_get(&incs, node);
+        int edgeslen1 = igraph_vector_int_size(edges1);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /* Mark neighbors. We also count the edges that are incident to ego.
+           Note that this time we do not mark ego, because we don't want to
+           double count its incident edges later, when we are going over the
+           incident edges of ego's neighbors. */
+        for (i = 0; i < edgeslen1; i++) {
+            int e = VECTOR(*edges1)[i];
+            int nei = IGRAPH_OTHER(graph, e, node);
+            igraph_real_t w = weights ? VECTOR(*weights)[e] : 1;
+            VECTOR(neis)[nei] = node + 1;
+            VECTOR(*res)[node] += w;
+        }
+
+        /* Crawl neighbors. We make sure that each neighbor of 'node' is
+           only crawed once. We count all qualifying edges of ego, and
+           then unmark ego to avoid double counting. */
+        for (i = 0; i < edgeslen1; i++) {
+            int e2 = VECTOR(*edges1)[i];
+            int nei = IGRAPH_OTHER(graph, e2, node);
+            igraph_vector_int_t *edges2;
+            int j, edgeslen2;
+            if (VECTOR(neis)[nei] != node + 1) {
+                continue;
+            }
+            edges2 = igraph_inclist_get(&incs, nei);
+            edgeslen2 = igraph_vector_int_size(edges2);
+            for (j = 0; j < edgeslen2; j++) {
+                int e2 = VECTOR(*edges2)[j];
+                int nei2 = IGRAPH_OTHER(graph, e2, nei);
+                igraph_real_t w2 = weights ? VECTOR(*weights)[e2] : 1;
+                if (VECTOR(neis)[nei2] == node + 1) {
+                    VECTOR(*res)[node] += w2;
+                }
+            }
+            VECTOR(neis)[nei] = 0;
+        }
+
+    } /* node < no_of_nodes */
+
+    igraph_vector_int_destroy(&neis);
+    igraph_inclist_destroy(&incs);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_local_scan_1_sumweights(const igraph_t *graph,
+                                     igraph_vector_t *res,
+                                     const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int node, i, j, nn;
+    igraph_inclist_t allinc;
+    igraph_vector_int_t *neis1, *neis2;
+    long int neilen1, neilen2;
+    long int *neis;
+    long int maxdegree;
+
+    igraph_vector_int_t order;
+    igraph_vector_int_t rank;
+    igraph_vector_t degree, *edge1 = &degree; /* reuse degree as edge1 */
+
+    if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    igraph_vector_int_init(&order, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &order);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
+                               IGRAPH_LOOPS));
+    maxdegree = (long int) igraph_vector_max(&degree) + 1;
+    igraph_vector_order1_int(&degree, &order, maxdegree);
+    igraph_vector_int_init(&rank, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &rank);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(rank)[ VECTOR(order)[i] ] = no_of_nodes - i - 1;
+    }
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &allinc, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &allinc);
+    IGRAPH_CHECK(igraph_i_trans4_il_simplify(graph, &allinc, &rank));
+
+    neis = igraph_Calloc(no_of_nodes, long int);
+    if (neis == 0) {
+        IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, neis);
+
+    IGRAPH_CHECK(igraph_strength(graph, res, igraph_vss_all(), IGRAPH_ALL,
+                                 IGRAPH_LOOPS, weights));
+
+    for (nn = no_of_nodes - 1; nn >= 0; nn--) {
+        node = VECTOR(order)[nn];
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        neis1 = igraph_inclist_get(&allinc, node);
+        neilen1 = igraph_vector_int_size(neis1);
+
+        /* Mark the neighbors of the node */
+        for (i = 0; i < neilen1; i++) {
+            int edge = VECTOR(*neis1)[i];
+            int nei = IGRAPH_OTHER(graph, edge, node);
+            VECTOR(*edge1)[nei] = VECTOR(*weights)[edge];
+            neis[nei] = node + 1;
+        }
+
+        for (i = 0; i < neilen1; i++) {
+            long int edge = VECTOR(*neis1)[i];
+            long int nei = IGRAPH_OTHER(graph, edge, node);
+            igraph_real_t w = VECTOR(*weights)[edge];
+            neis2 = igraph_inclist_get(&allinc, nei);
+            neilen2 = igraph_vector_int_size(neis2);
+            for (j = 0; j < neilen2; j++) {
+                long int edge2 = VECTOR(*neis2)[j];
+                long int nei2 = IGRAPH_OTHER(graph, edge2, nei);
+                igraph_real_t w2 = VECTOR(*weights)[edge2];
+                if (neis[nei2] == node + 1) {
+                    VECTOR(*res)[node] += w2;
+                    VECTOR(*res)[nei2] += w;
+                    VECTOR(*res)[nei] += VECTOR(*edge1)[nei2];
+                }
+            }
+        }
+    }
+
+    igraph_free(neis);
+    igraph_inclist_destroy(&allinc);
+    igraph_vector_int_destroy(&rank);
+    igraph_vector_destroy(&degree);
+    igraph_vector_int_destroy(&order);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \function igraph_local_scan_1_ecount
+ * Local scan-statistics, k=1, edge count and sum of weights
+ *
+ * Count the number of edges or the sum the edge weights in the
+ * 1-neighborhood of vertices.
+ *
+ * \param graph The input graph
+ * \param res An initialized vector, the results are stored here.
+ * \param weights Weight vector for weighted graphs, null pointer for
+ *        unweighted graphs.
+ * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing,
+ *        \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges.
+ * \return Error code.
+ *
+ */
+
+int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res,
+                               const igraph_vector_t *weights,
+                               igraph_neimode_t mode) {
+
+    if (igraph_is_directed(graph)) {
+        if (mode != IGRAPH_ALL) {
+            return igraph_i_local_scan_1_directed(graph, res, weights, mode);
+        } else {
+            return igraph_i_local_scan_1_directed_all(graph, res, weights);
+        }
+    } else {
+        if (weights) {
+            return igraph_i_local_scan_1_sumweights(graph, res, weights);
+        } else {
+
+#define TRIEDGES
+#include "triangles_template.h"
+#undef TRIEDGES
+
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_local_scan_0_them_w(const igraph_t *us, const igraph_t *them,
+                                 igraph_vector_t *res,
+                                 const igraph_vector_t *weights_them,
+                                 igraph_neimode_t mode) {
+
+    igraph_t is;
+    igraph_vector_t map2;
+    int i, m;
+
+    if (!weights_them) {
+        IGRAPH_ERROR("Edge weights not given for weighted scan-0",
+                     IGRAPH_EINVAL);
+    }
+    if (igraph_vector_size(weights_them) != igraph_ecount(them)) {
+        IGRAPH_ERROR("Invalid weights length for scan-0", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&map2, 0);
+    igraph_intersection(&is, us, them, /*map1=*/ 0, &map2);
+    IGRAPH_FINALLY(igraph_destroy, &is);
+
+    /* Rewrite the map as edge weights */
+    m = igraph_vector_size(&map2);
+    for (i = 0; i < m; i++) {
+        VECTOR(map2)[i] = VECTOR(*weights_them)[ (int) VECTOR(map2)[i] ];
+    }
+
+    igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS,
+                    /*weights=*/ &map2);
+
+    igraph_destroy(&is);
+    igraph_vector_destroy(&map2);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_local_scan_0_them
+ * Local THEM scan-statistics, k=0
+ *
+ * K=0 scan-statistics is arbitrarily defined as the vertex degree for
+ * unweighted, and the vertex strength for weighted graphs. See \ref
+ * igraph_degree() and \ref igraph_strength().
+ *
+ * \param us The input graph, to use to extract the neighborhoods.
+ * \param them The input graph to use for the actually counting.
+ * \param res An initialized vector, the results are stored here.
+ * \param weights_them Weight vector for weighted graphs, null pointer for
+ *        unweighted graphs.
+ * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing,
+ *        \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges.
+ * \return Error code.
+ *
+ */
+
+int igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them,
+                             igraph_vector_t *res,
+                             const igraph_vector_t *weights_them,
+                             igraph_neimode_t mode) {
+
+    igraph_t is;
+
+    if (igraph_vcount(us) != igraph_vcount(them)) {
+        IGRAPH_ERROR("Number of vertices don't match in scan-0", IGRAPH_EINVAL);
+    }
+    if (igraph_is_directed(us) != igraph_is_directed(them)) {
+        IGRAPH_ERROR("Directedness don't match in scan-0", IGRAPH_EINVAL);
+    }
+
+    if (weights_them) {
+        return igraph_i_local_scan_0_them_w(us, them, res, weights_them, mode);
+    }
+
+    igraph_intersection(&is, us, them, /*edgemap1=*/ 0, /*edgemap2=*/ 0);
+    IGRAPH_FINALLY(igraph_destroy, &is);
+
+    igraph_degree(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS);
+
+    igraph_destroy(&is);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_local_scan_1_ecount_them
+ * Local THEM scan-statistics, k=1, edge count and sum of weights
+ *
+ * Count the number of edges or the sum the edge weights in the
+ * 1-neighborhood of vertices.
+ *
+ * \param us The input graph to extract the neighborhoods.
+ * \param them The input graph to perform the counting.
+ * \param weights_them Weight vector for weighted graphs, null pointer for
+ *        unweighted graphs.
+ * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing,
+ *        \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges.
+ * \return Error code.
+ *
+ * \sa \ref igraph_local_scan_1_ecount() for the US statistics.
+ */
+
+int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them,
+                                    igraph_vector_t *res,
+                                    const igraph_vector_t *weights_them,
+                                    igraph_neimode_t mode) {
+
+    int no_of_nodes = igraph_vcount(us);
+    igraph_adjlist_t adj_us;
+    igraph_inclist_t incs_them;
+    igraph_vector_int_t neis;
+    int node;
+
+    if (igraph_vcount(them) != no_of_nodes) {
+        IGRAPH_ERROR("Number of vertices must match in scan-1", IGRAPH_EINVAL);
+    }
+    if (igraph_is_directed(us) != igraph_is_directed(them)) {
+        IGRAPH_ERROR("Directedness must match in scan-1", IGRAPH_EINVAL);
+    }
+    if (weights_them &&
+        igraph_vector_size(weights_them) != igraph_ecount(them)) {
+        IGRAPH_ERROR("Invalid weight vector length in scan-1 (them)",
+                     IGRAPH_EINVAL);
+    }
+
+    igraph_adjlist_init(us, &adj_us, mode);
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adj_us);
+    igraph_adjlist_simplify(&adj_us);
+    igraph_inclist_init(them, &incs_them, mode);
+    IGRAPH_FINALLY(igraph_inclist_destroy, &incs_them);
+
+    igraph_vector_int_init(&neis, no_of_nodes);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &neis);
+
+    igraph_vector_resize(res, no_of_nodes);
+    igraph_vector_null(res);
+
+    for (node = 0; node < no_of_nodes; node++) {
+        igraph_vector_int_t *neis_us = igraph_adjlist_get(&adj_us, node);
+        igraph_vector_int_t *edges1_them = igraph_inclist_get(&incs_them, node);
+        int len1_us = igraph_vector_int_size(neis_us);
+        int len1_them = igraph_vector_int_size(edges1_them);
+        int i;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /* Mark neighbors and self in us */
+        VECTOR(neis)[node] = node + 1;
+        for (i = 0; i < len1_us; i++) {
+            int nei = VECTOR(*neis_us)[i];
+            VECTOR(neis)[nei] = node + 1;
+        }
+
+        /* Crawl neighbors in them, first ego */
+        for (i = 0; i < len1_them; i++) {
+            int e = VECTOR(*edges1_them)[i];
+            int nei = IGRAPH_OTHER(them, e, node);
+            if (VECTOR(neis)[nei] == node + 1) {
+                igraph_real_t w = weights_them ? VECTOR(*weights_them)[e] : 1;
+                VECTOR(*res)[node] += w;
+            }
+        }
+        /* Then the rest */
+        for (i = 0; i < len1_us; i++) {
+            int nei = VECTOR(*neis_us)[i];
+            igraph_vector_int_t *edges2_them = igraph_inclist_get(&incs_them, nei);
+            int j, len2_them = igraph_vector_int_size(edges2_them);
+            for (j = 0; j < len2_them; j++) {
+                int e2 = VECTOR(*edges2_them)[j];
+                int nei2 = IGRAPH_OTHER(them, e2, nei);
+                if (VECTOR(neis)[nei2] == node + 1) {
+                    igraph_real_t w = weights_them ? VECTOR(*weights_them)[e2] : 1;
+                    VECTOR(*res)[node] += w;
+                }
+            }
+        }
+
+        /* For undirected, it was double counted */
+        if (mode == IGRAPH_ALL || ! igraph_is_directed(us)) {
+            VECTOR(*res)[node] /= 2.0;
+        }
+
+    } /* node < no_of_nodes */
+
+    igraph_vector_int_destroy(&neis);
+    igraph_inclist_destroy(&incs_them);
+    igraph_adjlist_destroy(&adj_us);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_local_scan_k_ecount
+ * Local scan-statistics, general function, edge count and sum of weights
+ *
+ * Count the number of edges or the sum the edge weights in the
+ * k-neighborhood of vertices.
+ *
+ * \param graph The input graph
+ * \param k The size of the neighborhood, non-negative integer.
+ *        The k=0 case is special, see \ref igraph_local_scan_0().
+ * \param res An initialized vector, the results are stored here.
+ * \param weights Weight vector for weighted graphs, null pointer for
+ *        unweighted graphs.
+ * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing,
+ *        \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges.
+ * \return Error code.
+ *
+ */
+
+int igraph_local_scan_k_ecount(const igraph_t *graph, int k,
+                               igraph_vector_t *res,
+                               const igraph_vector_t *weights,
+                               igraph_neimode_t mode) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    int node;
+    igraph_dqueue_int_t Q;
+    igraph_vector_int_t marked;
+    igraph_inclist_t incs;
+
+    if (k < 0) {
+        IGRAPH_ERROR("k must be non-negative in k-scan", IGRAPH_EINVAL);
+    }
+    if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weight vector length in k-scan", IGRAPH_EINVAL);
+    }
+
+    if (k == 0) {
+        return igraph_local_scan_0(graph, res, weights, mode);
+    }
+    if (k == 1) {
+        return igraph_local_scan_1_ecount(graph, res, weights, mode);
+    }
+
+    /* We do a BFS form each node, and simply count the number
+       of edges on the way */
+
+    IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q);
+    IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &marked);
+    IGRAPH_CHECK(igraph_inclist_init(graph, &incs, mode));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &incs);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
+    igraph_vector_null(res);
+
+    for (node = 0 ; node < no_of_nodes ; node++) {
+        igraph_dqueue_int_push(&Q, node);
+        igraph_dqueue_int_push(&Q, 0);
+        VECTOR(marked)[node] = node + 1;
+        while (!igraph_dqueue_int_empty(&Q)) {
+            int act = igraph_dqueue_int_pop(&Q);
+            int dist = igraph_dqueue_int_pop(&Q) + 1;
+            igraph_vector_int_t *edges = igraph_inclist_get(&incs, act);
+            int i, edgeslen = igraph_vector_int_size(edges);
+            for (i = 0; i < edgeslen; i++) {
+                int edge = VECTOR(*edges)[i];
+                int nei = IGRAPH_OTHER(graph, edge, act);
+                if (dist <= k || VECTOR(marked)[nei] == node + 1) {
+                    igraph_real_t w = weights ? VECTOR(*weights)[edge] : 1;
+                    VECTOR(*res)[node] += w;
+                }
+                if (dist <= k && VECTOR(marked)[nei] != node + 1) {
+                    igraph_dqueue_int_push(&Q, nei);
+                    igraph_dqueue_int_push(&Q, dist);
+                    VECTOR(marked)[nei] = node + 1;
+                }
+            }
+        }
+
+        if (mode == IGRAPH_ALL || ! igraph_is_directed(graph)) {
+            VECTOR(*res)[node] /= 2.0;
+        }
+
+    } /* node < no_of_nodes */
+
+    igraph_inclist_destroy(&incs);
+    igraph_vector_int_destroy(&marked);
+    igraph_dqueue_int_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_local_scan_k_ecount_them
+ * Local THEM scan-statistics, general function, edge count and sum of weights
+ *
+ * Count the number of edges or the sum the edge weights in the
+ * k-neighborhood of vertices.
+ *
+ * \param us The input graph to extract the neighborhoods.
+ * \param them The input graph to perform the counting.
+ * \param k The size of the neighborhood, non-negative integer.
+ *        The k=0 case is special, see \ref igraph_local_scan_0_them().
+ * \param weights_them Weight vector for weighted graphs, null pointer for
+ *        unweighted graphs.
+ * \param mode Type of the neighborhood, \c IGRAPH_OUT means outgoing,
+ *        \c IGRAPH_IN means incoming and \c IGRAPH_ALL means all edges.
+ * \return Error code.
+ *
+ * \sa \ref igraph_local_scan_1_ecount() for the US statistics.
+ */
+
+int igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them,
+                                    int k, igraph_vector_t *res,
+                                    const igraph_vector_t *weights_them,
+                                    igraph_neimode_t mode) {
+
+    int no_of_nodes = igraph_vcount(us);
+    int node;
+    igraph_dqueue_int_t Q;
+    igraph_vector_int_t marked;
+    igraph_stack_int_t ST;
+    igraph_inclist_t incs_us, incs_them;
+
+    if (igraph_vcount(them) != no_of_nodes) {
+        IGRAPH_ERROR("Number of vertices must match in scan-k", IGRAPH_EINVAL);
+    }
+    if (igraph_is_directed(us) != igraph_is_directed(them)) {
+        IGRAPH_ERROR("Directedness must match in scan-k", IGRAPH_EINVAL);
+    }
+    if (k < 0) {
+        IGRAPH_ERROR("k must be non-negative in k-scan", IGRAPH_EINVAL);
+    }
+    if (weights_them &&
+        igraph_vector_size(weights_them) != igraph_ecount(them)) {
+        IGRAPH_ERROR("Invalid weight vector length in k-scan (them)",
+                     IGRAPH_EINVAL);
+    }
+
+    if (k == 0) {
+        return igraph_local_scan_0_them(us, them, res, weights_them, mode);
+    }
+    if (k == 1) {
+        return igraph_local_scan_1_ecount_them(us, them, res, weights_them, mode);
+    }
+
+    /* We mark the nodes in US in a BFS. Then we check the outgoing edges
+       of all marked nodes in THEM. */
+
+    IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q);
+    IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &marked);
+    IGRAPH_CHECK(igraph_inclist_init(us, &incs_us, mode));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &incs_us);
+    IGRAPH_CHECK(igraph_inclist_init(them, &incs_them, mode));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &incs_them);
+    IGRAPH_CHECK(igraph_stack_int_init(&ST, 100));
+    IGRAPH_FINALLY(igraph_stack_int_destroy, &ST);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
+    igraph_vector_null(res);
+
+    for (node = 0; node < no_of_nodes; node++) {
+
+        /* BFS to mark the nodes in US */
+        IGRAPH_CHECK(igraph_dqueue_int_push(&Q, node));
+        IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0));
+        IGRAPH_CHECK(igraph_stack_int_push(&ST, node));
+        VECTOR(marked)[node] = node + 1;
+        while (!igraph_dqueue_int_empty(&Q)) {
+            int act = igraph_dqueue_int_pop(&Q);
+            int dist = igraph_dqueue_int_pop(&Q) + 1;
+            igraph_vector_int_t *edges = igraph_inclist_get(&incs_us, act);
+            int i, edgeslen = igraph_vector_int_size(edges);
+            for (i = 0; i < edgeslen; i++) {
+                int edge = VECTOR(*edges)[i];
+                int nei = IGRAPH_OTHER(us, edge, act);
+                if (dist <= k && VECTOR(marked)[nei] != node + 1) {
+                    igraph_dqueue_int_push(&Q, nei);
+                    igraph_dqueue_int_push(&Q, dist);
+                    VECTOR(marked)[nei] = node + 1;
+                    igraph_stack_int_push(&ST, nei);
+                }
+            }
+        }
+
+        /* Now check the edges of all nodes in THEM */
+        while (!igraph_stack_int_empty(&ST)) {
+            int act = igraph_stack_int_pop(&ST);
+            igraph_vector_int_t *edges = igraph_inclist_get(&incs_them, act);
+            int i, edgeslen = igraph_vector_int_size(edges);
+            for (i = 0; i < edgeslen; i++) {
+                int edge = VECTOR(*edges)[i];
+                int nei = IGRAPH_OTHER(them, edge, act);
+                if (VECTOR(marked)[nei] == node + 1) {
+                    igraph_real_t w = weights_them ? VECTOR(*weights_them)[edge] : 1;
+                    VECTOR(*res)[node] += w;
+                }
+            }
+        }
+
+        if (mode == IGRAPH_ALL || ! igraph_is_directed(us)) {
+            VECTOR(*res)[node] /= 2;
+        }
+
+    } /* node < no_of_nodes */
+
+    igraph_stack_int_destroy(&ST);
+    igraph_inclist_destroy(&incs_them);
+    igraph_inclist_destroy(&incs_us);
+    igraph_vector_int_destroy(&marked);
+    igraph_dqueue_int_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \function igraph_local_scan_neighborhood_ecount
+ * Local scan-statistics with pre-calculated neighborhoods
+ *
+ * Count the number of edges, or sum the edge weigths in
+ * neighborhoods given as a parameter.
+ *
+ * \param graph The graph to perform the counting/summing in.
+ * \param res Initialized vector, the result is stored here.
+ * \param weights Weight vector for weighted graphs, null pointer for
+ *        unweighted graphs.
+ * \param neighborhoods List of <code>igraph_vector_int_t</code>
+ *        objects, the neighborhoods, one for each vertex in the
+ *        graph.
+ * \return Error code.
+ */
+
+int igraph_local_scan_neighborhood_ecount(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vector_t *weights,
+        const igraph_vector_ptr_t *neighborhoods) {
+
+    int node, no_of_nodes = igraph_vcount(graph);
+    igraph_inclist_t incs;
+    igraph_vector_int_t marked;
+    igraph_bool_t directed = igraph_is_directed(graph);
+
+    if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weight vector length in local scan", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_ptr_size(neighborhoods) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid neighborhood list length in local scan",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &marked);
+    IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &incs);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
+    igraph_vector_null(res);
+
+    for (node = 0; node < no_of_nodes; node++) {
+        igraph_vector_int_t *nei = VECTOR(*neighborhoods)[node];
+        int i, neilen = igraph_vector_int_size(nei);
+        VECTOR(marked)[node] = node + 1;
+        for (i = 0; i < neilen; i++) {
+            int vertex = VECTOR(*nei)[i];
+            if (vertex < 0 || vertex >= no_of_nodes) {
+                IGRAPH_ERROR("Invalid vertex id in neighborhood list in local scan",
+                             IGRAPH_EINVAL);
+            }
+            VECTOR(marked)[vertex] = node + 1;
+        }
+
+        for (i = 0; i < neilen; i++) {
+            int vertex = VECTOR(*nei)[i];
+            igraph_vector_int_t *edges = igraph_inclist_get(&incs, vertex);
+            int j, edgeslen = igraph_vector_int_size(edges);
+            for (j = 0; j < edgeslen; j++) {
+                int edge = VECTOR(*edges)[j];
+                int nei2 = IGRAPH_OTHER(graph, edge, vertex);
+                if (VECTOR(marked)[nei2] == node + 1) {
+                    igraph_real_t w = weights ? VECTOR(*weights)[edge] : 1;
+                    VECTOR(*res)[node] += w;
+                }
+            }
+        }
+        if (!directed) {
+            VECTOR(*res)[node] /= 2.0;
+        }
+    }
+
+    igraph_inclist_destroy(&incs);
+    igraph_vector_int_destroy(&marked);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
diff --git a/igraph/src/scg.c b/igraph/src/scg.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/scg.c
@@ -0,0 +1,2292 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-12  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/*
+ *  SCGlib : A C library for the spectral coarse graining of matrices
+ *  as described in the paper: Shrinking Matrices while preserving their
+ *  eigenpairs with Application to the Spectral Coarse Graining of Graphs.
+ *  Preprint available at <http://people.epfl.ch/david.morton>
+ *
+ *  Copyright (C) 2008 David Morton de Lachapelle <david.morton@a3.epfl.ch>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301 USA
+ *
+ *  DESCRIPTION
+ *  -----------
+ *    The grouping function takes as argument 'nev' eigenvectors and
+ *    and tries to minimize the eigenpair shifts induced by the coarse
+ *    graining (Section 5 of the above reference). The eigenvectors are
+ *    stored in a 'nev'x'n' matrix 'v'.
+ *    The 'algo' parameter can take the following values
+ *      1  ->  Optimal method (sec. 5.3.1)
+ *      2  ->  Intervals+k-means (sec. 5.3.3)
+ *      3  ->  Intervals (sec. 5.3.2)
+ *      4  ->  Exact SCG (sec. 5.4.1--last paragraph)
+ *    'nt' is a vector of length 'nev' giving either the size of the
+ *    partitions (if algo = 1) or the number of intervals to cut the
+ *    eigenvectors if algo = 2 or algo = 3. When algo = 4 this parameter
+ *    is ignored. 'maxiter' fixes the maximum number of iterations of
+ *    the k-means algorithm, and is only considered when algo = 2.
+ *    All the algorithms try to find a minimizing partition of
+ *    ||v_i-Pv_i|| where P is a problem-specific projector and v_i denotes
+ *    the eigenvectors stored in v. The final partition is worked out
+ *    as decribed in Method 1 of Section 5.4.2.
+ *    'matrix' provides the type of SCG (i.e. the form of P). So far,
+ *    the options are those described in section 6, that is:
+ *      1  ->  Symmetric (sec. 6.1)
+ *      2  ->  Laplacian (sec. 6.2)
+ *      3  ->  Stochastic (sec. 6.3)
+ *    In the stochastic case, a valid distribution probability 'p' must be
+ *    provided. In all other cases, 'p' is ignored and can be set to NULL.
+ *    The group labels in the final partition are given in 'gr' as positive
+ *    consecutive integers starting from 0.
+ */
+
+#include "igraph_scg.h"
+#include "igraph_eigen.h"
+#include "igraph_interface.h"
+#include "igraph_structural.h"
+#include "igraph_constructors.h"
+#include "igraph_conversion.h"
+#include "igraph_memory.h"
+
+#include "scg_headers.h"
+
+#include "math.h"
+
+/**
+ * \section about_scg
+ *
+ * <para>
+ * The SCG functions provide a framework, called Spectral Coarse Graining
+ * (SCG), for reducing large graphs while preserving their
+ * <emphasis>spectral-related features</emphasis>, that is features
+ * closely related with the eigenvalues and eigenvectors of a graph
+ * matrix (which for now can be the adjacency, the stochastic, or the
+ * Laplacian matrix).
+ * </para>
+ *
+ * <para>
+ * Common examples of such features comprise the first-passage-time of
+ * random walkers on Markovian graphs, thermodynamic properties of
+ * lattice models in statistical physics (e.g. Ising model), and the
+ * epidemic threshold of epidemic network models (SIR and SIS models).
+ * </para>
+ *
+ * <para>
+ * SCG differs from traditional clustering schemes by producing a
+ * <emphasis>coarse-grained graph</emphasis> (not just a partition of
+ * the vertices), representative of the original one. As shown in [1],
+ * Principal Component Analysis can be viewed as a particular SCG,
+ * called <emphasis>exact SCG</emphasis>, where the matrix to be
+ * coarse-grained is the covariance matrix of some data set.
+ * </para>
+ *
+ * <para>
+ * SCG should be of interest to practitioners of various
+ * fields dealing with problems where matrix eigenpairs play an important
+ * role, as for instance is the case of dynamical processes on networks.
+ * </para>
+ *
+ * <section><title>SCG in brief</title>
+ * <para>
+ * The main idea of SCG is to operate on a matrix a shrinkage operation
+ * specifically designed to preserve some of the matrix eigenpairs while
+ * not altering other important matrix features (such as its structure).
+ * Mathematically, this idea was expressed as follows. Consider a
+ * (complex) n x n matrix M and form the product
+ * <blockquote><para><phrase role="math">
+ *   M'=LMR*,
+ * </phrase></para></blockquote>
+ * where n' &lt; n and L, R are from C[n'xn]} and are such
+ * that LR*=I[n'] (R* denotes the conjugate transpose of R). Under
+ * these assumptions, it can be shown that P=R*L is an n'-rank
+ * projector and that, if (lambda, v) is a (right)
+ * eigenpair of M (i.e. Mv=lambda v} and P is orthogonal, there exists
+ * an eigenvalue lambda' of M' such that
+ * <blockquote><para><phrase role="math">
+ *   |lambda-lambda'| &lt;= const ||e[P](v)||
+ *   [1+O(||e[P](v)||<superscript>2</superscript>)],
+ * </phrase></para></blockquote>
+ * where ||e[P](v)||=||v-Pv||. Hence, if P (or equivalently
+ * L, R) is chosen so as to make ||e[P](v)|| as small as possible, one
+ * can preserve to any desired level the original eigenvalue
+ * lambda in the coarse-grained matrix M';
+ * under extra assumptions on M, this result can be generalized to
+ * eigenvectors [1]. This leads to the following generic definition of a
+ * SCG problem.
+ * </para>
+ *
+ * <para>
+ * Given M (C[nxn]) and (lambda, v), a (right) eigenpair of M to be
+ * preserved by the coarse graining, the problem is to find a projector
+ * P' solving
+ * <blockquote><para><phrase role="math">
+ *   min(||e[P](v)||, p in Omega),
+ * </phrase></para></blockquote>
+ * where Omega is a set of projectors in C[nxn] described by some
+ * ad hoc constraints c[1], ..., c[r]
+ * (e.g. c[1]: P in R[nxn], c[2]: P=t(P), c[3]: P[i,j] >= 0}, etc).
+ * </para>
+ *
+ * <para>
+ * Choosing pertinent constraints to solve the SCG problem is of great
+ * importance in applications. For instance, in the absence of
+ * constraints the SCG problem is solved trivially by
+ * P'=vv* (v is assumed normalized). We have designed a particular
+ * constraint, called <emphasis>homogeneous mixing</emphasis>, which
+ * ensures that vertices belonging to the same group are merged
+ * consistently from a physical point of view (see [1] for
+ * details). Under this constraint the SCG problem reduces to finding
+ * the partition of 1, ..., n (labeling the original vertices)
+ * minimizing
+ * <blockquote><para><phrase role="math">
+ *   ||e[P](v)||<superscript>2</superscript> =
+ *   sum([v(i)-(Pv)(i)]<superscript>2</superscript>;
+ *   alpha=1,...,n', i in alpha),
+ * </phrase></para></blockquote>
+ * where alpha denotes a group (i.e. a block) in a partition of
+ * {1, ..., n}, and |alpha| is the number of elements in alpha.
+ * </para>
+ *
+ * <para>
+ * If M is symmetric or stochastic, for instance, then it may be
+ * desirable (or mandatory) to choose L, R so that M' is symmetric or
+ * stochastic as well. This <emphasis>structural constraint</emphasis>
+ * has led to the construction of particular semi-projectors for
+ * symmetric [1], stochastic [3] and Laplacian [2] matrices, that are
+ * made available.
+ * </para>
+ *
+ * <para>
+ * In short, the coarse graining of matrices and graphs involves:
+ * \olist
+ *   \oli Retrieving a matrix or a graph matrix M from the
+ *     problem.
+ *   \oli Computing the eigenpairs of M to be preserved in the
+ *     coarse-grained graph or matrix.
+ *   \oli Setting some problem-specific constraints (e.g. dimension of
+ *     the coarse-grained object).
+ *   \oli Solving the constrained SCG problem, that is finding P'.
+ *   \oli Computing from P' two semi-projectors L' and R'
+ *     (e.g. following the method proposed in [1]).
+ *   \oli Working out the product M'=L'MR'* and, if needed, defining
+ *     from M' a coarse-grained graph.
+ * \endolist
+ * </para>
+ * </section>
+ *
+ * <section><title>Functions for performing SCG</title>
+ * <para>
+ * The main functions are \ref igraph_scg_adjacency(), \ref
+ * igraph_scg_laplacian() and \ref igraph_scg_stochastic().
+ * These functions handle all the steps involved in the
+ * Spectral Coarse Graining (SCG) of some particular matrices and graphs
+ * as described above and in reference [1]. In more details,
+ * they compute some prescribed eigenpairs of a matrix or a
+ * graph matrix, (for now adjacency, Laplacian and stochastic matrices are
+ * available), work out an optimal partition to preserve the eigenpairs,
+ * and finally output a coarse-grained matrix or graph along with other
+ * useful information.
+ * </para>
+ *
+ * <para>
+ * These steps can also be carried out independently: (1) Use
+ * \ref igraph_get_adjacency(), \ref igraph_get_sparsemat(),
+ * \ref igraph_laplacian(), \ref igraph_get_stochastic() or \ref
+ * igraph_get_stochastic_sparsemat() to compute a matrix M.
+ * (2) Work out some prescribed eigenpairs of M e.g. by
+ * means of \ref igraph_arpack_rssolve() or \ref
+ * igraph_arpack_rnsolve(). (3) Invoke one the four
+ * algorithms of the function \ref igraph_scg_grouping() to get a
+ * partition that will preserve the eigenpairs in the coarse-grained
+ * matrix. (4) Compute the semi-projectors L and R using
+ * \ref igraph_scg_semiprojectors() and from there the coarse-grained
+ * matrix M'=LMR*. If necessary, construct a coarse-grained graph from
+ * M' (e.g. as in [1]).
+ * </para>
+ * </section>
+ *
+ * <section><title>References</title>
+ * <para>
+ * [1] D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios,
+ * Shrinking Matrices while Preserving their Eigenpairs with Application
+ * to the Spectral Coarse Graining of Graphs. Submitted to
+ * <emphasis>SIAM Journal on Matrix Analysis and
+ * Applications</emphasis>, 2008.
+ * http://people.epfl.ch/david.morton
+ * </para>
+ * <para>
+ * [2] D. Gfeller, and P. De Los Rios, Spectral Coarse Graining and
+ * Synchronization in Oscillator Networks.
+ * <emphasis>Physical Review Letters</emphasis>,
+ * <emphasis role="strong">100</emphasis>(17), 2008.
+ * http://arxiv.org/abs/0708.2055
+ * </para>
+ * <para>
+ * [3] D. Gfeller, and P. De Los Rios, Spectral Coarse Graining of Complex
+ * Networks, <emphasis>Physical Review Letters</emphasis>,
+ * <emphasis role="strong">99</emphasis>(3), 2007.
+ * http://arxiv.org/abs/0706.0812
+ * </para>
+ * </section>
+ */
+
+/**
+ * \function igraph_scg_grouping
+ * \brief SCG problem solver
+ *
+ * This function solves the Spectral Coarse Graining (SCG) problem;
+ * either exactly, or approximately but faster.
+ *
+ * </para><para>
+ * The algorithm \c IGRAPH_SCG_OPTIMUM solves exactly the SCG problem
+ * for each eigenvector in \p V. The running time of this algorithm is
+ * O(max(nt) m^2) for the symmetric and laplacian matrix problems
+ * It is O(m^3) for the stochastic problem. Here m is the number
+ * of rows in \p V. In all three cases, the memory usage is O(m^2).
+ *
+ * </para><para>
+ * The algorithms \c IGRAPH_SCG_INTERV and \c IGRAPH_SCG_INTERV_KM solve
+ * approximately the SCG problem by performing a (for now) constant
+ * binning of the components of the eigenvectors, that is \p nt
+ * <code>VECTOR(nt_vec)[i]</code>) constant-size bins are used to
+ * partition <code>V[,i]</code>. When \p algo is \c
+ * IGRAPH_SCG_INTERV_KM, the (Lloyd) k-means algorithm is
+ * run on each partition obtained by \c IGRAPH_SCG_INTERV to improve
+ * accuracy.
+ *
+ * </para><para>
+ * Once a minimizing partition (either exact or approximate) has been
+ * found for each eigenvector, the final grouping is worked out as
+ * follows: two vertices are grouped together in the final partition if
+ * they are grouped together in each minimizing partition. In general the
+ * size of the final partition is not known in advance when the number
+ * of columns in \p V is larger than one.
+ *
+ * </para><para>
+ * Finally, the algorithm \c IGRAPH_SCG_EXACT groups the vertices with
+ * equal components in each eigenvector. The last three algorithms
+ * essentially have linear running time and memory load.
+ *
+ * \param V The matrix of eigenvectors to be preserved by coarse
+ *    graining, each column is an eigenvector.
+ * \param groups Pointer to an initialized vector, the result of the
+ *    SCG is stored here.
+ * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM,
+ *    it gives the number of groups to partition each eigenvector
+ *    separately. When \p algo is \c IGRAPH_SCG_INTERV or \c
+ *    IGRAPH_SCG_INTERV_KM, it gives the number of intervals to
+ *    partition each eigenvector. This is ignored when \p algo is \c
+ *    IGRAPH_SCG_EXACT.
+ * \param nt_vec A numeric vector of length one or the length must
+ *    match the number of eigenvectors given in \p V, or a \c NULL
+ *    pointer. If not \c NULL, then this argument gives the number of
+ *    groups or intervals, and \p nt is ignored. Different number of
+ *    groups or intervals can be specified for each eigenvector.
+ * \param mtype The type of semi-projectors used in the SCG. Possible
+ *    values are \c IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and
+ *    \c IGRAPH_SCG_LAPLACIAN.
+ * \param algo The algorithm to solve the SCG problem. Possible
+ *    values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c
+ *    IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the
+ *    details about them above.
+ * \param p A probability vector, or \c NULL. This argument must be
+ *    given if \p mtype is \c IGRAPH_SCG_STOCHASTIC, but it is ignored
+ *    otherwise. For the stochastic case it gives the stationary
+ *    probability distribution of a Markov chain, the one specified by
+ *    the graph/matrix under study.
+ * \param maxiter A positive integer giving the number of iterations
+ *    of the k-means algorithm when \p algo is \c
+ *    IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable
+ *    (initial) value for this argument is 100.
+ * \return Error code.
+ *
+ * Time complexity: see description above.
+ *
+ * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_laplacian(), \ref
+ * igraph_scg_stochastic().
+ *
+ * \example examples/simple/igraph_scg_grouping.c
+ * \example examples/simple/igraph_scg_grouping2.c
+ * \example examples/simple/igraph_scg_grouping3.c
+ * \example examples/simple/igraph_scg_grouping4.c
+ */
+
+int igraph_scg_grouping(const igraph_matrix_t *V,
+                        igraph_vector_t *groups,
+                        igraph_integer_t nt,
+                        const igraph_vector_t *nt_vec,
+                        igraph_scg_matrix_t mtype,
+                        igraph_scg_algorithm_t algo,
+                        const igraph_vector_t *p,
+                        igraph_integer_t maxiter) {
+
+    int no_of_nodes = (int) igraph_matrix_nrow(V);
+    int nev = (int) igraph_matrix_ncol(V);
+    igraph_matrix_int_t gr_mat;
+    int i;
+
+    if (nt_vec && igraph_vector_size(nt_vec) != 1 &&
+        igraph_vector_size(nt_vec) != nev) {
+        IGRAPH_ERROR("Invalid length for interval specification", IGRAPH_EINVAL);
+    }
+    if (nt_vec && igraph_vector_size(nt_vec) == 1) {
+        nt = (igraph_integer_t) VECTOR(*nt_vec)[0];
+        nt_vec = 0;
+    }
+
+    if (!nt_vec && algo != IGRAPH_SCG_EXACT) {
+        if (nt <= 1 || nt >= no_of_nodes) {
+            IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL);
+        }
+    } else if (algo != IGRAPH_SCG_EXACT) {
+        igraph_real_t min, max;
+        igraph_vector_minmax(nt_vec, &min, &max);
+        if (min <= 1 || max >= no_of_nodes) {
+            IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL);
+        }
+    }
+
+    if (mtype == IGRAPH_SCG_STOCHASTIC && !p) {
+        IGRAPH_ERROR("`p' must be given for the stochastic matrix case",
+                     IGRAPH_EINVAL);
+    }
+
+    if (p && igraph_vector_size(p) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid `p' vector size", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(groups, no_of_nodes));
+
+#define INVEC(i) (nt_vec ? VECTOR(*nt_vec)[i] : nt)
+
+    IGRAPH_CHECK(igraph_matrix_int_init(&gr_mat, no_of_nodes, nev));
+    IGRAPH_FINALLY(igraph_matrix_int_destroy, &gr_mat);
+
+    switch (algo) {
+    case IGRAPH_SCG_OPTIMUM:
+        for (i = 0; i < nev; i++) {
+            IGRAPH_CHECK(igraph_i_optimal_partition(&MATRIX(*V, 0, i),
+                                                    &MATRIX(gr_mat, 0, i),
+                                                    no_of_nodes, (int) INVEC(i),
+                                                    mtype,
+                                                    p ? VECTOR(*p) : 0, 0));
+        }
+        break;
+    case IGRAPH_SCG_INTERV_KM:
+        for (i = 0; i < nev; i++) {
+            igraph_vector_t tmpv;
+            igraph_vector_view(&tmpv, &MATRIX(*V, 0, i), no_of_nodes);
+            IGRAPH_CHECK(igraph_i_intervals_plus_kmeans(&tmpv,
+                         &MATRIX(gr_mat, 0, i),
+                         no_of_nodes, (int) INVEC(i),
+                         maxiter));
+        }
+        break;
+    case IGRAPH_SCG_INTERV:
+        for (i = 0; i < nev; i++) {
+            igraph_vector_t tmpv;
+            igraph_vector_view(&tmpv, &MATRIX(*V, 0, i), no_of_nodes);
+            IGRAPH_CHECK(igraph_i_intervals_method(&tmpv,
+                                                   &MATRIX(gr_mat, 0, i),
+                                                   no_of_nodes, (int) INVEC(i)));
+        }
+        break;
+    case IGRAPH_SCG_EXACT:
+        for (i = 0; i < nev; i++) {
+            IGRAPH_CHECK(igraph_i_exact_coarse_graining(&MATRIX(*V, 0, i),
+                         &MATRIX(gr_mat, 0, i),
+                         no_of_nodes));
+        }
+        break;
+    }
+
+#undef INVEC
+
+    if (nev == 1) {
+        for (i = 0; i < no_of_nodes; i++) {
+            VECTOR(*groups)[i] = MATRIX(gr_mat, i, 0);
+        }
+    } else {
+        igraph_i_scg_groups_t *g = igraph_Calloc(no_of_nodes,
+                                   igraph_i_scg_groups_t);
+        int gr_nb = 0;
+
+        IGRAPH_CHECK(igraph_matrix_int_transpose(&gr_mat));
+        for (i = 0; i < no_of_nodes; i++) {
+            g[i].ind = i;
+            g[i].n = nev;
+            g[i].gr = &MATRIX(gr_mat, 0, i);
+        }
+
+        qsort(g, (size_t) no_of_nodes, sizeof(igraph_i_scg_groups_t),
+              igraph_i_compare_groups);
+        VECTOR(*groups)[g[0].ind] = gr_nb;
+        for (i = 1; i < no_of_nodes; i++) {
+            if (igraph_i_compare_groups(&g[i], &g[i - 1]) != 0) {
+                gr_nb++;
+            }
+            VECTOR(*groups)[g[i].ind] = gr_nb;
+        }
+        igraph_Free(g);
+    }
+
+    igraph_matrix_int_destroy(&gr_mat);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_scg_semiprojectors_sym(const igraph_vector_t *groups,
+                                    igraph_matrix_t *L,
+                                    igraph_matrix_t *R,
+                                    igraph_sparsemat_t *Lsparse,
+                                    igraph_sparsemat_t *Rsparse,
+                                    int no_of_groups,
+                                    int no_of_nodes) {
+
+    igraph_vector_t tab;
+    int i;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tab, no_of_groups);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(tab)[ (int) VECTOR(*groups)[i] ] += 1;
+    }
+    for (i = 0; i < no_of_groups; i++) {
+        VECTOR(tab)[i] = sqrt(VECTOR(tab)[i]);
+    }
+
+    if (L) {
+        IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes));
+        igraph_matrix_null(L);
+        for (i = 0; i < no_of_nodes; i++) {
+            int g = (int) VECTOR(*groups)[i];
+            MATRIX(*L, g, i) = 1 / VECTOR(tab)[g];
+        }
+    }
+
+    if (R) {
+        if (L) {
+            IGRAPH_CHECK(igraph_matrix_update(R, L));
+        } else {
+            IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes));
+            igraph_matrix_null(R);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                MATRIX(*R, g, i) = 1 / VECTOR(tab)[g];
+            }
+        }
+    }
+
+    if (Lsparse) {
+        IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes,
+                                           /* nzmax= */ no_of_nodes));
+        for (i = 0; i < no_of_nodes; i++) {
+            int g = (int) VECTOR(*groups)[i];
+            IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i, 1 / VECTOR(tab)[g]));
+        }
+    }
+
+    if (Rsparse) {
+        IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes,
+                                           /* nzmax= */ no_of_nodes));
+        for (i = 0; i < no_of_nodes; i++) {
+            int g = (int) VECTOR(*groups)[i];
+            IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i, 1 / VECTOR(tab)[g]));
+        }
+    }
+
+    igraph_vector_destroy(&tab);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_scg_semiprojectors_lap(const igraph_vector_t *groups,
+                                    igraph_matrix_t *L,
+                                    igraph_matrix_t *R,
+                                    igraph_sparsemat_t *Lsparse,
+                                    igraph_sparsemat_t *Rsparse,
+                                    int no_of_groups,
+                                    int no_of_nodes,
+                                    igraph_scg_norm_t norm) {
+
+    igraph_vector_t tab;
+    int i;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tab, no_of_groups);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(tab)[ (int) VECTOR(*groups)[i] ] += 1;
+    }
+    for (i = 0; i < no_of_groups; i++) {
+        VECTOR(tab)[i] = VECTOR(tab)[i];
+    }
+
+    if (norm == IGRAPH_SCG_NORM_ROW) {
+        if (L) {
+            IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes));
+            igraph_matrix_null(L);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                MATRIX(*L, g, i) = 1.0 / VECTOR(tab)[g];
+            }
+        }
+        if (R) {
+            IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes));
+            igraph_matrix_null(R);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                MATRIX(*R, g, i) = 1.0;
+            }
+        }
+        if (Lsparse) {
+            IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes,
+                                               /* nzmax= */ no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i,
+                                                    1.0 / VECTOR(tab)[g]));
+            }
+        }
+        if (Rsparse) {
+            IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes,
+                                               /* nzmax= */ no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i, 1.0));
+            }
+        }
+    } else {
+        if (L) {
+            IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes));
+            igraph_matrix_null(L);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                MATRIX(*L, g, i) = 1.0;
+            }
+        }
+        if (R) {
+            IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes));
+            igraph_matrix_null(R);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                MATRIX(*R, g, i) = 1.0 / VECTOR(tab)[g];
+            }
+        }
+        if (Lsparse) {
+            IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes,
+                                               /* nzmax= */ no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i, 1.0));
+            }
+        }
+        if (Rsparse) {
+            IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes,
+                                               /* nzmax= */ no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i,
+                                                    1.0 / VECTOR(tab)[g]));
+            }
+        }
+
+    }
+
+    igraph_vector_destroy(&tab);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_scg_semiprojectors_sto(const igraph_vector_t *groups,
+                                    igraph_matrix_t *L,
+                                    igraph_matrix_t *R,
+                                    igraph_sparsemat_t *Lsparse,
+                                    igraph_sparsemat_t *Rsparse,
+                                    int no_of_groups,
+                                    int no_of_nodes,
+                                    const igraph_vector_t *p,
+                                    igraph_scg_norm_t norm) {
+
+    igraph_vector_t pgr, pnormed;
+    int i;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&pgr, no_of_groups);
+    IGRAPH_VECTOR_INIT_FINALLY(&pnormed, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        int g = (int) VECTOR(*groups)[i];
+        VECTOR(pgr)[g] += VECTOR(*p)[i];
+    }
+    for (i = 0; i < no_of_nodes; i++) {
+        int g = (int) VECTOR(*groups)[i];
+        VECTOR(pnormed)[i] = VECTOR(*p)[i] / VECTOR(pgr)[g];
+    }
+
+    if (norm == IGRAPH_SCG_NORM_ROW) {
+        if (L) {
+            IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes));
+            igraph_matrix_null(L);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                MATRIX(*L, g, i) = VECTOR(pnormed)[i];
+            }
+        }
+        if (R) {
+            IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes));
+            igraph_matrix_null(R);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                MATRIX(*R, g, i) = 1.0;
+            }
+        }
+        if (Lsparse) {
+            IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes,
+                                               /* nzmax= */ no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i,
+                                                    VECTOR(pnormed)[i]));
+            }
+        }
+        if (Rsparse) {
+            IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes,
+                                               /* nzmax= */ no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i, 1.0));
+            }
+        }
+    } else {
+        if (L) {
+            IGRAPH_CHECK(igraph_matrix_resize(L, no_of_groups, no_of_nodes));
+            igraph_matrix_null(L);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int ) VECTOR(*groups)[i];
+                MATRIX(*L, g, i) = 1.0;
+            }
+        }
+        if (R) {
+            IGRAPH_CHECK(igraph_matrix_resize(R, no_of_groups, no_of_nodes));
+            igraph_matrix_null(R);
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                MATRIX(*R, g, i) = VECTOR(pnormed)[i];
+            }
+        }
+        if (Lsparse) {
+            IGRAPH_CHECK(igraph_sparsemat_init(Lsparse, no_of_groups, no_of_nodes,
+                                               /* nzmax= */ no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                IGRAPH_CHECK(igraph_sparsemat_entry(Lsparse, g, i, 1.0));
+            }
+        }
+        if (Rsparse) {
+            IGRAPH_CHECK(igraph_sparsemat_init(Rsparse, no_of_groups, no_of_nodes,
+                                               /* nzmax= */ no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                int g = (int) VECTOR(*groups)[i];
+                IGRAPH_CHECK(igraph_sparsemat_entry(Rsparse, g, i,
+                                                    VECTOR(pnormed)[i]));
+            }
+        }
+    }
+
+
+    igraph_vector_destroy(&pnormed);
+    igraph_vector_destroy(&pgr);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_scg_semiprojectors
+ * \brief Compute SCG semi-projectors for a given partition
+ *
+ * The three types of semi-projectors are defined as follows.
+ * Let gamma(j) label the group of vertex j in a partition of all the
+ * vertices.
+ *
+ * </para><para>
+ * The symmetric semi-projectors are defined as
+ * <blockquote><para><phrase role="math">
+ *   L[alpha,j] = R[alpha,j] = 1/sqrt(|alpha|) delta[alpha,gamma(j)],
+ * </phrase></para></blockquote>
+ * the (row) Laplacian semi-projectors as
+ * <blockquote><para><phrase role="math">
+ *   L[alpha,j] = 1/|alpha| delta[alpha,gamma(j)]
+ * </phrase></para></blockquote>
+ * and
+ * <blockquote><para><phrase role="math">
+ *   R[alpha,j] = delta[alpha,gamma(j)],
+ * </phrase></para></blockquote>
+ * and the (row) stochastic semi-projectors as
+ * <blockquote><para><phrase role="math">
+ *     L[alpha,j] = p[1][j] / sum(p[1][k]; k in gamma(j))
+ *     delta[alpha,gamma(j)]
+ * </phrase></para></blockquote>
+ * and
+ * <blockquote><para><phrase role="math">
+ *     R[alpha,j] = delta[alpha,gamma(j)],
+ * </phrase></para></blockquote>
+ * where p[1] is the (left) eigenvector associated with the
+ * one-eigenvalue of the stochastic matrix. L and R are
+ * defined in a symmetric way when \p norm is \c
+ * IGRAPH_SCG_NORM_COL. All these semi-projectors verify various
+ * properties described in the reference.
+ * \param groups A vector of integers, giving the group label of every
+ *    vertex in the partition. Group labels should start at zero and
+ *    should be sequential.
+ * \param mtype The type of semi-projectors. For now \c
+ *    IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and \c
+ *    IGRAP_SCG_LAPLACIAN are supported.
+ * \param L If not a \c NULL pointer, then it must be a pointer to
+ *    an initialized matrix. The left semi-projector is stored here.
+ * \param R If not a \c NULL pointer, then it must be a pointer to
+ *    an initialized matrix. The right semi-projector is stored here.
+ * \param Lsparse If not a \c NULL pointer, then it must be a pointer
+ *    to an uninitialized sparse matrix. The left semi-projector is
+ *    stored here.
+ * \param Rsparse If not a \c NULL pointer, then it must be a pointer
+ *    to an uninitialized sparse matrix. The right semi-projector is
+ *    stored here.
+ * \param p \c NULL, or a probability vector of the same length as \p
+ *    groups. \p p is the stationary probability distribution of a
+ *    Markov chain when \p mtype is \c IGRAPH_SCG_STOCHASTIC. This
+ *    argument is ignored in all other cases.
+ * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL.
+ *    Specifies whether the rows or the columns of the Laplacian
+ *    matrix sum up to zero, or whether the rows or the columns of the
+ *    stochastic matrix sum up to one.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_stochastic() and
+ * \ref igraph_scg_laplacian(), \ref igraph_scg_grouping().
+ *
+ * \example examples/simple/igraph_scg_semiprojectors.c
+ * \example examples/simple/igraph_scg_semiprojectors2.c
+ * \example examples/simple/igraph_scg_semiprojectors3.c
+ */
+
+int igraph_scg_semiprojectors(const igraph_vector_t *groups,
+                              igraph_scg_matrix_t mtype,
+                              igraph_matrix_t *L,
+                              igraph_matrix_t *R,
+                              igraph_sparsemat_t *Lsparse,
+                              igraph_sparsemat_t *Rsparse,
+                              const igraph_vector_t *p,
+                              igraph_scg_norm_t norm) {
+
+    int no_of_nodes = (int) igraph_vector_size(groups);
+    int no_of_groups;
+    igraph_real_t min, max;
+
+    igraph_vector_minmax(groups, &min, &max);
+    no_of_groups = (int) max + 1;
+
+    if (min < 0 || max >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid membership vector", IGRAPH_EINVAL);
+    }
+
+    if (mtype == IGRAPH_SCG_STOCHASTIC && !p) {
+        IGRAPH_ERROR("`p' must be given for the stochastic matrix case",
+                     IGRAPH_EINVAL);
+    }
+
+    if (p && igraph_vector_size(p) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid `p' vector length, should match number of vertices",
+                     IGRAPH_EINVAL);
+    }
+
+    switch (mtype) {
+    case IGRAPH_SCG_SYMMETRIC:
+        IGRAPH_CHECK(igraph_i_scg_semiprojectors_sym(groups, L, R, Lsparse,
+                     Rsparse, no_of_groups,
+                     no_of_nodes));
+        break;
+
+    case IGRAPH_SCG_LAPLACIAN:
+        IGRAPH_CHECK(igraph_i_scg_semiprojectors_lap(groups, L, R, Lsparse,
+                     Rsparse, no_of_groups,
+                     no_of_nodes, norm));
+        break;
+
+    case IGRAPH_SCG_STOCHASTIC:
+        IGRAPH_CHECK(igraph_i_scg_semiprojectors_sto(groups, L, R, Lsparse,
+                     Rsparse, no_of_groups,
+                     no_of_nodes, p, norm));
+        break;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_scg_norm_eps
+ * Calculate SCG residuals
+ *
+ * Computes |v[i]-Pv[i]|, where v[i] is the i-th eigenvector in \p V
+ * and P is the projector corresponding to the \p mtype argument.
+ *
+ * \param V The matrix of eigenvectors to be preserved by coarse
+ *    graining, each column is an eigenvector.
+ * \param groups A vector of integers, giving the group label of every
+ *    vertex in the partition. Group labels should start at zero and
+ *    should be sequential.
+ * \param eps Pointer to a real value, the result is stored here.
+ * \param mtype The type of semi-projectors. For now \c
+ *    IGRAPH_SCG_SYMMETRIC, \c IGRAPH_SCG_STOCHASTIC and \c
+ *    IGRAP_SCG_LAPLACIAN are supported.
+ * \param p \c NULL, or a probability vector of the same length as \p
+ *    groups. \p p is the stationary probability distribution of a
+ *    Markov chain when \p mtype is \c IGRAPH_SCG_STOCHASTIC. This
+ *    argument is ignored in all other cases.
+ * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL.
+ *    Specifies whether the rows or the columns of the Laplacian
+ *    matrix sum up to zero, or whether the rows or the columns of the
+ *    stochastic matrix sum up to one.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \sa \ref igraph_scg_adjacency(), \ref igraph_scg_stochastic() and
+ * \ref igraph_scg_laplacian(), \ref igraph_scg_grouping(), \ref
+ * igraph_scg_semiprojectors().
+ */
+
+int igraph_scg_norm_eps(const igraph_matrix_t *V,
+                        const igraph_vector_t *groups,
+                        igraph_vector_t *eps,
+                        igraph_scg_matrix_t mtype,
+                        const igraph_vector_t *p,
+                        igraph_scg_norm_t norm) {
+
+    int no_of_nodes = (int) igraph_vector_size(groups);
+    int no_of_groups;
+    int no_of_vectors = (int) igraph_matrix_ncol(V);
+    igraph_real_t min, max;
+    igraph_sparsemat_t Lsparse, Rsparse, Lsparse2, Rsparse2, Rsparse3, proj;
+    igraph_vector_t x, res;
+    int k, i;
+
+    if (igraph_matrix_nrow(V) != no_of_nodes) {
+        IGRAPH_ERROR("Eigenvector length and group vector length do not match",
+                     IGRAPH_EINVAL);
+    }
+
+    igraph_vector_minmax(groups, &min, &max);
+    no_of_groups = (int) max + 1;
+
+    if (min < 0 || max >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid membership vector", IGRAPH_EINVAL);
+    }
+
+    if (mtype == IGRAPH_SCG_STOCHASTIC && !p) {
+        IGRAPH_ERROR("`p' must be given for the stochastic matrix case",
+                     IGRAPH_EINVAL);
+    }
+
+    if (p && igraph_vector_size(p) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid `p' vector length, should match number of vertices",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_scg_semiprojectors(groups, mtype, /* L= */ 0,
+                                           /* R= */ 0, &Lsparse, &Rsparse, p,
+                                           norm));
+
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &Lsparse);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse);
+
+    IGRAPH_CHECK(igraph_sparsemat_compress(&Lsparse, &Lsparse2));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &Lsparse2);
+    IGRAPH_CHECK(igraph_sparsemat_compress(&Rsparse, &Rsparse2));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse2);
+    IGRAPH_CHECK(igraph_sparsemat_transpose(&Rsparse2, &Rsparse3,
+                                            /*values=*/ 1));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse3);
+
+    IGRAPH_CHECK(igraph_sparsemat_multiply(&Rsparse3, &Lsparse2, &proj));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &proj);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&res, no_of_nodes);
+    IGRAPH_CHECK(igraph_vector_resize(eps, no_of_vectors));
+
+    for (k = 0; k < no_of_vectors; k++) {
+        igraph_vector_view(&x, &MATRIX(*V, 0, k), no_of_nodes);
+        igraph_vector_null(&res);
+        IGRAPH_CHECK(igraph_sparsemat_gaxpy(&proj, &x, &res));
+        VECTOR(*eps)[k] = 0.0;
+        for (i = 0; i < no_of_nodes; i++) {
+            igraph_real_t di = MATRIX(*V, i, k) - VECTOR(res)[i];
+            VECTOR(*eps)[k] += di * di;
+        }
+        VECTOR(*eps)[k] = sqrt(VECTOR(*eps)[k]);
+    }
+
+    igraph_vector_destroy(&res);
+    igraph_sparsemat_destroy(&proj);
+    igraph_sparsemat_destroy(&Rsparse3);
+    igraph_sparsemat_destroy(&Rsparse2);
+    igraph_sparsemat_destroy(&Lsparse2);
+    igraph_sparsemat_destroy(&Rsparse);
+    igraph_sparsemat_destroy(&Lsparse);
+    IGRAPH_FINALLY_CLEAN(7);
+
+    return 0;
+}
+
+int igraph_i_matrix_laplacian(const igraph_matrix_t *matrix,
+                              igraph_matrix_t *mymatrix,
+                              igraph_scg_norm_t norm) {
+
+    igraph_vector_t degree;
+    int i, j, n = (int) igraph_matrix_nrow(matrix);
+    IGRAPH_CHECK(igraph_matrix_resize(mymatrix, n, n));
+
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, n);
+
+    if (norm == IGRAPH_SCG_NORM_ROW) {
+        IGRAPH_CHECK(igraph_matrix_rowsum(matrix, &degree));
+    } else {
+        IGRAPH_CHECK(igraph_matrix_colsum(matrix, &degree));
+    }
+    for (i = 0; i < n; i++) {
+        VECTOR(degree)[i] -= MATRIX(*matrix, i, i);
+    }
+
+    for (i = 0; i < n; i++) {
+        for (j = 0; j < n; j++) {
+            MATRIX(*mymatrix, i, j) = - MATRIX(*matrix, i, j);
+        }
+        MATRIX(*mymatrix, i, i) = VECTOR(degree)[i];
+    }
+
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_sparsemat_laplacian(const igraph_sparsemat_t *sparse,
+                                 igraph_sparsemat_t *mysparse,
+                                 igraph_scg_norm_t norm) {
+
+    igraph_vector_t degree;
+    int i, n = (int) igraph_sparsemat_nrow(sparse);
+    int nzmax = igraph_sparsemat_nzmax(sparse);
+    igraph_sparsemat_iterator_t it;
+
+    IGRAPH_CHECK(igraph_sparsemat_init(mysparse, n, n, nzmax + n));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparse);
+    igraph_sparsemat_iterator_init(&it, (igraph_sparsemat_t *) sparse);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, n);
+    for (igraph_sparsemat_iterator_reset(&it);
+         !igraph_sparsemat_iterator_end(&it);
+         igraph_sparsemat_iterator_next(&it)) {
+        int row = igraph_sparsemat_iterator_row(&it);
+        int col = igraph_sparsemat_iterator_col(&it);
+        if (row != col) {
+            igraph_real_t val = igraph_sparsemat_iterator_get(&it);
+            if (norm == IGRAPH_SCG_NORM_ROW) {
+                VECTOR(degree)[row] += val;
+            } else {
+                VECTOR(degree)[col] += val;
+            }
+        }
+    }
+
+    /* Diagonal */
+    for (i = 0; i < n; i++) {
+        igraph_sparsemat_entry(mysparse, i, i, VECTOR(degree)[i]);
+    }
+
+    /* And the rest, filter out diagonal elements */
+    for (igraph_sparsemat_iterator_reset(&it);
+         !igraph_sparsemat_iterator_end(&it);
+         igraph_sparsemat_iterator_next(&it)) {
+        int row = igraph_sparsemat_iterator_row(&it);
+        int col = igraph_sparsemat_iterator_col(&it);
+        if (row != col) {
+            igraph_real_t val = igraph_sparsemat_iterator_get(&it);
+            igraph_sparsemat_entry(mysparse, row, col, -val);
+        }
+    }
+
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(2);  /* + mysparse */
+
+    return 0;
+}
+
+int igraph_i_matrix_stochastic(const igraph_matrix_t *matrix,
+                               igraph_matrix_t *mymatrix,
+                               igraph_scg_norm_t norm) {
+
+    int i, j, n = (int) igraph_matrix_nrow(matrix);
+    IGRAPH_CHECK(igraph_matrix_copy(mymatrix, matrix));
+
+    if (norm == IGRAPH_SCG_NORM_ROW) {
+        for (i = 0; i < n; i++) {
+            igraph_real_t sum = 0.0;
+            for (j = 0; j < n; j++) {
+                sum += MATRIX(*matrix, i, j);
+            }
+            if (sum == 0) {
+                IGRAPH_WARNING("Zero degree vertices");
+            }
+            for (j = 0; j < n; j++) {
+                MATRIX(*mymatrix, i, j) = MATRIX(*matrix, i, j) / sum;
+            }
+        }
+    } else {
+        for (i = 0; i < n; i++) {
+            igraph_real_t sum = 0.0;
+            for (j = 0; j < n; j++) {
+                sum += MATRIX(*matrix, j, i);
+            }
+            if (sum == 0) {
+                IGRAPH_WARNING("Zero degree vertices");
+            }
+            for (j = 0; j < n; j++) {
+                MATRIX(*mymatrix, j, i) = MATRIX(*matrix, j, i) / sum;
+            }
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat,
+                                 igraph_bool_t column_wise);
+
+int igraph_i_sparsemat_stochastic(const igraph_sparsemat_t *sparse,
+                                  igraph_sparsemat_t *mysparse,
+                                  igraph_scg_norm_t norm) {
+
+    IGRAPH_CHECK(igraph_sparsemat_copy(mysparse, sparse));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparse);
+    IGRAPH_CHECK(igraph_i_normalize_sparsemat(mysparse,
+                 norm == IGRAPH_SCG_NORM_COL));
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_scg_get_result(igraph_scg_matrix_t type,
+                            const igraph_matrix_t *matrix,
+                            const igraph_sparsemat_t *sparsemat,
+                            const igraph_sparsemat_t *Lsparse,
+                            const igraph_sparsemat_t *Rsparse_t,
+                            igraph_t *scg_graph,
+                            igraph_matrix_t *scg_matrix,
+                            igraph_sparsemat_t *scg_sparsemat,
+                            igraph_bool_t directed) {
+
+    /* We need to calculate either scg_matrix (if input is dense), or
+       scg_sparsemat (if input is sparse). For the latter we might need
+       to temporarily use another matrix. */
+
+
+    if (matrix) {
+        igraph_matrix_t *my_scg_matrix = scg_matrix, v_scg_matrix;
+        igraph_matrix_t tmp;
+        igraph_sparsemat_t *myLsparse = (igraph_sparsemat_t *) Lsparse, v_Lsparse;
+
+        if (!scg_matrix) {
+            my_scg_matrix = &v_scg_matrix;
+            IGRAPH_CHECK(igraph_matrix_init(my_scg_matrix, 0, 0));
+            IGRAPH_FINALLY(igraph_matrix_destroy, my_scg_matrix);
+        }
+
+        if (!igraph_sparsemat_is_cc(Lsparse)) {
+            myLsparse = &v_Lsparse;
+            IGRAPH_CHECK(igraph_sparsemat_compress(Lsparse, myLsparse));
+            IGRAPH_FINALLY(igraph_sparsemat_destroy, myLsparse);
+        }
+
+        IGRAPH_CHECK(igraph_matrix_init(&tmp, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &tmp);
+        IGRAPH_CHECK(igraph_sparsemat_dense_multiply(matrix, Rsparse_t, &tmp));
+        IGRAPH_CHECK(igraph_sparsemat_multiply_by_dense(myLsparse, &tmp,
+                     my_scg_matrix));
+        igraph_matrix_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        if (scg_sparsemat) {
+            IGRAPH_CHECK(igraph_matrix_as_sparsemat(scg_sparsemat, my_scg_matrix,
+                                                    /* tol= */ 0));
+            IGRAPH_FINALLY(igraph_sparsemat_destroy, scg_sparsemat);
+        }
+
+        if (scg_graph) {
+            if (type != IGRAPH_SCG_LAPLACIAN) {
+                IGRAPH_CHECK(igraph_weighted_adjacency(scg_graph, my_scg_matrix,
+                                                       directed ?
+                                                       IGRAPH_ADJ_DIRECTED :
+                                                       IGRAPH_ADJ_UNDIRECTED,
+                                                       "weight", /*loops=*/ 1));
+            } else {
+                int i, j, n = (int) igraph_matrix_nrow(my_scg_matrix);
+                igraph_matrix_t tmp;
+                IGRAPH_MATRIX_INIT_FINALLY(&tmp, n, n);
+                for (i = 0; i < n; i++) {
+                    for (j = 0; j < n; j++) {
+                        MATRIX(tmp, i, j) = -MATRIX(*my_scg_matrix, i, j);
+                    }
+                    MATRIX(tmp, i, i) = 0;
+                }
+                IGRAPH_CHECK(igraph_weighted_adjacency(scg_graph, &tmp, directed ?
+                                                       IGRAPH_ADJ_DIRECTED :
+                                                       IGRAPH_ADJ_UNDIRECTED,
+                                                       "weight", /*loops=*/ 0));
+                igraph_matrix_destroy(&tmp);
+                IGRAPH_FINALLY_CLEAN(1);
+            }
+            IGRAPH_FINALLY(igraph_destroy, scg_graph);
+        }
+
+        if (scg_graph)     {
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        if (scg_sparsemat) {
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        if (!igraph_sparsemat_is_cc(Lsparse)) {
+            igraph_sparsemat_destroy(myLsparse);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        if (!scg_matrix) {
+            igraph_matrix_destroy(my_scg_matrix);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+    } else { /* sparsemat */
+        igraph_sparsemat_t *my_scg_sparsemat = scg_sparsemat, v_scg_sparsemat;
+        igraph_sparsemat_t tmp, *mysparsemat = (igraph_sparsemat_t *) sparsemat,
+                                 v_sparsemat, *myLsparse = (igraph_sparsemat_t *) Lsparse, v_Lsparse;
+        if (!scg_sparsemat) {
+            my_scg_sparsemat = &v_scg_sparsemat;
+        }
+        if (!igraph_sparsemat_is_cc(sparsemat)) {
+            mysparsemat = &v_sparsemat;
+            IGRAPH_CHECK(igraph_sparsemat_compress(sparsemat, mysparsemat));
+            IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat);
+        }
+        if (!igraph_sparsemat_is_cc(Lsparse)) {
+            myLsparse = &v_Lsparse;
+            IGRAPH_CHECK(igraph_sparsemat_compress(Lsparse, myLsparse));
+            IGRAPH_FINALLY(igraph_sparsemat_destroy, myLsparse);
+        }
+        IGRAPH_CHECK(igraph_sparsemat_multiply(mysparsemat, Rsparse_t,
+                                               &tmp));
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
+        IGRAPH_CHECK(igraph_sparsemat_multiply(myLsparse, &tmp,
+                                               my_scg_sparsemat));
+        igraph_sparsemat_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(1);
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, my_scg_sparsemat);
+
+        if (scg_matrix) {
+            IGRAPH_CHECK(igraph_sparsemat_as_matrix(scg_matrix, my_scg_sparsemat));
+        }
+        if (scg_graph) {
+            if (type != IGRAPH_SCG_LAPLACIAN) {
+                IGRAPH_CHECK(igraph_weighted_sparsemat(scg_graph, my_scg_sparsemat,
+                                                       directed, "weight",
+                                                       /*loops=*/ 1));
+            } else {
+                igraph_sparsemat_t tmp;
+                IGRAPH_CHECK(igraph_sparsemat_copy(&tmp, my_scg_sparsemat));
+                IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
+                IGRAPH_CHECK(igraph_sparsemat_neg(&tmp));
+                IGRAPH_CHECK(igraph_weighted_sparsemat(scg_graph, &tmp, directed,
+                                                       "weight", /*loops=*/ 0));
+                igraph_sparsemat_destroy(&tmp);
+                IGRAPH_FINALLY_CLEAN(1);
+            }
+            IGRAPH_FINALLY(igraph_destroy, scg_graph);
+        }
+
+        if (scg_graph) {
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        if (!scg_sparsemat) {
+            igraph_sparsemat_destroy(my_scg_sparsemat);
+        }
+        IGRAPH_FINALLY_CLEAN(1);    /* my_scg_sparsemat */
+        if (!igraph_sparsemat_is_cc(Lsparse)) {
+            igraph_sparsemat_destroy(myLsparse);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        if (!igraph_sparsemat_is_cc(sparsemat)) {
+            igraph_sparsemat_destroy(mysparsemat);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_scg_common_checks(const igraph_t *graph,
+                               const igraph_matrix_t *matrix,
+                               const igraph_sparsemat_t *sparsemat,
+                               const igraph_vector_t *ev,
+                               igraph_integer_t nt,
+                               const igraph_vector_t *nt_vec,
+                               const igraph_matrix_t *vectors,
+                               const igraph_matrix_complex_t *vectors_cmplx,
+                               const igraph_vector_t *groups,
+                               const igraph_t *scg_graph,
+                               const igraph_matrix_t *scg_matrix,
+                               const igraph_sparsemat_t *scg_sparsemat,
+                               const igraph_vector_t *p,
+                               igraph_real_t *evmin, igraph_real_t *evmax) {
+
+    int no_of_nodes = -1;
+    igraph_real_t min, max;
+    int no_of_ev = (int) igraph_vector_size(ev);
+
+    if ( (graph ? 1 : 0) + (matrix ? 1 : 0) + (sparsemat ? 1 : 0) != 1 ) {
+        IGRAPH_ERROR("Give exactly one of `graph', `matrix' and `sparsemat'",
+                     IGRAPH_EINVAL);
+    }
+
+    if (graph) {
+        no_of_nodes = igraph_vcount(graph);
+    } else if (matrix) {
+        no_of_nodes = (int) igraph_matrix_nrow(matrix);
+    } else if (sparsemat) {
+        no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat);
+    }
+
+    if ((matrix && igraph_matrix_ncol(matrix) != no_of_nodes) ||
+        (sparsemat && igraph_sparsemat_ncol(sparsemat) != no_of_nodes)) {
+        IGRAPH_ERROR("Matrix must be square", IGRAPH_NONSQUARE);
+    }
+
+    igraph_vector_minmax(ev, evmin, evmax);
+    if (*evmin < 0 || *evmax >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid eigenvectors given", IGRAPH_EINVAL);
+    }
+
+    if (!nt_vec && (nt <= 1 || nt >= no_of_nodes)) {
+        IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL);
+    }
+
+    if (nt_vec) {
+        if (igraph_vector_size(nt_vec) != 1 &&
+            igraph_vector_size(nt_vec) != no_of_ev) {
+            IGRAPH_ERROR("Invalid length for interval specification",
+                         IGRAPH_EINVAL);
+        }
+        igraph_vector_minmax(nt_vec, &min, &max);
+        if (min <= 1 || max >= no_of_nodes) {
+            IGRAPH_ERROR("Invalid interval specification", IGRAPH_EINVAL);
+        }
+    }
+
+    if (vectors && igraph_matrix_size(vectors) != 0 &&
+        (igraph_matrix_ncol(vectors) != no_of_ev ||
+         igraph_matrix_nrow(vectors) != no_of_nodes)) {
+        IGRAPH_ERROR("Invalid eigenvector matrix size", IGRAPH_EINVAL);
+    }
+
+    if (vectors_cmplx && igraph_matrix_complex_size(vectors_cmplx) != 0 &&
+        (igraph_matrix_complex_ncol(vectors_cmplx) != no_of_ev ||
+         igraph_matrix_complex_nrow(vectors_cmplx) != no_of_nodes)) {
+        IGRAPH_ERROR("Invalid eigenvector matrix size", IGRAPH_EINVAL);
+    }
+
+    if (groups && igraph_vector_size(groups) != 0 &&
+        igraph_vector_size(groups) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid `groups' vector size", IGRAPH_EINVAL);
+    }
+
+    if ( (scg_graph != 0) + (scg_matrix != 0) + (scg_sparsemat != 0) == 0 ) {
+        IGRAPH_ERROR("No output is requested, please give at least one of "
+                     "`scg_graph', `scg_matrix' and `scg_sparsemat'",
+                     IGRAPH_EINVAL);
+    }
+
+    if (p && igraph_vector_size(p) != 0 &&
+        igraph_vector_size(p) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid `p' vector size", IGRAPH_EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_scg_adjacency
+ * Spectral coarse graining, symmetric case.
+ *
+ * This function handles all the steps involved in the Spectral Coarse
+ * Graining (SCG) of some matrices and graphs as described in the
+ * reference below.
+ *
+ * \param graph The input graph. Exactly one of \p graph, \p matrix
+ *    and \p sparsemat must be given, the other two must be \c NULL
+ *    pointers.
+ * \param matrix The input matrix. Exactly one of \p graph, \p matrix
+ *    and \p sparsemat must be given, the other two must be \c NULL
+ *    pointers.
+ * \param sparsemat The input sparse matrix. Exactly one of \p graph,
+ *    \p matrix and \p sparsemat must be given, the other two must be
+ *    \c NULL pointers.
+ * \param ev A vector of positive integers giving the indexes of the
+ *   eigenpairs to be preserved. 1 designates the eigenvalue with
+ *    largest algebraic value, 2 the one with second largest algebraic
+ *    value, etc.
+ * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM,
+ *    it gives the number of groups to partition each eigenvector
+ *    separately. When \p algo is \c IGRAPH_SCG_INTERV or \c
+ *    IGRAPH_SCG_INTERV_KM, it gives the number of intervals to
+ *    partition each eigenvector. This is ignored when \p algo is \c
+ *    IGRAPH_SCG_EXACT.
+ * \param nt_vec A numeric vector of length one or the length must
+ *    match the number of eigenvectors given in \p V, or a \c NULL
+ *    pointer. If not \c NULL, then this argument gives the number of
+ *    groups or intervals, and \p nt is ignored. Different number of
+ *    groups or intervals can be specified for each eigenvector.
+ * \param algo The algorithm to solve the SCG problem. Possible
+ *    values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c
+ *    IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the
+ *    details about them above.
+ * \param values If this is not \c NULL and the eigenvectors are
+ *    re-calculated, then the eigenvalues are stored here.
+ * \param vectors If this is not \c NULL, and not a zero-length
+ *    matrix, then it is interpreted as the eigenvectors to use for
+ *    the coarse-graining. Otherwise the eigenvectors are
+ *    re-calculated, and they are stored here. (If this is not \c NULL.)
+ * \param groups If this is not \c NULL, and not a zero-length vector,
+ *    then it is interpreted as the vector of group labels. (Group
+ *    labels are integers from zero and are sequential.) Otherwise
+ *    group labels are re-calculated and stored here, if this argument
+ *    is not a null pointer.
+ * \param use_arpack Whether to use ARPACK for solving the
+ *    eigenproblem. Currently ARPACK is not implemented.
+ * \param maxiter A positive integer giving the number of iterations
+ *    of the k-means algorithm when \p algo is \c
+ *    IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable
+ *    (initial) value for this argument is 100.
+ * \param scg_graph If not a \c NULL pointer, then the coarse-grained
+ *    graph is returned here.
+ * \param scg_matrix If not a \c NULL pointer, then it must be an
+ *    initialied matrix, and the coarse-grained matrix is returned
+ *    here.
+ * \param scg_sparsemat If not a \c NULL pointer, then the coarse
+ *    grained matrix is returned here, in sparse matrix form.
+ * \param L If not a \c NULL pointer, then it must be an initialized
+ *    matrix and the left semi-projector is returned here.
+ * \param R If not a \c NULL pointer, then it must be an initialized
+ *    matrix and the right semi-projector is returned here.
+ * \param Lsparse If not a \c NULL pointer, then the left
+ *    semi-projector is returned here.
+ * \param Rsparse If not a \c NULL pointer, then the right
+ *    semi-projector is returned here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \sa \ref igraph_scg_grouping(), \ref igraph_scg_semiprojectors(),
+ * \ref igraph_scg_stochastic() and \ref igraph_scg_laplacian().
+ *
+ * \example examples/simple/scg.c
+ */
+
+int igraph_scg_adjacency(const igraph_t *graph,
+                         const igraph_matrix_t *matrix,
+                         const igraph_sparsemat_t *sparsemat,
+                         const igraph_vector_t *ev,
+                         igraph_integer_t nt,
+                         const igraph_vector_t *nt_vec,
+                         igraph_scg_algorithm_t algo,
+                         igraph_vector_t *values,
+                         igraph_matrix_t *vectors,
+                         igraph_vector_t *groups,
+                         igraph_bool_t use_arpack,
+                         igraph_integer_t maxiter,
+                         igraph_t *scg_graph,
+                         igraph_matrix_t *scg_matrix,
+                         igraph_sparsemat_t *scg_sparsemat,
+                         igraph_matrix_t *L,
+                         igraph_matrix_t *R,
+                         igraph_sparsemat_t *Lsparse,
+                         igraph_sparsemat_t *Rsparse) {
+
+    igraph_sparsemat_t *mysparsemat = (igraph_sparsemat_t*) sparsemat,
+                        real_sparsemat;
+    int no_of_ev = (int) igraph_vector_size(ev);
+    /* eigenvectors are calculated and returned */
+    igraph_bool_t do_vectors = vectors && igraph_matrix_size(vectors) == 0;
+    /* groups are calculated */
+    igraph_bool_t do_groups = !groups || igraph_vector_size(groups) == 0;
+    /* eigenvectors are not returned but must be calculated for groups */
+    igraph_bool_t tmp_vectors = !do_vectors && do_groups;
+    /* need temporary vector for groups */
+    igraph_bool_t tmp_groups = !groups;
+    igraph_matrix_t myvectors;
+    igraph_vector_t mygroups;
+    igraph_bool_t tmp_lsparse = !Lsparse, tmp_rsparse = !Rsparse;
+    igraph_sparsemat_t myLsparse, myRsparse, tmpsparse, Rsparse_t;
+    int no_of_nodes;
+    igraph_real_t evmin, evmax;
+    igraph_bool_t directed;
+
+    /* --------------------------------------------------------------------*/
+    /* Argument checks */
+
+    IGRAPH_CHECK(igraph_i_scg_common_checks(graph, matrix, sparsemat,
+                                            ev, nt, nt_vec,
+                                            vectors, 0, groups, scg_graph,
+                                            scg_matrix, scg_sparsemat,
+                                            /*p=*/ 0, &evmin, &evmax));
+
+    if (graph) {
+        no_of_nodes = igraph_vcount(graph);
+        directed = igraph_is_directed(graph);
+    } else if (matrix) {
+        no_of_nodes = (int) igraph_matrix_nrow(matrix);
+        directed = !igraph_matrix_is_symmetric(matrix);
+    } else {
+        no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat);
+        directed = !igraph_sparsemat_is_symmetric(sparsemat);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Convert graph, if needed */
+
+    if (graph) {
+        mysparsemat = &real_sparsemat;
+        IGRAPH_CHECK(igraph_get_sparsemat(graph, mysparsemat));
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Compute eigenpairs, if needed */
+    if (tmp_vectors) {
+        vectors = &myvectors;
+        IGRAPH_MATRIX_INIT_FINALLY(vectors, no_of_nodes, no_of_ev);
+    }
+
+    if (do_vectors || tmp_vectors) {
+        igraph_arpack_options_t options;
+        igraph_eigen_which_t which;
+        igraph_matrix_t tmp;
+        igraph_vector_t tmpev;
+        igraph_vector_t tmpeval;
+        int i;
+
+        which.pos = IGRAPH_EIGEN_SELECT;
+        which.il = (int) (no_of_nodes - evmax + 1);
+        which.iu = (int) (no_of_nodes - evmin + 1);
+
+        if (values) {
+            IGRAPH_VECTOR_INIT_FINALLY(&tmpeval, 0);
+        }
+        IGRAPH_CHECK(igraph_matrix_init(&tmp, no_of_nodes,
+                                        which.iu - which.il + 1));
+        IGRAPH_FINALLY(igraph_matrix_destroy, &tmp);
+        IGRAPH_CHECK(igraph_eigen_matrix_symmetric(matrix, mysparsemat,
+                     /* fun= */ 0, no_of_nodes,
+                     /* extra= */ 0,
+                     /* algorithm= */
+                     use_arpack ?
+                     IGRAPH_EIGEN_ARPACK :
+                     IGRAPH_EIGEN_LAPACK, &which,
+                     &options, /*storage=*/ 0,
+                     values ? &tmpeval : 0,
+                     &tmp));
+        IGRAPH_VECTOR_INIT_FINALLY(&tmpev, no_of_ev);
+        for (i = 0; i < no_of_ev; i++) {
+            VECTOR(tmpev)[i] = evmax - VECTOR(*ev)[i];
+        }
+        if (values) {
+            IGRAPH_CHECK(igraph_vector_index(&tmpeval, values, &tmpev));
+        }
+        IGRAPH_CHECK(igraph_matrix_select_cols(&tmp, vectors, &tmpev));
+        igraph_vector_destroy(&tmpev);
+        igraph_matrix_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(2);
+        if (values) {
+            igraph_vector_destroy(&tmpeval);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Work out groups, if needed */
+    if (tmp_groups) {
+        groups = &mygroups;
+        IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)groups, no_of_nodes);
+    }
+    if (do_groups) {
+        IGRAPH_CHECK(igraph_scg_grouping(vectors, (igraph_vector_t*)groups,
+                                         nt, nt_vec,
+                                         IGRAPH_SCG_SYMMETRIC, algo,
+                                         /*p=*/ 0, maxiter));
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Perform coarse graining */
+    if (tmp_lsparse) {
+        Lsparse = &myLsparse;
+    }
+    if (tmp_rsparse) {
+        Rsparse = &myRsparse;
+    }
+    IGRAPH_CHECK(igraph_scg_semiprojectors(groups, IGRAPH_SCG_SYMMETRIC,
+                                           L, R, Lsparse, Rsparse, /*p=*/ 0,
+                                           IGRAPH_SCG_NORM_ROW));
+    if (tmp_groups) {
+        igraph_vector_destroy((igraph_vector_t*) groups);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (tmp_vectors) {
+        igraph_matrix_destroy(vectors);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (Rsparse) {
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, Rsparse);
+    }
+    if (Lsparse) {
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, Lsparse);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Compute coarse grained matrix/graph/sparse matrix */
+    IGRAPH_CHECK(igraph_sparsemat_compress(Rsparse, &tmpsparse));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmpsparse);
+    IGRAPH_CHECK(igraph_sparsemat_transpose(&tmpsparse, &Rsparse_t,
+                                            /*values=*/ 1));
+    igraph_sparsemat_destroy(&tmpsparse);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse_t);
+
+    IGRAPH_CHECK(igraph_i_scg_get_result(IGRAPH_SCG_SYMMETRIC,
+                                         matrix, mysparsemat,
+                                         Lsparse, &Rsparse_t,
+                                         scg_graph, scg_matrix,
+                                         scg_sparsemat, directed));
+
+    /* -------------------------------------------------------------------- */
+    /* Clean up */
+
+    igraph_sparsemat_destroy(&Rsparse_t);
+    IGRAPH_FINALLY_CLEAN(1);
+    if (Lsparse) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (Rsparse) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (graph) {
+        igraph_sparsemat_destroy(mysparsemat);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_scg_stochastic
+ * Spectral coarse graining, stochastic case.
+ *
+ * This function handles all the steps involved in the Spectral Coarse
+ * Graining (SCG) of some matrices and graphs as described in the
+ * reference below.
+ *
+ * \param graph The input graph. Exactly one of \p graph, \p matrix
+ *    and \p sparsemat must be given, the other two must be \c NULL
+ *    pointers.
+ * \param matrix The input matrix. Exactly one of \p graph, \p matrix
+ *    and \p sparsemat must be given, the other two must be \c NULL
+ *    pointers.
+ * \param sparsemat The input sparse matrix. Exactly one of \p graph,
+ *    \p matrix and \p sparsemat must be given, the other two must be
+ *    \c NULL pointers.
+ * \param ev A vector of positive integers giving the indexes of the
+ *   eigenpairs to be preserved. 1 designates the eigenvalue with
+ *    largest magnitude, 2 the one with second largest magnitude, etc.
+ * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM,
+ *    it gives the number of groups to partition each eigenvector
+ *    separately. When \p algo is \c IGRAPH_SCG_INTERV or \c
+ *    IGRAPH_SCG_INTERV_KM, it gives the number of intervals to
+ *    partition each eigenvector. This is ignored when \p algo is \c
+ *    IGRAPH_SCG_EXACT.
+ * \param nt_vec A numeric vector of length one or the length must
+ *    match the number of eigenvectors given in \p V, or a \c NULL
+ *    pointer. If not \c NULL, then this argument gives the number of
+ *    groups or intervals, and \p nt is ignored. Different number of
+ *    groups or intervals can be specified for each eigenvector.
+ * \param algo The algorithm to solve the SCG problem. Possible
+ *    values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c
+ *    IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the
+ *    details about them above.
+ * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL.
+ *    Specifies whether the rows or the columns of the
+ *    stochastic matrix sum up to one.
+ * \param values If this is not \c NULL and the eigenvectors are
+ *    re-calculated, then the eigenvalues are stored here.
+ * \param vectors If this is not \c NULL, and not a zero-length
+ *    matrix, then it is interpreted as the eigenvectors to use for
+ *    the coarse-graining. Otherwise the eigenvectors are
+ *    re-calculated, and they are stored here. (If this is not \c NULL.)
+ * \param groups If this is not \c NULL, and not a zero-length vector,
+ *    then it is interpreted as the vector of group labels. (Group
+ *    labels are integers from zero and are sequential.) Otherwise
+ *    group labels are re-calculated and stored here, if this argument
+ *    is not a null pointer.
+ * \param p If this is not \c NULL, and not zero length, then it is
+ *    interpreted as the stationary probability distribution of the
+ *    Markov chain corresponding to the input matrix/graph. Its length
+ *    must match the number of  vertices in the input graph (or number
+ *    of rows in the input matrix). If not given, then the stationary
+ *    distribution is calculated and stored here. (Unless this
+ *    argument is a \c NULL pointer, in which case it is not stored.)
+ * \param use_arpack Whether to use ARPACK for solving the
+ *    eigenproblem. Currently ARPACK is not implemented.
+ * \param maxiter A positive integer giving the number of iterations
+ *    of the k-means algorithm when \p algo is \c
+ *    IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable
+ *    (initial) value for this argument is 100.
+ * \param scg_graph If not a \c NULL pointer, then the coarse-grained
+ *    graph is returned here.
+ * \param scg_matrix If not a \c NULL pointer, then it must be an
+ *    initialied matrix, and the coarse-grained matrix is returned
+ *    here.
+ * \param scg_sparsemat If not a \c NULL pointer, then the coarse
+ *    grained matrix is returned here, in sparse matrix form.
+ * \param L If not a \c NULL pointer, then it must be an initialized
+ *    matrix and the left semi-projector is returned here.
+ * \param R If not a \c NULL pointer, then it must be an initialized
+ *    matrix and the right semi-projector is returned here.
+ * \param Lsparse If not a \c NULL pointer, then the left
+ *    semi-projector is returned here.
+ * \param Rsparse If not a \c NULL pointer, then the right
+ *    semi-projector is returned here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \sa \ref igraph_scg_grouping(), \ref igraph_scg_semiprojectors(),
+ * \ref igraph_scg_adjacency() and \ref igraph_scg_laplacian().
+ *
+ * \example examples/simple/scg2.c
+ */
+
+int igraph_scg_stochastic(const igraph_t *graph,
+                          const igraph_matrix_t *matrix,
+                          const igraph_sparsemat_t *sparsemat,
+                          const igraph_vector_t *ev,
+                          igraph_integer_t nt,
+                          const igraph_vector_t *nt_vec,
+                          igraph_scg_algorithm_t algo,
+                          igraph_scg_norm_t norm,
+                          igraph_vector_complex_t *values,
+                          igraph_matrix_complex_t *vectors,
+                          igraph_vector_t *groups,
+                          igraph_vector_t *p,
+                          igraph_bool_t use_arpack,
+                          igraph_integer_t maxiter,
+                          igraph_t *scg_graph,
+                          igraph_matrix_t *scg_matrix,
+                          igraph_sparsemat_t *scg_sparsemat,
+                          igraph_matrix_t *L,
+                          igraph_matrix_t *R,
+                          igraph_sparsemat_t *Lsparse,
+                          igraph_sparsemat_t *Rsparse) {
+
+    igraph_matrix_t *mymatrix = (igraph_matrix_t*) matrix, real_matrix;
+    igraph_sparsemat_t *mysparsemat = (igraph_sparsemat_t*) sparsemat,
+                        real_sparsemat;
+    int no_of_nodes;
+    igraph_real_t evmin, evmax;
+    igraph_arpack_options_t options;
+    igraph_eigen_which_t which;
+    /* eigenvectors are calculated and returned */
+    igraph_bool_t do_vectors = vectors && igraph_matrix_complex_size(vectors) == 0;
+    /* groups are calculated */
+    igraph_bool_t do_groups = !groups || igraph_vector_size(groups) == 0;
+    igraph_bool_t tmp_groups = !groups;
+    /* eigenvectors are not returned but must be calculated for groups */
+    igraph_bool_t tmp_vectors = !do_vectors && do_groups;
+    igraph_matrix_complex_t myvectors;
+    igraph_vector_t mygroups;
+    igraph_bool_t do_p = !p || igraph_vector_size(p) == 0;
+    igraph_vector_t *myp = (igraph_vector_t *) p, real_p;
+    int no_of_ev = (int) igraph_vector_size(ev);
+    igraph_bool_t tmp_lsparse = !Lsparse, tmp_rsparse = !Rsparse;
+    igraph_sparsemat_t myLsparse, myRsparse, tmpsparse, Rsparse_t;
+
+    /* --------------------------------------------------------------------*/
+    /* Argument checks */
+
+    IGRAPH_CHECK(igraph_i_scg_common_checks(graph, matrix, sparsemat,
+                                            ev, nt, nt_vec,
+                                            0, vectors, groups, scg_graph,
+                                            scg_matrix, scg_sparsemat, p,
+                                            &evmin, &evmax));
+
+    if (graph) {
+        no_of_nodes = igraph_vcount(graph);
+    } else if (matrix) {
+        no_of_nodes = (int) igraph_matrix_nrow(matrix);
+    } else {
+        no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Convert graph, if needed */
+
+    if (graph) {
+        mysparsemat = &real_sparsemat;
+        IGRAPH_CHECK(igraph_get_stochastic_sparsemat(graph, mysparsemat,
+                     norm == IGRAPH_SCG_NORM_COL));
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat);
+    } else if (matrix) {
+        mymatrix = &real_matrix;
+        IGRAPH_CHECK(igraph_i_matrix_stochastic(matrix, mymatrix, norm));
+        IGRAPH_FINALLY(igraph_matrix_destroy, mymatrix);
+    } else { /* sparsemat */
+        mysparsemat = &real_sparsemat;
+        IGRAPH_CHECK(igraph_i_sparsemat_stochastic(sparsemat, mysparsemat, norm));
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Compute eigenpairs, if needed */
+
+    if (tmp_vectors) {
+        vectors = &myvectors;
+        IGRAPH_CHECK(igraph_matrix_complex_init(vectors, no_of_nodes, no_of_ev));
+        IGRAPH_FINALLY(igraph_matrix_complex_destroy, vectors);
+    }
+
+    if (do_vectors || tmp_vectors) {
+        igraph_matrix_complex_t tmp;
+        igraph_vector_t tmpev;
+        igraph_vector_complex_t tmpeval;
+        int i;
+
+        which.pos = IGRAPH_EIGEN_SELECT;
+        which.il = (int) (no_of_nodes - evmax + 1);
+        which.iu = (int) (no_of_nodes - evmin + 1);
+
+        if (values) {
+            IGRAPH_CHECK(igraph_vector_complex_init(&tmpeval, 0));
+            IGRAPH_FINALLY(igraph_vector_complex_destroy, &tmpeval);
+        }
+        IGRAPH_CHECK(igraph_matrix_complex_init(&tmp, no_of_nodes,
+                                                which.iu - which.il + 1));
+        IGRAPH_FINALLY(igraph_matrix_complex_destroy, &tmp);
+        IGRAPH_CHECK(igraph_eigen_matrix(mymatrix, mysparsemat, /*fun=*/ 0,
+                                         no_of_nodes, /*extra=*/ 0, use_arpack ?
+                                         IGRAPH_EIGEN_ARPACK :
+                                         IGRAPH_EIGEN_LAPACK, &which, &options,
+                                         /*storage=*/ 0,
+                                         values ? &tmpeval : 0, &tmp));
+
+        IGRAPH_VECTOR_INIT_FINALLY(&tmpev, no_of_ev);
+        for (i = 0; i < no_of_ev; i++) {
+            VECTOR(tmpev)[i] = evmax - VECTOR(*ev)[i];
+        }
+        if (values) {
+            IGRAPH_CHECK(igraph_vector_complex_index(&tmpeval, values, &tmpev));
+        }
+        IGRAPH_CHECK(igraph_matrix_complex_select_cols(&tmp, vectors, &tmpev));
+        igraph_vector_destroy(&tmpev);
+        igraph_matrix_complex_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(2);
+        if (values) {
+            igraph_vector_complex_destroy(&tmpeval);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    /* Compute p if not supplied */
+    if (do_p) {
+        igraph_eigen_which_t w;
+        igraph_matrix_complex_t tmp;
+        igraph_arpack_options_t o;
+        igraph_matrix_t trans, *mytrans = &trans;
+        igraph_sparsemat_t sparse_trans, *mysparse_trans = &sparse_trans;
+        int i;
+        igraph_arpack_options_init(&o);
+        if (!p) {
+            IGRAPH_VECTOR_INIT_FINALLY(&real_p, no_of_nodes);
+            myp = &real_p;
+        } else {
+            IGRAPH_CHECK(igraph_vector_resize(p, no_of_nodes));
+        }
+        IGRAPH_CHECK(igraph_matrix_complex_init(&tmp, 0, 0));
+        IGRAPH_FINALLY(igraph_matrix_complex_destroy, &tmp);
+        w.pos = IGRAPH_EIGEN_LR;
+        w.howmany = 1;
+
+        if (mymatrix) {
+            IGRAPH_CHECK(igraph_matrix_copy(&trans, mymatrix));
+            IGRAPH_FINALLY(igraph_matrix_destroy, &trans);
+            IGRAPH_CHECK(igraph_matrix_transpose(&trans));
+            mysparse_trans = 0;
+        } else {
+            IGRAPH_CHECK(igraph_sparsemat_transpose(mysparsemat, &sparse_trans,
+                                                    /*values=*/ 1));
+            IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparse_trans);
+            mytrans = 0;
+        }
+
+        IGRAPH_CHECK(igraph_eigen_matrix(mytrans, mysparse_trans, /*fun=*/ 0,
+                                         no_of_nodes, /*extra=*/ 0, /*algorith=*/
+                                         use_arpack ?
+                                         IGRAPH_EIGEN_ARPACK :
+                                         IGRAPH_EIGEN_LAPACK, &w, &o,
+                                         /*storage=*/ 0, /*values=*/ 0, &tmp));
+
+        if (mymatrix) {
+            igraph_matrix_destroy(&trans);
+            IGRAPH_FINALLY_CLEAN(1);
+        } else {
+            igraph_sparsemat_destroy(mysparse_trans);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        for (i = 0; i < no_of_nodes; i++) {
+            VECTOR(*myp)[i] = fabs(IGRAPH_REAL(MATRIX(tmp, i, 0)));
+        }
+        igraph_matrix_complex_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Work out groups, if needed */
+    /* TODO: use complex part as well */
+    if (tmp_groups) {
+        groups = &mygroups;
+        IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)groups, no_of_nodes);
+    }
+    if (do_groups) {
+        igraph_matrix_t tmp;
+        IGRAPH_MATRIX_INIT_FINALLY(&tmp, 0, 0);
+        IGRAPH_CHECK(igraph_matrix_complex_real(vectors, &tmp));
+        IGRAPH_CHECK(igraph_scg_grouping(&tmp, (igraph_vector_t*)groups,
+                                         nt, nt_vec,
+                                         IGRAPH_SCG_STOCHASTIC, algo,
+                                         myp, maxiter));
+        igraph_matrix_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Perform coarse graining */
+    if (tmp_lsparse) {
+        Lsparse = &myLsparse;
+    }
+    if (tmp_rsparse) {
+        Rsparse = &myRsparse;
+    }
+    IGRAPH_CHECK(igraph_scg_semiprojectors(groups, IGRAPH_SCG_STOCHASTIC,
+                                           L, R, Lsparse, Rsparse, myp, norm));
+    if (tmp_groups) {
+        igraph_vector_destroy((igraph_vector_t*) groups);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (!p && do_p) {
+        igraph_vector_destroy(myp);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (tmp_vectors) {
+        igraph_matrix_complex_destroy(vectors);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (Rsparse) {
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, Rsparse);
+    }
+    if (Lsparse) {
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, Lsparse);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Compute coarse grained matrix/graph/sparse matrix */
+    IGRAPH_CHECK(igraph_sparsemat_compress(Rsparse, &tmpsparse));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmpsparse);
+    IGRAPH_CHECK(igraph_sparsemat_transpose(&tmpsparse, &Rsparse_t,
+                                            /*values=*/ 1));
+    igraph_sparsemat_destroy(&tmpsparse);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse_t);
+
+    IGRAPH_CHECK(igraph_i_scg_get_result(IGRAPH_SCG_STOCHASTIC,
+                                         mymatrix, mysparsemat,
+                                         Lsparse, &Rsparse_t,
+                                         scg_graph, scg_matrix,
+                                         scg_sparsemat, /*directed=*/ 1));
+
+    /* -------------------------------------------------------------------- */
+    /* Clean up */
+
+    igraph_sparsemat_destroy(&Rsparse_t);
+    IGRAPH_FINALLY_CLEAN(1);
+    if (Lsparse) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (Rsparse) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (graph) {
+        igraph_sparsemat_destroy(mysparsemat);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else if (matrix) {
+        igraph_matrix_destroy(mymatrix);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        igraph_sparsemat_destroy(mysparsemat);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_scg_laplacian
+ * Spectral coarse graining, laplacian matrix.
+ * This function handles all the steps involved in the Spectral Coarse
+ * Graining (SCG) of some matrices and graphs as described in the
+ * reference below.
+ *
+ * \param graph The input graph. Exactly one of \p graph, \p matrix
+ *    and \p sparsemat must be given, the other two must be \c NULL
+ *    pointers.
+ * \param matrix The input matrix. Exactly one of \p graph, \p matrix
+ *    and \p sparsemat must be given, the other two must be \c NULL
+ *    pointers.
+ * \param sparsemat The input sparse matrix. Exactly one of \p graph,
+ *    \p matrix and \p sparsemat must be given, the other two must be
+ *    \c NULL pointers.
+ * \param ev A vector of positive integers giving the indexes of the
+ *   eigenpairs to be preserved. 1 designates the eigenvalue with
+ *    largest magnitude, 2 the one with second largest magnitude, etc.
+ * \param nt Positive integer. When \p algo is \c IGRAPH_SCG_OPTIMUM,
+ *    it gives the number of groups to partition each eigenvector
+ *    separately. When \p algo is \c IGRAPH_SCG_INTERV or \c
+ *    IGRAPH_SCG_INTERV_KM, it gives the number of intervals to
+ *    partition each eigenvector. This is ignored when \p algo is \c
+ *    IGRAPH_SCG_EXACT.
+ * \param nt_vec A numeric vector of length one or the length must
+ *    match the number of eigenvectors given in \p V, or a \c NULL
+ *    pointer. If not \c NULL, then this argument gives the number of
+ *    groups or intervals, and \p nt is ignored. Different number of
+ *    groups or intervals can be specified for each eigenvector.
+ * \param algo The algorithm to solve the SCG problem. Possible
+ *    values: \c IGRAPH_SCG_OPTIMUM, \c IGRAPH_SCG_INTERV_KM, \c
+ *    IGRAPH_SCG_INTERV and \c IGRAPH_SCG_EXACT. Please see the
+ *    details about them above.
+ * \param norm Either \c IGRAPH_SCG_NORM_ROW or \c IGRAPH_SCG_NORM_COL.
+ *    Specifies whether the rows or the columns of the Laplacian
+ *    matrix sum up to zero.
+ * \param direction Whether to work with left or right eigenvectors.
+ *    Possible values: \c IGRAPH_SCG_DIRECTION_DEFAULT, \c
+ *    IGRAPH_SCG_DIRECTION_LEFT, \c IGRAPH_SCG_DIRECTION_RIGHT. This
+ *    argument is currently ignored and right eigenvectors are always
+ *    used.
+ * \param values If this is not \c NULL and the eigenvectors are
+ *    re-calculated, then the eigenvalues are stored here.
+ * \param vectors If this is not \c NULL, and not a zero-length
+ *    matrix, then it is interpreted as the eigenvectors to use for
+ *    the coarse-graining. Otherwise the eigenvectors are
+ *    re-calculated, and they are stored here. (If this is not \c NULL.)
+ * \param groups If this is not \c NULL, and not a zero-length vector,
+ *    then it is interpreted as the vector of group labels. (Group
+ *    labels are integers from zero and are sequential.) Otherwise
+ *    group labels are re-calculated and stored here, if this argument
+ *    is not a null pointer.
+ * \param use_arpack Whether to use ARPACK for solving the
+ *    eigenproblem. Currently ARPACK is not implemented.
+ * \param maxiter A positive integer giving the number of iterations
+ *    of the k-means algorithm when \p algo is \c
+ *    IGRAPH_SCG_INTERV_KM. It is ignored in other cases. A reasonable
+ *    (initial) value for this argument is 100.
+ * \param scg_graph If not a \c NULL pointer, then the coarse-grained
+ *    graph is returned here.
+ * \param scg_matrix If not a \c NULL pointer, then it must be an
+ *    initialied matrix, and the coarse-grained matrix is returned
+ *    here.
+ * \param scg_sparsemat If not a \c NULL pointer, then the coarse
+ *    grained matrix is returned here, in sparse matrix form.
+ * \param L If not a \c NULL pointer, then it must be an initialized
+ *    matrix and the left semi-projector is returned here.
+ * \param R If not a \c NULL pointer, then it must be an initialized
+ *    matrix and the right semi-projector is returned here.
+ * \param Lsparse If not a \c NULL pointer, then the left
+ *    semi-projector is returned here.
+ * \param Rsparse If not a \c NULL pointer, then the right
+ *    semi-projector is returned here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \sa \ref igraph_scg_grouping(), \ref igraph_scg_semiprojectors(),
+ * \ref igraph_scg_stochastic() and \ref igraph_scg_adjacency().
+ *
+ * \example examples/simple/scg3.c
+ */
+
+int igraph_scg_laplacian(const igraph_t *graph,
+                         const igraph_matrix_t *matrix,
+                         const igraph_sparsemat_t *sparsemat,
+                         const igraph_vector_t *ev,
+                         igraph_integer_t nt,
+                         const igraph_vector_t *nt_vec,
+                         igraph_scg_algorithm_t algo,
+                         igraph_scg_norm_t norm,
+                         igraph_scg_direction_t direction,
+                         igraph_vector_complex_t *values,
+                         igraph_matrix_complex_t *vectors,
+                         igraph_vector_t *groups,
+                         igraph_bool_t use_arpack,
+                         igraph_integer_t maxiter,
+                         igraph_t *scg_graph,
+                         igraph_matrix_t *scg_matrix,
+                         igraph_sparsemat_t *scg_sparsemat,
+                         igraph_matrix_t *L,
+                         igraph_matrix_t *R,
+                         igraph_sparsemat_t *Lsparse,
+                         igraph_sparsemat_t *Rsparse) {
+
+    igraph_matrix_t *mymatrix = (igraph_matrix_t*) matrix, real_matrix;
+    igraph_sparsemat_t *mysparsemat = (igraph_sparsemat_t*) sparsemat,
+                        real_sparsemat;
+    int no_of_nodes;
+    igraph_real_t evmin, evmax;
+    igraph_arpack_options_t options;
+    igraph_eigen_which_t which;
+    /* eigenvectors are calculated and returned */
+    igraph_bool_t do_vectors = vectors && igraph_matrix_complex_size(vectors) == 0;
+    /* groups are calculated */
+    igraph_bool_t do_groups = !groups || igraph_vector_size(groups) == 0;
+    igraph_bool_t tmp_groups = !groups;
+    /* eigenvectors are not returned but must be calculated for groups */
+    igraph_bool_t tmp_vectors = !do_vectors && do_groups;
+    igraph_matrix_complex_t myvectors;
+    igraph_vector_t mygroups;
+    int no_of_ev = (int) igraph_vector_size(ev);
+    igraph_bool_t tmp_lsparse = !Lsparse, tmp_rsparse = !Rsparse;
+    igraph_sparsemat_t myLsparse, myRsparse, tmpsparse, Rsparse_t;
+
+    /* --------------------------------------------------------------------*/
+    /* Argument checks */
+
+    IGRAPH_CHECK(igraph_i_scg_common_checks(graph, matrix, sparsemat,
+                                            ev, nt, nt_vec,
+                                            0, vectors, groups, scg_graph,
+                                            scg_matrix, scg_sparsemat, /*p=*/ 0,
+                                            &evmin, &evmax));
+
+    if (graph) {
+        no_of_nodes = igraph_vcount(graph);
+    } else if (matrix) {
+        no_of_nodes = (int) igraph_matrix_nrow(matrix);
+    } else {
+        no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Convert graph, if needed, get Laplacian matrix */
+
+    if (graph) {
+        mysparsemat = &real_sparsemat;
+        IGRAPH_CHECK(igraph_sparsemat_init(mysparsemat, 0, 0, 0));
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat);
+        IGRAPH_CHECK(igraph_laplacian(graph, 0, mysparsemat, /*normalized=*/ 0,
+                                      /*weights=*/ 0));
+    } else if (matrix) {
+        mymatrix = &real_matrix;
+        IGRAPH_MATRIX_INIT_FINALLY(mymatrix, no_of_nodes, no_of_nodes);
+        IGRAPH_CHECK(igraph_i_matrix_laplacian(matrix, mymatrix, norm));
+    } else { /* sparsemat */
+        mysparsemat = &real_sparsemat;
+        IGRAPH_CHECK(igraph_i_sparsemat_laplacian(sparsemat, mysparsemat,
+                     norm == IGRAPH_SCG_NORM_COL));
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, mysparsemat);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Compute eigenpairs, if needed */
+
+    if (tmp_vectors) {
+        vectors = &myvectors;
+        IGRAPH_CHECK(igraph_matrix_complex_init(vectors, no_of_nodes, no_of_ev));
+        IGRAPH_FINALLY(igraph_matrix_complex_destroy, vectors);
+    }
+
+    if (do_vectors || tmp_vectors) {
+        igraph_matrix_complex_t tmp;
+        igraph_vector_t tmpev;
+        igraph_vector_complex_t tmpeval;
+        int i;
+
+        which.pos = IGRAPH_EIGEN_SELECT;
+        which.il = (int) (no_of_nodes - evmax + 1);
+        which.iu = (int) (no_of_nodes - evmin + 1);
+
+        if (values) {
+            IGRAPH_CHECK(igraph_vector_complex_init(&tmpeval, 0));
+            IGRAPH_FINALLY(igraph_vector_complex_destroy, &tmpeval);
+        }
+        IGRAPH_CHECK(igraph_matrix_complex_init(&tmp, no_of_nodes,
+                                                which.iu - which.il + 1));
+        IGRAPH_FINALLY(igraph_matrix_complex_destroy, &tmp);
+        IGRAPH_CHECK(igraph_eigen_matrix(mymatrix, mysparsemat, /*fun=*/ 0,
+                                         no_of_nodes, /*extra=*/ 0, use_arpack ?
+                                         IGRAPH_EIGEN_ARPACK :
+                                         IGRAPH_EIGEN_LAPACK, &which, &options,
+                                         /*storage=*/ 0,
+                                         values ? &tmpeval : 0, &tmp));
+
+        IGRAPH_VECTOR_INIT_FINALLY(&tmpev, no_of_ev);
+        for (i = 0; i < no_of_ev; i++) {
+            VECTOR(tmpev)[i] = evmax - VECTOR(*ev)[i];
+        }
+        if (values) {
+            IGRAPH_CHECK(igraph_vector_complex_index(&tmpeval, values, &tmpev));
+        }
+        IGRAPH_CHECK(igraph_matrix_complex_select_cols(&tmp, vectors, &tmpev));
+        igraph_vector_destroy(&tmpev);
+        igraph_matrix_complex_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(2);
+        if (values) {
+            igraph_vector_complex_destroy(&tmpeval);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Work out groups, if needed */
+    /* TODO: use complex part as well */
+    if (tmp_groups) {
+        groups = &mygroups;
+        IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)groups, no_of_nodes);
+    }
+    if (do_groups) {
+        igraph_matrix_t tmp;
+        IGRAPH_MATRIX_INIT_FINALLY(&tmp, 0, 0);
+        IGRAPH_CHECK(igraph_matrix_complex_real(vectors, &tmp));
+        IGRAPH_CHECK(igraph_scg_grouping(&tmp, (igraph_vector_t*)groups,
+                                         nt, nt_vec,
+                                         IGRAPH_SCG_LAPLACIAN, algo,
+                                         /*p=*/ 0, maxiter));
+        igraph_matrix_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Perform coarse graining */
+    if (tmp_lsparse) {
+        Lsparse = &myLsparse;
+    }
+    if (tmp_rsparse) {
+        Rsparse = &myRsparse;
+    }
+    IGRAPH_CHECK(igraph_scg_semiprojectors(groups, IGRAPH_SCG_LAPLACIAN,
+                                           L, R, Lsparse, Rsparse, /*p=*/ 0,
+                                           norm));
+    if (tmp_groups) {
+        igraph_vector_destroy((igraph_vector_t*) groups);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (tmp_vectors) {
+        igraph_matrix_complex_destroy(vectors);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (Rsparse) {
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, Rsparse);
+    }
+    if (Lsparse) {
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, Lsparse);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Compute coarse grained matrix/graph/sparse matrix */
+    IGRAPH_CHECK(igraph_sparsemat_compress(Rsparse, &tmpsparse));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmpsparse);
+    IGRAPH_CHECK(igraph_sparsemat_transpose(&tmpsparse, &Rsparse_t,
+                                            /*values=*/ 1));
+    igraph_sparsemat_destroy(&tmpsparse);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &Rsparse_t);
+
+    IGRAPH_CHECK(igraph_i_scg_get_result(IGRAPH_SCG_LAPLACIAN,
+                                         mymatrix, mysparsemat,
+                                         Lsparse, &Rsparse_t,
+                                         scg_graph, scg_matrix,
+                                         scg_sparsemat, /*directed=*/ 1));
+
+    /* -------------------------------------------------------------------- */
+    /* Clean up */
+
+    igraph_sparsemat_destroy(&Rsparse_t);
+    IGRAPH_FINALLY_CLEAN(1);
+    if (Lsparse) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (Rsparse) {
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (graph) {
+        igraph_sparsemat_destroy(mysparsemat);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else if (matrix) {
+        igraph_matrix_destroy(mymatrix);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        igraph_sparsemat_destroy(mysparsemat);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/scg_approximate_methods.c b/igraph/src/scg_approximate_methods.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/scg_approximate_methods.c
@@ -0,0 +1,173 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2011-12  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard st, Cambridge, MA, 02138 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/*
+ *  SCGlib : A C library for the spectral coarse graining of matrices
+ *  as described in the paper: Shrinking Matrices while preserving their
+ *  eigenpairs with Application to the Spectral Coarse Graining of Graphs.
+ *  Preprint available at <http://people.epfl.ch/david.morton>
+ *
+ *  Copyright (C) 2008 David Morton de Lachapelle <david.morton@a3.epfl.ch>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301 USA
+ *
+ *  DESCRIPTION
+ *  -----------
+ *    The intervals_method and intervals_plus_kmeans implements the
+ *    methods of sec. 5.3.2 and sec. 5.3.3 of the above reference.
+ *    They take an eigenvector 'v' as parameter and a vector 'breaks'
+ *    of length 'nb', which provide the intervals used to cut 'v'.
+ *    Then all components of 'v' that fall into the same interval are
+ *    assigned the same group label in 'gr'. The group labels are
+ *    positive consecutive integers starting from 0.
+ *    The intervals_method function is adapted from bincode of the R
+ *    base package.
+ *    The intervals_plus_kmeans is initialized with regularly-spaced
+ *    breaks, which rougly corresponds to the intervals_method. Then
+ *    kmeans minimizes iteratively the objective function until it gets
+ *    stuck in a (usually) local minimum, or until 'itermax' is reached.
+ *    So far, the breaks_computation function allows computation of
+ *    constant bins, as used in intervals_method, and of equidistant
+ *    centers as used in intervals_plus_kmeans.
+ */
+
+#include "igraph_error.h"
+#include "igraph_types.h"
+#include "scg_headers.h"
+#include "igraph_memory.h"
+#include "igraph_vector.h"
+
+int igraph_i_intervals_plus_kmeans(const igraph_vector_t *v, int *gr,
+                                   int n, int n_interv,
+                                   int maxiter) {
+    int i;
+    igraph_vector_t centers;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&centers, n_interv);
+
+    igraph_i_breaks_computation(v, &centers, n_interv, 2);
+    IGRAPH_CHECK(igraph_i_kmeans_Lloyd(v, n, 1, &centers, n_interv, gr,
+                                       maxiter));
+
+    /*renumber the groups*/
+    for (i = 0; i < n; i++) {
+        gr[i] = gr[i] - 1;
+    }
+
+    igraph_vector_destroy(&centers);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_intervals_method(const igraph_vector_t *v, int *gr, int n,
+                              int n_interv) {
+    int i, lo, hi, new;
+    const int lft = 1;
+    const int include_border = 1;
+    igraph_vector_t breaks;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&breaks, n_interv + 1);
+
+    IGRAPH_CHECK(igraph_i_breaks_computation(v, &breaks, n_interv + 1, 1));
+
+    for (i = 0; i < n; i++) {
+        lo = 0;
+        hi = n_interv;
+        if (VECTOR(*v)[i] <  VECTOR(breaks)[lo] ||
+            VECTOR(breaks)[hi] < VECTOR(*v)[i] ||
+            (VECTOR(*v)[i] == VECTOR(breaks)[lft ? hi : lo] && !include_border)) {
+            /* Do nothing */
+        } else {
+            while (hi - lo >= 2) {
+                new = (hi + lo) / 2;
+                if (VECTOR(*v)[i] > VECTOR(breaks)[new] ||
+                    (lft && VECTOR(*v)[i] == VECTOR(breaks)[new])) {
+                    lo = new;
+                } else {
+                    hi = new;
+                }
+            }
+            gr[i] = lo;
+        }
+    }
+    igraph_vector_destroy(&breaks);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_breaks_computation(const igraph_vector_t *v,
+                                igraph_vector_t *breaks,
+                                int nb, int method) {
+    int i;
+    igraph_real_t eps, vmin, vmax;
+    igraph_vector_minmax(v, &vmin, &vmax);
+
+    if (vmax == vmin) {
+        IGRAPH_ERROR("There is only one (repeated) value in argument 'v' "
+                     "of bin_size_computation()", IGRAPH_EINVAL);
+    }
+
+    if (nb < 2) {
+        IGRAPH_ERROR("'nb' in bin_size_computation() must be >= 2",
+                     IGRAPH_EINVAL);
+    }
+
+    switch (method) {
+    case 1: /* constant bins for fixed-size intervals method */
+        eps = (vmax - vmin) / (igraph_real_t)(nb - 1);
+        VECTOR(*breaks)[0] = vmin;
+        for (i = 1; i < nb - 1; i++) {
+            VECTOR(*breaks)[i] = VECTOR(*breaks)[i - 1] + eps;
+        }
+        VECTOR(*breaks)[nb - 1] = vmax;
+        break;
+    case 2: /* equidistant centers for kmeans */
+        eps = (vmax - vmin) / (igraph_real_t)nb;
+        VECTOR(*breaks)[0] = vmin + eps / 2.;
+        for (i = 1; i < nb; i++) {
+            VECTOR(*breaks)[i] = VECTOR(*breaks)[i - 1] + eps;
+        }
+        break;
+    /* TODO: implement logarithmic binning for power-law-like distributions */
+    default:
+        IGRAPH_ERROR("Internal SCG error, this should ot happen",
+                     IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/scg_exact_scg.c b/igraph/src/scg_exact_scg.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/scg_exact_scg.c
@@ -0,0 +1,68 @@
+/*
+ *  SCGlib : A C library for the spectral coarse graining of matrices
+ *  as described in the paper: Shrinking Matrices while preserving their
+ *  eigenpairs with Application to the Spectral Coarse Graining of Graphs.
+ *  Preprint available at <http://people.epfl.ch/david.morton>
+ *
+ *  Copyright (C) 2008 David Morton de Lachapelle <david.morton@a3.epfl.ch>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301 USA
+ *
+ *  DESCRIPTION
+ *  -----------
+ *    The exact_coarse_graining function labels all the objects whose
+ *    components in 'v' are equal. The result is stored in 'gr'. Labels
+ *    are positive consecutive integers starting from 0.
+ *    See also Section 5.4.1 (last paragraph) of the above reference.
+ */
+
+#include "igraph_memory.h"
+#include "scg_headers.h"
+#include <math.h>
+
+int igraph_i_exact_coarse_graining(const igraph_real_t *v,
+                                   int *gr, const int n) {
+    int i, gr_nb;
+    igraph_i_scg_indval_t *w = igraph_Calloc(n, igraph_i_scg_indval_t);
+
+    if (!w) {
+        IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, w);
+
+    for (i = 0; i < n; i++) {
+        w[i].val = v[i];
+        w[i].ind = i;
+    }
+
+    qsort(w, (size_t) n, sizeof(igraph_i_scg_indval_t), igraph_i_compare_ind_val);
+
+    gr_nb = 0;
+    gr[w[0].ind] = gr_nb;
+    for (i = 1; i < n; i++) {
+        if ( fabs(w[i].val - w[i - 1].val) > 1e-14 ) {
+            gr_nb++;
+        }
+        gr[w[i].ind] = gr_nb;
+    }
+
+    igraph_Free(w);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+
diff --git a/igraph/src/scg_kmeans.c b/igraph/src/scg_kmeans.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/scg_kmeans.c
@@ -0,0 +1,105 @@
+/*
+ *  SCGlib : A C library for the spectral coarse graining of matrices
+ *  as described in the paper: Shrinking Matrices while preserving their
+ *  eigenpairs with Application to the Spectral Coarse Graining of Graphs.
+ *  Preprint available at <http://people.epfl.ch/david.morton>
+ *
+ *  Copyright (C) 2008 David Morton de Lachapelle <david.morton@a3.epfl.ch>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301 USA
+ *
+ *  DESCRIPTION
+ *  -----------
+ *    The kmeans_Lloyd function is adapted from the R-stats package.
+ *    It perfoms Lloyd's k-means clustering on a p x n data matrix
+ *    stored row-wise in a vector 'x'. 'cen' contains k initial centers.
+ *    The group label to which each object belongs is stored in 'cl'.
+ *    Labels are positive consecutive integers starting from 0.
+ *    See also Section 5.3.3 of the above reference.
+ */
+
+#include "igraph_memory.h"
+
+#include "scg_headers.h"
+
+int igraph_i_kmeans_Lloyd(const igraph_vector_t *x, int n, int p,
+                          igraph_vector_t *cen, int k, int *cl, int maxiter) {
+
+    int iter, i, j, c, it, inew = 0;
+    igraph_real_t best, dd, tmp;
+    int updated;
+    igraph_vector_int_t nc;
+
+    IGRAPH_CHECK(igraph_vector_int_init(&nc, k));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &nc);
+
+    for (i = 0; i < n; i++) {
+        cl[i] = -1;
+    }
+    for (iter = 0; iter < maxiter; iter++) {
+        updated = 0;
+        for (i = 0; i < n; i++) {
+            /* find nearest centre for each point */
+            best = IGRAPH_INFINITY;
+            for (j = 0; j < k; j++) {
+                dd = 0.0;
+                for (c = 0; c < p; c++) {
+                    tmp = VECTOR(*x)[i + n * c] - VECTOR(*cen)[j + k * c];
+                    dd += tmp * tmp;
+                }
+                if (dd < best) {
+                    best = dd;
+                    inew = j + 1;
+                }
+            }
+            if (cl[i] != inew) {
+                updated = 1;
+                cl[i] = inew;
+            }
+        }
+        if (!updated) {
+            break;
+        }
+
+        /* update each centre */
+        for (j = 0; j < k * p; j++) {
+            VECTOR(*cen)[j] = 0.0;
+        }
+        for (j = 0; j < k; j++) {
+            VECTOR(nc)[j] = 0;
+        }
+        for (i = 0; i < n; i++) {
+            it = cl[i] - 1;
+            VECTOR(nc)[it]++;
+            for (c = 0; c < p; c++) {
+                VECTOR(*cen)[it + c * k] += VECTOR(*x)[i + c * n];
+            }
+        }
+        for (j = 0; j < k * p; j++) {
+            VECTOR(*cen)[j] /= VECTOR(nc)[j % k];
+        }
+    }
+    igraph_vector_int_destroy(&nc);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* convervenge check */
+    if (iter >= maxiter - 1) {
+        IGRAPH_ERROR("Lloyd k-means did not converge", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
diff --git a/igraph/src/scg_optimal_method.c b/igraph/src/scg_optimal_method.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/scg_optimal_method.c
@@ -0,0 +1,241 @@
+/*
+ *  SCGlib : A C library for the spectral coarse graining of matrices
+ *  as described in the paper: Shrinking Matrices while preserving their
+ *  eigenpairs with Application to the Spectral Coarse Graining of Graphs.
+ *  Preprint available at <http://people.epfl.ch/david.morton>
+ *
+ *  Copyright (C) 2008 David Morton de Lachapelle <david.morton@a3.epfl.ch>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301 USA
+ *
+ *  DESCRIPTION
+ *  -----------
+ *    This file implements algorithm 5.8 of the above reference.
+ *    The optimal_partition function returns the minimizing partition
+ *    with size 'nt' of the objective function ||v-Pv||, where P is
+ *    a problem-specific projector. So far, Symmetric (matrix=1),
+ *    Laplacian (matrix=2) and Stochastic (matrix=3) projectors
+ *    have been implemented (the cost_matrix function below).
+ *    In the stochastic case, 'p' is expected to be a valid propability
+ *    vector. In all other cases, 'p' is ignored and can be set to NULL.
+ *    The group labels are given in 'gr' as positive consecutive integers
+ *    starting from 0.
+ */
+
+#include "igraph_error.h"
+#include "igraph_memory.h"
+#include "igraph_matrix.h"
+#include "igraph_vector.h"
+
+#include "scg_headers.h"
+
+int igraph_i_optimal_partition(const igraph_real_t *v, int *gr, int n,
+                               int nt, int matrix, const igraph_real_t *p,
+                               igraph_real_t *value) {
+
+    int i, non_ties, q, j, l, part_ind, col;
+    igraph_i_scg_indval_t *vs = igraph_Calloc(n, igraph_i_scg_indval_t);
+    igraph_real_t *Cv, temp, sumOfSquares;
+    igraph_vector_t ps;
+    igraph_matrix_t F;
+    igraph_matrix_int_t Q;
+
+    /*-----------------------------------------------
+      -----Sorts v and counts non-ties-----------------
+      -----------------------------------------------*/
+
+    if (!vs) {
+        IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, vs);
+
+    for (i = 0; i < n; i++) {
+        vs[i].val = v[i];
+        vs[i].ind = i;
+    }
+
+    qsort(vs, (size_t) n, sizeof(igraph_i_scg_indval_t),
+          igraph_i_compare_ind_val);
+
+    non_ties = 1;
+    for (i = 1; i < n; i++) {
+        if (vs[i].val < vs[i - 1].val - 1e-14 ||
+            vs[i].val > vs[i - 1].val + 1e-14) {
+            non_ties++;
+        }
+    }
+
+    if (nt >= non_ties) {
+        IGRAPH_ERROR("`Invalid number of intervals, should be smaller than "
+                     "number of unique values in V", IGRAPH_EINVAL);
+    }
+
+    /*------------------------------------------------
+      ------Computes Cv, the matrix of costs------------
+      ------------------------------------------------*/
+    Cv = igraph_i_real_sym_matrix(n);
+    if (!Cv) {
+        IGRAPH_ERROR("SCG error", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, Cv);
+
+    /* if stochastic SCG orders p */
+    if (matrix == 3) {
+        IGRAPH_VECTOR_INIT_FINALLY(&ps, n);
+        for (i = 0; i < n; i++) {
+            VECTOR(ps)[i] = p[vs[i].ind];
+        }
+    }
+
+    IGRAPH_CHECK(igraph_i_cost_matrix(Cv, vs, n, matrix, &ps));
+    if (matrix == 3) {
+        igraph_vector_destroy(&ps);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    /*-------------------------------------------------
+      -------Fills up matrices F and Q-------------------
+      -------------------------------------------------*/
+    /*here j also is a counter but the use of unsigned variables
+      is to be proscribed in "for (unsigned int j=...;j>=0;j--)",
+      for such loops never ends!*/
+
+    IGRAPH_MATRIX_INIT_FINALLY(&F, nt, n);
+    IGRAPH_CHECK(igraph_matrix_int_init(&Q, nt, n));
+    IGRAPH_FINALLY(igraph_matrix_destroy, &Q);
+
+    for (i = 0; i < n; i++) {
+        MATRIX(Q, 0, i)++;
+    }
+    for (i = 0; i < nt; i++) {
+        MATRIX(Q, i, i) = i + 1;
+    }
+
+    for (i = 0; i < n; i++) {
+        MATRIX(F, 0, i) = igraph_i_real_sym_mat_get(Cv, 0, i);
+    }
+
+    for (i = 1; i < nt; i++)
+        for (j = i + 1; j < n; j++) {
+            MATRIX(F, i, j) = MATRIX(F, i - 1, i - 1) + igraph_i_real_sym_mat_get(Cv, i, j);
+            MATRIX(Q, i, j) = 2;
+
+            for (q = i - 1; q <= j - 1; q++) {
+                temp = MATRIX(F, i - 1, q) + igraph_i_real_sym_mat_get(Cv, q + 1, j);
+                if (temp < MATRIX(F, i, j)) {
+                    MATRIX(F, i, j) = temp;
+                    MATRIX(Q, i, j) = q + 2;
+                }
+            }
+        }
+    igraph_i_free_real_sym_matrix(Cv);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /*--------------------------------------------------
+      -------Back-tracks through Q to work out the groups-
+      --------------------------------------------------*/
+    part_ind = nt;
+    col = n - 1;
+
+    for (j = nt - 1; j >= 0; j--) {
+        for (i = MATRIX(Q, j, col) - 1; i <= col; i++) {
+            gr[vs[i].ind] = part_ind - 1;
+        }
+        if (MATRIX(Q, j, col) != 2) {
+            col = MATRIX(Q, j, col) - 2;
+            part_ind -= 1;
+        } else {
+            if (j > 1) {
+                for (l = 0; l <= (j - 1); l++) {
+                    gr[vs[l].ind] = l;
+                }
+                break;
+            } else {
+                col = MATRIX(Q, j, col) - 2;
+                part_ind -= 1;
+            }
+        }
+    }
+
+    sumOfSquares = MATRIX(F, nt - 1, n - 1);
+
+    igraph_matrix_destroy(&F);
+    igraph_matrix_int_destroy(&Q);
+    igraph_Free(vs);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (value) {
+        *value = sumOfSquares;
+    }
+    return 0;
+}
+
+int igraph_i_cost_matrix(igraph_real_t*Cv, const igraph_i_scg_indval_t *vs,
+                         int n,  int matrix, const igraph_vector_t *ps) {
+
+    /* if symmetric of Laplacian SCG -> same Cv */
+    if (matrix == 1 || matrix == 2) {
+        int i, j;
+        igraph_vector_t w, w2;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&w, n + 1);
+        IGRAPH_VECTOR_INIT_FINALLY(&w2, n + 1);
+
+        VECTOR(w)[1] = vs[0].val;
+        VECTOR(w2)[1] = vs[0].val * vs[0].val;
+
+        for (i = 2; i <= n; i++) {
+            VECTOR(w)[i] = VECTOR(w)[i - 1] + vs[i - 1].val;
+            VECTOR(w2)[i] = VECTOR(w2)[i - 1] + vs[i - 1].val * vs[i - 1].val;
+        }
+
+        for (i = 0; i < n; i++) {
+            for (j = i + 1; j < n; j++) {
+                igraph_real_t v = (VECTOR(w2)[j + 1] - VECTOR(w2)[i]) -
+                                  (VECTOR(w)[j + 1] - VECTOR(w)[i]) * (VECTOR(w)[j + 1] - VECTOR(w)[i]) /
+                                  (j - i + 1);
+                igraph_i_real_sym_mat_set(Cv, i, j, v);
+            }
+        }
+
+        igraph_vector_destroy(&w);
+        igraph_vector_destroy(&w2);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+    /* if stochastic */
+    /* TODO: optimize it to O(n^2) instead of O(n^3) (as above) */
+    if (matrix == 3) {
+        int i, j, k;
+        igraph_real_t t1, t2;
+        for (i = 0; i < n; i++) {
+            for (j = i + 1; j < n; j++) {
+                t1 = t2 = 0;
+                for (k = i; k < j; k++) {
+                    t1 += VECTOR(*ps)[k];
+                    t2 += VECTOR(*ps)[k] * vs[k].val;
+                }
+                t1 = t2 / t1;
+                t2 = 0;
+                for (k = i; k < j; k++) {
+                    t2 += (vs[k].val - t1) * (vs[k].val - t1);
+                }
+                igraph_i_real_sym_mat_set(Cv, i, j, t2);
+            }
+        }
+    }
+
+    return 0;
+}
+
diff --git a/igraph/src/scg_utils.c b/igraph/src/scg_utils.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/scg_utils.c
@@ -0,0 +1,94 @@
+/*
+ *  SCGlib : A C library for the spectral coarse graining of matrices
+ *  as described in the paper: Shrinking Matrices while preserving their
+ *  eigenpairs with Application to the Spectral Coarse Graining of Graphs.
+ *  Preprint available at <http://people.epfl.ch/david.morton>
+ *
+ *  Copyright (C) 2008 David Morton de Lachapelle <david.morton@a3.epfl.ch>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301 USA
+ *
+ *  DESCRIPTION
+ *  -----------
+ *    This files contains the data structures and error handing
+ *    functions used throughout the SCGlib.
+ */
+
+#include "igraph_error.h"
+#include "igraph_memory.h"
+
+#include "scg_headers.h"
+
+/*to be used with qsort and struct ind_val arrays */
+int igraph_i_compare_ind_val(const void *a, const void *b) {
+    igraph_i_scg_indval_t *arg1 = (igraph_i_scg_indval_t *) a;
+    igraph_i_scg_indval_t *arg2 = (igraph_i_scg_indval_t *) b;
+
+    if ( arg1->val < arg2->val ) {
+        return -1;
+    } else if ( arg1->val == arg2->val ) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+/*to be used with qsort and struct groups*/
+int igraph_i_compare_groups(const void *a, const void *b) {
+    igraph_i_scg_groups_t *arg1 = (igraph_i_scg_groups_t *) a;
+    igraph_i_scg_groups_t *arg2 = (igraph_i_scg_groups_t *) b;
+    int i;
+    for (i = 0; i < arg1->n; i++) {
+        if (arg1->gr[i] > arg2->gr[i]) {
+            return 1;
+        } else if (arg1->gr[i] < arg2->gr[i]) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/*to be used with qsort and real_vectors */
+int igraph_i_compare_real(const void *a, const void *b) {
+    igraph_real_t arg1 = * (igraph_real_t *) a;
+    igraph_real_t arg2 = * (igraph_real_t *) b;
+
+    if (arg1 < arg2) {
+        return -1;
+    } else if (arg1 == arg2) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+/*to be used with qsort and integer vectors */
+int igraph_i_compare_int(const void *a, const void *b) {
+    int arg1 = * (int *) a;
+    int arg2 = * (int *) b;
+    return (arg1 - arg2);
+}
+
+/* allocate a igraph_real_t symmetrix matrix with dimension size x size
+   in vector format*/
+igraph_real_t *igraph_i_real_sym_matrix(const int size)  {
+    igraph_real_t *S = igraph_Calloc(size * (size + 1) / 2, igraph_real_t);
+    if (!S) {
+        igraph_error("allocation failure in real_sym_matrix()",
+                     __FILE__, __LINE__, IGRAPH_ENOMEM);
+    }
+    return S;
+}
diff --git a/igraph/src/second.c b/igraph/src/second.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/second.c
@@ -0,0 +1,42 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Subroutine */ int igraphsecond_(real *t)
+{
+    real t1;
+    extern doublereal etime_(real *);
+    real tarray[2];
+
+
+
+/*  -- LAPACK auxiliary routine (preliminary version) --   
+       Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,   
+       Courant Institute, Argonne National Lab, and Rice University   
+       July 26, 1991   
+
+    Purpose   
+    =======   
+
+    SECOND returns the user time for a process in seconds.   
+    This version gets the time from the system function ETIME. */
+
+
+    t1 = etime_(tarray);
+    *t = tarray[0];
+    return 0;
+
+/*     End of SECOND */
+
+} /* igraphsecond_ */
+
diff --git a/igraph/src/separators.c b/igraph/src/separators.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/separators.c
@@ -0,0 +1,836 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_separators.h"
+#include "igraph_memory.h"
+#include "igraph_adjlist.h"
+#include "igraph_dqueue.h"
+#include "igraph_vector.h"
+#include "igraph_interface.h"
+#include "igraph_flow.h"
+#include "igraph_flow_internal.h"
+#include "igraph_components.h"
+#include "igraph_structural.h"
+#include "igraph_constructors.h"
+#include "igraph_stack.h"
+#include "igraph_interrupt_internal.h"
+
+int igraph_i_is_separator(const igraph_t *graph,
+                          igraph_vit_t *vit,
+                          long int except,
+                          igraph_bool_t *res,
+                          igraph_vector_bool_t *removed,
+                          igraph_dqueue_t *Q,
+                          igraph_vector_t *neis,
+                          long int no_of_nodes) {
+
+    long int start = 0;
+
+    if (IGRAPH_VIT_SIZE(*vit) >= no_of_nodes - 1) {
+        /* Just need to check that we really have at least n-1 vertices in it */
+        igraph_vector_bool_t hit;
+        long int nohit = 0;
+        IGRAPH_CHECK(igraph_vector_bool_init(&hit, no_of_nodes));
+        IGRAPH_FINALLY(igraph_vector_bool_destroy, &hit);
+        for (IGRAPH_VIT_RESET(*vit);
+             !IGRAPH_VIT_END(*vit);
+             IGRAPH_VIT_NEXT(*vit)) {
+            long int v = IGRAPH_VIT_GET(*vit);
+            if (!VECTOR(hit)[v]) {
+                nohit++;
+                VECTOR(hit)[v] = 1;
+            }
+        }
+        igraph_vector_bool_destroy(&hit);
+        IGRAPH_FINALLY_CLEAN(1);
+        if (nohit >= no_of_nodes - 1) {
+            *res = 0;
+            return 0;
+        }
+    }
+
+    /* Remove the given vertices from the graph, do a breadth-first
+       search and check the number of components */
+
+    if (except < 0) {
+        for (IGRAPH_VIT_RESET(*vit);
+             !IGRAPH_VIT_END(*vit);
+             IGRAPH_VIT_NEXT(*vit)) {
+            VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1;
+        }
+    } else {
+        /* There is an exception */
+        long int i;
+        for (i = 0, IGRAPH_VIT_RESET(*vit);
+             i < except;
+             i++, IGRAPH_VIT_NEXT(*vit)) {
+            VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1;
+        }
+        for (IGRAPH_VIT_NEXT(*vit);
+             !IGRAPH_VIT_END(*vit);
+             IGRAPH_VIT_NEXT(*vit)) {
+            VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1;
+        }
+    }
+
+    /* Look for the first node that is not removed */
+    while (start < no_of_nodes && VECTOR(*removed)[start]) {
+        start++;
+    }
+
+    if (start == no_of_nodes) {
+        IGRAPH_ERROR("All vertices are included in the separator",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_dqueue_push(Q, start));
+    VECTOR(*removed)[start] = 1;
+    while (!igraph_dqueue_empty(Q)) {
+        long int node = (long int) igraph_dqueue_pop(Q);
+        long int j, n;
+        IGRAPH_CHECK(igraph_neighbors(graph, neis, (igraph_integer_t) node, IGRAPH_ALL));
+        n = igraph_vector_size(neis);
+        for (j = 0; j < n; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            if (!VECTOR(*removed)[nei]) {
+                IGRAPH_CHECK(igraph_dqueue_push(Q, nei));
+                VECTOR(*removed)[nei] = 1;
+            }
+        }
+    }
+
+    /* Look for the next node that was neighter removed, not visited */
+    while (start < no_of_nodes && VECTOR(*removed)[start]) {
+        start++;
+    }
+
+    /* If there is another component, then we have a separator */
+    *res = (start < no_of_nodes);
+
+    return 0;
+}
+
+/**
+ * \function igraph_is_separator
+ * Decides whether the removal of a set of vertices disconnects the graph
+ *
+ * \param graph The input graph. It may be directed, but edge
+ *        directions are ignored.
+ * \param condidate The candidate separator. It must not contain all
+ *        vertices.
+ * \param res Pointer to a boolean variable, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number vertices and edges.
+ *
+ * \example examples/simple/igraph_is_separator.c
+ */
+
+int igraph_is_separator(const igraph_t *graph,
+                        const igraph_vs_t candidate,
+                        igraph_bool_t *res) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_bool_t removed;
+    igraph_dqueue_t Q;
+    igraph_vector_t neis;
+    igraph_vit_t vit;
+
+    IGRAPH_CHECK(igraph_vit_create(graph, candidate, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    IGRAPH_CHECK(igraph_vector_bool_init(&removed, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed);
+    IGRAPH_CHECK(igraph_dqueue_init(&Q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &Q);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    IGRAPH_CHECK(igraph_i_is_separator(graph, &vit, -1, res, &removed,
+                                       &Q, &neis, no_of_nodes));
+
+    igraph_vector_destroy(&neis);
+    igraph_dqueue_destroy(&Q);
+    igraph_vector_bool_destroy(&removed);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/**
+ * \function igraph_is_minimal_separator
+ * Decides whether a set of vertices is a minimal separator
+ *
+ * A set of vertices is a minimal separator, if the removal of the
+ * vertices disconnects the graph, and this is not true for any subset
+ * of the set.
+ *
+ * </para><para>This implementation first checks that the given
+ * candidate is a separator, by calling \ref
+ * igraph_is_separator(). If it is a separator, then it checks that
+ * each subset of size n-1, where n is the size of the candidate, is
+ * not a separator.
+ * \param graph The input graph. It may be directed, but edge
+ *        directions are ignored.
+ * \param candidate Pointer to a vector of long integers, the
+ *        candidate minimal separator.
+ * \param res Pointer to a boolean variable, the result is stored
+ *        here.
+ * \return Error code.
+ *
+ * Time complexity: O(n(|V|+|E|)), |V| is the number of vertices, |E|
+ * is the number of edges, n is the number vertices in the candidate
+ * separator.
+ *
+ * \example examples/simple/igraph_is_minimal_separator.c
+ */
+
+int igraph_is_minimal_separator(const igraph_t *graph,
+                                const igraph_vs_t candidate,
+                                igraph_bool_t *res) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_bool_t removed;
+    igraph_dqueue_t Q;
+    igraph_vector_t neis;
+    long int candsize;
+    igraph_vit_t vit;
+
+    IGRAPH_CHECK(igraph_vit_create(graph, candidate, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    candsize = IGRAPH_VIT_SIZE(vit);
+
+    IGRAPH_CHECK(igraph_vector_bool_init(&removed, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed);
+    IGRAPH_CHECK(igraph_dqueue_init(&Q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &Q);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    /* Is it a separator at all? */
+    IGRAPH_CHECK(igraph_i_is_separator(graph, &vit, -1, res, &removed,
+                                       &Q, &neis, no_of_nodes));
+    if (!(*res)) {
+        /* Not a separator at all, nothing to do, *res is already set */
+    } else if (candsize == 0) {
+        /* Nothing to do, minimal, *res is already set */
+    } else {
+        /* General case, we need to remove each vertex from 'candidate'
+         * and check whether the remainder is a separator. If this is
+         * false for all vertices, then 'candidate' is a minimal
+         * separator.
+         */
+        long int i;
+        for (i = 0, *res = 0; i < candsize && (!*res); i++) {
+            igraph_vector_bool_null(&removed);
+            IGRAPH_CHECK(igraph_i_is_separator(graph, &vit, i, res, &removed,
+                                               &Q, &neis, no_of_nodes));
+        }
+        (*res) = (*res) ? 0 : 1;    /* opposite */
+    }
+
+    igraph_vector_destroy(&neis);
+    igraph_dqueue_destroy(&Q);
+    igraph_vector_bool_destroy(&removed);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/* --------------------------------------------------------------------*/
+
+#define UPDATEMARK() do {                              \
+        (*mark)++;                         \
+        if (!(*mark)) {                    \
+            igraph_vector_null(leaveout);                \
+            (*mark)=1;                       \
+        }                                                  \
+    } while (0)
+
+int igraph_i_clusters_leaveout(const igraph_adjlist_t *adjlist,
+                               igraph_vector_t *components,
+                               igraph_vector_t *leaveout,
+                               unsigned long int *mark,
+                               igraph_dqueue_t *Q) {
+
+    /* Another trick: we use the same 'leaveout' vector to mark the
+     * vertices that were already found in the BFS
+     */
+
+    long int i, no_of_nodes = igraph_adjlist_size(adjlist);
+
+    igraph_dqueue_clear(Q);
+    igraph_vector_clear(components);
+
+    for (i = 0; i < no_of_nodes; i++) {
+
+        if (VECTOR(*leaveout)[i] == *mark) {
+            continue;
+        }
+
+        VECTOR(*leaveout)[i] = *mark;
+        igraph_dqueue_push(Q, i);
+        igraph_vector_push_back(components, i);
+
+        while (!igraph_dqueue_empty(Q)) {
+            long int act_node = (long int) igraph_dqueue_pop(Q);
+            igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, act_node);
+            long int j, n = igraph_vector_int_size(neis);
+            for (j = 0; j < n; j++) {
+                long int nei = (long int) VECTOR(*neis)[j];
+                if (VECTOR(*leaveout)[nei] == *mark) {
+                    continue;
+                }
+                IGRAPH_CHECK(igraph_dqueue_push(Q, nei));
+                VECTOR(*leaveout)[nei] = *mark;
+                igraph_vector_push_back(components, nei);
+            }
+        }
+
+        igraph_vector_push_back(components, -1);
+    }
+
+    UPDATEMARK();
+
+    return 0;
+}
+
+igraph_bool_t igraph_i_separators_newsep(const igraph_vector_ptr_t *comps,
+        const igraph_vector_t *newc) {
+
+    long int co, nocomps = igraph_vector_ptr_size(comps);
+
+    for (co = 0; co < nocomps; co++) {
+        igraph_vector_t *act = VECTOR(*comps)[co];
+        if (igraph_vector_all_e(act, newc)) {
+            return 0;
+        }
+    }
+
+    /* If not found, then it is new */
+    return 1;
+}
+
+int igraph_i_separators_store(igraph_vector_ptr_t *separators,
+                              const igraph_adjlist_t *adjlist,
+                              igraph_vector_t *components,
+                              igraph_vector_t *leaveout,
+                              unsigned long int *mark,
+                              igraph_vector_t *sorter) {
+
+    /* We need to stote N(C), the neighborhood of C, but only if it is
+     * not already stored among the separators.
+     */
+
+    long int cptr = 0, next, complen = igraph_vector_size(components);
+
+    while (cptr < complen) {
+        long int saved = cptr;
+        igraph_vector_clear(sorter);
+
+        /* Calculate N(C) for the next C */
+
+        while ( (next = (long int) VECTOR(*components)[cptr++]) != -1) {
+            VECTOR(*leaveout)[next] = *mark;
+        }
+        cptr = saved;
+
+        while ( (next = (long int) VECTOR(*components)[cptr++]) != -1) {
+            igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, next);
+            long int j, nn = igraph_vector_int_size(neis);
+            for (j = 0; j < nn; j++) {
+                long int nei = (long int) VECTOR(*neis)[j];
+                if (VECTOR(*leaveout)[nei] != *mark) {
+                    igraph_vector_push_back(sorter, nei);
+                    VECTOR(*leaveout)[nei] = *mark;
+                }
+            }
+        }
+        igraph_vector_sort(sorter);
+
+        UPDATEMARK();
+
+        /* Add it to the list of separators, if it is new */
+
+        if (igraph_i_separators_newsep(separators, sorter)) {
+            igraph_vector_t *newc = igraph_Calloc(1, igraph_vector_t);
+            if (!newc) {
+                IGRAPH_ERROR("Cannot calculate minimal separators", IGRAPH_ENOMEM);
+            }
+            IGRAPH_FINALLY(igraph_free, newc);
+            igraph_vector_copy(newc, sorter);
+            IGRAPH_FINALLY(igraph_vector_destroy, newc);
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(separators, newc));
+            IGRAPH_FINALLY_CLEAN(2);
+        }
+    } /* while cptr < complen */
+
+    return 0;
+}
+
+void igraph_i_separators_free(igraph_vector_ptr_t *separators) {
+    long int i, n = igraph_vector_ptr_size(separators);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *vec = VECTOR(*separators)[i];
+        if (vec) {
+            igraph_vector_destroy(vec);
+            igraph_Free(vec);
+        }
+    }
+}
+
+/**
+ * \function igraph_all_minimal_st_separators
+ * List all vertex sets that are minimal (s,t) separators for some s and t
+ *
+ * This function lists all vertex sets that are minimal (s,t)
+ * separators for some (s,t) vertex pair.
+ *
+ * </para><para>See more about the implemented algorithm in
+ * Anne Berry, Jean-Paul Bordat and Olivier Cogis: Generating All the
+ * Minimal Separators of a Graph, In: Peter Widmayer, Gabriele Neyer
+ * and Stephan Eidenbenz (editors): Graph-theoretic concepts in
+ * computer science, 1665, 167--172, 1999. Springer.
+ *
+ * \param graph The input graph. It may be directed, but edge
+ *        directions are ignored.
+ * \param separators An initialized pointer vector, the separators
+ *        are stored here. It is a list of pointers to igraph_vector_t
+ *        objects. Each vector will contain the ids of the vertices in
+ *        the separator.
+ *        To free all memory allocated for \c separators, you need call
+ *        \ref igraph_vector_destroy() and then \ref igraph_free() on
+ *        each element, before destroying the pointer vector itself.
+ * \return Error code.
+ *
+ * Time complexity: O(n|V|^3), |V| is the number of vertices, n is the
+ * number of separators.
+ *
+ * \example examples/simple/igraph_minimal_separators.c
+ */
+
+int igraph_all_minimal_st_separators(const igraph_t *graph,
+                                     igraph_vector_ptr_t *separators) {
+
+    /*
+     * Some notes about the tricks used here. For finding the components
+     * of the graph after removing some vertices, we do the
+     * following. First we mark the vertices with the actual mark stamp
+     * (mark), then run breadth-first search on the graph, but not
+     * considering the marked vertices. Then we increase the mark. If
+     * there is integer overflow here, then we zero out the mark and set
+     * it to one. (We might as well just always zero it out.)
+     *
+     * For each separator the vertices are stored in vertex id order.
+     * This facilitates the comparison of the separators when we find a
+     * potential new candidate.
+     *
+     * To keep track of which separator we already used as a basis, we
+     * keep a boolean vector (already_tried). The try_next pointer show
+     * the next separator to try as a basis.
+     */
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t leaveout;
+    igraph_vector_bool_t already_tried;
+    long int try_next = 0;
+    unsigned long int mark = 1;
+    long int v;
+
+    igraph_adjlist_t adjlist;
+    igraph_vector_t components;
+    igraph_dqueue_t Q;
+    igraph_vector_t sorter;
+
+    igraph_vector_ptr_clear(separators);
+    IGRAPH_FINALLY(igraph_i_separators_free, separators);
+
+    IGRAPH_CHECK(igraph_vector_init(&leaveout, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_destroy, &leaveout);
+    IGRAPH_CHECK(igraph_vector_bool_init(&already_tried, 0));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &already_tried);
+    IGRAPH_CHECK(igraph_vector_init(&components, 0));
+    IGRAPH_FINALLY(igraph_vector_destroy, &components);
+    IGRAPH_CHECK(igraph_vector_reserve(&components, no_of_nodes * 2));
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+    IGRAPH_CHECK(igraph_dqueue_init(&Q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &Q);
+    IGRAPH_CHECK(igraph_vector_init(&sorter, 0));
+    IGRAPH_FINALLY(igraph_vector_destroy, &sorter);
+    IGRAPH_CHECK(igraph_vector_reserve(&sorter, no_of_nodes));
+
+    /* ---------------------------------------------------------------
+     * INITIALIZATION, we check whether the neighborhoods of the
+     * vertices separate the graph. The ones that do will form the
+     * initial basis.
+     */
+
+    for (v = 0; v < no_of_nodes; v++) {
+
+        /* Mark v and its neighbors */
+        igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, v);
+        long int i, n = igraph_vector_int_size(neis);
+        VECTOR(leaveout)[v] = mark;
+        for (i = 0; i < n; i++) {
+            long int nei = (long int) VECTOR(*neis)[i];
+            VECTOR(leaveout)[nei] = mark;
+        }
+
+        /* Find the components */
+        IGRAPH_CHECK(igraph_i_clusters_leaveout(&adjlist, &components, &leaveout,
+                                                &mark, &Q));
+
+        /* Store the corresponding separators, N(C) for each component C */
+        IGRAPH_CHECK(igraph_i_separators_store(separators, &adjlist, &components,
+                                               &leaveout, &mark, &sorter));
+
+    }
+
+    /* ---------------------------------------------------------------
+     * GENERATION, we need to use all already found separators as
+     * basis and see if they generate more separators
+     */
+
+    while (try_next < igraph_vector_ptr_size(separators)) {
+        igraph_vector_t *basis = VECTOR(*separators)[try_next];
+        long int b, basislen = igraph_vector_size(basis);
+        for (b = 0; b < basislen; b++) {
+
+            /* Remove N(x) U basis */
+            long int x = (long int) VECTOR(*basis)[b];
+            igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, x);
+            long int i, n = igraph_vector_int_size(neis);
+            for (i = 0; i < basislen; i++) {
+                long int sn = (long int) VECTOR(*basis)[i];
+                VECTOR(leaveout)[sn] = mark;
+            }
+            for (i = 0; i < n; i++) {
+                long int nei = (long int) VECTOR(*neis)[i];
+                VECTOR(leaveout)[nei] = mark;
+            }
+
+            /* Find the components */
+            IGRAPH_CHECK(igraph_i_clusters_leaveout(&adjlist, &components,
+                                                    &leaveout, &mark, &Q));
+
+            /* Store the corresponding separators, N(C) for each component C */
+            IGRAPH_CHECK(igraph_i_separators_store(separators, &adjlist,
+                                                   &components, &leaveout, &mark,
+                                                   &sorter));
+        }
+
+        try_next++;
+    }
+
+    /* --------------------------------------------------------------- */
+
+    igraph_vector_destroy(&sorter);
+    igraph_dqueue_destroy(&Q);
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_destroy(&components);
+    igraph_vector_bool_destroy(&already_tried);
+    igraph_vector_destroy(&leaveout);
+    IGRAPH_FINALLY_CLEAN(7);  /* +1 for separators */
+
+    return 0;
+}
+
+#undef UPDATEMARK
+
+int igraph_i_minimum_size_separators_append(igraph_vector_ptr_t *old,
+        igraph_vector_ptr_t *new) {
+
+    long int olen = igraph_vector_ptr_size(old);
+    long int nlen = igraph_vector_ptr_size(new);
+    long int i;
+
+    for (i = 0; i < nlen; i++) {
+        igraph_vector_t *newvec = VECTOR(*new)[i];
+        long int j;
+        for (j = 0; j < olen; j++) {
+            igraph_vector_t *oldvec = VECTOR(*old)[j];
+            if (igraph_vector_all_e(oldvec, newvec)) {
+                break;
+            }
+        }
+        if (j == olen) {
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(old, newvec));
+            olen++;
+        } else {
+            igraph_vector_destroy(newvec);
+            igraph_free(newvec);
+        }
+        VECTOR(*new)[i] = 0;
+    }
+    igraph_vector_ptr_clear(new);
+
+    return 0;
+}
+
+int igraph_i_minimum_size_separators_topkdeg(const igraph_t *graph,
+        igraph_vector_t *res,
+        long int k) {
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t deg, order;
+    long int i;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&deg, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
+    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(), IGRAPH_ALL,
+                               /*loops=*/ 0));
+
+    IGRAPH_CHECK(igraph_vector_order1(&deg, &order, no_of_nodes));
+    IGRAPH_CHECK(igraph_vector_resize(res, k));
+    for (i = 0; i < k; i++) {
+        VECTOR(*res)[i] = VECTOR(order)[no_of_nodes - 1 - i];
+    }
+
+    igraph_vector_destroy(&order);
+    igraph_vector_destroy(&deg);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+void igraph_i_separators_stcuts_free(igraph_vector_ptr_t *p) {
+    long int i, n = igraph_vector_ptr_size(p);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *v = VECTOR(*p)[i];
+        if (v) {
+            igraph_vector_destroy(v);
+            igraph_free(v);
+            VECTOR(*p)[i] = 0;
+        }
+    }
+    igraph_vector_ptr_destroy(p);
+}
+
+/**
+ * \function igraph_minimum_size_separators
+ * Find all minimum size separating vertex sets
+ *
+ * This function lists all separator vertex sets of minimum size.
+ * A vertex set is a separator if its removal disconnects the graph.
+ *
+ * </para><para>The implementation is based on the following paper:
+ * Arkady Kanevsky: Finding all minimum-size separating vertex sets in
+ * a graph, Networks 23, 533--541, 1993.
+ *
+ * \param graph The input graph, which must be undirected.
+ * \param separators An initialized pointer vector, the separators
+ *        are stored here. It is a list of pointers to igraph_vector_t
+ *        objects. Each vector will contain the ids of the vertices in
+ *        the separator.
+ *        To free all memory allocated for \c separators, you need call
+ *        \ref igraph_vector_destroy() and then \ref igraph_free() on
+ *        each element, before destroying the pointer vector itself.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/igraph_minimum_size_separators.c
+ */
+
+int igraph_minimum_size_separators(const igraph_t *graph,
+                                   igraph_vector_ptr_t *separators) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_integer_t conn; long int k;
+    igraph_vector_t X;
+    long int i, j;
+    igraph_bool_t issepX;
+    igraph_t Gbar;
+    igraph_vector_t phi;
+    igraph_t graph_copy;
+    igraph_vector_t capacity;
+    igraph_maxflow_stats_t stats;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Minimum size separators currently only works on undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    igraph_vector_ptr_clear(separators);
+    IGRAPH_FINALLY(igraph_i_separators_free, separators);
+
+    /* ---------------------------------------------------------------- */
+    /* 1 Find the vertex connectivity of 'graph' */
+    IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn,
+                                            /* checks= */ 1)); k = conn;
+
+    /* Special cases for low connectivity, two exits here! */
+    if (conn == 0) {
+        /* Nothing to do */
+        IGRAPH_FINALLY_CLEAN(1);    /* separators */
+        return 0;
+    } else if (conn == 1) {
+        igraph_vector_t ap;
+        long int i, n;
+        IGRAPH_VECTOR_INIT_FINALLY(&ap, 0);
+        IGRAPH_CHECK(igraph_articulation_points(graph, &ap));
+        n = igraph_vector_size(&ap);
+        IGRAPH_CHECK(igraph_vector_ptr_resize(separators, n));
+        igraph_vector_ptr_null(separators);
+        for (i = 0; i < n; i++) {
+            igraph_vector_t *v = igraph_Calloc(1, igraph_vector_t);
+            if (!v) {
+                IGRAPH_ERROR("Minimum size separators failed", IGRAPH_ENOMEM);
+            }
+            IGRAPH_VECTOR_INIT_FINALLY(v, 1);
+            VECTOR(*v)[0] = VECTOR(ap)[i];
+            VECTOR(*separators)[i] = v;
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        igraph_vector_destroy(&ap);
+        IGRAPH_FINALLY_CLEAN(2);    /* +1 for separators */
+        return 0;
+    } else if (conn == no_of_nodes - 1) {
+        long int k;
+        IGRAPH_CHECK(igraph_vector_ptr_resize(separators, no_of_nodes));
+        igraph_vector_ptr_null(separators);
+        for (i = 0; i < no_of_nodes; i++) {
+            igraph_vector_t *v = igraph_Calloc(1, igraph_vector_t);
+            if (!v) {
+                IGRAPH_ERROR("Cannot list minimum size separators", IGRAPH_ENOMEM);
+            }
+            IGRAPH_VECTOR_INIT_FINALLY(v, no_of_nodes - 1);
+            for (j = 0, k = 0; j < no_of_nodes; j++) {
+                if (j != i) {
+                    VECTOR(*v)[k++] = j;
+                }
+            }
+            VECTOR(*separators)[i] = v;
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        IGRAPH_FINALLY_CLEAN(1);    /* separators */
+        return 0;
+    }
+
+    /* Work on a copy of 'graph' */
+    IGRAPH_CHECK(igraph_copy(&graph_copy, graph));
+    IGRAPH_FINALLY(igraph_destroy, &graph_copy);
+
+    /* ---------------------------------------------------------------- */
+    /* 2 Find k vertices with the largest degrees (x1;..,xk). Check
+       if these k vertices form a separating k-set of G */
+    IGRAPH_CHECK(igraph_vector_init(&X, conn));
+    IGRAPH_FINALLY(igraph_vector_destroy, &X);
+    IGRAPH_CHECK(igraph_i_minimum_size_separators_topkdeg(graph, &X, k));
+    IGRAPH_CHECK(igraph_is_separator(&graph_copy, igraph_vss_vector(&X),
+                                     &issepX));
+    if (issepX) {
+        igraph_vector_t *v = igraph_Calloc(1, igraph_vector_t);
+        if (!v) {
+            IGRAPH_ERROR("Cannot find minimal size separators", IGRAPH_ENOMEM);
+        }
+        IGRAPH_VECTOR_INIT_FINALLY(v, k);
+        for (i = 0; i < k; i++) {
+            VECTOR(*v)[i] = VECTOR(X)[i];
+        }
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(separators, v));
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* Create Gbar, the Even-Tarjan reduction of graph */
+    IGRAPH_VECTOR_INIT_FINALLY(&capacity, 0);
+    IGRAPH_CHECK(igraph_even_tarjan_reduction(&graph_copy, &Gbar, &capacity));
+    IGRAPH_FINALLY(igraph_destroy, &Gbar);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&phi, no_of_edges);
+
+    /* ---------------------------------------------------------------- */
+    /* 3 If v[j] != x[i] and v[j] is not adjacent to x[i] then */
+    for (i = 0; i < k; i++) {
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        for (j = 0; j < no_of_nodes; j++) {
+            long int ii = (long int) VECTOR(X)[i];
+            igraph_real_t phivalue;
+            igraph_bool_t conn;
+
+            if (ii == j) {
+                continue;    /* the same vertex */
+            }
+            igraph_are_connected(&graph_copy, (igraph_integer_t) ii,
+                                 (igraph_integer_t) j, &conn);
+            if (conn) {
+                continue;    /* they are connected */
+            }
+
+            /* --------------------------------------------------------------- */
+            /* 4 Compute a maximum flow phi in Gbar from x[i] to v[j].
+            If |phi|=k, then */
+            IGRAPH_CHECK(igraph_maxflow(&Gbar, &phivalue, &phi, /*cut=*/ 0,
+                                        /*partition=*/ 0, /*partition2=*/ 0,
+                                        /* source= */
+                                        (igraph_integer_t) (ii + no_of_nodes),
+                                        /* target= */ (igraph_integer_t) j,
+                                        &capacity, &stats));
+
+            if (phivalue == k) {
+
+                /* ------------------------------------------------------------- */
+                /* 5-6-7. Find all k-sets separating x[i] and v[j]. */
+                igraph_vector_ptr_t stcuts;
+                IGRAPH_CHECK(igraph_vector_ptr_init(&stcuts, 0));
+                IGRAPH_FINALLY(igraph_i_separators_stcuts_free, &stcuts);
+                IGRAPH_CHECK(igraph_all_st_mincuts(&Gbar, /*value=*/ 0,
+                                                   /*cuts=*/ &stcuts,
+                                                   /*partition1s=*/ 0,
+                                                   /*source=*/ (igraph_integer_t)
+                                                   (ii + no_of_nodes),
+                                                   /*target=*/ (igraph_integer_t) j,
+                                                   /*capacity=*/ &capacity));
+
+                IGRAPH_CHECK(igraph_i_minimum_size_separators_append(separators,
+                             &stcuts));
+                igraph_vector_ptr_destroy(&stcuts);
+                IGRAPH_FINALLY_CLEAN(1);
+
+            } /* if phivalue == k */
+
+            /* --------------------------------------------------------------- */
+            /* 8 Add edge (x[i],v[j]) to G. */
+            IGRAPH_CHECK(igraph_add_edge(&graph_copy, (igraph_integer_t) ii,
+                                         (igraph_integer_t) j));
+            IGRAPH_CHECK(igraph_add_edge(&Gbar, (igraph_integer_t) (ii + no_of_nodes),
+                                         (igraph_integer_t) j));
+            IGRAPH_CHECK(igraph_add_edge(&Gbar, (igraph_integer_t) (j + no_of_nodes),
+                                         (igraph_integer_t) ii));
+            IGRAPH_CHECK(igraph_vector_push_back(&capacity, no_of_nodes));
+            IGRAPH_CHECK(igraph_vector_push_back(&capacity, no_of_nodes));
+
+        } /* for j<no_of_nodes */
+    } /* for i<k */
+
+    igraph_vector_destroy(&phi);
+    igraph_destroy(&Gbar);
+    igraph_vector_destroy(&capacity);
+    igraph_vector_destroy(&X);
+    igraph_destroy(&graph_copy);
+    IGRAPH_FINALLY_CLEAN(6);  /* +1 for separators */
+
+    return 0;
+}
diff --git a/igraph/src/sfe.c b/igraph/src/sfe.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/sfe.c
@@ -0,0 +1,47 @@
+/* sequential formatted external common routines*/
+#include "f2c.h"
+#include "fio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern char *f__fmtbuf;
+#else
+extern const char *f__fmtbuf;
+#endif
+
+integer e_rsfe(Void)
+{	int n;
+	n=en_fio();
+	f__fmtbuf=NULL;
+	return(n);
+}
+
+ int
+#ifdef KR_headers
+c_sfe(a) cilist *a; /* check */
+#else
+c_sfe(cilist *a) /* check */
+#endif
+{	unit *p;
+	f__curunit = p = &f__units[a->ciunit];
+	if(a->ciunit >= MXUNIT || a->ciunit<0)
+		err(a->cierr,101,"startio");
+	if(p->ufd==NULL && fk_open(SEQ,FMT,a->ciunit)) err(a->cierr,114,"sfe")
+	if(!p->ufmt) err(a->cierr,102,"sfe")
+	return(0);
+}
+integer e_wsfe(Void)
+{
+	int n = en_fio();
+	f__fmtbuf = NULL;
+#ifdef ALWAYS_FLUSH
+	if (!n && fflush(f__cf))
+		err(f__elist->cierr, errno, "write end");
+#endif
+	return n;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/sig_die.c b/igraph/src/sig_die.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/sig_die.c
@@ -0,0 +1,51 @@
+#include "stdio.h"
+#include "signal.h"
+
+#ifndef SIGIOT
+#ifdef SIGABRT
+#define SIGIOT SIGABRT
+#endif
+#endif
+
+#ifdef KR_headers
+void sig_die(s, kill) char *s; int kill;
+#else
+#include "stdlib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern void f_exit(void);
+
+void sig_die(const char *s, int kill)
+#endif
+{
+	/* print error message, then clear buffers */
+	fprintf(stderr, "%s\n", s);
+
+	if(kill)
+		{
+		fflush(stderr);
+		f_exit();
+		fflush(stderr);
+		/* now get a core */
+#ifdef SIGIOT
+		signal(SIGIOT, SIG_DFL);
+#endif
+		abort();
+		}
+	else {
+#ifdef NO_ONEXIT
+		f_exit();
+#endif
+		exit(1);
+		}
+	}
+#ifdef __cplusplus
+}
+#endif
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/signal_.c b/igraph/src/signal_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/signal_.c
@@ -0,0 +1,21 @@
+#include "f2c.h"
+#include "signal1.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ ftnint
+#ifdef KR_headers
+signal_(sigp, proc) integer *sigp; sig_pf proc;
+#else
+signal_(integer *sigp, sig_pf proc)
+#endif
+{
+	int sig;
+	sig = (int)*sigp;
+
+	return (ftnint)signal(sig, proc);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/signbit.c b/igraph/src/signbit.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/signbit.c
@@ -0,0 +1,24 @@
+#include "arith.h"
+
+#ifndef Long
+#define Long long
+#endif
+
+ int
+#ifdef KR_headers
+signbit_f2c(x) double *x;
+#else
+signbit_f2c(double *x)
+#endif
+{
+#ifdef IEEE_MC68k
+	if (*(Long*)x & 0x80000000)
+		return 1;
+#else
+#ifdef IEEE_8087
+	if (((Long*)x)[1] & 0x80000000)
+		return 1;
+#endif /*IEEE_8087*/
+#endif /*IEEE_MC68k*/
+	return 0;
+	}
diff --git a/igraph/src/sir.c b/igraph/src/sir.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/sir.c
@@ -0,0 +1,263 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2014  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_epidemics.h"
+#include "igraph_random.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+#include "igraph_psumtree.h"
+#include "igraph_memory.h"
+#include "igraph_structural.h"
+
+int igraph_sir_init(igraph_sir_t *sir) {
+    igraph_vector_init(&sir->times, 1);
+    IGRAPH_FINALLY(igraph_vector_destroy, &sir->times);
+    igraph_vector_int_init(&sir->no_s, 1);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_s);
+    igraph_vector_int_init(&sir->no_i, 1);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_i);
+    igraph_vector_int_init(&sir->no_r, 1);
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+/**
+ * \function igraph_sir_destroy
+ * Deallocate memory associated with a SIR simulation run
+ *
+ * \param sir The \ref igraph_sir_t object storing the simulation.
+ */
+
+void igraph_sir_destroy(igraph_sir_t *sir) {
+    igraph_vector_destroy(&sir->times);
+    igraph_vector_int_destroy(&sir->no_s);
+    igraph_vector_int_destroy(&sir->no_i);
+    igraph_vector_int_destroy(&sir->no_r);
+}
+
+void igraph_i_sir_destroy(igraph_vector_ptr_t *v) {
+    int i, n = igraph_vector_ptr_size(v);
+    for (i = 0; i < n; i++) {
+        igraph_sir_t *s = VECTOR(*v)[i];
+        if (s) {
+            igraph_sir_destroy(s);
+        }
+    }
+}
+
+#define S_S 0
+#define S_I 1
+#define S_R 2
+
+/**
+ * \function igraph_sir
+ * Perform a number of SIR epidemics model runs on a graph
+ *
+ * The SIR model is a simple model from epidemiology. The individuals
+ * of the population might be in three states: susceptible, infected
+ * and recovered. Recovered people are assumed to be immune to the
+ * disease. Susceptibles become infected with a rate that depends on
+ * their number of infected neigbors. Infected people become recovered
+ * with a constant rate. See these parameters below.
+ *
+ * </para><para>
+ * This function runs multiple simulations, all starting with a
+ * single uniformly randomly chosen infected individual. A simulation
+ * is stopped when no infected individuals are left.
+ *
+ * \param graph The graph to perform the model on. For directed graphs
+ *        edge directions are ignored and a warning is given.
+ * \param beta The rate of infection of an individual that is
+ *        susceptible and has a single infected neighbor.
+ *        The infection rate of a susceptible individual with n
+ *        infected neighbors is n times beta. Formally
+ *        this is the rate parameter of an exponential distribution.
+ * \param gamma The rate of recovery of an infected individual.
+ *        Formally, this is the rate parameter of an exponential
+ *        distribution.
+ * \param no_sim The number of simulation runs to perform.
+ * \param result The result of the simulation is stored here,
+ *        in a list of \ref igraph_sir_t objects. To deallocate
+ *        memory, the user needs to call \ref igraph_sir_destroy on
+ *        each element, before destroying the pointer vector itself.
+ * \return Error code.
+ *
+ * Time complexity: O(no_sim * (|V| + |E| log(|V|))).
+ */
+
+int igraph_sir(const igraph_t *graph, igraph_real_t beta,
+               igraph_real_t gamma, igraph_integer_t no_sim,
+               igraph_vector_ptr_t *result) {
+
+    int infected;
+    igraph_vector_int_t status;
+    igraph_adjlist_t adjlist;
+    int no_of_nodes = igraph_vcount(graph);
+    int i, j, ns, ni, nr;
+    igraph_vector_int_t *neis;
+    igraph_psumtree_t tree;
+    igraph_real_t psum;
+    int neilen;
+    igraph_bool_t simple;
+
+    if (no_of_nodes == 0) {
+        IGRAPH_ERROR("Cannot run SIR model on empty graph", IGRAPH_EINVAL);
+    }
+    if (igraph_is_directed(graph)) {
+        IGRAPH_WARNING("Edge directions are ignored in SIR model");
+    }
+    if (beta < 0) {
+        IGRAPH_ERROR("Beta must be non-negative in SIR model", IGRAPH_EINVAL);
+    }
+    if (gamma < 0) {
+        IGRAPH_ERROR("Gamma must be non-negative in SIR model", IGRAPH_EINVAL);
+    }
+    if (no_sim <= 0) {
+        IGRAPH_ERROR("Number of SIR simulations must be positive", IGRAPH_EINVAL);
+    }
+
+    igraph_is_simple(graph, &simple);
+    if (!simple) {
+        IGRAPH_ERROR("SIR model only works with simple graphs", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_int_init(&status, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &status);
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+    IGRAPH_CHECK(igraph_psumtree_init(&tree, no_of_nodes));
+    IGRAPH_FINALLY(igraph_psumtree_destroy, &tree);
+
+    IGRAPH_CHECK(igraph_vector_ptr_resize(result, no_sim));
+    igraph_vector_ptr_null(result);
+    IGRAPH_FINALLY(igraph_i_sir_destroy, result);
+    for (i = 0; i < no_sim; i++) {
+        igraph_sir_t *sir = igraph_Calloc(1, igraph_sir_t);
+        if (!sir) {
+            IGRAPH_ERROR("Cannot run SIR model", IGRAPH_ENOMEM);
+        }
+        igraph_sir_init(sir);
+        VECTOR(*result)[i] = sir;
+    }
+
+    RNG_BEGIN();
+
+    for (j = 0; j < no_sim; j++) {
+
+        igraph_sir_t *sir = VECTOR(*result)[j];
+        igraph_vector_t *times_v = &sir->times;
+        igraph_vector_int_t *no_s_v = &sir->no_s;
+        igraph_vector_int_t *no_i_v = &sir->no_i;
+        igraph_vector_int_t *no_r_v = &sir->no_r;
+
+        infected = RNG_INTEGER(0, no_of_nodes - 1);
+
+        /* Initially infected */
+        igraph_vector_int_null(&status);
+        VECTOR(status)[infected] = S_I;
+        ns = no_of_nodes - 1;
+        ni = 1;
+        nr = 0;
+
+        VECTOR(*times_v)[0] = 0.0;
+        VECTOR(*no_s_v)[0]  = ns;
+        VECTOR(*no_i_v)[0]  = ni;
+        VECTOR(*no_r_v)[0]  = nr;
+
+        if (igraph_psumtree_sum(&tree) != 0) {
+            igraph_psumtree_reset(&tree);
+        }
+
+        /* Rates */
+        igraph_psumtree_update(&tree, infected, gamma);
+        neis = igraph_adjlist_get(&adjlist, infected);
+        neilen = igraph_vector_int_size(neis);
+        for (i = 0; i < neilen; i++) {
+            int nei = VECTOR(*neis)[i];
+            igraph_psumtree_update(&tree, nei, beta);
+        }
+
+        while (ni > 0) {
+            igraph_real_t tt;
+            igraph_real_t r;
+            long int vchange;
+
+            psum = igraph_psumtree_sum(&tree);
+            tt = igraph_rng_get_exp(igraph_rng_default(), psum);
+            r = RNG_UNIF(0, psum);
+
+            igraph_psumtree_search(&tree, &vchange, r);
+            neis = igraph_adjlist_get(&adjlist, vchange);
+            neilen = igraph_vector_int_size(neis);
+
+            if (VECTOR(status)[vchange] == S_I) {
+                VECTOR(status)[vchange] = S_R;
+                ni--; nr++;
+                igraph_psumtree_update(&tree, vchange, 0.0);
+                for (i = 0; i < neilen; i++) {
+                    int nei = VECTOR(*neis)[i];
+                    if (VECTOR(status)[nei] == S_S) {
+                        igraph_real_t rate = igraph_psumtree_get(&tree, nei);
+                        igraph_psumtree_update(&tree, nei, rate - beta);
+                    }
+                }
+
+            } else { /* S_S */
+                VECTOR(status)[vchange] = S_I;
+                ns--; ni++;
+                igraph_psumtree_update(&tree, vchange, gamma);
+                for (i = 0; i < neilen; i++) {
+                    int nei = VECTOR(*neis)[i];
+                    if (VECTOR(status)[nei] == S_S) {
+                        igraph_real_t rate = igraph_psumtree_get(&tree, nei);
+                        igraph_psumtree_update(&tree, nei, rate + beta);
+                    }
+                }
+            }
+
+            if (times_v) {
+                igraph_vector_push_back(times_v, tt + igraph_vector_tail(times_v));
+            }
+            if (no_s_v)  {
+                igraph_vector_int_push_back(no_s_v, ns);
+            }
+            if (no_i_v)  {
+                igraph_vector_int_push_back(no_i_v, ni);
+            }
+            if (no_r_v)  {
+                igraph_vector_int_push_back(no_r_v, nr);
+            }
+
+        } /* psum > 0 */
+
+    } /* j < no_sim */
+
+    RNG_END();
+
+    igraph_psumtree_destroy(&tree);
+    igraph_adjlist_destroy(&adjlist);
+    igraph_vector_int_destroy(&status);
+    IGRAPH_FINALLY_CLEAN(4);  /* + result */
+
+    return 0;
+}
diff --git a/igraph/src/spanning_trees.c b/igraph/src/spanning_trees.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/spanning_trees.c
@@ -0,0 +1,521 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2011  Gabor Csardi <csardi.gabor@gmail.com>
+   Rue de l'Industrie 5, Lausanne 1005, Switzerland
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_structural.h"
+#include "igraph_dqueue.h"
+#include "igraph_interface.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_memory.h"
+#include "igraph_adjlist.h"
+#include "igraph_random.h"
+#include "igraph_components.h"
+#include "igraph_progress.h"
+#include "igraph_types_internal.h"
+
+int igraph_i_minimum_spanning_tree_unweighted(const igraph_t *graph,
+        igraph_vector_t *result);
+int igraph_i_minimum_spanning_tree_prim(const igraph_t *graph,
+                                        igraph_vector_t *result, const igraph_vector_t *weights);
+
+/**
+ * \ingroup structural
+ * \function igraph_minimum_spanning_tree
+ * \brief Calculates one minimum spanning tree of a graph.
+ *
+ * </para><para>
+ * If the graph has more minimum spanning trees (this is always the
+ * case, except if it is a forest) this implementation returns only
+ * the same one.
+ *
+ * </para><para>
+ * Directed graphs are considered as undirected for this computation.
+ *
+ * </para><para>
+ * If the graph is not connected then its minimum spanning forest is
+ * returned. This is the set of the minimum spanning trees of each
+ * component.
+ *
+ * \param graph The graph object.
+ * \param res An initialized vector, the IDs of the edges that constitute
+ *        a spanning tree will be returned here. Use
+ *        \ref igraph_subgraph_edges() to extract the spanning tree as
+ *        a separate graph object.
+ * \param weights A vector containing the weights of the edges
+ *        in the same order as the simple edge iterator visits them
+ *        (i.e. in increasing order of edge IDs).
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *
+ * Time complexity: O(|V|+|E|) for the unweighted case, O(|E| log |V|)
+ * for the weighted case. |V| is the number of vertices, |E| the
+ * number of edges in the graph.
+ *
+ * \sa \ref igraph_minimum_spanning_tree_unweighted() and
+ *     \ref igraph_minimum_spanning_tree_prim() if you only need the
+ *     tree as a separate graph object.
+ *
+ * \example examples/simple/igraph_minimum_spanning_tree.c
+ */
+int igraph_minimum_spanning_tree(const igraph_t* graph,
+                                 igraph_vector_t* res, const igraph_vector_t* weights) {
+    if (weights == 0) {
+        IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, res));
+    } else {
+        IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, res, weights));
+    }
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_minimum_spanning_tree_unweighted
+ * \brief Calculates one minimum spanning tree of an unweighted graph.
+ *
+ * </para><para>
+ * If the graph has more minimum spanning trees (this is always the
+ * case, except if it is a forest) this implementation returns only
+ * the same one.
+ *
+ * </para><para>
+ * Directed graphs are considered as undirected for this computation.
+ *
+ * </para><para>
+ * If the graph is not connected then its minimum spanning forest is
+ * returned. This is the set of the minimum spanning trees of each
+ * component.
+ * \param graph The graph object.
+ * \param mst The minimum spanning tree, another graph object. Do
+ *        \em not initialize this object before passing it to
+ *        this function, but be sure to call \ref igraph_destroy() on it if
+ *        you don't need it any more.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| is the
+ * number of vertices, |E| the number
+ * of edges in the graph.
+ *
+ * \sa \ref igraph_minimum_spanning_tree_prim() for weighted graphs,
+ *     \ref igraph_minimum_spanning_tree() if you need the IDs of the
+ *     edges that constitute the spanning tree.
+ */
+
+int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph,
+        igraph_t *mst) {
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, igraph_vcount(graph) - 1);
+    IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, &edges));
+    IGRAPH_CHECK(igraph_subgraph_edges(graph, mst,
+                                       igraph_ess_vector(&edges), /* delete_vertices = */ 0));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_minimum_spanning_tree_prim
+ * \brief Calculates one minimum spanning tree of a weighted graph.
+ *
+ * </para><para>
+ * This function uses Prim's method for carrying out the computation,
+ * see Prim, R.C.: Shortest connection networks and some
+ * generalizations, Bell System Technical
+ * Journal, Vol. 36,
+ * 1957, 1389--1401.
+ *
+ * </para><para>
+ * If the graph has more than one minimum spanning tree, the current
+ * implementation returns always the same one.
+ *
+ * </para><para>
+ * Directed graphs are considered as undirected for this computation.
+ *
+ * </para><para>
+ * If the graph is not connected then its minimum spanning forest is
+ * returned. This is the set of the minimum spanning trees of each
+ * component.
+ *
+ * \param graph The graph object.
+ * \param mst The result of the computation, a graph object containing
+ *        the minimum spanning tree of the graph.
+ *        Do \em not initialize this object before passing it to
+ *        this function, but be sure to call \ref igraph_destroy() on it if
+ *        you don't need it any more.
+ * \param weights A vector containing the weights of the edges
+ *        in the same order as the simple edge iterator visits them
+ *        (i.e. in increasing order of edge IDs).
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory.
+ *         \c IGRAPH_EINVAL, length of weight vector does not
+ *           match number of edges.
+ *
+ * Time complexity: O(|E| log |V|),
+ * |V| is the number of vertices,
+ * |E| the number of edges in the
+ * graph.
+ *
+ * \sa \ref igraph_minimum_spanning_tree_unweighted() for unweighted graphs,
+ *     \ref igraph_minimum_spanning_tree() if you need the IDs of the
+ *     edges that constitute the spanning tree.
+ *
+ * \example examples/simple/igraph_minimum_spanning_tree.c
+ */
+
+int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst,
+                                      const igraph_vector_t *weights) {
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, igraph_vcount(graph) - 1);
+    IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, &edges, weights));
+    IGRAPH_CHECK(igraph_subgraph_edges(graph, mst,
+                                       igraph_ess_vector(&edges), /* delete_vertices = */ 0));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+
+int igraph_i_minimum_spanning_tree_unweighted(const igraph_t* graph,
+        igraph_vector_t* res) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    char *already_added;
+    char *added_edges;
+
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    igraph_vector_t tmp = IGRAPH_VECTOR_NULL;
+    long int i, j;
+
+    igraph_vector_clear(res);
+
+    added_edges = igraph_Calloc(no_of_edges, char);
+    if (added_edges == 0) {
+        IGRAPH_ERROR("unweighted spanning tree failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added_edges);
+    already_added = igraph_Calloc(no_of_nodes, char);
+    if (already_added == 0) {
+        IGRAPH_ERROR("unweighted spanning tree failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, already_added);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        if (already_added[i] > 0) {
+            continue;
+        }
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        already_added[i] = 1;
+        IGRAPH_CHECK(igraph_dqueue_push(&q, i));
+        while (! igraph_dqueue_empty(&q)) {
+            long int act_node = (long int) igraph_dqueue_pop(&q);
+            IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act_node,
+                                         IGRAPH_ALL));
+            for (j = 0; j < igraph_vector_size(&tmp); j++) {
+                long int edge = (long int) VECTOR(tmp)[j];
+                if (added_edges[edge] == 0) {
+                    igraph_integer_t from, to;
+                    igraph_edge(graph, (igraph_integer_t) edge, &from, &to);
+                    if (act_node == to) {
+                        to = from;
+                    }
+                    if (already_added[(long int) to] == 0) {
+                        already_added[(long int) to] = 1;
+                        added_edges[edge] = 1;
+                        IGRAPH_CHECK(igraph_vector_push_back(res, edge));
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, to));
+                    }
+                }
+            }
+        }
+    }
+
+    igraph_dqueue_destroy(&q);
+    igraph_Free(already_added);
+    igraph_vector_destroy(&tmp);
+    igraph_Free(added_edges);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_i_minimum_spanning_tree_prim(const igraph_t* graph,
+                                        igraph_vector_t* res, const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    char *already_added;
+    char *added_edges;
+
+    igraph_d_indheap_t heap = IGRAPH_D_INDHEAP_NULL;
+    igraph_integer_t mode = IGRAPH_ALL;
+
+    igraph_vector_t adj;
+
+    long int i, j;
+
+    igraph_vector_clear(res);
+
+    if (weights == 0) {
+        return igraph_i_minimum_spanning_tree_unweighted(graph, res);
+    }
+
+    if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weights length", IGRAPH_EINVAL);
+    }
+
+    added_edges = igraph_Calloc(no_of_edges, char);
+    if (added_edges == 0) {
+        IGRAPH_ERROR("prim spanning tree failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added_edges);
+    already_added = igraph_Calloc(no_of_nodes, char);
+    if (already_added == 0) {
+        IGRAPH_ERROR("prim spanning tree failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, already_added);
+    IGRAPH_CHECK(igraph_d_indheap_init(&heap, 0));
+    IGRAPH_FINALLY(igraph_d_indheap_destroy, &heap);
+    IGRAPH_VECTOR_INIT_FINALLY(&adj, 0);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        if (already_added[i] > 0) {
+            continue;
+        }
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        already_added[i] = 1;
+        /* add all edges of the first vertex */
+        igraph_incident(graph, &adj, (igraph_integer_t) i, (igraph_neimode_t) mode);
+        for (j = 0; j < igraph_vector_size(&adj); j++) {
+            long int edgeno = (long int) VECTOR(adj)[j];
+            igraph_integer_t edgefrom, edgeto;
+            long int neighbor;
+            igraph_edge(graph, (igraph_integer_t) edgeno, &edgefrom, &edgeto);
+            neighbor = edgefrom != i ? edgefrom : edgeto;
+            if (already_added[neighbor] == 0) {
+                IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], i,
+                                                   edgeno));
+            }
+        }
+
+        while (! igraph_d_indheap_empty(&heap)) {
+            /* Get minimal edge */
+            long int from, edge;
+            igraph_integer_t tmp, to;
+            igraph_d_indheap_max_index(&heap, &from, &edge);
+            igraph_edge(graph, (igraph_integer_t) edge, &tmp, &to);
+
+            /* Erase it */
+            igraph_d_indheap_delete_max(&heap);
+
+            /* Is this edge already included? */
+            if (added_edges[edge] == 0) {
+                if (from == to) {
+                    to = tmp;
+                }
+                /* Does it point to a visited node? */
+                if (already_added[(long int)to] == 0) {
+                    already_added[(long int)to] = 1;
+                    added_edges[edge] = 1;
+                    IGRAPH_CHECK(igraph_vector_push_back(res, edge));
+                    /* add all outgoing edges */
+                    igraph_incident(graph, &adj, to, (igraph_neimode_t) mode);
+                    for (j = 0; j < igraph_vector_size(&adj); j++) {
+                        long int edgeno = (long int) VECTOR(adj)[j];
+                        igraph_integer_t edgefrom, edgeto;
+                        long int neighbor;
+                        igraph_edge(graph, (igraph_integer_t) edgeno, &edgefrom, &edgeto);
+                        neighbor = edgefrom != to ? edgefrom : edgeto;
+                        if (already_added[neighbor] == 0) {
+                            IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], to,
+                                                               edgeno));
+                        }
+                    }
+                } /* for */
+            } /* if !already_added */
+        } /* while in the same component */
+    } /* for all nodes */
+
+    igraph_d_indheap_destroy(&heap);
+    igraph_Free(already_added);
+    igraph_vector_destroy(&adj);
+    igraph_Free(added_edges);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/* igraph_random_spanning_tree */
+
+/* Loop-erased random walk (LERW) implementation.
+ * res must be an initialized vector. The edge IDs of the spanning tree
+ * will be added to the end of it. res will not be cleared before doing this.
+ *
+ * The walk is started from vertex start. comp_size must be the size of the connected
+ * component containing start.
+ */
+static int igraph_i_lerw(const igraph_t *graph, igraph_vector_t *res, igraph_integer_t start,
+                         igraph_integer_t comp_size, igraph_vector_bool_t *visited, const igraph_inclist_t *il) {
+    igraph_integer_t visited_count;
+
+    IGRAPH_CHECK(igraph_vector_reserve(res, igraph_vector_size(res) + comp_size - 1));
+
+    RNG_BEGIN();
+
+    VECTOR(*visited)[start] = 1;
+    visited_count = 1;
+
+    while (visited_count < comp_size) {
+        long degree, edge;
+        igraph_vector_int_t *edges;
+
+        edges = igraph_inclist_get(il, start);
+
+        /* choose a random edge */
+        degree = igraph_vector_int_size(edges);
+        edge = VECTOR(*edges)[ RNG_INTEGER(0, degree - 1) ];
+
+        /* set 'start' to the next vertex */
+        start = IGRAPH_OTHER(graph, edge, start);
+
+        /* if the next vertex hasn't been visited yet, register the edge we just traversed */
+        if (! VECTOR(*visited)[start]) {
+            IGRAPH_CHECK(igraph_vector_push_back(res, edge));
+            VECTOR(*visited)[start] = 1;
+            visited_count++;
+        }
+
+        IGRAPH_ALLOW_INTERRUPTION();
+    }
+
+    RNG_END();
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_random_spanning_tree
+ * \brief Uniformly sample the spanning trees of a graph
+ *
+ * Performs a loop-erased random walk on the graph to uniformly sample
+ * its spanning trees. Edge directions are ignored.
+ * </para><para>
+ *
+ * Multi-graphs are supported, and edge multiplicities will affect the sampling
+ * frequency. For example, consider the 3-cycle graph <code>1=2-3-1</code>, with two edges
+ * between vertices 1 and 2. Due to these parallel edges, the trees <code>1-2-3</code>
+ * and <code>3-1-2</code> will be sampled with multiplicity 2, while the tree
+ * <code>2-3-1</code> will be sampled with multiplicity 1.
+ *
+ * \param graph The input graph. Edge directions are ignored.
+ * \param res An initialized vector, the IDs of the edges that constitute
+ *        a spanning tree will be returned here. Use
+ *        \ref igraph_subgraph_edges() to extract the spanning tree as
+ *        a separate graph object.
+ * \param vid This parameter is relevant if the graph is not connected.
+ *        If negative, a random spanning forest of all components will be
+ *        generated. Otherwise, it should be the ID of a vertex. A random
+ *        spanning tree of the component containing the vertex will be
+ *        generated.
+ *
+ * \return Error code.
+ *
+ * \sa \ref igraph_minimum_spanning_tree(), \ref igraph_random_walk()
+ *
+ */
+int igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_t *res, igraph_integer_t vid) {
+    igraph_inclist_t il;
+    igraph_vector_bool_t visited;
+    igraph_integer_t vcount = igraph_vcount(graph);
+
+    if (vid >= vcount) {
+        IGRAPH_ERROR("Invalid vertex id given for random spanning tree", IGRAPH_EINVVID);
+    }
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &il);
+
+    IGRAPH_CHECK(igraph_vector_bool_init(&visited, vcount));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited);
+
+    igraph_vector_clear(res);
+
+    if (vid < 0) { /* generate random spanning forest: consider each component separately */
+        igraph_vector_t membership, csize;
+        igraph_integer_t comp_count;
+        igraph_integer_t i;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&membership, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&csize, 0);
+
+        IGRAPH_CHECK(igraph_clusters(graph, &membership, &csize, &comp_count, IGRAPH_WEAK));
+
+        /* for each component ... */
+        for (i = 0; i < comp_count; ++i) {
+            /* ... find a vertex to start the LERW from */
+            igraph_integer_t j = 0;
+            while (VECTOR(membership)[j] != i) {
+                ++j;
+            }
+
+            IGRAPH_CHECK(igraph_i_lerw(graph, res, j, (igraph_integer_t) VECTOR(csize)[i], &visited, &il));
+        }
+
+        igraph_vector_destroy(&membership);
+        igraph_vector_destroy(&csize);
+        IGRAPH_FINALLY_CLEAN(2);
+    } else { /* consider the component containing vid */
+        igraph_vector_t comp_vertices;
+        igraph_integer_t comp_size;
+
+        /* we measure the size of the component */
+        IGRAPH_VECTOR_INIT_FINALLY(&comp_vertices, 0);
+        IGRAPH_CHECK(igraph_subcomponent(graph, &comp_vertices, vid, IGRAPH_ALL));
+        comp_size = (igraph_integer_t) igraph_vector_size(&comp_vertices);
+        igraph_vector_destroy(&comp_vertices);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        IGRAPH_CHECK(igraph_i_lerw(graph, res, vid, comp_size, &visited, &il));
+    }
+
+    igraph_vector_bool_destroy(&visited);
+    igraph_inclist_destroy(&il);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return IGRAPH_SUCCESS;
+}
+
diff --git a/igraph/src/sparsemat.c b/igraph/src/sparsemat.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/sparsemat.c
@@ -0,0 +1,3055 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+
+#include "cs/cs.h"
+
+#include "igraph_sparsemat.h"
+#include "igraph_error.h"
+#include "igraph_interface.h"
+#include "igraph_constructors.h"
+#include "igraph_memory.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_attributes.h"
+
+#include <string.h>
+
+/**
+ * \section about_sparsemat About sparse matrices
+ *
+ * <para>
+ * The <code>igraph_sparsemat_t</code> data type stores sparse matrices,
+ * i.e. matrices in which the majority of the elements are zero.
+ * </para>
+ *
+ * <para>The data type is essentially a wrapper to some of the
+ * functions in the CXSparse library, by Tim Davis, see
+ * http://faculty.cse.tamu.edu/davis/suitesparse.html
+ * </para>
+ *
+ * <para>
+ * Matrices can be stored in two formats: triplet and
+ * column-compressed. The triplet format is intended for sparse matrix
+ * initialization, as it is easy to add new (non-zero) elements to
+ * it. Most of the computations are done on sparse matrices in
+ * column-compressed format, after the user has converted the triplet
+ * matrix to column-compressed, via \ref igraph_sparsemat_compress().
+ * </para>
+ *
+ * <para>
+ * Both formats are dynamic, in the sense that new elements can be
+ * added to them, possibly resulting the allocation of more memory.
+ * </para>
+ *
+ * <para>
+ * Row and column indices follow the C convention and are zero-based.
+ * </para>
+ *
+ * <para>
+ * \example examples/simple/igraph_sparsemat.c
+ * \example examples/simple/igraph_sparsemat2.c
+ * \example examples/simple/igraph_sparsemat3.c
+ * \example examples/simple/igraph_sparsemat4.c
+ * \example examples/simple/igraph_sparsemat5.c
+ * \example examples/simple/igraph_sparsemat6.c
+ * \example examples/simple/igraph_sparsemat7.c
+ * \example examples/simple/igraph_sparsemat8.c
+ * </para>
+ */
+
+/**
+ * \function igraph_sparsemat_init
+ * Initialize a sparse matrix, in triplet format
+ *
+ * This is the most common way to create a sparse matrix, together
+ * with the \ref igraph_sparsemat_entry() function, which can be used to
+ * add the non-zero elements one by one. Once done, the user can call
+ * \ref igraph_sparsemat_compress() to convert the matrix to
+ * column-compressed, to allow computations with it.
+ *
+ * </para><para>The user must call \ref igraph_sparsemat_destroy() on
+ * the matrix to deallocate the memory, once the matrix is no more
+ * needed.
+ * \param A Pointer to a not yet initialized sparse matrix.
+ * \param rows The number of rows in the matrix.
+ * \param cols The number of columns.
+ * \param nzmax The maximum number of non-zero elements in the
+ *    matrix. It is not compulsory to get this right, but it is
+ *    useful for the allocation of the proper amount of memory.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax) {
+
+    if (rows < 0) {
+        IGRAPH_ERROR("Negative number of rows", IGRAPH_EINVAL);
+    }
+    if (cols < 0) {
+        IGRAPH_ERROR("Negative number of columns", IGRAPH_EINVAL);
+    }
+
+    A->cs = cs_spalloc( rows, cols, nzmax, /*values=*/ 1,
+                        /*triplet=*/ 1);
+    if (!A->cs) {
+        IGRAPH_ERROR("Cannot allocate memory for sparse matrix", IGRAPH_ENOMEM);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_copy
+ * Copy a sparse matrix
+ *
+ * Create a sparse matrix object, by copying another one. The source
+ * matrix can be either in triplet or column-compressed format.
+ *
+ * </para><para>
+ * Exactly the same amount of memory will be allocated to the
+ * copy matrix, as it is currently for the original one.
+ * \param to Pointer to an uninitialized sparse matrix, the copy will
+ *    be created here.
+ * \param from The sparse matrix to copy.
+ * \return Error code.
+ *
+ * Time complexity: O(n+nzmax), the number of columns plus the maximum
+ * number of non-zero elements.
+ */
+
+int igraph_sparsemat_copy(igraph_sparsemat_t *to,
+                          const igraph_sparsemat_t *from) {
+
+    int ne = from->cs->nz == -1 ? from->cs->n + 1 : from->cs->nzmax;
+
+    to->cs = cs_spalloc(from->cs->m, from->cs->n, from->cs->nzmax,
+                        /*values=*/ 1,
+                        /*triplet=*/ igraph_sparsemat_is_triplet(from));
+
+    to->cs->nzmax = from->cs->nzmax;
+    to->cs->m     = from->cs->m;
+    to->cs->n     = from->cs->n;
+    to->cs->nz    = from->cs->nz;
+
+    memcpy(to->cs->p, from->cs->p, sizeof(int) * (size_t) ne);
+    memcpy(to->cs->i, from->cs->i, sizeof(int) * (size_t) (from->cs->nzmax));
+    memcpy(to->cs->x, from->cs->x, sizeof(double) * (size_t) (from->cs->nzmax));
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_destroy
+ * Deallocate memory used by a sparse matrix
+ *
+ * One destroyed, the sparse matrix must be initialized again, before
+ * calling any other operation on it.
+ * \param A The sparse matrix to destroy.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_sparsemat_destroy(igraph_sparsemat_t *A) {
+    cs_spfree(A->cs);
+}
+
+/**
+ * \function igraph_sparsemat_realloc
+ * Allocate more (or less) memory for a sparse matrix
+ *
+ * Sparse matrices automatically allocate more memory, as needed. To
+ * control memory allocation, the user can call this function, to
+ * allocate memory for a given number of non-zero elements.
+ * \param A The sparse matrix, it can be in triplet or
+ *    column-compressed format.
+ * \param nzmax The new maximum number of non-zero elements.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax) {
+    return !cs_sprealloc(A->cs, nzmax);
+}
+
+/**
+ * \function igraph_sparsemat_nrow
+ * Number of rows
+ *
+ * \param A The input matrix, in triplet or column-compressed format.
+ * \return The number of rows in the \p A matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A) {
+    return A->cs->m;
+}
+
+/**
+ * \function igraph_sparsemat_ncol
+ * Number of columns.
+ *
+ * \param A The input matrix, in triplet or column-compressed format.
+ * \return The number of columns in the \p A matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_sparsemat_ncol(const igraph_sparsemat_t *A) {
+    return A->cs->n;
+}
+
+/**
+ * \function igraph_sparsemat_type
+ * Type of a sparse matrix (triplet or column-compressed)
+ *
+ * Gives whether a sparse matrix is stored in the triplet format or in
+ * column-compressed format.
+ * \param A The input matrix.
+ * \return Either \c IGRAPH_SPARSEMAT_CC or \c
+ * IGRAPH_SPARSEMAT_TRIPLET.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A) {
+    return A->cs->nz < 0 ? IGRAPH_SPARSEMAT_CC : IGRAPH_SPARSEMAT_TRIPLET;
+}
+
+/**
+ * \function igraph_sparsemat_is_triplet
+ * Is this sparse matrix in triplet format?
+ *
+ * Decides whether a sparse matrix is in triplet format.
+ * \param A The input matrix.
+ * \return One if the input matrix is in triplet format, zero
+ * otherwise.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A) {
+    return A->cs->nz >= 0;
+}
+
+/**
+ * \function igraph_sparsemat_is_cc
+ * Is this sparse matrix in column-compressed format?
+ *
+ * Decides whether a sparse matrix is in column-compressed format.
+ * \param A The input matrix.
+ * \return One if the input matrix is in column-compressed format, zero
+ * otherwise.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A) {
+    return A->cs->nz < 0;
+}
+
+/**
+ * \function igraph_sparsemat_permute
+ * Permute the rows and columns of a sparse matrix
+ *
+ * \param A The input matrix, it must be in column-compressed format.
+ * \param p Integer vector, giving the permutation of the rows.
+ * \param q Integer vector, the permutation of the columns.
+ * \param res Pointer to an uninitialized sparse matrix, the result is
+ *   stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(m+n+nz), the number of rows plus the number of
+ * columns plus the number of non-zero elements in the matrix.
+ */
+
+int igraph_sparsemat_permute(const igraph_sparsemat_t *A,
+                             const igraph_vector_int_t *p,
+                             const igraph_vector_int_t *q,
+                             igraph_sparsemat_t *res) {
+
+    long int nrow = A->cs->m, ncol = A->cs->n;
+    igraph_vector_int_t pinv;
+    long int i;
+
+    if (nrow != igraph_vector_int_size(p)) {
+        IGRAPH_ERROR("Invalid row permutation length", IGRAPH_FAILURE);
+    }
+    if (ncol != igraph_vector_int_size(q)) {
+        IGRAPH_ERROR("Invalid column permutation length", IGRAPH_FAILURE);
+    }
+
+    /* We invert the permutation by hand */
+    IGRAPH_CHECK(igraph_vector_int_init(&pinv, nrow));
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &pinv);
+    for (i = 0; i < nrow; i++) {
+        VECTOR(pinv)[ VECTOR(*p)[i] ] = (int) i;
+    }
+
+    /* And call the permutation routine */
+    if (! (res->cs = cs_permute(A->cs, VECTOR(pinv), VECTOR(*q), /*values=*/ 1))) {
+        IGRAPH_ERROR("Cannot index sparse matrix", IGRAPH_FAILURE);
+    }
+
+    igraph_vector_int_destroy(&pinv);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_sparsemat_index_rows(const igraph_sparsemat_t *A,
+                                  const igraph_vector_int_t *p,
+                                  igraph_sparsemat_t *res,
+                                  igraph_real_t *constres) {
+
+    igraph_sparsemat_t II, II2;
+    long int nrow = A->cs->m;
+    long int idx_rows = igraph_vector_int_size(p);
+    long int k;
+
+    /* Create index matrix */
+    IGRAPH_CHECK(igraph_sparsemat_init(&II2, (int) idx_rows, (int) nrow,
+                                       (int) idx_rows));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2);
+    for (k = 0; k < idx_rows; k++) {
+        igraph_sparsemat_entry(&II2, (int) k, VECTOR(*p)[k], 1.0);
+    }
+    IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II));
+    igraph_sparsemat_destroy(&II2);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &II);
+
+    /* Multiply */
+    IGRAPH_CHECK(igraph_sparsemat_multiply(&II, A, res));
+    igraph_sparsemat_destroy(&II);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    if (constres) {
+        if (res->cs->p[1] != 0) {
+            *constres = res->cs->x[0];
+        } else {
+            *constres = 0.0;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A,
+                                  const igraph_vector_int_t *q,
+                                  igraph_sparsemat_t *res,
+                                  igraph_real_t *constres) {
+
+    igraph_sparsemat_t JJ, JJ2;
+    long int ncol = A->cs->n;
+    long int idx_cols = igraph_vector_int_size(q);
+    long int k;
+
+    /* Create index matrix */
+    IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, (int) ncol, (int) idx_cols,
+                                       (int) idx_cols));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2);
+    for (k = 0; k < idx_cols; k++) {
+        igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], (int) k, 1.0);
+    }
+    IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ));
+    igraph_sparsemat_destroy(&JJ2);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ);
+
+    /* Multiply */
+    IGRAPH_CHECK(igraph_sparsemat_multiply(A, &JJ, res));
+    igraph_sparsemat_destroy(&JJ);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    if (constres) {
+        if (res->cs->p [1] != 0) {
+            *constres = res->cs->x [0];
+        } else {
+            *constres = 0.0;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_index
+ * Index a sparse matrix, extract a submatrix, or a single element
+ *
+ * This function serves two purposes. First, it can extract
+ * submatrices from a sparse matrix. Second, as a special case, it can
+ * extract a single element from a sparse matrix.
+ * \param A The input matrix, it must be in column-compressed format.
+ * \param p An integer vector, or a null pointer. The selected row
+ *    index or indices. A null pointer selects all rows.
+ * \param q An integer vector, or a null pointer. The selected column
+ *    index or indices. A null pointer selects all columns.
+ * \param res Pointer to an uninitialized sparse matrix, or a null
+ *    pointer. If not a null pointer, then the selected submatrix is
+ *    stored here.
+ * \param constres Pointer to a real variable or a null pointer. If
+ *    not a null pointer, then the first non-zero element in the
+ *    selected submatrix is stored here, if there is one. Otherwise
+ *    zero is stored here. This behavior is handy if one
+ *    wants to select a single entry from the matrix.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_index(const igraph_sparsemat_t *A,
+                           const igraph_vector_int_t *p,
+                           const igraph_vector_int_t *q,
+                           igraph_sparsemat_t *res,
+                           igraph_real_t *constres) {
+
+    igraph_sparsemat_t II, JJ, II2, JJ2, tmp;
+    long int nrow = A->cs->m;
+    long int ncol = A->cs->n;
+    long int idx_rows = p ? igraph_vector_int_size(p) : -1;
+    long int idx_cols = q ? igraph_vector_int_size(q) : -1;
+    long int k;
+
+    igraph_sparsemat_t *myres = res, mres;
+
+    if (!p && !q) {
+        IGRAPH_ERROR("No index vectors", IGRAPH_EINVAL);
+    }
+
+    if (!res && (idx_rows != 1 || idx_cols != 1)) {
+        IGRAPH_ERROR("Sparse matrix indexing: must give `res' if not a "
+                     "single element is selected", IGRAPH_EINVAL);
+    }
+
+    if (!q) {
+        return igraph_i_sparsemat_index_rows(A, p, res, constres);
+    }
+    if (!p) {
+        return igraph_i_sparsemat_index_cols(A, q, res, constres);
+    }
+
+    if (!res) {
+        myres = &mres;
+    }
+
+    /* Create first index matrix */
+    IGRAPH_CHECK(igraph_sparsemat_init(&II2, (int) idx_rows, (int) nrow,
+                                       (int) idx_rows));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2);
+    for (k = 0; k < idx_rows; k++) {
+        igraph_sparsemat_entry(&II2, (int) k, VECTOR(*p)[k], 1.0);
+    }
+    IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II));
+    igraph_sparsemat_destroy(&II2);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &II);
+
+    /* Create second index matrix */
+    IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, (int) ncol, (int) idx_cols,
+                                       (int) idx_cols));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2);
+    for (k = 0; k < idx_cols; k++) {
+        igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], (int) k, 1.0);
+    }
+    IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ));
+    igraph_sparsemat_destroy(&JJ2);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ);
+
+    /* Multiply */
+    IGRAPH_CHECK(igraph_sparsemat_multiply(&II, A, &tmp));
+    igraph_sparsemat_destroy(&II);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
+    IGRAPH_CHECK(igraph_sparsemat_multiply(&tmp, &JJ, myres));
+    igraph_sparsemat_destroy(&tmp);
+    igraph_sparsemat_destroy(&JJ);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    if (constres) {
+        if (myres->cs->p [1] != 0) {
+            *constres = myres->cs->x [0];
+        } else {
+            *constres = 0.0;
+        }
+    }
+
+    if (!res) {
+        igraph_sparsemat_destroy(myres);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_entry
+ * Add an element to a sparse matrix
+ *
+ * This function can be used to add the entries to a sparse matrix,
+ * after initializing it with \ref igraph_sparsemat_init().
+ * \param A The input matrix, it must be in triplet format.
+ * \param row The row index of the entry to add.
+ * \param col The column index of the entry to add.
+ * \param elem The value of the entry.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col,
+                           igraph_real_t elem) {
+
+    if (!cs_entry(A->cs, row, col, elem)) {
+        IGRAPH_ERROR("Cannot add entry to sparse matrix",
+                     IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_compress
+ * Compress a sparse matrix, i.e. convert it to column-compress format
+ *
+ * Almost all sparse matrix operations require that the matrix is in
+ * column-compressed format.
+ * \param A The input matrix, it must be in triplet format.
+ * \param res Pointer to an uninitialized sparse matrix object, the
+ *    compressed version of \p A is stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_compress(const igraph_sparsemat_t *A,
+                              igraph_sparsemat_t *res) {
+
+    if (! (res->cs = cs_compress(A->cs)) ) {
+        IGRAPH_ERROR("Cannot compress sparse matrix", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_transpose
+ * Transpose a sparse matrix
+ *
+ * \param A The input matrix, column-compressed or triple format.
+ * \param res Pointer to an uninitialized sparse matrix, the result is
+ *    stored here.
+ * \param values If this is non-zero, the matrix transpose is
+ *    calculated the normal way. If it is zero, then only the pattern
+ *    of the input matrix is stored in the result, the values are not.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_transpose(const igraph_sparsemat_t *A,
+                               igraph_sparsemat_t *res,
+                               int values) {
+
+    if (A->cs->nz < 0) {
+        /* column-compressed */
+        if (! (res->cs = cs_transpose(A->cs, values)) ) {
+            IGRAPH_ERROR("Cannot transpose sparse matrix", IGRAPH_FAILURE);
+        }
+    } else {
+        /* triplets */
+        int *tmp;
+        IGRAPH_CHECK(igraph_sparsemat_copy(res, A));
+        tmp = res->cs->p;
+        res->cs->p = res->cs->i;
+        res->cs->i = tmp;
+    }
+    return 0;
+}
+
+igraph_bool_t
+igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A) {
+    igraph_sparsemat_t t, tt;
+    igraph_bool_t res;
+    int nz;
+
+    IGRAPH_CHECK(igraph_sparsemat_transpose(A, &t, /*values=*/ 1));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &t);
+    IGRAPH_CHECK(igraph_sparsemat_dupl(&t));
+    IGRAPH_CHECK(igraph_sparsemat_transpose(&t, &tt, /*values=*/ 1));
+    igraph_sparsemat_destroy(&t);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tt);
+    IGRAPH_CHECK(igraph_sparsemat_transpose(&tt, &t, /*values=*/ 1));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &t);
+
+    nz = t.cs->p[t.cs->n];
+    res = memcmp(t.cs->i, tt.cs->i, sizeof(int) * (size_t) nz) == 0;
+    res = res && memcmp(t.cs->p, tt.cs->p, sizeof(int) *
+                        (size_t)(t.cs->n + 1)) == 0;
+    res = res && memcmp(t.cs->x, tt.cs->x, sizeof(igraph_real_t) * (size_t)nz) == 0;
+
+    igraph_sparsemat_destroy(&t);
+    igraph_sparsemat_destroy(&tt);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return res;
+}
+
+igraph_bool_t
+igraph_i_sparsemat_is_symmetric_triplet(const igraph_sparsemat_t *A) {
+    igraph_sparsemat_t tmp;
+    igraph_bool_t res;
+    IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
+    res = igraph_i_sparsemat_is_symmetric_cc(&tmp);
+    igraph_sparsemat_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+    return res;
+}
+
+igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A) {
+
+    if (A->cs->m != A->cs->n) {
+        return 0;
+    }
+
+    if (A->cs->nz < 0) {
+        return igraph_i_sparsemat_is_symmetric_cc(A);
+    } else {
+        return igraph_i_sparsemat_is_symmetric_triplet(A);
+    }
+}
+
+/**
+ * \function igraph_sparsemat_dupl
+ * Remove duplicate elements from a sparse matrix
+ *
+ * It is possible that a column-compressed sparse matrix stores a
+ * single matrix entry in multiple pieces. The entry is then the sum
+ * of all its pieces. (Some functions create matrices like this.) This
+ * function eliminates the multiple pieces.
+ * \param A The input matrix, in column-compressed format.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_dupl(igraph_sparsemat_t *A) {
+
+    if (!cs_dupl(A->cs)) {
+        IGRAPH_ERROR("Cannot remove duplicates from sparse matrix",
+                     IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_fkeep
+ * Filter the elements of a sparse matrix
+ *
+ * This function can be used to filter the (non-zero) elements of a
+ * sparse matrix. For all entries, it calls the supplied function and
+ * depending on the return values either keeps, or deleted the element
+ * from the matrix.
+ * \param A The input matrix, in column-compressed format.
+ * \param fkeep The filter function. It must take four arguments: the
+ *    first is an \c int, the row index of the entry, the second is
+ *    another \c int, the column index. The third is \c igraph_real_t,
+ *    the value of the entry. The fourth element is a \c void pointer,
+ *    the \p other argument is passed here. The function must return
+ *    an \c int. If this is zero, then the entry is deleted, otherwise
+ *    it is kept.
+ * \param other A \c void pointer that is passed to the filtering
+ * function.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_fkeep(igraph_sparsemat_t *A,
+                           int (*fkeep)(int, int, igraph_real_t, void*),
+                           void *other) {
+
+    if (!cs_fkeep(A->cs, fkeep, other)) {
+        IGRAPH_ERROR("Cannot filter sparse matrix", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_dropzeros
+ * Drop the zero elements from a sparse matrix
+ *
+ * As a result of matrix operations, some of the entries in a sparse
+ * matrix might be zero. This function removes these entries.
+ * \param A The input matrix, it must be in column-compressed format.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) {
+
+    if (!cs_dropzeros(A->cs)) {
+        IGRAPH_ERROR("Cannot drop zeros from sparse matrix", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_droptol
+ * Drop the almost zero elements of a sparse matrix
+ *
+ * This function is similar to \ref igraph_sparsemat_dropzeros(), but it
+ * also drops entries that are closer to zero than the given tolerance
+ * threshold.
+ * \param A The input matrix, it must be in column-compressed format.
+ * \param tol Real number, giving the tolerance threshold.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) {
+
+    if (!cs_droptol(A->cs, tol)) {
+        IGRAPH_ERROR("Cannot drop (almost) zeros from sparse matrix",
+                     IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_multiply
+ * Matrix multiplication
+ *
+ * Multiplies two sparse matrices.
+ * \param A The first input matrix (left hand side), in
+ *   column-compressed format.
+ * \param B The second input matrix (right hand side), in
+ *   column-compressed format.
+ * \param res Pointer to an uninitialized sparse matrix, the result is
+ *   stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_multiply(const igraph_sparsemat_t *A,
+                              const igraph_sparsemat_t *B,
+                              igraph_sparsemat_t *res) {
+
+    if (! (res->cs = cs_multiply(A->cs, B->cs))) {
+        IGRAPH_ERROR("Cannot multiply matrices", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_add
+ * Sum of two sparse matrices
+ *
+ * \param A The first input matrix, in column-compressed format.
+ * \param B The second input matrix, in column-compressed format.
+ * \param alpha Real scalar, \p A is multiplied by \p alpha before the
+ *    addition.
+ * \param beta Real scalar, \p B is multiplied by \p beta before the
+ *    addition.
+ * \param res Pointer to an uninitialized sparse matrix, the result
+ *    is stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_add(const igraph_sparsemat_t *A,
+                         const igraph_sparsemat_t *B,
+                         igraph_real_t alpha,
+                         igraph_real_t beta,
+                         igraph_sparsemat_t *res) {
+
+    if (! (res->cs = cs_add(A->cs, B->cs, alpha, beta))) {
+        IGRAPH_ERROR("Cannot add matrices", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_gaxpy
+ * Matrix-vector product, added to another vector.
+ *
+ * \param A The input matrix, in column-compressed format.
+ * \param x The input vector, its size must match the number of
+ *    columns in \p A.
+ * \param res This vector is added to the matrix-vector product
+ *    and it is overwritten by the result.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A,
+                           const igraph_vector_t *x,
+                           igraph_vector_t *res) {
+
+    if (A->cs->n != igraph_vector_size(x) ||
+        A->cs->m != igraph_vector_size(res)) {
+        IGRAPH_ERROR("Invalid matrix/vector size for multiplication",
+                     IGRAPH_EINVAL);
+    }
+
+    if (! (cs_gaxpy(A->cs, VECTOR(*x), VECTOR(*res)))) {
+        IGRAPH_ERROR("Cannot perform sparse matrix vector multiplication",
+                     IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_lsolve
+ * Solve a lower-triangular linear system
+ *
+ * Solve the Lx=b linear equation system, where the L coefficient
+ * matrix is square and lower-triangular, with a zero-free diagonal.
+ * \param L The input matrix, in column-compressed format.
+ * \param b The right hand side of the linear system.
+ * \param res An initialized vector, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_lsolve(const igraph_sparsemat_t *L,
+                            const igraph_vector_t *b,
+                            igraph_vector_t *res) {
+
+    if (L->cs->m != L->cs->n) {
+        IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_NONSQUARE);
+    }
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_vector_update(res, b));
+    }
+
+    if (! cs_lsolve(L->cs, VECTOR(*res))) {
+        IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_ltsolve
+ * Solve an upper-triangular linear system
+ *
+ * Solve the L'x=b linear equation system, where the L
+ * matrix is square and lower-triangular, with a zero-free diagonal.
+ * \param L The input matrix, in column-compressed format.
+ * \param b The right hand side of the linear system.
+ * \param res An initialized vector, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res) {
+
+    if (L->cs->m != L->cs->n) {
+        IGRAPH_ERROR("Cannot perform transposed lower triangular solve",
+                     IGRAPH_NONSQUARE);
+    }
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_vector_update(res, b));
+    }
+
+    if (!cs_ltsolve(L->cs, VECTOR(*res))) {
+        IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_usolve
+ * Solve an upper-triangular linear system
+ *
+ * Solves the Ux=b upper triangular system.
+ * \param U The input matrix, in column-compressed format.
+ * \param b The right hand side of the linear system.
+ * \param res An initialized vector, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_usolve(const igraph_sparsemat_t *U,
+                            const igraph_vector_t *b,
+                            igraph_vector_t *res) {
+
+    if (U->cs->m != U->cs->n) {
+        IGRAPH_ERROR("Cannot perform upper triangular solve", IGRAPH_NONSQUARE);
+    }
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_vector_update(res, b));
+    }
+
+    if (! cs_usolve(U->cs, VECTOR(*res))) {
+        IGRAPH_ERROR("Cannot perform upper triangular solve", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_utsolve
+ * Solve a lower-triangular linear system
+ *
+ * This is the same as \ref igraph_sparsemat_usolve(), but U'x=b is
+ * solved, where the apostrophe denotes the transpose.
+ * \param U The input matrix, in column-compressed format.
+ * \param b The right hand side of the linear system.
+ * \param res An initialized vector, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_utsolve(const igraph_sparsemat_t *U,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res) {
+
+    if (U->cs->m != U->cs->n) {
+        IGRAPH_ERROR("Cannot perform transposed upper triangular solve",
+                     IGRAPH_NONSQUARE);
+    }
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_vector_update(res, b));
+    }
+
+    if (!cs_utsolve(U->cs, VECTOR(*res))) {
+        IGRAPH_ERROR("Cannot perform transposed upper triangular solve",
+                     IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_cholsol
+ * Solve a symmetric linear system via Cholesky decomposition
+ *
+ * Solve Ax=b, where A is a symmetric positive definite matrix.
+ * \param A The input matrix, in column-compressed format.
+ * \param v The right hand side.
+ * \param res An initialized vector, the result is stored here.
+ * \param order An integer giving the ordering method to use for the
+ *    factorization. Zero is the natural ordering; if it is one, then
+ *    the fill-reducing minimum-degree ordering of A+A' is used.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res,
+                             int order) {
+
+    if (A->cs->m != A->cs->n) {
+        IGRAPH_ERROR("Cannot perform sparse symmetric solve",
+                     IGRAPH_NONSQUARE);
+    }
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_vector_update(res, b));
+    }
+
+    if (! cs_cholsol(order, A->cs, VECTOR(*res))) {
+        IGRAPH_ERROR("Cannot perform sparse symmetric solve", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_lusol
+ * Solve a linear system via LU decomposition
+ *
+ * Solve Ax=b, via LU factorization of A.
+ * \param A The input matrix, in column-compressed format.
+ * \param b The right hand side of the equation.
+ * \param res An initialized vector, the result is stored here.
+ * \param order The ordering method to use, zero means the natural
+ *    ordering, one means the fill-reducing minimum-degree ordering of
+ *    A+A', two means the ordering of A'*A, after removing the dense
+ *    rows from A. Three means the ordering of A'*A.
+ * \param tol Real number, the tolerance limit to use for the numeric
+ *    LU factorization.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_lusol(const igraph_sparsemat_t *A,
+                           const igraph_vector_t *b,
+                           igraph_vector_t *res,
+                           int order,
+                           igraph_real_t tol) {
+
+    if (A->cs->m != A->cs->n) {
+        IGRAPH_ERROR("Cannot perform LU solve",
+                     IGRAPH_NONSQUARE);
+    }
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_vector_update(res, b));
+    }
+
+    if (! cs_lusol(order, A->cs, VECTOR(*res), tol)) {
+        IGRAPH_ERROR("Cannot perform LU solve", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A,
+                          igraph_bool_t directed) {
+
+    igraph_vector_t edges;
+    long int no_of_nodes = A->cs->m;
+    long int no_of_edges = A->cs->p[A->cs->n];
+    int *p = A->cs->p;
+    int *i = A->cs->i;
+    long int from = 0;
+    long int to = 0;
+    long int e = 0;
+
+    if (no_of_nodes != A->cs->n) {
+        IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+
+    while (*p < no_of_edges) {
+        while (to < * (p + 1)) {
+            if (directed || from >= *i) {
+                VECTOR(edges)[e++] = from;
+                VECTOR(edges)[e++] = (*i);
+            }
+            to++;
+            i++;
+        }
+        from++;
+        p++;
+    }
+    igraph_vector_resize(&edges, e);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A,
+                               igraph_bool_t directed) {
+
+    igraph_vector_t edges;
+    long int no_of_nodes = A->cs->m;
+    long int no_of_edges = A->cs->nz;
+    int *i = A->cs->p;
+    int *j = A->cs->i;
+    long int e;
+
+    if (no_of_nodes != A->cs->n) {
+        IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+
+    for (e = 0; e < 2 * no_of_edges; i++, j++) {
+        if (directed || *i >= *j) {
+            VECTOR(edges)[e++] = (*i);
+            VECTOR(edges)[e++] = (*j);
+        }
+    }
+    igraph_vector_resize(&edges, e);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat
+ * Create an igraph graph from a sparse matrix
+ *
+ * One edge is created for each non-zero entry in the matrix. If you
+ * have a symmetric matrix, and want to create an undirected graph,
+ * then delete the entries in the upper diagonal first, or call \ref
+ * igraph_simplify() on the result graph to eliminate the multiple
+ * edges.
+ * \param graph Pointer to an uninitialized igraph_t object, the
+ *    graphs is stored here.
+ * \param A The input matrix, in triplet or column-compressed format.
+ * \param directed Boolean scalar, whether to create a directed
+ *    graph.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A,
+                     igraph_bool_t directed) {
+
+    if (A->cs->nz < 0) {
+        return (igraph_i_sparsemat_cc(graph, A, directed));
+    } else {
+        return (igraph_i_sparsemat_triplet(graph, A, directed));
+    }
+}
+
+int igraph_i_weighted_sparsemat_cc(const igraph_sparsemat_t *A,
+                                   igraph_bool_t directed, const char *attr,
+                                   igraph_bool_t loops,
+                                   igraph_vector_t *edges,
+                                   igraph_vector_t *weights) {
+
+    long int no_of_edges = A->cs->p[A->cs->n];
+    int *p = A->cs->p;
+    int *i = A->cs->i;
+    igraph_real_t *x = A->cs->x;
+    long int from = 0;
+    long int to = 0;
+    long int e = 0, w = 0;
+
+    IGRAPH_UNUSED(attr);
+
+    igraph_vector_resize(edges, no_of_edges * 2);
+    igraph_vector_resize(weights, no_of_edges);
+
+    while (*p < no_of_edges) {
+        while (to < * (p + 1)) {
+            if ( (loops || from != *i) && (directed || from >= *i) && *x != 0) {
+                VECTOR(*edges)[e++] = (*i);
+                VECTOR(*edges)[e++] = from;
+                VECTOR(*weights)[w++] = (*x);
+            }
+            to++;
+            i++;
+            x++;
+        }
+        from++;
+        p++;
+    }
+
+    igraph_vector_resize(edges, e);
+    igraph_vector_resize(weights, w);
+
+    return 0;
+}
+
+int igraph_i_weighted_sparsemat_triplet(const igraph_sparsemat_t *A,
+                                        igraph_bool_t directed,
+                                        const char *attr,
+                                        igraph_bool_t loops,
+                                        igraph_vector_t *edges,
+                                        igraph_vector_t *weights) {
+
+    IGRAPH_UNUSED(A); IGRAPH_UNUSED(directed); IGRAPH_UNUSED(attr);
+    IGRAPH_UNUSED(loops); IGRAPH_UNUSED(edges); IGRAPH_UNUSED(weights);
+
+    /* TODO */
+    IGRAPH_ERROR("Triplet matrices are not implemented",
+                 IGRAPH_UNIMPLEMENTED);
+    return 0;
+}
+
+int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A,
+                              igraph_bool_t directed, const char *attr,
+                              igraph_bool_t loops) {
+
+    igraph_vector_t edges, weights;
+    int pot_edges = A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz;
+    const char* default_attr = "weight";
+    igraph_vector_ptr_t attr_vec;
+    igraph_attribute_record_t attr_rec;
+    long int no_of_nodes = A->cs->m;
+
+    if (no_of_nodes != A->cs->n) {
+        IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, pot_edges * 2);
+    IGRAPH_VECTOR_INIT_FINALLY(&weights, pot_edges);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&attr_vec, 1);
+
+    if (A->cs->nz < 0) {
+        IGRAPH_CHECK(igraph_i_weighted_sparsemat_cc(A, directed, attr, loops,
+                     &edges, &weights));
+    } else {
+        IGRAPH_CHECK(igraph_i_weighted_sparsemat_triplet(A, directed, attr,
+                     loops, &edges,
+                     &weights));
+    }
+
+    /* Prepare attribute record */
+    attr_rec.name = attr ? attr : default_attr;
+    attr_rec.type = IGRAPH_ATTRIBUTE_NUMERIC;
+    attr_rec.value = &weights;
+    VECTOR(attr_vec)[0] = &attr_rec;
+
+    /* Create graph */
+    IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes, directed));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+    if (igraph_vector_size(&edges) > 0) {
+        IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec));
+    }
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Cleanup */
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&weights);
+    igraph_vector_ptr_destroy(&attr_vec);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_get_sparsemat
+ * Convert an igraph graph to a sparse matrix
+ *
+ * If the graph is undirected, then a symmetric matrix is created.
+ * \param graph The input graph.
+ * \param res Pointer to an uninitialized sparse matrix. The result
+ *    will be stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+    long int nzmax = directed ? no_of_edges : no_of_edges * 2;
+    long int i;
+
+    IGRAPH_CHECK(igraph_sparsemat_init(res, (igraph_integer_t) no_of_nodes,
+                                       (igraph_integer_t) no_of_nodes,
+                                       (igraph_integer_t) nzmax));
+
+    for (i = 0; i < no_of_edges; i++) {
+        long int from = IGRAPH_FROM(graph, i);
+        long int to = IGRAPH_TO(graph, i);
+        IGRAPH_CHECK(igraph_sparsemat_entry(res, (int) from, (int) to, 1.0));
+        if (!directed && from != to) {
+            IGRAPH_CHECK(igraph_sparsemat_entry(res, (int) to, (int) from, 1.0));
+        }
+    }
+
+    return 0;
+}
+
+#define CHECK(x) if ((x)<0) { IGRAPH_ERROR("Cannot write to file", IGRAPH_EFILE); }
+
+/**
+ * \function igraph_sparsemat_print
+ * Print a sparse matrix to a file
+ *
+ * Only the non-zero entries are printed. This function serves more as
+ * a debugging utility, as currently there is no function that could
+ * read back the printed matrix from the file.
+ * \param A The input matrix, triplet or column-compressed format.
+ * \param outstream The stream to print it to.
+ * \return Error code.
+ *
+ * Time complexity: O(nz) for triplet matrices, O(n+nz) for
+ * column-compressed matrices. nz is the number of non-zero elements,
+ * n is the number columns in the matrix.
+ */
+
+int igraph_sparsemat_print(const igraph_sparsemat_t *A,
+                           FILE *outstream) {
+
+    if (A->cs->nz < 0) {
+        /* CC */
+        int j, p;
+        for (j = 0; j < A->cs->n; j++) {
+            CHECK(fprintf(outstream, "col %i: locations %i to %i\n",
+                          j, A->cs->p[j], A->cs->p[j + 1] - 1));
+            for (p = A->cs->p[j]; p < A->cs->p[j + 1]; p++) {
+                CHECK(fprintf(outstream, "%i : %g\n", A->cs->i[p], A->cs->x[p]));
+            }
+        }
+    } else {
+        /* Triplet */
+        int p;
+        for (p = 0; p < A->cs->nz; p++) {
+            CHECK(fprintf(outstream, "%i %i : %g\n",
+                          A->cs->i[p], A->cs->p[p], A->cs->x[p]));
+        }
+    }
+
+    return 0;
+}
+
+#undef CHECK
+
+int igraph_i_sparsemat_eye_triplet(igraph_sparsemat_t *A, int n, int nzmax,
+                                   igraph_real_t value) {
+    long int i;
+
+    IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax));
+
+    for (i = 0; i < n; i++) {
+        igraph_sparsemat_entry(A, (int) i, (int) i, value);
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_eye_cc(igraph_sparsemat_t *A, int n,
+                              igraph_real_t value) {
+    long int i;
+
+    if (! (A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0)) ) {
+        IGRAPH_ERROR("Cannot create eye sparse matrix", IGRAPH_FAILURE);
+    }
+
+    for (i = 0; i < n; i++) {
+        A->cs->p [i] = (int) i;
+        A->cs->i [i] = (int) i;
+        A->cs->x [i] = value;
+    }
+    A->cs->p [n] = n;
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_eye
+ * Create a sparse identity matrix
+ *
+ * \param A An uninitialized sparse matrix, the result is stored
+ *   here.
+ * \param n The number of rows and number of columns in the matrix.
+ * \param nzmax The maximum number of non-zero elements, this
+ *   essentially gives the amount of memory that will be allocated for
+ *   matrix elements.
+ * \param value The value to store in the diagonal.
+ * \param compress Whether to create a column-compressed matrix. If
+ *   false, then a triplet matrix is created.
+ * \return Error code.
+ *
+ * Time complexity: O(n).
+ */
+
+int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax,
+                         igraph_real_t value,
+                         igraph_bool_t compress) {
+    if (compress) {
+        return (igraph_i_sparsemat_eye_cc(A, n, value));
+    } else {
+        return (igraph_i_sparsemat_eye_triplet(A, n, nzmax, value));
+    }
+}
+
+int igraph_i_sparsemat_diag_triplet(igraph_sparsemat_t *A, int nzmax,
+                                    const igraph_vector_t *values) {
+
+    int i, n = (int) igraph_vector_size(values);
+
+    IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax));
+
+    for (i = 0; i < n; i++) {
+        igraph_sparsemat_entry(A, i, i, VECTOR(*values)[i]);
+    }
+
+    return 0;
+
+}
+
+int igraph_i_sparsemat_diag_cc(igraph_sparsemat_t *A,
+                               const igraph_vector_t *values) {
+
+    int i, n = (int) igraph_vector_size(values);
+
+    if (! (A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0)) ) {
+        IGRAPH_ERROR("Cannot create eye sparse matrix", IGRAPH_FAILURE);
+    }
+
+    for (i = 0; i < n; i++) {
+        A->cs->p [i] = i;
+        A->cs->i [i] = i;
+        A->cs->x [i] = VECTOR(*values)[i];
+    }
+    A->cs->p [n] = n;
+
+    return 0;
+
+}
+
+/**
+ * \function igraph_sparsemat_diag
+ * Create a sparse diagonal matrix
+ *
+ * \param A An uninitialized sparse matrix, the result is stored
+ *    here.
+ * \param nzmax The maximum number of non-zero elements, this
+ *   essentially gives the amount of memory that will be allocated for
+ *   matrix elements.
+ * \param values The values to store in the diagonal, the size of the
+ *    matrix defined by the length of this vector.
+ * \param compress Whether to create a column-compressed matrix. If
+ *   false, then a triplet matrix is created.
+ * \return Error code.
+ *
+ * Time complexity: O(n), the length of the diagonal vector.
+ */
+
+int igraph_sparsemat_diag(igraph_sparsemat_t *A, int nzmax,
+                          const igraph_vector_t *values,
+                          igraph_bool_t compress) {
+
+    if (compress) {
+        return (igraph_i_sparsemat_diag_cc(A, values));
+    } else {
+        return (igraph_i_sparsemat_diag_triplet(A, nzmax, values));
+    }
+}
+
+int igraph_i_sparsemat_arpack_multiply(igraph_real_t *to,
+                                       const igraph_real_t *from,
+                                       int n,
+                                       void *extra) {
+    igraph_sparsemat_t *A = extra;
+    igraph_vector_t vto, vfrom;
+    igraph_vector_view(&vto, to, n);
+    igraph_vector_view(&vfrom, from, n);
+    igraph_vector_null(&vto);
+    IGRAPH_CHECK(igraph_sparsemat_gaxpy(A, &vfrom, &vto));
+    return 0;
+}
+
+typedef struct igraph_i_sparsemat_arpack_rssolve_data_t {
+    igraph_sparsemat_symbolic_t *dis;
+    igraph_sparsemat_numeric_t *din;
+    igraph_real_t tol;
+    igraph_sparsemat_solve_t method;
+} igraph_i_sparsemat_arpack_rssolve_data_t;
+
+int igraph_i_sparsemat_arpack_solve(igraph_real_t *to,
+                                    const igraph_real_t *from,
+                                    int n,
+                                    void *extra) {
+
+    igraph_i_sparsemat_arpack_rssolve_data_t *data = extra;
+    igraph_vector_t vfrom, vto;
+
+    igraph_vector_view(&vfrom, from, n);
+    igraph_vector_view(&vto, to, n);
+
+    if (data->method == IGRAPH_SPARSEMAT_SOLVE_LU) {
+        IGRAPH_CHECK(igraph_sparsemat_luresol(data->dis, data->din, &vfrom,
+                                              &vto));
+    } else if (data->method == IGRAPH_SPARSEMAT_SOLVE_QR) {
+        IGRAPH_CHECK(igraph_sparsemat_qrresol(data->dis, data->din, &vfrom,
+                                              &vto));
+
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_arpack_rssolve
+ * Eigenvalues and eigenvectors of a symmetric sparse matrix via ARPACK
+ *
+ * \param The input matrix, must be column-compressed.
+ * \param options It is passed to \ref igraph_arpack_rssolve(). See
+ *    \ref igraph_arpack_options_t for the details. If \c mode is 1,
+ *    then ARPACK uses regular mode, if \c mode is 3, then shift and
+ *    invert mode is used and the \c sigma structure member defines
+ *    the shift.
+ * \param storage Storage for ARPACK. See \ref
+ *    igraph_arpack_rssolve() and \ref igraph_arpack_storage_t for
+ *    details.
+ * \param values An initialized vector or a null pointer, the
+ *    eigenvalues are stored here.
+ * \param vectors An initialised matrix, or a null pointer, the
+ *    eigenvectors are stored here, in the columns.
+ * \param solvemethod The method to solve the linear system, if \c
+ *    mode is 3, i.e. the shift and invert mode is used.
+ *    Possible values:
+ *    \clist
+ *      \cli IGRAPH_SPARSEMAT_SOLVE_LU
+ *           The linear system is solved using LU decomposition.
+ *      \cli IGRAPH_SPARSEMAT_SOLVE_QR
+ *           The linear system is solved using QR decomposition.
+ *    \endclist
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A,
+                                    igraph_arpack_options_t *options,
+                                    igraph_arpack_storage_t *storage,
+                                    igraph_vector_t *values,
+                                    igraph_matrix_t *vectors,
+                                    igraph_sparsemat_solve_t solvemethod) {
+
+    int n = (int) igraph_sparsemat_nrow(A);
+
+    if (n != igraph_sparsemat_ncol(A)) {
+        IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE);
+    }
+
+    options->n = n;
+
+    if (options->mode == 1) {
+        IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_multiply,
+                                           (void*) A, options, storage,
+                                           values, vectors));
+    } else if (options->mode == 3) {
+        igraph_real_t sigma = options->sigma;
+        igraph_sparsemat_t OP, eye;
+        igraph_sparsemat_symbolic_t symb;
+        igraph_sparsemat_numeric_t num;
+        igraph_i_sparsemat_arpack_rssolve_data_t data;
+        /*-----------------------------------*/
+        /* We need to factor the (A-sigma*I) */
+        /*-----------------------------------*/
+
+        /* Create (A-sigma*I) */
+        IGRAPH_CHECK(igraph_sparsemat_eye(&eye, /*n=*/ n, /*nzmax=*/ n,
+                                          /*value=*/ -sigma, /*compress=*/ 1));
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, &eye);
+        IGRAPH_CHECK(igraph_sparsemat_add(/*A=*/ A, /*B=*/ &eye, /*alpha=*/ 1.0,
+                     /*beta=*/ 1.0, /*res=*/ &OP));
+        igraph_sparsemat_destroy(&eye);
+        IGRAPH_FINALLY_CLEAN(1);
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, &OP);
+
+        if (solvemethod == IGRAPH_SPARSEMAT_SOLVE_LU) {
+            /* Symbolic analysis */
+            IGRAPH_CHECK(igraph_sparsemat_symblu(/*order=*/ 0, &OP, &symb));
+            IGRAPH_FINALLY(igraph_sparsemat_symbolic_destroy, &symb);
+            /* Numeric LU factorization */
+            IGRAPH_CHECK(igraph_sparsemat_lu(&OP, &symb, &num, /*tol=*/ 0));
+            IGRAPH_FINALLY(igraph_sparsemat_numeric_destroy, &num);
+        } else if (solvemethod == IGRAPH_SPARSEMAT_SOLVE_QR) {
+            /* Symbolic analysis */
+            IGRAPH_CHECK(igraph_sparsemat_symbqr(/*order=*/ 0, &OP, &symb));
+            IGRAPH_FINALLY(igraph_sparsemat_symbolic_destroy, &symb);
+            /* Numeric QR factorization */
+            IGRAPH_CHECK(igraph_sparsemat_qr(&OP, &symb, &num));
+            IGRAPH_FINALLY(igraph_sparsemat_numeric_destroy, &num);
+        }
+
+        data.dis = &symb;
+        data.din = &num;
+        data.tol = options->tol;
+        data.method = solvemethod;
+        IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_solve,
+                                           (void*) &data, options, storage,
+                                           values, vectors));
+
+        igraph_sparsemat_numeric_destroy(&num);
+        igraph_sparsemat_symbolic_destroy(&symb);
+        igraph_sparsemat_destroy(&OP);
+        IGRAPH_FINALLY_CLEAN(3);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_arpack_rnsolve
+ * Eigenvalues and eigenvectors of a nonsymmetric sparse matrix via ARPACK
+ *
+ * Eigenvalues and/or eigenvectors of a nonsymmetric sparse matrix.
+ * \param A The input matrix, in column-compressed mode.
+ * \param options ARPACK options, it is passed to \ref
+ *    igraph_arpack_rnsolve(). See also \ref igraph_arpack_options_t
+ *    for details.
+ * \param storage Storage for ARPACK, this is passed to \ref
+ *    igraph_arpack_rnsolve(). See \ref igraph_arpack_storage_t for
+ *    details.
+ * \param values An initialized matrix, or a null pointer. If not a
+ *    null pointer, then the eigenvalues are stored here, the first
+ *    column is the real part, the second column is the imaginary
+ *    part.
+ * \param vectors An initialized matrix, or a null pointer. If not a
+ *    null pointer, then the eigenvectors are stored here, please see
+ *    \ref igraph_arpack_rnsolve() for the format.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A,
+                                    igraph_arpack_options_t *options,
+                                    igraph_arpack_storage_t *storage,
+                                    igraph_matrix_t *values,
+                                    igraph_matrix_t *vectors) {
+
+    int n = (int) igraph_sparsemat_nrow(A);
+
+    if (n != igraph_sparsemat_ncol(A)) {
+        IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE);
+    }
+
+    options->n = n;
+
+    return igraph_arpack_rnsolve(igraph_i_sparsemat_arpack_multiply,
+                                 (void*) A, options, storage,
+                                 values, vectors);
+}
+
+/**
+ * \function igraph_sparsemat_symbqr
+ * Symbolic QR decomposition
+ *
+ * QR decomposition of sparse matrices involves two steps, the first
+ * is calling this function, and then \ref
+ * igraph_sparsemat_qr().
+ * \param order The ordering to use: 0 means natural ordering, 1 means
+ *   minimum degree ordering of A+A', 2 is minimum degree ordering of
+ *   A'A after removing the dense rows from A, and 3 is the minimum
+ *   degree ordering of A'A.
+ * \param A The input matrix, in column-compressed format.
+ * \param dis The result of the symbolic analysis is stored here. Once
+ *    not needed anymore, it must be destroyed by calling \ref
+ *    igraph_sparsemat_symbolic_destroy().
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A,
+                            igraph_sparsemat_symbolic_t *dis) {
+
+    dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 1);
+    if (!dis->symbolic) {
+        IGRAPH_ERROR("Cannot do symbolic QR decomposition", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_symblu
+ * Symbolic LU decomposition
+ *
+ * LU decomposition of sparse matrices involves two steps, the first
+ * is calling this function, and then \ref igraph_sparsemat_lu().
+ * \param order The ordering to use: 0 means natural ordering, 1 means
+ *   minimum degree ordering of A+A', 2 is minimum degree ordering of
+ *   A'A after removing the dense rows from A, and 3 is the minimum
+ *   degree ordering of A'A.
+ * \param A The input matrix, in column-compressed format.
+ * \param dis The result of the symbolic analysis is stored here. Once
+ *    not needed anymore, it must be destroyed by calling \ref
+ *    igraph_sparsemat_symbolic_destroy().
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A,
+                            igraph_sparsemat_symbolic_t *dis) {
+
+    dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 0);
+    if (!dis->symbolic) {
+        IGRAPH_ERROR("Cannot do symbolic LU decomposition", IGRAPH_FAILURE);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_lu
+ * LU decomposition of a sparse matrix
+ *
+ * Performs numeric sparse LU decomposition of a matrix.
+ * \param A The input matrix, in column-compressed format.
+ * \param dis The symbolic analysis for LU decomposition, coming from
+ *    a call to the \ref igraph_sparsemat_symblu() function.
+ * \param din The numeric decomposition, the result is stored here. It
+ *    can be used to solve linear systems with changing right hand
+ *    side vectors, by calling \ref igraph_sparsemat_luresol(). Once
+ *    not needed any more, it must be destroyed by calling \ref
+ *    igraph_sparsemat_symbolic_destroy() on it.
+ * \param tol The tolerance for the numeric LU decomposition.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_lu(const igraph_sparsemat_t *A,
+                        const igraph_sparsemat_symbolic_t *dis,
+                        igraph_sparsemat_numeric_t *din, double tol) {
+    din->numeric = cs_lu(A->cs, dis->symbolic, tol);
+    if (!din->numeric) {
+        IGRAPH_ERROR("Cannot do LU decomposition", IGRAPH_FAILURE);
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_qr
+ * QR decomposition of a sparse matrix
+ *
+ * Numeric QR decomposition of a sparse matrix.
+ * \param A The input matrix, in column-compressed format.
+ * \param dis The result of the symbolic QR analysis, from the
+ *    function \ref igraph_sparsemat_symbqr().
+ * \param din The result of the decomposition is stored here, it can
+ *    be used to solve many linear systems with the same coefficient
+ *    matrix and changing right hand sides, using the \ref
+ *    igraph_sparsemat_qrresol() function. Once not needed any more,
+ *    one should call \ref igraph_sparsemat_numeric_destroy() on it to
+ *    free the allocated memory.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_qr(const igraph_sparsemat_t *A,
+                        const igraph_sparsemat_symbolic_t *dis,
+                        igraph_sparsemat_numeric_t *din) {
+    din->numeric = cs_qr(A->cs, dis->symbolic);
+    if (!din->numeric) {
+        IGRAPH_ERROR("Cannot do QR decomposition", IGRAPH_FAILURE);
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_luresol
+ * Solve linear system using a precomputed LU decomposition
+ *
+ * Uses the LU decomposition of a matrix to solve linear systems.
+ * \param dis The symbolic analysis of the coefficient matrix, the
+ *    result of \ref igraph_sparsemat_symblu().
+ * \param din The LU decomposition, the result of a call to \ref
+ *    igraph_sparsemat_lu().
+ * \param b A vector that defines the right hand side of the linear
+ *    equation system.
+ * \param res An initialized vector, the solution of the linear system
+ *    is stored here.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis,
+                             const igraph_sparsemat_numeric_t *din,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res) {
+    int n = din->numeric->L->n;
+    igraph_real_t *workspace;
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_vector_update(res, b));
+    }
+
+    workspace = igraph_Calloc(n, igraph_real_t);
+    if (!workspace) {
+        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, workspace);
+
+    if (!cs_ipvec(din->numeric->pinv, VECTOR(*res), workspace, n)) {
+        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE);
+    }
+    if (!cs_lsolve(din->numeric->L, workspace)) {
+        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE);
+    }
+    if (!cs_usolve(din->numeric->U, workspace)) {
+        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE);
+    }
+    if (!cs_ipvec(dis->symbolic->q, workspace, VECTOR(*res), n)) {
+        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE);
+    }
+
+    igraph_Free(workspace);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_qrresol
+ * Solve a linear system using a precomputed QR decomposition
+ *
+ * Solves a linear system using a QR decomposition of its coefficient
+ * matrix.
+ * \param dis Symbolic analysis of the coefficient matrix, the result
+ *    of \ref igraph_sparsemat_symbqr().
+ * \param din The QR decomposition of the coefficient matrix, the
+ *    result of \ref igraph_sparsemat_qr().
+ * \param b Vector, giving the right hand side of the linear equation
+ *    system.
+ * \param res An initialized vector, the solution is stored here. It
+ *    is resized as needed.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis,
+                             const igraph_sparsemat_numeric_t *din,
+                             const igraph_vector_t *b,
+                             igraph_vector_t *res) {
+    int n = din->numeric->L->n;
+    igraph_real_t *workspace;
+    int k;
+
+    if (res != b) {
+        IGRAPH_CHECK(igraph_vector_update(res, b));
+    }
+
+    workspace = igraph_Calloc(dis->symbolic ? dis->symbolic->m2 : 1,
+                              igraph_real_t);
+    if (!workspace) {
+        IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
+    }
+    IGRAPH_FINALLY(igraph_free, workspace);
+
+    if (!cs_ipvec(dis->symbolic->pinv, VECTOR(*res), workspace, n)) {
+        IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
+    }
+    for (k = 0; k < n; k++) {
+        if (!cs_happly(din->numeric->L, k, din->numeric->B[k], workspace)) {
+            IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
+        }
+    }
+    if (!cs_usolve(din->numeric->U, workspace)) {
+        IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
+    }
+    if (!cs_ipvec(dis->symbolic->q, workspace, VECTOR(*res), n)) {
+        IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
+    }
+
+    igraph_Free(workspace);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_symbolic_destroy
+ * Deallocate memory for a symbolic decomposition
+ *
+ * Frees the memory allocated by \ref igraph_sparsemat_symbqr() or
+ * \ref igraph_sparsemat_symblu().
+ * \param dis The symbolic analysis.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_sparsemat_symbolic_destroy(igraph_sparsemat_symbolic_t *dis) {
+    cs_sfree(dis->symbolic);
+    dis->symbolic = 0;
+}
+
+/**
+ * \function igraph_sparsemat_numeric_destroy
+ * Deallocate memory for a numeric decomposition
+ *
+ * Frees the memoty allocated by \ref igraph_sparsemat_qr() or \ref
+ * igraph_sparsemat_lu().
+ * \param din The LU or QR decomposition.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din) {
+    cs_nfree(din->numeric);
+    din->numeric = 0;
+}
+
+/**
+ * \function igraph_matrix_as_sparsemat
+ * Convert a dense matrix to a sparse matrix
+ *
+ * \param res An uninitialized sparse matrix, the result is stored
+ *    here.
+ * \param mat The dense input matrix.
+ * \param tol Real scalar, the tolerance. Values closer than \p tol to
+ *    zero are considered as zero, and will not be included in the
+ *    sparse matrix.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements in the dense
+ * matrix.
+ */
+
+int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res,
+                               const igraph_matrix_t *mat,
+                               igraph_real_t tol) {
+    int nrow = (int) igraph_matrix_nrow(mat);
+    int ncol = (int) igraph_matrix_ncol(mat);
+    int i, j, nzmax = 0;
+
+    for (i = 0; i < nrow; i++) {
+        for (j = 0; j < ncol; j++) {
+            if (fabs(MATRIX(*mat, i, j)) > tol) {
+                nzmax++;
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_sparsemat_init(res, nrow, ncol, nzmax));
+
+    for (i = 0; i < nrow; i++) {
+        for (j = 0; j < ncol; j++) {
+            if (fabs(MATRIX(*mat, i, j)) > tol) {
+                IGRAPH_CHECK(igraph_sparsemat_entry(res, i, j, MATRIX(*mat, i, j)));
+            }
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res,
+                                    const igraph_sparsemat_t *spmat) {
+
+    int nrow = (int) igraph_sparsemat_nrow(spmat);
+    int ncol = (int) igraph_sparsemat_ncol(spmat);
+    int *p = spmat->cs->p;
+    int *i = spmat->cs->i;
+    igraph_real_t *x = spmat->cs->x;
+    int nzmax = spmat->cs->nzmax;
+    int from = 0, to = 0;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol));
+    igraph_matrix_null(res);
+
+    while (*p < nzmax) {
+        while (to < * (p + 1)) {
+            MATRIX(*res, *i, from) += *x;
+            to++;
+            i++;
+            x++;
+        }
+        from++;
+        p++;
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res,
+        const igraph_sparsemat_t *spmat) {
+    int nrow = (int) igraph_sparsemat_nrow(spmat);
+    int ncol = (int) igraph_sparsemat_ncol(spmat);
+    int *i = spmat->cs->p;
+    int *j = spmat->cs->i;
+    igraph_real_t *x = spmat->cs->x;
+    int nz = spmat->cs->nz;
+    int e;
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol));
+    igraph_matrix_null(res);
+
+    for (e = 0; e < nz; e++, i++, j++, x++) {
+        MATRIX(*res, *j, *i) += *x;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_as_matrix
+ * Convert a sparse matrix to a dense matrix
+ *
+ * \param res Pointer to an initialized matrix, the result is stored
+ *    here. It will be resized to the required size.
+ * \param spmat The input sparse matrix, in triplet or
+ *    column-compressed format.
+ * \return Error code.
+ *
+ * Time complexity: O(mn), the number of elements in the dense
+ * matrix.
+ */
+
+int igraph_sparsemat_as_matrix(igraph_matrix_t *res,
+                               const igraph_sparsemat_t *spmat) {
+    if (spmat->cs->nz < 0) {
+        return (igraph_i_sparsemat_as_matrix_cc(res, spmat));
+    } else {
+        return (igraph_i_sparsemat_as_matrix_triplet(res, spmat));
+    }
+}
+
+/**
+ * \function igraph_sparsemat_max
+ * Maximum of a sparse matrix
+ *
+ * \param A The input matrix, column-compressed.
+ * \return The maximum in the input matrix, or \c IGRAPH_NEGINFINITY
+ *    if the matrix has zero elements.
+ *
+ * Time complexity: TODO.
+ */
+
+igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A) {
+    int i, n;
+    igraph_real_t *ptr;
+    igraph_real_t res;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    ptr = A->cs->x;
+    n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz;
+    if (n == 0) {
+        return IGRAPH_NEGINFINITY;
+    }
+    res = *ptr;
+    for (i = 1; i < n; i++, ptr++) {
+        if (*ptr > res) {
+            res = *ptr;
+        }
+    }
+    return res;
+}
+
+/* TODO: CC matrix don't actually need _dupl,
+   because the elements are right beside each other.
+   Same for max and minmax. */
+
+/**
+ * \function igraph_sparsemat_min
+ * Minimum of a sparse matrix
+ *
+ * \param A The input matrix, column-compressed.
+ * \return The minimum in the input matrix, or \c IGRAPH_POSINFINITY
+ *    if the matrix has zero elements.
+ *
+ * Time complexity: TODO.
+ */
+
+igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A) {
+    int i, n;
+    igraph_real_t *ptr;
+    igraph_real_t res;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    ptr = A->cs->x;
+    n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz;
+    if (n == 0) {
+        return IGRAPH_POSINFINITY;
+    }
+    res = *ptr;
+    for (i = 1; i < n; i++, ptr++) {
+        if (*ptr < res) {
+            res = *ptr;
+        }
+    }
+    return res;
+}
+
+/**
+ * \function igraph_sparsemat_minmax
+ * Minimum and maximum of a sparse matrix
+ *
+ * \param A The input matrix, column-compressed.
+ * \param min The minimum in the input matrix is stored here, or \c
+ *    IGRAPH_POSINFINITY if the matrix has zero elements.
+ * \param max The maximum in the input matrix is stored here, or \c
+ *    IGRAPH_NEGINFINITY if the matrix has zero elements.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+
+int igraph_sparsemat_minmax(igraph_sparsemat_t *A,
+                            igraph_real_t *min, igraph_real_t *max) {
+    int i, n;
+    igraph_real_t *ptr;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    ptr = A->cs->x;
+    n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz;
+    if (n == 0) {
+        *min = IGRAPH_POSINFINITY;
+        *max = IGRAPH_NEGINFINITY;
+        return 0;
+    }
+    *min = *max = *ptr;
+    for (i = 1; i < n; i++, ptr++) {
+        if (*ptr > *max) {
+            *max = *ptr;
+        } else if (*ptr < *min) {
+            *min = *ptr;
+        }
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_count_nonzero
+ * Count nonzero elements of a sparse matrix
+ *
+ * \param A The input matrix, column-compressed.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) {
+    int i, n;
+    int res = 0;
+    igraph_real_t *ptr;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    ptr = A->cs->x;
+    n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz;
+    if (n == 0) {
+        return 0;
+    }
+    for (i = 0; i < n; i++, ptr++) {
+        if (*ptr) {
+            res++;
+        }
+    }
+    return res;
+}
+
+/**
+ * \function igraph_sparsemat_count_nonzerotol
+ * Count nonzero elements of a sparse matrix, ignoring elements close to zero
+ *
+ * Count the number of matrix entries that are closer to zero than \p
+ * tol.
+ * \param The input matrix, column-compressed.
+ * \param Real scalar, the tolerance.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A,
+        igraph_real_t tol) {
+    int i, n;
+    int res = 0;
+    igraph_real_t *ptr;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    ptr = A->cs->x;
+    n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz;
+    if (n == 0) {
+        return 0;
+    }
+    for (i = 0; i < n; i++, ptr++) {
+        if (*ptr < - tol || *ptr > tol) {
+            res++;
+        }
+    }
+    return res;
+}
+
+int igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A,
+                                       igraph_vector_t *res) {
+    int i;
+    int *pi = A->cs->i;
+    double *px = A->cs->x;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
+    igraph_vector_null(res);
+
+    for (i = 0; i < A->cs->nz; i++, pi++, px++) {
+        VECTOR(*res)[ *pi ] += *px;
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A,
+                                  igraph_vector_t *res) {
+    int ne = A->cs->p[A->cs->n];
+    double *px = A->cs->x;
+    int *pi = A->cs->i;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
+    igraph_vector_null(res);
+
+    for (; pi < A->cs->i + ne; pi++, px++) {
+        VECTOR(*res)[ *pi ] += *px;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_rowsums
+ * Row-wise sums.
+ *
+ * \param A The input matrix, in triplet or column-compressed format.
+ * \param res An initialized vector, the result is stored here. It
+ *    will be resized as needed.
+ * \return Error code.
+ *
+ * Time complexity: O(nz), the number of non-zero elements.
+ */
+
+int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A,
+                             igraph_vector_t *res) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        return igraph_i_sparsemat_rowsums_triplet(A, res);
+    } else {
+        return igraph_i_sparsemat_rowsums_cc(A, res);
+    }
+}
+
+int igraph_i_sparsemat_rowmins_triplet(const igraph_sparsemat_t *A,
+                                       igraph_vector_t *res) {
+    int i;
+    int *pi = A->cs->i;
+    double *px = A->cs->x;
+    double inf = IGRAPH_INFINITY;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
+    igraph_vector_fill(res, inf);
+
+    for (i = 0; i < A->cs->nz; i++, pi++, px++) {
+        if (*px < VECTOR(*res)[ *pi ]) {
+            VECTOR(*res)[ *pi ] = *px;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A,
+                                  igraph_vector_t *res) {
+    int ne;
+    double *px;
+    int *pi;
+    double inf = IGRAPH_INFINITY;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    ne = A->cs->p[A->cs->n];
+    px = A->cs->x;
+    pi = A->cs->i;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
+    igraph_vector_fill(res, inf);
+
+    for (; pi < A->cs->i + ne; pi++, px++) {
+        if (*px < VECTOR(*res)[ *pi ]) {
+            VECTOR(*res)[ *pi ] = *px;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_sparsemat_rowmins(igraph_sparsemat_t *A,
+                             igraph_vector_t *res) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        return igraph_i_sparsemat_rowmins_triplet(A, res);
+    } else {
+        return igraph_i_sparsemat_rowmins_cc(A, res);
+    }
+}
+
+
+int igraph_i_sparsemat_rowmaxs_triplet(const igraph_sparsemat_t *A,
+                                       igraph_vector_t *res) {
+    int i;
+    int *pi = A->cs->i;
+    double *px = A->cs->x;
+    double inf = IGRAPH_NEGINFINITY;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
+    igraph_vector_fill(res, inf);
+
+    for (i = 0; i < A->cs->nz; i++, pi++, px++) {
+        if (*px > VECTOR(*res)[ *pi ]) {
+            VECTOR(*res)[ *pi ] = *px;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A,
+                                  igraph_vector_t *res) {
+    int ne;
+    double *px;
+    int *pi;
+    double inf = IGRAPH_NEGINFINITY;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    ne = A->cs->p[A->cs->n];
+    px = A->cs->x;
+    pi = A->cs->i;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
+    igraph_vector_fill(res, inf);
+
+    for (; pi < A->cs->i + ne; pi++, px++) {
+        if (*px > VECTOR(*res)[ *pi ]) {
+            VECTOR(*res)[ *pi ] = *px;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A,
+                             igraph_vector_t *res) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        return igraph_i_sparsemat_rowmaxs_triplet(A, res);
+    } else {
+        return igraph_i_sparsemat_rowmaxs_cc(A, res);
+    }
+}
+
+int igraph_i_sparsemat_colmins_triplet(const igraph_sparsemat_t *A,
+                                       igraph_vector_t *res) {
+    int i;
+    int *pp = A->cs->p;
+    double *px = A->cs->x;
+    double inf = IGRAPH_INFINITY;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n));
+    igraph_vector_fill(res, inf);
+
+    for (i = 0; i < A->cs->nz; i++, pp++, px++) {
+        if (*px < VECTOR(*res)[ *pp ]) {
+            VECTOR(*res)[ *pp ] = *px;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A,
+                                  igraph_vector_t *res) {
+    int n;
+    double *px;
+    int *pp;
+    int *pi;
+    double *pr;
+    double inf = IGRAPH_INFINITY;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    n = A->cs->n;
+    px = A->cs->x;
+    pp = A->cs->p;
+    pi = A->cs->i;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, n));
+    igraph_vector_fill(res, inf);
+    pr = VECTOR(*res);
+
+    for (; pp < A->cs->p + n; pp++, pr++) {
+        for (; pi < A->cs->i + * (pp + 1); pi++, px++) {
+            if (*px < *pr) {
+                *pr = *px;
+            }
+        }
+    }
+    return 0;
+}
+
+int igraph_sparsemat_colmins(igraph_sparsemat_t *A,
+                             igraph_vector_t *res) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        return igraph_i_sparsemat_colmins_triplet(A, res);
+    } else {
+        return igraph_i_sparsemat_colmins_cc(A, res);
+    }
+}
+
+int igraph_i_sparsemat_colmaxs_triplet(const igraph_sparsemat_t *A,
+                                       igraph_vector_t *res) {
+    int i;
+    int *pp = A->cs->p;
+    double *px = A->cs->x;
+    double inf = IGRAPH_NEGINFINITY;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n));
+    igraph_vector_fill(res, inf);
+
+    for (i = 0; i < A->cs->nz; i++, pp++, px++) {
+        if (*px > VECTOR(*res)[ *pp ]) {
+            VECTOR(*res)[ *pp ] = *px;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A,
+                                  igraph_vector_t *res) {
+    int n;
+    double *px;
+    int *pp;
+    int *pi;
+    double *pr;
+    double inf = IGRAPH_NEGINFINITY;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    n = A->cs->n;
+    px = A->cs->x;
+    pp = A->cs->p;
+    pi = A->cs->i;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, n));
+    igraph_vector_fill(res, inf);
+    pr = VECTOR(*res);
+
+    for (; pp < A->cs->p + n; pp++, pr++) {
+        for (; pi < A->cs->i + * (pp + 1); pi++, px++) {
+            if (*px > *pr) {
+                *pr = *px;
+            }
+        }
+    }
+    return 0;
+}
+
+int igraph_sparsemat_colmaxs(igraph_sparsemat_t *A,
+                             igraph_vector_t *res) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        return igraph_i_sparsemat_colmaxs_triplet(A, res);
+    } else {
+        return igraph_i_sparsemat_colmaxs_cc(A, res);
+    }
+}
+
+int igraph_i_sparsemat_which_min_rows_triplet(igraph_sparsemat_t *A,
+        igraph_vector_t *res,
+        igraph_vector_int_t *pos) {
+    int i;
+    int *pi = A->cs->i;
+    int *pp = A->cs->p;
+    double *px = A->cs->x;
+    double inf = IGRAPH_INFINITY;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
+    IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->m));
+    igraph_vector_fill(res, inf);
+    igraph_vector_int_null(pos);
+
+    for (i = 0; i < A->cs->nz; i++, pi++, px++, pp++) {
+        if (*px < VECTOR(*res)[ *pi ]) {
+            VECTOR(*res)[ *pi ] = *px;
+            VECTOR(*pos)[ *pi ] = *pp;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A,
+        igraph_vector_t *res,
+        igraph_vector_int_t *pos) {
+    int n;
+    double *px;
+    int *pp;
+    int *pi;
+    double inf = IGRAPH_INFINITY;
+    int j;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    n = A->cs->n;
+    px = A->cs->x;
+    pp = A->cs->p;
+    pi = A->cs->i;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
+    IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->m));
+    igraph_vector_fill(res, inf);
+    igraph_vector_int_null(pos);
+
+    for (j = 0; pp < A->cs->p + n; pp++, j++) {
+        for (; pi < A->cs->i + * (pp + 1); pi++, px++) {
+            if (*px < VECTOR(*res)[ *pi ]) {
+                VECTOR(*res)[ *pi ] = *px;
+                VECTOR(*pos)[ *pi ] = j;
+            }
+        }
+    }
+
+    return 0;
+}
+
+int igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A,
+                                    igraph_vector_t *res,
+                                    igraph_vector_int_t *pos) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        return igraph_i_sparsemat_which_min_rows_triplet(A, res, pos);
+    } else {
+        return igraph_i_sparsemat_which_min_rows_cc(A, res, pos);
+    }
+}
+
+int igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A,
+        igraph_vector_t *res,
+        igraph_vector_int_t *pos) {
+
+    int i;
+    int *pi = A->cs->i;
+    int *pp = A->cs->p;
+    double *px = A->cs->x;
+    double inf = IGRAPH_INFINITY;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n));
+    IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->n));
+    igraph_vector_fill(res, inf);
+    igraph_vector_int_null(pos);
+
+    for (i = 0; i < A->cs->nz; i++, pi++, pp++, px++) {
+        if (*px < VECTOR(*res)[ *pp ]) {
+            VECTOR(*res)[ *pp ] = *px;
+            VECTOR(*pos)[ *pp ] = *pi;
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A,
+        igraph_vector_t *res,
+        igraph_vector_int_t *pos) {
+    int n, j, p;
+    double *px;
+    double *pr;
+    int *ppos;
+    double inf = IGRAPH_INFINITY;
+
+    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
+
+    n = A->cs->n;
+    px = A->cs->x;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, n));
+    igraph_vector_fill(res, inf);
+    pr = VECTOR(*res);
+    IGRAPH_CHECK(igraph_vector_int_resize(pos, n));
+    igraph_vector_int_null(pos);
+    ppos = VECTOR(*pos);
+
+    for (j = 0; j < A->cs->n; j++, pr++, ppos++) {
+        for (p = A->cs->p[j]; p < A->cs->p[j + 1]; p++, px++) {
+            if (*px < *pr) {
+                *pr = *px;
+                *ppos = A->cs->i[p];
+            }
+        }
+    }
+    return 0;
+}
+
+int igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A,
+                                    igraph_vector_t *res,
+                                    igraph_vector_int_t *pos) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        return igraph_i_sparsemat_which_min_cols_triplet(A, res, pos);
+    } else {
+        return igraph_i_sparsemat_which_min_cols_cc(A, res, pos);
+    }
+}
+
+int igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A,
+                                       igraph_vector_t *res) {
+    int i;
+    int *pp = A->cs->p;
+    double *px = A->cs->x;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n));
+    igraph_vector_null(res);
+
+    for (i = 0; i < A->cs->nz; i++, pp++, px++) {
+        VECTOR(*res)[ *pp ] += *px;
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A,
+                                  igraph_vector_t *res) {
+    int n = A->cs->n;
+    double *px = A->cs->x;
+    int *pp = A->cs->p;
+    int *pi = A->cs->i;
+    double *pr;
+
+    IGRAPH_CHECK(igraph_vector_resize(res, n));
+    igraph_vector_null(res);
+    pr = VECTOR(*res);
+
+    for (; pp < A->cs->p + n; pp++, pr++) {
+        for (; pi < A->cs->i + * (pp + 1); pi++, px++) {
+            *pr += *px;
+        }
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_colsums
+ * Column-wise sums
+ *
+ * \param A The input matrix, in triplet or column-compressed format.
+ * \param res An initialized vector, the result is stored here. It
+ *    will be resized as needed.
+ * \return Error code.
+ *
+ * Time complexity: O(nz) for triplet matrices, O(nz+n) for
+ * column-compressed ones, nz is the number of non-zero elements, n is
+ * the number of columns.
+ */
+
+int igraph_sparsemat_colsums(const igraph_sparsemat_t *A,
+                             igraph_vector_t *res) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        return igraph_i_sparsemat_colsums_triplet(A, res);
+    } else {
+        return igraph_i_sparsemat_colsums_cc(A, res);
+    }
+}
+
+/**
+ * \function igraph_sparsemat_scale
+ * Scale a sparse matrix
+ *
+ * Multiplies all elements of a sparse matrix, by the given scalar.
+ * \param A The input matrix.
+ * \param by The scaling factor.
+ * \return Error code.
+ *
+ * Time complexity: O(nz), the number of non-zero elements in the
+ * matrix.
+ */
+
+int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) {
+
+    double *px = A->cs->x;
+    int n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz;
+    double *stop = px + n;
+
+    for (; px < stop; px++) {
+        *px *= by;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_add_rows
+ * Add rows to a sparse matrix
+ *
+ * The current matrix elements are retained and all elements in the
+ * new rows are zero.
+ * \param A The input matrix, in triplet or column-compressed format.
+ * \param n The number of rows to add.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n) {
+    A->cs->m += n;
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_add_cols
+ * Add columns to a sparse matrix
+ *
+ * The current matrix elements are retained, and all elements in the
+ * new columns are zero.
+ * \param A The input matrix, in triplet or column-compressed format.
+ * \param n The number of columns to add.
+ * \return Error code.
+ *
+ * Time complexity: TODO.
+ */
+
+int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n) {
+    if (igraph_sparsemat_is_triplet(A)) {
+        A->cs->n += n;
+    } else {
+        int *newp = realloc(A->cs->p, sizeof(int) * (size_t) (A->cs->n + n + 1));
+        int i;
+        if (!newp) {
+            IGRAPH_ERROR("Cannot add columns to sparse matrix", IGRAPH_ENOMEM);
+        }
+        if (newp != A->cs->p) {
+            A->cs->p = newp;
+        }
+        for (i = A->cs->n + 1; i < A->cs->n + n + 1; i++) {
+            A->cs->p[i] = A->cs->p[i - 1];
+        }
+        A->cs->n += n;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_sparsemat_resize
+ * Resize a sparse matrix
+ *
+ * This function resizes a sparse matrix. The resized sparse matrix
+ * will be empty.
+ *
+ * \param A The initialized sparse matrix to resize.
+ * \param nrow The new number of rows.
+ * \param ncol The new number of columns.
+ * \param nzmax The new maximum number of elements.
+ * \return Error code.
+ *
+ * Time complexity: O(nzmax), the maximum number of non-zero elements.
+ */
+
+int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow,
+                            long int ncol, int nzmax) {
+
+    if (A->cs->nz < 0) {
+        igraph_sparsemat_t tmp;
+        IGRAPH_CHECK(igraph_sparsemat_init(&tmp, (int) nrow, (int) ncol, nzmax));
+        igraph_sparsemat_destroy(A);
+        *A = tmp;
+    } else {
+        IGRAPH_CHECK(igraph_sparsemat_realloc(A, nzmax));
+        A->cs->m = (int) nrow;
+        A->cs->n = (int) ncol;
+        A->cs->nz = 0;
+    }
+    return 0;
+}
+
+int igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A) {
+    if (A->cs->nz < 0) {
+        return A->cs->p[A->cs->n];
+    } else {
+        return A->cs->nz;
+    }
+}
+
+int igraph_sparsemat_getelements(const igraph_sparsemat_t *A,
+                                 igraph_vector_int_t *i,
+                                 igraph_vector_int_t *j,
+                                 igraph_vector_t *x) {
+    int nz = A->cs->nz;
+    if (nz < 0) {
+        nz = A->cs->p[A->cs->n];
+        IGRAPH_CHECK(igraph_vector_int_resize(i, nz));
+        IGRAPH_CHECK(igraph_vector_int_resize(j, A->cs->n + 1));
+        IGRAPH_CHECK(igraph_vector_resize(x, nz));
+        memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(int));
+        memcpy(VECTOR(*j), A->cs->p, (size_t) (A->cs->n + 1) * sizeof(int));
+        memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(igraph_real_t));
+    } else {
+        IGRAPH_CHECK(igraph_vector_int_resize(i, nz));
+        IGRAPH_CHECK(igraph_vector_int_resize(j, nz));
+        IGRAPH_CHECK(igraph_vector_resize(x, nz));
+        memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(int));
+        memcpy(VECTOR(*j), A->cs->p, (size_t) nz * sizeof(int));
+        memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(igraph_real_t));
+    }
+    return 0;
+}
+
+int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A,
+                                const igraph_vector_t *fact) {
+    int *i = A->cs->i;
+    igraph_real_t *x = A->cs->x;
+    int no_of_edges = A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz;
+    int e;
+
+    for (e = 0; e < no_of_edges; e++, x++, i++) {
+        igraph_real_t f = VECTOR(*fact)[*i];
+        (*x) *= f;
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_scale_cols_cc(igraph_sparsemat_t *A,
+                                     const igraph_vector_t *fact) {
+    int *i = A->cs->i;
+    igraph_real_t *x = A->cs->x;
+    int no_of_edges = A->cs->p[A->cs->n];
+    int e;
+    int c = 0;        /* actual column */
+
+    for (e = 0; e < no_of_edges; e++, x++, i++) {
+        igraph_real_t f;
+        while (c < A->cs->n && A->cs->p[c + 1] == e) {
+            c++;
+        }
+        f = VECTOR(*fact)[c];
+        (*x) *= f;
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t *A,
+        const igraph_vector_t *fact) {
+    int *j = A->cs->p;
+    igraph_real_t *x = A->cs->x;
+    int no_of_edges = A->cs->nz;
+    int e;
+
+    for (e = 0; e < no_of_edges; e++, x++, j++) {
+        igraph_real_t f = VECTOR(*fact)[*j];
+        (*x) *= f;
+    }
+
+    return 0;
+}
+
+int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A,
+                                const igraph_vector_t *fact) {
+    if (A->cs->nz < 0) {
+        return igraph_i_sparsemat_scale_cols_cc(A, fact);
+    } else {
+        return igraph_i_sparsemat_scale_cols_triplet(A, fact);
+    }
+}
+
+int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A,
+                                       const igraph_matrix_t *B,
+                                       igraph_matrix_t *res) {
+
+    int m = (int) igraph_sparsemat_nrow(A);
+    int n = (int) igraph_sparsemat_ncol(A);
+    int p = (int) igraph_matrix_ncol(B);
+    int i;
+
+    if (igraph_matrix_nrow(B) != n) {
+        IGRAPH_ERROR("Invalid dimensions in sparse-dense matrix product",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, m, p));
+    igraph_matrix_null(res);
+
+    for (i = 0; i < p; i++) {
+        if (!(cs_gaxpy(A->cs, &MATRIX(*B, 0, i), &MATRIX(*res, 0, i)))) {
+            IGRAPH_ERROR("Cannot perform sparse-dense matrix multiplication",
+                         IGRAPH_FAILURE);
+        }
+    }
+
+    return 0;
+}
+
+int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A,
+                                    const igraph_sparsemat_t *B,
+                                    igraph_matrix_t *res) {
+    int m = (int) igraph_matrix_nrow(A);
+    int n = (int) igraph_matrix_ncol(A);
+    int p = (int) igraph_sparsemat_ncol(B);
+    int r, c;
+    int *Bp = B->cs->p;
+
+    if (igraph_sparsemat_nrow(B) != n) {
+        IGRAPH_ERROR("Invalid dimensions in dense-sparse matrix product",
+                     IGRAPH_EINVAL);
+    }
+
+    if (!igraph_sparsemat_is_cc(B)) {
+        IGRAPH_ERROR("Dense-sparse product is only implemented for "
+                     "column-compressed sparse matrices", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, m, p));
+    igraph_matrix_null(res);
+
+    for (c = 0; c < p; c++) {
+        for (r = 0; r < m; r++) {
+            int idx = *Bp;
+            while (idx < * (Bp + 1)) {
+                MATRIX(*res, r, c) += MATRIX(*A, r, B->cs->i[idx]) * B->cs->x[idx];
+                idx++;
+            }
+        }
+        Bp++;
+    }
+
+    return 0;
+}
+
+int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n,
+                            int *p, int *i, double *x, int nz) {
+
+    A->cs = cs_calloc(1, sizeof(cs_di));
+    A->cs->nzmax = nzmax;
+    A->cs->m = m;
+    A->cs->n = n;
+    A->cs->p = p;
+    A->cs->i = i;
+    A->cs->x = x;
+    A->cs->nz = nz;
+
+    return 0;
+}
+
+int igraph_sparsemat_sort(const igraph_sparsemat_t *A,
+                          igraph_sparsemat_t *sorted) {
+
+    igraph_sparsemat_t tmp;
+
+    IGRAPH_CHECK(igraph_sparsemat_transpose(A, &tmp, /*values=*/ 1));
+    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
+    IGRAPH_CHECK(igraph_sparsemat_transpose(&tmp, sorted, /*values=*/ 1));
+    igraph_sparsemat_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A,
+                                        igraph_vector_int_t *i,
+                                        igraph_vector_int_t *j,
+                                        igraph_vector_t *x) {
+    if (A->cs->nz < 0) {
+        igraph_sparsemat_t tmp;
+        IGRAPH_CHECK(igraph_sparsemat_sort(A, &tmp));
+        IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
+        IGRAPH_CHECK(igraph_sparsemat_getelements(&tmp, i, j, x));
+        igraph_sparsemat_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        IGRAPH_CHECK(igraph_sparsemat_getelements(A, i, j, x));
+    }
+
+    return 0;
+}
+
+int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) {
+    return A->cs->nzmax;
+}
+
+int igraph_sparsemat_neg(igraph_sparsemat_t *A) {
+    int i, nz = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz;
+    igraph_real_t *px = A->cs->x;
+
+    for (i = 0; i < nz; i++, px++) {
+        *px = - (*px);
+    }
+
+    return 0;
+}
+
+int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it,
+                                   igraph_sparsemat_t *sparsemat) {
+
+    it->mat = sparsemat;
+    igraph_sparsemat_iterator_reset(it);
+    return 0;
+}
+
+int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) {
+    it->pos = 0;
+    if (!igraph_sparsemat_is_triplet(it->mat)) {
+        it->col = 0;
+        while (it->col < it->mat->cs->n &&
+               it->mat->cs->p[it->col + 1] == it->pos) {
+            it->col ++;
+        }
+    }
+    return 0;
+}
+
+igraph_bool_t
+igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it) {
+    int nz = it->mat->cs->nz == -1 ? it->mat->cs->p[it->mat->cs->n] :
+             it->mat->cs->nz;
+    return it->pos >= nz;
+}
+
+int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) {
+    return it->mat->cs->i[it->pos];
+}
+
+int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it) {
+    if (igraph_sparsemat_is_triplet(it->mat)) {
+        return it->mat->cs->p[it->pos];
+    } else {
+        return it->col;
+    }
+}
+
+igraph_real_t
+igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it) {
+    return it->mat->cs->x[it->pos];
+}
+
+int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it) {
+    it->pos += 1;
+    while (it->col < it->mat->cs->n &&
+           it->mat->cs->p[it->col + 1] == it->pos) {
+        it->col++;
+    }
+    return it->pos;
+}
+
+int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) {
+    return it->pos;
+}
diff --git a/igraph/src/spectral_properties.c b/igraph/src/spectral_properties.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/spectral_properties.c
@@ -0,0 +1,436 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_structural.h"
+#include "igraph_interface.h"
+#include "config.h"
+#include <math.h>
+
+int igraph_i_weighted_laplacian(const igraph_t *graph, igraph_matrix_t *res,
+                                igraph_sparsemat_t *sparseres,
+                                igraph_bool_t normalized,
+                                const igraph_vector_t *weights) {
+
+    igraph_eit_t edgeit;
+    int no_of_nodes = (int) igraph_vcount(graph);
+    int no_of_edges = (int) igraph_ecount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+    igraph_vector_t degree;
+    long int i;
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid edge weight vector length", IGRAPH_EINVAL);
+    }
+
+    if (res) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes));
+        igraph_matrix_null(res);
+    }
+    if (sparseres) {
+        int nz = directed ? no_of_edges + no_of_nodes :
+                 no_of_edges * 2 + no_of_nodes;
+        igraph_sparsemat_resize(sparseres, no_of_nodes, no_of_nodes, nz);
+    }
+
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &edgeit);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    if (directed) {
+
+        if (!normalized) {
+
+            while (!IGRAPH_EIT_END(edgeit)) {
+                long int edge = IGRAPH_EIT_GET(edgeit);
+                long int from = IGRAPH_FROM(graph, edge);
+                long int to  = IGRAPH_TO  (graph, edge);
+                igraph_real_t weight = VECTOR(*weights)[edge];
+                if (from != to) {
+                    if (res) {
+                        MATRIX(*res, from, to) -= weight;
+                    }
+                    if (sparseres) {
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int)to,
+                                                            -weight));
+                    }
+                    VECTOR(degree)[from] += weight;
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+
+            /* And the diagonal */
+            for (i = 0; i < no_of_nodes; i++) {
+                if (res) {
+                    MATRIX(*res, i, i) = VECTOR(degree)[i];
+                }
+                if (sparseres) {
+                    IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i,
+                                                        VECTOR(degree)[i]));
+                }
+            }
+
+        } else { /* normalized */
+
+            while (!IGRAPH_EIT_END(edgeit)) {
+                long int edge = IGRAPH_EIT_GET(edgeit);
+                long int from = IGRAPH_FROM(graph, edge);
+                long int to  = IGRAPH_TO  (graph, edge);
+                igraph_real_t weight = VECTOR(*weights)[edge];
+                if (from != to) {
+                    VECTOR(degree)[from] += weight;
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+
+            for (i = 0; i < no_of_nodes; i++) {
+                int t = VECTOR(degree)[i] > 0 ? 1 : 0;
+                if (res) {
+                    MATRIX(*res, i, i) = t;
+                }
+                if (sparseres) {
+                    IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i, t));
+                }
+            }
+
+            IGRAPH_EIT_RESET(edgeit);
+            while (!IGRAPH_EIT_END(edgeit)) {
+                long int edge = IGRAPH_EIT_GET(edgeit);
+                long int from = IGRAPH_FROM(graph, edge);
+                long int to  = IGRAPH_TO  (graph, edge);
+                igraph_real_t weight = VECTOR(*weights)[edge];
+                if (from != to) {
+                    igraph_real_t t = weight / VECTOR(degree)[from];
+                    if (res) {
+                        MATRIX(*res, from, to) -= t;
+                    }
+                    if (sparseres) {
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to,
+                                                            -t));
+                    }
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+
+        }
+
+    } else { /* undirected */
+
+        if (!normalized) {
+
+            while (!IGRAPH_EIT_END(edgeit)) {
+                long int edge = IGRAPH_EIT_GET(edgeit);
+                long int from = IGRAPH_FROM(graph, edge);
+                long int to  = IGRAPH_TO  (graph, edge);
+                igraph_real_t weight = VECTOR(*weights)[edge];
+                if (from != to) {
+                    if (res) {
+                        MATRIX(*res, from, to) -= weight;
+                        MATRIX(*res, to, from) -= weight;
+                    }
+                    if (sparseres) {
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to,
+                                                            -weight));
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) to, (int) from,
+                                                            -weight));
+                    }
+                    VECTOR(degree)[from] += weight;
+                    VECTOR(degree)[to] += weight;
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+
+            /* And the diagonal */
+            for (i = 0; i < no_of_nodes; i++) {
+                if (res) {
+                    MATRIX(*res, i, i) = VECTOR(degree)[i];
+                }
+                if (sparseres) {
+                    IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i,
+                                                        VECTOR(degree)[i]));
+                }
+            }
+
+        } else { /* normalized */
+
+            while (!IGRAPH_EIT_END(edgeit)) {
+                long int edge = IGRAPH_EIT_GET(edgeit);
+                long int from = IGRAPH_FROM(graph, edge);
+                long int to  = IGRAPH_TO  (graph, edge);
+                igraph_real_t weight = VECTOR(*weights)[edge];
+                if (from != to) {
+                    VECTOR(degree)[from] += weight;
+                    VECTOR(degree)[to] += weight;
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+
+            for (i = 0; i < no_of_nodes; i++) {
+                int t = VECTOR(degree)[i] > 0 ? 1 : 0;
+                if (res) {
+                    MATRIX(*res, i, i) = t;
+                }
+                if (sparseres) {
+                    IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) i, (int) i, t));
+                }
+                VECTOR(degree)[i] = sqrt(VECTOR(degree)[i]);
+            }
+
+            IGRAPH_EIT_RESET(edgeit);
+            while (!IGRAPH_EIT_END(edgeit)) {
+                long int edge = IGRAPH_EIT_GET(edgeit);
+                long int from = IGRAPH_FROM(graph, edge);
+                long int to  = IGRAPH_TO  (graph, edge);
+                igraph_real_t weight = VECTOR(*weights)[edge];
+                if (from != to) {
+                    double diff = weight / (VECTOR(degree)[from] * VECTOR(degree)[to]);
+                    if (res) {
+                        MATRIX(*res, from, to) -= diff;
+                        MATRIX(*res, to, from) -= diff;
+                    }
+                    if (sparseres) {
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) from, (int) to,
+                                                            -diff));
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, (int) to, (int) from,
+                                                            -diff));
+                    }
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+
+        }
+
+    }
+
+    igraph_vector_destroy(&degree);
+    igraph_eit_destroy(&edgeit);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_laplacian
+ * \brief Returns the Laplacian matrix of a graph
+ *
+ * </para><para>
+ * The graph Laplacian matrix is similar to an adjacency matrix but
+ * contains -1's instead of 1's and the vertex degrees are included in
+ * the diagonal. So the result for edge i--j is -1 if i!=j and is equal
+ * to the degree of vertex i if i==j. igraph_laplacian will work on a
+ * directed graph; in this case, the diagonal will contain the out-degrees.
+ * Loop edges will be ignored.
+ *
+ * </para><para>
+ * The normalized version of the Laplacian matrix has 1 in the diagonal and
+ * -1/sqrt(d[i]d[j]) if there is an edge from i to j.
+ *
+ * </para><para>
+ * The first version of this function was written by Vincent Matossian.
+ * \param graph Pointer to the graph to convert.
+ * \param res Pointer to an initialized matrix object, the result is
+ *        stored here. It will be resized if needed.
+ *        If it is a null pointer, then it is ignored.
+ *        At least one of \p res and \p sparseres must be a non-null pointer.
+ * \param sparseres Pointer to an initialized sparse matrix object, the
+ *        result is stored here, if it is not a null pointer.
+ *        At least one of \p res and \p sparseres must be a non-null pointer.
+ * \param normalized Whether to create a normalized Laplacian matrix.
+ * \param weights An optional vector containing edge weights, to calculate
+ *        the weighted Laplacian matrix. Set it to a null pointer to
+ *        calculate the unweighted Laplacian.
+ * \return Error code.
+ *
+ * Time complexity: O(|V||V|),
+ * |V| is the
+ * number of vertices in the graph.
+ *
+ * \example examples/simple/igraph_laplacian.c
+ */
+
+int igraph_laplacian(const igraph_t *graph, igraph_matrix_t *res,
+                     igraph_sparsemat_t *sparseres,
+                     igraph_bool_t normalized,
+                     const igraph_vector_t *weights) {
+
+    igraph_eit_t edgeit;
+    int no_of_nodes = (int) igraph_vcount(graph);
+    int no_of_edges = (int) igraph_ecount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+    int from, to;
+    igraph_integer_t ffrom, fto;
+    igraph_vector_t degree;
+    int i;
+
+    if (!res && !sparseres) {
+        IGRAPH_ERROR("Laplacian: give at least one of `res' or `sparseres'",
+                     IGRAPH_EINVAL);
+    }
+
+    if (weights) {
+        return igraph_i_weighted_laplacian(graph, res, sparseres, normalized,
+                                           weights);
+    }
+
+    if (res) {
+        IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes));
+        igraph_matrix_null(res);
+    }
+    if (sparseres) {
+        int nz = directed ? no_of_edges + no_of_nodes :
+                 no_of_edges * 2 + no_of_nodes;
+        IGRAPH_CHECK(igraph_sparsemat_resize(sparseres, no_of_nodes,
+                                             no_of_nodes, nz));
+    }
+    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &edgeit);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(),
+                               IGRAPH_OUT, IGRAPH_NO_LOOPS));
+
+    if (directed) {
+        if (!normalized) {
+            for (i = 0; i < no_of_nodes; i++) {
+                if (res) {
+                    MATRIX(*res, i, i) = VECTOR(degree)[i];
+                }
+                if (sparseres) {
+                    IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i,
+                                                        VECTOR(degree)[i]));
+                }
+            }
+            while (!IGRAPH_EIT_END(edgeit)) {
+                igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto);
+                from = ffrom;
+                to = fto;
+                if (from != to) {
+                    if (res) {
+                        MATRIX(*res, from, to) -= 1;
+                    }
+                    if (sparseres) {
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -1.0));
+                    }
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+        } else {
+            for (i = 0; i < no_of_nodes; i++) {
+                int t = VECTOR(degree)[i] > 0 ? 1 : 0;
+                if (res) {
+                    MATRIX(*res, i, i) = t;
+                }
+                if (sparseres) {
+                    IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, t));
+                }
+                if (VECTOR(degree)[i] > 0) {
+                    VECTOR(degree)[i] = 1.0 / VECTOR(degree)[i];
+                }
+            }
+
+            while (!IGRAPH_EIT_END(edgeit)) {
+                igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto);
+                from = ffrom; to = fto;
+                if (from != to) {
+                    if (res) {
+                        MATRIX(*res, from, to) -= VECTOR(degree)[from];
+                    }
+                    if (sparseres) {
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to,
+                                                            -VECTOR(degree)[from]));
+                    }
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+        }
+
+    } else {
+
+        if (!normalized) {
+            for (i = 0; i < no_of_nodes; i++) {
+                if (res) {
+                    MATRIX(*res, i, i) = VECTOR(degree)[i];
+                }
+                if (sparseres) {
+                    IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i,
+                                                        VECTOR(degree)[i]));
+                }
+            }
+
+            while (!IGRAPH_EIT_END(edgeit)) {
+                igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto);
+                from = ffrom;
+                to = fto;
+
+                if (from != to) {
+                    if (res) {
+                        MATRIX(*res, to, from) -= 1;
+                        MATRIX(*res, from, to) -= 1;
+                    }
+                    if (sparseres) {
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -1.0));
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -1.0));
+                    }
+                }
+
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+        } else {
+            for (i = 0; i < no_of_nodes; i++) {
+                int t = VECTOR(degree)[i] > 0 ? 1 : 0;
+                if (res) {
+                    MATRIX(*res, i, i) = t;
+                }
+                if (sparseres) {
+                    IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, t));
+                }
+                VECTOR(degree)[i] = sqrt(VECTOR(degree)[i]);
+            }
+
+            while (!IGRAPH_EIT_END(edgeit)) {
+                igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto);
+                from = ffrom; to = fto;
+                if (from != to) {
+                    double diff = 1.0 / (VECTOR(degree)[from] * VECTOR(degree)[to]);
+                    if (res) {
+                        MATRIX(*res, from, to) -= diff;
+                        MATRIX(*res, to, from) -= diff;
+                    }
+                    if (sparseres) {
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -diff));
+                        IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -diff));
+                    }
+                }
+                IGRAPH_EIT_NEXT(edgeit);
+            }
+        }
+
+    }
+
+    igraph_vector_destroy(&degree);
+    igraph_eit_destroy(&edgeit);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
diff --git a/igraph/src/spmatrix.c b/igraph/src/spmatrix.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/spmatrix.c
@@ -0,0 +1,1053 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_spmatrix.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>     /* memcpy & co. */
+#include <stdlib.h>
+
+/**
+ * \section igraph_spmatrix_constructor_and_destructor Sparse matrix constructors
+ * and destructors.
+ */
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_init
+ * \brief Initializes a sparse matrix.
+ *
+ * </para><para>
+ * Every sparse matrix needs to be initialized before using it, this is done
+ * by calling this function. A matrix has to be destroyed if it is not
+ * needed any more, see \ref igraph_spmatrix_destroy().
+ * \param m Pointer to a not yet initialized sparse matrix object to be
+ *        initialized.
+ * \param nrow The number of rows in the matrix.
+ * \param ncol The number of columns in the matrix.
+ * \return Error code.
+ *
+ * Time complexity: operating system dependent.
+ */
+
+int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol) {
+    assert(m != NULL);
+    IGRAPH_VECTOR_INIT_FINALLY(&m->ridx, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&m->cidx, ncol + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&m->data, 0);
+    IGRAPH_FINALLY_CLEAN(3);
+    m->nrow = nrow;
+    m->ncol = ncol;
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_destroy
+ * \brief Destroys a sparse matrix object.
+ *
+ * </para><para>
+ * This function frees all the memory allocated for a sparse matrix
+ * object. The destroyed object needs to be reinitialized before using
+ * it again.
+ * \param m The matrix to destroy.
+ *
+ * Time complexity: operating system dependent.
+ */
+
+void igraph_spmatrix_destroy(igraph_spmatrix_t *m) {
+    assert(m != NULL);
+    igraph_vector_destroy(&m->ridx);
+    igraph_vector_destroy(&m->cidx);
+    igraph_vector_destroy(&m->data);
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_copy
+ * \brief Copies a sparse matrix.
+ *
+ * </para><para>
+ * Creates a sparse matrix object by copying another one.
+ * \param to Pointer to an uninitialized sparse matrix object.
+ * \param from The initialized sparse matrix object to copy.
+ * \return Error code, \c IGRAPH_ENOMEM if there
+ *   isn't enough memory to allocate the new sparse matrix.
+ *
+ * Time complexity: O(n), the number
+ * of elements in the matrix.
+ */
+
+int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from) {
+    assert(from != NULL);
+    assert(to != NULL);
+    to->nrow = from->nrow;
+    to->ncol = from->ncol;
+    IGRAPH_CHECK(igraph_vector_copy(&to->ridx, &from->ridx));
+    IGRAPH_CHECK(igraph_vector_copy(&to->cidx, &from->cidx));
+    IGRAPH_CHECK(igraph_vector_copy(&to->data, &from->data));
+    return 0;
+}
+
+/**
+ * \section igraph_spmatrix_accessing_elements Accessing elements of a sparse matrix
+ */
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_e
+ * \brief Accessing an element of a sparse matrix.
+ *
+ * Note that there are no range checks right now.
+ * \param m The matrix object.
+ * \param row The index of the row, starting with zero.
+ * \param col The index of the column, starting with zero.
+ *
+ * Time complexity: O(log n), where n is the number of nonzero elements in
+ * the requested column.
+ */
+igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m,
+                                long int row, long int col) {
+    long int start, end;
+
+    assert(m != NULL);
+    start = (long) VECTOR(m->cidx)[col];
+    end = (long) VECTOR(m->cidx)[col + 1] - 1;
+
+    if (end < start) {
+        return 0;
+    }
+    /* Elements residing in column col are between m->data[start] and
+     * m->data[end], inclusive, ordered by row index */
+    while (start < end - 1) {
+        long int mid = (start + end) / 2;
+        if (VECTOR(m->ridx)[mid] > row) {
+            end = mid;
+        } else if (VECTOR(m->ridx)[mid] < row) {
+            start = mid;
+        } else {
+            start = mid;
+            break;
+        }
+    }
+
+    if (VECTOR(m->ridx)[start] == row) {
+        return VECTOR(m->data)[start];
+    }
+    if (VECTOR(m->ridx)[start] != row && VECTOR(m->ridx)[end] == row) {
+        return VECTOR(m->data)[end];
+    }
+    return 0;
+}
+
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_set
+ * \brief Setting an element of a sparse matrix.
+ *
+ * Note that there are no range checks right now.
+ * \param m The matrix object.
+ * \param row The index of the row, starting with zero.
+ * \param col The index of the column, starting with zero.
+ * \param value The new value.
+ *
+ * Time complexity: O(log n), where n is the number of nonzero elements in
+ * the requested column.
+ */
+int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col,
+                        igraph_real_t value) {
+    long int start, end;
+
+    assert(m != NULL);
+    start = (long) VECTOR(m->cidx)[col];
+    end = (long) VECTOR(m->cidx)[col + 1] - 1;
+
+    if (end < start) {
+        /* First element in the column */
+        if (value == 0.0) {
+            return 0;
+        }
+        IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row));
+        IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value));
+        for (start = col + 1; start < m->ncol + 1; start++) {
+            VECTOR(m->cidx)[start]++;
+        }
+        return 0;
+    }
+
+    /* Elements residing in column col are between m->data[start] and
+     * m->data[end], inclusive, ordered by row index */
+    while (start < end - 1) {
+        long int mid = (start + end) / 2;
+        if (VECTOR(m->ridx)[mid] > row) {
+            end = mid;
+        } else if (VECTOR(m->ridx)[mid] < row) {
+            start = mid;
+        } else {
+            start = mid;
+            break;
+        }
+    }
+
+    if (VECTOR(m->ridx)[start] == row) {
+        /* Overwriting a value - or deleting it if it has been overwritten by zero */
+        if (value == 0) {
+            igraph_vector_remove(&m->ridx, start);
+            igraph_vector_remove(&m->data, start);
+            for (start = col + 1; start < m->ncol + 1; start++) {
+                VECTOR(m->cidx)[start]--;
+            }
+        } else {
+            VECTOR(m->data)[start] = value;
+        }
+        return 0;
+    } else if (VECTOR(m->ridx)[end] == row) {
+        /* Overwriting a value - or deleting it if it has been overwritten by zero */
+        if (value == 0) {
+            igraph_vector_remove(&m->ridx, end);
+            igraph_vector_remove(&m->data, end);
+            for (start = col + 1; start < m->ncol + 1; start++) {
+                VECTOR(m->cidx)[start]--;
+            }
+        } else {
+            VECTOR(m->data)[end] = value;
+        }
+        return 0;
+    }
+
+    /* New element has to be inserted, but only if not a zero is
+     * being written into the matrix */
+    if (value != 0.0) {
+        if (VECTOR(m->ridx)[end] < row) {
+            IGRAPH_CHECK(igraph_vector_insert(&m->ridx, end + 1, row));
+            IGRAPH_CHECK(igraph_vector_insert(&m->data, end + 1, value));
+        } else if (VECTOR(m->ridx)[start] < row) {
+            IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start + 1, row));
+            IGRAPH_CHECK(igraph_vector_insert(&m->data, start + 1, value));
+        } else {
+            IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row));
+            IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value));
+        }
+        for (start = col + 1; start < m->ncol + 1; start++) {
+            VECTOR(m->cidx)[start]++;
+        }
+    }
+    return 0;
+}
+
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_add_e
+ * \brief Adding a real value to an element of a sparse matrix.
+ *
+ * Note that there are no range checks right now. This is implemented to avoid
+ * double lookup of a given element in the matrix by using \ref igraph_spmatrix_e()
+ * and \ref igraph_spmatrix_set() consecutively.
+ *
+ * \param m The matrix object.
+ * \param row The index of the row, starting with zero.
+ * \param col The index of the column, starting with zero.
+ * \param value The value to add.
+ *
+ * Time complexity: O(log n), where n is the number of nonzero elements in
+ * the requested column.
+ */
+int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col,
+                          igraph_real_t value) {
+    long int start, end;
+
+    assert(m != NULL);
+    start = (long) VECTOR(m->cidx)[col];
+    end = (long) VECTOR(m->cidx)[col + 1] - 1;
+
+    if (end < start) {
+        /* First element in the column */
+        if (value == 0.0) {
+            return 0;
+        }
+        IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row));
+        IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value));
+        for (start = col + 1; start < m->ncol + 1; start++) {
+            VECTOR(m->cidx)[start]++;
+        }
+        return 0;
+    }
+
+    /* Elements residing in column col are between m->data[start] and
+     * m->data[end], inclusive, ordered by row index */
+    while (start < end - 1) {
+        long int mid = (start + end) / 2;
+        if (VECTOR(m->ridx)[mid] > row) {
+            end = mid;
+        } else if (VECTOR(m->ridx)[mid] < row) {
+            start = mid;
+        } else {
+            start = mid;
+            break;
+        }
+    }
+
+    if (VECTOR(m->ridx)[start] == row) {
+        /* Overwriting a value */
+        if (VECTOR(m->data)[start] == -1) {
+            igraph_vector_remove(&m->ridx, start);
+            igraph_vector_remove(&m->data, start);
+            for (start = col + 1; start < m->ncol + 1; start++) {
+                VECTOR(m->cidx)[start]--;
+            }
+        } else {
+            VECTOR(m->data)[start] += value;
+        }
+        return 0;
+    } else if (VECTOR(m->ridx)[end] == row) {
+        /* Overwriting a value */
+        if (VECTOR(m->data)[end] == -1) {
+            igraph_vector_remove(&m->ridx, end);
+            igraph_vector_remove(&m->data, end);
+            for (start = col + 1; start < m->ncol + 1; start++) {
+                VECTOR(m->cidx)[start]--;
+            }
+        } else {
+            VECTOR(m->data)[end] += value;
+        }
+        return 0;
+    }
+
+    /* New element has to be inserted, but only if not a zero is
+     * being added to a zero element of the matrix */
+    if (value != 0.0) {
+        if (VECTOR(m->ridx)[end] < row) {
+            IGRAPH_CHECK(igraph_vector_insert(&m->ridx, end + 1, row));
+            IGRAPH_CHECK(igraph_vector_insert(&m->data, end + 1, value));
+        } else if (VECTOR(m->ridx)[start] < row) {
+            IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start + 1, row));
+            IGRAPH_CHECK(igraph_vector_insert(&m->data, start + 1, value));
+        } else {
+            IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row));
+            IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value));
+        }
+        for (start = col + 1; start < m->ncol + 1; start++) {
+            VECTOR(m->cidx)[start]++;
+        }
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_add_col_values
+ * \brief Adds the values of a column to another column.
+ *
+ * \param to The index of the column to be added to
+ * \param from The index of the column to be added
+ * \return Error code.
+ */
+int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from) {
+    long int i;
+    /* TODO: I think this implementation could be speeded up if I don't use
+     * igraph_spmatrix_add_e directly -- but maybe it's not worth the fuss */
+    for (i = (long int) VECTOR(m->cidx)[from]; i < VECTOR(m->cidx)[from + 1]; i++) {
+        IGRAPH_CHECK(igraph_spmatrix_add_e(m, (long int) VECTOR(m->ridx)[i],
+                                           to, VECTOR(m->data)[i]));
+    }
+
+    return 0;
+}
+
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_resize
+ * \brief Resizes a sparse matrix.
+ *
+ * </para><para>
+ * This function resizes a sparse matrix by adding more elements to it.
+ * The matrix retains its data even after resizing it, except for the data
+ * which lies outside the new boundaries (if the new size is smaller).
+ * \param m Pointer to an already initialized sparse matrix object.
+ * \param nrow The number of rows in the resized matrix.
+ * \param ncol The number of columns in the resized matrix.
+ * \return Error code.
+ *
+ * Time complexity: O(n).
+ * n is the number of elements in the old matrix.
+ */
+
+int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol) {
+    long int i, j, ci, ei, mincol;
+    assert(m != NULL);
+    /* Iterating through the matrix data and deleting unnecessary data. */
+    /* At the same time, we create the new indices as well */
+    if (nrow < m->nrow) {
+        ei = j = 0;
+        mincol = (m->ncol < ncol) ? m->ncol : ncol;
+        for (ci = 0; ci < mincol; ci++) {
+            for (; ei < VECTOR(m->cidx)[ci + 1]; ei++) {
+                if (VECTOR(m->ridx)[ei] < nrow) {
+                    VECTOR(m->ridx)[j] = VECTOR(m->ridx)[ei];
+                    VECTOR(m->data)[j] = VECTOR(m->data)[ei];
+                    j++;
+                }
+            }
+            VECTOR(m->cidx)[ci] = j;
+        }
+        /* Contract the row index and the data vector */
+        IGRAPH_CHECK(igraph_vector_resize(&m->ridx, j));
+        IGRAPH_CHECK(igraph_vector_resize(&m->cidx, j));
+    }
+    /* Updating cidx */
+    IGRAPH_CHECK(igraph_vector_resize(&m->cidx, ncol + 1));
+    for (i = m->ncol + 1; i < ncol + 1; i++) {
+        VECTOR(m->cidx)[i] = VECTOR(m->cidx)[m->ncol];
+    }
+    m->nrow = nrow;
+    m->ncol = ncol;
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_count_nonzero
+ * \brief The number of non-zero elements in a sparse matrix.
+ *
+ * \param m Pointer to an initialized sparse matrix object.
+ * \return The size of the matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m) {
+    assert(m != NULL);
+    return igraph_vector_size(&m->data);
+}
+
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_size
+ * \brief The number of elements in a sparse matrix.
+ *
+ * \param m Pointer to an initialized sparse matrix object.
+ * \return The size of the matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_spmatrix_size(const igraph_spmatrix_t *m) {
+    assert(m != NULL);
+    return (m->nrow) * (m->ncol);
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_nrow
+ * \brief The number of rows in a sparse matrix.
+ *
+ * \param m Pointer to an initialized sparse matrix object.
+ * \return The number of rows in the matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m) {
+    assert(m != NULL);
+    return m->nrow;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_ncol
+ * \brief The number of columns in a sparse matrix.
+ *
+ * \param m Pointer to an initialized sparse matrix object.
+ * \return The number of columns in the sparse matrix.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m) {
+    assert(m != NULL);
+    return m->ncol;
+}
+
+/**
+ * \ingroup matrix
+ * \brief Copies a sparse matrix to a regular C array.
+ *
+ * </para><para>
+ * The matrix is copied columnwise, as this is the format most
+ * programs and languages use.
+ * The C array should be of sufficient size, there are (of course) no
+ * range checks done.
+ * \param m Pointer to an initialized sparse matrix object.
+ * \param to Pointer to a C array, the place to copy the data to.
+ * \return Error code.
+ *
+ * Time complexity: O(n),
+ * n is the number of
+ * elements in the matrix.
+ */
+
+int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to) {
+    long int c, dest_idx, idx;
+
+    memset(to, 0, sizeof(igraph_real_t) * (size_t) igraph_spmatrix_size(m));
+    for (c = 0, dest_idx = 0; c < m->ncol; c++, dest_idx += m->nrow) {
+        for (idx = (long int) VECTOR(m->cidx)[c]; idx < VECTOR(m->cidx)[c + 1]; idx++) {
+            to[dest_idx + (long)VECTOR(m->ridx)[idx]] = VECTOR(m->data)[idx];
+        }
+    }
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \brief Sets all element in a sparse matrix to zero.
+ *
+ * \param m Pointer to an initialized matrix object.
+ * \return Error code, always returns with success.
+ *
+ * Time complexity: O(n),
+ * n is the number of columns in the matrix
+ */
+
+int igraph_spmatrix_null(igraph_spmatrix_t *m) {
+    assert(m != NULL);
+    igraph_vector_clear(&m->data);
+    igraph_vector_clear(&m->ridx);
+    igraph_vector_null(&m->cidx);
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_add_cols
+ * \brief Adds columns to a sparse matrix.
+ * \param m The sparse matrix object.
+ * \param n The number of columns to add.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n) {
+    igraph_spmatrix_resize(m, m->nrow, m->ncol + n);
+    return 0;
+}
+
+/**
+ * \ingroup matrix
+ * \function igraph_spmatrix_add_rows
+ * \brief Adds rows to a sparse matrix.
+ * \param m The sparse matrix object.
+ * \param n The number of rows to add.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n) {
+    igraph_spmatrix_resize(m, m->nrow + n, m->ncol);
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_clear_row
+ * \brief Clears a row in the matrix (sets all of its elements to zero)
+ * \param m The matrix.
+ * \param row The index of the row to be cleared.
+ *
+ * Time complexity: O(n), the number of nonzero elements in the matrix.
+ */
+
+int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row) {
+    long int ci, ei, i, j, nremove = 0, nremove_old = 0;
+    igraph_vector_t permvec;
+
+    assert(m != NULL);
+    IGRAPH_VECTOR_INIT_FINALLY(&permvec, igraph_vector_size(&m->data));
+    for (ci = 0, i = 0, j = 1; ci < m->ncol; ci++) {
+        for (ei = (long int) VECTOR(m->cidx)[ci]; ei < VECTOR(m->cidx)[ci + 1]; ei++) {
+            if (VECTOR(m->ridx)[ei] == row) {
+                /* this element will be deleted, so all elements in cidx from the
+                 * column index of this element will have to be decreased by one */
+                nremove++;
+            } else {
+                /* this element will be kept */
+                VECTOR(permvec)[i] = j;
+                j++;
+            }
+            i++;
+        }
+        if (ci > 0) {
+            VECTOR(m->cidx)[ci] -= nremove_old;
+        }
+        nremove_old = nremove;
+    }
+    VECTOR(m->cidx)[m->ncol] -= nremove;
+    igraph_vector_permdelete(&m->ridx, &permvec, nremove);
+    igraph_vector_permdelete(&m->data, &permvec, nremove);
+    igraph_vector_destroy(&permvec);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_i_spmatrix_clear_row_fast(igraph_spmatrix_t *m, long int row) {
+    long int ei, n;
+
+    assert(m != NULL);
+    n = igraph_vector_size(&m->data);
+    for (ei = 0; ei < n; ei++) {
+        if (VECTOR(m->ridx)[ei] == row) {
+            VECTOR(m->data)[ei] = 0.0;
+        }
+    }
+    return 0;
+}
+
+int igraph_i_spmatrix_cleanup(igraph_spmatrix_t *m) {
+    long int ci, ei, i, j, nremove = 0, nremove_old = 0;
+    igraph_vector_t permvec;
+
+    assert(m != NULL);
+    IGRAPH_VECTOR_INIT_FINALLY(&permvec, igraph_vector_size(&m->data));
+    for (ci = 0, i = 0, j = 1; ci < m->ncol; ci++) {
+        for (ei = (long int) VECTOR(m->cidx)[ci]; ei < VECTOR(m->cidx)[ci + 1]; ei++) {
+            if (VECTOR(m->data)[ei] == 0.0) {
+                /* this element will be deleted, so all elements in cidx from the
+                 * column index of this element will have to be decreased by one */
+                nremove++;
+            } else {
+                /* this element will be kept */
+                VECTOR(permvec)[i] = j;
+                j++;
+            }
+            i++;
+        }
+        if (ci > 0) {
+            VECTOR(m->cidx)[ci] -= nremove_old;
+        }
+        nremove_old = nremove;
+    }
+    VECTOR(m->cidx)[m->ncol] -= nremove;
+    igraph_vector_permdelete(&m->ridx, &permvec, nremove);
+    igraph_vector_permdelete(&m->data, &permvec, nremove);
+    igraph_vector_destroy(&permvec);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_clear_col
+ * \brief Clears a column in the matrix (sets all of its elements to zero)
+ * \param m The matrix.
+ * \param col The index of the column to be cleared.
+ * \return Error code. The current implementation always succeeds.
+ *
+ * Time complexity: TODO
+ */
+
+int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col) {
+    long int i, n;
+    assert(m != NULL);
+    n = (long)VECTOR(m->cidx)[col + 1] - (long)VECTOR(m->cidx)[col];
+    if (n == 0) {
+        return 0;
+    }
+    igraph_vector_remove_section(&m->ridx, (long int) VECTOR(m->cidx)[col],
+                                 (long int) VECTOR(m->cidx)[col + 1]);
+    igraph_vector_remove_section(&m->data, (long int) VECTOR(m->cidx)[col],
+                                 (long int) VECTOR(m->cidx)[col + 1]);
+    for (i = col + 1; i <= m->ncol; i++) {
+        VECTOR(m->cidx)[i] -= n;
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_scale
+ * \brief Multiplies each element of the sparse matrix by a constant.
+ * \param m The matrix.
+ * \param by The constant.
+ *
+ * Time complexity: O(n), the number of elements in the matrix.
+ */
+
+void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by) {
+    assert(m != NULL);
+    igraph_vector_scale(&m->data, by);
+}
+
+/**
+ * \function igraph_spmatrix_colsums
+ * \brief Calculates the column sums of the matrix.
+ * \param m The matrix.
+ * \param res An initialized \c igraph_vector_t, the result will be stored here.
+ *   The vector will be resized as needed.
+ *
+ * Time complexity: O(n), the number of nonzero elements in the matrix.
+ */
+
+int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res) {
+    long int i, c;
+    assert(m != NULL);
+    IGRAPH_CHECK(igraph_vector_resize(res, m->ncol));
+    igraph_vector_null(res);
+    for (c = 0; c < m->ncol; c++) {
+        for (i = (long int) VECTOR(m->cidx)[c]; i < VECTOR(m->cidx)[c + 1]; i++) {
+            VECTOR(*res)[c] += VECTOR(m->data)[i];
+        }
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_rowsums
+ * \brief Calculates the row sums of the matrix.
+ * \param m The matrix.
+ * \param res An initialized \c igraph_vector_t, the result will be stored here.
+ *   The vector will be resized as needed.
+ *
+ * Time complexity: O(n), the number of nonzero elements in the matrix.
+ */
+
+int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res) {
+    long int i, n;
+    assert(m != NULL);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, m->nrow));
+    n = igraph_vector_size(&m->data);
+    igraph_vector_null(res);
+    for (i = 0; i < n; i++) {
+        VECTOR(*res)[(long int)VECTOR(m->ridx)[i]] += VECTOR(m->data)[i];
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_max_nonzero
+ * \brief Returns the maximum nonzero element of a matrix.
+ * If the matrix is empty, zero is returned.
+ *
+ * \param m the matrix object.
+ * \param ridx the row index of the maximum element if not \c NULL.
+ * \param cidx the column index of the maximum element if not \c NULL.
+ *
+ * Time complexity: O(n), the number of nonzero elements in the matrix.
+ */
+igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m,
+        igraph_real_t *ridx, igraph_real_t *cidx) {
+    igraph_real_t res;
+    long int i, n, maxidx;
+
+    assert(m != NULL);
+    n = igraph_vector_size(&m->data);
+    if (n == 0) {
+        return 0.0;
+    }
+
+    maxidx = -1;
+    for (i = 0; i < n; i++)
+        if (VECTOR(m->data)[i] != 0.0 &&
+            (maxidx == -1 || VECTOR(m->data)[i] >= VECTOR(m->data)[maxidx])) {
+            maxidx = i;
+        }
+
+    if (maxidx == -1) {
+        return 0.0;
+    }
+
+    res = VECTOR(m->data)[maxidx];
+    if (ridx != 0) {
+        *ridx = VECTOR(m->ridx)[maxidx];
+    }
+    if (cidx != 0) {
+        igraph_vector_binsearch(&m->cidx, maxidx, &i);
+        while (VECTOR(m->cidx)[i + 1] == VECTOR(m->cidx)[i]) {
+            i++;
+        }
+        *cidx = (igraph_real_t)i;
+    }
+    return res;
+}
+
+/**
+ * \function igraph_spmatrix_max
+ * \brief Returns the maximum element of a matrix.
+ * If the matrix is empty, zero is returned.
+ *
+ * \param m the matrix object.
+ * \param ridx the row index of the maximum element if not \c NULL.
+ * \param cidx the column index of the maximum element if not \c NULL.
+ *
+ * Time complexity: O(n), the number of nonzero elements in the matrix.
+ */
+igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m,
+                                  igraph_real_t *ridx, igraph_real_t *cidx) {
+    igraph_real_t res;
+    long int i, j, k, maxidx;
+
+    assert(m != NULL);
+    i = igraph_vector_size(&m->data);
+    if (i == 0) {
+        return 0.0;
+    }
+
+    maxidx = (long)igraph_vector_which_max(&m->data);
+    res = VECTOR(m->data)[maxidx];
+    if (res >= 0.0 || i == m->nrow * m->ncol) {
+        if (ridx != 0) {
+            *ridx = VECTOR(m->ridx)[maxidx];
+        }
+        if (cidx != 0) {
+            igraph_vector_binsearch(&m->cidx, maxidx, &i);
+            i--;
+            while (i < m->ncol - 1 && VECTOR(m->cidx)[i + 1] == VECTOR(m->cidx)[i]) {
+                i++;
+            }
+            *cidx = (igraph_real_t)i;
+        }
+        return res;
+    }
+    /* the maximal nonzero element is negative and there is at least a
+     * single zero
+     */
+    res = 0.0;
+    if (cidx != 0 || ridx != 0) {
+        for (i = 0; i < m->ncol; i++) {
+            if (VECTOR(m->cidx)[i + 1] - VECTOR(m->cidx)[i] < m->nrow) {
+                if (cidx != 0) {
+                    *cidx = i;
+                }
+                if (ridx != 0) {
+                    for (j = (long int) VECTOR(m->cidx)[i], k = 0;
+                         j < VECTOR(m->cidx)[i + 1]; j++, k++) {
+                        if (VECTOR(m->ridx)[j] != k) {
+                            *ridx = k;
+                            break;
+                        }
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    return res;
+}
+
+int igraph_i_spmatrix_get_col_nonzero_indices(const igraph_spmatrix_t *m,
+        igraph_vector_t *res, long int col) {
+    long int i, n;
+    assert(m != NULL);
+    n = (long int) (VECTOR(m->cidx)[col + 1] - VECTOR(m->cidx)[col]);
+    IGRAPH_CHECK(igraph_vector_resize(res, n));
+    for (i = (long int) VECTOR(m->cidx)[col], n = 0;
+         i < VECTOR(m->cidx)[col + 1]; i++, n++)
+        if (VECTOR(m->data)[i] != 0.0) {
+            VECTOR(*res)[n] = VECTOR(m->ridx)[i];
+        }
+    return 0;
+}
+
+
+/**
+ * \section igraph_spmatrix_iterating Iterating over the non-zero elements of a sparse matrix
+ *
+ * <para>The \type igraph_spmatrix_iter_t type represents an iterator that can
+ * be used to step over the non-zero elements of a sparse matrix in columnwise
+ * order efficiently. In general, you shouldn't modify the elements of the matrix
+ * while iterating over it; doing so will probably invalidate the iterator, but
+ * there are no checks to prevent you from doing this.</para>
+ *
+ * <para>To access the row index of the current element of the iterator, use its
+ * \c ri field. Similarly, the \c ci field stores the column index of the current
+ * element and the \c value field stores the value of the element.</para>
+ */
+
+/**
+ * \function igraph_spmatrix_iter_create
+ * \brief Creates a sparse matrix iterator corresponding to the given matrix.
+ *
+ * \param  mit  pointer to the matrix iterator being initialized
+ * \param  m    pointer to the matrix we will be iterating over
+ * \return  Error code. The current implementation is always successful.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m) {
+    mit->m = m;
+    IGRAPH_CHECK(igraph_spmatrix_iter_reset(mit));
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_iter_reset
+ * \brief Resets a sparse matrix iterator.
+ *
+ * </para><para>
+ * After resetting, the iterator will point to the first nonzero element (if any).
+ *
+ * \param  mit  pointer to the matrix iterator being reset
+ * \return  Error code. The current implementation is always successful.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit) {
+    assert(mit->m);
+
+    if (igraph_spmatrix_count_nonzero(mit->m) == 0) {
+        mit->pos = mit->ri = mit->ci = -1L;
+        mit->value = -1;
+        return 0;
+    }
+
+    mit->ci = 0;
+    mit->pos = -1;
+
+    IGRAPH_CHECK(igraph_spmatrix_iter_next(mit));
+
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_iter_next
+ * \brief Moves a sparse matrix iterator to the next nonzero element.
+ *
+ * </para><para>
+ * You should call this function only if \ref igraph_spmatrix_iter_end()
+ * returns FALSE (0).
+ *
+ * \param  mit  pointer to the matrix iterator being moved
+ * \return  Error code. The current implementation is always successful.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit) {
+    mit->pos++;
+
+    if (igraph_spmatrix_iter_end(mit)) {
+        return 0;
+    }
+
+    mit->ri = (long int)VECTOR(mit->m->ridx)[mit->pos];
+    mit->value = VECTOR(mit->m->data)[mit->pos];
+
+    while (VECTOR(mit->m->cidx)[mit->ci + 1] <= mit->pos) {
+        mit->ci++;
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_spmatrix_iter_end
+ * \brief Checks whether there are more elements in the iterator.
+ *
+ * </para><para>
+ * You should call this function before calling \ref igraph_spmatrix_iter_next()
+ * to make sure you have more elements in the iterator.
+ *
+ * \param  mit  pointer to the matrix iterator being checked
+ * \return   TRUE (1) if there are more elements in the iterator,
+ *           FALSE (0) otherwise.
+ *
+ * Time complexity: O(1).
+ */
+igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit) {
+    return mit->pos >= igraph_spmatrix_count_nonzero(mit->m);
+}
+
+/**
+ * \function igraph_spmatrix_iter_destroy
+ * \brief Frees the memory used by the iterator.
+ *
+ * </para><para>
+ * The current implementation does not allocate any memory upon
+ * creation, so this function does nothing. However, since there is
+ * no guarantee that future implementations will not allocate any
+ * memory in \ref igraph_spmatrix_iter_create(), you are still
+ * required to call this function whenever you are done with the
+ * iterator.
+ *
+ * \param  mit  pointer to the matrix iterator being destroyed
+ *
+ * Time complexity: O(1).
+ */
+void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit) {
+    IGRAPH_UNUSED(mit);
+    /* Nothing to do at the moment */
+}
+
+#ifndef USING_R
+/**
+ * \function igraph_spmatrix_print
+ * \brief Prints a sparse matrix.
+ *
+ * Prints a sparse matrix to the standard output. Only the non-zero entries
+ * are printed.
+ *
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of non-zero elements.
+ */
+int igraph_spmatrix_print(const igraph_spmatrix_t* matrix) {
+    return igraph_spmatrix_fprint(matrix, stdout);
+}
+#endif
+
+/**
+ * \function igraph_spmatrix_fprint
+ * \brief Prints a sparse matrix to the given file.
+ *
+ * Prints a sparse matrix to the given file. Only the non-zero entries
+ * are printed.
+ *
+ * \return Error code.
+ *
+ * Time complexity: O(n), the number of non-zero elements.
+ */
+int igraph_spmatrix_fprint(const igraph_spmatrix_t* matrix, FILE *file) {
+    igraph_spmatrix_iter_t mit;
+
+    IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, matrix));
+    IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit);
+    while (!igraph_spmatrix_iter_end(&mit)) {
+        fprintf(file, "[%ld, %ld] = %.4f\n", (long int)mit.ri,
+                (long int)mit.ci, mit.value);
+        igraph_spmatrix_iter_next(&mit);
+    }
+    igraph_spmatrix_iter_destroy(&mit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+
diff --git a/igraph/src/st-cuts.c b/igraph/src/st-cuts.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/st-cuts.c
@@ -0,0 +1,1549 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_flow.h"
+#include "igraph_flow_internal.h"
+#include "igraph_error.h"
+#include "igraph_memory.h"
+#include "igraph_constants.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_conversion.h"
+#include "igraph_constructors.h"
+#include "igraph_structural.h"
+#include "igraph_components.h"
+#include "igraph_types_internal.h"
+#include "config.h"
+#include "igraph_math.h"
+#include "igraph_dqueue.h"
+#include "igraph_visitor.h"
+#include "igraph_marked_queue.h"
+#include "igraph_stack.h"
+#include "igraph_estack.h"
+
+/*
+ * \function igraph_even_tarjan_reduction
+ * Even-Tarjan reduction of a graph
+ *
+ * \example examples/simple/even_tarjan.c
+ */
+
+int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar,
+                                 igraph_vector_t *capacity) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+
+    long int new_no_of_nodes = no_of_nodes * 2;
+    long int new_no_of_edges = no_of_nodes + no_of_edges * 2;
+
+    igraph_vector_t edges;
+    long int edgeptr = 0, capptr = 0;
+    long int i;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, new_no_of_edges * 2);
+
+    if (capacity) {
+        IGRAPH_CHECK(igraph_vector_resize(capacity, new_no_of_edges));
+    }
+
+    /* Every vertex 'i' is replaced by two vertices, i' and i'' */
+    /* id[i'] := id[i] ; id[i''] := id[i] + no_of_nodes */
+
+    /* One edge for each original vertex, for i, we add (i',i'') */
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(edges)[edgeptr++] = i;
+        VECTOR(edges)[edgeptr++] = i + no_of_nodes;
+        if (capacity) {
+            VECTOR(*capacity)[capptr++] = 1.0;
+        }
+    }
+
+    /* Two news edges for each original edge
+       (from,to) becomes (from'',to'), (to'',from') */
+    for (i = 0; i < no_of_edges; i++) {
+        long int from = IGRAPH_FROM(graph, i);
+        long int to = IGRAPH_TO(graph, i);
+        VECTOR(edges)[edgeptr++] = from + no_of_nodes;
+        VECTOR(edges)[edgeptr++] = to;
+        VECTOR(edges)[edgeptr++] = to + no_of_nodes;
+        VECTOR(edges)[edgeptr++] = from;
+        if (capacity) {
+            VECTOR(*capacity)[capptr++] = no_of_nodes; /* TODO: should be Inf */
+            VECTOR(*capacity)[capptr++] = no_of_nodes; /* TODO: should be Inf */
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graphbar, &edges, (igraph_integer_t)
+                               new_no_of_nodes, IGRAPH_DIRECTED));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_residual_graph(const igraph_t *graph,
+                            const igraph_vector_t *capacity,
+                            igraph_t *residual,
+                            igraph_vector_t *residual_capacity,
+                            const igraph_vector_t *flow,
+                            igraph_vector_t *tmp) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int i, no_new_edges = 0;
+    long int edgeptr = 0, capptr = 0;
+
+    for (i = 0; i < no_of_edges; i++) {
+        if (VECTOR(*flow)[i] < VECTOR(*capacity)[i]) {
+            no_new_edges++;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges * 2));
+    if (residual_capacity) {
+        IGRAPH_CHECK(igraph_vector_resize(residual_capacity, no_new_edges));
+    }
+
+    for (i = 0; i < no_of_edges; i++) {
+        if (VECTOR(*capacity)[i] - VECTOR(*flow)[i] > 0) {
+            long int from = IGRAPH_FROM(graph, i);
+            long int to = IGRAPH_TO(graph, i);
+            igraph_real_t c = VECTOR(*capacity)[i];
+            VECTOR(*tmp)[edgeptr++] = from;
+            VECTOR(*tmp)[edgeptr++] = to;
+            if (residual_capacity) {
+                VECTOR(*residual_capacity)[capptr++] = c;
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes,
+                               IGRAPH_DIRECTED));
+
+    return 0;
+}
+
+int igraph_residual_graph(const igraph_t *graph,
+                          const igraph_vector_t *capacity,
+                          igraph_t *residual,
+                          igraph_vector_t *residual_capacity,
+                          const igraph_vector_t *flow) {
+
+    igraph_vector_t tmp;
+    long int no_of_edges = igraph_ecount(graph);
+
+    if (igraph_vector_size(capacity) != no_of_edges) {
+        IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_size(flow) != no_of_edges) {
+        IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+
+    IGRAPH_CHECK(igraph_i_residual_graph(graph, capacity, residual,
+                                         residual_capacity, flow, &tmp));
+
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_reverse_residual_graph(const igraph_t *graph,
+                                    const igraph_vector_t *capacity,
+                                    igraph_t *residual,
+                                    const igraph_vector_t *flow,
+                                    igraph_vector_t *tmp) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int i, no_new_edges = 0;
+    long int edgeptr = 0;
+
+    for (i = 0; i < no_of_edges; i++) {
+        igraph_real_t cap = capacity ? VECTOR(*capacity)[i] : 1.0;
+        if (VECTOR(*flow)[i] > 0) {
+            no_new_edges++;
+        }
+        if (VECTOR(*flow)[i] < cap) {
+            no_new_edges++;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges * 2));
+
+    for (i = 0; i < no_of_edges; i++) {
+        long int from = IGRAPH_FROM(graph, i);
+        long int to = IGRAPH_TO(graph, i);
+        igraph_real_t cap = capacity ? VECTOR(*capacity)[i] : 1.0;
+        if (VECTOR(*flow)[i] > 0) {
+            VECTOR(*tmp)[edgeptr++] = from;
+            VECTOR(*tmp)[edgeptr++] = to;
+        }
+        if (VECTOR(*flow)[i] < cap) {
+            VECTOR(*tmp)[edgeptr++] = to;
+            VECTOR(*tmp)[edgeptr++] = from;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes,
+                               IGRAPH_DIRECTED));
+
+    return 0;
+}
+
+int igraph_reverse_residual_graph(const igraph_t *graph,
+                                  const igraph_vector_t *capacity,
+                                  igraph_t *residual,
+                                  const igraph_vector_t *flow) {
+    igraph_vector_t tmp;
+    long int no_of_edges = igraph_ecount(graph);
+
+    if (capacity && igraph_vector_size(capacity) != no_of_edges) {
+        IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_size(flow) != no_of_edges) {
+        IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL);
+    }
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+
+    IGRAPH_CHECK(igraph_i_reverse_residual_graph(graph, capacity, residual,
+                 flow, &tmp));
+
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+typedef struct igraph_i_dbucket_t {
+    igraph_vector_long_t head;
+    igraph_vector_long_t next;
+} igraph_i_dbucket_t;
+
+int igraph_i_dbucket_init(igraph_i_dbucket_t *buck, long int size) {
+    IGRAPH_CHECK(igraph_vector_long_init(&buck->head, size));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &buck->head);
+    IGRAPH_CHECK(igraph_vector_long_init(&buck->next, size));
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+void igraph_i_dbucket_destroy(igraph_i_dbucket_t *buck) {
+    igraph_vector_long_destroy(&buck->head);
+    igraph_vector_long_destroy(&buck->next);
+}
+
+int igraph_i_dbucket_insert(igraph_i_dbucket_t *buck, long int bid,
+                            long int elem) {
+    /* Note: we can do this, since elem is not in any buckets */
+    VECTOR(buck->next)[elem] = VECTOR(buck->head)[bid];
+    VECTOR(buck->head)[bid] = elem + 1;
+    return 0;
+}
+
+long int igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck,
+                                long int bid) {
+    return VECTOR(buck->head)[bid] == 0;
+}
+
+long int igraph_i_dbucket_delete(igraph_i_dbucket_t *buck, long int bid) {
+    long int elem = VECTOR(buck->head)[bid] - 1;
+    VECTOR(buck->head)[bid] = VECTOR(buck->next)[elem];
+    return elem;
+}
+
+int igraph_i_dominator_LINK(long int v, long int w,
+                            igraph_vector_long_t *ancestor) {
+    VECTOR(*ancestor)[w] = v + 1;
+    return 0;
+}
+
+/* TODO: don't always reallocate path */
+
+int igraph_i_dominator_COMPRESS(long int v,
+                                igraph_vector_long_t *ancestor,
+                                igraph_vector_long_t *label,
+                                igraph_vector_long_t *semi) {
+    igraph_stack_long_t path;
+    long int w = v;
+    long int top, pretop;
+
+    IGRAPH_CHECK(igraph_stack_long_init(&path, 10));
+    IGRAPH_FINALLY(igraph_stack_long_destroy, &path);
+
+    while (VECTOR(*ancestor)[w] != 0) {
+        IGRAPH_CHECK(igraph_stack_long_push(&path, w));
+        w = VECTOR(*ancestor)[w] - 1;
+    }
+
+    top = igraph_stack_long_pop(&path);
+    while (!igraph_stack_long_empty(&path)) {
+        pretop = igraph_stack_long_pop(&path);
+
+        if (VECTOR(*semi)[VECTOR(*label)[top]] <
+            VECTOR(*semi)[VECTOR(*label)[pretop]]) {
+            VECTOR(*label)[pretop] = VECTOR(*label)[top];
+        }
+        VECTOR(*ancestor)[pretop] = VECTOR(*ancestor)[top];
+
+        top = pretop;
+    }
+
+    igraph_stack_long_destroy(&path);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+long int igraph_i_dominator_EVAL(long int v,
+                                 igraph_vector_long_t *ancestor,
+                                 igraph_vector_long_t *label,
+                                 igraph_vector_long_t *semi) {
+    if (VECTOR(*ancestor)[v] == 0) {
+        return v;
+    } else {
+        igraph_i_dominator_COMPRESS(v, ancestor, label, semi);
+        return VECTOR(*label)[v];
+    }
+}
+
+/* TODO: implement the faster version. */
+
+/**
+ * \function igraph_dominator_tree
+ * Calculates the dominator tree of a flowgraph
+ *
+ * A flowgraph is a directed graph with a distinguished start (or
+ * root) vertex r, such that for any vertex v, there is a path from r
+ * to v. A vertex v dominates another vertex w (not equal to v), if
+ * every path from r to w contains v. Vertex v is the immediate
+ * dominator or w, v=idom(w), if v dominates w and every other
+ * dominator of w dominates v. The edges {(idom(w), w)| w is not r}
+ * form a directed tree, rooted at r, called the dominator tree of the
+ * graph. Vertex v dominates vertex w if and only if v is an ancestor
+ * of w in the dominator tree.
+ *
+ * </para><para>This function implements the Lengauer-Tarjan algorithm
+ * to construct the dominator tree of a directed graph. For details
+ * please see Thomas Lengauer, Robert Endre Tarjan: A fast algorithm
+ * for finding dominators in a flowgraph, ACM Transactions on
+ * Programming Languages and Systems (TOPLAS) I/1, 121--141, 1979.
+ *
+ * \param graph A directed graph. If it is not a flowgraph, and it
+ *        contains some vertices not reachable from the root vertex,
+ *        then these vertices will be collected in the \c leftout
+ *        vector.
+ * \param root The id of the root (or source) vertex, this will be the
+ *        root of the tree.
+ * \param dom Pointer to an initialized vector or a null pointer. If
+ *        not a null pointer, then the immediate dominator of each
+ *        vertex will be stored here. For vertices that are not
+ *        reachable from the root, \c IGRAPH_NAN is stored here. For
+ *        the root vertex itself, -1 is added.
+ * \param domtree Pointer to an uninitialized igraph_t, or NULL. If
+ *        not a null pointer, then the dominator tree is returned
+ *        here. The graph contains the vertices that are unreachable
+ *        from the root (if any), these will be isolates.
+ * \param leftout Pointer to an initialized vector object, or NULL. If
+ *        not NULL, then the ids of the vertices that are unreachable
+ *        from the root vertex (and thus not part of the dominator
+ *        tree) are stored here.
+ * \param mode Constant, must be \c IGRAPH_IN or \c IGRAPH_OUT. If it
+ *        is \c IGRAPH_IN, then all directions are considered as
+ *        opposite to the original one in the input graph.
+ * \return Error code.
+ *
+ * Time complexity: very close to O(|E|+|V|), linear in the number of
+ * edges and vertices. More precisely, it is O(|V|+|E|alpha(|E|,|V|)),
+ * where alpha(|E|,|V|) is a functional inverse of Ackermann's
+ * function.
+ *
+ * \example examples/simple/dominator_tree.c
+ */
+
+int igraph_dominator_tree(const igraph_t *graph,
+                          igraph_integer_t root,
+                          igraph_vector_t *dom,
+                          igraph_t *domtree,
+                          igraph_vector_t *leftout,
+                          igraph_neimode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+
+    igraph_adjlist_t succ, pred;
+    igraph_vector_t parent;
+    igraph_vector_long_t semi;    /* +1 always */
+    igraph_vector_t vertex;   /* +1 always */
+    igraph_i_dbucket_t bucket;
+    igraph_vector_long_t ancestor;
+    igraph_vector_long_t label;
+
+    igraph_neimode_t invmode = mode == IGRAPH_IN ? IGRAPH_OUT : IGRAPH_IN;
+
+    long int i;
+
+    igraph_vector_t vdom, *mydom = dom;
+
+    long int component_size = 0;
+
+    if (root < 0 || root >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid root vertex id for dominator tree",
+                     IGRAPH_EINVAL);
+    }
+
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Dominator tree of an undirected graph requested",
+                     IGRAPH_EINVAL);
+    }
+
+    if (mode == IGRAPH_ALL) {
+        IGRAPH_ERROR("Invalid neighbor mode for dominator tree",
+                     IGRAPH_EINVAL);
+    }
+
+    if (dom) {
+        IGRAPH_CHECK(igraph_vector_resize(dom, no_of_nodes));
+    } else {
+        mydom = &vdom;
+        IGRAPH_VECTOR_INIT_FINALLY(mydom, no_of_nodes);
+    }
+    igraph_vector_fill(mydom, IGRAPH_NAN);
+
+    IGRAPH_CHECK(igraph_vector_init(&parent, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_destroy, &parent);
+    IGRAPH_CHECK(igraph_vector_long_init(&semi, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &semi);
+    IGRAPH_CHECK(igraph_vector_init(&vertex, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_destroy, &vertex);
+    IGRAPH_CHECK(igraph_vector_long_init(&ancestor, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &ancestor);
+    IGRAPH_CHECK(igraph_vector_long_init_seq(&label, 0, no_of_nodes - 1));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &label);
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &succ, mode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &succ);
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &pred, invmode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &pred);
+    IGRAPH_CHECK(igraph_i_dbucket_init(&bucket, no_of_nodes));
+    IGRAPH_FINALLY(igraph_i_dbucket_destroy, &bucket);
+
+    /* DFS first, to set semi, vertex and parent, step 1 */
+
+    IGRAPH_CHECK(igraph_dfs(graph, root, mode, /*unreachable=*/ 0,
+                            /*order=*/ &vertex,
+                            /*order_out=*/ 0, /*father=*/ &parent,
+                            /*dist=*/ 0, /*in_callback=*/ 0,
+                            /*out_callback=*/ 0, /*extra=*/ 0));
+
+    for (i = 0; i < no_of_nodes; i++) {
+        if (IGRAPH_FINITE(VECTOR(vertex)[i])) {
+            long int t = (long int) VECTOR(vertex)[i];
+            VECTOR(semi)[t] = component_size + 1;
+            VECTOR(vertex)[component_size] = t + 1;
+            component_size++;
+        }
+    }
+    if (leftout) {
+        long int n = no_of_nodes - component_size;
+        long int p = 0, j;
+        IGRAPH_CHECK(igraph_vector_resize(leftout, n));
+        for (j = 0; j < no_of_nodes && p < n; j++) {
+            if (!IGRAPH_FINITE(VECTOR(parent)[j])) {
+                VECTOR(*leftout)[p++] = j;
+            }
+        }
+    }
+
+    /* We need to go over 'pred' because it should contain only the
+       edges towards the target vertex. */
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_vector_int_t *v = igraph_adjlist_get(&pred, i);
+        long int j, n = igraph_vector_int_size(v);
+        for (j = 0; j < n; ) {
+            long int v2 = (long int) VECTOR(*v)[j];
+            if (IGRAPH_FINITE(VECTOR(parent)[v2])) {
+                j++;
+            } else {
+                VECTOR(*v)[j] = VECTOR(*v)[n - 1];
+                igraph_vector_int_pop_back(v);
+                n--;
+            }
+        }
+    }
+
+    /* Now comes the main algorithm, steps 2 & 3 */
+
+    for (i = component_size - 1; i > 0; i--) {
+        long int w = (long int) VECTOR(vertex)[i] - 1;
+        igraph_vector_int_t *predw = igraph_adjlist_get(&pred, w);
+        long int j, n = igraph_vector_int_size(predw);
+        for (j = 0; j < n; j++) {
+            long int v = (long int) VECTOR(*predw)[j];
+            long int u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi);
+            if (VECTOR(semi)[u] < VECTOR(semi)[w]) {
+                VECTOR(semi)[w] = VECTOR(semi)[u];
+            }
+        }
+        igraph_i_dbucket_insert(&bucket, (long int)
+                                VECTOR(vertex)[ VECTOR(semi)[w] - 1 ] - 1, w);
+        igraph_i_dominator_LINK((long int) VECTOR(parent)[w], w, &ancestor);
+        while (!igraph_i_dbucket_empty(&bucket, (long int) VECTOR(parent)[w])) {
+            long int v = igraph_i_dbucket_delete(&bucket, (long int) VECTOR(parent)[w]);
+            long int u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi);
+            VECTOR(*mydom)[v] = VECTOR(semi)[u] < VECTOR(semi)[v] ? u :
+                                VECTOR(parent)[w];
+        }
+    }
+
+    /* Finally, step 4 */
+
+    for (i = 1; i < component_size; i++) {
+        long int w = (long int) VECTOR(vertex)[i] - 1;
+        if (VECTOR(*mydom)[w] != VECTOR(vertex)[VECTOR(semi)[w] - 1] - 1) {
+            VECTOR(*mydom)[w] = VECTOR(*mydom)[(long int)VECTOR(*mydom)[w]];
+        }
+    }
+    VECTOR(*mydom)[(long int)root] = -1;
+
+    igraph_i_dbucket_destroy(&bucket);
+    igraph_adjlist_destroy(&pred);
+    igraph_adjlist_destroy(&succ);
+    igraph_vector_long_destroy(&label);
+    igraph_vector_long_destroy(&ancestor);
+    igraph_vector_destroy(&vertex);
+    igraph_vector_long_destroy(&semi);
+    igraph_vector_destroy(&parent);
+    IGRAPH_FINALLY_CLEAN(8);
+
+    if (domtree) {
+        igraph_vector_t edges;
+        long int ptr = 0;
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, component_size * 2 - 2);
+        for (i = 0; i < no_of_nodes; i++) {
+            if (i != root && IGRAPH_FINITE(VECTOR(*mydom)[i])) {
+                if (mode == IGRAPH_OUT) {
+                    VECTOR(edges)[ptr++] = VECTOR(*mydom)[i];
+                    VECTOR(edges)[ptr++] = i;
+                } else {
+                    VECTOR(edges)[ptr++] = i;
+                    VECTOR(edges)[ptr++] = VECTOR(*mydom)[i];
+                }
+            }
+        }
+        IGRAPH_CHECK(igraph_create(domtree, &edges, (igraph_integer_t) no_of_nodes,
+                                   IGRAPH_DIRECTED));
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        IGRAPH_I_ATTRIBUTE_DESTROY(domtree);
+        IGRAPH_I_ATTRIBUTE_COPY(domtree, graph, /*graph=*/ 1, /*vertex=*/ 1,
+                                /*edge=*/ 0);
+    }
+
+    if (!dom) {
+        igraph_vector_destroy(&vdom);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+typedef struct igraph_i_all_st_cuts_minimal_dfs_data_t {
+    igraph_stack_t *stack;
+    igraph_vector_bool_t *nomark;
+    const igraph_vector_bool_t *GammaX;
+    long int root;
+    const igraph_vector_t *map;
+} igraph_i_all_st_cuts_minimal_dfs_data_t;
+
+igraph_bool_t igraph_i_all_st_cuts_minimal_dfs_incb(const igraph_t *graph,
+        igraph_integer_t vid,
+        igraph_integer_t dist,
+        void *extra) {
+
+    igraph_i_all_st_cuts_minimal_dfs_data_t *data = extra;
+    igraph_stack_t *stack = data->stack;
+    igraph_vector_bool_t *nomark = data->nomark;
+    const igraph_vector_bool_t *GammaX = data->GammaX;
+    const igraph_vector_t *map = data->map;
+    long int realvid = (long int) VECTOR(*map)[(long int)vid];
+
+    IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist);
+
+    if (VECTOR(*GammaX)[(long int)realvid]) {
+        if (!igraph_stack_empty(stack)) {
+            long int top = (long int) igraph_stack_top(stack);
+            VECTOR(*nomark)[top] = 1; /* we just found a smaller one */
+        }
+        igraph_stack_push(stack, realvid); /* TODO: error check */
+    }
+
+    return 0;
+}
+
+igraph_bool_t igraph_i_all_st_cuts_minimal_dfs_otcb(const igraph_t *graph,
+        igraph_integer_t vid,
+        igraph_integer_t dist,
+        void *extra) {
+    igraph_i_all_st_cuts_minimal_dfs_data_t *data = extra;
+    igraph_stack_t *stack = data->stack;
+    const igraph_vector_t *map = data->map;
+    long int realvid = (long int) VECTOR(*map)[(long int)vid];
+
+    IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist);
+
+    if (!igraph_stack_empty(stack) &&
+        igraph_stack_top(stack) == realvid) {
+        igraph_stack_pop(stack);
+    }
+
+    return 0;
+}
+
+int igraph_i_all_st_cuts_minimal(const igraph_t *graph,
+                                 const igraph_t *domtree,
+                                 long int root,
+                                 const igraph_marked_queue_t *X,
+                                 const igraph_vector_bool_t *GammaX,
+                                 const igraph_vector_t *invmap,
+                                 igraph_vector_t *minimal) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_stack_t stack;
+    igraph_vector_bool_t nomark;
+    igraph_i_all_st_cuts_minimal_dfs_data_t data;
+    long int i;
+
+    IGRAPH_UNUSED(X);
+
+    IGRAPH_CHECK(igraph_stack_init(&stack, 10));
+    IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+    IGRAPH_CHECK(igraph_vector_bool_init(&nomark, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &nomark);
+
+    data.stack = &stack;
+    data.nomark = &nomark;
+    data.GammaX = GammaX;
+    data.root = root;
+    data.map = invmap;
+
+    /* We mark all GammaX elements as minimal first.
+       TODO: actually, we could just use GammaX to return the minimal
+       elements. */
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(nomark)[i] = VECTOR(*GammaX)[i] == 0 ? 1 : 0;
+    }
+
+    /* We do a reverse DFS from root. If, along a path we find a GammaX
+       vertex after (=below) another GammaX vertex, we mark the higher
+       one as non-minimal. */
+
+    IGRAPH_CHECK(igraph_dfs(domtree, (igraph_integer_t) root, IGRAPH_IN,
+                            /*unreachable=*/ 0, /*order=*/ 0,
+                            /*order_out=*/ 0, /*father=*/ 0,
+                            /*dist=*/ 0, /*in_callback=*/
+                            igraph_i_all_st_cuts_minimal_dfs_incb,
+                            /*out_callback=*/
+                            igraph_i_all_st_cuts_minimal_dfs_otcb,
+                            /*extra=*/ &data));
+
+    igraph_vector_clear(minimal);
+    for (i = 0; i < no_of_nodes; i++) {
+        if (!VECTOR(nomark)[i]) {
+            IGRAPH_CHECK(igraph_vector_push_back(minimal, i));
+        }
+    }
+
+    igraph_vector_bool_destroy(&nomark);
+    igraph_stack_destroy(&stack);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_all_st_cuts_pivot(const igraph_t *graph,
+                               const igraph_marked_queue_t *S,
+                               const igraph_estack_t *T,
+                               long int source,
+                               long int target,
+                               long int *v,
+                               igraph_vector_t *Isv,
+                               void *arg) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_t Sbar;
+    igraph_vector_t Sbar_map, Sbar_invmap;
+    igraph_vector_t keep;
+    igraph_t domtree;
+    igraph_vector_t leftout;
+    long int i, nomin, n;
+    long int root;
+    igraph_vector_t M;
+    igraph_vector_bool_t GammaS;
+    igraph_vector_t Nuv;
+    igraph_vector_t Isv_min;
+    igraph_vector_t GammaS_vec;
+    long int Sbar_size;
+
+    IGRAPH_UNUSED(arg);
+
+    /* We need to create the graph induced by Sbar */
+    IGRAPH_VECTOR_INIT_FINALLY(&Sbar_map, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&Sbar_invmap, 0);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&keep, 0);
+    for (i = 0; i < no_of_nodes; i++) {
+        if (!igraph_marked_queue_iselement(S, i)) {
+            IGRAPH_CHECK(igraph_vector_push_back(&keep, i));
+        }
+    }
+    Sbar_size = igraph_vector_size(&keep);
+
+    IGRAPH_CHECK(igraph_induced_subgraph_map(graph, &Sbar,
+                 igraph_vss_vector(&keep),
+                 IGRAPH_SUBGRAPH_AUTO,
+                 /* map= */ &Sbar_map,
+                 /* invmap= */ &Sbar_invmap));
+    igraph_vector_destroy(&keep);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_FINALLY(igraph_destroy, &Sbar);
+
+    root = (long int) VECTOR(Sbar_map)[target] - 1;
+
+    /* -------------------------------------------------------------*/
+    /* Construct the dominator tree of Sbar */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&leftout, 0);
+    IGRAPH_CHECK(igraph_dominator_tree(&Sbar, (igraph_integer_t) root,
+                                       /*dom=*/ 0, &domtree,
+                                       &leftout, IGRAPH_IN));
+    IGRAPH_FINALLY(igraph_destroy, &domtree);
+
+    /* -------------------------------------------------------------*/
+    /* Identify the set M of minimal elements of Gamma(S) with respect
+       to the dominator relation. */
+
+    /* First we create GammaS */
+    /* TODO: use the adjacency list, instead of neighbors() */
+    IGRAPH_CHECK(igraph_vector_bool_init(&GammaS, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &GammaS);
+    if (igraph_marked_queue_size(S) == 0) {
+        VECTOR(GammaS)[(long int) VECTOR(Sbar_map)[source] - 1] = 1;
+    } else {
+        for (i = 0; i < no_of_nodes; i++) {
+            if (igraph_marked_queue_iselement(S, i)) {
+                igraph_vector_t neis;
+                long int j;
+                IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+                IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i,
+                                              IGRAPH_OUT));
+                n = igraph_vector_size(&neis);
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (!igraph_marked_queue_iselement(S, nei)) {
+                        VECTOR(GammaS)[nei] = 1;
+                    }
+                }
+                igraph_vector_destroy(&neis);
+                IGRAPH_FINALLY_CLEAN(1);
+            }
+        }
+    }
+
+    /* Relabel left out vertices (set K in Provan & Shier) to
+       correspond to node labelling of graph instead of SBar.
+       At the same time ensure that GammaS is a proper subset of
+       L, where L are the nodes in the dominator tree. */
+    n = igraph_vector_size(&leftout);
+    for (i = 0; i < n; i++) {
+        VECTOR(leftout)[i] = VECTOR(Sbar_invmap)[(long int)VECTOR(leftout)[i]];
+        VECTOR(GammaS)[(long int)VECTOR(leftout)[i]] = 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&M, 0);
+    if (igraph_ecount(&domtree) > 0) {
+        IGRAPH_CHECK(igraph_i_all_st_cuts_minimal(graph, &domtree, root, S,
+                     &GammaS, &Sbar_invmap, &M));
+    }
+
+    igraph_vector_clear(Isv);
+    IGRAPH_VECTOR_INIT_FINALLY(&Nuv, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&Isv_min, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&GammaS_vec, 0);
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(GammaS)[i]) {
+            IGRAPH_CHECK(igraph_vector_push_back(&GammaS_vec, i));
+        }
+    }
+
+    nomin = igraph_vector_size(&M);
+    for (i = 0; i < nomin; i++) {
+        /* -------------------------------------------------------------*/
+        /* For each v in M find the set Nu(v)=dom(Sbar, v)-K
+           Nu(v) contains all vertices that are dominated by v, for every
+           v, this is a subtree of the dominator tree, rooted at v. The
+           different subtrees are disjoint. */
+        long int min = (long int) VECTOR(Sbar_map)[(long int) VECTOR(M)[i] ] - 1;
+        long int nuvsize, isvlen, j;
+        IGRAPH_CHECK(igraph_dfs(&domtree, (igraph_integer_t) min, IGRAPH_IN,
+                                /*unreachable=*/ 0, /*order=*/ &Nuv,
+                                /*order_out=*/ 0, /*father=*/ 0, /*dist=*/ 0,
+                                /*in_callback=*/ 0, /*out_callback=*/ 0,
+                                /*extra=*/ 0));
+        /* Remove the NAN values from the end of the vector */
+        for (nuvsize = 0; nuvsize < Sbar_size; nuvsize++) {
+            igraph_real_t t = VECTOR(Nuv)[nuvsize];
+            if (IGRAPH_FINITE(t)) {
+                VECTOR(Nuv)[nuvsize] = VECTOR(Sbar_invmap)[(long int) t];
+            } else {
+                break;
+            }
+        }
+        igraph_vector_resize(&Nuv, nuvsize);
+
+        /* -------------------------------------------------------------*/
+        /* By a BFS search of <Nu(v)> determine I(S,v)-K.
+           I(S,v) contains all vertices that are in Nu(v) and that are
+           reachable from Gamma(S) via a path in Nu(v). */
+        IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ -1, /*roots=*/ &GammaS_vec,
+                                /*mode=*/ IGRAPH_OUT, /*unreachable=*/ 0,
+                                /*restricted=*/ &Nuv,
+                                /*order=*/ &Isv_min, /*rank=*/ 0,
+                                /*father=*/ 0, /*pred=*/ 0, /*succ=*/ 0,
+                                /*dist=*/ 0, /*callback=*/ 0, /*extra=*/ 0));
+        for (isvlen = 0; isvlen < no_of_nodes; isvlen++) {
+            if (!IGRAPH_FINITE(VECTOR(Isv_min)[isvlen])) {
+                break;
+            }
+        }
+        igraph_vector_resize(&Isv_min, isvlen);
+
+        /* -------------------------------------------------------------*/
+        /* For each c in M check whether Isv-K is included in Tbar. If
+           such a v is found, compute Isv={x|v[Nu(v) U K]x} and return v and
+           Isv; otherwise return Isv={}. */
+        for (j = 0; j < isvlen; j++) {
+            long int v = (long int) VECTOR(Isv_min)[j];
+            if (igraph_estack_iselement(T, v) || v == target) {
+                break;
+            }
+        }
+        /* We might have found one */
+        if (j == isvlen) {
+            *v = (long int) VECTOR(M)[i];
+            /* Calculate real Isv */
+            IGRAPH_CHECK(igraph_vector_append(&Nuv, &leftout));
+            IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ (igraph_integer_t) *v,
+                                    /*roots=*/ 0, /*mode=*/ IGRAPH_OUT,
+                                    /*unreachable=*/ 0, /*restricted=*/ &Nuv,
+                                    /*order=*/ &Isv_min, /*rank=*/ 0,
+                                    /*father=*/ 0, /*pred=*/ 0, /*succ=*/ 0,
+                                    /*dist=*/ 0, /*callback=*/ 0, /*extra=*/ 0));
+            for (isvlen = 0; isvlen < no_of_nodes; isvlen++) {
+                if (!IGRAPH_FINITE(VECTOR(Isv_min)[isvlen])) {
+                    break;
+                }
+            }
+            igraph_vector_resize(&Isv_min, isvlen);
+            igraph_vector_update(Isv, &Isv_min);
+
+            break;
+        }
+    }
+
+    igraph_vector_destroy(&GammaS_vec);
+    igraph_vector_destroy(&Isv_min);
+    igraph_vector_destroy(&Nuv);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    igraph_vector_destroy(&M);
+    igraph_vector_bool_destroy(&GammaS);
+    igraph_destroy(&domtree);
+    igraph_vector_destroy(&leftout);
+    igraph_destroy(&Sbar);
+    igraph_vector_destroy(&Sbar_map);
+    igraph_vector_destroy(&Sbar_invmap);
+    IGRAPH_FINALLY_CLEAN(7);
+
+    return 0;
+}
+
+/* TODO: This is a temporary recursive version, without proper error
+   handling */
+
+int igraph_provan_shier_list(const igraph_t *graph,
+                             igraph_marked_queue_t *S,
+                             igraph_estack_t *T,
+                             long int source,
+                             long int target,
+                             igraph_vector_ptr_t *result,
+                             igraph_provan_shier_pivot_t *pivot,
+                             void *pivot_arg) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t Isv;
+    long int v = 0;
+    long int i, n;
+
+    igraph_vector_init(&Isv, 0);
+
+    pivot(graph, S, T, source, target, &v, &Isv, pivot_arg);
+    if (igraph_vector_size(&Isv) == 0) {
+        if (igraph_marked_queue_size(S) != 0 &&
+            igraph_marked_queue_size(S) != no_of_nodes) {
+            igraph_vector_t *vec = igraph_Calloc(1, igraph_vector_t);
+            igraph_vector_init(vec, igraph_marked_queue_size(S));
+            igraph_marked_queue_as_vector(S, vec);
+            IGRAPH_CHECK(igraph_vector_ptr_push_back(result, vec));
+        }
+    } else {
+        /* Put v into T */
+        igraph_estack_push(T, v);
+
+        /* Go down left in the search tree */
+        igraph_provan_shier_list(graph, S, T, source, target,
+                                 result, pivot, pivot_arg);
+
+        /* Take out v from T */
+        igraph_estack_pop(T);
+
+        /* Add Isv to S */
+        igraph_marked_queue_start_batch(S);
+        n = igraph_vector_size(&Isv);
+        for (i = 0; i < n; i++) {
+            if (!igraph_marked_queue_iselement(S, (long int) VECTOR(Isv)[i])) {
+                igraph_marked_queue_push(S, (long int) VECTOR(Isv)[i]);
+            }
+        }
+
+        /* Go down right in the search tree */
+
+        igraph_provan_shier_list(graph, S, T, source, target,
+                                 result, pivot, pivot_arg);
+
+        /* Take out Isv from S */
+        igraph_marked_queue_pop_back_batch(S);
+    }
+
+    igraph_vector_destroy(&Isv);
+
+    return 0;
+}
+
+/**
+ * \function igraph_all_st_cuts
+ * List all edge-cuts between two vertices in a directed graph
+ *
+ * This function lists all edge-cuts between a source and a target
+ * vertex. Every cut is listed exactly once. The implemented algorithm
+ * is described in JS Provan and DR Shier: A Paradigm for listing
+ * (s,t)-cuts in graphs, Algorithmica 15, 351--372, 1996.
+ *
+ * \param graph The input graph, is must be directed.
+ * \param cuts An initialized pointer vector, the cuts are stored
+ *        here. It is a list of pointers to igraph_vector_t
+ *        objects. Each vector will contain the ids of the edges in
+ *        the cut. This argument is ignored if it is a null pointer.
+ *        To free all memory allocated for \c cuts, you need call
+ *        \ref igraph_vector_destroy() and then \ref igraph_free() on
+ *        each element, before destroying the pointer vector itself.
+ * \param partition1s An initialized pointer vector, the list of
+ *        vertex sets, generating the actual edge cuts, are stored
+ *        here. Each vector contains a set of vertex ids. If X is such
+ *        a set, then all edges going from X to the complement of X
+ *        form an (s,t) edge-cut in the graph. This argument is
+ *        ignored if it is a null pointer.
+ *        To free all memory allocated for \c partition1s, you need call
+ *        \ref igraph_vector_destroy() and then \ref igraph_free() on
+ *        each element, before destroying the pointer vector itself.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \return Error code.
+ *
+ * Time complexity: O(n(|V|+|E|)), where |V| is the number of
+ * vertices, |E| is the number of edges, and n is the number of cuts.
+ *
+ * \example examples/simple/igraph_all_st_cuts.c
+ */
+
+int igraph_all_st_cuts(const igraph_t *graph,
+                       igraph_vector_ptr_t *cuts,
+                       igraph_vector_ptr_t *partition1s,
+                       igraph_integer_t source,
+                       igraph_integer_t target) {
+
+    /* S is a special stack, in which elements are pushed in batches.
+       It is then possible to remove the whole batch in one step.
+
+       T is a stack with an is-element operation.
+       Every element is included at most once.
+    */
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_marked_queue_t S;
+    igraph_estack_t T;
+    igraph_vector_ptr_t *mypartition1s = partition1s, vpartition1s;
+    long int i, nocuts;
+
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Listing all s-t cuts only implemented for "
+                     "directed graphs", IGRAPH_UNIMPLEMENTED);
+    }
+
+    if (!partition1s) {
+        mypartition1s = &vpartition1s;
+        IGRAPH_CHECK(igraph_vector_ptr_init(mypartition1s, 0));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, mypartition1s);
+    } else {
+        igraph_vector_ptr_clear(mypartition1s);
+    }
+
+    IGRAPH_CHECK(igraph_marked_queue_init(&S, no_of_nodes));
+    IGRAPH_FINALLY(igraph_marked_queue_destroy, &S);
+    IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0));
+    IGRAPH_FINALLY(igraph_estack_destroy, &T);
+
+    if (cuts)        {
+        igraph_vector_ptr_clear(cuts);
+    }
+
+    /* We call it with S={}, T={} */
+    IGRAPH_CHECK(igraph_provan_shier_list(graph, &S, &T,
+                                          source, target, mypartition1s,
+                                          igraph_i_all_st_cuts_pivot,
+                                          /*pivot_arg=*/ 0));
+
+    nocuts = igraph_vector_ptr_size(mypartition1s);
+
+    if (cuts) {
+        igraph_vector_long_t inS;
+        IGRAPH_CHECK(igraph_vector_long_init(&inS, no_of_nodes));
+        IGRAPH_FINALLY(igraph_vector_long_destroy, &inS);
+        IGRAPH_CHECK(igraph_vector_ptr_resize(cuts, nocuts));
+        for (i = 0; i < nocuts; i++) {
+            igraph_vector_t *cut;
+            igraph_vector_t *part = VECTOR(*mypartition1s)[i];
+            long int cutsize = 0;
+            long int j, partlen = igraph_vector_size(part);
+            /* Mark elements */
+            for (j = 0; j < partlen; j++) {
+                long int v = (long int) VECTOR(*part)[j];
+                VECTOR(inS)[v] = i + 1;
+            }
+            /* Check how many edges */
+            for (j = 0; j < no_of_edges; j++) {
+                long int from = IGRAPH_FROM(graph, j);
+                long int to = IGRAPH_TO(graph, j);
+                long int pfrom = VECTOR(inS)[from];
+                long int pto = VECTOR(inS)[to];
+                if (pfrom == i + 1 && pto != i + 1) {
+                    cutsize++;
+                }
+            }
+            /* Add the edges */
+            cut = igraph_Calloc(1, igraph_vector_t);
+            if (!cut) {
+                IGRAPH_ERROR("Cannot calculate s-t cuts", IGRAPH_ENOMEM);
+            }
+            IGRAPH_VECTOR_INIT_FINALLY(cut, cutsize);
+            cutsize = 0;
+            for (j = 0; j < no_of_edges; j++) {
+                long int from = IGRAPH_FROM(graph, j);
+                long int to = IGRAPH_TO(graph, j);
+                long int pfrom = VECTOR(inS)[from];
+                long int pto = VECTOR(inS)[to];
+                if ((pfrom == i + 1 && pto != i + 1)) {
+                    VECTOR(*cut)[cutsize++] = j;
+                }
+            }
+            VECTOR(*cuts)[i] = cut;
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        igraph_vector_long_destroy(&inS);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_estack_destroy(&T);
+    igraph_marked_queue_destroy(&S);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    if (!partition1s) {
+        for (i = 0; i < nocuts; i++) {
+            igraph_vector_t *cut = VECTOR(*mypartition1s)[i];
+            igraph_vector_destroy(cut);
+            igraph_free(cut);
+            VECTOR(*mypartition1s)[i] = 0;
+        }
+        igraph_vector_ptr_destroy(mypartition1s);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/* We need to find the minimal active elements of Sbar. I.e. all
+   active Sbar elements 'v', s.t. there is no other 'w' active Sbar
+   element from which 'v' is reachable. (Not necessarily through
+   active vertices.)
+
+   We calculate the in-degree of all vertices in Sbar first. Then we
+   look at the vertices with zero in-degree. If these are active,
+   then they are minimal. If they are are not active, then we remove
+   them from the graph, and check whether they resulted in more
+   zero-indegree vertices.
+*/
+
+int igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar,
+                                    const igraph_vector_bool_t *active,
+                                    const igraph_vector_t *invmap,
+                                    igraph_vector_t *minimal) {
+
+    long int no_of_nodes = igraph_vcount(Sbar);
+    igraph_vector_t indeg;
+    long int i, minsize;
+    igraph_vector_t neis;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&indeg, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_degree(Sbar, &indeg, igraph_vss_all(),
+                               IGRAPH_IN, /*loops=*/ 1));
+
+#define ACTIVE(x) (VECTOR(*active)[(long int)VECTOR(*invmap)[(x)]])
+#define ZEROIN(x) (VECTOR(indeg)[(x)]==0)
+
+    for (i = 0; i < no_of_nodes; i++) {
+        if (!ACTIVE(i)) {
+            long int j, n;
+            IGRAPH_CHECK(igraph_neighbors(Sbar, &neis, (igraph_integer_t) i,
+                                          IGRAPH_OUT));
+            n = igraph_vector_size(&neis);
+            for (j = 0; j < n; j++) {
+                long int nei = (long int) VECTOR(neis)[j];
+                VECTOR(indeg)[nei] -= 1;
+            }
+        }
+    }
+
+    for (minsize = 0, i = 0; i < no_of_nodes; i++) {
+        if (ACTIVE(i) && ZEROIN(i)) {
+            minsize++;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(minimal, minsize));
+
+    for (minsize = 0, i = 0; i < no_of_nodes; i++) {
+        if (ACTIVE(i) && ZEROIN(i)) {
+            VECTOR(*minimal)[minsize++] = i;
+        }
+    }
+
+#undef ACTIVE
+#undef ZEROIN
+
+    igraph_vector_destroy(&indeg);
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+typedef struct igraph_i_all_st_mincuts_data_t {
+    const igraph_vector_bool_t *active;
+} igraph_i_all_st_mincuts_data_t;
+
+int igraph_i_all_st_mincuts_pivot(const igraph_t *graph,
+                                  const igraph_marked_queue_t *S,
+                                  const igraph_estack_t *T,
+                                  long int source,
+                                  long int target,
+                                  long int *v,
+                                  igraph_vector_t *Isv,
+                                  void *arg) {
+
+    igraph_i_all_st_mincuts_data_t *data = arg;
+    const igraph_vector_bool_t *active = data->active;
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, j;
+    igraph_vector_t Sbar_map, Sbar_invmap;
+    igraph_vector_t keep;
+    igraph_t Sbar;
+    igraph_vector_t M;
+    long int nomin;
+
+    IGRAPH_UNUSED(source); IGRAPH_UNUSED(target);
+
+    if (igraph_marked_queue_size(S) == no_of_nodes) {
+        igraph_vector_clear(Isv);
+        return 0;
+    }
+
+    /* Create the graph induced by Sbar */
+    IGRAPH_VECTOR_INIT_FINALLY(&Sbar_map, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&Sbar_invmap, 0);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&keep, 0);
+    for (i = 0; i < no_of_nodes; i++) {
+        if (!igraph_marked_queue_iselement(S, i)) {
+            IGRAPH_CHECK(igraph_vector_push_back(&keep, i));
+        }
+    }
+
+    /* TODO: it is not even necessary to create Sbar explicitly, we
+       just need to find the M elements efficiently. See the
+       Provan-Shier paper for details. */
+    IGRAPH_CHECK(igraph_induced_subgraph_map(graph, &Sbar,
+                 igraph_vss_vector(&keep),
+                 IGRAPH_SUBGRAPH_AUTO,
+                 /* map= */ &Sbar_map,
+                 /* invmap= */ &Sbar_invmap));
+    IGRAPH_FINALLY(igraph_destroy, &Sbar);
+
+    /* ------------------------------------------------------------- */
+    /* Identify the set M of minimal elements that are active */
+    IGRAPH_VECTOR_INIT_FINALLY(&M, 0);
+    IGRAPH_CHECK(igraph_i_all_st_mincuts_minimal(&Sbar, active,
+                 &Sbar_invmap, &M));
+
+    /* ------------------------------------------------------------- */
+    /* Now find a minimal element that is not in T */
+    igraph_vector_clear(Isv);
+    nomin = igraph_vector_size(&M);
+    for (i = 0; i < nomin; i++) {
+        long int min = (long int) VECTOR(Sbar_invmap)[ (long int) VECTOR(M)[i] ];
+        if (min != target)
+            if (!igraph_estack_iselement(T, min)) {
+                break;
+            }
+    }
+    if (i != nomin) {
+        /* OK, we found a pivot element. I(S,v) contains all elements
+           that can reach the pivot element */
+        igraph_vector_t Isv_min;
+        IGRAPH_VECTOR_INIT_FINALLY(&Isv_min, 0);
+        *v = (long int) VECTOR(Sbar_invmap)[ (long int) VECTOR(M)[i] ];
+        /* TODO: restricted == keep ? */
+        IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ (igraph_integer_t) *v,/*roots=*/ 0,
+                                /*mode=*/ IGRAPH_IN, /*unreachable=*/ 0,
+                                /*restricted=*/ &keep, /*order=*/ &Isv_min,
+                                /*rank=*/ 0, /*father=*/ 0, /*pred=*/ 0,
+                                /*succ=*/ 0, /*dist=*/ 0, /*callback=*/ 0,
+                                /*extra=*/ 0));
+        for (j = 0; j < no_of_nodes; j++) {
+            igraph_real_t u = VECTOR(Isv_min)[j];
+            if (!IGRAPH_FINITE(u)) {
+                break;
+            }
+            if (!igraph_estack_iselement(T, u)) {
+                IGRAPH_CHECK(igraph_vector_push_back(Isv, u));
+            }
+        }
+        igraph_vector_destroy(&Isv_min);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&M);
+    igraph_destroy(&Sbar);
+    igraph_vector_destroy(&keep);
+    igraph_vector_destroy(&Sbar_invmap);
+    igraph_vector_destroy(&Sbar_map);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \function igraph_all_st_mincuts
+ * All minimum s-t cuts of a directed graph
+ *
+ * This function lists all minimum edge cuts between two vertices, in a
+ * directed graph. The implemented algorithm
+ * is described in JS Provan and DR Shier: A Paradigm for listing
+ * (s,t)-cuts in graphs, Algorithmica 15, 351--372, 1996.
+ *
+ * \param graph The input graph, it must be directed.
+ * \param value Pointer to a real number, the value of the minimum cut
+ *        is stored here, unless it is a null pointer.
+ * \param cuts An initialized pointer vector, the cuts are stored
+ *        here. It is a list of pointers to igraph_vector_t
+ *        objects. Each vector will contain the ids of the edges in
+ *        the cut. This argument is ignored if it is a null pointer.
+ *        To free all memory allocated for \c cuts, you need call
+ *        \ref igraph_vector_destroy() and then \ref igraph_free() on
+ *        each element, before destroying the pointer vector itself.
+ * \param partition1s An initialized pointer vector, the list of
+ *        vertex sets, generating the actual edge cuts, are stored
+ *        here. Each vector contains a set of vertex ids. If X is such
+ *        a set, then all edges going from X to the complement of X
+ *        form an (s,t) edge-cut in the graph. This argument is
+ *        ignored if it is a null pointer.
+ * \param source The id of the source vertex.
+ * \param target The id of the target vertex.
+ * \param capacity Vector of edge capacities. If this is a null
+ *        pointer, then all edges are assumed to have capacity one.
+ * \return Error code.
+ *
+ * Time complexity: O(n(|V|+|E|))+O(F), where |V| is the number of
+ * vertices, |E| is the number of edges, and n is the number of cuts;
+ * O(F) is the time complexity of the maximum flow algorithm, see \ref
+ * igraph_maxflow().
+ *
+ * \example examples/simple/igraph_all_st_mincuts.c
+ */
+
+int igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value,
+                          igraph_vector_ptr_t *cuts,
+                          igraph_vector_ptr_t *partition1s,
+                          igraph_integer_t source,
+                          igraph_integer_t target,
+                          const igraph_vector_t *capacity) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vector_t flow;
+    igraph_t residual;
+    igraph_vector_t NtoL;
+    long int newsource, newtarget;
+    igraph_marked_queue_t S;
+    igraph_estack_t T;
+    igraph_i_all_st_mincuts_data_t pivot_data;
+    igraph_vector_bool_t VE1bool;
+    igraph_vector_t VE1;
+    long int VE1size = 0;
+    long int i, nocuts;
+    igraph_integer_t proj_nodes;
+    igraph_vector_t revmap_ptr, revmap_next;
+    igraph_vector_ptr_t closedsets;
+    igraph_vector_ptr_t *mypartition1s = partition1s, vpartition1s;
+    igraph_maxflow_stats_t stats;
+
+    /* -------------------------------------------------------------------- */
+    /* Error checks */
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_ERROR("S-t cuts can only be listed in directed graphs",
+                     IGRAPH_UNIMPLEMENTED);
+    }
+    if (source < 0 || source >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid `source' vertex", IGRAPH_EINVAL);
+    }
+    if (target < 0 || target >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid `target' vertex", IGRAPH_EINVAL);
+    }
+    if (source == target) {
+        IGRAPH_ERROR("`source' and 'target' are the same vertex", IGRAPH_EINVAL);
+    }
+
+    if (!partition1s) {
+        mypartition1s = &vpartition1s;
+        IGRAPH_CHECK(igraph_vector_ptr_init(mypartition1s, 0));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, mypartition1s);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* We need to calculate the maximum flow first */
+    IGRAPH_VECTOR_INIT_FINALLY(&flow, 0);
+    IGRAPH_CHECK(igraph_maxflow(graph, value, &flow, /*cut=*/ 0,
+                                /*partition1=*/ 0, /*partition2=*/ 0,
+                                /*source=*/ source, /*target=*/ target,
+                                capacity, &stats));
+
+    /* -------------------------------------------------------------------- */
+    /* Then we need the reverse residual graph */
+    IGRAPH_CHECK(igraph_reverse_residual_graph(graph, capacity, &residual,
+                 &flow));
+    IGRAPH_FINALLY(igraph_destroy, &residual);
+
+    /* -------------------------------------------------------------------- */
+    /* We shrink it to its strongly connected components */
+    IGRAPH_VECTOR_INIT_FINALLY(&NtoL, 0);
+    IGRAPH_CHECK(igraph_clusters(&residual, /*membership=*/ &NtoL,
+                                 /*csize=*/ 0, /*no=*/ &proj_nodes,
+                                 IGRAPH_STRONG));
+    IGRAPH_CHECK(igraph_contract_vertices(&residual, /*mapping=*/ &NtoL,
+                                          /*vertex_comb=*/ 0));
+    IGRAPH_CHECK(igraph_simplify(&residual, /*multiple=*/ 1, /*loops=*/ 1,
+                                 /*edge_comb=*/ 0));
+
+    newsource = (long int) VECTOR(NtoL)[(long int)source];
+    newtarget = (long int) VECTOR(NtoL)[(long int)target];
+
+    /* TODO: handle the newsource == newtarget case */
+
+    /* -------------------------------------------------------------------- */
+    /* Determine the active vertices in the projection */
+    IGRAPH_VECTOR_INIT_FINALLY(&VE1, 0);
+    IGRAPH_CHECK(igraph_vector_bool_init(&VE1bool, proj_nodes));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &VE1bool);
+    for (i = 0; i < no_of_edges; i++) {
+        if (VECTOR(flow)[i] > 0) {
+            long int from = IGRAPH_FROM(graph, i);
+            long int to = IGRAPH_TO(graph, i);
+            long int pfrom = (long int) VECTOR(NtoL)[from];
+            long int pto = (long int) VECTOR(NtoL)[to];
+            if (!VECTOR(VE1bool)[pfrom]) {
+                VECTOR(VE1bool)[pfrom] = 1;
+                VE1size++;
+            }
+            if (!VECTOR(VE1bool)[pto]) {
+                VECTOR(VE1bool)[pto] = 1;
+                VE1size++;
+            }
+        }
+    }
+    IGRAPH_CHECK(igraph_vector_reserve(&VE1, VE1size));
+    for (i = 0; i < proj_nodes; i++) {
+        if (VECTOR(VE1bool)[i]) {
+            igraph_vector_push_back(&VE1, i);
+        }
+    }
+
+    if (cuts)        {
+        igraph_vector_ptr_clear(cuts);
+    }
+    if (partition1s) {
+        igraph_vector_ptr_clear(partition1s);
+    }
+
+    /* -------------------------------------------------------------------- */
+    /* Everything is ready, list the cuts, using the right PIVOT
+       function  */
+    IGRAPH_CHECK(igraph_marked_queue_init(&S, no_of_nodes));
+    IGRAPH_FINALLY(igraph_marked_queue_destroy, &S);
+    IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0));
+    IGRAPH_FINALLY(igraph_estack_destroy, &T);
+
+    pivot_data.active = &VE1bool;
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&closedsets, 0));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy, &closedsets); /* TODO */
+    IGRAPH_CHECK(igraph_provan_shier_list(&residual, &S, &T,
+                                          newsource, newtarget, &closedsets,
+                                          igraph_i_all_st_mincuts_pivot,
+                                          &pivot_data));
+
+    /* Convert the closed sets in the contracted graphs to cutsets in the
+       original graph */
+    IGRAPH_VECTOR_INIT_FINALLY(&revmap_ptr, igraph_vcount(&residual));
+    IGRAPH_VECTOR_INIT_FINALLY(&revmap_next, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        long int id = (long int) VECTOR(NtoL)[i];
+        VECTOR(revmap_next)[i] = VECTOR(revmap_ptr)[id];
+        VECTOR(revmap_ptr)[id] = i + 1;
+    }
+
+    /* Create partitions in original graph */
+    nocuts = igraph_vector_ptr_size(&closedsets);
+    igraph_vector_ptr_clear(mypartition1s);
+    IGRAPH_CHECK(igraph_vector_ptr_reserve(mypartition1s, nocuts));
+    for (i = 0; i < nocuts; i++) {
+        igraph_vector_t *supercut = VECTOR(closedsets)[i];
+        long int j, supercutsize = igraph_vector_size(supercut);
+        igraph_vector_t *cut = igraph_Calloc(1, igraph_vector_t);
+        IGRAPH_VECTOR_INIT_FINALLY(cut, 0); /* TODO: better allocation */
+        for (j = 0; j < supercutsize; j++) {
+            long int vtx = (long int) VECTOR(*supercut)[j];
+            long int ovtx = (long int) VECTOR(revmap_ptr)[vtx];
+            while (ovtx != 0) {
+                ovtx--;
+                IGRAPH_CHECK(igraph_vector_push_back(cut, ovtx));
+                ovtx = (long int) VECTOR(revmap_next)[ovtx];
+            }
+        }
+        igraph_vector_ptr_push_back(mypartition1s, cut);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        igraph_vector_destroy(supercut);
+        igraph_free(supercut);
+        VECTOR(closedsets)[i] = 0;
+    }
+
+    igraph_vector_destroy(&revmap_next);
+    igraph_vector_destroy(&revmap_ptr);
+    igraph_vector_ptr_destroy(&closedsets);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    /* Create cuts in original graph */
+    if (cuts) {
+        igraph_vector_long_t memb;
+        IGRAPH_CHECK(igraph_vector_long_init(&memb, no_of_nodes));
+        IGRAPH_FINALLY(igraph_vector_long_destroy, &memb);
+        IGRAPH_CHECK(igraph_vector_ptr_resize(cuts, nocuts));
+        for (i = 0; i < nocuts; i++) {
+            igraph_vector_t *part = VECTOR(*mypartition1s)[i];
+            long int j, n = igraph_vector_size(part);
+            igraph_vector_t *v;
+            v = igraph_Calloc(1, igraph_vector_t);
+            if (!v) {
+                IGRAPH_ERROR("Cannot list minimum s-t cuts", IGRAPH_ENOMEM);
+            }
+            IGRAPH_VECTOR_INIT_FINALLY(v, 0);
+            for (j = 0; j < n; j++) {
+                long int vtx = (long int) VECTOR(*part)[j];
+                VECTOR(memb)[vtx] = i + 1;
+            }
+            for (j = 0; j < no_of_edges; j++) {
+                if (VECTOR(flow)[j] > 0) {
+                    long int from = IGRAPH_FROM(graph, j);
+                    long int to = IGRAPH_TO(graph, j);
+                    if (VECTOR(memb)[from] == i + 1 && VECTOR(memb)[to] != i + 1) {
+                        IGRAPH_CHECK(igraph_vector_push_back(v, j)); /* TODO: allocation */
+                    }
+                }
+            }
+            VECTOR(*cuts)[i] = v;
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        igraph_vector_long_destroy(&memb);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_estack_destroy(&T);
+    igraph_marked_queue_destroy(&S);
+    igraph_vector_bool_destroy(&VE1bool);
+    igraph_vector_destroy(&VE1);
+    igraph_vector_destroy(&NtoL);
+    igraph_destroy(&residual);
+    igraph_vector_destroy(&flow);
+    IGRAPH_FINALLY_CLEAN(7);
+
+    if (!partition1s) {
+        for (i = 0; i < nocuts; i++) {
+            igraph_vector_t *cut = VECTOR(*mypartition1s)[i];
+            igraph_vector_destroy(cut);
+            igraph_free(cut);
+            VECTOR(*mypartition1s)[i] = 0;
+        }
+        igraph_vector_ptr_destroy(mypartition1s);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
diff --git a/igraph/src/statusbar.c b/igraph/src/statusbar.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/statusbar.c
@@ -0,0 +1,130 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include "igraph_types.h"
+#include "igraph_statusbar.h"
+#include "igraph_error.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler = 0;
+
+/**
+ * \function igraph_status
+ * Report status from an igraph function.
+ *
+ * It calls the installed status handler function, if there is
+ * one. Otherwise it does nothing. Note that the standard way to
+ * report the status from an igraph function is the
+ * \ref IGRAPH_STATUS or \ref IGRAPH_STATUSF macro, as these
+ * take care of the termination of the calling function if the
+ * status handler returns with \c IGRAPH_INTERRUPTED.
+ * \param message The status message.
+ * \param data Additional context, with user-defined semantics.
+ *        Existing igraph functions pass a null pointer here.
+ * \return Error code. If a status handler function was called
+ *        and it did not return with \c IGRAPH_SUCCESS, then
+ *        \c IGRAPH_INTERRUPTED is returned by \c igraph_status().
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_status(const char *message, void *data) {
+    if (igraph_i_status_handler) {
+        if (igraph_i_status_handler(message, data) != IGRAPH_SUCCESS) {
+            return IGRAPH_INTERRUPTED;
+        }
+    }
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_statusf
+ * Report status, more flexible printf-like version.
+ *
+ * This is the more flexible version of \ref igraph_status(),
+ * that has a syntax similar to the \c printf standard C library function.
+ * It substitutes the values of the additional arguments into the
+ * \p message template string and calls \ref igraph_status().
+ * \param message Status message template string, the syntax is the same
+ *        as for the \c printf function.
+ * \param data Additional context, with user-defined semantics.
+ *        Existing igraph functions pass a null pointer here.
+ * \param ... The additional arguments to fill the template given in the
+ *        \p message argument.
+ * \return Error code. If a status handler function was called
+ *        and it did not return with \c IGRAPH_SUCCESS, then
+ *        \c IGRAPH_INTERRUPTED is returned by \c igraph_status().
+ */
+
+int igraph_statusf(const char *message, void *data, ...) {
+    char buffer[300];
+    va_list ap;
+    va_start(ap, data);
+    vsnprintf(buffer, sizeof(buffer) - 1, message, ap);
+    return igraph_status(buffer, data);
+}
+
+#ifndef USING_R
+
+/**
+ * \function igraph_status_handler_stderr
+ * A simple predefined status handler function.
+ *
+ * A simple status handler function, that writes the status
+ * message to the standard errror.
+ * \param message The status message.
+ * \param data Additional context, with user-defined semantics.
+ *        Existing igraph functions pass a null pointer here.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_status_handler_stderr(const char *message, void *data) {
+    IGRAPH_UNUSED(data);
+    fputs(message, stderr);
+    return 0;
+}
+#endif
+
+/**
+ * \function igraph_set_status_handler
+ * Install of uninstall a status handler function.
+ *
+ * To uninstall the currently installed status handler, call
+ * this function with a null pointer.
+ * \param new_handler The status handler function to install.
+ * \return The previously installed status handler function.
+ *
+ * Time complexity: O(1).
+ */
+
+igraph_status_handler_t *
+igraph_set_status_handler(igraph_status_handler_t new_handler) {
+    igraph_status_handler_t *previous_handler = igraph_i_status_handler;
+    igraph_i_status_handler = new_handler;
+    return previous_handler;
+}
+
diff --git a/igraph/src/structural_properties.c b/igraph/src/structural_properties.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/structural_properties.c
@@ -0,0 +1,7238 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_structural.h"
+#include "igraph_transitivity.h"
+#include "igraph_paths.h"
+#include "igraph_math.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+#include "igraph_progress.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_centrality.h"
+#include "igraph_components.h"
+#include "igraph_constructors.h"
+#include "igraph_conversion.h"
+#include "igraph_types_internal.h"
+#include "igraph_dqueue.h"
+#include "igraph_attributes.h"
+#include "igraph_neighborhood.h"
+#include "igraph_topology.h"
+#include "igraph_qsort.h"
+#include "config.h"
+#include "structural_properties_internal.h"
+
+#include <assert.h>
+#include <string.h>
+#include <limits.h>
+
+/**
+ * \section about_structural
+ *
+ * <para>These functions usually calculate some structural property
+ * of a graph, like its diameter, the degree of the nodes, etc.</para>
+ */
+
+/**
+ * \ingroup structural
+ * \function igraph_diameter
+ * \brief Calculates the diameter of a graph (longest geodesic).
+ *
+ * \param graph The graph object.
+ * \param pres Pointer to an integer, if not \c NULL then it will contain
+ *        the diameter (the actual distance).
+ * \param pfrom Pointer to an integer, if not \c NULL it will be set to the
+ *        source vertex of the diameter path.
+ * \param pto Pointer to an integer, if not \c NULL it will be set to the
+ *        target vertex of the diameter path.
+ * \param path Pointer to an initialized vector. If not \c NULL the actual
+ *        longest geodesic path will be stored here. The vector will be
+ *        resized as needed.
+ * \param directed Boolean, whether to consider directed
+ *        paths. Ignored for undirected graphs.
+ * \param unconn What to do if the graph is not connected. If
+ *        \c TRUE the longest geodesic within a component
+ *        will be returned, otherwise the number of vertices is
+ *        returned. (The rationale behind the latter is that this is
+ *        always longer than the longest possible diameter in a
+ *        graph.)
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *
+ * Time complexity: O(|V||E|), the
+ * number of vertices times the number of edges.
+ *
+ * \example examples/simple/igraph_diameter.c
+ */
+
+int igraph_diameter(const igraph_t *graph, igraph_integer_t *pres,
+                    igraph_integer_t *pfrom, igraph_integer_t *pto,
+                    igraph_vector_t *path,
+                    igraph_bool_t directed, igraph_bool_t unconn) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, j, n;
+    long int *already_added;
+    long int nodes_reached;
+    long int from = 0, to = 0;
+    long int res = 0;
+
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    igraph_vector_int_t *neis;
+    igraph_neimode_t dirmode;
+    igraph_adjlist_t allneis;
+
+    if (directed) {
+        dirmode = IGRAPH_OUT;
+    } else {
+        dirmode = IGRAPH_ALL;
+    }
+    already_added = igraph_Calloc(no_of_nodes, long int);
+    if (already_added == 0) {
+        IGRAPH_ERROR("diameter failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, already_added);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, dirmode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        nodes_reached = 1;
+        IGRAPH_CHECK(igraph_dqueue_push(&q, i));
+        IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
+        already_added[i] = i + 1;
+
+        IGRAPH_PROGRESS("Diameter: ", 100.0 * i / no_of_nodes, NULL);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            long int actdist = (long int) igraph_dqueue_pop(&q);
+            if (actdist > res) {
+                res = actdist;
+                from = i;
+                to = actnode;
+            }
+
+            neis = igraph_adjlist_get(&allneis, actnode);
+            n = igraph_vector_int_size(neis);
+            for (j = 0; j < n; j++) {
+                long int neighbor = (long int) VECTOR(*neis)[j];
+                if (already_added[neighbor] == i + 1) {
+                    continue;
+                }
+                already_added[neighbor] = i + 1;
+                nodes_reached++;
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+            }
+        } /* while !igraph_dqueue_empty */
+
+        /* not connected, return largest possible */
+        if (nodes_reached != no_of_nodes && !unconn) {
+            res = no_of_nodes;
+            from = -1;
+            to = -1;
+            break;
+        }
+    } /* for i<no_of_nodes */
+
+    IGRAPH_PROGRESS("Diameter: ", 100.0, NULL);
+
+    /* return the requested info */
+    if (pres != 0) {
+        *pres = (igraph_integer_t) res;
+    }
+    if (pfrom != 0) {
+        *pfrom = (igraph_integer_t) from;
+    }
+    if (pto != 0) {
+        *pto = (igraph_integer_t) to;
+    }
+    if (path != 0) {
+        if (res == no_of_nodes) {
+            igraph_vector_clear(path);
+        } else {
+            igraph_vector_ptr_t tmpptr;
+            igraph_vector_ptr_init(&tmpptr, 1);
+            IGRAPH_FINALLY(igraph_vector_ptr_destroy, &tmpptr);
+            VECTOR(tmpptr)[0] = path;
+            IGRAPH_CHECK(igraph_get_shortest_paths(graph, &tmpptr, 0,
+                                                   (igraph_integer_t) from,
+                                                   igraph_vss_1((igraph_integer_t)to),
+                                                   dirmode, 0, 0));
+            igraph_vector_ptr_destroy(&tmpptr);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    /* clean */
+    igraph_Free(already_added);
+    igraph_dqueue_destroy(&q);
+    igraph_adjlist_destroy(&allneis);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_average_path_length
+ * \brief Calculates the average shortest path length between all vertex pairs.
+ *
+ * \param graph The graph object.
+ * \param res Pointer to a real number, this will contain the result.
+ * \param directed Boolean, whether to consider directed
+ *        paths. Ignored for undirected graphs.
+ * \param unconn What to do if the graph is not connected. If
+ *        \c TRUE, only those vertex pairs will be included in the calculation
+ *        between which there is a path. If \c FALSE, the number of vertices is
+ *        used as the distance between vertices unreachable from each other.
+ *        The rationale behind this is that this is always longer than the longest
+ *        possible geodesic in a graph.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         data structures
+ *
+ * Time complexity: O(|V||E|), the
+ * number of vertices times the number of edges.
+ *
+ * \example examples/simple/igraph_average_path_length.c
+ */
+
+int igraph_average_path_length(const igraph_t *graph, igraph_real_t *res,
+                               igraph_bool_t directed, igraph_bool_t unconn) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, j, n;
+    long int *already_added;
+    long int nodes_reached = 0;
+    igraph_real_t normfact = 0.0;
+
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    igraph_vector_int_t *neis;
+    igraph_neimode_t dirmode;
+    igraph_adjlist_t allneis;
+
+    *res = 0;
+    if (directed) {
+        dirmode = IGRAPH_OUT;
+    } else {
+        dirmode = IGRAPH_ALL;
+    }
+    already_added = igraph_Calloc(no_of_nodes, long int);
+    if (already_added == 0) {
+        IGRAPH_ERROR("average path length failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, already_added); /* TODO: hack */
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    igraph_adjlist_init(graph, &allneis, dirmode);
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        nodes_reached = 0;
+        IGRAPH_CHECK(igraph_dqueue_push(&q, i));
+        IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
+        already_added[i] = i + 1;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            long int actdist = (long int) igraph_dqueue_pop(&q);
+
+            neis = igraph_adjlist_get(&allneis, actnode);
+            n = igraph_vector_int_size(neis);
+            for (j = 0; j < n; j++) {
+                long int neighbor = (long int) VECTOR(*neis)[j];
+                if (already_added[neighbor] == i + 1) {
+                    continue;
+                }
+                already_added[neighbor] = i + 1;
+                nodes_reached++;
+                *res += actdist + 1;
+                normfact += 1;
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+            }
+        } /* while !igraph_dqueue_empty */
+
+        /* not connected, return largest possible */
+        if (!unconn) {
+            *res += (no_of_nodes * (no_of_nodes - 1 - nodes_reached));
+            normfact += no_of_nodes - 1 - nodes_reached;
+        }
+    } /* for i<no_of_nodes */
+
+
+    if (normfact > 0) {
+        *res /= normfact;
+    } else {
+        *res = IGRAPH_NAN;
+    }
+
+    /* clean */
+    igraph_Free(already_added);
+    igraph_dqueue_destroy(&q);
+    igraph_adjlist_destroy(&allneis);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_path_length_hist
+ * Create a histogram of all shortest path lengths.
+ *
+ * This function calculates a histogram, by calculating the
+ * shortest path length between each pair of vertices. For directed
+ * graphs both directions might be considered and then every pair of vertices
+ * appears twice in the histogram.
+ * \param graph The input graph.
+ * \param res Pointer to an initialized vector, the result is stored
+ *     here. The first (i.e. zeroth) element contains the number of
+ *     shortest paths of length 1, etc. The supplied vector is resized
+ *     as needed.
+ * \param unconnected Pointer to a real number, the number of
+ *     pairs for which the second vertex is not reachable from the
+ *     first is stored here.
+ * \param directed Whether to consider directed paths in a directed
+ *     graph (if not zero). This argument is ignored for undirected
+ *     graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|V||E|), the number of vertices times the number
+ * of edges.
+ *
+ * \sa \ref igraph_average_path_length() and \ref igraph_shortest_paths()
+ */
+
+int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res,
+                            igraph_real_t *unconnected, igraph_bool_t directed) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, j, n;
+    igraph_vector_long_t already_added;
+    long int nodes_reached;
+
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    igraph_vector_int_t *neis;
+    igraph_neimode_t dirmode;
+    igraph_adjlist_t allneis;
+    igraph_real_t unconn = 0;
+    long int ressize;
+
+    if (directed) {
+        dirmode = IGRAPH_OUT;
+    } else {
+        dirmode = IGRAPH_ALL;
+    }
+
+    IGRAPH_CHECK(igraph_vector_long_init(&already_added, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &already_added);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, dirmode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, 0));
+    ressize = 0;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        nodes_reached = 1;      /* itself */
+        IGRAPH_CHECK(igraph_dqueue_push(&q, i));
+        IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
+        VECTOR(already_added)[i] = i + 1;
+
+        IGRAPH_PROGRESS("Path-hist: ", 100.0 * i / no_of_nodes, NULL);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            long int actdist = (long int) igraph_dqueue_pop(&q);
+
+            neis = igraph_adjlist_get(&allneis, actnode);
+            n = igraph_vector_int_size(neis);
+            for (j = 0; j < n; j++) {
+                long int neighbor = (long int) VECTOR(*neis)[j];
+                if (VECTOR(already_added)[neighbor] == i + 1) {
+                    continue;
+                }
+                VECTOR(already_added)[neighbor] = i + 1;
+                nodes_reached++;
+                if (actdist + 1 > ressize) {
+                    IGRAPH_CHECK(igraph_vector_resize(res, actdist + 1));
+                    for (; ressize < actdist + 1; ressize++) {
+                        VECTOR(*res)[ressize] = 0;
+                    }
+                }
+                VECTOR(*res)[actdist] += 1;
+
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+            }
+        } /* while !igraph_dqueue_empty */
+
+        unconn += (no_of_nodes - nodes_reached);
+
+    } /* for i<no_of_nodes */
+
+    IGRAPH_PROGRESS("Path-hist: ", 100.0, NULL);
+
+    /* count every pair only once for an undirected graph */
+    if (!directed || !igraph_is_directed(graph)) {
+        for (i = 0; i < ressize; i++) {
+            VECTOR(*res)[i] /= 2;
+        }
+        unconn /= 2;
+    }
+
+    igraph_vector_long_destroy(&already_added);
+    igraph_dqueue_destroy(&q);
+    igraph_adjlist_destroy(&allneis);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (unconnected) {
+        *unconnected = unconn;
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_shortest_paths
+ * \brief The length of the shortest paths between vertices.
+ *
+ * \param graph The graph object.
+ * \param res The result of the calculation, a matrix. A pointer to an
+ *        initialized matrix, to be more precise. The matrix will be
+ *        resized if needed. It will have the same
+ *        number of rows as the length of the \c from
+ *        argument, and its number of columns is the number of
+ *        vertices in the \c to argument. One row of the matrix shows the
+ *        distances from/to a given vertex to the ones in \c to.
+ *        For the unreachable vertices IGRAPH_INFINITY is returned.
+ * \param from Vector of the vertex ids for which the path length
+ *        calculations are done.
+ * \param to Vector of the vertex ids to which the path length
+ *        calculations are done. It is not allowed to have duplicated
+ *        vertex ids here.
+ * \param mode The type of shortest paths to be used for the
+ *        calculation in directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the lengths of the outgoing paths are calculated.
+ *        \cli IGRAPH_IN
+ *          the lengths of the incoming paths are calculated.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an undirected one for
+ *          the computation.
+ *        \endclist
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary
+ *           data.
+ *        \cli IGRAPH_EINVVID
+ *           invalid vertex id passed.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(n(|V|+|E|)),
+ * n is the
+ * number of vertices to calculate, |V| and
+ * |E| are the number of vertices and
+ * edges in the graph.
+ *
+ * \sa \ref igraph_get_shortest_paths() to get the paths themselves,
+ * \ref igraph_shortest_paths_dijkstra() for the weighted version.
+ */
+
+int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res,
+                          const igraph_vs_t from, const igraph_vs_t to,
+                          igraph_neimode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_from, no_of_to;
+    long int *already_counted;
+    igraph_adjlist_t adjlist;
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    igraph_vector_int_t *neis;
+    igraph_bool_t all_to;
+
+    long int i, j;
+    igraph_vit_t fromvit, tovit;
+    igraph_real_t my_infinity = IGRAPH_INFINITY;
+    igraph_vector_t indexv;
+
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &fromvit);
+    no_of_from = IGRAPH_VIT_SIZE(fromvit);
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
+
+    already_counted = igraph_Calloc(no_of_nodes, long int);
+    if (already_counted == 0) {
+        IGRAPH_ERROR("shortest paths failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, already_counted);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    if ( (all_to = igraph_vs_is_all(&to)) ) {
+        no_of_to = no_of_nodes;
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&indexv, no_of_nodes);
+        IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &tovit);
+        no_of_to = IGRAPH_VIT_SIZE(tovit);
+        for (i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit)) {
+            long int v = IGRAPH_VIT_GET(tovit);
+            if (VECTOR(indexv)[v]) {
+                IGRAPH_ERROR("Duplicate vertices in `to', this is not allowed",
+                             IGRAPH_EINVAL);
+            }
+            VECTOR(indexv)[v] = ++i;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to));
+    igraph_matrix_fill(res, my_infinity);
+
+    for (IGRAPH_VIT_RESET(fromvit), i = 0;
+         !IGRAPH_VIT_END(fromvit);
+         IGRAPH_VIT_NEXT(fromvit), i++) {
+        long int reached = 0;
+        IGRAPH_CHECK(igraph_dqueue_push(&q, IGRAPH_VIT_GET(fromvit)));
+        IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
+        already_counted[ (long int) IGRAPH_VIT_GET(fromvit) ] = i + 1;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int act = (long int) igraph_dqueue_pop(&q);
+            long int actdist = (long int) igraph_dqueue_pop(&q);
+
+            if (all_to) {
+                MATRIX(*res, i, act) = actdist;
+            } else {
+                if (VECTOR(indexv)[act]) {
+                    MATRIX(*res, i, (long int)(VECTOR(indexv)[act] - 1)) = actdist;
+                    reached++;
+                    if (reached == no_of_to) {
+                        igraph_dqueue_clear(&q);
+                        break;
+                    }
+                }
+            }
+
+            neis = igraph_adjlist_get(&adjlist, act);
+            for (j = 0; j < igraph_vector_int_size(neis); j++) {
+                long int neighbor = (long int) VECTOR(*neis)[j];
+                if (already_counted[neighbor] == i + 1) {
+                    continue;
+                }
+                already_counted[neighbor] = i + 1;
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+            }
+        }
+    }
+
+    /* Clean */
+    if (!all_to) {
+        igraph_vit_destroy(&tovit);
+        igraph_vector_destroy(&indexv);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_Free(already_counted);
+    igraph_dqueue_destroy(&q);
+    igraph_vit_destroy(&fromvit);
+    igraph_adjlist_destroy(&adjlist);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_get_shortest_paths
+ * \brief Calculates the shortest paths from/to one vertex.
+ *
+ * </para><para>
+ * If there is more than one geodesic between two vertices, this
+ * function gives only one of them.
+ * \param graph The graph object.
+ * \param vertices The result, the ids of the vertices along the paths.
+ *        This is a pointer vector, each element points to a vector
+ *        object. These should be initialized before passing them to
+ *        the function, which will properly clear and/or resize them
+ *        and fill the ids of the vertices along the geodesics from/to
+ *        the vertices. Supply a null pointer here if you don't need
+ *        these vectors.
+ * \param edges The result, the ids of the edges along the paths.
+ *        This is a pointer vector, each element points to a vector
+ *        object. These should be initialized before passing them to
+ *        the function, which will properly clear and/or resize them
+ *        and fill the ids of the vertices along the geodesics from/to
+ *        the vertices. Supply a null pointer here if you don't need
+ *        these vectors.
+ * \param from The id of the vertex from/to which the geodesics are
+ *        calculated.
+ * \param to Vertex sequence with the ids of the vertices to/from which the
+ *        shortest paths will be calculated. A vertex might be given multiple
+ *        times.
+ * \param mode The type of shortest paths to be used for the
+ *        calculation in directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing paths are calculated.
+ *        \cli IGRAPH_IN
+ *          the incoming paths are calculated.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an
+ *          undirected one for the computation.
+ *        \endclist
+ * \param predecessors A pointer to an initialized igraph vector or null.
+ *        If not null, a vector containing the predecessor of each vertex in
+ *        the single source shortest path tree is returned here. The
+ *        predecessor of vertex i in the tree is the vertex from which vertex i
+ *        was reached. The predecessor of the start vertex (in the \c from
+ *        argument) is itself by definition. If the predecessor is -1, it means
+ *        that the given vertex was not reached from the source during the
+ *        search. Note that the search terminates if all the vertices in
+ *        \c to are reached.
+ * \param inbound_edges A pointer to an initialized igraph vector or null.
+ *        If not null, a vector containing the inbound edge of each vertex in
+ *        the single source shortest path tree is returned here. The
+ *        inbound edge of vertex i in the tree is the edge via which vertex i
+ *        was reached. The start vertex and vertices that were not reached
+ *        during the search will have -1 in the corresponding entry of the
+ *        vector. Note that the search terminates if all the vertices in
+ *        \c to are reached.
+ *
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           \p from is invalid vertex id, or the length of \p to is
+ *           not the same as the length of \p res.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| is the number of vertices,
+ * |E| the number of edges in the
+ * graph.
+ *
+ * \sa \ref igraph_shortest_paths() if you only need the path length but
+ * not the paths themselves.
+ *
+ * \example examples/simple/igraph_get_shortest_paths.c
+ */
+
+
+int igraph_get_shortest_paths(const igraph_t *graph,
+                              igraph_vector_ptr_t *vertices,
+                              igraph_vector_ptr_t *edges,
+                              igraph_integer_t from, const igraph_vs_t to,
+                              igraph_neimode_t mode,
+                              igraph_vector_long_t *predecessors,
+                              igraph_vector_long_t *inbound_edges) {
+
+    /* TODO: use inclist_t if to is long (longer than 1?) */
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int *father;
+
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+
+    long int i, j;
+    igraph_vector_t tmp = IGRAPH_VECTOR_NULL;
+
+    igraph_vit_t vit;
+
+    long int to_reach;
+    long int reached = 0;
+
+    if (from < 0 || from >= no_of_nodes) {
+        IGRAPH_ERROR("cannot get shortest paths", IGRAPH_EINVVID);
+    }
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, to, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    if (vertices && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(vertices)) {
+        IGRAPH_ERROR("Size of the `vertices' and the `to' should match", IGRAPH_EINVAL);
+    }
+    if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) {
+        IGRAPH_ERROR("Size of the `edges' and the `to' should match", IGRAPH_EINVAL);
+    }
+
+    father = igraph_Calloc(no_of_nodes, long int);
+    if (father == 0) {
+        IGRAPH_ERROR("cannot get shortest paths", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, father);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    /* Mark the vertices we need to reach */
+    to_reach = IGRAPH_VIT_SIZE(vit);
+    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+        if (father[ (long int) IGRAPH_VIT_GET(vit) ] == 0) {
+            father[ (long int) IGRAPH_VIT_GET(vit) ] = -1;
+        } else {
+            to_reach--;       /* this node was given multiple times */
+        }
+    }
+
+    /* Meaning of father[i]:
+     *
+     * - If father[i] < 0, it means that vertex i has to be reached and has not
+     *   been reached yet.
+     *
+     * - If father[i] = 0, it means that vertex i does not have to be reached and
+     *   it has not been reached yet.
+     *
+     * - If father[i] = 1, it means that vertex i is the start vertex.
+     *
+     * - Otherwise, father[i] is the ID of the edge from which vertex i was
+     *   reached plus 2.
+     */
+
+    IGRAPH_CHECK(igraph_dqueue_push(&q, from + 1));
+    if (father[ (long int) from ] < 0) {
+        reached++;
+    }
+    father[ (long int)from ] = 1;
+
+    while (!igraph_dqueue_empty(&q) && reached < to_reach) {
+        long int act = (long int) igraph_dqueue_pop(&q) - 1;
+
+        IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act, mode));
+        for (j = 0; j < igraph_vector_size(&tmp); j++) {
+            long int edge = (long int) VECTOR(tmp)[j];
+            long int neighbor = IGRAPH_OTHER(graph, edge, act);
+            if (father[neighbor] > 0) {
+                continue;
+            } else if (father[neighbor] < 0) {
+                reached++;
+            }
+            father[neighbor] = edge + 2;
+            IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor + 1));
+        }
+    }
+
+    if (reached < to_reach) {
+        IGRAPH_WARNING("Couldn't reach some vertices");
+    }
+
+    /* Create `predecessors' if needed */
+    if (predecessors) {
+        IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes));
+
+        for (i = 0; i < no_of_nodes; i++) {
+            if (father[i] <= 0) {
+                /* i was not reached */
+                VECTOR(*predecessors)[i] = -1;
+            } else if (father[i] == 1) {
+                /* i is the start vertex */
+                VECTOR(*predecessors)[i] = i;
+            } else {
+                /* i was reached via the edge with ID = father[i] - 2 */
+                VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, father[i] - 2, i);
+            }
+        }
+    }
+
+    /* Create `inbound_edges' if needed */
+    if (inbound_edges) {
+        IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes));
+
+        for (i = 0; i < no_of_nodes; i++) {
+            if (father[i] <= 1) {
+                /* i was not reached or i is the start vertex */
+                VECTOR(*inbound_edges)[i] = -1;
+            } else {
+                /* i was reached via the edge with ID = father[i] - 2 */
+                VECTOR(*inbound_edges)[i] = father[i] - 2;
+            }
+        }
+    }
+
+    /* Create `vertices' and `edges' if needed */
+    if (vertices || edges) {
+        for (IGRAPH_VIT_RESET(vit), j = 0;
+             !IGRAPH_VIT_END(vit);
+             IGRAPH_VIT_NEXT(vit), j++) {
+            long int node = IGRAPH_VIT_GET(vit);
+            igraph_vector_t *vvec = 0, *evec = 0;
+            if (vertices) {
+                vvec = VECTOR(*vertices)[j];
+                igraph_vector_clear(vvec);
+            }
+            if (edges) {
+                evec = VECTOR(*edges)[j];
+                igraph_vector_clear(evec);
+            }
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            if (father[node] > 0) {
+                long int act = node;
+                long int size = 0;
+                long int edge;
+                while (father[act] > 1) {
+                    size++;
+                    edge = father[act] - 2;
+                    act = IGRAPH_OTHER(graph, edge, act);
+                }
+                if (vvec) {
+                    IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1));
+                    VECTOR(*vvec)[size] = node;
+                }
+                if (evec) {
+                    IGRAPH_CHECK(igraph_vector_resize(evec, size));
+                }
+                act = node;
+                while (father[act] > 1) {
+                    size--;
+                    edge = father[act] - 2;
+                    act = IGRAPH_OTHER(graph, edge, act);
+                    if (vvec) {
+                        VECTOR(*vvec)[size] = act;
+                    }
+                    if (evec) {
+                        VECTOR(*evec)[size] = edge;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Clean */
+    igraph_Free(father);
+    igraph_dqueue_destroy(&q);
+    igraph_vector_destroy(&tmp);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/**
+ * \function igraph_get_shortest_path
+ * Shortest path from one vertex to another one.
+ *
+ * Calculates and returns a single unweighted shortest path from a
+ * given vertex to another one. If there are more than one shortest
+ * paths between the two vertices, then an arbitrary one is returned.
+ *
+ * </para><para>This function is a wrapper to \ref
+ * igraph_get_shortest_paths(), for the special case when only one
+ * target vertex is considered.
+ * \param graph The input graph, it can be directed or
+ *        undirected. Directed paths are considered in directed
+ *        graphs.
+ * \param vertices Pointer to an initialized vector or a null
+ *        pointer. If not a null pointer, then the vertex ids along
+ *        the path are stored here, including the source and target
+ *        vertices.
+ * \param edges Pointer to an uninitialized vector or a null
+ *        pointer. If not a null pointer, then the edge ids along the
+ *        path are stored here.
+ * \param from The id of the source vertex.
+ * \param to The id of the target vertex.
+ * \param mode A constant specifying how edge directions are
+ *        considered in directed graphs. Valid modes are:
+ *        \c IGRAPH_OUT, follows edge directions;
+ *        \c IGRAPH_IN, follows the opposite directions; and
+ *        \c IGRAPH_ALL, ignores edge directions. This argument is
+ *        ignored for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges in the graph.
+ *
+ * \sa \ref igraph_get_shortest_paths() for the version with more target
+ * vertices.
+ */
+
+int igraph_get_shortest_path(const igraph_t *graph,
+                             igraph_vector_t *vertices,
+                             igraph_vector_t *edges,
+                             igraph_integer_t from,
+                             igraph_integer_t to,
+                             igraph_neimode_t mode) {
+
+    igraph_vector_ptr_t vertices2, *vp = &vertices2;
+    igraph_vector_ptr_t edges2, *ep = &edges2;
+
+    if (vertices) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2);
+        VECTOR(vertices2)[0] = vertices;
+    } else {
+        vp = 0;
+    }
+    if (edges) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2);
+        VECTOR(edges2)[0] = edges;
+    } else {
+        ep = 0;
+    }
+
+    IGRAPH_CHECK(igraph_get_shortest_paths(graph, vp, ep, from,
+                                           igraph_vss_1(to), mode, 0, 0));
+
+    if (edges) {
+        igraph_vector_ptr_destroy(&edges2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (vertices) {
+        igraph_vector_ptr_destroy(&vertices2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v);
+
+void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v) {
+    long int i;
+    for (i = 0; i < igraph_vector_ptr_size(v); i++) {
+        if (VECTOR(*v)[i] != 0) {
+            igraph_vector_destroy(VECTOR(*v)[i]);
+            igraph_Free(VECTOR(*v)[i]);
+        }
+    }
+    igraph_vector_ptr_destroy(v);
+}
+
+/**
+ * \function igraph_get_all_shortest_paths
+ * \brief Finds all shortest paths (geodesics) from a vertex to all other vertices.
+ *
+ * \param graph The graph object.
+ * \param res Pointer to an initialized pointer vector, the result
+ *   will be stored here in igraph_vector_t objects. Each vector
+ *   object contains the vertices along a shortest path from \p from
+ *   to another vertex. The vectors are ordered according to their
+ *   target vertex: first the shortest paths to vertex 0, then to
+ *   vertex 1, etc. No data is included for unreachable vertices.
+ * \param nrgeo Pointer to an initialized igraph_vector_t object or
+ *   NULL. If not NULL the number of shortest paths from \p from are
+ *   stored here for every vertex in the graph. Note that the values
+ *   will be accurate only for those vertices that are in the target
+ *   vertex sequence (see \p to), since the search terminates as soon
+ *   as all the target vertices have been found.
+ * \param from The id of the vertex from/to which the geodesics are
+ *        calculated.
+ * \param to Vertex sequence with the ids of the vertices to/from which the
+ *        shortest paths will be calculated. A vertex might be given multiple
+ *        times.
+ * \param mode The type of shortest paths to be use for the
+ *        calculation in directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the lengths of the outgoing paths are calculated.
+ *        \cli IGRAPH_IN
+ *          the lengths of the incoming paths are calculated.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an
+ *          undirected one for the computation.
+ *        \endclist
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           \p from is invalid vertex id.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(|V|+|E|) for most graphs, O(|V|^2) in the worst
+ * case.
+ */
+
+int igraph_get_all_shortest_paths(const igraph_t *graph,
+                                  igraph_vector_ptr_t *res,
+                                  igraph_vector_t *nrgeo,
+                                  igraph_integer_t from, const igraph_vs_t to,
+                                  igraph_neimode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int *geodist;
+    igraph_vector_ptr_t paths;
+    igraph_dqueue_t q;
+    igraph_vector_t *vptr;
+    igraph_vector_t neis;
+    igraph_vector_t ptrlist;
+    igraph_vector_t ptrhead;
+    long int n, j, i;
+    long int to_reach, reached = 0, maxdist = 0;
+
+    igraph_vit_t vit;
+
+    if (from < 0 || from >= no_of_nodes) {
+        IGRAPH_ERROR("cannot get shortest paths", IGRAPH_EINVVID);
+    }
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, to, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    /* paths will store the shortest paths during the search */
+    IGRAPH_CHECK(igraph_vector_ptr_init(&paths, 0));
+    IGRAPH_FINALLY(igraph_i_gasp_paths_destroy, &paths);
+    /* neis is a temporary vector holding the neighbors of the
+     * node being examined */
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    /* ptrlist stores indices into the paths vector, in the order
+     * of how they were found. ptrhead is a second-level index that
+     * will be used to find paths that terminate in a given vertex */
+    IGRAPH_VECTOR_INIT_FINALLY(&ptrlist, 0);
+    /* ptrhead contains indices into ptrlist.
+     * ptrhead[i] = j means that element #j-1 in ptrlist contains
+     * the shortest path from the root to node i. ptrhead[i] = 0
+     * means that node i was not reached so far */
+    IGRAPH_VECTOR_INIT_FINALLY(&ptrhead, no_of_nodes);
+    /* geodist[i] == 0 if i was not reached yet and it is not in the
+     * target vertex sequence, or -1 if i was not reached yet and it
+     * is in the target vertex sequence. Otherwise it is
+     * one larger than the length of the shortest path from the
+     * source */
+    geodist = igraph_Calloc(no_of_nodes, long int);
+    if (geodist == 0) {
+        IGRAPH_ERROR("Cannot calculate shortest paths", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, geodist);
+    /* dequeue to store the BFS queue -- odd elements are the vertex indices,
+     * even elements are the distances from the root */
+    IGRAPH_CHECK(igraph_dqueue_init(&q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &q);
+
+    if (nrgeo) {
+        IGRAPH_CHECK(igraph_vector_resize(nrgeo, no_of_nodes));
+        igraph_vector_null(nrgeo);
+    }
+
+    /* use geodist to count how many vertices we have to reach */
+    to_reach = IGRAPH_VIT_SIZE(vit);
+    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+        if (geodist[ (long int) IGRAPH_VIT_GET(vit) ] == 0) {
+            geodist[ (long int) IGRAPH_VIT_GET(vit) ] = -1;
+        } else {
+            to_reach--;       /* this node was given multiple times */
+        }
+    }
+
+    if (geodist[ (long int) from ] < 0) {
+        reached++;
+    }
+
+    /* from -> from */
+    vptr = igraph_Calloc(1, igraph_vector_t); /* TODO: dirty */
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(&paths, vptr));
+    IGRAPH_CHECK(igraph_vector_init(vptr, 1));
+    VECTOR(*vptr)[0] = from;
+    geodist[(long int)from] = 1;
+    VECTOR(ptrhead)[(long int)from] = 1;
+    IGRAPH_CHECK(igraph_vector_push_back(&ptrlist, 0));
+    if (nrgeo) {
+        VECTOR(*nrgeo)[(long int)from] = 1;
+    }
+
+    /* Init queue */
+    IGRAPH_CHECK(igraph_dqueue_push(&q, from));
+    IGRAPH_CHECK(igraph_dqueue_push(&q, 0.0));
+    while (!igraph_dqueue_empty(&q)) {
+        long int actnode = (long int) igraph_dqueue_pop(&q);
+        long int actdist = (long int) igraph_dqueue_pop(&q);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (reached >= to_reach) {
+            /* all nodes were reached. Since we need all the shortest paths
+             * to all these nodes, we can stop the search only if the distance
+             * of the current node to the root is larger than the distance of
+             * any of the nodes we wanted to reach */
+            if (actdist > maxdist) {
+                /* safety check, maxdist should have been set when we reached the last node */
+                if (maxdist < 0) {
+                    IGRAPH_ERROR("possible bug in igraph_get_all_shortest_paths, "
+                                 "maxdist is negative", IGRAPH_EINVAL);
+                }
+                break;
+            }
+        }
+
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode,
+                                      mode));
+        n = igraph_vector_size(&neis);
+        for (j = 0; j < n; j++) {
+            long int neighbor = (long int) VECTOR(neis)[j];
+            long int fatherptr;
+
+            if (geodist[neighbor] > 0 &&
+                geodist[neighbor] - 1 < actdist + 1) {
+                /* this node was reached via a shorter path before */
+                continue;
+            }
+
+            /* yay, found another shortest path to neighbor */
+
+            if (nrgeo) {
+                /* the number of geodesics leading to neighbor must be
+                 * increased by the number of geodesics leading to actnode */
+                VECTOR(*nrgeo)[neighbor] += VECTOR(*nrgeo)[actnode];
+            }
+            if (geodist[neighbor] <= 0) {
+                /* this node was not reached yet, push it into the queue */
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+                if (geodist[neighbor] < 0) {
+                    reached++;
+                }
+                if (reached == to_reach) {
+                    maxdist = actdist;
+                }
+            }
+            geodist[neighbor] = actdist + 2;
+
+            /* copy all existing paths to the parent */
+            fatherptr = (long int) VECTOR(ptrhead)[actnode];
+            while (fatherptr != 0) {
+                /* allocate a new igraph_vector_t at the end of paths */
+                vptr = igraph_Calloc(1, igraph_vector_t);
+                IGRAPH_CHECK(igraph_vector_ptr_push_back(&paths, vptr));
+                IGRAPH_CHECK(igraph_vector_copy(vptr, VECTOR(paths)[fatherptr - 1]));
+                IGRAPH_CHECK(igraph_vector_reserve(vptr, actdist + 2));
+                IGRAPH_CHECK(igraph_vector_push_back(vptr, neighbor));
+
+                IGRAPH_CHECK(igraph_vector_push_back(&ptrlist,
+                                                     VECTOR(ptrhead)[neighbor]));
+                VECTOR(ptrhead)[neighbor] = igraph_vector_size(&ptrlist);
+
+                fatherptr = (long int) VECTOR(ptrlist)[fatherptr - 1];
+            }
+        }
+    }
+
+    igraph_dqueue_destroy(&q);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* mark the nodes for which we need the result */
+    memset(geodist, 0, sizeof(long int) * (size_t) no_of_nodes);
+    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+        geodist[ (long int) IGRAPH_VIT_GET(vit) ] = 1;
+    }
+
+    /* count the number of paths in the result */
+    n = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        long int fatherptr = (long int) VECTOR(ptrhead)[i];
+        if (geodist[i] > 0) {
+            while (fatherptr != 0) {
+                n++;
+                fatherptr = (long int) VECTOR(ptrlist)[fatherptr - 1];
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_resize(res, n));
+    j = 0;
+    for (i = 0; i < no_of_nodes; i++) {
+        long int fatherptr = (long int) VECTOR(ptrhead)[i];
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /* do we need the paths leading to vertex i? */
+        if (geodist[i] > 0) {
+            /* yes, copy them to the result vector */
+            while (fatherptr != 0) {
+                VECTOR(*res)[j++] = VECTOR(paths)[fatherptr - 1];
+                fatherptr = (long int) VECTOR(ptrlist)[fatherptr - 1];
+            }
+        } else {
+            /* no, free them */
+            while (fatherptr != 0) {
+                igraph_vector_destroy(VECTOR(paths)[fatherptr - 1]);
+                igraph_Free(VECTOR(paths)[fatherptr - 1]);
+                fatherptr = (long int) VECTOR(ptrlist)[fatherptr - 1];
+            }
+        }
+    }
+
+    igraph_Free(geodist);
+    igraph_vector_destroy(&ptrlist);
+    igraph_vector_destroy(&ptrhead);
+    igraph_vector_destroy(&neis);
+    igraph_vector_ptr_destroy(&paths);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return 0;
+}
+
+
+/**
+ * \ingroup structural
+ * \function igraph_subcomponent
+ * \brief The vertices in the same component as a given vertex.
+ *
+ * \param graph The graph object.
+ * \param res The result, vector with the ids of the vertices in the
+ *        same component.
+ * \param vertex The id of the vertex of which the component is
+ *        searched.
+ * \param mode Type of the component for directed graphs, possible
+ *        values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the set of vertices reachable \em from the
+ *          \p vertex,
+ *        \cli IGRAPH_IN
+ *          the set of vertices from which the
+ *          \p vertex is reachable.
+ *        \cli IGRAPH_ALL
+ *          the graph is considered as an
+ *          undirected graph. Note that this is \em not the same
+ *          as the union of the previous two.
+ *        \endclist
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *          not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           \p vertex is an invalid vertex id
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument passed.
+ *        \endclist
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the graph.
+ *
+ * \sa \ref igraph_subgraph() if you want a graph object consisting only
+ * a given set of vertices and the edges between them.
+ */
+
+int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vertex,
+                        igraph_neimode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
+    char *already_added;
+    long int i;
+    igraph_vector_t tmp = IGRAPH_VECTOR_NULL;
+
+    if (!IGRAPH_FINITE(vertex) || vertex < 0 || vertex >= no_of_nodes) {
+        IGRAPH_ERROR("subcomponent failed", IGRAPH_EINVVID);
+    }
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    already_added = igraph_Calloc(no_of_nodes, char);
+    if (already_added == 0) {
+        IGRAPH_ERROR("subcomponent failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, already_added); /* TODO: hack */
+
+    igraph_vector_clear(res);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+
+    IGRAPH_CHECK(igraph_dqueue_push(&q, vertex));
+    IGRAPH_CHECK(igraph_vector_push_back(res, vertex));
+    already_added[(long int)vertex] = 1;
+
+    while (!igraph_dqueue_empty(&q)) {
+        long int actnode = (long int) igraph_dqueue_pop(&q);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        IGRAPH_CHECK(igraph_neighbors(graph, &tmp, (igraph_integer_t) actnode,
+                                      mode));
+        for (i = 0; i < igraph_vector_size(&tmp); i++) {
+            long int neighbor = (long int) VECTOR(tmp)[i];
+
+            if (already_added[neighbor]) {
+                continue;
+            }
+            already_added[neighbor] = 1;
+            IGRAPH_CHECK(igraph_vector_push_back(res, neighbor));
+            IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+        }
+    }
+
+    igraph_dqueue_destroy(&q);
+    igraph_vector_destroy(&tmp);
+    igraph_Free(already_added);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_pagerank_old
+ * \brief Calculates the Google PageRank for the specified vertices.
+ *
+ * </para><para>This is an old implementation,
+ * it is provided for compatibility with igraph versions earlier than
+ * 0.5. Please use the new implementation \ref igraph_pagerank() in
+ * new projects.
+ *
+ * </para><para>
+ * From version 0.7 this function is deprecated and its use gives a
+ * warning message.
+ *
+ * </para><para>
+ * Please note that the PageRank of a given vertex depends on the PageRank
+ * of all other vertices, so even if you want to calculate the PageRank for
+ * only some of the vertices, all of them must be calculated. Requesting
+ * the PageRank for only some of the vertices does not result in any
+ * performance increase at all.
+ * </para>
+ * <para>
+ * Since the calculation is an iterative
+ * process, the algorithm is stopped after a given count of iterations
+ * or if the PageRank value differences between iterations are less than
+ * a predefined value.
+ * </para>
+ *
+ * <para>
+ * For the explanation of the PageRank algorithm, see the following
+ * webpage:
+ * http://infolab.stanford.edu/~backrub/google.html , or the
+ * following reference:
+ * </para>
+ *
+ * <para>
+ * Sergey Brin and Larry Page: The Anatomy of a Large-Scale Hypertextual
+ * Web Search Engine. Proceedings of the 7th World-Wide Web Conference,
+ * Brisbane, Australia, April 1998.
+ * </para>
+ * <para>
+ * \param graph The graph object.
+ * \param res The result vector containing the PageRank values for the
+ * given nodes.
+ * \param vids Vector with the vertex ids
+ * \param directed Logical, if true directed paths will be considered
+ *        for directed graphs. It is ignored for undirected graphs.
+ * \param niter The maximum number of iterations to perform
+ * \param eps The algorithm will consider the calculation as complete
+ *        if the difference of PageRank values between iterations change
+ *        less than this value for every node
+ * \param damping The damping factor ("d" in the original paper)
+ * \param old Boolean, whether to use the pre-igraph 0.5 way to
+ *        calculate page rank. Not recommended for new applications,
+ *        only included for compatibility. If this is non-zero then the damping
+ *        factor is not divided by the number of vertices before adding it
+ *        to the weighted page rank scores to calculate the
+ *        new scores. I.e. the formula in the original PageRank paper
+ *        is used. Furthermore, if this is non-zero then the PageRank
+ *        vector is renormalized after each iteration.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *         \c IGRAPH_EINVVID, invalid vertex id in
+ *         \p vids.
+ *
+ * Time complexity: O(|V|+|E|) per iteration. A handful iterations
+ * should be enough. Note that if the old-style dumping is used then
+ * the iteration might not converge at all.
+ *
+ * \sa \ref igraph_pagerank() for the new implementation.
+ */
+
+int igraph_pagerank_old(const igraph_t *graph, igraph_vector_t *res,
+                        const igraph_vs_t vids, igraph_bool_t directed,
+                        igraph_integer_t niter, igraph_real_t eps,
+                        igraph_real_t damping, igraph_bool_t old) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i, j, n, nodes_to_calc;
+    igraph_real_t *prvec, *prvec_new, *prvec_aux, *prvec_scaled;
+    igraph_vector_int_t *neis;
+    igraph_vector_t outdegree;
+    igraph_neimode_t dirmode;
+    igraph_adjlist_t allneis;
+    igraph_real_t maxdiff = eps;
+    igraph_vit_t vit;
+
+    IGRAPH_WARNING("igraph_pagerank_old is deprecated from igraph 0.7, "
+                   "use igraph_pagerank instead");
+
+    if (niter <= 0) {
+        IGRAPH_ERROR("Invalid iteration count", IGRAPH_EINVAL);
+    }
+    if (eps <= 0) {
+        IGRAPH_ERROR("Invalid epsilon value", IGRAPH_EINVAL);
+    }
+    if (damping <= 0 || damping >= 1) {
+        IGRAPH_ERROR("Invalid damping factor", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
+    igraph_vector_null(res);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&outdegree, no_of_nodes);
+
+    prvec = igraph_Calloc(no_of_nodes, igraph_real_t);
+    if (prvec == 0) {
+        IGRAPH_ERROR("pagerank failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, prvec);
+
+    prvec_new = igraph_Calloc(no_of_nodes, igraph_real_t);
+    if (prvec_new == 0) {
+        IGRAPH_ERROR("pagerank failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, prvec_new);
+
+    prvec_scaled = igraph_Calloc(no_of_nodes, igraph_real_t);
+    if (prvec_scaled == 0) {
+        IGRAPH_ERROR("pagerank failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, prvec_scaled);
+
+    if (directed) {
+        dirmode = IGRAPH_IN;
+    } else {
+        dirmode = IGRAPH_ALL;
+    }
+    igraph_adjlist_init(graph, &allneis, dirmode);
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+
+    /* Calculate outdegrees for every node */
+    igraph_degree(graph, &outdegree, igraph_vss_all(),
+                  directed ? IGRAPH_OUT : IGRAPH_ALL, 0);
+    /* Initialize PageRank values */
+    for (i = 0; i < no_of_nodes; i++) {
+        prvec[i] = 1 - damping;
+        /* The next line is necessary to avoid division by zero in the
+         * calculation of prvec_scaled. This won't cause any problem,
+         * since if a node doesn't have any outgoing links, its
+         * prvec_scaled value won't be used anywhere */
+        if (VECTOR(outdegree)[i] == 0) {
+            VECTOR(outdegree)[i] = 1;
+        }
+    }
+
+    /* We will always calculate the new PageRank values into prvec_new
+     * based on the existing values from prvec. To avoid unnecessary
+     * copying from prvec_new to prvec at the end of every iteration,
+     * the pointers are swapped after every iteration */
+    while (niter > 0 && maxdiff >= eps) {
+        igraph_real_t sumfrom = 0, sum = 0;
+        niter--;
+        maxdiff = 0;
+
+        /* Calculate the quotient of the actual PageRank value and the
+         * outdegree for every node */
+        sumfrom = 0.0; sum = 0.0;
+        for (i = 0; i < no_of_nodes; i++) {
+            sumfrom += prvec[i];
+            prvec_scaled[i] = prvec[i] / VECTOR(outdegree)[i];
+        }
+
+        /* Calculate new PageRank values based on the old ones */
+        for (i = 0; i < no_of_nodes; i++) {
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            prvec_new[i] = 0;
+            neis = igraph_adjlist_get(&allneis, i);
+            n = igraph_vector_int_size(neis);
+            for (j = 0; j < n; j++) {
+                long int neighbor = (long int) VECTOR(*neis)[j];
+                prvec_new[i] += prvec_scaled[neighbor];
+            }
+            prvec_new[i] *= damping;
+            if (!old) {
+                prvec_new[i] += (1 - damping) / no_of_nodes;
+            } else {
+                prvec_new[i] += (1 - damping);
+            }
+            sum += prvec_new[i];
+
+        }
+        for (i = 0; i < no_of_nodes; i++) {
+            if (!old) {
+                prvec_new[i] /= sum;
+            }
+
+            if (prvec_new[i] - prvec[i] > maxdiff) {
+                maxdiff = prvec_new[i] - prvec[i];
+            } else if (prvec[i] - prvec_new[i] > maxdiff) {
+                maxdiff = prvec[i] - prvec_new[i];
+            }
+        }
+
+        /* Swap the vectors */
+        prvec_aux = prvec_new;
+        prvec_new = prvec;
+        prvec = prvec_aux;
+    }
+
+    /* Copy results from prvec to res */
+    for (IGRAPH_VIT_RESET(vit), i = 0;
+         !IGRAPH_VIT_END(vit);
+         IGRAPH_VIT_NEXT(vit), i++) {
+        long int vid = IGRAPH_VIT_GET(vit);
+        VECTOR(*res)[i] = prvec[vid];
+    }
+
+    igraph_adjlist_destroy(&allneis);
+    igraph_vit_destroy(&vit);
+    igraph_vector_destroy(&outdegree);
+    igraph_Free(prvec);
+    igraph_Free(prvec_new);
+    igraph_Free(prvec_scaled);
+
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return 0;
+}
+
+/* Not declared static so that the testsuite can use it, but not part of the public API. */
+int igraph_rewire_core(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, igraph_bool_t use_adjlist) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    char message[256];
+    igraph_integer_t a, b, c, d, dummy, num_swaps, num_successful_swaps;
+    igraph_vector_t eids, edgevec, alledges;
+    igraph_bool_t directed, loops, ok;
+    igraph_es_t es;
+    igraph_adjlist_t al;
+
+    if (no_of_nodes < 4) {
+        IGRAPH_ERROR("graph unsuitable for rewiring", IGRAPH_EINVAL);
+    }
+
+    directed = igraph_is_directed(graph);
+    loops = (mode & IGRAPH_REWIRING_SIMPLE_LOOPS);
+
+    RNG_BEGIN();
+
+    IGRAPH_VECTOR_INIT_FINALLY(&eids, 2);
+
+    if (use_adjlist) {
+        /* As well as the sorted adjacency list, we maintain an unordered
+         * list of edges for picking a random edge in constant time.
+         */
+        IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_OUT));
+        IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
+        IGRAPH_VECTOR_INIT_FINALLY(&alledges, no_of_edges * 2);
+        igraph_get_edgelist(graph, &alledges, /*bycol=*/ 0);
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&edgevec, 4);
+        es = igraph_ess_vector(&eids);
+    }
+
+    /* We don't want the algorithm to get stuck in an infinite loop when
+     * it can't choose two edges satisfying the conditions. Instead of
+     * this, we choose two arbitrary edges and if they have endpoints
+     * in common, we just decrease the number of trials left and continue
+     * (so unsuccessful rewirings still count as a trial)
+     */
+
+    num_swaps = num_successful_swaps = 0;
+    while (num_swaps < n) {
+
+        IGRAPH_ALLOW_INTERRUPTION();
+        if (num_swaps % 1000 == 0) {
+            snprintf(message, sizeof(message),
+                     "Random rewiring (%.2f%% of the trials were successful)",
+                     num_swaps > 0 ? ((100.0 * num_successful_swaps) / num_swaps) : 0.0);
+            IGRAPH_PROGRESS(message, (100.0 * num_swaps) / n, 0);
+        }
+
+        switch (mode) {
+        case IGRAPH_REWIRING_SIMPLE:
+        case IGRAPH_REWIRING_SIMPLE_LOOPS:
+            ok = 1;
+
+            /* Choose two edges randomly */
+            VECTOR(eids)[0] = RNG_INTEGER(0, no_of_edges - 1);
+            do {
+                VECTOR(eids)[1] = RNG_INTEGER(0, no_of_edges - 1);
+            } while (VECTOR(eids)[0] == VECTOR(eids)[1]);
+
+            /* Get the endpoints */
+            if (use_adjlist) {
+                a = VECTOR(alledges)[((igraph_integer_t)VECTOR(eids)[0]) * 2];
+                b = VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[0]) * 2) + 1];
+                c = VECTOR(alledges)[((igraph_integer_t)VECTOR(eids)[1]) * 2];
+                d = VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[1]) * 2) + 1];
+            } else {
+                IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) VECTOR(eids)[0],
+                                         &a, &b));
+                IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) VECTOR(eids)[1],
+                                         &c, &d));
+            }
+
+            /* For an undirected graph, we have two "variants" of each edge, i.e.
+             * a -- b and b -- a. Since some rewirings can be performed only when we
+             * "swap" the endpoints, we do it now with probability 0.5 */
+            if (!directed && RNG_UNIF01() < 0.5) {
+                dummy = c; c = d; d = dummy;
+                if (use_adjlist) {
+                    /* Flip the edge in the unordered edge-list, so the update later on
+                     * hits the correct end. */
+                    VECTOR(alledges)[((igraph_integer_t)VECTOR(eids)[1]) * 2] = c;
+                    VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[1]) * 2) + 1] = d;
+                }
+            }
+
+            /* If we do not touch loops, check whether a == b or c == d and disallow
+             * the swap if needed */
+            if (!loops && (a == b || c == d)) {
+                ok = 0;
+            } else {
+                /* Check whether they are suitable for rewiring */
+                if (a == c || b == d) {
+                    /* Swapping would have no effect */
+                    ok = 0;
+                } else {
+                    /* a != c && b != d */
+                    /* If a == d or b == c, the swap would generate at least one loop, so
+                     * we disallow them unless we want to have loops */
+                    ok = loops || (a != d && b != c);
+                    /* Also, if a == b and c == d and we allow loops, doing the swap
+                     * would result in a multiple edge if the graph is undirected */
+                    ok = ok && (directed || a != b || c != d);
+                }
+            }
+
+            /* All good so far. Now check for the existence of a --> d and c --> b to
+             * disallow the creation of multiple edges */
+            if (ok) {
+                if (use_adjlist) {
+                    if (igraph_adjlist_has_edge(&al, a, d, directed)) {
+                        ok = 0;
+                    }
+                } else {
+                    IGRAPH_CHECK(igraph_are_connected(graph, a, d, &ok));
+                    ok = !ok;
+                }
+            }
+            if (ok) {
+                if (use_adjlist) {
+                    if (igraph_adjlist_has_edge(&al, c, b, directed)) {
+                        ok = 0;
+                    }
+                } else {
+                    IGRAPH_CHECK(igraph_are_connected(graph, c, b, &ok));
+                    ok = !ok;
+                }
+            }
+
+            /* If we are still okay, we can perform the rewiring */
+            if (ok) {
+                /* printf("Deleting: %ld -> %ld, %ld -> %ld\n",
+                              (long)a, (long)b, (long)c, (long)d); */
+                if (use_adjlist) {
+                    // Replace entry in sorted adjlist:
+                    IGRAPH_CHECK(igraph_adjlist_replace_edge(&al, a, b, d, directed));
+                    IGRAPH_CHECK(igraph_adjlist_replace_edge(&al, c, d, b, directed));
+                    // Also replace in unsorted edgelist:
+                    VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[0]) * 2) + 1] = d;
+                    VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[1]) * 2) + 1] = b;
+                } else {
+                    IGRAPH_CHECK(igraph_delete_edges(graph, es));
+                    VECTOR(edgevec)[0] = a; VECTOR(edgevec)[1] = d;
+                    VECTOR(edgevec)[2] = c; VECTOR(edgevec)[3] = b;
+                    /* printf("Adding: %ld -> %ld, %ld -> %ld\n",
+                                (long)a, (long)d, (long)c, (long)b); */
+                    igraph_add_edges(graph, &edgevec, 0);
+                }
+                num_successful_swaps++;
+            }
+            break;
+        default:
+            RNG_END();
+            IGRAPH_ERROR("unknown rewiring mode", IGRAPH_EINVMODE);
+        }
+        num_swaps++;
+    }
+
+    if (use_adjlist) {
+        /* Replace graph edges with the adjlist current state */
+        IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID)));
+        IGRAPH_CHECK(igraph_add_edges(graph, &alledges, 0));
+    }
+
+    IGRAPH_PROGRESS("Random rewiring: ", 100.0, 0);
+
+    if (use_adjlist) {
+        igraph_vector_destroy(&alledges);
+        igraph_adjlist_destroy(&al);
+    } else {
+        igraph_vector_destroy(&edgevec);
+    }
+
+    igraph_vector_destroy(&eids);
+    IGRAPH_FINALLY_CLEAN(use_adjlist ? 3 : 2);
+
+    RNG_END();
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_rewire
+ * \brief Randomly rewires a graph while preserving the degree distribution.
+ *
+ * </para><para>
+ * This function generates a new graph based on the original one by randomly
+ * rewiring edges while preserving the original graph's degree distribution.
+ * Please note that the rewiring is done "in place", so no new graph will
+ * be allocated. If you would like to keep the original graph intact, use
+ * \ref igraph_copy() beforehand.
+ *
+ * \param graph The graph object to be rewired.
+ * \param n Number of rewiring trials to perform.
+ * \param mode The rewiring algorithm to be used. It can be one of the following flags:
+ *         \clist
+ *           \cli IGRAPH_REWIRING_SIMPLE
+ *                Simple rewiring algorithm which chooses two arbitrary edges
+ *                in each step (namely (a,b) and (c,d)) and substitutes them
+ *                with (a,d) and (c,b) if they don't exist.  The method will
+ *                neither destroy nor create self-loops.
+ *           \cli IGRAPH_REWIRING_SIMPLE_LOOPS
+ *                Same as \c IGRAPH_REWIRING_SIMPLE but allows the creation or
+ *                destruction of self-loops.
+ *         \endclist
+ *
+ * \return Error code:
+ *         \clist
+ *           \cli IGRAPH_EINVMODE
+ *                Invalid rewiring mode.
+ *           \cli IGRAPH_EINVAL
+ *                Graph unsuitable for rewiring (e.g. it has
+ *                less than 4 nodes in case of \c IGRAPH_REWIRING_SIMPLE)
+ *           \cli IGRAPH_ENOMEM
+ *                Not enough memory for temporary data.
+ *         \endclist
+ *
+ * Time complexity: TODO.
+ *
+ * \example examples/simple/igraph_rewire.c
+ */
+
+#define REWIRE_ADJLIST_THRESHOLD 10
+
+int igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode) {
+
+    igraph_bool_t use_adjlist = n >= REWIRE_ADJLIST_THRESHOLD;
+    return igraph_rewire_core(graph, n, mode, use_adjlist);
+
+}
+
+/**
+ * Subgraph creation, old version: it copies the graph and then deletes
+ * unneeded vertices.
+ */
+int igraph_i_subgraph_copy_and_delete(const igraph_t *graph, igraph_t *res,
+                                      const igraph_vs_t vids,
+                                      igraph_vector_t *map,
+                                      igraph_vector_t *invmap) {
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t delete = IGRAPH_VECTOR_NULL;
+    char *remain;
+    long int i;
+    igraph_vit_t vit;
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&delete, 0);
+    remain = igraph_Calloc(no_of_nodes, char);
+    if (remain == 0) {
+        IGRAPH_ERROR("subgraph failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, remain); /* TODO: hack */
+    IGRAPH_CHECK(igraph_vector_reserve(&delete, no_of_nodes - IGRAPH_VIT_SIZE(vit)));
+
+    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+        remain[ (long int) IGRAPH_VIT_GET(vit) ] = 1;
+    }
+
+    for (i = 0; i < no_of_nodes; i++) {
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (remain[i] == 0) {
+            IGRAPH_CHECK(igraph_vector_push_back(&delete, i));
+        }
+    }
+
+    igraph_Free(remain);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* must set res->attr to 0 before calling igraph_copy */
+    res->attr = 0;         /* Why is this needed? TODO */
+    IGRAPH_CHECK(igraph_copy(res, graph));
+    IGRAPH_FINALLY(igraph_destroy, res);
+    IGRAPH_CHECK(igraph_delete_vertices_idx(res, igraph_vss_vector(&delete),
+                                            map, invmap));
+
+    igraph_vector_destroy(&delete);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+/**
+ * Subgraph creation, new version: creates the new graph instead of
+ * copying the old one.
+ */
+int igraph_i_subgraph_create_from_scratch(const igraph_t *graph,
+        igraph_t *res,
+        const igraph_vs_t vids,
+        igraph_vector_t *map,
+        igraph_vector_t *invmap) {
+    igraph_bool_t directed = igraph_is_directed(graph);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_new_nodes = 0;
+    long int i, j, n;
+    long int to;
+    igraph_integer_t eid;
+    igraph_vector_t vids_old2new, vids_new2old;
+    igraph_vector_t eids_new2old;
+    igraph_vector_t nei_edges;
+    igraph_vector_t new_edges;
+    igraph_vit_t vit;
+    igraph_vector_t *my_vids_old2new = &vids_old2new,
+                     *my_vids_new2old = &vids_new2old;
+
+    /* The order of initialization is important here, they will be destroyed in the
+     * opposite order */
+    IGRAPH_VECTOR_INIT_FINALLY(&eids_new2old, 0);
+    if (invmap) {
+        my_vids_new2old = invmap;
+        igraph_vector_clear(my_vids_new2old);
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&vids_new2old, 0);
+    }
+    IGRAPH_VECTOR_INIT_FINALLY(&new_edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&nei_edges, 0);
+    if (map) {
+        my_vids_old2new = map;
+        IGRAPH_CHECK(igraph_vector_resize(map, no_of_nodes));
+        igraph_vector_null(map);
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    /* Calculate the mapping from the old node IDs to the new ones. The other
+     * igraph_simplify implementation in igraph_i_simplify_copy_and_delete
+     * ensures that the order of vertex IDs is kept during remapping (i.e.
+     * if the old ID of vertex A is less than the old ID of vertex B, then
+     * the same will also be true for the new IDs). To ensure compatibility
+     * with the other implementation, we have to fetch the vertex IDs into
+     * a vector first and then sort it. We temporarily use new_edges for that.
+     */
+    IGRAPH_CHECK(igraph_vit_as_vector(&vit, &nei_edges));
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_vector_sort(&nei_edges);
+    n = igraph_vector_size(&nei_edges);
+    for (i = 0; i < n; i++) {
+        long int vid = (long int) VECTOR(nei_edges)[i];
+        if (VECTOR(*my_vids_old2new)[vid] == 0) {
+            IGRAPH_CHECK(igraph_vector_push_back(my_vids_new2old, vid));
+            no_of_new_nodes++;
+            VECTOR(*my_vids_old2new)[vid] = no_of_new_nodes;
+        }
+    }
+
+    /* Create the new edge list */
+    for (i = 0; i < no_of_new_nodes; i++) {
+        long int old_vid = (long int) VECTOR(*my_vids_new2old)[i];
+        long int new_vid = i;
+
+        IGRAPH_CHECK(igraph_incident(graph, &nei_edges, old_vid, IGRAPH_OUT));
+        n = igraph_vector_size(&nei_edges);
+
+        if (directed) {
+            for (j = 0; j < n; j++) {
+                eid = (igraph_integer_t) VECTOR(nei_edges)[j];
+
+                to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid) ];
+                if (!to) {
+                    continue;
+                }
+
+                IGRAPH_CHECK(igraph_vector_push_back(&new_edges, new_vid));
+                IGRAPH_CHECK(igraph_vector_push_back(&new_edges, to - 1));
+                IGRAPH_CHECK(igraph_vector_push_back(&eids_new2old, eid));
+            }
+        } else {
+            for (j = 0; j < n; j++) {
+                eid = (igraph_integer_t) VECTOR(nei_edges)[j];
+
+                if (IGRAPH_FROM(graph, eid) != old_vid) { /* avoid processing edges twice */
+                    continue;
+                }
+
+                to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid) ];
+                if (!to) {
+                    continue;
+                }
+
+                IGRAPH_CHECK(igraph_vector_push_back(&new_edges, new_vid));
+                IGRAPH_CHECK(igraph_vector_push_back(&new_edges, to - 1));
+                IGRAPH_CHECK(igraph_vector_push_back(&eids_new2old, eid));
+            }
+        }
+    }
+
+    /* Get rid of some vectors that are not needed anymore */
+    if (!map) {
+        igraph_vector_destroy(&vids_old2new);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    igraph_vector_destroy(&nei_edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Create the new graph */
+    IGRAPH_CHECK(igraph_create(res, &new_edges, (igraph_integer_t)
+                               no_of_new_nodes, directed));
+    IGRAPH_I_ATTRIBUTE_DESTROY(res);
+
+    /* Now we can also get rid of the new_edges vector */
+    igraph_vector_destroy(&new_edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Make sure that the newly created graph is destroyed if something happens from
+     * now on */
+    IGRAPH_FINALLY(igraph_destroy, res);
+
+    /* Copy the graph attributes */
+    IGRAPH_CHECK(igraph_i_attribute_copy(res, graph,
+                                         /* ga = */ 1, /* va = */ 0, /* ea = */ 0));
+
+    /* Copy the vertex attributes */
+    IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res,
+                 my_vids_new2old));
+
+    /* Copy the edge attributes */
+    IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, res, &eids_new2old));
+
+    if (!invmap) {
+        igraph_vector_destroy(my_vids_new2old);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    igraph_vector_destroy(&eids_new2old);
+    IGRAPH_FINALLY_CLEAN(2);   /* 1 + 1 since we don't need to destroy res */
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_subgraph
+ * \brief Creates a subgraph induced by the specified vertices.
+ *
+ * </para><para>
+ * This function is an alias to \ref igraph_induced_subgraph(), it is
+ * left here to ensure API compatibility with igraph versions prior to 0.6.
+ *
+ * </para><para>
+ * This function collects the specified vertices and all edges between
+ * them to a new graph.
+ * As the vertex ids in a graph always start with zero, this function
+ * very likely needs to reassign ids to the vertices.
+ * \param graph The graph object.
+ * \param res The subgraph, another graph object will be stored here,
+ *        do \em not initialize this object before calling this
+ *        function, and call \ref igraph_destroy() on it if you don't need
+ *        it any more.
+ * \param vids A vertex selector describing which vertices to keep.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *         \c IGRAPH_EINVVID, invalid vertex id in
+ *         \p vids.
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the original graph.
+ *
+ * \sa \ref igraph_delete_vertices() to delete the specified set of
+ * vertices from a graph, the opposite of this function.
+ */
+
+int igraph_subgraph(const igraph_t *graph, igraph_t *res,
+                    const igraph_vs_t vids) {
+    IGRAPH_WARNING("igraph_subgraph is deprecated from igraph 0.6, "
+                   "use igraph_induced_subgraph instead");
+    return igraph_induced_subgraph(graph, res, vids, IGRAPH_SUBGRAPH_AUTO);
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_induced_subgraph
+ * \brief Creates a subgraph induced by the specified vertices.
+ *
+ * </para><para>
+ * This function collects the specified vertices and all edges between
+ * them to a new graph.
+ * As the vertex ids in a graph always start with zero, this function
+ * very likely needs to reassign ids to the vertices.
+ * \param graph The graph object.
+ * \param res The subgraph, another graph object will be stored here,
+ *        do \em not initialize this object before calling this
+ *        function, and call \ref igraph_destroy() on it if you don't need
+ *        it any more.
+ * \param vids A vertex selector describing which vertices to keep.
+ * \param impl This parameter selects which implementation should we
+ *        use when constructing the new graph. Basically there are two
+ *        possibilities: \c IGRAPH_SUBGRAPH_COPY_AND_DELETE copies the
+ *        existing graph and deletes the vertices that are not needed
+ *        in the new graph, while \c IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH
+ *        constructs the new graph from scratch without copying the old
+ *        one. The latter is more efficient if you are extracting a
+ *        relatively small subpart of a very large graph, while the
+ *        former is better if you want to extract a subgraph whose size
+ *        is comparable to the size of the whole graph. There is a third
+ *        possibility: \c IGRAPH_SUBGRAPH_AUTO will select one of the
+ *        two methods automatically based on the ratio of the number
+ *        of vertices in the new and the old graph.
+ *
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *         \c IGRAPH_EINVVID, invalid vertex id in
+ *         \p vids.
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the original graph.
+ *
+ * \sa \ref igraph_delete_vertices() to delete the specified set of
+ * vertices from a graph, the opposite of this function.
+ */
+int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res,
+                            const igraph_vs_t vids, igraph_subgraph_implementation_t impl) {
+    return igraph_induced_subgraph_map(graph, res, vids, impl, /* map= */ 0,
+                                       /* invmap= */ 0);
+}
+
+int igraph_i_induced_subgraph_suggest_implementation(
+    const igraph_t *graph, const igraph_vs_t vids,
+    igraph_subgraph_implementation_t *result) {
+    double ratio;
+    igraph_integer_t num_vs;
+
+    if (igraph_vs_is_all(&vids)) {
+        ratio = 1.0;
+    } else {
+        IGRAPH_CHECK(igraph_vs_size(graph, &vids, &num_vs));
+        ratio = (igraph_real_t) num_vs / igraph_vcount(graph);
+    }
+
+    /* TODO: needs benchmarking; threshold was chosen totally arbitrarily */
+    if (ratio > 0.5) {
+        *result = IGRAPH_SUBGRAPH_COPY_AND_DELETE;
+    } else {
+        *result = IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH;
+    }
+
+    return 0;
+}
+
+int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res,
+                                const igraph_vs_t vids,
+                                igraph_subgraph_implementation_t impl,
+                                igraph_vector_t *map,
+                                igraph_vector_t *invmap) {
+
+    if (impl == IGRAPH_SUBGRAPH_AUTO) {
+        IGRAPH_CHECK(igraph_i_induced_subgraph_suggest_implementation(graph, vids, &impl));
+    }
+
+    switch (impl) {
+    case IGRAPH_SUBGRAPH_COPY_AND_DELETE:
+        return igraph_i_subgraph_copy_and_delete(graph, res, vids, map, invmap);
+
+    case IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH:
+        return igraph_i_subgraph_create_from_scratch(graph, res, vids, map,
+                invmap);
+
+    default:
+        IGRAPH_ERROR("unknown subgraph implementation type", IGRAPH_EINVAL);
+    }
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_subgraph_edges
+ * \brief Creates a subgraph with the specified edges and their endpoints.
+ *
+ * </para><para>
+ * This function collects the specified edges and their endpoints to a new
+ * graph.
+ * As the vertex ids in a graph always start with zero, this function
+ * very likely needs to reassign ids to the vertices.
+ * \param graph The graph object.
+ * \param res The subgraph, another graph object will be stored here,
+ *        do \em not initialize this object before calling this
+ *        function, and call \ref igraph_destroy() on it if you don't need
+ *        it any more.
+ * \param eids An edge selector describing which edges to keep.
+ * \param delete_vertices Whether to delete the vertices not incident on any
+ *        of the specified edges as well. If \c FALSE, the number of vertices
+ *        in the result graph will always be equal to the number of vertices
+ *        in the input graph.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM, not enough memory for
+ *         temporary data.
+ *         \c IGRAPH_EINVEID, invalid edge id in
+ *         \p eids.
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the original graph.
+ *
+ * \sa \ref igraph_delete_edges() to delete the specified set of
+ * edges from a graph, the opposite of this function.
+ */
+
+int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res,
+                          const igraph_es_t eids, igraph_bool_t delete_vertices) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vector_t delete = IGRAPH_VECTOR_NULL;
+    char *vremain, *eremain;
+    long int i;
+    igraph_eit_t eit;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&delete, 0);
+    vremain = igraph_Calloc(no_of_nodes, char);
+    if (vremain == 0) {
+        IGRAPH_ERROR("subgraph_edges failed", IGRAPH_ENOMEM);
+    }
+    eremain = igraph_Calloc(no_of_edges, char);
+    if (eremain == 0) {
+        IGRAPH_ERROR("subgraph_edges failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, vremain);    /* TODO: hack */
+    IGRAPH_FINALLY(free, eremain);    /* TODO: hack */
+    IGRAPH_CHECK(igraph_vector_reserve(&delete, no_of_edges - IGRAPH_EIT_SIZE(eit)));
+
+    /* Collect the vertex and edge IDs that will remain */
+    for (IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
+        igraph_integer_t from, to;
+        long int eid = (long int) IGRAPH_EIT_GET(eit);
+        IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) eid, &from, &to));
+        eremain[eid] = vremain[(long int)from] = vremain[(long int)to] = 1;
+    }
+
+    /* Collect the edge IDs to be deleted */
+    for (i = 0; i < no_of_edges; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        if (eremain[i] == 0) {
+            IGRAPH_CHECK(igraph_vector_push_back(&delete, i));
+        }
+    }
+
+    igraph_Free(eremain);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Delete the unnecessary edges */
+    /* must set res->attr to 0 before calling igraph_copy */
+    res->attr = 0;         /* Why is this needed? TODO */
+    IGRAPH_CHECK(igraph_copy(res, graph));
+    IGRAPH_FINALLY(igraph_destroy, res);
+    IGRAPH_CHECK(igraph_delete_edges(res, igraph_ess_vector(&delete)));
+
+    if (delete_vertices) {
+        /* Collect the vertex IDs to be deleted */
+        igraph_vector_clear(&delete);
+        for (i = 0; i < no_of_nodes; i++) {
+            IGRAPH_ALLOW_INTERRUPTION();
+            if (vremain[i] == 0) {
+                IGRAPH_CHECK(igraph_vector_push_back(&delete, i));
+            }
+        }
+    }
+
+    igraph_Free(vremain);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Delete the unnecessary vertices */
+    if (delete_vertices) {
+        IGRAPH_CHECK(igraph_delete_vertices(res, igraph_vss_vector(&delete)));
+    }
+
+    igraph_vector_destroy(&delete);
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(3);
+    return 0;
+}
+
+void igraph_i_simplify_free(igraph_vector_ptr_t *p);
+
+void igraph_i_simplify_free(igraph_vector_ptr_t *p) {
+    long int i, n = igraph_vector_ptr_size(p);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *v = VECTOR(*p)[i];
+        if (v) {
+            igraph_vector_destroy(v);
+        }
+    }
+    igraph_vector_ptr_destroy(p);
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_simplify
+ * \brief Removes loop and/or multiple edges from the graph.
+ *
+ * \param graph The graph object.
+ * \param multiple Logical, if true, multiple edges will be removed.
+ * \param loops Logical, if true, loops (self edges) will be removed.
+ * \param edge_comb What to do with the edge attributes. See the igraph
+ *        manual section about attributes for details.
+ * \return Error code:
+ *    \c IGRAPH_ENOMEM if we are out of memory.
+ *
+ * Time complexity: O(|V|+|E|).
+ *
+ * \example examples/simple/igraph_simplify.c
+ */
+
+int igraph_simplify(igraph_t *graph, igraph_bool_t multiple,
+                    igraph_bool_t loops,
+                    const igraph_attribute_combination_t *edge_comb) {
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int edge;
+    igraph_bool_t attr = edge_comb && igraph_has_attribute_table();
+    long int from, to, pfrom = -1, pto = -2;
+    igraph_t res;
+    igraph_es_t es;
+    igraph_eit_t eit;
+    igraph_vector_t mergeinto;
+    long int actedge;
+
+    if (!multiple && !loops)
+        /* nothing to do */
+    {
+        return IGRAPH_SUCCESS;
+    }
+
+    if (!multiple) {
+        /* removing loop edges only, this is simple. No need to combine anything
+         * and the whole process can be done in-place */
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+        IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID));
+        IGRAPH_FINALLY(igraph_es_destroy, &es);
+        IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+        IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+        while (!IGRAPH_EIT_END(eit)) {
+            edge = IGRAPH_EIT_GET(eit);
+            from = IGRAPH_FROM(graph, edge);
+            to = IGRAPH_TO(graph, edge);
+            if (from == to) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, edge));
+            }
+            IGRAPH_EIT_NEXT(eit);
+        }
+
+        igraph_eit_destroy(&eit);
+        igraph_es_destroy(&es);
+        IGRAPH_FINALLY_CLEAN(2);
+
+        if (igraph_vector_size(&edges) > 0) {
+            IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_vector(&edges)));
+        }
+
+        igraph_vector_destroy(&edges);
+        IGRAPH_FINALLY_CLEAN(1);
+
+        return IGRAPH_SUCCESS;
+    }
+
+    if (attr) {
+        IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges);
+    }
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+
+    IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_FROM));
+    IGRAPH_FINALLY(igraph_es_destroy, &es);
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+    for (actedge = -1; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
+        edge = IGRAPH_EIT_GET(eit);
+        from = IGRAPH_FROM(graph, edge);
+        to = IGRAPH_TO(graph, edge);
+
+        if (loops && from == to) {
+            /* Loop edge to be removed */
+            if (attr) {
+                VECTOR(mergeinto)[edge] = -1;
+            }
+        } else if (multiple && from == pfrom && to == pto) {
+            /* Multiple edge to be contracted */
+            if (attr) {
+                VECTOR(mergeinto)[edge] = actedge;
+            }
+        } else {
+            /* Edge to be kept */
+            igraph_vector_push_back(&edges, from);
+            igraph_vector_push_back(&edges, to);
+            if (attr) {
+                actedge++;
+                VECTOR(mergeinto)[edge] = actedge;
+            }
+        }
+        pfrom = from; pto = to;
+    }
+
+    igraph_eit_destroy(&eit);
+    igraph_es_destroy(&es);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_of_nodes,
+                               igraph_is_directed(graph)));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_FINALLY(igraph_destroy, &res);
+
+    IGRAPH_I_ATTRIBUTE_DESTROY(&res);
+    IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ 1,
+                            /*vertex=*/ 1, /*edge=*/ 0);
+
+    if (attr) {
+        igraph_fixed_vectorlist_t vl;
+        IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto,
+                     actedge + 1));
+        IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl);
+
+        IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &res, &vl.v,
+                     edge_comb));
+
+        igraph_fixed_vectorlist_destroy(&vl);
+        igraph_vector_destroy(&mergeinto);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+    igraph_destroy(graph);
+    *graph = res;
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_reciprocity
+ * \brief Calculates the reciprocity of a directed graph.
+ *
+ * </para><para>
+ * The measure of reciprocity defines the proportion of mutual
+ * connections, in a directed graph. It is most commonly defined as
+ * the probability that the opposite counterpart of a directed edge is
+ * also included in the graph. In adjacency matrix notation:
+ * <code>sum(i, j, (A.*A')ij) / sum(i, j, Aij)</code>, where
+ * <code>A.*A'</code> is the element-wise product of matrix
+ * <code>A</code> and its transpose. This measure is
+ * calculated if the \p mode argument is \c
+ * IGRAPH_RECIPROCITY_DEFAULT.
+ *
+ * </para><para>
+ * Prior to igraph version 0.6, another measure was implemented,
+ * defined as the probability of mutual connection between a vertex
+ * pair if we know that there is a (possibly non-mutual) connection
+ * between them. In other words, (unordered) vertex pairs are
+ * classified into three groups: (1) disconnected, (2)
+ * non-reciprocally connected, (3) reciprocally connected.
+ * The result is the size of group (3), divided by the sum of group
+ * sizes (2)+(3). This measure is calculated if \p mode is \c
+ * IGRAPH_RECIPROCITY_RATIO.
+ *
+ * \param graph The graph object.
+ * \param res Pointer to an \c igraph_real_t which will contain the result.
+ * \param ignore_loops Whether to ignore loop edges.
+ * \param mode Type of reciprocity to calculate, possible values are
+ *    \c IGRAPH_RECIPROCITY_DEFAULT and \c IGRAPH_RECIPROCITY_RATIO,
+ *    please see their description above.
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: graph has no edges
+ *         \c IGRAPH_ENOMEM: not enough memory for
+ *         temporary data.
+ *
+ * Time complexity: O(|V|+|E|), |V| is the number of vertices,
+ * |E| is the number of edges.
+ *
+ * \example examples/simple/igraph_reciprocity.c
+ */
+
+int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res,
+                       igraph_bool_t ignore_loops,
+                       igraph_reciprocity_t mode) {
+
+    igraph_integer_t nonrec = 0, rec = 0, loops = 0;
+    igraph_vector_t inneis, outneis;
+    long int i;
+    long int no_of_nodes = igraph_vcount(graph);
+
+    if (mode != IGRAPH_RECIPROCITY_DEFAULT &&
+        mode != IGRAPH_RECIPROCITY_RATIO) {
+        IGRAPH_ERROR("Invalid reciprocity type", IGRAPH_EINVAL);
+    }
+
+    /* THIS IS AN EXIT HERE !!!!!!!!!!!!!! */
+    if (!igraph_is_directed(graph)) {
+        *res = 1.0;
+        return 0;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        long int ip, op;
+        igraph_neighbors(graph, &inneis, (igraph_integer_t) i, IGRAPH_IN);
+        igraph_neighbors(graph, &outneis, (igraph_integer_t) i, IGRAPH_OUT);
+
+        ip = op = 0;
+        while (ip < igraph_vector_size(&inneis) &&
+               op < igraph_vector_size(&outneis)) {
+            if (VECTOR(inneis)[ip] < VECTOR(outneis)[op]) {
+                nonrec += 1;
+                ip++;
+            } else if (VECTOR(inneis)[ip] > VECTOR(outneis)[op]) {
+                nonrec += 1;
+                op++;
+            } else {
+
+                /* loop edge? */
+                if (VECTOR(inneis)[ip] == i) {
+                    loops += 1;
+                    if (!ignore_loops) {
+                        rec += 1;
+                    }
+                } else {
+                    rec += 1;
+                }
+
+                ip++;
+                op++;
+            }
+        }
+        nonrec += (igraph_vector_size(&inneis) - ip) +
+                  (igraph_vector_size(&outneis) - op);
+    }
+
+    if (mode == IGRAPH_RECIPROCITY_DEFAULT) {
+        if (ignore_loops) {
+            *res = (igraph_real_t) rec / (igraph_ecount(graph) - loops);
+        } else {
+            *res = (igraph_real_t) rec / (igraph_ecount(graph));
+        }
+    } else if (mode == IGRAPH_RECIPROCITY_RATIO) {
+        *res = (igraph_real_t) rec / (rec + nonrec);
+    }
+
+    igraph_vector_destroy(&inneis);
+    igraph_vector_destroy(&outneis);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_constraint
+ * \brief Burt's constraint scores.
+ *
+ * </para><para>
+ * This function calculates Burt's constraint scores for the given
+ * vertices, also known as structural holes.
+ *
+ * </para><para>
+ * Burt's constraint is higher if ego has less, or mutually stronger
+ * related (i.e. more redundant) contacts. Burt's measure of
+ * constraint, C[i], of vertex i's ego network V[i], is defined for
+ * directed and valued graphs,
+ * <blockquote><para>
+ * C[i] = sum( sum( (p[i,q] p[q,j])^2, q in V[i], q != i,j ), j in
+ * V[], j != i)
+ * </para></blockquote>
+ * for a graph of order (ie. number of vertices) N, where proportional
+ * tie strengths are defined as
+ * <blockquote><para>
+ * p[i,j]=(a[i,j]+a[j,i]) / sum(a[i,k]+a[k,i], k in V[i], k != i),
+ * </para></blockquote>
+ * a[i,j] are elements of A and
+ * the latter being the graph adjacency matrix. For isolated vertices,
+ * constraint is undefined.
+ *
+ * </para><para>
+ * Burt, R.S. (2004). Structural holes and good ideas. American
+ * Journal of Sociology 110, 349-399.
+ *
+ * </para><para>
+ * The first R version of this function was contributed by Jeroen
+ * Bruggeman.
+ * \param graph A graph object.
+ * \param res Pointer to an initialized vector, the result will be
+ *        stored here. The vector will be resized to have the
+ *        appropriate size for holding the result.
+ * \param vids Vertex selector containing the vertices for which the
+ *        constraint should be calculated.
+ * \param weights Vector giving the weights of the edges. If it is
+ *        \c NULL then each edge is supposed to have the same weight.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+E|+n*d^2), n is the number of vertices for
+ * which the constraint is calculated and d is the average degree, |V|
+ * is the number of vertices, |E| the number of edges in the
+ * graph. If the weights argument is \c NULL then the time complexity
+ * is O(|V|+n*d^2).
+ */
+
+int igraph_constraint(const igraph_t *graph, igraph_vector_t *res,
+                      igraph_vs_t vids, const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vit_t vit;
+    long int nodes_to_calc;
+    long int a, b, c, i, j, q;
+    igraph_integer_t edge, from, to, edge2, from2, to2;
+
+    igraph_vector_t contrib;
+    igraph_vector_t degree;
+    igraph_vector_t ineis_in, ineis_out, jneis_in, jneis_out;
+
+    if (weights != 0 && igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&contrib, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&ineis_in, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&ineis_out, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&jneis_in, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&jneis_out, 0);
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+
+    if (weights == 0) {
+        IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(),
+                                   IGRAPH_ALL, IGRAPH_NO_LOOPS));
+    } else {
+        for (a = 0; a < no_of_edges; a++) {
+            igraph_edge(graph, (igraph_integer_t) a, &from, &to);
+            if (from != to) {
+                VECTOR(degree)[(long int) from] += VECTOR(*weights)[a];
+                VECTOR(degree)[(long int) to  ] += VECTOR(*weights)[a];
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
+    igraph_vector_null(res);
+
+    for (a = 0; a < nodes_to_calc; a++, IGRAPH_VIT_NEXT(vit)) {
+        i = IGRAPH_VIT_GET(vit);
+
+        /* get neighbors of i */
+        IGRAPH_CHECK(igraph_incident(graph, &ineis_in, (igraph_integer_t) i,
+                                     IGRAPH_IN));
+        IGRAPH_CHECK(igraph_incident(graph, &ineis_out, (igraph_integer_t) i,
+                                     IGRAPH_OUT));
+
+        /* NaN for isolates */
+        if (igraph_vector_size(&ineis_in) == 0 &&
+            igraph_vector_size(&ineis_out) == 0) {
+            VECTOR(*res)[a] = IGRAPH_NAN;
+        }
+
+        /* zero their contribution */
+        for (b = 0; b < igraph_vector_size(&ineis_in); b++) {
+            edge = (igraph_integer_t) VECTOR(ineis_in)[b];
+            igraph_edge(graph, edge, &from, &to);
+            if (to == i) {
+                to = from;
+            }
+            j = to;
+            VECTOR(contrib)[j] = 0.0;
+        }
+        for (b = 0; b < igraph_vector_size(&ineis_out); b++) {
+            edge = (igraph_integer_t) VECTOR(ineis_out)[b];
+            igraph_edge(graph, edge, &from, &to);
+            if (to == i) {
+                to = from;
+            }
+            j = to;
+            VECTOR(contrib)[j] = 0.0;
+        }
+
+        /* add the direct contributions, in-neighbors and out-neighbors */
+        for (b = 0; b < igraph_vector_size(&ineis_in); b++) {
+            edge = (igraph_integer_t) VECTOR(ineis_in)[b];
+            igraph_edge(graph, edge, &from, &to);
+            if (to == i) {
+                to = from;
+            }
+            j = to;
+            if (i != j) {     /* excluding loops */
+                if (weights) {
+                    VECTOR(contrib)[j] +=
+                        VECTOR(*weights)[(long int)edge] / VECTOR(degree)[i];
+                } else {
+                    VECTOR(contrib)[j] += 1.0 / VECTOR(degree)[i];
+                }
+            }
+        }
+        if (igraph_is_directed(graph)) {
+            for (b = 0; b < igraph_vector_size(&ineis_out); b++) {
+                edge = (igraph_integer_t) VECTOR(ineis_out)[b];
+                igraph_edge(graph, edge, &from, &to);
+                if (to == i) {
+                    to = from;
+                }
+                j = to;
+                if (i != j) {
+                    if (weights) {
+                        VECTOR(contrib)[j] +=
+                            VECTOR(*weights)[(long int)edge] / VECTOR(degree)[i];
+                    } else {
+                        VECTOR(contrib)[j] += 1.0 / VECTOR(degree)[i];
+                    }
+                }
+            }
+        }
+
+        /* add the indirect contributions, in-in, in-out, out-in, out-out */
+        for (b = 0; b < igraph_vector_size(&ineis_in); b++) {
+            edge = (igraph_integer_t) VECTOR(ineis_in)[b];
+            igraph_edge(graph, edge, &from, &to);
+            if (to == i) {
+                to = from;
+            }
+            j = to;
+            if (i == j) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_incident(graph, &jneis_in, (igraph_integer_t) j,
+                                         IGRAPH_IN));
+            IGRAPH_CHECK(igraph_incident(graph, &jneis_out, (igraph_integer_t) j,
+                                         IGRAPH_OUT));
+            for (c = 0; c < igraph_vector_size(&jneis_in); c++) {
+                edge2 = (igraph_integer_t) VECTOR(jneis_in)[c];
+                igraph_edge(graph, edge2, &from2, &to2);
+                if (to2 == j) {
+                    to2 = from2;
+                }
+                q = to2;
+                if (j != q) {
+                    if (weights) {
+                        VECTOR(contrib)[q] +=
+                            VECTOR(*weights)[(long int)edge] *
+                            VECTOR(*weights)[(long int)edge2] /
+                            VECTOR(degree)[i] / VECTOR(degree)[j];
+                    } else {
+                        VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j];
+                    }
+                }
+            }
+            if (igraph_is_directed(graph)) {
+                for (c = 0; c < igraph_vector_size(&jneis_out); c++) {
+                    edge2 = (igraph_integer_t) VECTOR(jneis_out)[c];
+                    igraph_edge(graph, edge2, &from2, &to2);
+                    if (to2 == j) {
+                        to2 = from2;
+                    }
+                    q = to2;
+                    if (j != q) {
+                        if (weights) {
+                            VECTOR(contrib)[q] +=
+                                VECTOR(*weights)[(long int)edge] *
+                                VECTOR(*weights)[(long int)edge2] /
+                                VECTOR(degree)[i] / VECTOR(degree)[j];
+                        } else {
+                            VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j];
+                        }
+                    }
+                }
+            }
+        }
+        if (igraph_is_directed(graph)) {
+            for (b = 0; b < igraph_vector_size(&ineis_out); b++) {
+                edge = (igraph_integer_t) VECTOR(ineis_out)[b];
+                igraph_edge(graph, edge, &from, &to);
+                if (to == i) {
+                    to = from;
+                }
+                j = to;
+                if (i == j) {
+                    continue;
+                }
+                IGRAPH_CHECK(igraph_incident(graph, &jneis_in, (igraph_integer_t) j,
+                                             IGRAPH_IN));
+                IGRAPH_CHECK(igraph_incident(graph, &jneis_out, (igraph_integer_t) j,
+                                             IGRAPH_OUT));
+                for (c = 0; c < igraph_vector_size(&jneis_in); c++) {
+                    edge2 = (igraph_integer_t) VECTOR(jneis_in)[c];
+                    igraph_edge(graph, edge2, &from2, &to2);
+                    if (to2 == j) {
+                        to2 = from2;
+                    }
+                    q = to2;
+                    if (j != q) {
+                        if (weights) {
+                            VECTOR(contrib)[q] +=
+                                VECTOR(*weights)[(long int)edge] *
+                                VECTOR(*weights)[(long int)edge2] /
+                                VECTOR(degree)[i] / VECTOR(degree)[j];
+                        } else {
+                            VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j];
+                        }
+                    }
+                }
+                for (c = 0; c < igraph_vector_size(&jneis_out); c++) {
+                    edge2 = (igraph_integer_t) VECTOR(jneis_out)[c];
+                    igraph_edge(graph, edge2, &from2, &to2);
+                    if (to2 == j) {
+                        to2 = from2;
+                    }
+                    q = to2;
+                    if (j != q) {
+                        if (weights) {
+                            VECTOR(contrib)[q] +=
+                                VECTOR(*weights)[(long int)edge] *
+                                VECTOR(*weights)[(long int)edge2] /
+                                VECTOR(degree)[i] / VECTOR(degree)[j];
+                        } else {
+                            VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j];
+                        }
+                    }
+                }
+            }
+        }
+
+        /* squared sum of the contributions */
+        for (b = 0; b < igraph_vector_size(&ineis_in); b++) {
+            edge = (igraph_integer_t) VECTOR(ineis_in)[b];
+            igraph_edge(graph, edge, &from, &to);
+            if (to == i) {
+                to = from;
+            }
+            j = to;
+            if (i == j) {
+                continue;
+            }
+            VECTOR(*res)[a] += VECTOR(contrib)[j] * VECTOR(contrib)[j];
+            VECTOR(contrib)[j] = 0.0;
+        }
+        if (igraph_is_directed(graph)) {
+            for (b = 0; b < igraph_vector_size(&ineis_out); b++) {
+                edge = (igraph_integer_t) VECTOR(ineis_out)[b];
+                igraph_edge(graph, edge, &from, &to);
+                if (to == i) {
+                    to = from;
+                }
+                j = to;
+                if (i == j) {
+                    continue;
+                }
+                VECTOR(*res)[a] += VECTOR(contrib)[j] * VECTOR(contrib)[j];
+                VECTOR(contrib)[j] = 0.0;
+            }
+        }
+    }
+
+    igraph_vit_destroy(&vit);
+    igraph_vector_destroy(&jneis_out);
+    igraph_vector_destroy(&jneis_in);
+    igraph_vector_destroy(&ineis_out);
+    igraph_vector_destroy(&ineis_in);
+    igraph_vector_destroy(&degree);
+    igraph_vector_destroy(&contrib);
+    IGRAPH_FINALLY_CLEAN(7);
+
+    return 0;
+}
+
+/**
+ * \function igraph_maxdegree
+ * \brief Calculate the maximum degree in a graph (or set of vertices).
+ *
+ * </para><para>
+ * The largest in-, out- or total degree of the specified vertices is
+ * calculated.
+ * \param graph The input graph.
+ * \param res Pointer to an integer (\c igraph_integer_t), the result
+ *        will be stored here.
+ * \param vids Vector giving the vertex IDs for which the maximum degree will
+ *        be calculated.
+ * \param mode Defines the type of the degree.
+ *        \c IGRAPH_OUT, out-degree,
+ *        \c IGRAPH_IN, in-degree,
+ *        \c IGRAPH_ALL, total degree (sum of the
+ *        in- and out-degree).
+ *        This parameter is ignored for undirected graphs.
+ * \param loops Boolean, gives whether the self-loops should be
+ *        counted.
+ * \return Error code:
+ *         \c IGRAPH_EINVVID: invalid vertex id.
+ *         \c IGRAPH_EINVMODE: invalid mode argument.
+ *
+ * Time complexity: O(v) if
+ * loops is
+ * TRUE, and
+ * O(v*d)
+ * otherwise. v is the number
+ * vertices for which the degree will be calculated, and
+ * d is their (average) degree.
+ */
+
+int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res,
+                     igraph_vs_t vids, igraph_neimode_t mode,
+                     igraph_bool_t loops) {
+
+    igraph_vector_t tmp;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+
+    igraph_degree(graph, &tmp, vids, mode, loops);
+    *res = (igraph_integer_t) igraph_vector_max(&tmp);
+
+    igraph_vector_destroy(&tmp);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_density
+ * Calculate the density of a graph.
+ *
+ * </para><para>The density of a graph is simply the ratio number of
+ * edges and the number of possible edges. Note that density is
+ * ill-defined for graphs with multiple and/or loop edges, so consider
+ * calling \ref igraph_simplify() on the graph if you know that it
+ * contains multiple or loop edges.
+ * \param graph The input graph object.
+ * \param res Pointer to a real number, the result will be stored
+ *   here.
+ * \param loops Logical constant, whether to include loops in the
+ *   calculation. If this constant is TRUE then
+ *   loop edges are thought to be possible in the graph (this does not
+ *   necessarily mean that the graph really contains any loops). If
+ *   this is FALSE then the result is only correct if the graph does not
+ *   contain loops.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_density(const igraph_t *graph, igraph_real_t *res,
+                   igraph_bool_t loops) {
+
+    igraph_integer_t no_of_nodes = igraph_vcount(graph);
+    igraph_real_t no_of_edges = igraph_ecount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+
+    if (no_of_nodes == 0) {
+        *res = IGRAPH_NAN;
+        return 0;
+    }
+
+    if (!loops) {
+        if (no_of_nodes == 1) {
+            *res = IGRAPH_NAN;
+        } else if (directed) {
+            *res = no_of_edges / no_of_nodes / (no_of_nodes - 1);
+        } else {
+            *res = no_of_edges / no_of_nodes * 2.0 / (no_of_nodes - 1);
+        }
+    } else {
+        if (directed) {
+            *res = no_of_edges / no_of_nodes / no_of_nodes;
+        } else {
+            *res = no_of_edges / no_of_nodes * 2.0 / (no_of_nodes + 1);
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_neighborhood_size
+ * \brief Calculates the size of the neighborhood of a given vertex.
+ *
+ * The neighborhood of a given order of a vertex includes all vertices
+ * which are closer to the vertex than the order. Ie. order 0 is
+ * always the vertex itself, order 1 is the vertex plus its immediate
+ * neighbors, order 2 is order 1 plus the immediate neighbors of the
+ * vertices in order 1, etc.
+ *
+ * </para><para> This function calculates the size of the neighborhood
+ * of the given order for the given vertices.
+ * \param graph The input graph.
+ * \param res Pointer to an initialized vector, the result will be
+ *    stored here. It will be resized as needed.
+ * \param vids The vertices for which the calculation is performed.
+ * \param order Integer giving the order of the neighborhood.
+ * \param mode Specifies how to use the direction of the edges if a
+ *   directed graph is analyzed. For \c IGRAPH_OUT only the outgoing
+ *   edges are followed, so all vertices reachable from the source
+ *   vertex in at most \c order steps are counted. For \c IGRAPH_IN
+ *   all vertices from which the source vertex is reachable in at most
+ *   \c order steps are counted. \c IGRAPH_ALL ignores the direction
+ *   of the edges. This argument is ignored for undirected graphs.
+ * \param mindist The minimum distance to include a vertex in the counting.
+ *   If this is one, then the starting vertex is not counted. If this is
+ *   two, then its neighbors are not counted, either, etc.
+ * \return Error code.
+ *
+ * \sa \ref igraph_neighborhood() for calculating the actual neighborhood,
+ * \ref igraph_neighborhood_graphs() for creating separate graphs from
+ * the neighborhoods.
+ *
+ * Time complexity: O(n*d*o), where n is the number vertices for which
+ * the calculation is performed, d is the average degree, o is the order.
+ */
+
+int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res,
+                             igraph_vs_t vids, igraph_integer_t order,
+                             igraph_neimode_t mode,
+                             igraph_integer_t mindist) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_dqueue_t q;
+    igraph_vit_t vit;
+    long int i, j;
+    long int *added;
+    igraph_vector_t neis;
+
+    if (order < 0) {
+        IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL);
+    }
+
+    if (mindist < 0 || mindist > order) {
+        IGRAPH_ERROR("Minimum distance should be between zero and order",
+                     IGRAPH_EINVAL);
+    }
+
+    added = igraph_Calloc(no_of_nodes, long int);
+    if (added == 0) {
+        IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit)));
+
+    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        long int node = IGRAPH_VIT_GET(vit);
+        long int size = mindist == 0 ? 1 : 0;
+        added[node] = i + 1;
+        igraph_dqueue_clear(&q);
+        if (order > 0) {
+            igraph_dqueue_push(&q, node);
+            igraph_dqueue_push(&q, 0);
+        }
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            long int actdist = (long int) igraph_dqueue_pop(&q);
+            long int n;
+            igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode);
+            n = igraph_vector_size(&neis);
+
+            if (actdist < order - 1) {
+                /* we add them to the q */
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (added[nei] != i + 1) {
+                        added[nei] = i + 1;
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, nei));
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+                        if (actdist + 1 >= mindist) {
+                            size++;
+                        }
+                    }
+                }
+            } else {
+                /* we just count them, but don't add them */
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (added[nei] != i + 1) {
+                        added[nei] = i + 1;
+                        if (actdist + 1 >= mindist) {
+                            size++;
+                        }
+                    }
+                }
+            }
+
+        } /* while q not empty */
+
+        VECTOR(*res)[i] = size;
+    } /* for VIT, i */
+
+    igraph_vector_destroy(&neis);
+    igraph_vit_destroy(&vit);
+    igraph_dqueue_destroy(&q);
+    igraph_Free(added);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/**
+ * \function igraph_neighborhood
+ * Calculate the neighborhood of vertices.
+ *
+ * The neighborhood of a given order of a vertex includes all vertices
+ * which are closer to the vertex than the order. Ie. order 0 is
+ * always the vertex itself, order 1 is the vertex plus its immediate
+ * neighbors, order 2 is order 1 plus the immediate neighbors of the
+ * vertices in order 1, etc.
+ *
+ * </para><para> This function calculates the vertices within the
+ * neighborhood of the specified vertices.
+ * \param graph The input graph.
+ * \param res An initialized pointer vector. Note that the objects
+ *    (pointers) in the vector will \em not be freed, but the pointer
+ *    vector will be resized as needed. The result of the calculation
+ *    will be stored here in \c vector_t objects.
+ * \param vids The vertices for which the calculation is performed.
+ * \param order Integer giving the order of the neighborhood.
+ * \param mode Specifies how to use the direction of the edges if a
+ *   directed graph is analyzed. For \c IGRAPH_OUT only the outgoing
+ *   edges are followed, so all vertices reachable from the source
+ *   vertex in at most \c order steps are included. For \c IGRAPH_IN
+ *   all vertices from which the source vertex is reachable in at most
+ *   \c order steps are included. \c IGRAPH_ALL ignores the direction
+ *   of the edges. This argument is ignored for undirected graphs.
+ * \param mindist The minimum distance to include a vertex in the counting.
+ *   If this is one, then the starting vertex is not counted. If this is
+ *   two, then its neighbors are not counted, either, etc.
+ * \return Error code.
+ *
+ * \sa \ref igraph_neighborhood_size() to calculate the size of the
+ * neighborhood, \ref igraph_neighborhood_graphs() for creating
+ * graphs from the neighborhoods.
+ *
+ * Time complexity: O(n*d*o), n is the number of vertices for which
+ * the calculation is performed, d is the average degree, o is the
+ * order.
+ */
+
+int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res,
+                        igraph_vs_t vids, igraph_integer_t order,
+                        igraph_neimode_t mode, igraph_integer_t mindist) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_dqueue_t q;
+    igraph_vit_t vit;
+    long int i, j;
+    long int *added;
+    igraph_vector_t neis;
+    igraph_vector_t tmp;
+    igraph_vector_t *newv;
+
+    if (order < 0) {
+        IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL);
+    }
+
+    if (mindist < 0 || mindist > order) {
+        IGRAPH_ERROR("Minimum distance should be between zero and order",
+                     IGRAPH_EINVAL);
+    }
+
+    added = igraph_Calloc(no_of_nodes, long int);
+    if (added == 0) {
+        IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+    IGRAPH_CHECK(igraph_vector_ptr_resize(res, IGRAPH_VIT_SIZE(vit)));
+
+    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        long int node = IGRAPH_VIT_GET(vit);
+        added[node] = i + 1;
+        igraph_vector_clear(&tmp);
+        if (mindist == 0) {
+            IGRAPH_CHECK(igraph_vector_push_back(&tmp, node));
+        }
+        if (order > 0) {
+            igraph_dqueue_push(&q, node);
+            igraph_dqueue_push(&q, 0);
+        }
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            long int actdist = (long int) igraph_dqueue_pop(&q);
+            long int n;
+            igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode);
+            n = igraph_vector_size(&neis);
+
+            if (actdist < order - 1) {
+                /* we add them to the q */
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (added[nei] != i + 1) {
+                        added[nei] = i + 1;
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, nei));
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+                        if (actdist + 1 >= mindist) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei));
+                        }
+                    }
+                }
+            } else {
+                /* we just count them but don't add them to q */
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (added[nei] != i + 1) {
+                        added[nei] = i + 1;
+                        if (actdist + 1 >= mindist) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei));
+                        }
+                    }
+                }
+            }
+
+        } /* while q not empty */
+
+        newv = igraph_Calloc(1, igraph_vector_t);
+        if (newv == 0) {
+            IGRAPH_ERROR("Cannot calculate neighborhood", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, newv);
+        IGRAPH_CHECK(igraph_vector_copy(newv, &tmp));
+        VECTOR(*res)[i] = newv;
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&tmp);
+    igraph_vector_destroy(&neis);
+    igraph_vit_destroy(&vit);
+    igraph_dqueue_destroy(&q);
+    igraph_Free(added);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \function igraph_neighborhood_graphs
+ * Create graphs from the neighborhood(s) of some vertex/vertices.
+ *
+ * The neighborhood of a given order of a vertex includes all vertices
+ * which are closer to the vertex than the order. Ie. order 0 is
+ * always the vertex itself, order 1 is the vertex plus its immediate
+ * neighbors, order 2 is order 1 plus the immediate neighbors of the
+ * vertices in order 1, etc.
+ *
+ * </para><para> This function finds every vertex in the neighborhood
+ * of a given parameter vertex and creates a graph from these
+ * vertices.
+ *
+ * </para><para> The first version of this function was written by
+ * Vincent Matossian, thanks Vincent.
+ * \param graph The input graph.
+ * \param res Pointer to a pointer vector, the result will be stored
+ *   here, ie. \c res will contain pointers to \c igraph_t
+ *   objects. It will be resized if needed but note that the
+ *   objects in the pointer vector will not be freed.
+ * \param vids The vertices for which the calculation is performed.
+ * \param order Integer giving the order of the neighborhood.
+ * \param mode Specifies how to use the direction of the edges if a
+ *   directed graph is analyzed. For \c IGRAPH_OUT only the outgoing
+ *   edges are followed, so all vertices reachable from the source
+ *   vertex in at most \c order steps are counted. For \c IGRAPH_IN
+ *   all vertices from which the source vertex is reachable in at most
+ *   \c order steps are counted. \c IGRAPH_ALL ignores the direction
+ *   of the edges. This argument is ignored for undirected graphs.
+ * \param mindist The minimum distance to include a vertex in the counting.
+ *   If this is one, then the starting vertex is not counted. If this is
+ *   two, then its neighbors are not counted, either, etc.
+ * \return Error code.
+ *
+ * \sa \ref igraph_neighborhood_size() for calculating the neighborhood
+ * sizes only, \ref igraph_neighborhood() for calculating the
+ * neighborhoods (but not creating graphs).
+ *
+ * Time complexity: O(n*(|V|+|E|)), where n is the number vertices for
+ * which the calculation is performed, |V| and |E| are the number of
+ * vertices and edges in the original input graph.
+ */
+
+int igraph_neighborhood_graphs(const igraph_t *graph, igraph_vector_ptr_t *res,
+                               igraph_vs_t vids, igraph_integer_t order,
+                               igraph_neimode_t mode,
+                               igraph_integer_t mindist) {
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_dqueue_t q;
+    igraph_vit_t vit;
+    long int i, j;
+    long int *added;
+    igraph_vector_t neis;
+    igraph_vector_t tmp;
+    igraph_t *newg;
+
+    if (order < 0) {
+        IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL);
+    }
+
+    if (mindist < 0 || mindist > order) {
+        IGRAPH_ERROR("Minimum distance should be between zero and order",
+                     IGRAPH_EINVAL);
+    }
+
+    added = igraph_Calloc(no_of_nodes, long int);
+    if (added == 0) {
+        IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+    IGRAPH_CHECK(igraph_vector_ptr_resize(res, IGRAPH_VIT_SIZE(vit)));
+
+    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        long int node = IGRAPH_VIT_GET(vit);
+        added[node] = i + 1;
+        igraph_vector_clear(&tmp);
+        if (mindist == 0) {
+            IGRAPH_CHECK(igraph_vector_push_back(&tmp, node));
+        }
+        if (order > 0) {
+            igraph_dqueue_push(&q, node);
+            igraph_dqueue_push(&q, 0);
+        }
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            long int actdist = (long int) igraph_dqueue_pop(&q);
+            long int n;
+            igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode);
+            n = igraph_vector_size(&neis);
+
+            if (actdist < order - 1) {
+                /* we add them to the q */
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (added[nei] != i + 1) {
+                        added[nei] = i + 1;
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, nei));
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+                        if (actdist + 1 >= mindist) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei));
+                        }
+                    }
+                }
+            } else {
+                /* we just count them but don't add them to q */
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (added[nei] != i + 1) {
+                        added[nei] = i + 1;
+                        if (actdist + 1 >= mindist) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei));
+                        }
+                    }
+                }
+            }
+
+        } /* while q not empty */
+
+        newg = igraph_Calloc(1, igraph_t);
+        if (newg == 0) {
+            IGRAPH_ERROR("Cannot create neighborhood graph", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, newg);
+        if (igraph_vector_size(&tmp) < no_of_nodes) {
+            IGRAPH_CHECK(igraph_induced_subgraph(graph, newg,
+                                                 igraph_vss_vector(&tmp),
+                                                 IGRAPH_SUBGRAPH_AUTO));
+        } else {
+            IGRAPH_CHECK(igraph_copy(newg, graph));
+        }
+        VECTOR(*res)[i] = newg;
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&tmp);
+    igraph_vector_destroy(&neis);
+    igraph_vit_destroy(&vit);
+    igraph_dqueue_destroy(&q);
+    igraph_Free(added);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \function igraph_topological_sorting
+ * \brief Calculate a possible topological sorting of the graph.
+ *
+ * </para><para>
+ * A topological sorting of a directed acyclic graph is a linear ordering
+ * of its nodes where each node comes before all nodes to which it has
+ * edges. Every DAG has at least one topological sort, and may have many.
+ * This function returns a possible topological sort among them. If the
+ * graph is not acyclic (it has at least one cycle), a partial topological
+ * sort is returned and a warning is issued.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a vector, the result will be stored here.
+ *   It will be resized if needed.
+ * \param mode Specifies how to use the direction of the edges.
+ *   For \c IGRAPH_OUT, the sorting order ensures that each node comes
+ *   before all nodes to which it has edges, so nodes with no incoming
+ *   edges go first. For \c IGRAPH_IN, it is quite the opposite: each
+ *   node comes before all nodes from which it receives edges. Nodes
+ *   with no outgoing edges go first.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), where |V| and |E| are the number of
+ * vertices and edges in the original input graph.
+ *
+ * \sa \ref igraph_is_dag() if you are only interested in whether a given
+ *     graph is a DAG or not, or \ref igraph_feedback_arc_set() to find a
+ *     set of edges whose removal makes the graph a DAG.
+ *
+ * \example examples/simple/igraph_topological_sorting.c
+ */
+int igraph_topological_sorting(const igraph_t* graph, igraph_vector_t *res,
+                               igraph_neimode_t mode) {
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t degrees, neis;
+    igraph_dqueue_t sources;
+    igraph_neimode_t deg_mode;
+    long int node, i, j;
+
+    if (mode == IGRAPH_ALL || !igraph_is_directed(graph)) {
+        IGRAPH_ERROR("topological sorting does not make sense for undirected graphs", IGRAPH_EINVAL);
+    } else if (mode == IGRAPH_OUT) {
+        deg_mode = IGRAPH_IN;
+    } else if (mode == IGRAPH_IN) {
+        deg_mode = IGRAPH_OUT;
+    } else {
+        IGRAPH_ERROR("invalid mode", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&degrees, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_dqueue_init(&sources, 0));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &sources);
+    IGRAPH_CHECK(igraph_degree(graph, &degrees, igraph_vss_all(), deg_mode, 0));
+
+    igraph_vector_clear(res);
+
+    /* Do we have nodes with no incoming vertices? */
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(degrees)[i] == 0) {
+            IGRAPH_CHECK(igraph_dqueue_push(&sources, i));
+        }
+    }
+
+    /* Take all nodes with no incoming vertices and remove them */
+    while (!igraph_dqueue_empty(&sources)) {
+        igraph_real_t tmp = igraph_dqueue_pop(&sources); node = (long) tmp;
+        /* Add the node to the result vector */
+        igraph_vector_push_back(res, node);
+        /* Exclude the node from further source searches */
+        VECTOR(degrees)[node] = -1;
+        /* Get the neighbors and decrease their degrees by one */
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) node, mode));
+        j = igraph_vector_size(&neis);
+        for (i = 0; i < j; i++) {
+            VECTOR(degrees)[(long)VECTOR(neis)[i]]--;
+            if (VECTOR(degrees)[(long)VECTOR(neis)[i]] == 0) {
+                IGRAPH_CHECK(igraph_dqueue_push(&sources, VECTOR(neis)[i]));
+            }
+        }
+    }
+
+    if (igraph_vector_size(res) < no_of_nodes) {
+        IGRAPH_WARNING("graph contains a cycle, partial result is returned");
+    }
+
+    igraph_vector_destroy(&degrees);
+    igraph_vector_destroy(&neis);
+    igraph_dqueue_destroy(&sources);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_is_dag
+ * Checks whether a graph is a directed acyclic graph (DAG) or not.
+ *
+ * </para><para>
+ * A directed acyclic graph (DAG) is a directed graph with no cycles.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a boolean constant, the result
+ *     is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), where |V| and |E| are the number of
+ * vertices and edges in the original input graph.
+ *
+ * \sa \ref igraph_topological_sorting() to get a possible topological
+ *     sorting of a DAG.
+ */
+int igraph_is_dag(const igraph_t* graph, igraph_bool_t *res) {
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t degrees, neis;
+    igraph_dqueue_t sources;
+    long int node, i, j, nei, vertices_left;
+
+    if (!igraph_is_directed(graph)) {
+        *res = 0;
+        return IGRAPH_SUCCESS;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&degrees, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_dqueue_init(&sources, 0));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &sources);
+    IGRAPH_CHECK(igraph_degree(graph, &degrees, igraph_vss_all(), IGRAPH_OUT, 1));
+
+    vertices_left = no_of_nodes;
+
+    /* Do we have nodes with no incoming edges? */
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(degrees)[i] == 0) {
+            IGRAPH_CHECK(igraph_dqueue_push(&sources, i));
+        }
+    }
+
+    /* Take all nodes with no incoming edges and remove them */
+    while (!igraph_dqueue_empty(&sources)) {
+        igraph_real_t tmp = igraph_dqueue_pop(&sources); node = (long) tmp;
+        /* Exclude the node from further source searches */
+        VECTOR(degrees)[node] = -1;
+        vertices_left--;
+        /* Get the neighbors and decrease their degrees by one */
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) node,
+                                      IGRAPH_IN));
+        j = igraph_vector_size(&neis);
+        for (i = 0; i < j; i++) {
+            nei = (long)VECTOR(neis)[i];
+            if (nei == node) {
+                continue;
+            }
+            VECTOR(degrees)[nei]--;
+            if (VECTOR(degrees)[nei] == 0) {
+                IGRAPH_CHECK(igraph_dqueue_push(&sources, nei));
+            }
+        }
+    }
+
+    *res = (vertices_left == 0);
+    if (vertices_left < 0) {
+        IGRAPH_WARNING("vertices_left < 0 in igraph_is_dag, possible bug");
+    }
+
+    igraph_vector_destroy(&degrees);
+    igraph_vector_destroy(&neis);
+    igraph_dqueue_destroy(&sources);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_is_simple
+ * \brief Decides whether the input graph is a simple graph.
+ *
+ * </para><para>
+ * A graph is a simple graph if it does not contain loop edges and
+ * multiple edges.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a boolean constant, the result
+ *     is stored here.
+ * \return Error code.
+ *
+ * \sa \ref igraph_is_loop() and \ref igraph_is_multiple() to
+ * find the loops and multiple edges, \ref igraph_simplify() to
+ * get rid of them, or \ref igraph_has_multiple() to decide whether
+ * there is at least one multiple edge.
+ *
+ * Time complexity: O(|V|+|E|).
+ */
+
+int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) {
+    long int vc = igraph_vcount(graph);
+    long int ec = igraph_ecount(graph);
+
+    if (vc == 0 || ec == 0) {
+        *res = 1;
+    } else {
+        igraph_vector_t neis;
+        long int i, j, n;
+        igraph_bool_t found = 0;
+        IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+        for (i = 0; i < vc; i++) {
+            igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_OUT);
+            n = igraph_vector_size(&neis);
+            for (j = 0; j < n; j++) {
+                if (VECTOR(neis)[j] == i) {
+                    found = 1; break;
+                }
+                if (j > 0 && VECTOR(neis)[j - 1] == VECTOR(neis)[j]) {
+                    found = 1; break;
+                }
+            }
+        }
+        *res = !found;
+        igraph_vector_destroy(&neis);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_has_loop
+ * \brief Returns whether the graph has at least one loop edge.
+ *
+ * </para><para>
+ * A loop edge is an edge from a vertex to itself.
+ * \param graph The input graph.
+ * \param res Pointer to an initialized boolean vector for storing the result.
+ *
+ * \sa \ref igraph_simplify() to get rid of loop edges.
+ *
+ * Time complexity: O(e), the number of edges to check.
+ *
+ * \example examples/simple/igraph_has_loop.c
+ */
+
+int igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) {
+    long int i, m = igraph_ecount(graph);
+
+    *res = 0;
+
+    for (i = 0; i < m; i++) {
+        if (IGRAPH_FROM(graph, i) == IGRAPH_TO(graph, i)) {
+            *res = 1;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_is_loop
+ * \brief Find the loop edges in a graph.
+ *
+ * </para><para>
+ * A loop edge is an edge from a vertex to itself.
+ * \param graph The input graph.
+ * \param res Pointer to an initialized boolean vector for storing the result,
+ *         it will be resized as needed.
+ * \param es The edges to check, for all edges supply \ref igraph_ess_all() here.
+ * \return Error code.
+ *
+ * \sa \ref igraph_simplify() to get rid of loop edges.
+ *
+ * Time complexity: O(e), the number of edges to check.
+ *
+ * \example examples/simple/igraph_is_loop.c
+ */
+
+int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res,
+                   igraph_es_t es) {
+    igraph_eit_t eit;
+    long int i;
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+    IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit)));
+
+    for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) {
+        long int e = IGRAPH_EIT_GET(eit);
+        VECTOR(*res)[i] = (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)) ? 1 : 0;
+    }
+
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_has_multiple
+ * \brief Check whether the graph has at least one multiple edge.
+ *
+ * </para><para>
+ * An edge is a multiple edge if there is another
+ * edge with the same head and tail vertices in the graph.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to a boolean variable, the result will be stored here.
+ * \return Error code.
+ *
+ * \sa \ref igraph_count_multiple(), \ref igraph_is_multiple() and \ref igraph_simplify().
+ *
+ * Time complexity: O(e*d), e is the number of edges to check and d is the
+ * average degree (out-degree in directed graphs) of the vertices at the
+ * tail of the edges.
+ *
+ * \example examples/simple/igraph_has_multiple.c
+ */
+
+int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) {
+    long int vc = igraph_vcount(graph);
+    long int ec = igraph_ecount(graph);
+    igraph_bool_t directed = igraph_is_directed(graph);
+
+    if (vc == 0 || ec == 0) {
+        *res = 0;
+    } else {
+        igraph_vector_t neis;
+        long int i, j, n;
+        igraph_bool_t found = 0;
+        IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+        for (i = 0; i < vc && !found; i++) {
+            IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i,
+                                          IGRAPH_OUT));
+            n = igraph_vector_size(&neis);
+            for (j = 1; j < n; j++) {
+                if (VECTOR(neis)[j - 1] == VECTOR(neis)[j]) {
+                    /* If the graph is undirected, loop edges appear twice in the neighbor
+                     * list, so check the next item as well */
+                    if (directed) {
+                        /* Directed, so this is a real multiple edge */
+                        found = 1; break;
+                    } else if (VECTOR(neis)[j - 1] != i) {
+                        /* Undirected, but not a loop edge */
+                        found = 1; break;
+                    } else if (j < n - 1 && VECTOR(neis)[j] == VECTOR(neis)[j + 1]) {
+                        /* Undirected, loop edge, multiple times */
+                        found = 1; break;
+                    }
+                }
+            }
+        }
+        *res = found;
+        igraph_vector_destroy(&neis);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_is_multiple
+ * \brief Find the multiple edges in a graph.
+ *
+ * </para><para>
+ * An edge is a multiple edge if there is another
+ * edge with the same head and tail vertices in the graph.
+ *
+ * </para><para>
+ * Note that this function returns true only for the second or more
+ * appearances of the multiple edges.
+ * \param graph The input graph.
+ * \param res Pointer to a boolean vector, the result will be stored
+ *        here. It will be resized as needed.
+ * \param es The edges to check. Supply \ref igraph_ess_all() if you want
+ *        to check all edges.
+ * \return Error code.
+ *
+ * \sa \ref igraph_count_multiple(), \ref igraph_has_multiple() and \ref igraph_simplify().
+ *
+ * Time complexity: O(e*d), e is the number of edges to check and d is the
+ * average degree (out-degree in directed graphs) of the vertices at the
+ * tail of the edges.
+ *
+ * \example examples/simple/igraph_is_multiple.c
+ */
+
+int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res,
+                       igraph_es_t es) {
+    igraph_eit_t eit;
+    long int i;
+    igraph_lazy_inclist_t inclist;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+    IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
+
+    IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit)));
+
+    for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) {
+        long int e = IGRAPH_EIT_GET(eit);
+        long int from = IGRAPH_FROM(graph, e);
+        long int to = IGRAPH_TO(graph, e);
+        igraph_vector_t *neis = igraph_lazy_inclist_get(&inclist,
+                                (igraph_integer_t) from);
+        long int j, n = igraph_vector_size(neis);
+        VECTOR(*res)[i] = 0;
+        for (j = 0; j < n; j++) {
+            long int e2 = (long int) VECTOR(*neis)[j];
+            long int to2 = IGRAPH_OTHER(graph, e2, from);
+            if (to2 == to && e2 < e) {
+                VECTOR(*res)[i] = 1;
+            }
+        }
+    }
+
+    igraph_lazy_inclist_destroy(&inclist);
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_count_multiple
+ * \brief Count the number of appearances of the edges in a graph.
+ *
+ * </para><para>
+ * If the graph has no multiple edges then the result vector will be
+ * filled with ones.
+ * (An edge is a multiple edge if there is another
+ * edge with the same head and tail vertices in the graph.)
+ *
+ * </para><para>
+ * \param graph The input graph.
+ * \param res Pointer to a vector, the result will be stored
+ *        here. It will be resized as needed.
+ * \param es The edges to check. Supply \ref igraph_ess_all() if you want
+ *        to check all edges.
+ * \return Error code.
+ *
+ * \sa \ref igraph_is_multiple() and \ref igraph_simplify().
+ *
+ * Time complexity: O(e*d), e is the number of edges to check and d is the
+ * average degree (out-degree in directed graphs) of the vertices at the
+ * tail of the edges.
+ */
+
+
+int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es) {
+    igraph_eit_t eit;
+    long int i;
+    igraph_lazy_inclist_t inclist;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+    IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_OUT));
+    IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit)));
+
+    for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) {
+        long int e = IGRAPH_EIT_GET(eit);
+        long int from = IGRAPH_FROM(graph, e);
+        long int to = IGRAPH_TO(graph, e);
+        igraph_vector_t *neis = igraph_lazy_inclist_get(&inclist,
+                                (igraph_integer_t) from);
+        long int j, n = igraph_vector_size(neis);
+        VECTOR(*res)[i] = 0;
+        for (j = 0; j < n; j++) {
+            long int e2 = (long int) VECTOR(*neis)[j];
+            long int to2 = IGRAPH_OTHER(graph, e2, from);
+            if (to2 == to) {
+                VECTOR(*res)[i] += 1;
+            }
+        }
+        /* for loop edges, divide the result by two */
+        if (to == from) {
+            VECTOR(*res)[i] /= 2;
+        }
+    }
+
+    igraph_lazy_inclist_destroy(&inclist);
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+/**
+ * \function igraph_girth
+ * \brief The girth of a graph is the length of the shortest circle in it.
+ *
+ * </para><para>
+ * The current implementation works for undirected graphs only,
+ * directed graphs are treated as undirected graphs. Loop edges and
+ * multiple edges are ignored.
+ * </para><para>
+ * If the graph is a forest (ie. acyclic), then zero is returned.
+ * </para><para>
+ * This implementation is based on Alon Itai and Michael Rodeh:
+ * Finding a minimum circuit in a graph
+ * \emb Proceedings of the ninth annual ACM symposium on Theory of
+ * computing \eme, 1-10, 1977. The first implementation of this
+ * function was done by Keith Briggs, thanks Keith.
+ * \param graph The input graph.
+ * \param girth Pointer to an integer, if not \c NULL then the result
+ *     will be stored here.
+ * \param circle Pointer to an initialized vector, the vertex ids in
+ *     the shortest circle will be stored here. If \c NULL then it is
+ *     ignored.
+ * \return Error code.
+ *
+ * Time complexity: O((|V|+|E|)^2), |V| is the number of vertices, |E|
+ * is the number of edges in the general case. If the graph has no
+ * circles at all then the function needs O(|V|+|E|) time to realize
+ * this and then it stops.
+ *
+ * \example examples/simple/igraph_girth.c
+ */
+
+int igraph_girth(const igraph_t *graph, igraph_integer_t *girth,
+                 igraph_vector_t *circle) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_dqueue_t q;
+    igraph_lazy_adjlist_t adjlist;
+    long int mincirc = LONG_MAX, minvertex = 0;
+    long int node;
+    igraph_bool_t triangle = 0;
+    igraph_vector_t *neis;
+    igraph_vector_long_t level;
+    long int stoplevel = no_of_nodes + 1;
+    igraph_bool_t anycircle = 0;
+    long int t1 = 0, t2 = 0;
+
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    IGRAPH_CHECK(igraph_vector_long_init(&level, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &level);
+
+    for (node = 0; !triangle && node < no_of_nodes; node++) {
+
+        /* Are there circles in this graph at all? */
+        if (node == 1 && anycircle == 0) {
+            igraph_bool_t conn;
+            IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
+            if (conn) {
+                /* No, there are none */
+                break;
+            }
+        }
+
+        anycircle = 0;
+        igraph_dqueue_clear(&q);
+        igraph_vector_long_null(&level);
+        IGRAPH_CHECK(igraph_dqueue_push(&q, node));
+        VECTOR(level)[node] = 1;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            long int actlevel = VECTOR(level)[actnode];
+            long int i, n;
+
+            if (actlevel >= stoplevel) {
+                break;
+            }
+
+            neis = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actnode);
+            n = igraph_vector_size(neis);
+            for (i = 0; i < n; i++) {
+                long int nei = (long int) VECTOR(*neis)[i];
+                long int neilevel = VECTOR(level)[nei];
+                if (neilevel != 0) {
+                    if (neilevel == actlevel - 1) {
+                        continue;
+                    } else {
+                        /* found circle */
+                        stoplevel = neilevel;
+                        anycircle = 1;
+                        if (actlevel < mincirc) {
+                            /* Is it a minimum circle? */
+                            mincirc = actlevel + neilevel - 1;
+                            minvertex = node;
+                            t1 = actnode; t2 = nei;
+                            if (neilevel == 2) {
+                                /* Is it a triangle? */
+                                triangle = 1;
+                            }
+                        }
+                        if (neilevel == actlevel) {
+                            break;
+                        }
+                    }
+                } else {
+                    igraph_dqueue_push(&q, nei);
+                    VECTOR(level)[nei] = actlevel + 1;
+                }
+            }
+
+        } /* while q !empty */
+    } /* node */
+
+    if (girth) {
+        if (mincirc == LONG_MAX) {
+            *girth = mincirc = 0;
+        } else {
+            *girth = (igraph_integer_t) mincirc;
+        }
+    }
+
+    /* Store the actual circle, if needed */
+    if (circle) {
+        IGRAPH_CHECK(igraph_vector_resize(circle, mincirc));
+        if (mincirc != 0) {
+            long int i, n, idx = 0;
+            igraph_dqueue_clear(&q);
+            igraph_vector_long_null(&level); /* used for father pointers */
+#define FATHER(x) (VECTOR(level)[(x)])
+            IGRAPH_CHECK(igraph_dqueue_push(&q, minvertex));
+            FATHER(minvertex) = minvertex;
+            while (FATHER(t1) == 0 || FATHER(t2) == 0) {
+                long int actnode = (long int) igraph_dqueue_pop(&q);
+                neis = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actnode);
+                n = igraph_vector_size(neis);
+                for (i = 0; i < n; i++) {
+                    long int nei = (long int) VECTOR(*neis)[i];
+                    if (FATHER(nei) == 0) {
+                        FATHER(nei) = actnode + 1;
+                        igraph_dqueue_push(&q, nei);
+                    }
+                }
+            }  /* while q !empty */
+            /* Ok, now use FATHER to create the path */
+            while (t1 != minvertex) {
+                VECTOR(*circle)[idx++] = t1;
+                t1 = FATHER(t1) - 1;
+            }
+            VECTOR(*circle)[idx] = minvertex;
+            idx = mincirc - 1;
+            while (t2 != minvertex) {
+                VECTOR(*circle)[idx--] = t2;
+                t2 = FATHER(t2) - 1;
+            }
+        } /* anycircle */
+    } /* circle */
+#undef FATHER
+
+    igraph_vector_long_destroy(&level);
+    igraph_dqueue_destroy(&q);
+    igraph_lazy_adjlist_destroy(&adjlist);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+int igraph_i_linegraph_undirected(const igraph_t *graph, igraph_t *linegraph);
+
+int igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegraph);
+
+/* Note to self: tried using adjacency lists instead of igraph_incident queries,
+ * with minimal performance improvements on a graph with 70K vertices and 360K
+ * edges. (1.09s instead of 1.10s). I think it's not worth the fuss. */
+int igraph_i_linegraph_undirected(const igraph_t *graph, igraph_t *linegraph) {
+    long int no_of_edges = igraph_ecount(graph);
+    long int i, j, n;
+    igraph_vector_t adjedges, adjedges2;
+    igraph_vector_t edges;
+    long int prev = -1;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&adjedges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&adjedges2, 0);
+
+    for (i = 0; i < no_of_edges; i++) {
+        long int from = IGRAPH_FROM(graph, i);
+        long int to = IGRAPH_TO(graph, i);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (from != prev) {
+            IGRAPH_CHECK(igraph_incident(graph, &adjedges, (igraph_integer_t) from,
+                                         IGRAPH_ALL));
+        }
+        n = igraph_vector_size(&adjedges);
+        for (j = 0; j < n; j++) {
+            long int e = (long int) VECTOR(adjedges)[j];
+            if (e < i) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, e));
+            }
+        }
+
+        IGRAPH_CHECK(igraph_incident(graph, &adjedges2, (igraph_integer_t) to,
+                                     IGRAPH_ALL));
+        n = igraph_vector_size(&adjedges2);
+        for (j = 0; j < n; j++) {
+            long int e = (long int) VECTOR(adjedges2)[j];
+            if (e < i) {
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(&edges, e));
+            }
+        }
+
+        prev = from;
+    }
+
+    igraph_vector_destroy(&adjedges);
+    igraph_vector_destroy(&adjedges2);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    igraph_create(linegraph, &edges, (igraph_integer_t) no_of_edges,
+                  igraph_is_directed(graph));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegraph) {
+    long int no_of_edges = igraph_ecount(graph);
+    long int i, j, n;
+    igraph_vector_t adjedges;
+    igraph_vector_t edges;
+    long int prev = -1;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&adjedges, 0);
+
+    for (i = 0; i < no_of_edges; i++) {
+        long int from = IGRAPH_FROM(graph, i);
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (from != prev) {
+            IGRAPH_CHECK(igraph_incident(graph, &adjedges, (igraph_integer_t) from,
+                                         IGRAPH_IN));
+        }
+        n = igraph_vector_size(&adjedges);
+        for (j = 0; j < n; j++) {
+            long int e = (long int) VECTOR(adjedges)[j];
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, e));
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+        }
+
+        prev = from;
+    }
+
+    igraph_vector_destroy(&adjedges);
+    IGRAPH_FINALLY_CLEAN(1);
+    igraph_create(linegraph, &edges, (igraph_integer_t) no_of_edges, igraph_is_directed(graph));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_linegraph
+ * \brief Create the line graph of a graph.
+ *
+ * The line graph L(G) of a G undirected graph is defined as follows.
+ * L(G) has one vertex for each edge in G and two vertices in L(G) are connected
+ * by an edge if their corresponding edges share an end point.
+ *
+ * </para><para>
+ * The line graph L(G) of a G directed graph is slightly different,
+ * L(G) has one vertex for each edge in G and two vertices in L(G) are connected
+ * by a directed edge if the target of the first vertex's corresponding edge
+ * is the same as the source of the second vertex's corresponding edge.
+ *
+ * </para><para>
+ * Edge \em i  in the original graph will correspond to vertex \em i
+ * in the line graph.
+ *
+ * </para><para>
+ * The first version of this function was contributed by Vincent Matossian,
+ * thanks.
+ * \param graph The input graph, may be directed or undirected.
+ * \param linegraph Pointer to an uninitialized graph object, the
+ *        result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), the number of edges plus the number of vertices.
+ */
+
+int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph) {
+
+    if (igraph_is_directed(graph)) {
+        return igraph_i_linegraph_directed(graph, linegraph);
+    } else {
+        return igraph_i_linegraph_undirected(graph, linegraph);
+    }
+}
+
+/**
+ * \function igraph_add_edge
+ * \brief Adds a single edge to a graph.
+ *
+ * </para><para>
+ * For directed graphs the edge points from \p from to \p to.
+ *
+ * </para><para>
+ * Note that if you want to add many edges to a big graph, then it is
+ * inefficient to add them one by one, it is better to collect them into
+ * a vector and add all of them via a single \ref igraph_add_edges() call.
+ * \param igraph The graph.
+ * \param from The id of the first vertex of the edge.
+ * \param to The id of the second vertex of the edge.
+ * \return Error code.
+ *
+ * \sa \ref igraph_add_edges() to add many edges, \ref
+ * igraph_delete_edges() to remove edges and \ref
+ * igraph_add_vertices() to add vertices.
+ *
+ * Time complexity: O(|V|+|E|), the number of edges plus the number of
+ * vertices.
+ */
+
+int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to) {
+
+    igraph_vector_t edges;
+    int ret;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 2);
+
+    VECTOR(edges)[0] = from;
+    VECTOR(edges)[1] = to;
+    IGRAPH_CHECK(ret = igraph_add_edges(graph, &edges, 0));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return ret;
+}
+
+/*
+ * \example examples/simple/graph_convergence_degree.c
+ */
+
+int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result,
+                              igraph_vector_t *ins, igraph_vector_t *outs) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int i, j, k, n;
+    long int *geodist;
+    igraph_vector_int_t *eids;
+    igraph_vector_t *ins_p, *outs_p, ins_v, outs_v;
+    igraph_dqueue_t q;
+    igraph_inclist_t inclist;
+    igraph_bool_t directed = igraph_is_directed(graph);
+
+    if (result != 0) {
+        IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges));
+    }
+    IGRAPH_CHECK(igraph_dqueue_init(&q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &q);
+
+    if (ins == 0) {
+        ins_p = &ins_v;
+        IGRAPH_VECTOR_INIT_FINALLY(ins_p, no_of_edges);
+    } else {
+        ins_p = ins;
+        IGRAPH_CHECK(igraph_vector_resize(ins_p, no_of_edges));
+        igraph_vector_null(ins_p);
+    }
+
+    if (outs == 0) {
+        outs_p = &outs_v;
+        IGRAPH_VECTOR_INIT_FINALLY(outs_p, no_of_edges);
+    } else {
+        outs_p = outs;
+        IGRAPH_CHECK(igraph_vector_resize(outs_p, no_of_edges));
+        igraph_vector_null(outs_p);
+    }
+
+    geodist = igraph_Calloc(no_of_nodes, long int);
+    if (geodist == 0) {
+        IGRAPH_ERROR("Cannot calculate convergence degrees", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, geodist);
+
+    /* Collect shortest paths originating from/to every node to correctly
+     * determine input field sizes */
+    for (k = 0; k < (directed ? 2 : 1); k++) {
+        igraph_neimode_t neimode = (k == 0) ? IGRAPH_OUT : IGRAPH_IN;
+        igraph_real_t *vec;
+        IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, neimode));
+        IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+        vec = (k == 0) ? VECTOR(*ins_p) : VECTOR(*outs_p);
+        for (i = 0; i < no_of_nodes; i++) {
+            igraph_dqueue_clear(&q);
+            memset(geodist, 0, sizeof(long int) * (size_t) no_of_nodes);
+            geodist[i] = 1;
+            IGRAPH_CHECK(igraph_dqueue_push(&q, i));
+            IGRAPH_CHECK(igraph_dqueue_push(&q, 0.0));
+            while (!igraph_dqueue_empty(&q)) {
+                long int actnode = (long int) igraph_dqueue_pop(&q);
+                long int actdist = (long int) igraph_dqueue_pop(&q);
+                IGRAPH_ALLOW_INTERRUPTION();
+                eids = igraph_inclist_get(&inclist, actnode);
+                n = igraph_vector_int_size(eids);
+                for (j = 0; j < n; j++) {
+                    long int neighbor = IGRAPH_OTHER(graph, VECTOR(*eids)[j], actnode);
+                    if (geodist[neighbor] != 0) {
+                        /* we've already seen this node, another shortest path? */
+                        if (geodist[neighbor] - 1 == actdist + 1) {
+                            /* Since this edge is in the BFS tree rooted at i, we must
+                             * increase either the size of the infield or the outfield */
+                            if (!directed) {
+                                if (actnode < neighbor) {
+                                    VECTOR(*ins_p)[(long int)VECTOR(*eids)[j]] += 1;
+                                } else {
+                                    VECTOR(*outs_p)[(long int)VECTOR(*eids)[j]] += 1;
+                                }
+                            } else {
+                                vec[(long int)VECTOR(*eids)[j]] += 1;
+                            }
+                        } else if (geodist[neighbor] - 1 < actdist + 1) {
+                            continue;
+                        }
+                    } else {
+                        /* we haven't seen this node yet */
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+                        /* Since this edge is in the BFS tree rooted at i, we must
+                         * increase either the size of the infield or the outfield */
+                        if (!directed) {
+                            if (actnode < neighbor) {
+                                VECTOR(*ins_p)[(long int)VECTOR(*eids)[j]] += 1;
+                            } else {
+                                VECTOR(*outs_p)[(long int)VECTOR(*eids)[j]] += 1;
+                            }
+                        } else {
+                            vec[(long int)VECTOR(*eids)[j]] += 1;
+                        }
+                        geodist[neighbor] = actdist + 2;
+                    }
+                }
+            }
+        }
+
+        igraph_inclist_destroy(&inclist);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    if (result != 0) {
+        for (i = 0; i < no_of_edges; i++)
+            VECTOR(*result)[i] = (VECTOR(*ins_p)[i] - VECTOR(*outs_p)[i]) /
+                                 (VECTOR(*ins_p)[i] + VECTOR(*outs_p)[i]);
+        if (!directed) {
+            for (i = 0; i < no_of_edges; i++)
+                if (VECTOR(*result)[i] < 0) {
+                    VECTOR(*result)[i] = -VECTOR(*result)[i];
+                }
+        }
+    }
+
+    if (ins == 0) {
+        igraph_vector_destroy(ins_p);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (outs == 0) {
+        igraph_vector_destroy(outs_p);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_free(geodist);
+    igraph_dqueue_destroy(&q);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_shortest_paths_dijkstra
+ * Weighted shortest paths from some sources.
+ *
+ * This function is Dijkstra's algorithm to find the weighted
+ * shortest paths to all vertices from a single source. (It is run
+ * independently for the given sources.) It uses a binary heap for
+ * efficient implementation.
+ *
+ * \param graph The input graph, can be directed.
+ * \param res The result, a matrix. A pointer to an initialized matrix
+ *    should be passed here. The matrix will be resized as needed.
+ *    Each row contains the distances from a single source, to the
+ *    vertices given in the \c to argument.
+ *    Unreachable vertices has distance
+ *    \c IGRAPH_INFINITY.
+ * \param from The source vertices.
+ * \param to The target vertices. It is not allowed to include a
+ *    vertex twice or more.
+ * \param weights The edge weights. They must be all non-negative for
+ *    Dijkstra's algorithm to work. An error code is returned if there
+ *    is a negative edge weight in the weight vector. If this is a null
+ *    pointer, then the
+ *    unweighted version, \ref igraph_shortest_paths() is called.
+ * \param mode For directed graphs; whether to follow paths along edge
+ *    directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or
+ *    ignore edge directions completely (\c IGRAPH_ALL). It is ignored
+ *    for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(s*|E|log|E|+|V|), where |V| is the number of
+ * vertices, |E| the number of edges and s the number of sources.
+ *
+ * \sa \ref igraph_shortest_paths() for a (slightly) faster unweighted
+ * version or \ref igraph_shortest_paths_bellman_ford() for a weighted
+ * variant that works in the presence of negative edge weights (but no
+ * negative loops).
+ *
+ * \example examples/simple/dijkstra.c
+ */
+
+int igraph_shortest_paths_dijkstra(const igraph_t *graph,
+                                   igraph_matrix_t *res,
+                                   const igraph_vs_t from,
+                                   const igraph_vs_t to,
+                                   const igraph_vector_t *weights,
+                                   igraph_neimode_t mode) {
+
+    /* Implementation details. This is the basic Dijkstra algorithm,
+       with a binary heap. The heap is indexed, i.e. it stores not only
+       the distances, but also which vertex they belong to.
+
+       From now on we use a 2-way heap, so the distances can be queried
+       directly from the heap.
+
+       Dirty tricks:
+       - the opposite of the distance is stored in the heap, as it is a
+         maximum heap and we need a minimum heap.
+       - we don't use IGRAPH_INFINITY in the res matrix during the
+         computation, as IGRAPH_FINITE() might involve a function call
+         and we want to spare that. -1 will denote infinity instead.
+    */
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_2wheap_t Q;
+    igraph_vit_t fromvit, tovit;
+    long int no_of_from, no_of_to;
+    igraph_lazy_inclist_t inclist;
+    long int i, j;
+    igraph_real_t my_infinity = IGRAPH_INFINITY;
+    igraph_bool_t all_to;
+    igraph_vector_t indexv;
+
+    if (!weights) {
+        return igraph_shortest_paths(graph, res, from, to, mode);
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_min(weights) < 0) {
+        IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &fromvit);
+    no_of_from = IGRAPH_VIT_SIZE(fromvit);
+
+    IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
+    IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
+    IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode));
+    IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
+
+    if ( (all_to = igraph_vs_is_all(&to)) ) {
+        no_of_to = no_of_nodes;
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&indexv, no_of_nodes);
+        IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &tovit);
+        no_of_to = IGRAPH_VIT_SIZE(tovit);
+        for (i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit)) {
+            long int v = IGRAPH_VIT_GET(tovit);
+            if (VECTOR(indexv)[v]) {
+                IGRAPH_ERROR("Duplicate vertices in `to', this is not allowed",
+                             IGRAPH_EINVAL);
+            }
+            VECTOR(indexv)[v] = ++i;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to));
+    igraph_matrix_fill(res, my_infinity);
+
+    for (IGRAPH_VIT_RESET(fromvit), i = 0;
+         !IGRAPH_VIT_END(fromvit);
+         IGRAPH_VIT_NEXT(fromvit), i++) {
+
+        long int reached = 0;
+        long int source = IGRAPH_VIT_GET(fromvit);
+        igraph_2wheap_clear(&Q);
+        igraph_2wheap_push_with_index(&Q, source, -1.0);
+
+        while (!igraph_2wheap_empty(&Q)) {
+            long int minnei = igraph_2wheap_max_index(&Q);
+            igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q);
+            igraph_vector_t *neis;
+            long int nlen;
+
+            if (all_to) {
+                MATRIX(*res, i, minnei) = mindist - 1.0;
+            } else {
+                if (VECTOR(indexv)[minnei]) {
+                    MATRIX(*res, i, (long int)(VECTOR(indexv)[minnei] - 1)) = mindist - 1.0;
+                    reached++;
+                    if (reached == no_of_to) {
+                        igraph_2wheap_clear(&Q);
+                        break;
+                    }
+                }
+            }
+
+            /* Now check all neighbors of 'minnei' for a shorter path */
+            neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei);
+            nlen = igraph_vector_size(neis);
+            for (j = 0; j < nlen; j++) {
+                long int edge = (long int) VECTOR(*neis)[j];
+                long int tto = IGRAPH_OTHER(graph, edge, minnei);
+                igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
+                igraph_bool_t active = igraph_2wheap_has_active(&Q, tto);
+                igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto);
+                igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0;
+                if (!has) {
+                    /* This is the first non-infinite distance */
+                    IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist));
+                } else if (altdist < curdist) {
+                    /* This is a shorter path */
+                    IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist));
+                }
+            }
+
+        } /* !igraph_2wheap_empty(&Q) */
+
+    } /* !IGRAPH_VIT_END(fromvit) */
+
+    if (!all_to) {
+        igraph_vit_destroy(&tovit);
+        igraph_vector_destroy(&indexv);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+
+    igraph_lazy_inclist_destroy(&inclist);
+    igraph_2wheap_destroy(&Q);
+    igraph_vit_destroy(&fromvit);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_get_shortest_paths_dijkstra
+ * \brief Calculates the weighted shortest paths from/to one vertex.
+ *
+ * </para><para>
+ * If there is more than one path with the smallest weight between two vertices, this
+ * function gives only one of them.
+ * \param graph The graph object.
+ * \param vertices The result, the ids of the vertices along the paths.
+ *        This is a pointer vector, each element points to a vector
+ *        object. These should be initialized before passing them to
+ *        the function, which will properly clear and/or resize them
+ *        and fill the ids of the vertices along the geodesics from/to
+ *        the vertices. Supply a null pointer here if you don't need
+ *        these vectors. Normally, either this argument, or the \c
+ *        edges should be non-null, but no error or warning is given
+ *        if they are both null pointers.
+ * \param edges The result, the ids of the edges along the paths.
+ *        This is a pointer vector, each element points to a vector
+ *        object. These should be initialized before passing them to
+ *        the function, which will properly clear and/or resize them
+ *        and fill the ids of the vertices along the geodesics from/to
+ *        the vertices. Supply a null pointer here if you don't need
+ *        these vectors. Normally, either this argument, or the \c
+ *        vertices should be non-null, but no error or warning is given
+ *        if they are both null pointers.
+ * \param from The id of the vertex from/to which the geodesics are
+ *        calculated.
+ * \param to Vertex sequence with the ids of the vertices to/from which the
+ *        shortest paths will be calculated. A vertex might be given multiple
+ *        times.
+ * \param weights a vector holding the edge weights. All weights must be
+ *        positive.
+ * \param mode The type of shortest paths to be use for the
+ *        calculation in directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing paths are calculated.
+ *        \cli IGRAPH_IN
+ *          the incoming paths are calculated.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an
+ *          undirected one for the computation.
+ *        \endclist
+ * \param predecessors A pointer to an initialized igraph vector or null.
+ *        If not null, a vector containing the predecessor of each vertex in
+ *        the single source shortest path tree is returned here. The
+ *        predecessor of vertex i in the tree is the vertex from which vertex i
+ *        was reached. The predecessor of the start vertex (in the \c from
+ *        argument) is itself by definition. If the predecessor is -1, it means
+ *        that the given vertex was not reached from the source during the
+ *        search. Note that the search terminates if all the vertices in
+ *        \c to are reached.
+ * \param inbound_edges A pointer to an initialized igraph vector or null.
+ *        If not null, a vector containing the inbound edge of each vertex in
+ *        the single source shortest path tree is returned here. The
+ *        inbound edge of vertex i in the tree is the edge via which vertex i
+ *        was reached. The start vertex and vertices that were not reached
+ *        during the search will have -1 in the corresponding entry of the
+ *        vector. Note that the search terminates if all the vertices in
+ *        \c to are reached.
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           \p from is invalid vertex id, or the length of \p to is
+ *           not the same as the length of \p res.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(|E|log|E|+|V|), where |V| is the number of
+ * vertices and |E| is the number of edges
+ *
+ * \sa \ref igraph_shortest_paths_dijkstra() if you only need the path length but
+ * not the paths themselves, \ref igraph_get_shortest_paths() if all edge
+ * weights are equal.
+ *
+ * \example examples/simple/igraph_get_shortest_paths_dijkstra.c
+ */
+int igraph_get_shortest_paths_dijkstra(const igraph_t *graph,
+                                       igraph_vector_ptr_t *vertices,
+                                       igraph_vector_ptr_t *edges,
+                                       igraph_integer_t from,
+                                       igraph_vs_t to,
+                                       const igraph_vector_t *weights,
+                                       igraph_neimode_t mode,
+                                       igraph_vector_long_t *predecessors,
+                                       igraph_vector_long_t *inbound_edges) {
+    /* Implementation details. This is the basic Dijkstra algorithm,
+       with a binary heap. The heap is indexed, i.e. it stores not only
+       the distances, but also which vertex they belong to. The other
+       mapping, i.e. getting the distance for a vertex is not in the
+       heap (that would by the double-indexed heap), but in the result
+       matrix.
+
+       Dirty tricks:
+       - the opposite of the distance is stored in the heap, as it is a
+         maximum heap and we need a minimum heap.
+       - we don't use IGRAPH_INFINITY in the distance vector during the
+         computation, as IGRAPH_FINITE() might involve a function call
+         and we want to spare that. So we store distance+1.0 instead of
+         distance, and zero denotes infinity.
+       - `parents' assigns the inbound edge IDs of all vertices in the
+         shortest path tree to the vertices. In this implementation, the
+         edge ID + 1 is stored, zero means unreachable vertices.
+    */
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vit_t vit;
+    igraph_2wheap_t Q;
+    igraph_lazy_inclist_t inclist;
+    igraph_vector_t dists;
+    long int *parents;
+    igraph_bool_t *is_target;
+    long int i, to_reach;
+
+    if (!weights) {
+        return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode,
+                                         predecessors, inbound_edges);
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_min(weights) < 0) {
+        IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, to, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    if (vertices && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(vertices)) {
+        IGRAPH_ERROR("Size of `vertices' and `to' should match", IGRAPH_EINVAL);
+    }
+    if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) {
+        IGRAPH_ERROR("Size of `edges' and `to' should match", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
+    IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
+    IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode));
+    IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes);
+    igraph_vector_fill(&dists, -1.0);
+
+    parents = igraph_Calloc(no_of_nodes, long int);
+    if (parents == 0) {
+        IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, parents);
+    is_target = igraph_Calloc(no_of_nodes, igraph_bool_t);
+    if (is_target == 0) {
+        IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, is_target);
+
+    /* Mark the vertices we need to reach */
+    to_reach = IGRAPH_VIT_SIZE(vit);
+    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+        if (!is_target[ (long int) IGRAPH_VIT_GET(vit) ]) {
+            is_target[ (long int) IGRAPH_VIT_GET(vit) ] = 1;
+        } else {
+            to_reach--;       /* this node was given multiple times */
+        }
+    }
+
+    VECTOR(dists)[(long int)from] = 0.0;  /* zero distance */
+    parents[(long int)from] = 0;
+    igraph_2wheap_push_with_index(&Q, from, 0);
+
+    while (!igraph_2wheap_empty(&Q) && to_reach > 0) {
+        long int nlen, minnei = igraph_2wheap_max_index(&Q);
+        igraph_real_t mindist = -igraph_2wheap_delete_max(&Q);
+        igraph_vector_t *neis;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if (is_target[minnei]) {
+            is_target[minnei] = 0;
+            to_reach--;
+        }
+
+        /* Now check all neighbors of 'minnei' for a shorter path */
+        neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei);
+        nlen = igraph_vector_size(neis);
+        for (i = 0; i < nlen; i++) {
+            long int edge = (long int) VECTOR(*neis)[i];
+            long int tto = IGRAPH_OTHER(graph, edge, minnei);
+            igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
+            igraph_real_t curdist = VECTOR(dists)[tto];
+            if (curdist < 0) {
+                /* This is the first finite distance */
+                VECTOR(dists)[tto] = altdist;
+                parents[tto] = edge + 1;
+                IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist));
+            } else if (altdist < curdist) {
+                /* This is a shorter path */
+                VECTOR(dists)[tto] = altdist;
+                parents[tto] = edge + 1;
+                IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist));
+            }
+        }
+    } /* !igraph_2wheap_empty(&Q) */
+
+    if (to_reach > 0) {
+        IGRAPH_WARNING("Couldn't reach some vertices");
+    }
+
+    /* Create `predecessors' if needed */
+    if (predecessors) {
+        IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes));
+
+        for (i = 0; i < no_of_nodes; i++) {
+            if (i == from) {
+                /* i is the start vertex */
+                VECTOR(*predecessors)[i] = i;
+            } else if (parents[i] <= 0) {
+                /* i was not reached */
+                VECTOR(*predecessors)[i] = -1;
+            } else {
+                /* i was reached via the edge with ID = parents[i] - 1 */
+                VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, parents[i] - 1, i);
+            }
+        }
+    }
+
+    /* Create `inbound_edges' if needed */
+    if (inbound_edges) {
+        IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes));
+
+        for (i = 0; i < no_of_nodes; i++) {
+            if (parents[i] <= 0) {
+                /* i was not reached */
+                VECTOR(*inbound_edges)[i] = -1;
+            } else {
+                /* i was reached via the edge with ID = parents[i] - 1 */
+                VECTOR(*inbound_edges)[i] = parents[i] - 1;
+            }
+        }
+    }
+
+    /* Reconstruct the shortest paths based on vertex and/or edge IDs */
+    if (vertices || edges) {
+        for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+            long int node = IGRAPH_VIT_GET(vit);
+            long int size, act, edge;
+            igraph_vector_t *vvec = 0, *evec = 0;
+            if (vertices) {
+                vvec = VECTOR(*vertices)[i];
+                igraph_vector_clear(vvec);
+            }
+            if (edges) {
+                evec = VECTOR(*edges)[i];
+                igraph_vector_clear(evec);
+            }
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            size = 0;
+            act = node;
+            while (parents[act]) {
+                size++;
+                edge = parents[act] - 1;
+                act = IGRAPH_OTHER(graph, edge, act);
+            }
+            if (vvec) {
+                IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1));
+                VECTOR(*vvec)[size] = node;
+            }
+            if (evec) {
+                IGRAPH_CHECK(igraph_vector_resize(evec, size));
+            }
+            act = node;
+            while (parents[act]) {
+                edge = parents[act] - 1;
+                act = IGRAPH_OTHER(graph, edge, act);
+                size--;
+                if (vvec) {
+                    VECTOR(*vvec)[size] = act;
+                }
+                if (evec) {
+                    VECTOR(*evec)[size] = edge;
+                }
+            }
+        }
+    }
+
+    igraph_lazy_inclist_destroy(&inclist);
+    igraph_2wheap_destroy(&Q);
+    igraph_vector_destroy(&dists);
+    igraph_Free(is_target);
+    igraph_Free(parents);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return 0;
+}
+
+/**
+ * \function igraph_get_shortest_path_dijkstra
+ * Weighted shortest path from one vertex to another one.
+ *
+ * Calculates a single (positively) weighted shortest path from
+ * a single vertex to another one, using Dijkstra's algorithm.
+ *
+ * </para><para>This function is a special case (and a wrapper) to
+ * \ref igraph_get_shortest_paths_dijkstra().
+ *
+ * \param graph The input graph, it can be directed or undirected.
+ * \param vertices Pointer to an initialized vector or a null
+ *        pointer. If not a null pointer, then the vertex ids along
+ *        the path are stored here, including the source and target
+ *        vertices.
+ * \param edges Pointer to an uninitialized vector or a null
+ *        pointer. If not a null pointer, then the edge ids along the
+ *        path are stored here.
+ * \param from The id of the source vertex.
+ * \param to The id of the target vertex.
+ * \param weights Vector of edge weights, in the order of edge
+ *        ids. They must be non-negative, otherwise the algorithm does
+ *        not work.
+ * \param mode A constant specifying how edge directions are
+ *        considered in directed graphs. \c IGRAPH_OUT follows edge
+ *        directions, \c IGRAPH_IN follows the opposite directions,
+ *        and \c IGRAPH_ALL ignores edge directions. This argument is
+ *        ignored for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|log|E|+|V|), |V| is the number of vertices,
+ * |E| is the number of edges in the graph.
+ *
+ * \sa \ref igraph_get_shortest_paths_dijkstra() for the version with
+ * more target vertices.
+ */
+
+int igraph_get_shortest_path_dijkstra(const igraph_t *graph,
+                                      igraph_vector_t *vertices,
+                                      igraph_vector_t *edges,
+                                      igraph_integer_t from,
+                                      igraph_integer_t to,
+                                      const igraph_vector_t *weights,
+                                      igraph_neimode_t mode) {
+
+    igraph_vector_ptr_t vertices2, *vp = &vertices2;
+    igraph_vector_ptr_t edges2, *ep = &edges2;
+
+    if (vertices) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2);
+        VECTOR(vertices2)[0] = vertices;
+    } else {
+        vp = 0;
+    }
+    if (edges) {
+        IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1));
+        IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2);
+        VECTOR(edges2)[0] = edges;
+    } else {
+        ep = 0;
+    }
+
+    IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph, vp, ep,
+                 from, igraph_vss_1(to),
+                 weights, mode, 0, 0));
+
+    if (edges) {
+        igraph_vector_ptr_destroy(&edges2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (vertices) {
+        igraph_vector_ptr_destroy(&vertices2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+int igraph_i_vector_tail_cmp(const void* path1, const void* path2);
+
+/* Compares two paths based on their last elements. Required by
+ * igraph_get_all_shortest_paths_dijkstra to put the final result
+ * in order. Assumes that both paths are pointers to igraph_vector_t
+ * objects and that they are not empty
+ */
+int igraph_i_vector_tail_cmp(const void* path1, const void* path2) {
+    return (int) (igraph_vector_tail(*(const igraph_vector_t**)path1) -
+                  igraph_vector_tail(*(const igraph_vector_t**)path2));
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_get_all_shortest_paths_dijkstra
+ * \brief Finds all shortest paths (geodesics) from a vertex to all other vertices.
+ *
+ * \param graph The graph object.
+ * \param res Pointer to an initialized pointer vector, the result
+ *   will be stored here in igraph_vector_t objects. Each vector
+ *   object contains the vertices along a shortest path from \p from
+ *   to another vertex. The vectors are ordered according to their
+ *   target vertex: first the shortest paths to vertex 0, then to
+ *   vertex 1, etc. No data is included for unreachable vertices.
+ * \param nrgeo Pointer to an initialized igraph_vector_t object or
+ *   NULL. If not NULL the number of shortest paths from \p from are
+ *   stored here for every vertex in the graph. Note that the values
+ *   will be accurate only for those vertices that are in the target
+ *   vertex sequence (see \p to), since the search terminates as soon
+ *   as all the target vertices have been found.
+ * \param from The id of the vertex from/to which the geodesics are
+ *        calculated.
+ * \param to Vertex sequence with the ids of the vertices to/from which the
+ *        shortest paths will be calculated. A vertex might be given multiple
+ *        times.
+ * \param weights a vector holding the edge weights. All weights must be
+ *        non-negative.
+ * \param mode The type of shortest paths to be use for the
+ *        calculation in directed graphs. Possible values:
+ *        \clist
+ *        \cli IGRAPH_OUT
+ *          the outgoing paths are calculated.
+ *        \cli IGRAPH_IN
+ *          the incoming paths are calculated.
+ *        \cli IGRAPH_ALL
+ *          the directed graph is considered as an
+ *          undirected one for the computation.
+ *        \endclist
+ * \return Error code:
+ *        \clist
+ *        \cli IGRAPH_ENOMEM
+ *           not enough memory for temporary data.
+ *        \cli IGRAPH_EINVVID
+ *           \p from is invalid vertex id, or the length of \p to is
+ *           not the same as the length of \p res.
+ *        \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *        \endclist
+ *
+ * Time complexity: O(|E|log|E|+|V|), where |V| is the number of
+ * vertices and |E| is the number of edges
+ *
+ * \sa \ref igraph_shortest_paths_dijkstra() if you only need the path
+ * length but not the paths themselves, \ref igraph_get_all_shortest_paths()
+ * if all edge weights are equal.
+ *
+ * \example examples/simple/igraph_get_all_shortest_paths_dijkstra.c
+ */
+int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph,
+        igraph_vector_ptr_t *res,
+        igraph_vector_t *nrgeo,
+        igraph_integer_t from, igraph_vs_t to,
+        const igraph_vector_t *weights,
+        igraph_neimode_t mode) {
+    /* Implementation details: see igraph_get_shortest_paths_dijkstra,
+       it's basically the same.
+    */
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vit_t vit;
+    igraph_2wheap_t Q;
+    igraph_lazy_inclist_t inclist;
+    igraph_vector_t dists, order;
+    igraph_vector_ptr_t parents;
+    unsigned char *is_target;
+    long int i, n, to_reach;
+
+    if (!weights) {
+        return igraph_get_all_shortest_paths(graph, res, nrgeo, from, to, mode);
+    }
+
+    if (res == 0 && nrgeo == 0) {
+        return IGRAPH_SUCCESS;
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL);
+    }
+    if (igraph_vector_min(weights) < 0) {
+        IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL);
+    }
+
+    /* parents stores a vector for each vertex, listing the parent vertices
+     * of each vertex in the traversal */
+    IGRAPH_CHECK(igraph_vector_ptr_init(&parents, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &parents);
+    igraph_vector_ptr_set_item_destructor(&parents, (igraph_finally_func_t*)igraph_vector_destroy);
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_vector_t* parent_vec;
+        parent_vec = igraph_Calloc(1, igraph_vector_t);
+        if (parent_vec == 0) {
+            IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths", IGRAPH_ENOMEM);
+        }
+        IGRAPH_CHECK(igraph_vector_init(parent_vec, 0));
+        VECTOR(parents)[i] = parent_vec;
+    }
+
+    /* distance of each vertex from the root */
+    IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes);
+    igraph_vector_fill(&dists, -1.0);
+
+    /* order lists the order of vertices in which they were found during
+     * the traversal */
+    IGRAPH_VECTOR_INIT_FINALLY(&order, 0);
+
+    /* boolean array to mark whether a given vertex is a target or not */
+    is_target = igraph_Calloc(no_of_nodes, unsigned char);
+    if (is_target == 0) {
+        IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, is_target);
+
+    /* two-way heap storing vertices and distances */
+    IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
+    IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
+
+    /* lazy adjacency edge list to query neighbours efficiently */
+    IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode));
+    IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
+
+    /* Mark the vertices we need to reach */
+    IGRAPH_CHECK(igraph_vit_create(graph, to, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    to_reach = IGRAPH_VIT_SIZE(vit);
+    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+        if (!is_target[ (long int) IGRAPH_VIT_GET(vit) ]) {
+            is_target[ (long int) IGRAPH_VIT_GET(vit) ] = 1;
+        } else {
+            to_reach--;       /* this node was given multiple times */
+        }
+    }
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    VECTOR(dists)[(long int)from] = 0.0;  /* zero distance */
+    igraph_2wheap_push_with_index(&Q, from, 0);
+
+    while (!igraph_2wheap_empty(&Q) && to_reach > 0) {
+        long int nlen, minnei = igraph_2wheap_max_index(&Q);
+        igraph_real_t mindist = -igraph_2wheap_delete_max(&Q);
+        igraph_vector_t *neis;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        /*
+        printf("Reached vertex %ld, is_target[%ld] = %d, %ld to go\n",
+            minnei, minnei, (int)is_target[minnei], to_reach - is_target[minnei]);
+        */
+
+        if (is_target[minnei]) {
+            is_target[minnei] = 0;
+            to_reach--;
+        }
+
+        /* Mark that we have reached this vertex */
+        IGRAPH_CHECK(igraph_vector_push_back(&order, minnei));
+
+        /* Now check all neighbors of 'minnei' for a shorter path */
+        neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei);
+        nlen = igraph_vector_size(neis);
+        for (i = 0; i < nlen; i++) {
+            long int edge = (long int) VECTOR(*neis)[i];
+            long int tto = IGRAPH_OTHER(graph, edge, minnei);
+            igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
+            igraph_real_t curdist = VECTOR(dists)[tto];
+            igraph_vector_t *parent_vec;
+
+            if (curdist < 0) {
+                /* This is the first non-infinite distance */
+                VECTOR(dists)[tto] = altdist;
+                parent_vec = (igraph_vector_t*)VECTOR(parents)[tto];
+                IGRAPH_CHECK(igraph_vector_push_back(parent_vec, minnei));
+                IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist));
+            } else if (altdist == curdist && VECTOR(*weights)[edge] > 0) {
+                /* This is an alternative path with exactly the same length.
+                     * Note that we consider this case only if the edge via which we
+                     * reached the node has a nonzero weight; otherwise we could create
+                     * infinite loops in undirected graphs by traversing zero-weight edges
+                     * back-and-forth */
+                parent_vec = (igraph_vector_t*)VECTOR(parents)[tto];
+                IGRAPH_CHECK(igraph_vector_push_back(parent_vec, minnei));
+            } else if (altdist < curdist) {
+                /* This is a shorter path */
+                VECTOR(dists)[tto] = altdist;
+                parent_vec = (igraph_vector_t*)VECTOR(parents)[tto];
+                igraph_vector_clear(parent_vec);
+                IGRAPH_CHECK(igraph_vector_push_back(parent_vec, minnei));
+                IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist));
+            }
+        }
+    } /* !igraph_2wheap_empty(&Q) */
+
+    if (to_reach > 0) {
+        IGRAPH_WARNING("Couldn't reach some vertices");
+    }
+
+    /* we don't need these anymore */
+    igraph_lazy_inclist_destroy(&inclist);
+    igraph_2wheap_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    /*
+    printf("Order:\n");
+    igraph_vector_print(&order);
+
+    printf("Parent vertices:\n");
+    for (i = 0; i < no_of_nodes; i++) {
+      if (igraph_vector_size(VECTOR(parents)[i]) > 0) {
+        printf("[%ld]: ", (long int)i);
+        igraph_vector_print(VECTOR(parents)[i]);
+      }
+    }
+    */
+
+    if (nrgeo) {
+        IGRAPH_CHECK(igraph_vector_resize(nrgeo, no_of_nodes));
+        igraph_vector_null(nrgeo);
+
+        /* Theoretically, we could calculate nrgeo in parallel with the traversal.
+         * However, that way we would have to check whether nrgeo is null or not
+         * every time we want to update some element in nrgeo. Since we need the
+         * order vector anyway for building the final result, we could just as well
+         * build nrgeo here.
+         */
+        VECTOR(*nrgeo)[(long int)from] = 1;
+        n = igraph_vector_size(&order);
+        for (i = 1; i < n; i++) {
+            long int node, j, k;
+            igraph_vector_t *parent_vec;
+
+            node = (long int)VECTOR(order)[i];
+            /* now, take the parent vertices */
+            parent_vec = (igraph_vector_t*)VECTOR(parents)[node];
+            k = igraph_vector_size(parent_vec);
+            for (j = 0; j < k; j++) {
+                VECTOR(*nrgeo)[node] += VECTOR(*nrgeo)[(long int)VECTOR(*parent_vec)[j]];
+            }
+        }
+    }
+
+    if (res) {
+        igraph_vector_t *path, *paths_index, *parent_vec;
+        igraph_stack_t stack;
+        long int j, node;
+
+        /* a shortest path from the starting vertex to vertex i can be
+         * obtained by calculating the shortest paths from the "parents"
+         * of vertex i in the traversal. Knowing which of the vertices
+         * are "targets" (see is_target), we can collect for which other
+         * vertices do we need to calculate the shortest paths. We reuse
+         * is_target for that; is_target = 0 means that we don't need the
+         * vertex, is_target = 1 means that the vertex is a target (hence
+         * we need it), is_target = 2 means that the vertex is not a target
+         * but it stands between a shortest path between the root and one
+         * of the targets
+         */
+        if (igraph_vs_is_all(&to)) {
+            memset(is_target, 1, sizeof(unsigned char) * (size_t) no_of_nodes);
+        } else {
+            memset(is_target, 0, sizeof(unsigned char) * (size_t) no_of_nodes);
+
+            IGRAPH_CHECK(igraph_stack_init(&stack, 0));
+            IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+
+            /* Add the target vertices to the queue */
+            IGRAPH_CHECK(igraph_vit_create(graph, to, &vit));
+            IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+            for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
+                i = (long int) IGRAPH_VIT_GET(vit);
+                if (!is_target[i]) {
+                    is_target[i] = 1;
+                    IGRAPH_CHECK(igraph_stack_push(&stack, i));
+                }
+            }
+            igraph_vit_destroy(&vit);
+            IGRAPH_FINALLY_CLEAN(1);
+
+            while (!igraph_stack_empty(&stack)) {
+                /* For each parent of node i, get its parents */
+                igraph_real_t el = igraph_stack_pop(&stack);
+                parent_vec = (igraph_vector_t*)VECTOR(parents)[(long int) el];
+                i = igraph_vector_size(parent_vec);
+
+                for (j = 0; j < i; j++) {
+                    /* For each parent, check if it's already in the stack.
+                     * If not, push it and mark it in is_target */
+                    n = (long int) VECTOR(*parent_vec)[j];
+                    if (!is_target[n]) {
+                        is_target[n] = 2;
+                        IGRAPH_CHECK(igraph_stack_push(&stack, n));
+                    }
+                }
+            }
+            igraph_stack_destroy(&stack);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+
+        /* now, reconstruct the shortest paths from the parent list in the
+         * order we've found the nodes during the traversal.
+         * dists is being re-used as a vector where element i tells the
+         * index in res where the shortest paths leading to vertex i
+         * start, plus one (so that zero means that there are no paths
+         * for a given vertex).
+         */
+        paths_index = &dists;
+        n = igraph_vector_size(&order);
+        igraph_vector_null(paths_index);
+
+        /* clear the paths vector */
+        igraph_vector_ptr_clear(res);
+        igraph_vector_ptr_set_item_destructor(res,
+                                              (igraph_finally_func_t*)igraph_vector_destroy);
+
+        /* by definition, the shortest path leading to the starting vertex
+         * consists of the vertex itself only */
+        path = igraph_Calloc(1, igraph_vector_t);
+        if (path == 0)
+            IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths_dijkstra",
+                         IGRAPH_ENOMEM);
+        IGRAPH_FINALLY(igraph_free, path);
+        IGRAPH_CHECK(igraph_vector_init(path, 1));
+        IGRAPH_CHECK(igraph_vector_ptr_push_back(res, path));
+        IGRAPH_FINALLY_CLEAN(1);  /* ownership of path passed to res */
+        VECTOR(*path)[0] = from;
+        VECTOR(*paths_index)[(long int)from] = 1;
+
+        for (i = 1; i < n; i++) {
+            long int m, path_count;
+            igraph_vector_t *parent_path;
+
+            node = (long int) VECTOR(order)[i];
+
+            /* if we don't need the shortest paths for this node (because
+             * it is not standing in a shortest path between the source
+             * node and any of the target nodes), skip it */
+            if (!is_target[node]) {
+                continue;
+            }
+
+            IGRAPH_ALLOW_INTERRUPTION();
+
+            /* we are calculating the shortest paths of node now. */
+            /* first, we update the paths_index */
+            path_count = igraph_vector_ptr_size(res);
+            VECTOR(*paths_index)[node] = path_count + 1;
+            /* res_end = (igraph_vector_t*)&(VECTOR(*res)[path_count]); */
+
+            /* now, take the parent vertices */
+            parent_vec = (igraph_vector_t*)VECTOR(parents)[node];
+            m = igraph_vector_size(parent_vec);
+
+            /*
+            printf("Calculating shortest paths to vertex %ld\n", node);
+            printf("Parents are: ");
+            igraph_vector_print(parent_vec);
+            */
+
+            for (j = 0; j < m; j++) {
+                /* for each parent, copy the shortest paths leading to that parent
+                 * and add the current vertex in the end */
+                long int parent_node = (long int) VECTOR(*parent_vec)[j];
+                long int parent_path_idx = (long int) VECTOR(*paths_index)[parent_node] - 1;
+                /*
+                printf("  Considering parent: %ld\n", parent_node);
+                printf("  Paths to parent start at index %ld in res\n", parent_path_idx);
+                */
+                assert(parent_path_idx >= 0);
+                for (; parent_path_idx < path_count; parent_path_idx++) {
+                    parent_path = (igraph_vector_t*)VECTOR(*res)[parent_path_idx];
+                    if (igraph_vector_tail(parent_path) != parent_node) {
+                        break;
+                    }
+
+                    path = igraph_Calloc(1, igraph_vector_t);
+                    if (path == 0)
+                        IGRAPH_ERROR("cannot run igraph_get_all_shortest_paths_dijkstra",
+                                     IGRAPH_ENOMEM);
+                    IGRAPH_FINALLY(igraph_free, path);
+                    IGRAPH_CHECK(igraph_vector_copy(path, parent_path));
+                    IGRAPH_CHECK(igraph_vector_ptr_push_back(res, path));
+                    IGRAPH_FINALLY_CLEAN(1);  /* ownership of path passed to res */
+                    IGRAPH_CHECK(igraph_vector_push_back(path, node));
+                }
+            }
+        }
+
+        /* remove the destructor from the path vector */
+        igraph_vector_ptr_set_item_destructor(res, 0);
+
+        /* free those paths from the result vector which we won't need */
+        n = igraph_vector_ptr_size(res);
+        j = 0;
+        for (i = 0; i < n; i++) {
+            igraph_real_t tmp;
+            path = (igraph_vector_t*)VECTOR(*res)[i];
+            tmp = igraph_vector_tail(path);
+            if (is_target[(long int)tmp] == 1) {
+                /* we need this path, keep it */
+                VECTOR(*res)[j] = path;
+                j++;
+            } else {
+                /* we don't need this path, free it */
+                igraph_vector_destroy(path); free(path);
+            }
+        }
+        IGRAPH_CHECK(igraph_vector_ptr_resize(res, j));
+
+        /* sort the paths by the target vertices */
+        igraph_vector_ptr_sort(res, igraph_i_vector_tail_cmp);
+    }
+
+    /* free the allocated memory */
+    igraph_vector_destroy(&order);
+    igraph_Free(is_target);
+    igraph_vector_destroy(&dists);
+    igraph_vector_ptr_destroy_all(&parents);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    return 0;
+}
+
+/**
+ * \function igraph_shortest_paths_bellman_ford
+ * Weighted shortest paths from some sources allowing negative weights.
+ *
+ * This function is the Bellman-Ford algorithm to find the weighted
+ * shortest paths to all vertices from a single source. (It is run
+ * independently for the given sources.). If there are no negative
+ * weights, you are better off with \ref igraph_shortest_paths_dijkstra() .
+ *
+ * \param graph The input graph, can be directed.
+ * \param res The result, a matrix. A pointer to an initialized matrix
+ *    should be passed here, the matrix will be resized if needed.
+ *    Each row contains the distances from a single source, to all
+ *    vertices in the graph, in the order of vertex ids. For unreachable
+ *    vertices the matrix contains \c IGRAPH_INFINITY.
+ * \param from The source vertices.
+ * \param weights The edge weights. There mustn't be any closed loop in
+ *    the graph that has a negative total weight (since this would allow
+ *    us to decrease the weight of any path containing at least a single
+ *    vertex of this loop infinitely). If this is a null pointer, then the
+ *    unweighted version, \ref igraph_shortest_paths() is called.
+ * \param mode For directed graphs; whether to follow paths along edge
+ *    directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or
+ *    ignore edge directions completely (\c IGRAPH_ALL). It is ignored
+ *    for undirected graphs.
+ * \return Error code.
+ *
+ * Time complexity: O(s*|E|*|V|), where |V| is the number of
+ * vertices, |E| the number of edges and s the number of sources.
+ *
+ * \sa \ref igraph_shortest_paths() for a faster unweighted version
+ * or \ref igraph_shortest_paths_dijkstra() if you do not have negative
+ * edge weights.
+ *
+ * \example examples/simple/bellman_ford.c
+ */
+
+int igraph_shortest_paths_bellman_ford(const igraph_t *graph,
+                                       igraph_matrix_t *res,
+                                       const igraph_vs_t from,
+                                       const igraph_vs_t to,
+                                       const igraph_vector_t *weights,
+                                       igraph_neimode_t mode) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_lazy_inclist_t inclist;
+    long int i, j, k;
+    long int no_of_from, no_of_to;
+    igraph_dqueue_t Q;
+    igraph_vector_t clean_vertices;
+    igraph_vector_t num_queued;
+    igraph_vit_t fromvit, tovit;
+    igraph_real_t my_infinity = IGRAPH_INFINITY;
+    igraph_bool_t all_to;
+    igraph_vector_t dist;
+
+    /*
+       - speedup: a vertex is marked clean if its distance from the source
+         did not change during the last phase. Neighbors of a clean vertex
+         are not relaxed again, since it would mean no change in the
+         shortest path values. Dirty vertices are queued. Negative loops can
+         be detected by checking whether a vertex has been queued at least
+         n times.
+    */
+    if (!weights) {
+        return igraph_shortest_paths(graph, res, from, to, mode);
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &fromvit);
+    no_of_from = IGRAPH_VIT_SIZE(fromvit);
+
+    IGRAPH_DQUEUE_INIT_FINALLY(&Q, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&clean_vertices, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&num_queued, no_of_nodes);
+    IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode));
+    IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
+
+    if ( (all_to = igraph_vs_is_all(&to)) ) {
+        no_of_to = no_of_nodes;
+    } else {
+        IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &tovit);
+        no_of_to = IGRAPH_VIT_SIZE(tovit);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes);
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to));
+
+    for (IGRAPH_VIT_RESET(fromvit), i = 0;
+         !IGRAPH_VIT_END(fromvit);
+         IGRAPH_VIT_NEXT(fromvit), i++) {
+        long int source = IGRAPH_VIT_GET(fromvit);
+
+        igraph_vector_fill(&dist, my_infinity);
+        VECTOR(dist)[source] = 0;
+        igraph_vector_null(&clean_vertices);
+        igraph_vector_null(&num_queued);
+
+        /* Fill the queue with vertices to be checked */
+        for (j = 0; j < no_of_nodes; j++) {
+            IGRAPH_CHECK(igraph_dqueue_push(&Q, j));
+        }
+
+        while (!igraph_dqueue_empty(&Q)) {
+            igraph_vector_t *neis;
+            long int nlen;
+
+            j = (long int) igraph_dqueue_pop(&Q);
+            VECTOR(clean_vertices)[j] = 1;
+            VECTOR(num_queued)[j] += 1;
+            if (VECTOR(num_queued)[j] > no_of_nodes) {
+                IGRAPH_ERROR("cannot run Bellman-Ford algorithm", IGRAPH_ENEGLOOP);
+            }
+
+            /* If we cannot get to j in finite time yet, there is no need to relax
+             * its edges */
+            if (!IGRAPH_FINITE(VECTOR(dist)[j])) {
+                continue;
+            }
+
+            neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) j);
+            nlen = igraph_vector_size(neis);
+
+            for (k = 0; k < nlen; k++) {
+                long int nei = (long int) VECTOR(*neis)[k];
+                long int target = IGRAPH_OTHER(graph, nei, j);
+                if (VECTOR(dist)[target] > VECTOR(dist)[j] + VECTOR(*weights)[nei]) {
+                    /* relax the edge */
+                    VECTOR(dist)[target] = VECTOR(dist)[j] + VECTOR(*weights)[nei];
+                    if (VECTOR(clean_vertices)[target]) {
+                        VECTOR(clean_vertices)[target] = 0;
+                        IGRAPH_CHECK(igraph_dqueue_push(&Q, target));
+                    }
+                }
+            }
+        }
+
+        /* Copy it to the result */
+        if (all_to) {
+            igraph_matrix_set_row(res, &dist, i);
+        } else {
+            for (IGRAPH_VIT_RESET(tovit), j = 0; !IGRAPH_VIT_END(tovit);
+                 IGRAPH_VIT_NEXT(tovit), j++) {
+                long int v = IGRAPH_VIT_GET(tovit);
+                MATRIX(*res, i, j) = VECTOR(dist)[v];
+            }
+        }
+    }
+
+    igraph_vector_destroy(&dist);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    if (!all_to) {
+        igraph_vit_destroy(&tovit);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vit_destroy(&fromvit);
+    igraph_dqueue_destroy(&Q);
+    igraph_vector_destroy(&clean_vertices);
+    igraph_vector_destroy(&num_queued);
+    igraph_lazy_inclist_destroy(&inclist);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+/**
+ * \function igraph_shortest_paths_johnson
+ * Calculate shortest paths from some sources using Johnson's algorithm.
+ *
+ * See Wikipedia at http://en.wikipedia.org/wiki/Johnson's_algorithm
+ * for Johnson's algorithm. This algorithm works even if the graph
+ * contains negative edge weights, and it is worth using it if we
+ * calculate the shortest paths from many sources.
+ *
+ * </para><para> If no edge weights are supplied, then the unweighted
+ * version, \ref igraph_shortest_paths() is called.
+ *
+ * </para><para> If all the supplied edge weights are non-negative,
+ * then Dijkstra's algorithm is used by calling
+ * \ref igraph_shortest_paths_dijkstra().
+ *
+ * \param graph The input graph, typically it is directed.
+ * \param res Pointer to an initialized matrix, the result will be
+ *   stored here, one line for each source vertex, one column for each
+ *   target vertex.
+ * \param from The source vertices.
+ * \param to The target vertices. It is not allowed to include a
+ *   vertex twice or more.
+ * \param weights Optional edge weights. If it is a null-pointer, then
+ *   the unweighted breadth-first search based \ref
+ *   igraph_shortest_paths() will be called.
+ * \return Error code.
+ *
+ * Time complexity: O(s|V|log|V|+|V||E|), |V| and |E| are the number
+ * of vertices and edges, s is the number of source vertices.
+ *
+ * \sa \ref igraph_shortest_paths() for a faster unweighted version
+ * or \ref igraph_shortest_paths_dijkstra() if you do not have negative
+ * edge weights, \ref igraph_shortest_paths_bellman_ford() if you only
+ * need to calculate shortest paths from a couple of sources.
+ */
+
+int igraph_shortest_paths_johnson(const igraph_t *graph,
+                                  igraph_matrix_t *res,
+                                  const igraph_vs_t from,
+                                  const igraph_vs_t to,
+                                  const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_t newgraph;
+    igraph_vector_t edges, newweights;
+    igraph_matrix_t bfres;
+    long int i, ptr;
+    long int nr, nc;
+    igraph_vit_t fromvit;
+
+    /* If no weights, then we can just run the unweighted version */
+    if (!weights) {
+        return igraph_shortest_paths(graph, res, from, to, IGRAPH_OUT);
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL);
+    }
+
+    /* If no negative weights, then we can run Dijkstra's algorithm */
+    if (igraph_vector_min(weights) >= 0) {
+        return igraph_shortest_paths_dijkstra(graph, res, from, to,
+                                              weights, IGRAPH_OUT);
+    }
+
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Johnson's shortest path: undirected graph and negative weight",
+                     IGRAPH_EINVAL);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* -------------------- Otherwise proceed --------------------- */
+
+    IGRAPH_MATRIX_INIT_FINALLY(&bfres, 0, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&newweights, 0);
+
+    IGRAPH_CHECK(igraph_empty(&newgraph, (igraph_integer_t) no_of_nodes + 1,
+                              igraph_is_directed(graph)));
+    IGRAPH_FINALLY(igraph_destroy, &newgraph);
+
+    /* Add a new node to the graph, plus edges from it to all the others. */
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2 + no_of_nodes * 2);
+    igraph_get_edgelist(graph, &edges, /*bycol=*/ 0);
+    igraph_vector_resize(&edges, no_of_edges * 2 + no_of_nodes * 2);
+    for (i = 0, ptr = no_of_edges * 2; i < no_of_nodes; i++) {
+        VECTOR(edges)[ptr++] = no_of_nodes;
+        VECTOR(edges)[ptr++] = i;
+    }
+    IGRAPH_CHECK(igraph_add_edges(&newgraph, &edges, 0));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_CHECK(igraph_vector_reserve(&newweights, no_of_edges + no_of_nodes));
+    igraph_vector_update(&newweights, weights);
+    igraph_vector_resize(&newweights, no_of_edges + no_of_nodes);
+    for (i = no_of_edges; i < no_of_edges + no_of_nodes; i++) {
+        VECTOR(newweights)[i] = 0;
+    }
+
+    /* Run Bellmann-Ford algorithm on the new graph, starting from the
+       new vertex.  */
+
+    IGRAPH_CHECK(igraph_shortest_paths_bellman_ford(&newgraph, &bfres,
+                 igraph_vss_1((igraph_integer_t) no_of_nodes),
+                 igraph_vss_all(), &newweights, IGRAPH_OUT));
+
+    igraph_destroy(&newgraph);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Now the edges of the original graph are reweighted, using the
+       values from the BF algorithm. Instead of w(u,v) we will have
+       w(u,v) + h(u) - h(v) */
+
+    igraph_vector_resize(&newweights, no_of_edges);
+    for (i = 0; i < no_of_edges; i++) {
+        long int ffrom = IGRAPH_FROM(graph, i);
+        long int tto = IGRAPH_TO(graph, i);
+        VECTOR(newweights)[i] += MATRIX(bfres, 0, ffrom) - MATRIX(bfres, 0, tto);
+    }
+
+    /* Run Dijkstra's algorithm on the new weights */
+    IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, res, from,
+                 to, &newweights,
+                 IGRAPH_OUT));
+
+    igraph_vector_destroy(&newweights);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Reweight the shortest paths */
+    nr = igraph_matrix_nrow(res);
+    nc = igraph_matrix_ncol(res);
+
+    IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &fromvit);
+
+    for (i = 0; i < nr; i++, IGRAPH_VIT_NEXT(fromvit)) {
+        long int v1 = IGRAPH_VIT_GET(fromvit);
+        if (igraph_vs_is_all(&to)) {
+            long int v2;
+            for (v2 = 0; v2 < nc; v2++) {
+                igraph_real_t sub = MATRIX(bfres, 0, v1) - MATRIX(bfres, 0, v2);
+                MATRIX(*res, i, v2) -= sub;
+            }
+        } else {
+            long int j;
+            igraph_vit_t tovit;
+            IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit));
+            IGRAPH_FINALLY(igraph_vit_destroy, &tovit);
+            for (j = 0, IGRAPH_VIT_RESET(tovit); j < nc; j++, IGRAPH_VIT_NEXT(tovit)) {
+                long int v2 = IGRAPH_VIT_GET(tovit);
+                igraph_real_t sub = MATRIX(bfres, 0, v1) - MATRIX(bfres, 0, v2);
+                MATRIX(*res, i, v2) -= sub;
+            }
+            igraph_vit_destroy(&tovit);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    igraph_vit_destroy(&fromvit);
+    igraph_matrix_destroy(&bfres);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_unfold_tree
+ * Unfolding a graph into a tree, by possibly multiplicating its vertices.
+ *
+ * A graph is converted into a tree (or forest, if it is unconnected),
+ * by performing a breadth-first search on it, and replicating
+ * vertices that were found a second, third, etc. time.
+ * \param graph The input graph, it can be either directed or
+ *   undirected.
+ * \param tree Pointer to an uninitialized graph object, the result is
+ *   stored here.
+ * \param mode For directed graphs; whether to follow paths along edge
+ *    directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or
+ *    ignore edge directions completely (\c IGRAPH_ALL). It is ignored
+ *    for undirected graphs.
+ * \param roots A numeric vector giving the root vertex, or vertices
+ *   (if the graph is not connected), to start from.
+ * \param vertex_index Pointer to an initialized vector, or a null
+ *   pointer. If not a null pointer, then a mapping from the vertices
+ *   in the new graph to the ones in the original is created here.
+ * \return Error code.
+ *
+ * Time complexity: O(n+m), linear in the number vertices and edges.
+ *
+ */
+
+int igraph_unfold_tree(const igraph_t *graph, igraph_t *tree,
+                       igraph_neimode_t mode, const igraph_vector_t *roots,
+                       igraph_vector_t *vertex_index) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int no_of_roots = igraph_vector_size(roots);
+    long int tree_vertex_count = no_of_nodes;
+
+    igraph_vector_t edges;
+    igraph_vector_bool_t seen_vertices;
+    igraph_vector_bool_t seen_edges;
+
+    igraph_dqueue_t Q;
+    igraph_vector_t neis;
+
+    long int i, n, r, v_ptr = no_of_nodes;
+
+    /* TODO: handle not-connected graphs, multiple root vertices */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    igraph_vector_reserve(&edges, no_of_edges * 2);
+    IGRAPH_DQUEUE_INIT_FINALLY(&Q, 100);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen_vertices, no_of_nodes);
+    IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen_edges, no_of_edges);
+
+    if (vertex_index) {
+        IGRAPH_CHECK(igraph_vector_resize(vertex_index, no_of_nodes));
+        for (i = 0; i < no_of_nodes; i++) {
+            VECTOR(*vertex_index)[i] = i;
+        }
+    }
+
+    for (r = 0; r < no_of_roots; r++) {
+
+        long int root = (long int) VECTOR(*roots)[r];
+        VECTOR(seen_vertices)[root] = 1;
+        igraph_dqueue_push(&Q, root);
+
+        while (!igraph_dqueue_empty(&Q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&Q);
+
+            IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) actnode, mode));
+            n = igraph_vector_size(&neis);
+            for (i = 0; i < n; i++) {
+
+                long int edge = (long int) VECTOR(neis)[i];
+                long int from = IGRAPH_FROM(graph, edge);
+                long int to = IGRAPH_TO(graph, edge);
+                long int nei = IGRAPH_OTHER(graph, edge, actnode);
+
+                if (! VECTOR(seen_edges)[edge]) {
+
+                    VECTOR(seen_edges)[edge] = 1;
+
+                    if (! VECTOR(seen_vertices)[nei]) {
+
+                        igraph_vector_push_back(&edges, from);
+                        igraph_vector_push_back(&edges, to);
+
+                        VECTOR(seen_vertices)[nei] = 1;
+                        IGRAPH_CHECK(igraph_dqueue_push(&Q, nei));
+
+                    } else {
+
+                        tree_vertex_count++;
+                        if (vertex_index) {
+                            IGRAPH_CHECK(igraph_vector_push_back(vertex_index, nei));
+                        }
+
+                        if (from == nei) {
+                            igraph_vector_push_back(&edges, v_ptr++);
+                            igraph_vector_push_back(&edges, to);
+                        } else {
+                            igraph_vector_push_back(&edges, from);
+                            igraph_vector_push_back(&edges, v_ptr++);
+                        }
+                    }
+                }
+
+            } /* for i<n */
+
+        } /* ! igraph_dqueue_empty(&Q) */
+
+    } /* r < igraph_vector_size(roots) */
+
+    igraph_vector_bool_destroy(&seen_edges);
+    igraph_vector_bool_destroy(&seen_vertices);
+    igraph_vector_destroy(&neis);
+    igraph_dqueue_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    IGRAPH_CHECK(igraph_create(tree, &edges, tree_vertex_count, igraph_is_directed(graph)));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_is_mutual
+ * Check whether the edges of a directed graph are mutual.
+ *
+ * An (A,B) edge is mutual if the graph contains the (B,A) edge, too.
+ * </para>
+ *
+ * <para>An undirected graph only has mutual edges, by definition.
+ * </para>
+ *
+ * <para>Edge multiplicity is not considered here, e.g. if there are two
+ * (A,B) edges and one (B,A) edge, then all three are considered to be
+ * mutual.
+ *
+ * \param graph The input graph.
+ * \param res Pointer to an initialized vector, the result is stored
+ *        here.
+ * \param es The sequence of edges to check. Supply
+ *        <code>igraph_ess_all()</code> for all edges, see \ref
+ *        igraph_ess_all().
+ * \return Error code.
+ *
+ * Time complexity: O(n log(d)), n is the number of edges supplied, d
+ * is the maximum in-degree of the vertices that are targets of the
+ * supplied edges. An upper limit of the time complexity is O(n log(|E|)),
+ * |E| is the number of edges in the graph.
+ */
+
+int igraph_is_mutual(igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es) {
+
+    igraph_eit_t eit;
+    igraph_lazy_adjlist_t adjlist;
+    long int i;
+
+    /* How many edges do we have? */
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+    IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit)));
+
+    /* An undirected graph has mutual edges by definition,
+       res is already properly resized */
+    if (! igraph_is_directed(graph)) {
+        igraph_vector_bool_fill(res, 1);
+        igraph_eit_destroy(&eit);
+        IGRAPH_FINALLY_CLEAN(1);
+        return 0;
+    }
+
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_DONT_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
+
+    for (i = 0; ! IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) {
+        long int edge = IGRAPH_EIT_GET(eit);
+        long int from = IGRAPH_FROM(graph, edge);
+        long int to = IGRAPH_TO(graph, edge);
+
+        /* Check whether there is a to->from edge, search for from in the
+           out-list of to. We don't search an empty vector, because
+           vector_binsearch seems to have a bug with this. */
+        igraph_vector_t *neis = igraph_lazy_adjlist_get(&adjlist,
+                                (igraph_integer_t) to);
+        if (igraph_vector_empty(neis)) {
+            VECTOR(*res)[i] = 0;
+        } else {
+            VECTOR(*res)[i] = igraph_vector_binsearch2(neis, from);
+        }
+    }
+
+    igraph_lazy_adjlist_destroy(&adjlist);
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph,
+        igraph_vs_t vids,
+        igraph_neimode_t mode,
+        igraph_neimode_t neighbor_degree_mode,
+        igraph_vector_t *knn,
+        igraph_vector_t *knnk,
+        const igraph_vector_t *weights);
+
+int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph,
+        igraph_vs_t vids,
+        igraph_neimode_t mode,
+        igraph_neimode_t neighbor_degree_mode,
+        igraph_vector_t *knn,
+        igraph_vector_t *knnk,
+        const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t neis, edge_neis;
+    long int i, j, no_vids;
+    igraph_vit_t vit;
+    igraph_vector_t my_knn_v, *my_knn = knn;
+    igraph_vector_t strength, deg;
+    igraph_integer_t maxdeg;
+    igraph_vector_t deghist;
+    igraph_real_t mynan = IGRAPH_NAN;
+
+    if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weight vector size", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    no_vids = IGRAPH_VIT_SIZE(vit);
+
+    if (!knn) {
+        IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids);
+        my_knn = &my_knn_v;
+    } else {
+        IGRAPH_CHECK(igraph_vector_resize(knn, no_vids));
+    }
+
+    // Get degree of neighbours
+    IGRAPH_VECTOR_INIT_FINALLY(&deg, no_of_nodes);
+    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(),
+                               neighbor_degree_mode, IGRAPH_LOOPS));
+    IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes);
+
+    // Get strength of all nodes
+    IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(),
+                                 mode, IGRAPH_LOOPS, weights));
+
+    // Get maximum degree for initialization
+    IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(),
+                                  mode, IGRAPH_LOOPS));
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, (long int)maxdeg);
+    IGRAPH_VECTOR_INIT_FINALLY(&edge_neis, (long int)maxdeg);
+    igraph_vector_resize(&neis, 0);
+    igraph_vector_resize(&edge_neis, 0);
+
+    if (knnk) {
+        IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg));
+        igraph_vector_null(knnk);
+        IGRAPH_VECTOR_INIT_FINALLY(&deghist, (long int)maxdeg);
+    }
+
+    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        igraph_real_t sum = 0.0;
+        long int v = IGRAPH_VIT_GET(vit);
+        long int nv;
+        igraph_real_t str = VECTOR(strength)[v];
+        // Get neighbours and incident edges
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, mode));
+        IGRAPH_CHECK(igraph_incident(graph, &edge_neis, (igraph_integer_t) v, mode));
+        nv = igraph_vector_size(&neis);
+        for (j = 0; j < nv; j++) {
+            long int nei = (long int) VECTOR(neis)[j];
+            long int e = (long int) VECTOR(edge_neis)[j];
+            double w = VECTOR(*weights)[e];
+            sum += w * VECTOR(deg)[nei];
+        }
+        if (str != 0.0) {
+            VECTOR(*my_knn)[i] = sum / str;
+        } else {
+            VECTOR(*my_knn)[i] = mynan;
+        }
+        if (knnk && nv > 0) {
+            VECTOR(*knnk)[nv - 1] += VECTOR(*my_knn)[i];
+            VECTOR(deghist)[nv - 1] += 1;
+        }
+    }
+
+    if (knnk) {
+        for (i = 0; i < maxdeg; i++) {
+            igraph_real_t dh = VECTOR(deghist)[i];
+            if (dh != 0) {
+                VECTOR(*knnk)[i] /= dh;
+            } else {
+                VECTOR(*knnk)[i] = mynan;
+            }
+        }
+
+        igraph_vector_destroy(&deghist);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&deg);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    if (!knn) {
+        igraph_vector_destroy(&my_knn_v);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_avg_nearest_neighbor_degree
+ * Average nearest neighbor degree.
+ *
+ * Calculates the average degree of the neighbors for each vertex, and
+ * optionally, the same quantity in the function of vertex degree.
+ *
+ * </para><para>For isolate vertices \p knn is set to \c
+ * IGRAPH_NAN. The same is done in \p knnk for vertex degrees that
+ * don't appear in the graph.
+ *
+ * \param graph The input graph, it can be directed but the
+ *   directedness of the edges is ignored.
+ * \param vids The vertices for which the calculation is performed.
+ * \param mode The neighbors over which is averaged.
+ * \param neighbor_degree_mode The degree of the neighbors which is
+ *   averaged.
+ * \param vids The vertices for which the calculation is performed.
+ * \param knn Pointer to an initialized vector, the result will be
+ *   stored here. It will be resized as needed. Supply a NULL pointer
+ *   here, if you only want to calculate \c knnk.
+ * \param knnk Pointer to an initialized vector, the average nearest
+ *   neighbor degree in the function of vertex degree is stored
+ *   here. The first (zeroth) element is for degree one vertices,
+ *   etc. Supply a NULL pointer here if you don't want to calculate
+ *   this.
+ * \param weights Optional edge weights. Supply a null pointer here
+ *   for the non-weighted version. The weighted version computes
+ *   a weighted average of the neighbor degrees, i.e.
+ *
+ *    k_nn_i = 1/s_i sum_j w_ij k_j
+ *
+ *   where s_i is the sum of the weights, the sum runs over
+ *   the neighbors as indicated by \c mode (with appropriate weights)
+ *   and k_j is the degree, specified by \c neighbor_degree_mode.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ *
+ * \example examples/simple/igraph_knn.c
+ */
+
+int igraph_avg_nearest_neighbor_degree(const igraph_t *graph,
+                                       igraph_vs_t vids,
+                                       igraph_neimode_t mode,
+                                       igraph_neimode_t neighbor_degree_mode,
+                                       igraph_vector_t *knn,
+                                       igraph_vector_t *knnk,
+                                       const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t neis;
+    long int i, j, no_vids;
+    igraph_vit_t vit;
+    igraph_vector_t my_knn_v, *my_knn = knn;
+    igraph_vector_t deg;
+    igraph_integer_t maxdeg;
+    igraph_vector_t deghist;
+    igraph_real_t mynan = IGRAPH_NAN;
+    igraph_bool_t simple;
+
+    IGRAPH_CHECK(igraph_is_simple(graph, &simple));
+    if (!simple) {
+        IGRAPH_ERROR("Average nearest neighbor degree works only with "
+                     "simple graphs", IGRAPH_EINVAL);
+    }
+
+    if (weights) {
+        return igraph_i_avg_nearest_neighbor_degree_weighted(graph, vids,
+                mode, neighbor_degree_mode, knn, knnk, weights);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    no_vids = IGRAPH_VIT_SIZE(vit);
+
+    if (!knn) {
+        IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids);
+        my_knn = &my_knn_v;
+    } else {
+        IGRAPH_CHECK(igraph_vector_resize(knn, no_vids));
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&deg, no_of_nodes);
+    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(),
+                               neighbor_degree_mode, IGRAPH_LOOPS));
+    igraph_maxdegree(graph, &maxdeg, igraph_vss_all(), mode, IGRAPH_LOOPS);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg);
+    igraph_vector_resize(&neis, 0);
+
+    if (knnk) {
+        IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg));
+        igraph_vector_null(knnk);
+        IGRAPH_VECTOR_INIT_FINALLY(&deghist, (long int)maxdeg);
+    }
+
+    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        igraph_real_t sum = 0.0;
+        long int v = IGRAPH_VIT_GET(vit);
+        long int nv;
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, mode));
+        nv = igraph_vector_size(&neis);
+        for (j = 0; j < nv; j++) {
+            long int nei = (long int) VECTOR(neis)[j];
+            sum += VECTOR(deg)[nei];
+        }
+        if (nv != 0) {
+            VECTOR(*my_knn)[i] = sum / nv;
+        } else {
+            VECTOR(*my_knn)[i] = mynan;
+        }
+        if (knnk && nv > 0) {
+            VECTOR(*knnk)[nv - 1] += VECTOR(*my_knn)[i];
+            VECTOR(deghist)[nv - 1] += 1;
+        }
+    }
+
+    if (knnk) {
+        for (i = 0; i < maxdeg; i++) {
+            long int dh = (long int) VECTOR(deghist)[i];
+            if (dh != 0) {
+                VECTOR(*knnk)[i] /= dh;
+            } else {
+                VECTOR(*knnk)[i] = mynan;
+            }
+        }
+        igraph_vector_destroy(&deghist);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&deg);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (!knn) {
+        igraph_vector_destroy(&my_knn_v);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_strength
+ * Strength of the vertices, weighted vertex degree in other words.
+ *
+ * In a weighted network the strength of a vertex is the sum of the
+ * weights of all incident edges. In a non-weighted network this is
+ * exactly the vertex degree.
+ * \param graph The input graph.
+ * \param res Pointer to an initialized vector, the result is stored
+ *   here. It will be resized as needed.
+ * \param vids The vertices for which the calculation is performed.
+ * \param mode Gives whether to count only outgoing (\c IGRAPH_OUT),
+ *   incoming (\c IGRAPH_IN) edges or both (\c IGRAPH_ALL).
+ * \param loops A logical scalar, whether to count loop edges as well.
+ * \param weights A vector giving the edge weights. If this is a NULL
+ *   pointer, then \ref igraph_degree() is called to perform the
+ *   calculation.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number vertices and
+ * edges.
+ *
+ * \sa \ref igraph_degree() for the traditional, non-weighted version.
+ */
+
+int igraph_strength(const igraph_t *graph, igraph_vector_t *res,
+                    const igraph_vs_t vids, igraph_neimode_t mode,
+                    igraph_bool_t loops, const igraph_vector_t *weights) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vit_t vit;
+    long int no_vids;
+    igraph_vector_t neis;
+    long int i;
+
+    if (!weights) {
+        return igraph_degree(graph, res, vids, mode, loops);
+    }
+
+    if (igraph_vector_size(weights) != igraph_ecount(graph)) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    no_vids = IGRAPH_VIT_SIZE(vit);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&neis, no_of_nodes));
+    IGRAPH_CHECK(igraph_vector_resize(res, no_vids));
+    igraph_vector_null(res);
+
+    if (loops) {
+        for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+            long int vid = IGRAPH_VIT_GET(vit);
+            long int j, n;
+            IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) vid, mode));
+            n = igraph_vector_size(&neis);
+            for (j = 0; j < n; j++) {
+                long int edge = (long int) VECTOR(neis)[j];
+                VECTOR(*res)[i] += VECTOR(*weights)[edge];
+            }
+        }
+    } else {
+        for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+            long int vid = IGRAPH_VIT_GET(vit);
+            long int j, n;
+            IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) vid, mode));
+            n = igraph_vector_size(&neis);
+            for (j = 0; j < n; j++) {
+                long int edge = (long int) VECTOR(neis)[j];
+                long int from = IGRAPH_FROM(graph, edge);
+                long int to = IGRAPH_TO(graph, edge);
+                if (from != to) {
+                    VECTOR(*res)[i] += VECTOR(*weights)[edge];
+                }
+            }
+        }
+    }
+
+    igraph_vit_destroy(&vit);
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+/**
+ * \function igraph_diameter_dijkstra
+ * Weighted diameter using Dijkstra's algorithm, non-negative weights only.
+ *
+ * The diameter of a graph is its longest geodesic. I.e. the
+ * (weighted) shortest path is calculated for all pairs of vertices
+ * and the longest one is the diameter.
+ * \param graph The input graph, can be directed or undirected.
+ * \param pres Pointer to a real number, if not \c NULL then it will contain
+ *        the diameter (the actual distance).
+ * \param pfrom Pointer to an integer, if not \c NULL it will be set to the
+ *        source vertex of the diameter path.
+ * \param pto Pointer to an integer, if not \c NULL it will be set to the
+ *        target vertex of the diameter path.
+ * \param path Pointer to an initialized vector. If not \c NULL the actual
+ *        longest geodesic path will be stored here. The vector will be
+ *        resized as needed.
+ * \param directed Boolean, whether to consider directed
+ *        paths. Ignored for undirected graphs.
+ * \param unconn What to do if the graph is not connected. If
+ *        \c TRUE the longest geodesic within a component
+ *        will be returned, otherwise \c IGRAPH_INFINITY is
+ *        returned.
+ * \return Error code.
+ *
+ * Time complexity: O(|V||E|*log|E|), |V| is the number of vertices,
+ * |E| is the number of edges.
+ */
+
+int igraph_diameter_dijkstra(const igraph_t *graph,
+                             const igraph_vector_t *weights,
+                             igraph_real_t *pres,
+                             igraph_integer_t *pfrom,
+                             igraph_integer_t *pto,
+                             igraph_vector_t *path,
+                             igraph_bool_t directed,
+                             igraph_bool_t unconn) {
+
+    /* Implementation details. This is the basic Dijkstra algorithm,
+       with a binary heap. The heap is indexed, i.e. it stores not only
+       the distances, but also which vertex they belong to.
+
+       From now on we use a 2-way heap, so the distances can be queried
+       directly from the heap.
+
+       Dirty tricks:
+       - the opposite of the distance is stored in the heap, as it is a
+         maximum heap and we need a minimum heap.
+       - we don't use IGRAPH_INFINITY during the computation, as IGRAPH_FINITE()
+         might involve a function call and we want to spare that. -1 will denote
+         infinity instead.
+    */
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+
+    igraph_2wheap_t Q;
+    igraph_inclist_t inclist;
+    long int source, j;
+    igraph_neimode_t dirmode = directed ? IGRAPH_OUT : IGRAPH_ALL;
+
+    long int from = -1, to = -1;
+    igraph_real_t res = 0;
+    long int nodes_reached = 0;
+
+    if (!weights) {
+        igraph_integer_t diameter;
+        IGRAPH_CHECK(igraph_diameter(graph, &diameter, pfrom, pto, path, directed, unconn));
+        if (pres) {
+            *pres = diameter;
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    if (weights && igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_min(weights) < 0) {
+        IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
+    IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
+    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, dirmode));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
+
+    for (source = 0; source < no_of_nodes; source++) {
+
+        IGRAPH_PROGRESS("Weighted diameter: ", source * 100.0 / no_of_nodes, NULL);
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        igraph_2wheap_clear(&Q);
+        igraph_2wheap_push_with_index(&Q, source, -1.0);
+
+        nodes_reached = 0.0;
+
+        while (!igraph_2wheap_empty(&Q)) {
+            long int minnei = igraph_2wheap_max_index(&Q);
+            igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q);
+            igraph_vector_int_t *neis;
+            long int nlen;
+
+            if (mindist > res) {
+                res = mindist; from = source; to = minnei;
+            }
+            nodes_reached++;
+
+            /* Now check all neighbors of 'minnei' for a shorter path */
+            neis = igraph_inclist_get(&inclist, minnei);
+            nlen = igraph_vector_int_size(neis);
+            for (j = 0; j < nlen; j++) {
+                long int edge = (long int) VECTOR(*neis)[j];
+                long int tto = IGRAPH_OTHER(graph, edge, minnei);
+                igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
+                igraph_bool_t active = igraph_2wheap_has_active(&Q, tto);
+                igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto);
+                igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0;
+
+                if (!has) {
+                    /* First finite distance */
+                    IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist));
+                } else if (altdist < curdist) {
+                    /* A shorter path */
+                    IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist));
+                }
+            }
+
+        } /* !igraph_2wheap_empty(&Q) */
+
+        /* not connected, return infinity */
+        if (nodes_reached != no_of_nodes && !unconn) {
+            res = IGRAPH_INFINITY;
+            from = to = -1;
+            break;
+        }
+
+    } /* source < no_of_nodes */
+
+    /* Compensate for the +1 that we have added to distances */
+    res -= 1;
+
+    igraph_inclist_destroy(&inclist);
+    igraph_2wheap_destroy(&Q);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_PROGRESS("Weighted diameter: ", 100.0, NULL);
+
+    if (pres) {
+        *pres = res;
+    }
+    if (pfrom) {
+        *pfrom = (igraph_integer_t) from;
+    }
+    if (pto) {
+        *pto = (igraph_integer_t) to;
+    }
+    if (path) {
+        if (!igraph_finite(res)) {
+            igraph_vector_clear(path);
+        } else {
+            igraph_vector_ptr_t tmpptr;
+            igraph_vector_ptr_init(&tmpptr, 1);
+            IGRAPH_FINALLY(igraph_vector_ptr_destroy, &tmpptr);
+            VECTOR(tmpptr)[0] = path;
+            IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph,
+                         /*vertices=*/ &tmpptr, /*edges=*/ 0,
+                         (igraph_integer_t) from,
+                         igraph_vss_1((igraph_integer_t) to),
+                         weights, dirmode, /*predecessors=*/ 0,
+                         /*inbound_edges=*/ 0));
+            igraph_vector_ptr_destroy(&tmpptr);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_sort_vertex_ids_by_degree
+ * \brief Calculate a list of vertex ids sorted by degree of the corresponding vertex.
+ *
+ * The list of vertex ids is returned in a vector that is sorted
+ * in ascending or descending order of vertex degree.
+ *
+ * \param graph The input graph.
+ * \param outvids Pointer to an initialized vector that will be
+ *        resized and will contain the ordered vertex ids.
+ * \param vids Input vertex selector of vertex ids to include in
+ *        calculation.
+ * \param mode Defines the type of the degree.
+ *        \c IGRAPH_OUT, out-degree,
+ *        \c IGRAPH_IN, in-degree,
+ *        \c IGRAPH_ALL, total degree (sum of the
+ *        in- and out-degree).
+ *        This parameter is ignored for undirected graphs.
+ * \param loops Boolean, gives whether the self-loops should be
+ *        counted.
+ * \param order Specifies whether the ordering should be ascending
+ *        (\c IGRAPH_ASCENDING) or descending (\c IGRAPH_DESCENDING).
+ * \param only_indices If true, then return a sorted list of indices
+ *        into a vector corresponding to \c vids, rather than a list
+ *        of vertex ids. This parameter is ignored if \c vids is set
+ *        to all vertices via igraph_vs_all() or igraph_vss_all(),
+ *        because in this case the indices and vertex ids are the
+ *        same.
+ * \return Error code:
+ *         \c IGRAPH_EINVVID: invalid vertex id.
+ *         \c IGRAPH_EINVMODE: invalid mode argument.
+ *
+ */
+
+int igraph_sort_vertex_ids_by_degree(const igraph_t *graph,
+                                     igraph_vector_t *outvids,
+                                     igraph_vs_t vids,
+                                     igraph_neimode_t mode,
+                                     igraph_bool_t loops,
+                                     igraph_order_t order,
+                                     igraph_bool_t only_indices) {
+    long int i;
+    igraph_vector_t degrees, vs_vec;
+    IGRAPH_VECTOR_INIT_FINALLY(&degrees, 0);
+    IGRAPH_CHECK(igraph_degree(graph, &degrees, vids, mode, loops));
+    IGRAPH_CHECK((int) igraph_vector_qsort_ind(&degrees, outvids,
+                 order == IGRAPH_DESCENDING));
+    if (only_indices || igraph_vs_is_all(&vids) ) {
+        igraph_vector_destroy(&degrees);
+        IGRAPH_FINALLY_CLEAN(1);
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&vs_vec, 0);
+        IGRAPH_CHECK(igraph_vs_as_vector(graph, vids, &vs_vec));
+        for (i = 0; i < igraph_vector_size(outvids); i++) {
+            VECTOR(*outvids)[i] = VECTOR(vs_vec)[(long int)VECTOR(*outvids)[i]];
+        }
+        igraph_vector_destroy(&vs_vec);
+        igraph_vector_destroy(&degrees);
+        IGRAPH_FINALLY_CLEAN(2);
+    }
+    return 0;
+}
+
+/**
+ * \function igraph_contract_vertices
+ * Replace multiple vertices with a single one.
+ *
+ * This function creates a new graph, by merging several
+ * vertices into one. The vertices in the new graph correspond
+ * to sets of vertices in the input graph.
+ * \param graph The input graph, it can be directed or
+ *        undirected.
+ * \param mapping A vector giving the mapping. For each
+ *        vertex in the original graph, it should contain
+ *        its id in the new graph.
+ * \param vertex_comb What to do with the vertex attributes.
+ *        See the igraph manual section about attributes for
+ *        details.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number
+ * or vertices plus edges.
+ */
+
+int igraph_contract_vertices(igraph_t *graph,
+                             const igraph_vector_t *mapping,
+                             const igraph_attribute_combination_t
+                             *vertex_comb) {
+    igraph_vector_t edges;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_bool_t vattr = vertex_comb && igraph_has_attribute_table();
+    igraph_t res;
+    long int e, last = -1;
+    long int no_new_vertices;
+
+    if (igraph_vector_size(mapping) != no_of_nodes) {
+        IGRAPH_ERROR("Invalid mapping vector length",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+
+    if (no_of_nodes > 0) {
+        last = (long int) igraph_vector_max(mapping);
+    }
+
+    for (e = 0; e < no_of_edges; e++) {
+        long int from = IGRAPH_FROM(graph, e);
+        long int to = IGRAPH_TO(graph, e);
+
+        long int nfrom = (long int) VECTOR(*mapping)[from];
+        long int nto = (long int) VECTOR(*mapping)[to];
+
+        igraph_vector_push_back(&edges, nfrom);
+        igraph_vector_push_back(&edges, nto);
+
+        if (nfrom > last) {
+            last = nfrom;
+        }
+        if (nto   > last) {
+            last = nto;
+        }
+    }
+
+    no_new_vertices = last + 1;
+
+    IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_new_vertices,
+                               igraph_is_directed(graph)));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_FINALLY(igraph_destroy, &res);
+
+    IGRAPH_I_ATTRIBUTE_DESTROY(&res);
+    IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ 1,
+                            /*vertex=*/ 0, /*edge=*/ 1);
+
+    if (vattr) {
+        long int i;
+        igraph_vector_ptr_t merges;
+        igraph_vector_t sizes;
+        igraph_vector_t *vecs;
+
+        vecs = igraph_Calloc(no_new_vertices, igraph_vector_t);
+        if (!vecs) {
+            IGRAPH_ERROR("Cannot combine attributes while contracting"
+                         " vertices", IGRAPH_ENOMEM);
+        }
+        IGRAPH_FINALLY(igraph_free, vecs);
+        IGRAPH_CHECK(igraph_vector_ptr_init(&merges, no_new_vertices));
+        IGRAPH_FINALLY(igraph_i_simplify_free, &merges);
+        IGRAPH_VECTOR_INIT_FINALLY(&sizes, no_new_vertices);
+
+        for (i = 0; i < no_of_nodes; i++) {
+            long int to = (long int) VECTOR(*mapping)[i];
+            VECTOR(sizes)[to] += 1;
+        }
+        for (i = 0; i < no_new_vertices; i++) {
+            igraph_vector_t *v = &vecs[i];
+            IGRAPH_CHECK(igraph_vector_init(v, (long int) VECTOR(sizes)[i]));
+            igraph_vector_clear(v);
+            VECTOR(merges)[i] = v;
+        }
+        for (i = 0; i < no_of_nodes; i++) {
+            long int to = (long int) VECTOR(*mapping)[i];
+            igraph_vector_t *v = &vecs[to];
+            igraph_vector_push_back(v, i);
+        }
+
+        IGRAPH_CHECK(igraph_i_attribute_combine_vertices(graph, &res,
+                     &merges,
+                     vertex_comb));
+
+        igraph_vector_destroy(&sizes);
+        igraph_i_simplify_free(&merges);
+        igraph_free(vecs);
+        IGRAPH_FINALLY_CLEAN(3);
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+    igraph_destroy(graph);
+    *graph = res;
+
+    return 0;
+}
+
+/* Create the transitive closure of a tree graph.
+   This is fairly simple, we just collect all ancestors of a vertex
+   using a depth-first search.
+ */
+
+int igraph_transitive_closure_dag(const igraph_t *graph,
+                                  igraph_t *closure) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t deg;
+    igraph_vector_t new_edges;
+    igraph_vector_t ancestors;
+    long int root;
+    igraph_vector_t neighbors;
+    igraph_stack_t path;
+    igraph_vector_bool_t done;
+
+    if (!igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Tree transitive closure of a directed graph",
+                     IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&new_edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&deg, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&ancestors, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&neighbors, 0);
+    IGRAPH_CHECK(igraph_stack_init(&path, 0));
+    IGRAPH_FINALLY(igraph_stack_destroy, &path);
+    IGRAPH_CHECK(igraph_vector_bool_init(&done, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &done);
+
+    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(),
+                               IGRAPH_OUT, IGRAPH_LOOPS));
+
+#define STAR (-1)
+
+    for (root = 0; root < no_of_nodes; root++) {
+        if (VECTOR(deg)[root] != 0) {
+            continue;
+        }
+        IGRAPH_CHECK(igraph_stack_push(&path, root));
+
+        while (!igraph_stack_empty(&path)) {
+            long int node = (long int) igraph_stack_top(&path);
+            if (node == STAR) {
+                /* Leaving a node */
+                long int j, n;
+                igraph_stack_pop(&path);
+                node = (long int) igraph_stack_pop(&path);
+                if (!VECTOR(done)[node]) {
+                    igraph_vector_pop_back(&ancestors);
+                    VECTOR(done)[node] = 1;
+                }
+                n = igraph_vector_size(&ancestors);
+                for (j = 0; j < n; j++) {
+                    IGRAPH_CHECK(igraph_vector_push_back(&new_edges, node));
+                    IGRAPH_CHECK(igraph_vector_push_back(&new_edges,
+                                                         VECTOR(ancestors)[j]));
+                }
+            } else {
+                /* Getting into a node */
+                long int n, j;
+                if (!VECTOR(done)[node]) {
+                    IGRAPH_CHECK(igraph_vector_push_back(&ancestors, node));
+                }
+                IGRAPH_CHECK(igraph_neighbors(graph, &neighbors,
+                                              (igraph_integer_t) node, IGRAPH_IN));
+                n = igraph_vector_size(&neighbors);
+                IGRAPH_CHECK(igraph_stack_push(&path, STAR));
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neighbors)[j];
+                    IGRAPH_CHECK(igraph_stack_push(&path, nei));
+                }
+            }
+        }
+    }
+
+#undef STAR
+
+    igraph_vector_bool_destroy(&done);
+    igraph_stack_destroy(&path);
+    igraph_vector_destroy(&neighbors);
+    igraph_vector_destroy(&ancestors);
+    igraph_vector_destroy(&deg);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    IGRAPH_CHECK(igraph_create(closure, &new_edges, (igraph_integer_t)no_of_nodes,
+                               IGRAPH_DIRECTED));
+
+    igraph_vector_destroy(&new_edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_diversity
+ * Structural diversity index of the vertices
+ *
+ * This measure was defined in Nathan Eagle, Michael Macy and Rob
+ * Claxton: Network Diversity and Economic Development, Science 328,
+ * 1029--1031, 2010.
+ *
+ * </para><para>
+ * It is simply the (normalized) Shannon entropy of the
+ * incident edges' weights. D(i)=H(i)/log(k[i]), and
+ * H(i) = -sum(p[i,j] log(p[i,j]), j=1..k[i]),
+ * where p[i,j]=w[i,j]/sum(w[i,l], l=1..k[i]),  k[i] is the (total)
+ * degree of vertex i, and w[i,j] is the weight of the edge(s) between
+ * vertex i and j.
+ * \param graph The input graph, edge directions are ignored.
+ * \param weights The edge weights, in the order of the edge ids, must
+ *    have appropriate length.
+ * \param res An initialized vector, the results are stored here.
+ * \param vids Vector with the vertex ids for which to calculate the
+ *    measure.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear.
+ *
+ */
+
+int igraph_diversity(igraph_t *graph, const igraph_vector_t *weights,
+                     igraph_vector_t *res, const igraph_vs_t vids) {
+
+    int no_of_nodes = igraph_vcount(graph);
+    int no_of_edges = igraph_ecount(graph);
+    igraph_vector_t incident;
+    igraph_vit_t vit;
+    igraph_real_t s, ent, w;
+    int i, j, k;
+
+    if (!weights) {
+        IGRAPH_ERROR("Edge weights must be given", IGRAPH_EINVAL);
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid edge weight vector length", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&incident, 10);
+
+    if (igraph_vs_is_all(&vids)) {
+        IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
+        for (i = 0; i < no_of_nodes; i++) {
+            s = ent = 0.0;
+            IGRAPH_CHECK(igraph_incident(graph, &incident, i, /*mode=*/ IGRAPH_ALL));
+            for (j = 0, k = (int) igraph_vector_size(&incident); j < k; j++) {
+                w = VECTOR(*weights)[(long int)VECTOR(incident)[j]];
+                s += w;
+                ent += (w * log(w));
+            }
+            VECTOR(*res)[i] = (log(s) - ent / s) / log(k);
+        }
+    } else {
+        IGRAPH_CHECK(igraph_vector_resize(res, 0));
+        IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+        for (IGRAPH_VIT_RESET(vit), i = 0;
+             !IGRAPH_VIT_END(vit);
+             IGRAPH_VIT_NEXT(vit), i++) {
+            long int v = IGRAPH_VIT_GET(vit);
+            s = ent = 0.0;
+            IGRAPH_CHECK(igraph_incident(graph, &incident, (igraph_integer_t) v,
+                                         /*mode=*/ IGRAPH_ALL));
+            for (j = 0, k = (int) igraph_vector_size(&incident); j < k; j++) {
+                w = VECTOR(*weights)[(long int)VECTOR(incident)[j]];
+                s += w;
+                ent += (w * log(w));
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(res, (log(s) - ent / s) / log(k)));
+        }
+
+        igraph_vit_destroy(&vit);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&incident);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+#define SUCCEED {   \
+        if (res) {        \
+            *res = 1;       \
+        }                 \
+        return IGRAPH_SUCCESS; \
+    }
+
+#define FAIL {   \
+        if (res) {     \
+            *res = 0;    \
+        }              \
+        return IGRAPH_SUCCESS; \
+    }
+
+/**
+ * \function igraph_is_degree_sequence
+ * Determines whether a degree sequence is valid.
+ *
+ * A sequence of n integers is a valid degree sequence if there exists some
+ * graph where the degree of the i-th vertex is equal to the i-th element of the
+ * sequence. Note that the graph may contain multiple or loop edges; if you are
+ * interested in whether the degrees of some \em simple graph may realize the
+ * given sequence, use \ref igraph_is_graphical_degree_sequence.
+ *
+ * </para><para>
+ * In particular, the function checks whether all the degrees are non-negative.
+ * For undirected graphs, it also checks whether the sum of degrees is even.
+ * For directed graphs, the function checks whether the lengths of the two
+ * degree vectors are equal and whether their sums are also equal. These are
+ * known sufficient and necessary conditions for a degree sequence to be
+ * valid.
+ *
+ * \param out_degrees  an integer vector specifying the degree sequence for
+ *     undirected graphs or the out-degree sequence for directed graphs.
+ * \param in_degrees   an integer vector specifying the in-degrees of the
+ *     vertices for directed graphs. For undirected graphs, this must be null.
+ * \param res  pointer to a boolean variable, the result will be stored here
+ * \return Error code.
+ *
+ * Time complexity: O(n), where n is the length of the degree sequence.
+ */
+int igraph_is_degree_sequence(const igraph_vector_t *out_degrees,
+                              const igraph_vector_t *in_degrees, igraph_bool_t *res) {
+    /* degrees must be non-negative */
+    if (igraph_vector_any_smaller(out_degrees, 0)) {
+        FAIL;
+    }
+    if (in_degrees && igraph_vector_any_smaller(in_degrees, 0)) {
+        FAIL;
+    }
+
+    if (in_degrees == 0) {
+        /* sum of degrees must be even */
+        if (((long int)igraph_vector_sum(out_degrees) % 2) != 0) {
+            FAIL;
+        }
+    } else {
+        /* length of the two degree vectors must be equal */
+        if (igraph_vector_size(out_degrees) != igraph_vector_size(in_degrees)) {
+            FAIL;
+        }
+        /* sum of in-degrees must be equal to sum of out-degrees */
+        if (igraph_vector_sum(out_degrees) != igraph_vector_sum(in_degrees)) {
+            FAIL;
+        }
+    }
+
+    SUCCEED;
+    return 0;
+}
+
+int igraph_i_is_graphical_degree_sequence_undirected(
+    const igraph_vector_t *degrees, igraph_bool_t *res);
+int igraph_i_is_graphical_degree_sequence_directed(
+    const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees,
+    igraph_bool_t *res);
+
+/**
+ * \function igraph_is_graphical_degree_sequence
+ * Determines whether a sequence of integers can be a degree sequence of some
+ * simple graph.
+ *
+ * </para><para>
+ * References:
+ *
+ * </para><para>
+ * Hakimi SL: On the realizability of a set of integers as degrees of the
+ * vertices of a simple graph. J SIAM Appl Math 10:496-506, 1962.
+ *
+ * </para><para>
+ * PL Erdos, I Miklos and Z Toroczkai: A simple Havel-Hakimi type algorithm
+ * to realize graphical degree sequences of directed graphs. The Electronic
+ * Journal of Combinatorics 17(1):R66, 2010.
+ *
+ * </para><para>
+ * Z Kiraly: Recognizing graphic degree sequences and generating all
+ * realizations. TR-2011-11, Egervary Research Group, H-1117, Budapest,
+ * Hungary. ISSN 1587-4451, 2012.
+ *
+ * \param out_degrees  an integer vector specifying the degree sequence for
+ *     undirected graphs or the out-degree sequence for directed graphs.
+ * \param in_degrees   an integer vector specifying the in-degrees of the
+ *     vertices for directed graphs. For undirected graphs, this must be null.
+ * \param res  pointer to a boolean variable, the result will be stored here
+ * \return Error code.
+ *
+ * Time complexity: O(n log n) for undirected graphs, O(n^2) for directed
+ *                  graphs, where n is the length of the degree sequence.
+ */
+int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees,
+                                        const igraph_vector_t *in_degrees, igraph_bool_t *res) {
+    IGRAPH_CHECK(igraph_is_degree_sequence(out_degrees, in_degrees, res));
+    if (!*res) {
+        FAIL;
+    }
+
+    if (igraph_vector_size(out_degrees) == 0) {
+        SUCCEED;
+    }
+
+    if (in_degrees == 0) {
+        return igraph_i_is_graphical_degree_sequence_undirected(out_degrees, res);
+    } else {
+        return igraph_i_is_graphical_degree_sequence_directed(out_degrees, in_degrees, res);
+    }
+}
+
+int igraph_i_is_graphical_degree_sequence_undirected(
+    const igraph_vector_t *degrees, igraph_bool_t *res) {
+    igraph_vector_t work;
+    long int w, b, s, c, n, k;
+
+    IGRAPH_CHECK(igraph_vector_copy(&work, degrees));
+    IGRAPH_FINALLY(igraph_vector_destroy, &work);
+
+    igraph_vector_sort(&work);
+
+    /* This algorithm is outlined in TR-2011-11 of the Egervary Research Group,
+     * ISSN 1587-4451. The main loop of the algorithm is O(n) but it is dominated
+     * by an O(n log n) quicksort; this could in theory be brought down to
+     * O(n) with binsort but it's probably not worth the fuss.
+     *
+     * Variables names are mostly according to the technical report, apart from
+     * the degrees themselves. w and k are zero-based here; in the technical
+     * report they are 1-based */
+    *res = 1;
+    n = igraph_vector_size(&work);
+    w = n - 1; b = 0; s = 0; c = 0;
+    for (k = 0; k < n; k++) {
+        b += VECTOR(*degrees)[k];
+        c += w;
+        while (w > k && VECTOR(*degrees)[w] <= k + 1) {
+            s += VECTOR(*degrees)[w];
+            c -= (k + 1);
+            w--;
+        }
+        if (b > c + s) {
+            *res = 0;
+            break;
+        }
+        if (w == k) {
+            break;
+        }
+    }
+
+    igraph_vector_destroy(&work);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+typedef struct {
+    const igraph_vector_t* first;
+    const igraph_vector_t* second;
+} igraph_i_qsort_dual_vector_cmp_data_t;
+
+int igraph_i_qsort_dual_vector_cmp_desc(void* data, const void *p1, const void *p2) {
+    igraph_i_qsort_dual_vector_cmp_data_t* sort_data =
+        (igraph_i_qsort_dual_vector_cmp_data_t*)data;
+    long int index1 = *((long int*)p1);
+    long int index2 = *((long int*)p2);
+    if (VECTOR(*sort_data->first)[index1] < VECTOR(*sort_data->first)[index2]) {
+        return 1;
+    }
+    if (VECTOR(*sort_data->first)[index1] > VECTOR(*sort_data->first)[index2]) {
+        return -1;
+    }
+    if (VECTOR(*sort_data->second)[index1] < VECTOR(*sort_data->second)[index2]) {
+        return 1;
+    }
+    if (VECTOR(*sort_data->second)[index1] > VECTOR(*sort_data->second)[index2]) {
+        return -1;
+    }
+    return 0;
+}
+
+int igraph_i_is_graphical_degree_sequence_directed(
+    const igraph_vector_t *out_degrees, const igraph_vector_t *in_degrees,
+    igraph_bool_t *res) {
+    igraph_vector_long_t index_array;
+    long int i, j, vcount, lhs, rhs;
+    igraph_i_qsort_dual_vector_cmp_data_t sort_data;
+
+    /* Create an index vector that sorts the vertices by decreasing in-degree */
+    vcount = igraph_vector_size(out_degrees);
+    IGRAPH_CHECK(igraph_vector_long_init_seq(&index_array, 0, vcount - 1));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &index_array);
+
+    /* Set up the auxiliary struct for sorting */
+    sort_data.first  = in_degrees;
+    sort_data.second = out_degrees;
+
+    /* Sort the index vector */
+    igraph_qsort_r(VECTOR(index_array), vcount, sizeof(long int), &sort_data,
+                   igraph_i_qsort_dual_vector_cmp_desc);
+
+    /* Be optimistic, then check whether the Fulkerson–Chen–Anstee condition
+     * holds for every k. In particular, for every k in [0; n), it must be true
+     * that:
+     *
+     * \sum_{i=0}^k indegree[i] <=
+     *     \sum_{i=0}^k min(outdegree[i], k) +
+     *     \sum_{i=k+1}^{n-1} min(outdegree[i], k + 1)
+     */
+
+#define INDEGREE(x) (VECTOR(*in_degrees)[VECTOR(index_array)[x]])
+#define OUTDEGREE(x) (VECTOR(*out_degrees)[VECTOR(index_array)[x]])
+
+    *res = 1;
+    lhs = 0;
+    for (i = 0; i < vcount; i++) {
+        lhs += INDEGREE(i);
+
+        /* It is enough to check for indexes where the in-degree is about to
+         * decrease in the next step; see "Stronger condition" in the Wikipedia
+         * entry for the Fulkerson-Chen-Anstee condition */
+        if (i != vcount - 1 && INDEGREE(i) == INDEGREE(i + 1)) {
+            continue;
+        }
+
+        rhs = 0;
+        for (j = 0; j <= i; j++) {
+            rhs += OUTDEGREE(j) < i ? OUTDEGREE(j) : i;
+        }
+        for (j = i + 1; j < vcount; j++) {
+            rhs += OUTDEGREE(j) < (i + 1) ? OUTDEGREE(j) : (i + 1);
+        }
+
+        if (lhs > rhs) {
+            *res = 0;
+            break;
+        }
+    }
+
+#undef INDEGREE
+#undef OUTDEGREE
+
+    igraph_vector_long_destroy(&index_array);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+#undef SUCCEED
+#undef FAIL
+
+
+/* igraph_is_tree -- check if a graph is a tree */
+
+/* count the number of vertices reachable from the root */
+static int igraph_i_is_tree_visitor(igraph_integer_t root, const igraph_adjlist_t *al, igraph_integer_t *visited_count) {
+    igraph_stack_int_t stack;
+    igraph_vector_bool_t visited;
+    long i;
+
+    IGRAPH_CHECK(igraph_vector_bool_init(&visited, igraph_adjlist_size(al)));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited);
+
+    IGRAPH_CHECK(igraph_stack_int_init(&stack, 0));
+    IGRAPH_FINALLY(igraph_stack_int_destroy, &stack);
+
+    *visited_count = 0;
+
+    /* push the root into the stack */
+    IGRAPH_CHECK(igraph_stack_int_push(&stack, root));
+
+    while (! igraph_stack_int_empty(&stack)) {
+        igraph_integer_t u;
+        igraph_vector_int_t *neighbors;
+        long ncount;
+
+        /* take a vertex from the stack, mark it as visited */
+        u = igraph_stack_int_pop(&stack);
+        if (IGRAPH_LIKELY(! VECTOR(visited)[u])) {
+            VECTOR(visited)[u] = 1;
+            *visited_count += 1;
+        }
+
+        /* register all its yet-unvisited neighbours for future processing */
+        neighbors = igraph_adjlist_get(al, u);
+        ncount = igraph_vector_int_size(neighbors);
+        for (i = 0; i < ncount; ++i) {
+            igraph_integer_t v = VECTOR(*neighbors)[i];
+            if (! VECTOR(visited)[v]) {
+                IGRAPH_CHECK(igraph_stack_int_push(&stack, v));
+            }
+        }
+    }
+
+    igraph_stack_int_destroy(&stack);
+    igraph_vector_bool_destroy(&visited);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return IGRAPH_SUCCESS;
+}
+
+
+/**
+ * \ingroup structural
+ * \function igraph_is_tree
+ * \brief Decides whether the graph is a tree.
+ *
+ * An undirected graph is a tree if it is connected and has no cycles.
+ * </para><para>
+ *
+ * In the directed case, a possible additional requirement is that all
+ * edges are oriented away from a root (out-tree or arborescence) or all edges
+ * are oriented towards a root (in-tree or anti-arborescence).
+ * This test can be controlled using the \p mode parameter.
+ * </para><para>
+ *
+ * By convention, the null graph (i.e. the graph with no vertices) is considered not to be a tree.
+ *
+ * \param graph The graph object to analyze.
+ * \param res Pointer to a logical variable, the result will be stored
+ *        here.
+ * \param root If not \c NULL, the root node will be stored here. When \p mode
+ *        is \c IGRAPH_ALL or the graph is undirected, any vertex can be the root
+ *        and \p root is set to 0 (the first vertex). When \p mode is \c IGRAPH_OUT
+ *        or \c IGRAPH_IN, the root is set to the vertex with zero in- or out-degree,
+ *        respectively.
+ * \param mode For a directed graph this specifies whether to test for an
+ *        out-tree, an in-tree or ignore edge directions. The respective
+ *        possible values are:
+ *        \c IGRAPH_OUT, \c IGRAPH_IN, \c IGRAPH_ALL. This argument is
+ *        ignored for undirected graphs.
+ * \return Error code:
+ *        \c IGRAPH_EINVAL: invalid mode argument.
+ *
+ * Time complexity: At most O(|V|+|E|), the
+ * number of vertices plus the number of edges in the graph.
+ *
+ * \sa igraph_is_weakly_connected()
+ *
+ * \example examples/simple/igraph_tree.c
+ */
+
+int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode) {
+    igraph_adjlist_t al;
+    igraph_integer_t iroot = 0;
+    igraph_integer_t visited_count;
+    igraph_integer_t vcount, ecount;
+
+    vcount = igraph_vcount(graph);
+    ecount = igraph_ecount(graph);
+
+    /* A tree must have precisely vcount-1 edges. */
+    /* By convention, the zero-vertex graph will not be considered a tree. */
+    if (ecount != vcount - 1) {
+        *res = 0;
+        return IGRAPH_SUCCESS;
+    }
+
+    /* The single-vertex graph is a tree, provided it has no edges (checked in the previous if (..)) */
+    if (vcount == 1) {
+        *res = 1;
+        if (root) {
+            *root = 0;
+        }
+        return IGRAPH_SUCCESS;
+    }
+
+    /* For higher vertex counts we cannot short-circuit due to the possibility
+     * of loops or multi-edges even when the edge count is correct. */
+
+    /* Ignore mode for undirected graphs. */
+    if (! igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &al, mode));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
+
+    /* The main algorithm:
+     * We find a root and check that all other vertices are reachable from it.
+     * We have already checked the number of edges, so with the additional
+     * reachability condition we can verify if the graph is a tree.
+     *
+     * For directed graphs, the root is the node with no incoming/outgoing
+     * connections, depending on 'mode'. For undirected, it is arbitrary, so
+     * we choose 0.
+     */
+
+    *res = 1; /* assume success */
+
+    switch (mode) {
+    case IGRAPH_ALL:
+        iroot = 0;
+        break;
+
+    case IGRAPH_IN:
+    case IGRAPH_OUT: {
+        igraph_vector_t degree;
+        igraph_integer_t i;
+
+        IGRAPH_CHECK(igraph_vector_init(&degree, 0));
+        IGRAPH_FINALLY(igraph_vector_destroy, &degree);
+
+        IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), mode == IGRAPH_IN ? IGRAPH_OUT : IGRAPH_IN, /* loops = */ 1));
+
+        for (i = 0; i < vcount; ++i)
+            if (VECTOR(degree)[i] == 0) {
+                break;
+            }
+
+        /* if no suitable root is found, the graph is not a tree */
+        if (i == vcount) {
+            *res = 0;
+        } else {
+            iroot = i;
+        }
+
+        igraph_vector_destroy(&degree);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    break;
+    default:
+        IGRAPH_ERROR("Invalid mode", IGRAPH_EINVMODE);
+    }
+
+    /* if no suitable root was found, skip visting vertices */
+    if (*res) {
+        IGRAPH_CHECK(igraph_i_is_tree_visitor(iroot, &al, &visited_count));
+        *res = visited_count == vcount;
+    }
+
+    if (root) {
+        *root = iroot;
+    }
+
+    igraph_adjlist_destroy(&al);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/structure_generators.c b/igraph/src/structure_generators.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/structure_generators.c
@@ -0,0 +1,2443 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_constructors.h"
+#include "igraph_structural.h"
+#include "igraph_memory.h"
+#include "igraph_interface.h"
+#include "igraph_attributes.h"
+#include "igraph_adjlist.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_dqueue.h"
+#include "config.h"
+
+#include <stdarg.h>
+#include <math.h>
+#include <string.h>
+
+/**
+ * \section about_generators
+ *
+ * <para>Graph generators create graphs.</para>
+ *
+ * <para>Almost all functions which create graph objects are documented
+ * here. The exceptions are \ref igraph_subgraph() and alike, these
+ * create graphs based on another graph.</para>
+ */
+
+
+/**
+ * \ingroup generators
+ * \function igraph_create
+ * \brief Creates a graph with the specified edges.
+ *
+ * \param graph An uninitialized graph object.
+ * \param edges The edges to add, the first two elements are the first
+ *        edge, etc.
+ * \param n The number of vertices in the graph, if smaller or equal
+ *        to the highest vertex id in the \p edges vector it
+ *        will be increased automatically. So it is safe to give 0
+ *        here.
+ * \param directed Boolean, whether to create a directed graph or
+ *        not. If yes, then the first edge points from the first
+ *        vertex id in \p edges to the second, etc.
+ * \return Error code:
+ *         \c IGRAPH_EINVEVECTOR: invalid edges
+ *         vector (odd number of vertices).
+ *         \c IGRAPH_EINVVID: invalid (negative)
+ *         vertex id.
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| is the number of vertices,
+ * |E| the number of edges in the
+ * graph.
+ *
+ * \example examples/simple/igraph_create.c
+ */
+int igraph_create(igraph_t *graph, const igraph_vector_t *edges,
+                  igraph_integer_t n, igraph_bool_t directed) {
+    igraph_bool_t has_edges = igraph_vector_size(edges) > 0;
+    igraph_real_t max = has_edges ? igraph_vector_max(edges) + 1 : 0;
+
+    if (igraph_vector_size(edges) % 2 != 0) {
+        IGRAPH_ERROR("Invalid (odd) edges vector", IGRAPH_EINVEVECTOR);
+    }
+    if (has_edges && !igraph_vector_isininterval(edges, 0, max - 1)) {
+        IGRAPH_ERROR("Invalid (negative) vertex id", IGRAPH_EINVVID);
+    }
+
+    IGRAPH_CHECK(igraph_empty(graph, n, directed));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+    if (has_edges) {
+        igraph_integer_t vc = igraph_vcount(graph);
+        if (vc < max) {
+            IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) (max - vc), 0));
+        }
+        IGRAPH_CHECK(igraph_add_edges(graph, edges, 0));
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_i_adjacency_directed(igraph_matrix_t *adjmatrix,
+                                igraph_vector_t *edges);
+int igraph_i_adjacency_max(igraph_matrix_t *adjmatrix,
+                           igraph_vector_t *edges);
+int igraph_i_adjacency_upper(igraph_matrix_t *adjmatrix,
+                             igraph_vector_t *edges);
+int igraph_i_adjacency_lower(igraph_matrix_t *adjmatrix,
+                             igraph_vector_t *edges);
+int igraph_i_adjacency_min(igraph_matrix_t *adjmatrix,
+                           igraph_vector_t *edges);
+
+int igraph_i_adjacency_directed(igraph_matrix_t *adjmatrix, igraph_vector_t *edges) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j, k;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j < no_of_nodes; j++) {
+            long int M = (long int) MATRIX(*adjmatrix, i, j);
+            for (k = 0; k < M; k++) {
+                IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            }
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_adjacency_max(igraph_matrix_t *adjmatrix, igraph_vector_t *edges) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j, k;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = i; j < no_of_nodes; j++) {
+            long int M1 = (long int) MATRIX(*adjmatrix, i, j);
+            long int M2 = (long int) MATRIX(*adjmatrix, j, i);
+            if (M1 < M2) {
+                M1 = M2;
+            }
+            for (k = 0; k < M1; k++) {
+                IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            }
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_adjacency_upper(igraph_matrix_t *adjmatrix, igraph_vector_t *edges) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j, k;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = i; j < no_of_nodes; j++) {
+            long int M = (long int) MATRIX(*adjmatrix, i, j);
+            for (k = 0; k < M; k++) {
+                IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            }
+        }
+    }
+    return 0;
+}
+
+int igraph_i_adjacency_lower(igraph_matrix_t *adjmatrix, igraph_vector_t *edges) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j, k;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j <= i; j++) {
+            long int M = (long int) MATRIX(*adjmatrix, i, j);
+            for (k = 0; k < M; k++) {
+                IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            }
+        }
+    }
+    return 0;
+}
+
+int igraph_i_adjacency_min(igraph_matrix_t *adjmatrix, igraph_vector_t *edges) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j, k;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = i; j < no_of_nodes; j++) {
+            long int M1 = (long int) MATRIX(*adjmatrix, i, j);
+            long int M2 = (long int) MATRIX(*adjmatrix, j, i);
+            if (M1 > M2) {
+                M1 = M2;
+            }
+            for (k = 0; k < M1; k++) {
+                IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+                IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            }
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_adjacency
+ * \brief Creates a graph object from an adjacency matrix.
+ *
+ * The order of the vertices in the matrix is preserved, i.e. the vertex
+ * corresponding to the first row/column will be vertex with id 0, the
+ * next row is for vertex 1, etc.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param adjmatrix The adjacency matrix. How it is interpreted
+ *        depends on the \p mode argument.
+ * \param mode Constant to specify how the given matrix is interpreted
+ *        as an adjacency matrix. Possible values
+ *        (A(i,j)
+ *        is the element in row i and column
+ *        j in the adjacency matrix
+ *        \p adjmatrix):
+ *        \clist
+ *        \cli IGRAPH_ADJ_DIRECTED
+ *          the graph will be directed and
+ *          an element gives the number of edges between two vertices.
+ *        \cli IGRAPH_ADJ_UNDIRECTED
+ *          this is the same as \c IGRAPH_ADJ_MAX,
+ *          for convenience.
+ *        \cli IGRAPH_ADJ_MAX
+ *          undirected graph will be created
+ *          and the number of edges between vertices
+ *          i and
+ *          j is
+ *          max(A(i,j), A(j,i)).
+ *        \cli IGRAPH_ADJ_MIN
+ *          undirected graph will be created
+ *          with min(A(i,j), A(j,i))
+ *          edges between vertices
+ *          i and
+ *          j.
+ *        \cli IGRAPH_ADJ_PLUS
+ *          undirected graph will be created
+ *          with A(i,j)+A(j,i) edges
+ *          between vertices
+ *          i and
+ *          j.
+ *        \cli IGRAPH_ADJ_UPPER
+ *          undirected graph will be created,
+ *          only the upper right triangle (including the diagonal) is
+ *          used for the number of edges.
+ *        \cli IGRAPH_ADJ_LOWER
+ *          undirected graph will be created,
+ *          only the lower left triangle (including the diagonal) is
+ *          used for creating the edges.
+ *       \endclist
+ * \return Error code,
+ *         \c IGRAPH_NONSQUARE: non-square matrix.
+ *
+ * Time complexity: O(|V||V|),
+ * |V| is the number of vertices in the graph.
+ *
+ * \example examples/simple/igraph_adjacency.c
+ */
+
+int igraph_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix,
+                     igraph_adjacency_t mode) {
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int no_of_nodes;
+
+    /* Some checks */
+    if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) {
+        IGRAPH_ERROR("Non-square matrix", IGRAPH_NONSQUARE);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    /* Collect the edges */
+    no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    switch (mode) {
+    case IGRAPH_ADJ_DIRECTED:
+        IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges));
+        break;
+    case IGRAPH_ADJ_MAX:
+        IGRAPH_CHECK(igraph_i_adjacency_max(adjmatrix, &edges));
+        break;
+    case IGRAPH_ADJ_UPPER:
+        IGRAPH_CHECK(igraph_i_adjacency_upper(adjmatrix, &edges));
+        break;
+    case IGRAPH_ADJ_LOWER:
+        IGRAPH_CHECK(igraph_i_adjacency_lower(adjmatrix, &edges));
+        break;
+    case IGRAPH_ADJ_MIN:
+        IGRAPH_CHECK(igraph_i_adjacency_min(adjmatrix, &edges));
+        break;
+    case IGRAPH_ADJ_PLUS:
+        IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges));
+        break;
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               (mode == IGRAPH_ADJ_DIRECTED)));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+int igraph_i_weighted_adjacency_directed(const igraph_matrix_t *adjmatrix,
+        igraph_vector_t *edges,
+        igraph_vector_t *weights,
+        igraph_bool_t loops);
+int igraph_i_weighted_adjacency_plus(const igraph_matrix_t *adjmatrix,
+                                     igraph_vector_t *edges,
+                                     igraph_vector_t *weights,
+                                     igraph_bool_t loops);
+int igraph_i_weighted_adjacency_max(const igraph_matrix_t *adjmatrix,
+                                    igraph_vector_t *edges,
+                                    igraph_vector_t *weights,
+                                    igraph_bool_t loops);
+int igraph_i_weighted_adjacency_upper(const igraph_matrix_t *adjmatrix,
+                                      igraph_vector_t *edges,
+                                      igraph_vector_t *weights,
+                                      igraph_bool_t loops);
+int igraph_i_weighted_adjacency_lower(const igraph_matrix_t *adjmatrix,
+                                      igraph_vector_t *edges,
+                                      igraph_vector_t *weights,
+                                      igraph_bool_t loops);
+int igraph_i_weighted_adjacency_min(const igraph_matrix_t *adjmatrix,
+                                    igraph_vector_t *edges,
+                                    igraph_vector_t *weights,
+                                    igraph_bool_t loops);
+
+int igraph_i_weighted_adjacency_directed(const igraph_matrix_t *adjmatrix,
+        igraph_vector_t *edges,
+        igraph_vector_t *weights,
+        igraph_bool_t loops) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j < no_of_nodes; j++) {
+            igraph_real_t M = MATRIX(*adjmatrix, i, j);
+            if (M == 0.0) {
+                continue;
+            }
+            if (i == j && !loops) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+            IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            IGRAPH_CHECK(igraph_vector_push_back(weights, M));
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_weighted_adjacency_plus(const igraph_matrix_t *adjmatrix,
+                                     igraph_vector_t *edges,
+                                     igraph_vector_t *weights,
+                                     igraph_bool_t loops) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = i; j < no_of_nodes; j++) {
+            igraph_real_t M = MATRIX(*adjmatrix, i, j) + MATRIX(*adjmatrix, j, i);
+            if (M == 0.0) {
+                continue;
+            }
+            if (i == j && !loops) {
+                continue;
+            }
+            if (i == j) {
+                M /= 2;
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+            IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            IGRAPH_CHECK(igraph_vector_push_back(weights, M));
+        }
+    }
+
+    return 0;
+}
+
+int igraph_i_weighted_adjacency_max(const igraph_matrix_t *adjmatrix,
+                                    igraph_vector_t *edges,
+                                    igraph_vector_t *weights,
+                                    igraph_bool_t loops) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = i; j < no_of_nodes; j++) {
+            igraph_real_t M1 = MATRIX(*adjmatrix, i, j);
+            igraph_real_t M2 = MATRIX(*adjmatrix, j, i);
+            if (M1 < M2) {
+                M1 = M2;
+            }
+            if (M1 == 0.0) {
+                continue;
+            }
+            if (i == j && !loops) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+            IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            IGRAPH_CHECK(igraph_vector_push_back(weights, M1));
+        }
+    }
+    return 0;
+}
+
+int igraph_i_weighted_adjacency_upper(const igraph_matrix_t *adjmatrix,
+                                      igraph_vector_t *edges,
+                                      igraph_vector_t *weights,
+                                      igraph_bool_t loops) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = i; j < no_of_nodes; j++) {
+            igraph_real_t M = MATRIX(*adjmatrix, i, j);
+            if (M == 0.0) {
+                continue;
+            }
+            if (i == j && !loops) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+            IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            IGRAPH_CHECK(igraph_vector_push_back(weights, M));
+        }
+    }
+    return 0;
+}
+
+int igraph_i_weighted_adjacency_lower(const igraph_matrix_t *adjmatrix,
+                                      igraph_vector_t *edges,
+                                      igraph_vector_t *weights,
+                                      igraph_bool_t loops) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = 0; j <= i; j++) {
+            igraph_real_t M = MATRIX(*adjmatrix, i, j);
+            if (M == 0.0) {
+                continue;
+            }
+            if (i == j && !loops) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+            IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            IGRAPH_CHECK(igraph_vector_push_back(weights, M));
+        }
+    }
+    return 0;
+}
+
+int igraph_i_weighted_adjacency_min(const igraph_matrix_t *adjmatrix,
+                                    igraph_vector_t *edges,
+                                    igraph_vector_t *weights,
+                                    igraph_bool_t loops) {
+
+    long int no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    long int i, j;
+
+    for (i = 0; i < no_of_nodes; i++) {
+        for (j = i; j < no_of_nodes; j++) {
+            igraph_real_t M1 = MATRIX(*adjmatrix, i, j);
+            igraph_real_t M2 = MATRIX(*adjmatrix, j, i);
+            if (M1 > M2) {
+                M1 = M2;
+            }
+            if (M1 == 0.0) {
+                continue;
+            }
+            if (i == j && !loops) {
+                continue;
+            }
+            IGRAPH_CHECK(igraph_vector_push_back(edges, i));
+            IGRAPH_CHECK(igraph_vector_push_back(edges, j));
+            IGRAPH_CHECK(igraph_vector_push_back(weights, M1));
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_weighted_adjacency
+ * \brief Creates a graph object from a weighted adjacency matrix.
+ *
+ * The order of the vertices in the matrix is preserved, i.e. the vertex
+ * corresponding to the first row/column will be vertex with id 0, the
+ * next row is for vertex 1, etc.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param adjmatrix The weighted adjacency matrix. How it is interpreted
+ *        depends on the \p mode argument. The common feature is that
+ *        edges with zero weights are considered nonexistent (however,
+ *        negative weights are permitted).
+ * \param mode Constant to specify how the given matrix is interpreted
+ *        as an adjacency matrix. Possible values
+ *        (A(i,j)
+ *        is the element in row i and column
+ *        j in the adjacency matrix
+ *        \p adjmatrix):
+ *        \clist
+ *        \cli IGRAPH_ADJ_DIRECTED
+ *          the graph will be directed and
+ *          an element gives the weight of the edge between two vertices.
+ *        \cli IGRAPH_ADJ_UNDIRECTED
+ *          this is the same as \c IGRAPH_ADJ_MAX,
+ *          for convenience.
+ *        \cli IGRAPH_ADJ_MAX
+ *          undirected graph will be created
+ *          and the weight of the edge between vertices
+ *          i and
+ *          j is
+ *          max(A(i,j), A(j,i)).
+ *        \cli IGRAPH_ADJ_MIN
+ *          undirected graph will be created
+ *          with edge weight min(A(i,j), A(j,i))
+ *          between vertices
+ *          i and
+ *          j.
+ *        \cli IGRAPH_ADJ_PLUS
+ *          undirected graph will be created
+ *          with edge weight A(i,j)+A(j,i)
+ *          between vertices
+ *          i and
+ *          j.
+ *        \cli IGRAPH_ADJ_UPPER
+ *          undirected graph will be created,
+ *          only the upper right triangle (including the diagonal) is
+ *          used for the edge weights.
+ *        \cli IGRAPH_ADJ_LOWER
+ *          undirected graph will be created,
+ *          only the lower left triangle (including the diagonal) is
+ *          used for the edge weights.
+ *       \endclist
+ * \param attr the name of the attribute that will store the edge weights.
+ *         If \c NULL , it will use \c weight as the attribute name.
+ * \param loops Logical scalar, whether to ignore the diagonal elements
+ *         in the adjacency matrix.
+ * \return Error code,
+ *         \c IGRAPH_NONSQUARE: non-square matrix.
+ *
+ * Time complexity: O(|V||V|),
+ * |V| is the number of vertices in the graph.
+ *
+ * \example examples/simple/igraph_weighted_adjacency.c
+ */
+
+int igraph_weighted_adjacency(igraph_t *graph, igraph_matrix_t *adjmatrix,
+                              igraph_adjacency_t mode, const char* attr,
+                              igraph_bool_t loops) {
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    igraph_vector_t weights = IGRAPH_VECTOR_NULL;
+    const char* default_attr = "weight";
+    igraph_vector_ptr_t attr_vec;
+    igraph_attribute_record_t attr_rec;
+    long int no_of_nodes;
+
+    /* Some checks */
+    if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) {
+        IGRAPH_ERROR("Non-square matrix", IGRAPH_NONSQUARE);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&weights, 0);
+    IGRAPH_VECTOR_PTR_INIT_FINALLY(&attr_vec, 1);
+
+    /* Collect the edges */
+    no_of_nodes = igraph_matrix_nrow(adjmatrix);
+    switch (mode) {
+    case IGRAPH_ADJ_DIRECTED:
+        IGRAPH_CHECK(igraph_i_weighted_adjacency_directed(adjmatrix, &edges,
+                     &weights, loops));
+        break;
+    case IGRAPH_ADJ_MAX:
+        IGRAPH_CHECK(igraph_i_weighted_adjacency_max(adjmatrix, &edges,
+                     &weights, loops));
+        break;
+    case IGRAPH_ADJ_UPPER:
+        IGRAPH_CHECK(igraph_i_weighted_adjacency_upper(adjmatrix, &edges,
+                     &weights, loops));
+        break;
+    case IGRAPH_ADJ_LOWER:
+        IGRAPH_CHECK(igraph_i_weighted_adjacency_lower(adjmatrix, &edges,
+                     &weights, loops));
+        break;
+    case IGRAPH_ADJ_MIN:
+        IGRAPH_CHECK(igraph_i_weighted_adjacency_min(adjmatrix, &edges,
+                     &weights, loops));
+        break;
+    case IGRAPH_ADJ_PLUS:
+        IGRAPH_CHECK(igraph_i_weighted_adjacency_plus(adjmatrix, &edges,
+                     &weights, loops));
+        break;
+    }
+
+    /* Prepare attribute record */
+    attr_rec.name = attr ? attr : default_attr;
+    attr_rec.type = IGRAPH_ATTRIBUTE_NUMERIC;
+    attr_rec.value = &weights;
+    VECTOR(attr_vec)[0] = &attr_rec;
+
+    /* Create graph */
+    IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes,
+                              (mode == IGRAPH_ADJ_DIRECTED)));
+    IGRAPH_FINALLY(igraph_destroy, graph);
+    if (igraph_vector_size(&edges) > 0) {
+        IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec));
+    }
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Cleanup */
+    igraph_vector_destroy(&edges);
+    igraph_vector_destroy(&weights);
+    igraph_vector_ptr_destroy(&attr_vec);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_star
+ * \brief Creates a \em star graph, every vertex connects only to the center.
+ *
+ * \param graph Pointer to an uninitialized graph object, this will
+ *        be the result.
+ * \param n Integer constant, the number of vertices in the graph.
+ * \param mode Constant, gives the type of the star graph to
+ *        create. Possible values:
+ *        \clist
+ *        \cli IGRAPH_STAR_OUT
+ *          directed star graph, edges point
+ *          \em from the center to the other vertices.
+ *        \cli IGRAPH_STAR_IN
+ *          directed star graph, edges point
+ *          \em to the center from the other vertices.
+ *        \cli IGRAPH_STAR_MUTUAL
+ *          directed star graph with mutual edges.
+ *        \cli IGRAPH_STAR_UNDIRECTED
+ *          an undirected star graph is
+ *          created.
+ *        \endclist
+ * \param center Id of the vertex which will be the center of the
+ *          graph.
+ * \return Error code:
+ *         \clist
+ *         \cli IGRAPH_EINVVID
+ *           invalid number of vertices.
+ *         \cli IGRAPH_EINVAL
+ *           invalid center vertex.
+ *         \cli IGRAPH_EINVMODE
+ *           invalid mode argument.
+ *         \endclist
+ *
+ * Time complexity: O(|V|), the
+ * number of vertices in the graph.
+ *
+ * \sa \ref igraph_lattice(), \ref igraph_ring(), \ref igraph_tree()
+ * for creating other regular structures.
+ *
+ * \example examples/simple/igraph_star.c
+ */
+
+int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode,
+                igraph_integer_t center) {
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int i;
+
+    if (n < 0) {
+        IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVVID);
+    }
+    if (center < 0 || center > n - 1) {
+        IGRAPH_ERROR("Invalid center vertex", IGRAPH_EINVAL);
+    }
+    if (mode != IGRAPH_STAR_OUT && mode != IGRAPH_STAR_IN &&
+        mode != IGRAPH_STAR_MUTUAL && mode != IGRAPH_STAR_UNDIRECTED) {
+        IGRAPH_ERROR("invalid mode", IGRAPH_EINVMODE);
+    }
+
+    if (mode != IGRAPH_STAR_MUTUAL) {
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, (n - 1) * 2);
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&edges, (n - 1) * 2 * 2);
+    }
+
+    if (mode == IGRAPH_STAR_OUT) {
+        for (i = 0; i < center; i++) {
+            VECTOR(edges)[2 * i] = center;
+            VECTOR(edges)[2 * i + 1] = i;
+        }
+        for (i = center + 1; i < n; i++) {
+            VECTOR(edges)[2 * (i - 1)] = center;
+            VECTOR(edges)[2 * (i - 1) + 1] = i;
+        }
+    } else if (mode == IGRAPH_STAR_MUTUAL) {
+        for (i = 0; i < center; i++) {
+            VECTOR(edges)[4 * i] = center;
+            VECTOR(edges)[4 * i + 1] = i;
+            VECTOR(edges)[4 * i + 2] = i;
+            VECTOR(edges)[4 * i + 3] = center;
+        }
+        for (i = center + 1; i < n; i++) {
+            VECTOR(edges)[4 * i - 4] = center;
+            VECTOR(edges)[4 * i - 3] = i;
+            VECTOR(edges)[4 * i - 2] = i;
+            VECTOR(edges)[4 * i - 1] = center;
+        }
+    } else {
+        for (i = 0; i < center; i++) {
+            VECTOR(edges)[2 * i + 1] = center;
+            VECTOR(edges)[2 * i] = i;
+        }
+        for (i = center + 1; i < n; i++) {
+            VECTOR(edges)[2 * (i - 1) + 1] = center;
+            VECTOR(edges)[2 * (i - 1)] = i;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, 0,
+                               (mode != IGRAPH_STAR_UNDIRECTED)));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_lattice
+ * \brief Creates most kinds of lattices.
+ *
+ * \param graph An uninitialized graph object.
+ * \param dimvector Vector giving the sizes of the lattice in each of
+ *        its dimensions. Ie. the dimension of the lattice will be the
+ *        same as the length of this vector.
+ * \param nei Integer value giving the distance (number of steps)
+ *        within which two vertices will be connected.
+ * \param directed Boolean, whether to create a directed graph. The
+ *        direction of the edges is determined by the generation
+ *        algorithm and is unlikely to suit you, so this isn't a very
+ *        useful option.
+ * \param mutual Boolean, if the graph is directed this gives whether
+ *        to create all connections as mutual.
+ * \param circular Boolean, defines whether the generated lattice is
+ *        periodic.
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid (negative)
+ *         dimension vector.
+ *
+ * Time complexity: if \p nei is less than two then it is O(|V|+|E|) (as
+ * far as I remember), |V| and |E| are the number of vertices
+ * and edges in the generated graph. Otherwise it is O(|V|*d^o+|E|), d
+ * is the average degree of the graph, o is the \p nei argument.
+ */
+int igraph_lattice(igraph_t *graph, const igraph_vector_t *dimvector,
+                   igraph_integer_t nei, igraph_bool_t directed, igraph_bool_t mutual,
+                   igraph_bool_t circular) {
+
+    long int dims = igraph_vector_size(dimvector);
+    long int no_of_nodes = (long int) igraph_vector_prod(dimvector);
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int *coords, *weights;
+    long int i, j;
+    int carry, pos;
+
+    if (igraph_vector_any_smaller(dimvector, 0)) {
+        IGRAPH_ERROR("Invalid dimension vector", IGRAPH_EINVAL);
+    }
+
+    /* init coords & weights */
+
+    coords = igraph_Calloc(dims, long int);
+    if (coords == 0) {
+        IGRAPH_ERROR("lattice failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, coords); /* TODO: hack */
+    weights = igraph_Calloc(dims, long int);
+    if (weights == 0) {
+        IGRAPH_ERROR("lattice failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(free, weights);
+    if (dims > 0) {
+        weights[0] = 1;
+        for (i = 1; i < dims; i++) {
+            weights[i] = weights[i - 1] * (long int) VECTOR(*dimvector)[i - 1];
+        }
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_nodes * dims +
+                                       mutual * directed * no_of_nodes * dims));
+
+    for (i = 0; i < no_of_nodes; i++) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        for (j = 0; j < dims; j++) {
+            if (circular || coords[j] != VECTOR(*dimvector)[j] - 1) {
+                long int new_nei;
+                if (coords[j] != VECTOR(*dimvector)[j] - 1) {
+                    new_nei = i + weights[j] + 1;
+                } else {
+                    new_nei = i - (long int) (VECTOR(*dimvector)[j] - 1) * weights[j] + 1;
+                }
+                if (new_nei != i + 1 &&
+                    (VECTOR(*dimvector)[j] != 2 || coords[j] != 1 || directed)) {
+                    igraph_vector_push_back(&edges, i); /* reserved */
+                    igraph_vector_push_back(&edges, new_nei - 1); /* reserved */
+                }
+            } /* if circular || coords[j] */
+            if (mutual && directed && (circular || coords[j] != 0)) {
+                long int new_nei;
+                if (coords[j] != 0) {
+                    new_nei = i - weights[j] + 1;
+                } else {
+                    new_nei = i + (long int) (VECTOR(*dimvector)[j] - 1) * weights[j] + 1;
+                }
+                if (new_nei != i + 1 &&
+                    (VECTOR(*dimvector)[j] != 2 || !circular)) {
+                    igraph_vector_push_back(&edges, i); /* reserved */
+                    igraph_vector_push_back(&edges, new_nei - 1); /* reserved */
+                }
+            } /* if circular || coords[0] */
+        } /* for j<dims */
+
+        /* increase coords */
+        carry = 1;
+        pos = 0;
+
+        while (carry == 1 && pos != dims) {
+            if (coords[pos] != VECTOR(*dimvector)[pos] - 1) {
+                coords[pos]++;
+                carry = 0;
+            } else {
+                coords[pos] = 0;
+                pos++;
+            }
+        }
+
+    } /* for i<no_of_nodes */
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    if (nei >= 2) {
+        IGRAPH_CHECK(igraph_connect_neighborhood(graph, nei, IGRAPH_ALL));
+    }
+
+    /* clean up */
+    igraph_Free(coords);
+    igraph_Free(weights);
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_ring
+ * \brief Creates a \em ring graph, a one dimensional lattice.
+ *
+ * An undirected (circular) ring on n vertices is commonly known in graph
+ * theory as the cycle graph C_n.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param n The number of vertices in the ring.
+ * \param directed Logical, whether to create a directed ring.
+ * \param mutual Logical, whether to create mutual edges in a directed
+ *        ring. It is ignored for undirected graphs.
+ * \param circular Logical, if false, the ring will be open (this is
+ *        not a real \em ring actually).
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid number of vertices.
+ *
+ * Time complexity: O(|V|), the
+ * number of vertices in the graph.
+ *
+ * \sa \ref igraph_lattice() for generating more general lattices.
+ *
+ * \example examples/simple/igraph_ring.c
+ */
+
+int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed,
+                igraph_bool_t mutual, igraph_bool_t circular) {
+
+    igraph_vector_t v = IGRAPH_VECTOR_NULL;
+
+    if (n < 0) {
+        IGRAPH_ERROR("negative number of vertices", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&v, 1);
+    VECTOR(v)[0] = n;
+
+    IGRAPH_CHECK(igraph_lattice(graph, &v, 1, directed, mutual, circular));
+    igraph_vector_destroy(&v);
+
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_tree
+ * \brief Creates a tree in which almost all vertices have the same number of children.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param n Integer, the number of vertices in the graph.
+ * \param children Integer, the number of children of a vertex in the
+ *        tree.
+ * \param type Constant, gives whether to create a directed tree, and
+ *        if this is the case, also its orientation. Possible values:
+ *        \clist
+ *        \cli IGRAPH_TREE_OUT
+ *          directed tree, the edges point
+ *          from the parents to their children,
+ *        \cli IGRAPH_TREE_IN
+ *          directed tree, the edges point from
+ *          the children to their parents.
+ *        \cli IGRAPH_TREE_UNDIRECTED
+ *          undirected tree.
+ *        \endclist
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid number of vertices.
+ *         \c IGRAPH_INVMODE: invalid mode argument.
+ *
+ * Time complexity: O(|V|+|E|), the
+ * number of vertices plus the number of edges in the graph.
+ *
+ * \sa \ref igraph_lattice(), \ref igraph_star() for creating other regular
+ * structures; \ref igraph_from_prufer() for creating arbitrary trees;
+ * \ref igraph_tree_game() for uniform random sampling of trees.
+ *
+ * \example examples/simple/igraph_tree.c
+ */
+
+int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children,
+                igraph_tree_mode_t type) {
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int i, j;
+    long int idx = 0;
+    long int to = 1;
+
+    if (n < 0 || children <= 0) {
+        IGRAPH_ERROR("Invalid number of vertices or children", IGRAPH_EINVAL);
+    }
+    if (type != IGRAPH_TREE_OUT && type != IGRAPH_TREE_IN &&
+        type != IGRAPH_TREE_UNDIRECTED) {
+        IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (n - 1));
+
+    i = 0;
+    if (type == IGRAPH_TREE_OUT) {
+        while (idx < 2 * (n - 1)) {
+            for (j = 0; j < children && idx < 2 * (n - 1); j++) {
+                VECTOR(edges)[idx++] = i;
+                VECTOR(edges)[idx++] = to++;
+            }
+            i++;
+        }
+    } else {
+        while (idx < 2 * (n - 1)) {
+            for (j = 0; j < children && idx < 2 * (n - 1); j++) {
+                VECTOR(edges)[idx++] = to++;
+                VECTOR(edges)[idx++] = i;
+            }
+            i++;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, type != IGRAPH_TREE_UNDIRECTED));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \ingroup generators
+ * \function igraph_full
+ * \brief Creates a full graph (directed or undirected, with or without loops).
+ *
+ * </para><para>
+ * In a full graph every possible edge is present, every vertex is
+ * connected to every other vertex. A full graph in \c igraph should be
+ * distinguished from the concept of complete graphs as used in graph theory.
+ * If n is a positive integer, then the complete graph K_n on n vertices is
+ * the undirected simple graph with the following property. For any distinct
+ * pair (u,v) of vertices in K_n, uv (or equivalently vu) is an edge of K_n.
+ * In \c igraph, a full graph on n vertices can be K_n, a directed version of
+ * K_n, or K_n with at least one loop edge. In any case, if F is a full graph
+ * on n vertices as generated by \c igraph, then K_n is a subgraph of the
+ * undirected version of F.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param n Integer, the number of vertices in the graph.
+ * \param directed Logical, whether to create a directed graph.
+ * \param loops Logical, whether to include self-edges (loops).
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid number of vertices.
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| is the number of vertices,
+ * |E| the number of edges in the
+ * graph. Of course this is the same as
+ * O(|E|)=O(|V||V|)
+ * here.
+ *
+ * \sa \ref igraph_lattice(), \ref igraph_star(), \ref igraph_tree()
+ * for creating other regular structures.
+ *
+ * \example examples/simple/igraph_full.c
+ */
+
+int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed,
+                igraph_bool_t loops) {
+
+    igraph_vector_t edges = IGRAPH_VECTOR_NULL;
+    long int i, j;
+
+    if (n < 0) {
+        IGRAPH_ERROR("invalid number of vertices", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    if (directed && loops) {
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, n * n));
+        for (i = 0; i < n; i++) {
+            for (j = 0; j < n; j++) {
+                igraph_vector_push_back(&edges, i); /* reserved */
+                igraph_vector_push_back(&edges, j); /* reserved */
+            }
+        }
+    } else if (directed && !loops) {
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n - 1)));
+        for (i = 0; i < n; i++) {
+            for (j = 0; j < i; j++) {
+                igraph_vector_push_back(&edges, i); /* reserved */
+                igraph_vector_push_back(&edges, j); /* reserved */
+            }
+            for (j = i + 1; j < n; j++) {
+                igraph_vector_push_back(&edges, i); /* reserved */
+                igraph_vector_push_back(&edges, j); /* reserved */
+            }
+        }
+    } else if (!directed && loops) {
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n + 1) / 2));
+        for (i = 0; i < n; i++) {
+            for (j = i; j < n; j++) {
+                igraph_vector_push_back(&edges, i); /* reserved */
+                igraph_vector_push_back(&edges, j); /* reserved */
+            }
+        }
+    } else {
+        IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n - 1) / 2));
+        for (i = 0; i < n; i++) {
+            for (j = i + 1; j < n; j++) {
+                igraph_vector_push_back(&edges, i); /* reserved */
+                igraph_vector_push_back(&edges, j); /* reserved */
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_full_citation
+ * Creates a full citation graph
+ *
+ * This is a directed graph, where every <code>i->j</code> edge is
+ * present if and only if <code>j&lt;i</code>.
+ * If the \c directed argument is zero then an undirected graph is
+ * created, and it is just a full graph.
+ * \param graph Pointer to an uninitialized graph object, the result
+ *    is stored here.
+ * \param n The number of vertices.
+ * \param directed Whether to created a directed graph. If zero an
+ *    undirected graph is created.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|^2), as we have many edges.
+ */
+
+int igraph_full_citation(igraph_t *graph, igraph_integer_t n,
+                         igraph_bool_t directed) {
+    igraph_vector_t edges;
+    long int i, j, ptr = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, n * (n - 1));
+    for (i = 1; i < n; i++) {
+        for (j = 0; j < i; j++) {
+            VECTOR(edges)[ptr++] = i;
+            VECTOR(edges)[ptr++] = j;
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_small
+ * \brief Shorthand to create a short graph, giving the edges as arguments.
+ *
+ * </para><para>
+ * This function is handy when a relatively small graph needs to be created.
+ * Instead of giving the edges as a vector, they are given simply as
+ * arguments and a '-1' needs to be given after the last meaningful
+ * edge argument.
+ *
+ * </para><para>Note that only graphs which have vertices less than
+ * the highest value of the 'int' type can be created this way. If you
+ * give larger values then the result is undefined.
+ *
+ * \param graph Pointer to an uninitialized graph object. The result
+ *        will be stored here.
+ * \param n The number of vertices in the graph; a nonnegative integer.
+ * \param directed Logical constant; gives whether the graph should be
+ *        directed. Supported values are:
+ *        \clist
+ *        \cli IGRAPH_DIRECTED
+ *          The graph to be created will be \em directed.
+ *        \cli IGRAPH_UNDIRECTED
+ *          The graph to be created will be \em undirected.
+ *        \endclist
+ * \param ... The additional arguments giving the edges of the
+ *        graph. Don't forget to supply an additional '-1' after the last
+ *        (meaningful) argument.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges in the graph to create.
+ *
+ * \example examples/simple/igraph_small.c
+ */
+
+int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed,
+                 ...) {
+    igraph_vector_t edges;
+    va_list ap;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    va_start(ap, directed);
+    while (1) {
+        int num = va_arg(ap, int);
+        if (num == -1) {
+            break;
+        }
+        igraph_vector_push_back(&edges, num);
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, n, directed));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_extended_chordal_ring
+ * Create an extended chordal ring
+ *
+ * An extended chordal ring is a cycle graph with additional chords
+ * connecting its vertices.
+ *
+ * Each row \c L of the matrix \p W specifies a set of chords to be
+ * inserted, in the following way: vertex \c i will connect to a vertex
+ * <code>L[(i mod p)]</code> steps ahead of it along the cycle, where
+ * \c p is the length of \c L.
+ * In other words, vertex \c i will be connected to vertex
+ * <code>(i + L[(i mod p)]) mod nodes</code>.
+ *
+ * </para><para>
+ * See also Kotsis, G: Interconnection Topologies for Parallel Processing
+ * Systems, PARS Mitteilungen 11, 1-6, 1993.
+ *
+ * \param graph Pointer to an uninitialized graph object, the result
+ *   will be stored here.
+ * \param nodes Integer constant, the number of vertices in the
+ *   graph. It must be at least 3.
+ * \param W The matrix specifying the extra edges. The number of
+ *   columns should divide the number of total vertices.
+ * \param directed Whether the graph should be directed.
+ * \return Error code.
+ *
+ * \sa \ref igraph_ring(), \ref igraph_lcf(), \ref igraph_lcf_vector()
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges.
+ */
+
+int igraph_extended_chordal_ring(
+    igraph_t *graph, igraph_integer_t nodes, const igraph_matrix_t *W,
+    igraph_bool_t directed) {
+    igraph_vector_t edges;
+    long int period = igraph_matrix_ncol(W);
+    long int nrow   = igraph_matrix_nrow(W);
+    long int i, j, mpos = 0, epos = 0;
+
+    if (nodes < 3) {
+        IGRAPH_ERROR("An extended chordal ring has at least 3 nodes", IGRAPH_EINVAL);
+    }
+
+    if ((long int)nodes % period != 0) {
+        IGRAPH_ERROR("The period (number of columns in W) should divide the "
+                     "number of nodes", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (nodes + nodes * nrow));
+
+    for (i = 0; i < nodes - 1; i++) {
+        VECTOR(edges)[epos++] = i;
+        VECTOR(edges)[epos++] = i + 1;
+    }
+    VECTOR(edges)[epos++] = nodes - 1;
+    VECTOR(edges)[epos++] = 0;
+
+    if (nrow > 0) {
+        for (i = 0; i < nodes; i++) {
+            for (j = 0; j < nrow; j++) {
+                long int offset = (long int) MATRIX(*W, j, mpos);
+                long int v = (i + offset) % nodes;
+
+                if (v < 0) {
+                    v += nodes;    /* handle negative offsets */
+                }
+
+                VECTOR(edges)[epos++] = i;
+                VECTOR(edges)[epos++] = v;
+
+            }
+            mpos++; if (mpos == period) {
+                mpos = 0;
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * \function igraph_connect_neighborhood
+ * \brief Connects every vertex to its neighborhood
+ *
+ * This function adds new edges to the input graph. Each vertex is connected
+ * to all vertices reachable by at most \p order steps from it
+ * (unless a connection already existed).  In other words, the \p order power of
+ * the graph is computed.
+ *
+ * </para><para> Note that the input graph is modified in place, no
+ * new graph is created. Call \ref igraph_copy() if you want to keep
+ * the original graph as well.
+ *
+ * </para><para> For undirected graphs reachability is always
+ * symmetric: if vertex A can be reached from vertex B in at
+ * most \p order steps, then the opposite is also true. Only one
+ * undirected (A,B) edge will be added in this case.
+ * \param graph The input graph, this is the output graph as well.
+ * \param order Integer constant, it gives the distance within which
+ *    the vertices will be connected to the source vertex.
+ * \param mode Constant, it specifies how the neighborhood search is
+ *    performed for directed graphs. If \c IGRAPH_OUT then vertices
+ *    reachable from the source vertex will be connected, \c IGRAPH_IN
+ *    is the opposite. If \c IGRAPH_ALL then the directed graph is
+ *    considered as an undirected one.
+ * \return Error code.
+ *
+ * \sa \ref igraph_lattice() uses this function to connect the
+ * neighborhood of the vertices.
+ *
+ * Time complexity: O(|V|*d^k), |V| is the number of vertices in the
+ * graph, d is the average degree and k is the \p order argument.
+ */
+
+int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order,
+                                igraph_neimode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_dqueue_t q;
+    igraph_vector_t edges;
+    long int i, j, in;
+    long int *added;
+    igraph_vector_t neis;
+
+    if (order < 0) {
+        IGRAPH_ERROR("Negative order, cannot connect neighborhood", IGRAPH_EINVAL);
+    }
+
+    if (order < 2) {
+        IGRAPH_WARNING("Order smaller than two, graph will be unchanged");
+    }
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    added = igraph_Calloc(no_of_nodes, long int);
+    if (added == 0) {
+        IGRAPH_ERROR("Cannot connect neighborhood", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added);
+    IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        added[i] = i + 1;
+        igraph_neighbors(graph, &neis, (igraph_integer_t) i, mode);
+        in = igraph_vector_size(&neis);
+        if (order > 1) {
+            for (j = 0; j < in; j++) {
+                long int nei = (long int) VECTOR(neis)[j];
+                added[nei] = i + 1;
+                igraph_dqueue_push(&q, nei);
+                igraph_dqueue_push(&q, 1);
+            }
+        }
+
+        while (!igraph_dqueue_empty(&q)) {
+            long int actnode = (long int) igraph_dqueue_pop(&q);
+            long int actdist = (long int) igraph_dqueue_pop(&q);
+            long int n;
+            igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, mode);
+            n = igraph_vector_size(&neis);
+
+            if (actdist < order - 1) {
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (added[nei] != i + 1) {
+                        added[nei] = i + 1;
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, nei));
+                        IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+                        if (mode != IGRAPH_ALL || i < nei) {
+                            if (mode == IGRAPH_IN) {
+                                IGRAPH_CHECK(igraph_vector_push_back(&edges, nei));
+                                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                            } else {
+                                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                                IGRAPH_CHECK(igraph_vector_push_back(&edges, nei));
+                            }
+                        }
+                    }
+                }
+            } else {
+                for (j = 0; j < n; j++) {
+                    long int nei = (long int) VECTOR(neis)[j];
+                    if (added[nei] != i + 1) {
+                        added[nei] = i + 1;
+                        if (mode != IGRAPH_ALL || i < nei) {
+                            if (mode == IGRAPH_IN) {
+                                IGRAPH_CHECK(igraph_vector_push_back(&edges, nei));
+                                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                            } else {
+                                IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
+                                IGRAPH_CHECK(igraph_vector_push_back(&edges, nei));
+                            }
+                        }
+                    }
+                }
+            }
+
+        } /* while q not empty */
+    } /* for i < no_of_nodes */
+
+    igraph_vector_destroy(&neis);
+    igraph_dqueue_destroy(&q);
+    igraph_free(added);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_de_bruijn
+ * \brief Generate a de Bruijn graph.
+ *
+ * A de Bruijn graph represents relationships between strings. An alphabet
+ * of \c m letters are used and strings of length \c n are considered.
+ * A vertex corresponds to every possible string and there is a directed edge
+ * from vertex \c v to vertex \c w if the string of \c v can be transformed into
+ * the string of \c w by removing its first letter and appending a letter to it.
+ *
+ * </para><para>
+ * Please note that the graph will have \c m to the power \c n vertices and
+ * even more edges, so probably you don't want to supply too big numbers for
+ * \c m and \c n.
+ *
+ * </para><para>
+ * De Bruijn graphs have some interesting properties, please see another source,
+ * eg. Wikipedia for details.
+ *
+ * \param graph Pointer to an uninitialized graph object, the result will be
+ *        stored here.
+ * \param m Integer, the number of letters in the alphabet.
+ * \param n Integer, the length of the strings.
+ * \return Error code.
+ *
+ * \sa \ref igraph_kautz().
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges.
+ */
+
+int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) {
+
+    /* m - number of symbols */
+    /* n - length of strings */
+
+    long int no_of_nodes, no_of_edges;
+    igraph_vector_t edges;
+    long int i, j;
+    long int mm = m;
+
+    if (m < 0 || n < 0) {
+        IGRAPH_ERROR("`m' and `n' should be non-negative in a de Bruijn graph",
+                     IGRAPH_EINVAL);
+    }
+
+    if (n == 0) {
+        return igraph_empty(graph, 1, IGRAPH_DIRECTED);
+    }
+    if (m == 0) {
+        return igraph_empty(graph, 0, IGRAPH_DIRECTED);
+    }
+
+    no_of_nodes = (long int) pow(m, n);
+    no_of_edges = no_of_nodes * m;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+
+    for (i = 0; i < no_of_nodes; i++) {
+        long int basis = (i * mm) % no_of_nodes;
+        for (j = 0; j < m; j++) {
+            igraph_vector_push_back(&edges, i);
+            igraph_vector_push_back(&edges, basis + j);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               IGRAPH_DIRECTED));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_kautz
+ * \brief Generate a Kautz graph.
+ *
+ * A Kautz graph is a labeled graph, vertices are labeled by strings
+ * of length \c n+1 above an alphabet with \c m+1 letters, with
+ * the restriction that every two consecutive letters in the string
+ * must be different. There is a directed edge from a vertex \c v to
+ * another vertex \c w if it is possible to transform the string of
+ * \c v into the string of \c w by removing the first letter and
+ * appending a letter to it.
+ *
+ * </para><para>
+ * Kautz graphs have some interesting properties, see eg. Wikipedia
+ * for details.
+ *
+ * </para><para>
+ * Vincent Matossian wrote the first version of this function in R,
+ * thanks.
+ * \param graph Pointer to an uninitialized graph object, the result
+ * will be stored here.
+ * \param m Integer, \c m+1 is the number of letters in the alphabet.
+ * \param n Integer, \c n+1 is the length of the strings.
+ * \return Error code.
+ *
+ * \sa \ref igraph_de_bruijn().
+ *
+ * Time complexity: O(|V|* [(m+1)/m]^n +|E|), in practice it is more
+ * like O(|V|+|E|). |V| is the number of vertices, |E| is the number
+ * of edges and \c m and \c n are the corresponding arguments.
+ */
+
+int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) {
+
+    /* m+1 - number of symbols */
+    /* n+1 - length of strings */
+
+    long int mm = m;
+    long int no_of_nodes, no_of_edges;
+    long int allstrings;
+    long int i, j, idx = 0;
+    igraph_vector_t edges;
+    igraph_vector_long_t digits, table;
+    igraph_vector_long_t index1, index2;
+    long int actb = 0;
+    long int actvalue = 0;
+
+    if (m < 0 || n < 0) {
+        IGRAPH_ERROR("`m' and `n' should be non-negative in a Kautz graph",
+                     IGRAPH_EINVAL);
+    }
+
+    if (n == 0) {
+        return igraph_full(graph, m + 1, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS);
+    }
+    if (m == 0) {
+        return igraph_empty(graph, 0, IGRAPH_DIRECTED);
+    }
+
+    no_of_nodes = (long int) ((m + 1) * pow(m, n));
+    no_of_edges = no_of_nodes * m;
+    allstrings = (long int) pow(m + 1, n + 1);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    IGRAPH_CHECK(igraph_vector_long_init(&table, n + 1));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &table);
+    j = 1;
+    for (i = n; i >= 0; i--) {
+        VECTOR(table)[i] = j;
+        j *= (m + 1);
+    }
+
+    IGRAPH_CHECK(igraph_vector_long_init(&digits, n + 1));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &digits);
+    IGRAPH_CHECK(igraph_vector_long_init(&index1, (long int) pow(m + 1, n + 1)));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &index1);
+    IGRAPH_CHECK(igraph_vector_long_init(&index2, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &index2);
+
+    /* Fill the index tables*/
+    while (1) {
+        /* at the beginning of the loop, 0:actb contain the valid prefix */
+        /* we might need to fill it to get a valid string */
+        long int z = 0;
+        if (VECTOR(digits)[actb] == 0) {
+            z = 1;
+        }
+        for (actb++; actb <= n; actb++) {
+            VECTOR(digits)[actb] = z;
+            actvalue += z * VECTOR(table)[actb];
+            z = 1 - z;
+        }
+        actb = n;
+
+        /* ok, we have a valid string now */
+        VECTOR(index1)[actvalue] = idx + 1;
+        VECTOR(index2)[idx] = actvalue;
+        idx++;
+
+        /* finished? */
+        if (idx >= no_of_nodes) {
+            break;
+        }
+
+        /* not yet, we need a valid prefix now */
+        while (1) {
+            /* try to increase digits at position actb */
+            long int next = VECTOR(digits)[actb] + 1;
+            if (actb != 0 && VECTOR(digits)[actb - 1] == next) {
+                next++;
+            }
+            if (next <= m) {
+                /* ok, no problem */
+                actvalue += (next - VECTOR(digits)[actb]) * VECTOR(table)[actb];
+                VECTOR(digits)[actb] = next;
+                break;
+            } else {
+                /* bad luck, try the previous digit */
+                actvalue -= VECTOR(digits)[actb] * VECTOR(table)[actb];
+                actb--;
+            }
+        }
+    }
+
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+
+    /* Now come the edges at last */
+    for (i = 0; i < no_of_nodes; i++) {
+        long int fromvalue = VECTOR(index2)[i];
+        long int lastdigit = fromvalue % (mm + 1);
+        long int basis = (fromvalue * (mm + 1)) % allstrings;
+        for (j = 0; j <= m; j++) {
+            long int tovalue, to;
+            if (j == lastdigit) {
+                continue;
+            }
+            tovalue = basis + j;
+            to = VECTOR(index1)[tovalue] - 1;
+            if (to < 0) {
+                continue;
+            }
+            igraph_vector_push_back(&edges, i);
+            igraph_vector_push_back(&edges, to);
+        }
+    }
+
+    igraph_vector_long_destroy(&index2);
+    igraph_vector_long_destroy(&index1);
+    igraph_vector_long_destroy(&digits);
+    igraph_vector_long_destroy(&table);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               IGRAPH_DIRECTED));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_lcf_vector
+ * \brief Create a graph from LCF notation
+ *
+ * This function is essentially the same as \ref igraph_lcf(), only
+ * the way for giving the arguments is different. See \ref
+ * igraph_lcf() for details.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param n Integer constant giving the number of vertices.
+ * \param shifts A vector giving the shifts.
+ * \param repeats An integer constant giving the number of repeats
+ *        for the shifts.
+ * \return Error code.
+ *
+ * \sa \ref igraph_lcf(), \ref igraph_extended_chordal_ring()
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices plus
+ * the number of edges.
+ */
+
+int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n,
+                      const igraph_vector_t *shifts,
+                      igraph_integer_t repeats) {
+
+    igraph_vector_t edges;
+    long int no_of_shifts = igraph_vector_size(shifts);
+    long int ptr = 0, i, sptr = 0;
+    long int no_of_nodes = n;
+    long int no_of_edges = n + no_of_shifts * repeats;
+
+    if (repeats < 0) {
+        IGRAPH_ERROR("number of repeats must be positive", IGRAPH_EINVAL);
+    }
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges);
+
+    if (no_of_nodes > 0) {
+        /* Create a ring first */
+        for (i = 0; i < no_of_nodes; i++) {
+            VECTOR(edges)[ptr++] = i;
+            VECTOR(edges)[ptr++] = i + 1;
+        }
+        VECTOR(edges)[ptr - 1] = 0;
+    }
+
+    /* Then add the rest */
+    while (ptr < 2 * no_of_edges) {
+        long int sh = (long int) VECTOR(*shifts)[sptr % no_of_shifts];
+        long int from = sptr % no_of_nodes;
+        long int to = (no_of_nodes + sptr + sh) % no_of_nodes;
+        VECTOR(edges)[ptr++] = from;
+        VECTOR(edges)[ptr++] = to;
+        sptr++;
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               IGRAPH_UNDIRECTED));
+    IGRAPH_CHECK(igraph_simplify(graph, 1 /* true */, 1 /* true */, NULL));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_lcf
+ * \brief Create a graph from LCF notation
+ *
+ * </para><para>
+ * LCF is short for Lederberg-Coxeter-Frucht, it is a concise notation for
+ * 3-regular Hamiltonian graphs. It consists of three parameters: the
+ * number of vertices in the graph, a list of shifts giving additional
+ * edges to a cycle backbone, and another integer giving how many times
+ * the shifts should be performed. See
+ * http://mathworld.wolfram.com/LCFNotation.html for details.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param n Integer, the number of vertices in the graph.
+ * \param ... The shifts and the number of repeats for the shifts,
+ *        plus an additional 0 to mark the end of the arguments.
+ * \return Error code.
+ *
+ * \sa See \ref igraph_lcf_vector() for a similar function using a
+ * vector_t instead of the variable length argument list.
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges.
+ *
+ * \example examples/simple/igraph_lcf.c
+ */
+
+int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...) {
+    igraph_vector_t shifts;
+    igraph_integer_t repeats;
+    va_list ap;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&shifts, 0);
+
+    va_start(ap, n);
+    while (1) {
+        int num = va_arg(ap, int);
+        if (num == 0) {
+            break;
+        }
+        IGRAPH_CHECK(igraph_vector_push_back(&shifts, num));
+    }
+    if (igraph_vector_size(&shifts) == 0) {
+        repeats = 0;
+    } else {
+        repeats = (igraph_integer_t) igraph_vector_pop_back(&shifts);
+    }
+
+    IGRAPH_CHECK(igraph_lcf_vector(graph, n, &shifts, repeats));
+    igraph_vector_destroy(&shifts);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+const igraph_real_t igraph_i_famous_bull[] = {
+    5, 5, 0,
+    0, 1, 0, 2, 1, 2, 1, 3, 2, 4
+};
+
+const igraph_real_t igraph_i_famous_chvatal[] = {
+    12, 24, 0,
+    5, 6, 6, 7, 7, 8, 8, 9, 5, 9, 4, 5, 4, 8, 2, 8, 2, 6, 0, 6, 0, 9, 3, 9, 3, 7,
+    1, 7, 1, 5, 1, 10, 4, 10, 4, 11, 2, 11, 0, 10, 0, 11, 3, 11, 3, 10, 1, 2
+};
+
+const igraph_real_t igraph_i_famous_coxeter[] = {
+    28, 42, 0,
+    0, 1, 0, 2, 0, 7, 1, 4, 1, 13, 2, 3, 2, 8, 3, 6, 3, 9, 4, 5, 4, 12, 5, 6, 5,
+    11, 6, 10, 7, 19, 7, 24, 8, 20, 8, 23, 9, 14, 9, 22, 10, 15, 10, 21, 11, 16,
+    11, 27, 12, 17, 12, 26, 13, 18, 13, 25, 14, 17, 14, 18, 15, 18, 15, 19, 16, 19,
+    16, 20, 17, 20, 21, 23, 21, 26, 22, 24, 22, 27, 23, 25, 24, 26, 25, 27
+};
+
+const igraph_real_t igraph_i_famous_cubical[] = {
+    8, 12, 0,
+    0, 1, 1, 2, 2, 3, 0, 3, 4, 5, 5, 6, 6, 7, 4, 7, 0, 4, 1, 5, 2, 6, 3, 7
+};
+
+const igraph_real_t igraph_i_famous_diamond[] = {
+    4, 5, 0,
+    0, 1, 0, 2, 1, 2, 1, 3, 2, 3
+};
+
+const igraph_real_t igraph_i_famous_dodecahedron[] = {
+    20, 30, 0,
+    0, 1, 0, 4, 0, 5, 1, 2, 1, 6, 2, 3, 2, 7, 3, 4, 3, 8, 4, 9, 5, 10, 5, 11, 6,
+    10, 6, 14, 7, 13, 7, 14, 8, 12, 8, 13, 9, 11, 9, 12, 10, 15, 11, 16, 12, 17,
+    13, 18, 14, 19, 15, 16, 15, 19, 16, 17, 17, 18, 18, 19
+};
+
+const igraph_real_t igraph_i_famous_folkman[] = {
+    20, 40, 0,
+    0, 5, 0, 8, 0, 10, 0, 13, 1, 7, 1, 9, 1, 12, 1, 14, 2, 6, 2, 8, 2, 11, 2, 13,
+    3, 5, 3, 7, 3, 10, 3, 12, 4, 6, 4, 9, 4, 11, 4, 14, 5, 15, 5, 19, 6, 15, 6, 16,
+    7, 16, 7, 17, 8, 17, 8, 18, 9, 18, 9, 19, 10, 15, 10, 19, 11, 15, 11, 16, 12,
+    16, 12, 17, 13, 17, 13, 18, 14, 18, 14, 19
+};
+
+const igraph_real_t igraph_i_famous_franklin[] = {
+    12, 18, 0,
+    0, 1, 0, 2, 0, 6, 1, 3, 1, 7, 2, 4, 2, 10, 3, 5, 3, 11, 4, 5, 4, 6, 5, 7, 6, 8,
+    7, 9, 8, 9, 8, 11, 9, 10, 10, 11
+};
+
+const igraph_real_t igraph_i_famous_frucht[] = {
+    12, 18, 0,
+    0, 1, 0, 2, 0, 11, 1, 3, 1, 6, 2, 5, 2, 10, 3, 4, 3, 6, 4, 8, 4, 11, 5, 9, 5,
+    10, 6, 7, 7, 8, 7, 9, 8, 9, 10, 11
+};
+
+const igraph_real_t igraph_i_famous_grotzsch[] = {
+    11, 20, 0,
+    0, 1, 0, 2, 0, 7, 0, 10, 1, 3, 1, 6, 1, 9, 2, 4, 2, 6, 2, 8, 3, 4, 3, 8, 3, 10,
+    4, 7, 4, 9, 5, 6, 5, 7, 5, 8, 5, 9, 5, 10
+};
+
+const igraph_real_t igraph_i_famous_heawood[] = {
+    14, 21, 0,
+    0, 1, 0, 5, 0, 13, 1, 2, 1, 10, 2, 3, 2, 7, 3, 4, 3, 12, 4, 5, 4, 9, 5, 6, 6,
+    7, 6, 11, 7, 8, 8, 9, 8, 13, 9, 10, 10, 11, 11, 12, 12, 13
+};
+
+const igraph_real_t igraph_i_famous_herschel[] = {
+    11, 18, 0,
+    0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 6, 1, 7, 2, 10, 3, 9, 4, 8, 4, 9, 5, 8,
+    5, 10, 6, 8, 6, 9, 7, 8, 7, 10
+};
+
+const igraph_real_t igraph_i_famous_house[] = {
+    5, 6, 0,
+    0, 1, 0, 2, 1, 3, 2, 3, 2, 4, 3, 4
+};
+
+const igraph_real_t igraph_i_famous_housex[] = {
+    5, 8, 0,
+    0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, 2, 4, 3, 4
+};
+
+const igraph_real_t igraph_i_famous_icosahedron[] = {
+    12, 30, 0,
+    0, 1, 0, 2, 0, 3, 0, 4, 0, 8, 1, 2, 1, 6, 1, 7, 1, 8, 2, 4, 2, 5, 2, 6, 3, 4,
+    3, 8, 3, 9, 3, 11, 4, 5, 4, 11, 5, 6, 5, 10, 5, 11, 6, 7, 6, 10, 7, 8, 7, 9, 7,
+    10, 8, 9, 9, 10, 9, 11, 10, 11
+};
+
+const igraph_real_t igraph_i_famous_krackhardt_kite[] = {
+    10, 18, 0,
+    0, 1, 0, 2, 0, 3, 0, 5, 1, 3, 1, 4, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6, 5, 7, 6, 7, 7, 8, 8, 9
+};
+
+const igraph_real_t igraph_i_famous_levi[] = {
+    30, 45, 0,
+    0, 1, 0, 7, 0, 29, 1, 2, 1, 24, 2, 3, 2, 11, 3, 4, 3, 16, 4, 5, 4, 21, 5, 6, 5,
+    26, 6, 7, 6, 13, 7, 8, 8, 9, 8, 17, 9, 10, 9, 22, 10, 11, 10, 27, 11, 12, 12,
+    13, 12, 19, 13, 14, 14, 15, 14, 23, 15, 16, 15, 28, 16, 17, 17, 18, 18, 19, 18,
+    25, 19, 20, 20, 21, 20, 29, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+    28, 28, 29
+};
+
+const igraph_real_t igraph_i_famous_mcgee[] = {
+    24, 36, 0,
+    0, 1, 0, 7, 0, 23, 1, 2, 1, 18, 2, 3, 2, 14, 3, 4, 3, 10, 4, 5, 4, 21, 5, 6, 5,
+    17, 6, 7, 6, 13, 7, 8, 8, 9, 8, 20, 9, 10, 9, 16, 10, 11, 11, 12, 11, 23, 12,
+    13, 12, 19, 13, 14, 14, 15, 15, 16, 15, 22, 16, 17, 17, 18, 18, 19, 19, 20, 20,
+    21, 21, 22, 22, 23
+};
+
+const igraph_real_t igraph_i_famous_meredith[] = {
+    70, 140, 0,
+    0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 6, 2, 4, 2, 5, 2, 6, 3, 4, 3, 5, 3, 6, 7, 11,
+    7, 12, 7, 13, 8, 11, 8, 12, 8, 13, 9, 11, 9, 12, 9, 13, 10, 11, 10, 12, 10, 13,
+    14, 18, 14, 19, 14, 20, 15, 18, 15, 19, 15, 20, 16, 18, 16, 19, 16, 20, 17, 18,
+    17, 19, 17, 20, 21, 25, 21, 26, 21, 27, 22, 25, 22, 26, 22, 27, 23, 25, 23, 26,
+    23, 27, 24, 25, 24, 26, 24, 27, 28, 32, 28, 33, 28, 34, 29, 32, 29, 33, 29, 34,
+    30, 32, 30, 33, 30, 34, 31, 32, 31, 33, 31, 34, 35, 39, 35, 40, 35, 41, 36, 39,
+    36, 40, 36, 41, 37, 39, 37, 40, 37, 41, 38, 39, 38, 40, 38, 41, 42, 46, 42, 47,
+    42, 48, 43, 46, 43, 47, 43, 48, 44, 46, 44, 47, 44, 48, 45, 46, 45, 47, 45, 48,
+    49, 53, 49, 54, 49, 55, 50, 53, 50, 54, 50, 55, 51, 53, 51, 54, 51, 55, 52, 53,
+    52, 54, 52, 55, 56, 60, 56, 61, 56, 62, 57, 60, 57, 61, 57, 62, 58, 60, 58, 61,
+    58, 62, 59, 60, 59, 61, 59, 62, 63, 67, 63, 68, 63, 69, 64, 67, 64, 68, 64, 69,
+    65, 67, 65, 68, 65, 69, 66, 67, 66, 68, 66, 69, 2, 50, 1, 51, 9, 57, 8, 58, 16,
+    64, 15, 65, 23, 36, 22, 37, 30, 43, 29, 44, 3, 21, 7, 24, 14, 31, 0, 17, 10,
+    28, 38, 42, 35, 66, 59, 63, 52, 56, 45, 49
+};
+
+const igraph_real_t igraph_i_famous_noperfectmatching[] = {
+    16, 27, 0,
+    0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, 2, 4, 3, 4, 4, 5, 5, 6, 5, 7, 6, 12, 6, 13,
+    7, 8, 7, 9, 8, 9, 8, 10, 8, 11, 9, 10, 9, 11, 10, 11, 12, 13, 12, 14, 12, 15,
+    13, 14, 13, 15, 14, 15
+};
+
+const igraph_real_t igraph_i_famous_nonline[] = {
+    50, 72, 0,
+    0, 1, 0, 2, 0, 3, 4, 6, 4, 7, 5, 6, 5, 7, 6, 7, 7, 8, 9, 11, 9, 12, 9, 13, 10,
+    11, 10, 12, 10, 13, 11, 12, 11, 13, 12, 13, 14, 15, 15, 16, 15, 17, 16, 17, 16,
+    18, 17, 18, 18, 19, 20, 21, 20, 22, 20, 23, 21, 22, 21, 23, 21, 24, 22, 23, 22,
+    24, 24, 25, 26, 27, 26, 28, 26, 29, 27, 28, 27, 29, 27, 30, 27, 31, 28, 29, 28,
+    30, 28, 31, 30, 31, 32, 34, 32, 35, 32, 36, 33, 34, 33, 35, 33, 37, 34, 35, 36,
+    37, 38, 39, 38, 40, 38, 43, 39, 40, 39, 41, 39, 42, 39, 43, 40, 41, 41, 42, 42,
+    43, 44, 45, 44, 46, 45, 46, 45, 47, 46, 47, 46, 48, 47, 48, 47, 49, 48, 49
+};
+
+const igraph_real_t igraph_i_famous_octahedron[] = {
+    6, 12, 0,
+    0, 1, 0, 2, 1, 2, 3, 4, 3, 5, 4, 5, 0, 3, 0, 5, 1, 3, 1, 4, 2, 4, 2, 5
+};
+
+const igraph_real_t igraph_i_famous_petersen[] = {
+    10, 15, 0,
+    0, 1, 0, 4, 0, 5, 1, 2, 1, 6, 2, 3, 2, 7, 3, 4, 3, 8, 4, 9, 5, 7, 5, 8, 6, 8, 6, 9, 7, 9
+};
+
+const igraph_real_t igraph_i_famous_robertson[] = {
+    19, 38, 0,
+    0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12,
+    12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 0, 18, 0, 4, 4, 9, 9, 13, 13,
+    17, 2, 17, 2, 6, 6, 10, 10, 15, 0, 15, 1, 8, 8, 16, 5, 16, 5, 12, 1, 12, 7, 18,
+    7, 14, 3, 14, 3, 11, 11, 18
+};
+
+const igraph_real_t igraph_i_famous_smallestcyclicgroup[] = {
+    9, 15, 0,
+    0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 1, 2, 1, 3, 1, 7, 1, 8, 2, 5, 2, 6, 2, 7, 3, 8,
+    4, 5, 6, 7
+};
+
+const igraph_real_t igraph_i_famous_tetrahedron[] = {
+    4, 6, 0,
+    0, 3, 1, 3, 2, 3, 0, 1, 1, 2, 0, 2
+};
+
+const igraph_real_t igraph_i_famous_thomassen[] = {
+    34, 52, 0,
+    0, 2, 0, 3, 1, 3, 1, 4, 2, 4, 5, 7, 5, 8, 6, 8, 6, 9, 7, 9, 10, 12, 10, 13, 11,
+    13, 11, 14, 12, 14, 15, 17, 15, 18, 16, 18, 16, 19, 17, 19, 9, 19, 4, 14, 24,
+    25, 25, 26, 20, 26, 20, 21, 21, 22, 22, 23, 23, 27, 27, 28, 28, 29, 29, 30, 30,
+    31, 31, 32, 32, 33, 24, 33, 5, 24, 6, 25, 7, 26, 8, 20, 0, 20, 1, 21, 2, 22, 3,
+    23, 10, 27, 11, 28, 12, 29, 13, 30, 15, 30, 16, 31, 17, 32, 18, 33
+};
+
+const igraph_real_t igraph_i_famous_tutte[] = {
+    46, 69, 0,
+    0, 10, 0, 11, 0, 12, 1, 2, 1, 7, 1, 19, 2, 3, 2, 41, 3, 4, 3, 27, 4, 5, 4, 33,
+    5, 6, 5, 45, 6, 9, 6, 29, 7, 8, 7, 21, 8, 9, 8, 22, 9, 24, 10, 13, 10, 14, 11,
+    26, 11, 28, 12, 30, 12, 31, 13, 15, 13, 21, 14, 15, 14, 18, 15, 16, 16, 17, 16,
+    20, 17, 18, 17, 23, 18, 24, 19, 25, 19, 40, 20, 21, 20, 22, 22, 23, 23, 24, 25,
+    26, 25, 38, 26, 34, 27, 28, 27, 39, 28, 34, 29, 30, 29, 44, 30, 35, 31, 32, 31,
+    35, 32, 33, 32, 42, 33, 43, 34, 36, 35, 37, 36, 38, 36, 39, 37, 42, 37, 44, 38,
+    40, 39, 41, 40, 41, 42, 43, 43, 45, 44, 45
+};
+
+const igraph_real_t igraph_i_famous_uniquely3colorable[] = {
+    12, 22, 0,
+    0, 1, 0, 3, 0, 6, 0, 8, 1, 4, 1, 7, 1, 9, 2, 3, 2, 6, 2, 7, 2, 9, 2, 11, 3, 4,
+    3, 10, 4, 5, 4, 11, 5, 6, 5, 7, 5, 8, 5, 10, 8, 11, 9, 10
+};
+
+const igraph_real_t igraph_i_famous_walther[] = {
+    25, 31, 0,
+    0, 1, 1, 2, 1, 8, 2, 3, 2, 13, 3, 4, 3, 16, 4, 5, 5, 6, 5, 19, 6, 7, 6, 20, 7,
+    21, 8, 9, 8, 13, 9, 10, 9, 22, 10, 11, 10, 20, 11, 12, 13, 14, 14, 15, 14, 23,
+    15, 16, 15, 17, 17, 18, 18, 19, 18, 24, 20, 24, 22, 23, 23, 24
+};
+
+const igraph_real_t igraph_i_famous_zachary[] = {
+    34, 78, 0,
+    0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8,
+    0, 10, 0, 11, 0, 12, 0, 13, 0, 17, 0, 19, 0, 21, 0, 31,
+    1, 2, 1, 3, 1, 7, 1, 13, 1, 17, 1, 19, 1, 21, 1, 30,
+    2, 3, 2, 7, 2, 27, 2, 28, 2, 32, 2, 9, 2, 8, 2, 13,
+    3, 7, 3, 12, 3, 13, 4, 6, 4, 10, 5, 6, 5, 10, 5, 16,
+    6, 16, 8, 30, 8, 32, 8, 33, 9, 33, 13, 33, 14, 32, 14, 33,
+    15, 32, 15, 33, 18, 32, 18, 33, 19, 33, 20, 32, 20, 33,
+    22, 32, 22, 33, 23, 25, 23, 27, 23, 32, 23, 33, 23, 29,
+    24, 25, 24, 27, 24, 31, 25, 31, 26, 29, 26, 33, 27, 33,
+    28, 31, 28, 33, 29, 32, 29, 33, 30, 32, 30, 33, 31, 32, 31, 33,
+    32, 33
+};
+
+int igraph_i_famous(igraph_t *graph, const igraph_real_t *data);
+
+int igraph_i_famous(igraph_t *graph, const igraph_real_t *data) {
+    long int no_of_nodes = (long int) data[0];
+    long int no_of_edges = (long int) data[1];
+    igraph_bool_t directed = (igraph_bool_t) data[2];
+    igraph_vector_t edges;
+
+    igraph_vector_view(&edges, data + 3, 2 * no_of_edges);
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes,
+                               directed));
+    return 0;
+}
+
+/**
+ * \function igraph_famous
+ * \brief Create a famous graph by simply providing its name
+ *
+ * </para><para>
+ * The name of the graph can be simply supplied as a string.
+ * Note that this function creates graphs which don't take any parameters,
+ * there are separate functions for graphs with parameters, eg. \ref
+ * igraph_full() for creating a full graph.
+ *
+ * </para><para>
+ * The following graphs are supported:
+ * \clist
+ *   \cli Bull
+ *           The bull graph, 5 vertices, 5 edges, resembles the
+ *           head of a bull if drawn properly.
+ *   \cli Chvatal
+ *           This is the smallest triangle-free graph that is
+ *           both 4-chromatic and 4-regular. According to the Grunbaum
+ *           conjecture there exists an m-regular, m-chromatic graph
+ *           with n vertices for every m>1 and n>2. The Chvatal graph
+ *           is an example for m=4 and n=12. It has 24 edges.
+ *   \cli Coxeter
+ *           A non-Hamiltonian cubic symmetric graph with 28
+ *           vertices and 42 edges.
+ *   \cli Cubical
+ *           The Platonic graph of the cube. A convex regular
+ *           polyhedron with 8 vertices and 12 edges.
+ *   \cli Diamond
+ *           A graph with 4 vertices and 5 edges, resembles a
+ *           schematic diamond if drawn properly.
+ *   \cli Dodecahedral, Dodecahedron
+ *           Another Platonic solid
+ *           with 20 vertices and 30 edges.
+ *   \cli Folkman
+ *           The semisymmetric graph with minimum number of
+ *           vertices, 20 and 40 edges. A semisymmetric graph is
+ *           regular, edge transitive and not vertex transitive.
+ *   \cli Franklin
+ *           This is a graph whose embedding to the Klein
+ *           bottle can be colored with six colors, it is a
+ *           counterexample to the necessity of the Heawood
+ *           conjecture on a Klein bottle. It has 12 vertices and 18
+ *           edges.
+ *   \cli Frucht
+ *           The Frucht Graph is the smallest cubical graph
+ *           whose automorphism group consists only of the identity
+ *           element. It has 12 vertices and 18 edges.
+ *   \cli Grotzsch
+ *           The Grötzsch graph is a triangle-free graph with
+ *           11 vertices, 20 edges, and chromatic number 4. It is named after
+ *           German mathematician Herbert Grötzsch, and its existence
+ *           demonstrates that the assumption of planarity is necessary in
+ *           Grötzsch's theorem that every triangle-free planar
+ *           graph is 3-colorable.
+ *   \cli Heawood
+ *           The Heawood graph is an undirected graph with 14
+ *           vertices and 21 edges. The graph is cubic, and all cycles in the
+ *           graph have six or more edges. Every smaller cubic graph has shorter
+ *           cycles, so this graph is the 6-cage, the smallest cubic graph of
+ *           girth 6.
+ *   \cli Herschel
+ *           The Herschel graph is the smallest
+ *           nonhamiltonian polyhedral graph. It is the
+ *           unique such graph on 11 nodes, and has 18 edges.
+ *   \cli House
+ *           The house graph is a 5-vertex, 6-edge graph, the
+ *           schematic draw of a house if drawn properly, basically a
+ *           triangle on top of a square.
+ *   \cli HouseX
+ *           The same as the house graph with an X in the square. 5
+ *           vertices and 8 edges.
+ *   \cli Icosahedral, Icosahedron
+ *           A Platonic solid with 12
+ *           vertices and 30 edges.
+ *   \cli Krackhardt_Kite
+ *           A social network with 10 vertices and 18 edges.
+ *           Krackhardt, D. Assessing the Political Landscape:
+ *           Structure, Cognition, and Power in Organizations.
+ *           Admin. Sci. Quart. 35, 342-369, 1990.
+ *   \cli Levi
+ *           The graph is a 4-arc transitive cubic graph, it has
+ *           30 vertices and 45 edges.
+ *   \cli McGee
+ *           The McGee graph is the unique 3-regular 7-cage
+ *           graph, it has 24 vertices and 36 edges.
+ *   \cli Meredith
+ *           The Meredith graph is a quartic graph on 70
+ *           nodes and 140 edges that is a counterexample to the conjecture that
+ *           every 4-regular 4-connected graph is Hamiltonian.
+ *   \cli Noperfectmatching
+ *           A connected graph with 16 vertices and
+ *           27 edges containing no perfect matching. A matching in a graph
+ *           is a set of pairwise non-incident edges; that is, no two edges
+ *           share a common vertex. A perfect matching is a matching
+ *           which covers all vertices of the graph.
+ *   \cli Nonline
+ *           A graph whose connected components are the 9
+ *           graphs whose presence as a vertex-induced subgraph in a
+ *           graph makes a nonline graph. It has 50 vertices and 72 edges.
+ *   \cli Octahedral, Octahedron
+ *           Platonic solid with 6
+ *           vertices and 12 edges.
+ *   \cli Petersen
+ *           A 3-regular graph with 10 vertices and 15 edges. It is
+ *           the smallest hypohamiltonian graph, ie. it is
+ *           non-hamiltonian but removing any single vertex from it makes it
+ *           Hamiltonian.
+ *   \cli Robertson
+ *           The unique (4,5)-cage graph, ie. a 4-regular
+ *           graph of girth 5. It has 19 vertices and 38 edges.
+ *   \cli Smallestcyclicgroup
+ *           A smallest nontrivial graph
+ *           whose automorphism group is cyclic. It has 9 vertices and
+ *           15 edges.
+ *   \cli Tetrahedral, Tetrahedron
+ *           Platonic solid with 4
+ *           vertices and 6 edges.
+ *   \cli Thomassen
+ *           The smallest hypotraceable graph,
+ *           on 34 vertices and 52 edges. A hypotracable graph does
+ *           not contain a Hamiltonian path but after removing any
+ *           single vertex from it the remainder always contains a
+ *           Hamiltonian path. A graph containing a Hamiltonian path
+ *           is called traceable.
+ *   \cli Tutte
+ *           Tait's Hamiltonian graph conjecture states that
+ *           every 3-connected 3-regular planar graph is Hamiltonian.
+ *           This graph is a counterexample. It has 46 vertices and 69
+ *           edges.
+ *   \cli Uniquely3colorable
+ *           Returns a 12-vertex, triangle-free
+ *           graph with chromatic number 3 that is uniquely
+ *           3-colorable.
+ *   \cli Walther
+ *           An identity graph with 25 vertices and 31
+ *           edges. An identity graph has a single graph automorphism,
+ *           the trivial one.
+ *   \cli Zachary
+ *           Social network of friendships between 34 members of a
+ *           karate club at a US university in the 1970s. See
+ *           W. W. Zachary, An information flow model for conflict and
+ *           fission in small groups, Journal of Anthropological
+ *           Research 33, 452-473 (1977).
+ * \endclist
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param name Character constant, the name of the graph to be
+ *     created, it is case insensitive.
+ * \return Error code, IGRAPH_EINVAL if there is no graph with the
+ *     given name.
+ *
+ * \sa Other functions for creating graph structures:
+ * \ref igraph_ring(), \ref igraph_tree(), \ref igraph_lattice(), \ref
+ * igraph_full().
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges in the graph.
+ */
+
+int igraph_famous(igraph_t *graph, const char *name) {
+
+    if (!strcasecmp(name, "bull")) {
+        return igraph_i_famous(graph, igraph_i_famous_bull);
+    } else if (!strcasecmp(name, "chvatal")) {
+        return igraph_i_famous(graph, igraph_i_famous_chvatal);
+    } else if (!strcasecmp(name, "coxeter")) {
+        return igraph_i_famous(graph, igraph_i_famous_coxeter);
+    } else if (!strcasecmp(name, "cubical")) {
+        return igraph_i_famous(graph, igraph_i_famous_cubical);
+    } else if (!strcasecmp(name, "diamond")) {
+        return igraph_i_famous(graph, igraph_i_famous_diamond);
+    } else if (!strcasecmp(name, "dodecahedral") ||
+               !strcasecmp(name, "dodecahedron")) {
+        return igraph_i_famous(graph, igraph_i_famous_dodecahedron);
+    } else if (!strcasecmp(name, "folkman")) {
+        return igraph_i_famous(graph, igraph_i_famous_folkman);
+    } else if (!strcasecmp(name, "franklin")) {
+        return igraph_i_famous(graph, igraph_i_famous_franklin);
+    } else if (!strcasecmp(name, "frucht")) {
+        return igraph_i_famous(graph, igraph_i_famous_frucht);
+    } else if (!strcasecmp(name, "grotzsch")) {
+        return igraph_i_famous(graph, igraph_i_famous_grotzsch);
+    } else if (!strcasecmp(name, "heawood")) {
+        return igraph_i_famous(graph, igraph_i_famous_heawood);
+    } else if (!strcasecmp(name, "herschel")) {
+        return igraph_i_famous(graph, igraph_i_famous_herschel);
+    } else if (!strcasecmp(name, "house")) {
+        return igraph_i_famous(graph, igraph_i_famous_house);
+    } else if (!strcasecmp(name, "housex")) {
+        return igraph_i_famous(graph, igraph_i_famous_housex);
+    } else if (!strcasecmp(name, "icosahedral") ||
+               !strcasecmp(name, "icosahedron")) {
+        return igraph_i_famous(graph, igraph_i_famous_icosahedron);
+    } else if (!strcasecmp(name, "krackhardt_kite")) {
+        return igraph_i_famous(graph, igraph_i_famous_krackhardt_kite);
+    } else if (!strcasecmp(name, "levi")) {
+        return igraph_i_famous(graph, igraph_i_famous_levi);
+    } else if (!strcasecmp(name, "mcgee")) {
+        return igraph_i_famous(graph, igraph_i_famous_mcgee);
+    } else if (!strcasecmp(name, "meredith")) {
+        return igraph_i_famous(graph, igraph_i_famous_meredith);
+    } else if (!strcasecmp(name, "noperfectmatching")) {
+        return igraph_i_famous(graph, igraph_i_famous_noperfectmatching);
+    } else if (!strcasecmp(name, "nonline")) {
+        return igraph_i_famous(graph, igraph_i_famous_nonline);
+    } else if (!strcasecmp(name, "octahedral") ||
+               !strcasecmp(name, "octahedron")) {
+        return igraph_i_famous(graph, igraph_i_famous_octahedron);
+    } else if (!strcasecmp(name, "petersen")) {
+        return igraph_i_famous(graph, igraph_i_famous_petersen);
+    } else if (!strcasecmp(name, "robertson")) {
+        return igraph_i_famous(graph, igraph_i_famous_robertson);
+    } else if (!strcasecmp(name, "smallestcyclicgroup")) {
+        return igraph_i_famous(graph, igraph_i_famous_smallestcyclicgroup);
+    } else if (!strcasecmp(name, "tetrahedral") ||
+               !strcasecmp(name, "tetrahedron")) {
+        return igraph_i_famous(graph, igraph_i_famous_tetrahedron);
+    } else if (!strcasecmp(name, "thomassen")) {
+        return igraph_i_famous(graph, igraph_i_famous_thomassen);
+    } else if (!strcasecmp(name, "tutte")) {
+        return igraph_i_famous(graph, igraph_i_famous_tutte);
+    } else if (!strcasecmp(name, "uniquely3colorable")) {
+        return igraph_i_famous(graph, igraph_i_famous_uniquely3colorable);
+    } else if (!strcasecmp(name, "walther")) {
+        return igraph_i_famous(graph, igraph_i_famous_walther);
+    } else if (!strcasecmp(name, "zachary")) {
+        return igraph_i_famous(graph, igraph_i_famous_zachary);
+    } else {
+        IGRAPH_ERROR("Unknown graph, see documentation", IGRAPH_EINVAL);
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_adjlist
+ * Create a graph from an adjacency list
+ *
+ * An adjacency list is a list of vectors, containing the neighbors
+ * of all vertices. For operations that involve many changes to the
+ * graph structure, it is recommended that you convert the graph into
+ * an adjacency list via \ref igraph_adjlist_init(), perform the
+ * modifications (these are cheap for an adjacency list) and then
+ * recreate the igraph graph via this function.
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param adjlist The adjacency list.
+ * \param mode Whether or not to create a directed graph. \c IGRAPH_ALL
+ *             means an undirected graph, \c IGRAPH_OUT means a
+ *             directed graph from an out-adjacency list (i.e. each
+ *             list contains the successors of the corresponding
+ *             vertices), \c IGRAPH_IN means a directed graph from an
+ *             in-adjacency list
+ * \param duplicate Logical, for undirected graphs this specified
+ *        whether each edge is included twice, in the vectors of
+ *        both adjacent vertices. If this is false (0), then it is
+ *        assumed that every edge is included only once. This argument
+ *        is ignored for directed graphs.
+ * \return Error code.
+ *
+ * \sa \ref igraph_adjlist_init() for the opposite operation.
+ *
+ * Time complexity: O(|V|+|E|).
+ *
+ */
+
+int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist,
+                   igraph_neimode_t mode, igraph_bool_t duplicate) {
+
+    long int no_of_nodes = igraph_adjlist_size(adjlist);
+    long int no_of_edges = 0;
+    long int i;
+
+    igraph_vector_t edges;
+    long int edgeptr = 0;
+
+    duplicate = duplicate && (mode == IGRAPH_ALL); /* only duplicate if undirected */
+
+    for (i = 0; i < no_of_nodes; i++) {
+        no_of_edges += igraph_vector_int_size(igraph_adjlist_get(adjlist, i));
+    }
+
+    if (duplicate) {
+        no_of_edges /= 2;
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges);
+
+    for (i = 0; i < no_of_nodes; i++) {
+        igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, i);
+        long int j, n = igraph_vector_int_size(neis);
+        long int loops = 0;
+
+        for (j = 0; j < n; j++) {
+            long int nei = (long int) VECTOR(*neis)[j];
+            if (nei == i) {
+                loops++;
+            } else {
+                if (! duplicate || nei > i) {
+                    if (edgeptr + 2 > 2 * no_of_edges) {
+                        IGRAPH_ERROR("Invalid adjacency list, most probably not correctly"
+                                     " duplicated edges for an undirected graph", IGRAPH_EINVAL);
+                    }
+                    if (mode == IGRAPH_IN) {
+                        VECTOR(edges)[edgeptr++] = nei;
+                        VECTOR(edges)[edgeptr++] = i;
+                    } else {
+                        VECTOR(edges)[edgeptr++] = i;
+                        VECTOR(edges)[edgeptr++] = nei;
+                    }
+                }
+            }
+        }
+        /* loops */
+        if (duplicate) {
+            loops = loops / 2;
+        }
+        if (edgeptr + 2 * loops > 2 * no_of_edges) {
+            IGRAPH_ERROR("Invalid adjacency list, most probably not correctly"
+                         " duplicated edges for an undirected graph", IGRAPH_EINVAL);
+        }
+        for (j = 0; j < loops; j++) {
+            VECTOR(edges)[edgeptr++] = i;
+            VECTOR(edges)[edgeptr++] = i;
+        }
+    }
+
+    if (mode == IGRAPH_ALL)
+        IGRAPH_CHECK(igraph_create(graph, &edges,
+                                   (igraph_integer_t) no_of_nodes, 0));
+    else
+        IGRAPH_CHECK(igraph_create(graph, &edges,
+                                   (igraph_integer_t) no_of_nodes, 1));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+
+/**
+ * \ingroup generators
+ * \function igraph_from_prufer
+ * \brief Generates a tree from a Pr&uuml;fer sequence
+ *
+ * A Pr&uuml;fer sequence is a unique sequence of integers associated
+ * with a labelled tree. A tree on n vertices can be represented by a
+ * sequence of n-2 integers, each between 0 and n-1 (inclusive).
+ *
+ * The algorithm used by this function is based on
+ * Paulius Micikevi&ccaron;ius, Saverio Caminiti, Narsingh Deo:
+ * Linear-time Algorithms for Encoding Trees as Sequences of Node Labels
+ *
+ * \param graph Pointer to an uninitialized graph object.
+ * \param prufer The Pr&uuml;fer sequence
+ * \return Error code:
+ *          \clist
+ *          \cli IGRAPH_ENOMEM
+ *             there is not enough memory to perform the operation.
+ *          \cli IGRAPH_EINVAL
+ *             invalid Pr&uuml;fer sequence given
+ *          \endclist
+ *
+ * \sa \ref igraph_tree(), \ref igraph_tree_game()
+ *
+ */
+
+int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer) {
+    igraph_vector_int_t degree;
+    igraph_vector_t edges;
+    long n;
+    long i, k;
+    long u, v; /* vertices */
+    long ec;
+
+    n = igraph_vector_int_size(prufer) + 2;
+
+    IGRAPH_VECTOR_INT_INIT_FINALLY(&degree, n); /* initializes vector to zeros */
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (n - 1));
+
+    /* build out-degree vector (i.e. number of child vertices) and verify Prufer sequence */
+    for (i = 0; i < n - 2; ++i) {
+        long u = VECTOR(*prufer)[i];
+        if (u >= n || u < 0) {
+            IGRAPH_ERROR("Invalid Prufer sequence", IGRAPH_EINVAL);
+        }
+        VECTOR(degree)[u] += 1;
+    }
+
+    v = 0;  /* initialize v now, in case Prufer sequence is empty */
+    k = 0;  /* index into the Prufer vector */
+    ec = 0; /* index into the edges vector */
+    for (i = 0; i < n; ++i) {
+        u = i;
+
+        while (k < n - 2 && u <= i && (VECTOR(degree)[u] == 0)) {
+            /* u is a leaf here */
+
+            v = VECTOR(*prufer)[k]; /* parent of u */
+
+            /* add edge */
+            VECTOR(edges)[ec++] = v;
+            VECTOR(edges)[ec++] = u;
+
+            k += 1;
+
+            VECTOR(degree)[v] -= 1;
+
+            u = v;
+        }
+
+        if (k == n - 2) {
+            break;
+        }
+    }
+
+    /* find u for last edge, v is already set */
+    for (u = i + 1; u < n; ++u)
+        if ((VECTOR(degree)[u] == 0) && u != v) {
+            break;
+        }
+
+    /* add last edge */
+    VECTOR(edges)[ec++] = v;
+    VECTOR(edges)[ec++] = u;
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) n, /* directed = */ 0));
+
+    igraph_vector_destroy(&edges);
+    igraph_vector_int_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/sue.c b/igraph/src/sue.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/sue.c
@@ -0,0 +1,90 @@
+#include "f2c.h"
+#include "fio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern uiolen f__reclen;
+OFF_T f__recloc;
+
+ int
+#ifdef KR_headers
+c_sue(a) cilist *a;
+#else
+c_sue(cilist *a)
+#endif
+{
+	f__external=f__sequential=1;
+	f__formatted=0;
+	f__curunit = &f__units[a->ciunit];
+	if(a->ciunit >= MXUNIT || a->ciunit < 0)
+		err(a->cierr,101,"startio");
+	f__elist=a;
+	if(f__curunit->ufd==NULL && fk_open(SEQ,UNF,a->ciunit))
+		err(a->cierr,114,"sue");
+	f__cf=f__curunit->ufd;
+	if(f__curunit->ufmt) err(a->cierr,103,"sue")
+	if(!f__curunit->useek) err(a->cierr,103,"sue")
+	return(0);
+}
+#ifdef KR_headers
+integer s_rsue(a) cilist *a;
+#else
+integer s_rsue(cilist *a)
+#endif
+{
+	int n;
+	if(!f__init) f_init();
+	f__reading=1;
+	if(n=c_sue(a)) return(n);
+	f__recpos=0;
+	if(f__curunit->uwrt && f__nowreading(f__curunit))
+		err(a->cierr, errno, "read start");
+	if(fread((char *)&f__reclen,sizeof(uiolen),1,f__cf)
+		!= 1)
+	{	if(feof(f__cf))
+		{	f__curunit->uend = 1;
+			err(a->ciend, EOF, "start");
+		}
+		clearerr(f__cf);
+		err(a->cierr, errno, "start");
+	}
+	return(0);
+}
+#ifdef KR_headers
+integer s_wsue(a) cilist *a;
+#else
+integer s_wsue(cilist *a)
+#endif
+{
+	int n;
+	if(!f__init) f_init();
+	if(n=c_sue(a)) return(n);
+	f__reading=0;
+	f__reclen=0;
+	if(f__curunit->uwrt != 1 && f__nowwriting(f__curunit))
+		err(a->cierr, errno, "write start");
+	f__recloc=FTELL(f__cf);
+	FSEEK(f__cf,(OFF_T)sizeof(uiolen),SEEK_CUR);
+	return(0);
+}
+integer e_wsue(Void)
+{	OFF_T loc;
+	fwrite((char *)&f__reclen,sizeof(uiolen),1,f__cf);
+#ifdef ALWAYS_FLUSH
+	if (fflush(f__cf))
+		err(f__elist->cierr, errno, "write end");
+#endif
+	loc=FTELL(f__cf);
+	FSEEK(f__cf,f__recloc,SEEK_SET);
+	fwrite((char *)&f__reclen,sizeof(uiolen),1,f__cf);
+	FSEEK(f__cf,loc,SEEK_SET);
+	return(0);
+}
+integer e_rsue(Void)
+{
+	FSEEK(f__cf,(OFF_T)(f__reclen-f__recpos+sizeof(uiolen)),SEEK_CUR);
+	return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/sugiyama.c b/igraph/src/sugiyama.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/sugiyama.c
@@ -0,0 +1,1340 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "config.h"
+#include "igraph_centrality.h"
+#include "igraph_components.h"
+#include "igraph_constants.h"
+#include "igraph_constructors.h"
+#include "igraph_datatype.h"
+#include "igraph_error.h"
+#include "igraph_glpk_support.h"
+#include "igraph_interface.h"
+#include "igraph_memory.h"
+#include "igraph_structural.h"
+#include "igraph_types.h"
+
+#include <limits.h>
+
+/* #define SUGIYAMA_DEBUG */
+
+#ifdef _MSC_VER
+/* MSVC does not support variadic macros */
+#include <stdarg.h>
+static void debug(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+#ifdef SUGIYAMA_DEBUG
+    vfprintf(stderr, fmt, args);
+#endif
+    va_end(args);
+}
+#else
+#ifdef SUGIYAMA_DEBUG
+    #define debug(...) fprintf(stderr, __VA_ARGS__)
+#else
+    #define debug(...)
+#endif
+#endif
+
+/* MSVC uses __forceinline instead of inline */
+#ifdef _MSC_VER
+    #define INLINE __forceinline
+#else
+    #define INLINE inline
+#endif
+
+/*
+ * Implementation of the Sugiyama layout algorithm as described in:
+ *
+ * [1] K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual Understanding of
+ * Hierarchical Systems". IEEE Transactions on Systems, Man and Cybernetics
+ * 11(2):109-125, 1981.
+ *
+ * The layering (if not given in advance) is calculated by ... TODO
+ *
+ * [2] TODO
+ *
+ * The X coordinates of nodes within a layer are calculated using the method of
+ * Brandes & Köpf:
+ *
+ * [3] U. Brandes and B. Köpf, "Fast and Simple Horizontal Coordinate
+ * Assignment".  In: Lecture Notes in Computer Science 2265:31-44, 2002.
+ *
+ * Layer compaction is done according to:
+ *
+ * [4] N.S. Nikolov and A. Tarassov, "Graph layering by promotion of nodes".
+ * Journal of Discrete Applied Mathematics, special issue: IV ALIO/EURO
+ * workshop on applied combinatorial optimization, 154(5).
+ *
+ * The steps of the algorithm are as follows:
+ *
+ *   1. Cycle removal by finding an approximately minimal feedback arc set
+ *      and reversing the direction of edges in the set.  Algorithms for
+ *      finding minimal feedback arc sets are as follows:
+ *
+ *        - Find a cycle and find its minimum weight edge. Decrease the weight
+ *          of all the edges by w. Remove those edges whose weight became zero.
+ *          Repeat until there are no cycles. Re-introduce removed edges in
+ *          decreasing order of weights, ensuring that no cycles are created.
+ *
+ *        - Order the vertices somehow and remove edges which point backwards
+ *          in the ordering. Eades et al proposed the following procedure:
+ *
+ *            1. Iteratively remove sinks and prepend them to a vertex sequence
+ *               s2.
+ *
+ *            2. Iteratively remove sources and append them to a vertex sequence
+ *               s1.
+ *
+ *            3. Choose a vertex u s.t. the difference between the number of
+ *               rightward arcs and the number of leftward arcs is the largest,
+ *               remove u and append it to s1. Goto step 1 if there are still
+ *               more vertices.
+ *
+ *            4. Concatenate s1 with s2.
+ *
+ *          This algorithm is known to produce feedback arc sets at most the
+ *          size of m/2 - n/6, where m is the number of edges. Further
+ *          improvements are possible in step 3 which bring down the size of
+ *          the set to at most m/4 for cubic directed graphs, see Eades (1995).
+ *
+ *        - For undirected graphs, find a maximum weight spanning tree and
+ *          remove all the edges not in the spanning tree. For directed graphs,
+ *          find minimal cuts iteratively and remove edges pointing from A to
+ *          B or from B to A in the cut, depending on which one is smaller. Yes,
+ *          this is time-consuming.
+ *
+ *   2. Assigning vertices to layers according to [2].
+ *
+ *   3. Extracting weakly connected components. The remaining steps are
+ *      executed for each component.
+ *
+ *   4. Compacting the layering using the method of [4]. TODO
+ *      Steps 2-4 are performed only when no layering is given in advance.
+ *
+ *   5. Adding dummy nodes to ensure that each edge spans at most one layer
+ *      only.
+ *
+ *   6. Finding an optimal ordering of vertices within a layer using the
+ *      Sugiyama framework [1].
+ *
+ *   7. Assigning horizontal coordinates to each vertex using [3].
+ *
+ *   8. ???
+ *
+ *   9. Profit!
+ */
+
+/**
+ * Data structure to store a layering of the graph.
+ */
+typedef struct {
+    igraph_vector_ptr_t layers;
+} igraph_i_layering_t;
+
+/**
+ * Initializes a layering.
+ */
+int igraph_i_layering_init(igraph_i_layering_t* layering,
+                           const igraph_vector_t* membership) {
+    long int i, n, num_layers;
+
+    if (igraph_vector_size(membership) == 0) {
+        num_layers = 0;
+    } else {
+        num_layers = (long int) igraph_vector_max(membership) + 1;
+    }
+
+    IGRAPH_CHECK(igraph_vector_ptr_init(&layering->layers, num_layers));
+    IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &layering->layers);
+
+    for (i = 0; i < num_layers; i++) {
+        igraph_vector_t* vec = igraph_Calloc(1, igraph_vector_t);
+        IGRAPH_VECTOR_INIT_FINALLY(vec, 0);
+        VECTOR(layering->layers)[i] = vec;
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&layering->layers, igraph_vector_destroy);
+
+    n = igraph_vector_size(membership);
+    for (i = 0; i < n; i++) {
+        long int l = (long int) VECTOR(*membership)[i];
+        igraph_vector_t* vec = VECTOR(layering->layers)[l];
+        IGRAPH_CHECK(igraph_vector_push_back(vec, i));
+    }
+
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * Destroys a layering.
+ */
+void igraph_i_layering_destroy(igraph_i_layering_t* layering) {
+    igraph_vector_ptr_destroy_all(&layering->layers);
+}
+
+/**
+ * Returns the number of layers in a layering.
+ */
+int igraph_i_layering_num_layers(const igraph_i_layering_t* layering) {
+    return (int) igraph_vector_ptr_size(&layering->layers);
+}
+
+/**
+ * Returns the list of vertices in a given layer
+ */
+igraph_vector_t* igraph_i_layering_get(const igraph_i_layering_t* layering,
+                                       long int index) {
+    return (igraph_vector_t*)VECTOR(layering->layers)[index];
+}
+
+
+/**
+ * Forward declarations
+ */
+
+static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph,
+        const igraph_vector_t* weights, igraph_vector_t* membership);
+static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph,
+        igraph_matrix_t* layout, const igraph_i_layering_t* layering,
+        long int maxiter);
+static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* graph,
+        igraph_matrix_t* layout, const igraph_i_layering_t* layering,
+        igraph_real_t hgap, igraph_integer_t no_of_real_nodes);
+
+/**
+ * Calculated the median of four numbers (not necessarily sorted).
+ */
+static INLINE igraph_real_t igraph_i_median_4(igraph_real_t x1,
+        igraph_real_t x2, igraph_real_t x3, igraph_real_t x4) {
+    igraph_real_t arr[4] = { x1, x2, x3, x4 };
+    igraph_vector_t vec;
+    igraph_vector_view(&vec, arr, 4);
+    igraph_vector_sort(&vec);
+    return (arr[1] + arr[2]) / 2.0;
+}
+
+
+/**
+ * \ingroup layout
+ * \function igraph_layout_sugiyama
+ * \brief Sugiyama layout algorithm for layered directed acyclic graphs.
+ *
+ * </para><para>
+ * This layout algorithm is designed for directed acyclic graphs where each
+ * vertex is assigned to a layer. Layers are indexed from zero, and vertices
+ * of the same layer will be placed on the same horizontal line. The X coordinates
+ * of vertices within each layer are decided by the heuristic proposed by
+ * Sugiyama et al to minimize edge crossings.
+ *
+ * </para><para>
+ * You can also try to lay out undirected graphs, graphs containing cycles, or
+ * graphs without an a priori layered assignment with this algorithm. igraph
+ * will try to eliminate cycles and assign vertices to layers, but there is no
+ * guarantee on the quality of the layout in such cases.
+ *
+ * </para><para>
+ * The Sugiyama layout may introduce "bends" on the edges in order to obtain a
+ * visually more pleasing layout. This is achieved by adding dummy nodes to
+ * edges spanning more than one layer. The resulting layout assigns coordinates
+ * not only to the nodes of the original graph but also to the dummy nodes.
+ * The layout algorithm will also return the extended graph with the dummy nodes.
+ * An edge in the original graph may either be mapped to a single edge in the
+ * extended graph or a \em path that starts and ends in the original
+ * source and target vertex and passes through multiple dummy vertices. In
+ * such cases, the user may also request the mapping of the edges of the extended
+ * graph back to the edges of the original graph.
+ *
+ * </para><para>
+ * For more details, see K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual
+ * Understanding of Hierarchical Systems". IEEE Transactions on Systems, Man and
+ * Cybernetics 11(2):109-125, 1981.
+ *
+ * \param graph Pointer to an initialized graph object.
+ * \param res   Pointer to an initialized matrix object. This will contain
+ *              the result and will be resized as needed. The first |V| rows
+ *              of the layout will contain the coordinates of the original graph,
+ *              the remaining rows contain the positions of the dummy nodes.
+ *              Therefore, you can use the result both with \p graph or with
+ *              \p extended_graph.
+ * \param extended_graph Pointer to an uninitialized graph object or \c NULL.
+ *                       The extended graph with the added dummy nodes will be
+ *                       returned here. In this graph, each edge points downwards
+ *                       to lower layers, spans exactly one layer and the first
+ *                       |V| vertices coincide with the vertices of the
+ *                       original graph.
+ * \param extd_to_orig_eids Pointer to a vector or \c NULL. If not \c NULL, the
+ *                          mapping from the edge IDs of the extended graph back
+ *                          to the edge IDs of the original graph will be stored
+ *                          here.
+ * \param layers  The layer index for each vertex or \c NULL if the layers should
+ *                be determined automatically by igraph.
+ * \param hgap  The preferred minimum horizontal gap between vertices in the same
+ *              layer.
+ * \param vgap  The distance between layers.
+ * \param maxiter Maximum number of iterations in the crossing minimization stage.
+ *                100 is a reasonable default; if you feel that you have too
+ *                many edge crossings, increase this.
+ * \param weights Weights of the edges. These are used only if the graph contains
+ *                cycles; igraph will tend to reverse edges with smaller
+ *                weights when breaking the cycles.
+ */
+int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res,
+                           igraph_t *extd_graph, igraph_vector_t *extd_to_orig_eids,
+                           const igraph_vector_t* layers, igraph_real_t hgap, igraph_real_t vgap,
+                           long int maxiter, const igraph_vector_t *weights) {
+    long int i, j, k, l, m, nei;
+    long int no_of_nodes = (long int)igraph_vcount(graph);
+    long int comp_idx;
+    long int next_extd_vertex_id = no_of_nodes;
+    igraph_bool_t directed = igraph_is_directed(graph);
+    igraph_integer_t no_of_components;  /* number of components of the original graph */
+    igraph_vector_t membership;         /* components of the original graph */
+    igraph_vector_t extd_edgelist;   /* edge list of the extended graph */
+    igraph_vector_t layers_own;  /* layer indices after having eliminated empty layers */
+    igraph_real_t dx = 0, dx2 = 0; /* displacement of the current component on the X axis */
+    igraph_vector_t layer_to_y; /* mapping from layer indices to final Y coordinates */
+
+    if (layers && igraph_vector_size(layers) != no_of_nodes) {
+        IGRAPH_ERROR("layer vector too short or too long", IGRAPH_EINVAL);
+    }
+
+    if (extd_graph != 0) {
+        IGRAPH_VECTOR_INIT_FINALLY(&extd_edgelist, 0);
+        if (extd_to_orig_eids != 0) {
+            igraph_vector_clear(extd_to_orig_eids);
+        }
+    }
+
+    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2));
+    IGRAPH_VECTOR_INIT_FINALLY(&membership, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&layer_to_y, 0);
+
+    /* 1. Find a feedback arc set if we don't have a layering yet. If we do have
+     *    a layering, we can leave all the edges as is as they will be re-oriented
+     *    to point downwards only anyway. */
+    if (layers == 0) {
+        IGRAPH_VECTOR_INIT_FINALLY(&layers_own, no_of_nodes);
+        IGRAPH_CHECK(igraph_i_layout_sugiyama_place_nodes_vertically(
+                         graph, weights, &layers_own));
+    } else {
+        IGRAPH_CHECK(igraph_vector_copy(&layers_own, layers));
+        IGRAPH_FINALLY(igraph_vector_destroy, &layers_own);
+    }
+
+    /* Normalize layering, eliminate empty layers */
+    if (no_of_nodes > 0) {
+        igraph_vector_t inds;
+        IGRAPH_VECTOR_INIT_FINALLY(&inds, 0);
+        IGRAPH_CHECK((int) igraph_vector_qsort_ind(&layers_own, &inds, 0));
+        j = -1; dx = VECTOR(layers_own)[(long int)VECTOR(inds)[0]] - 1;
+        for (i = 0; i < no_of_nodes; i++) {
+            k = (long int)VECTOR(inds)[i];
+            if (VECTOR(layers_own)[k] > dx) {
+                /* New layer starts here */
+                dx = VECTOR(layers_own)[k];
+                j++;
+                IGRAPH_CHECK(igraph_vector_push_back(&layer_to_y, dx * vgap));
+            }
+            VECTOR(layers_own)[k] = j;
+        }
+        igraph_vector_destroy(&inds);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* 2. Find the connected components. */
+    IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components,
+                                 IGRAPH_WEAK));
+
+    /* 3. For each component... */
+    dx = 0;
+    for (comp_idx = 0; comp_idx < no_of_components; comp_idx++) {
+        /* Extract the edges of the comp_idx'th component and add dummy nodes for edges
+         * spanning more than one layer. */
+        long int component_size, next_new_vertex_id;
+        igraph_vector_t old2new_vertex_ids;
+        igraph_vector_t new2old_vertex_ids;
+        igraph_vector_t new_layers;
+        igraph_vector_t edgelist;
+        igraph_vector_t neis;
+
+        IGRAPH_VECTOR_INIT_FINALLY(&edgelist, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&new2old_vertex_ids, no_of_nodes);
+        IGRAPH_VECTOR_INIT_FINALLY(&old2new_vertex_ids, no_of_nodes);
+        IGRAPH_VECTOR_INIT_FINALLY(&new_layers, 0);
+
+        igraph_vector_fill(&old2new_vertex_ids, -1);
+
+        /* Construct a mapping from the old vertex ids to the new ones */
+        for (i = 0, next_new_vertex_id = 0; i < no_of_nodes; i++) {
+            if (VECTOR(membership)[i] == comp_idx) {
+                IGRAPH_CHECK(igraph_vector_push_back(&new_layers, VECTOR(layers_own)[i]));
+                VECTOR(new2old_vertex_ids)[next_new_vertex_id] = i;
+                VECTOR(old2new_vertex_ids)[i] = next_new_vertex_id;
+                next_new_vertex_id++;
+            }
+        }
+        component_size = next_new_vertex_id;
+
+        /* Construct a proper layering of the component in new_graph where each edge
+         * points downwards and spans exactly one layer. */
+        for (i = 0; i < no_of_nodes; i++) {
+            if (VECTOR(membership)[i] != comp_idx) {
+                continue;
+            }
+
+            /* Okay, this vertex is in the component we are considering.
+             * Add the neighbors of this vertex, excluding loops */
+            IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i,
+                                         IGRAPH_OUT));
+            j = igraph_vector_size(&neis);
+            for (k = 0; k < j; k++) {
+                long int eid = (long int) VECTOR(neis)[k];
+                if (directed) {
+                    nei = IGRAPH_TO(graph, eid);
+                } else {
+                    nei = IGRAPH_OTHER(graph, eid, i);
+                    if (nei < i) { /* to avoid considering edges twice */
+                        continue;
+                    }
+                }
+                if (VECTOR(layers_own)[i] == VECTOR(layers_own)[nei]) {
+                    /* Edge goes within the same layer, we don't need this in the
+                     * layered graph, but we need it in the extended graph */
+                    if (extd_graph != 0) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i));
+                        IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei));
+                        if (extd_to_orig_eids != 0) {
+                            IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid));
+                        }
+                    }
+                } else if (VECTOR(layers_own)[i] > VECTOR(layers_own)[nei]) {
+                    /* Edge goes upwards, we have to flip it */
+                    IGRAPH_CHECK(igraph_vector_push_back(&edgelist,
+                                                         VECTOR(old2new_vertex_ids)[nei]));
+                    for (l = (long int) VECTOR(layers_own)[nei] + 1;
+                         l < VECTOR(layers_own)[i]; l++) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&new_layers, l));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id++));
+                    }
+                    IGRAPH_CHECK(igraph_vector_push_back(&edgelist,
+                                                         VECTOR(old2new_vertex_ids)[i]));
+                    /* Also add the edge to the extended graph if needed, but this time
+                     * with the proper orientation */
+                    if (extd_graph != 0) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i));
+                        next_extd_vertex_id += VECTOR(layers_own)[i] - VECTOR(layers_own)[nei] - 1;
+                        for (l = (long int) VECTOR(layers_own)[i] - 1, m = 1;
+                             l > VECTOR(layers_own)[nei]; l--, m++) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id - m));
+                            IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id - m));
+                            if (extd_to_orig_eids != 0) {
+                                IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid));
+                            }
+                        }
+                        IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei));
+                        if (extd_to_orig_eids != 0) {
+                            IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid));
+                        }
+                    }
+                } else {
+                    /* Edge goes downwards */
+                    IGRAPH_CHECK(igraph_vector_push_back(&edgelist,
+                                                         VECTOR(old2new_vertex_ids)[i]));
+                    for (l = (long int) VECTOR(layers_own)[i] + 1;
+                         l < VECTOR(layers_own)[nei]; l++) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&new_layers, l));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id));
+                        IGRAPH_CHECK(igraph_vector_push_back(&edgelist, next_new_vertex_id++));
+                    }
+                    IGRAPH_CHECK(igraph_vector_push_back(&edgelist,
+                                                         VECTOR(old2new_vertex_ids)[nei]));
+                    /* Also add the edge to the extended graph */
+                    if (extd_graph != 0) {
+                        IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i));
+                        for (l = (long int) VECTOR(layers_own)[i] + 1;
+                             l < VECTOR(layers_own)[nei]; l++) {
+                            IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id));
+                            IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, next_extd_vertex_id++));
+                            if (extd_to_orig_eids != 0) {
+                                IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid));
+                            }
+                        }
+                        IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei));
+                        if (extd_to_orig_eids != 0) {
+                            IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid));
+                        }
+                    }
+                }
+            }
+        }
+
+        /* At this point, we have the subgraph with the dummy nodes and
+         * edges, so we can run Sugiyama's algorithm on it. */
+        {
+            igraph_matrix_t layout;
+            igraph_i_layering_t layering;
+            igraph_t subgraph;
+
+            IGRAPH_CHECK(igraph_matrix_init(&layout, next_new_vertex_id, 2));
+            IGRAPH_FINALLY(igraph_matrix_destroy, &layout);
+            IGRAPH_CHECK(igraph_create(&subgraph, &edgelist, (igraph_integer_t)
+                                       next_new_vertex_id, 1));
+            IGRAPH_FINALLY(igraph_destroy, &subgraph);
+
+            /*
+            igraph_vector_print(&edgelist);
+            igraph_vector_print(&new_layers);
+            */
+
+            /* Assign the vertical coordinates */
+            for (i = 0; i < next_new_vertex_id; i++) {
+                MATRIX(layout, i, 1) = VECTOR(new_layers)[i];
+            }
+
+            /* Create a layering */
+            IGRAPH_CHECK(igraph_i_layering_init(&layering, &new_layers));
+            IGRAPH_FINALLY(igraph_i_layering_destroy, &layering);
+
+            /* Find the order in which the nodes within a layer should be placed */
+            IGRAPH_CHECK(igraph_i_layout_sugiyama_order_nodes_horizontally(&subgraph, &layout,
+                         &layering, maxiter));
+
+            /* Assign the horizontal coordinates. This is according to the algorithm
+             * of Brandes & Köpf */
+            IGRAPH_CHECK(igraph_i_layout_sugiyama_place_nodes_horizontally(&subgraph, &layout,
+                         &layering, hgap, (igraph_integer_t) component_size));
+
+            /* Re-assign rows into the result matrix, and at the same time, */
+            /* adjust dx so that the next component does not overlap this one */
+            j = next_new_vertex_id - component_size;
+            k = igraph_matrix_nrow(res);
+            IGRAPH_CHECK(igraph_matrix_add_rows(res, j));
+            dx2 = dx;
+            for (i = 0; i < component_size; i++) {
+                l = (long int)VECTOR(new2old_vertex_ids)[i];
+                MATRIX(*res, l, 0) = MATRIX(layout, i, 0) + dx;
+                MATRIX(*res, l, 1) = VECTOR(layer_to_y)[(long)MATRIX(layout, i, 1)];
+                if (dx2 < MATRIX(*res, l, 0)) {
+                    dx2 = MATRIX(*res, l, 0);
+                }
+            }
+            for (i = component_size; i < next_new_vertex_id; i++) {
+                MATRIX(*res, k, 0) = MATRIX(layout, i, 0) + dx;
+                MATRIX(*res, k, 1) = VECTOR(layer_to_y)[(long)MATRIX(layout, i, 1)];
+                if (dx2 < MATRIX(*res, k, 0)) {
+                    dx2 = MATRIX(*res, k, 0);
+                }
+                k++;
+            }
+            dx = dx2 + hgap;
+
+            igraph_destroy(&subgraph);
+            igraph_i_layering_destroy(&layering);
+            igraph_matrix_destroy(&layout);
+            IGRAPH_FINALLY_CLEAN(3);
+        }
+
+        igraph_vector_destroy(&new_layers);
+        igraph_vector_destroy(&old2new_vertex_ids);
+        igraph_vector_destroy(&new2old_vertex_ids);
+        igraph_vector_destroy(&edgelist);
+        igraph_vector_destroy(&neis);
+        IGRAPH_FINALLY_CLEAN(5);
+    }
+
+    igraph_vector_destroy(&layers_own);
+    igraph_vector_destroy(&layer_to_y);
+    igraph_vector_destroy(&membership);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    if (extd_graph != 0) {
+        IGRAPH_CHECK(igraph_create(extd_graph, &extd_edgelist, (igraph_integer_t)
+                                   next_extd_vertex_id, igraph_is_directed(graph)));
+        igraph_vector_destroy(&extd_edgelist);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph,
+        const igraph_vector_t* weights, igraph_vector_t* membership) {
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes));
+
+    if (no_of_edges == 0) {
+        igraph_vector_fill(membership, 0);
+        return IGRAPH_SUCCESS;
+    }
+
+#ifdef HAVE_GLPK
+    if (igraph_is_directed(graph) && no_of_nodes <= 1000) {
+        /* Network simplex algorithm of Gansner et al, using the original linear
+         * programming formulation */
+        long int i, j;
+        igraph_vector_t outdegs, indegs, feedback_edges;
+        glp_prob *ip;
+        glp_smcp parm;
+
+        /* Allocate storage and create the problem */
+        ip = glp_create_prob();
+        IGRAPH_FINALLY(glp_delete_prob, ip);
+        IGRAPH_VECTOR_INIT_FINALLY(&feedback_edges, 0);
+        IGRAPH_VECTOR_INIT_FINALLY(&outdegs, no_of_nodes);
+        IGRAPH_VECTOR_INIT_FINALLY(&indegs, no_of_nodes);
+
+        /* Find an approximate feedback edge set */
+        IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, &feedback_edges, weights, 0));
+        igraph_vector_sort(&feedback_edges);
+
+        /* Calculate in- and out-strengths for the remaining edges */
+        IGRAPH_CHECK(igraph_strength(graph, &indegs, igraph_vss_all(),
+                                     IGRAPH_IN, 1, weights));
+        IGRAPH_CHECK(igraph_strength(graph, &outdegs, igraph_vss_all(),
+                                     IGRAPH_IN, 1, weights));
+        j = igraph_vector_size(&feedback_edges);
+        for (i = 0; i < j; i++) {
+            long int eid = (long int) VECTOR(feedback_edges)[i];
+            long int from = IGRAPH_FROM(graph, eid);
+            long int to = IGRAPH_TO(graph, eid);
+            VECTOR(outdegs)[from] -= weights ? VECTOR(*weights)[eid] : 1;
+            VECTOR(indegs)[to] -= weights ? VECTOR(*weights)[eid] : 1;
+        }
+
+        /* Configure GLPK */
+        glp_term_out(GLP_OFF);
+        glp_init_smcp(&parm);
+        parm.msg_lev = GLP_MSG_OFF;
+        parm.presolve = GLP_OFF;
+
+        /* Set up variables and objective function coefficients */
+        glp_set_obj_dir(ip, GLP_MIN);
+        glp_add_cols(ip, (int) no_of_nodes);
+        IGRAPH_CHECK(igraph_vector_sub(&outdegs, &indegs));
+        for (i = 1; i <= no_of_nodes; i++) {
+            glp_set_col_kind(ip, (int) i, GLP_IV);
+            glp_set_col_bnds(ip, (int) i, GLP_LO, 0.0, 0.0);
+            glp_set_obj_coef(ip, (int) i, VECTOR(outdegs)[i - 1]);
+        }
+        igraph_vector_destroy(&indegs);
+        igraph_vector_destroy(&outdegs);
+        IGRAPH_FINALLY_CLEAN(2);
+
+        /* Add constraints */
+        glp_add_rows(ip, (int) no_of_edges);
+        IGRAPH_CHECK(igraph_vector_push_back(&feedback_edges, -1));
+        j = 0;
+        for (i = 0; i < no_of_edges; i++) {
+            int ind[3];
+            double val[3] = {0, -1, 1};
+            ind[1] = IGRAPH_FROM(graph, i) + 1;
+            ind[2] = IGRAPH_TO(graph, i) + 1;
+
+            if (ind[1] == ind[2]) {
+                if (VECTOR(feedback_edges)[j] == i) {
+                    j++;
+                }
+                continue;
+            }
+
+            if (VECTOR(feedback_edges)[j] == i) {
+                /* This is a feedback edge, add it reversed */
+                glp_set_row_bnds(ip, (int) i + 1, GLP_UP, -1, -1);
+                j++;
+            } else {
+                glp_set_row_bnds(ip, (int) i + 1, GLP_LO, 1, 1);
+            }
+            glp_set_mat_row(ip, (int) i + 1, 2, ind, val);
+        }
+
+        /* Solve the problem */
+        IGRAPH_GLPK_CHECK(glp_simplex(ip, &parm),
+                          "Vertical arrangement step using IP failed");
+
+        /* The problem is totally unimodular, therefore the output of the simplex
+         * solver can be converted to an integer solution easily */
+        for (i = 0; i < no_of_nodes; i++) {
+            VECTOR(*membership)[i] = floor(glp_get_col_prim(ip, (int) i + 1));
+        }
+
+        glp_delete_prob(ip);
+        igraph_vector_destroy(&feedback_edges);
+        IGRAPH_FINALLY_CLEAN(2);
+    } else if (igraph_is_directed(graph)) {
+        IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, 0, weights, membership));
+    } else {
+        IGRAPH_CHECK(igraph_i_feedback_arc_set_undirected(graph, 0, weights, membership));
+    }
+#else
+    if (igraph_is_directed(graph)) {
+        IGRAPH_CHECK(igraph_i_feedback_arc_set_eades(graph, 0, weights, membership));
+    } else {
+        IGRAPH_CHECK(igraph_i_feedback_arc_set_undirected(graph, 0, weights, membership));
+    }
+#endif
+
+    return IGRAPH_SUCCESS;
+}
+
+static int igraph_i_layout_sugiyama_calculate_barycenters(const igraph_t* graph,
+        const igraph_i_layering_t* layering, long int layer_index,
+        igraph_neimode_t direction, const igraph_matrix_t* layout,
+        igraph_vector_t* barycenters) {
+    long int i, j, m, n;
+    igraph_vector_t* layer_members = igraph_i_layering_get(layering, layer_index);
+    igraph_vector_t neis;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    n = igraph_vector_size(layer_members);
+    IGRAPH_CHECK(igraph_vector_resize(barycenters, n));
+    igraph_vector_null(barycenters);
+
+    for (i = 0; i < n; i++) {
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t)
+                                      VECTOR(*layer_members)[i], direction));
+        m = igraph_vector_size(&neis);
+        if (m == 0) {
+            /* No neighbors in this direction. Just use the current X coordinate */
+            VECTOR(*barycenters)[i] = MATRIX(*layout, i, 0);
+        } else {
+            for (j = 0; j < m; j++) {
+                VECTOR(*barycenters)[i] += MATRIX(*layout, (long)VECTOR(neis)[j], 0);
+            }
+            VECTOR(*barycenters)[i] /= m;
+        }
+    }
+
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+/**
+ * Given a properly layered graph where each edge points downwards and spans
+ * exactly one layer, arranges the nodes in each layer horizontally in a way
+ * that strives to minimize edge crossings.
+ */
+static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph,
+        igraph_matrix_t* layout, const igraph_i_layering_t* layering,
+        long int maxiter) {
+    long int i, n, nei;
+    long int no_of_vertices = igraph_vcount(graph);
+    long int no_of_layers = igraph_i_layering_num_layers(layering);
+    long int iter, layer_index;
+    igraph_vector_t* layer_members;
+    igraph_vector_t neis, barycenters, sort_indices;
+    igraph_bool_t changed;
+
+    /* The first column of the matrix will serve as the ordering */
+    /* Start with a first-seen ordering within each layer */
+    {
+        long int *xs = igraph_Calloc(no_of_layers, long int);
+        if (xs == 0) {
+            IGRAPH_ERROR("cannot order nodes horizontally", IGRAPH_ENOMEM);
+        }
+        for (i = 0; i < no_of_vertices; i++) {
+            MATRIX(*layout, i, 0) = xs[(long int)MATRIX(*layout, i, 1)]++;
+        }
+        free(xs);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&barycenters, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&sort_indices, 0);
+
+    /* Start the effective part of the Sugiyama algorithm */
+    iter = 0; changed = 1;
+    while (changed && iter < maxiter) {
+        changed = 0;
+
+        /* Phase 1 */
+
+        /* Moving downwards and sorting by upper barycenters */
+        for (layer_index = 1; layer_index < no_of_layers; layer_index++) {
+            layer_members = igraph_i_layering_get(layering, layer_index);
+            n = igraph_vector_size(layer_members);
+
+            igraph_i_layout_sugiyama_calculate_barycenters(graph,
+                    layering, layer_index, IGRAPH_IN, layout, &barycenters);
+
+#ifdef SUGIYAMA_DEBUG
+            printf("Layer %ld, aligning to upper barycenters\n", layer_index);
+            printf("Vertices: "); igraph_vector_print(layer_members);
+            printf("Barycenters: "); igraph_vector_print(&barycenters);
+#endif
+            IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters,
+                         &sort_indices, 0));
+            for (i = 0; i < n; i++) {
+                nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]];
+                VECTOR(barycenters)[i] = nei;
+                MATRIX(*layout, nei, 0) = i;
+            }
+            if (!igraph_vector_all_e(layer_members, &barycenters)) {
+                IGRAPH_CHECK(igraph_vector_update(layer_members, &barycenters));
+#ifdef SUGIYAMA_DEBUG
+                printf("New vertex order: "); igraph_vector_print(layer_members);
+#endif
+                changed = 1;
+            } else {
+#ifdef SUGIYAMA_DEBUG
+                printf("Order did not change.\n");
+#endif
+            }
+        }
+
+        /* Moving upwards and sorting by lower barycenters */
+        for (layer_index = no_of_layers - 2; layer_index >= 0; layer_index--) {
+            layer_members = igraph_i_layering_get(layering, layer_index);
+            n = igraph_vector_size(layer_members);
+
+            igraph_i_layout_sugiyama_calculate_barycenters(graph,
+                    layering, layer_index, IGRAPH_OUT, layout, &barycenters);
+
+#ifdef SUGIYAMA_DEBUG
+            printf("Layer %ld, aligning to lower barycenters\n", layer_index);
+            printf("Vertices: "); igraph_vector_print(layer_members);
+            printf("Barycenters: "); igraph_vector_print(&barycenters);
+#endif
+
+            IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters,
+                         &sort_indices, 0));
+            for (i = 0; i < n; i++) {
+                nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]];
+                VECTOR(barycenters)[i] = nei;
+                MATRIX(*layout, nei, 0) = i;
+            }
+            if (!igraph_vector_all_e(layer_members, &barycenters)) {
+                IGRAPH_CHECK(igraph_vector_update(layer_members, &barycenters));
+#ifdef SUGIYAMA_DEBUG
+                printf("New vertex order: "); igraph_vector_print(layer_members);
+#endif
+                changed = 1;
+            } else {
+#ifdef SUGIYAMA_DEBUG
+                printf("Order did not change.\n");
+#endif
+            }
+        }
+
+#ifdef SUGIYAMA_DEBUG
+        printf("==== Finished iteration %ld\n", iter);
+#endif
+
+        iter++;
+    }
+
+    igraph_vector_destroy(&barycenters);
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&sort_indices);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+#define IS_DUMMY(v) ((v >= no_of_real_nodes))
+#define IS_INNER_SEGMENT(u, v) (IS_DUMMY(u) && IS_DUMMY(v))
+#define X_POS(v) (MATRIX(*layout, v, 0))
+
+static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph,
+        const igraph_i_layering_t* layering, const igraph_matrix_t* layout,
+        const igraph_vector_bool_t* ignored_edges,
+        igraph_bool_t reverse, igraph_bool_t align_right,
+        igraph_vector_t* roots, igraph_vector_t* align);
+static int igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph,
+        const igraph_vector_t* vertex_to_the_left,
+        const igraph_vector_t* roots, const igraph_vector_t* align,
+        igraph_real_t hgap, igraph_vector_t* xs);
+static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v,
+        const igraph_vector_t* vertex_to_the_left,
+        const igraph_vector_t* roots, const igraph_vector_t* align,
+        igraph_vector_t* sinks, igraph_vector_t* shifts,
+        igraph_real_t hgap, igraph_vector_t* xs);
+
+static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* graph,
+        igraph_matrix_t* layout, const igraph_i_layering_t* layering,
+        igraph_real_t hgap, igraph_integer_t no_of_real_nodes) {
+
+    long int i, j, k, l, n;
+    long int no_of_layers = igraph_i_layering_num_layers(layering);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vector_t neis1, neis2;
+    igraph_vector_t xs[4];
+    igraph_vector_t roots, align;
+    igraph_vector_t vertex_to_the_left;
+    igraph_vector_bool_t ignored_edges;
+
+    /*
+    {
+      igraph_vector_t edgelist;
+      IGRAPH_VECTOR_INIT_FINALLY(&edgelist, 0);
+      IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0));
+      igraph_vector_print(&edgelist);
+      igraph_vector_destroy(&edgelist);
+      IGRAPH_FINALLY_CLEAN(1);
+
+      for (i = 0; i < no_of_layers; i++) {
+        igraph_vector_t* layer = igraph_i_layering_get(layering, i);
+        igraph_vector_print(layer);
+      }
+    }
+    */
+
+    IGRAPH_CHECK(igraph_vector_bool_init(&ignored_edges, no_of_edges));
+    IGRAPH_FINALLY(igraph_vector_bool_destroy, &ignored_edges);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&vertex_to_the_left, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis2, 0);
+
+    /* First, find all type 1 conflicts and mark one of the edges participating
+     * in the conflict as being ignored. If one of the edges in the conflict
+     * is a non-inner segment and the other is an inner segment, we ignore the
+     * non-inner segment as we want to keep inner segments vertical.
+     */
+    for (i = 0; i < no_of_layers - 1; i++) {
+        igraph_vector_t* vertices = igraph_i_layering_get(layering, i);
+        n = igraph_vector_size(vertices);
+
+        /* Find all the edges from this layer to the next */
+        igraph_vector_clear(&neis1);
+        for (j = 0; j < n; j++) {
+            IGRAPH_CHECK(igraph_neighbors(graph, &neis2, (igraph_integer_t)
+                                          VECTOR(*vertices)[j], IGRAPH_OUT));
+            IGRAPH_CHECK(igraph_vector_append(&neis1, &neis2));
+        }
+
+        /* Consider all pairs of edges and check whether they are in a type 1
+         * conflict */
+        n = igraph_vector_size(&neis1);
+        for (j = 0; j < n; j++) {
+            long int u = IGRAPH_FROM(graph, j);
+            long int v = IGRAPH_TO(graph, j);
+            igraph_bool_t j_inner = IS_INNER_SEGMENT(u, v);
+            igraph_bool_t crossing;
+
+            for (k = j + 1; k < n; k++) {
+                long int w = IGRAPH_FROM(graph, k);
+                long int x = IGRAPH_TO(graph, k);
+                if (IS_INNER_SEGMENT(w, x) == j_inner) {
+                    continue;
+                }
+                /* Do the u --> v and w --> x edges cross? */
+                crossing = (u == w || v == x);
+                if (!crossing) {
+                    if (X_POS(u) <= X_POS(w)) {
+                        crossing = X_POS(v) >= X_POS(x);
+                    } else {
+                        crossing = X_POS(v) <= X_POS(x);
+                    }
+                }
+                if (crossing) {
+                    if (j_inner) {
+                        VECTOR(ignored_edges)[k] = 1;
+                    } else {
+                        VECTOR(ignored_edges)[j] = 1;
+                    }
+                }
+            }
+        }
+    }
+
+    igraph_vector_destroy(&neis1);
+    igraph_vector_destroy(&neis2);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    /*
+     * Prepare vertex_to_the_left where the ith element stores
+     * the index of the vertex to the left of vertex i, or i itself if the
+     * vertex is the leftmost vertex in a layer.
+     */
+    for (i = 0; i < no_of_layers; i++) {
+        igraph_vector_t* vertices = igraph_i_layering_get(layering, i);
+        n = igraph_vector_size(vertices);
+        if (n == 0) {
+            continue;
+        }
+
+        k = l = (long int)VECTOR(*vertices)[0];
+        VECTOR(vertex_to_the_left)[k] = k;
+        for (j = 1; j < n; j++) {
+            k = (long int)VECTOR(*vertices)[j];
+            VECTOR(vertex_to_the_left)[k] = l;
+            l = k;
+        }
+    }
+
+    /* Type 1 conflicts found, ignored edges chosen, vertex_to_the_left
+     * prepared. Run vertical alignment for all four combinations */
+    for (i = 0; i < 4; i++) {
+        IGRAPH_VECTOR_INIT_FINALLY(&xs[i], no_of_nodes);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&roots, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&align, no_of_nodes);
+
+    for (i = 0; i < 4; i++) {
+        IGRAPH_CHECK(igraph_i_layout_sugiyama_vertical_alignment(graph,
+                     layering, layout, &ignored_edges,
+                     /* reverse = */ (igraph_bool_t) i / 2, /* align_right = */ i % 2,
+                     &roots, &align));
+        IGRAPH_CHECK(igraph_i_layout_sugiyama_horizontal_compaction(graph,
+                     &vertex_to_the_left, &roots, &align, hgap, &xs[i]));
+    }
+
+    {
+        igraph_real_t width, min_width, mins[4], maxs[4], diff;
+        /* Find the alignment with the minimum width */
+        min_width = IGRAPH_INFINITY; j = 0;
+        for (i = 0; i < 4; i++) {
+            mins[i] = igraph_vector_min(&xs[i]);
+            maxs[i] = igraph_vector_max(&xs[i]);
+            width = maxs[i] - mins[i];
+            if (width < min_width) {
+                min_width = width;
+                j = i;
+            }
+        }
+
+        /* Leftmost alignments: align them s.t. the min X coordinate is equal to
+         * the minimum X coordinate of the alignment with the smallest width.
+         * Rightmost alignments: align them s.t. the max X coordinate is equal to
+         * the max X coordinate of the alignment with the smallest width.
+         */
+        for (i = 0; i < 4; i++) {
+            if (j == i) {
+                continue;
+            }
+            if (i % 2 == 0) {
+                /* Leftmost alignment */
+                diff = mins[j] - mins[i];
+            } else {
+                /* Rightmost alignment */
+                diff = maxs[j] - maxs[i];
+            }
+            igraph_vector_add_constant(&xs[i], diff);
+        }
+    }
+
+    /* For every vertex, find the median of the X coordinates in the four
+     * alignments */
+    for (i = 0; i < no_of_nodes; i++) {
+        X_POS(i) = igraph_i_median_4(VECTOR(xs[0])[i], VECTOR(xs[1])[i],
+                                     VECTOR(xs[2])[i], VECTOR(xs[3])[i]);
+    }
+
+    igraph_vector_destroy(&roots);
+    igraph_vector_destroy(&align);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    for (i = 0; i < 4; i++) {
+        igraph_vector_destroy(&xs[i]);
+    }
+    IGRAPH_FINALLY_CLEAN(4);
+
+    igraph_vector_destroy(&vertex_to_the_left);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    igraph_vector_bool_destroy(&ignored_edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
+
+static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph,
+        const igraph_i_layering_t* layering, const igraph_matrix_t* layout,
+        const igraph_vector_bool_t* ignored_edges,
+        igraph_bool_t reverse, igraph_bool_t align_right,
+        igraph_vector_t* roots, igraph_vector_t* align) {
+    long int i, j, k, n, di, dj, i_limit, j_limit, r;
+    long int no_of_layers = igraph_i_layering_num_layers(layering);
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_neimode_t neimode = (reverse ? IGRAPH_OUT : IGRAPH_IN);
+    igraph_vector_t neis, xs, inds;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&xs, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&inds, 0);
+
+    IGRAPH_CHECK(igraph_vector_resize(roots, no_of_nodes));
+    IGRAPH_CHECK(igraph_vector_resize(align, no_of_nodes));
+
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(*roots)[i] = VECTOR(*align)[i] = i;
+    }
+
+    /* When reverse = False, we are aligning "upwards" in the tree, hence we
+     * have to loop i from 1 to no_of_layers-1 (inclusive) and use neimode=IGRAPH_IN.
+     * When reverse = True, we are aligning "downwards", hence we have to loop
+     * i from no_of_layers-2 to 0 (inclusive) and use neimode=IGRAPH_OUT.
+     */
+    i       = reverse ? (no_of_layers - 2) : 1;
+    di      = reverse ? -1 : 1;
+    i_limit = reverse ? -1 : no_of_layers;
+    for (; i != i_limit; i += di) {
+        igraph_vector_t *layer = igraph_i_layering_get(layering, i);
+
+        /* r = 0 in the paper, but C arrays are indexed from 0 */
+        r = align_right ? LONG_MAX : -1;
+
+        /* If align_right is 1, we have to process the layer in reverse order */
+        j       = align_right ? (igraph_vector_size(layer) - 1) : 0;
+        dj      = align_right ? -1 : 1;
+        j_limit = align_right ? -1 : igraph_vector_size(layer);
+        for (; j != j_limit; j += dj) {
+            long int medians[2];
+            long int vertex = (long int) VECTOR(*layer)[j];
+            long int pos;
+
+            if (VECTOR(*align)[vertex] != vertex)
+                /* This vertex is already aligned with some other vertex,
+                 * so there's nothing to do */
+            {
+                continue;
+            }
+
+            /* Find the neighbors of vertex j in layer i */
+            IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) vertex,
+                                          neimode));
+
+            n = igraph_vector_size(&neis);
+            if (n == 0)
+                /* No neighbors in this direction, continue */
+            {
+                continue;
+            }
+            if (n == 1) {
+                /* Just one neighbor; the median is trivial */
+                medians[0] = (long int) VECTOR(neis)[0];
+                medians[1] = -1;
+            } else {
+                /* Sort the neighbors by their X coordinates */
+                IGRAPH_CHECK(igraph_vector_resize(&xs, n));
+                for (k = 0; k < n; k++) {
+                    VECTOR(xs)[k] = X_POS((long int)VECTOR(neis)[k]);
+                }
+                IGRAPH_CHECK((int) igraph_vector_qsort_ind(&xs, &inds, 0));
+
+                if (n % 2 == 1) {
+                    /* Odd number of neighbors, so the median is unique */
+                    medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]];
+                    medians[1] = -1;
+                } else {
+                    /* Even number of neighbors, so we have two medians. The order
+                     * depends on whether we are processing the layer in leftmost
+                     * or rightmost fashion. */
+                    if (align_right) {
+                        medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]];
+                        medians[1] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2 - 1]];
+                    } else {
+                        medians[0] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2 - 1]];
+                        medians[1] = (long int) VECTOR(neis)[(long int)VECTOR(inds)[n / 2]];
+                    }
+                }
+            }
+
+            /* Try aligning with the medians */
+            for (k = 0; k < 2; k++) {
+                igraph_integer_t eid;
+                if (medians[k] < 0) {
+                    continue;
+                }
+                if (VECTOR(*align)[vertex] != vertex) {
+                    /* Vertex already aligned, continue */
+                    continue;
+                }
+                /* Is the edge between medians[k] and vertex ignored
+                 * because of a type 1 conflict? */
+                IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) vertex,
+                                            (igraph_integer_t) medians[k], 0, 1));
+                if (VECTOR(*ignored_edges)[(long int)eid]) {
+                    continue;
+                }
+                /* Okay, align with the median if possible */
+                pos = (long int) X_POS(medians[k]);
+                if ((align_right && r > pos) || (!align_right && r < pos)) {
+                    VECTOR(*align)[medians[k]] = vertex;
+                    VECTOR(*roots)[vertex] = VECTOR(*roots)[medians[k]];
+                    VECTOR(*align)[vertex] = VECTOR(*roots)[medians[k]];
+                    r = pos;
+                }
+            }
+        }
+    }
+
+    igraph_vector_destroy(&inds);
+    igraph_vector_destroy(&neis);
+    igraph_vector_destroy(&xs);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+/*
+ * Runs a horizontal compaction given a vertical alignment (in `align`)
+ * and the roots (in `roots`). These come out directly from
+ * igraph_i_layout_sugiyama_vertical_alignment.
+ *
+ * Returns the X coordinates for each vertex in `xs`.
+ *
+ * `graph` is the input graph, `layering` is the layering on which we operate.
+ * `hgap` is the preferred horizontal gap between vertices.
+ */
+static int igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph,
+        const igraph_vector_t* vertex_to_the_left,
+        const igraph_vector_t* roots, const igraph_vector_t* align,
+        igraph_real_t hgap, igraph_vector_t* xs) {
+    long int i;
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t sinks, shifts, old_xs;
+    igraph_real_t shift;
+
+    /* Initialization */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&sinks, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&shifts, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&old_xs, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_vector_resize(xs, no_of_nodes));
+
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(sinks)[i] = i;
+    }
+    igraph_vector_fill(&shifts, IGRAPH_INFINITY);
+    igraph_vector_fill(xs, -1);
+
+    /* Calculate the coordinates of the vertices relative to their sinks
+     * in their own class. At the end of this for loop, xs will contain the
+     * relative displacement of a vertex from its sink, while the shifts list
+     * will contain the absolute displacement of the sinks.
+     * (For the sinks only, of course, the rest is undefined and unused)
+     */
+    for (i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*roots)[i] == i) {
+            IGRAPH_CHECK(
+                igraph_i_layout_sugiyama_horizontal_compaction_place_block(i,
+                        vertex_to_the_left, roots, align, &sinks, &shifts, hgap, xs)
+            );
+        }
+    }
+
+    /* In "sinks", only those indices `i` matter for which `i` is in `roots`.
+     * All the other values will never be touched.
+     */
+
+    /* Calculate the absolute coordinates */
+    IGRAPH_CHECK(igraph_vector_update(&old_xs, xs));
+    for (i = 0; i < no_of_nodes; i++) {
+        long int root = (long int) VECTOR(*roots)[i];
+        VECTOR(*xs)[i] = VECTOR(old_xs)[root];
+        shift = VECTOR(shifts)[(long int)VECTOR(sinks)[root]];
+        if (shift < IGRAPH_INFINITY) {
+            VECTOR(*xs)[i] += shift;
+        }
+    }
+
+    igraph_vector_destroy(&sinks);
+    igraph_vector_destroy(&shifts);
+    igraph_vector_destroy(&old_xs);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return IGRAPH_SUCCESS;
+}
+
+static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v,
+        const igraph_vector_t* vertex_to_the_left,
+        const igraph_vector_t* roots, const igraph_vector_t* align,
+        igraph_vector_t* sinks, igraph_vector_t* shifts,
+        igraph_real_t hgap, igraph_vector_t* xs) {
+    long int u, w;
+    long int u_sink, v_sink;
+
+    if (VECTOR(*xs)[v] >= 0) {
+        return IGRAPH_SUCCESS;
+    }
+
+    VECTOR(*xs)[v] = 0;
+
+    w = v;
+    do {
+        /* Check whether vertex w is the leftmost in its own layer */
+        u = (long int) VECTOR(*vertex_to_the_left)[w];
+        if (u != w) {
+            /* Get the root of u (proceeding all the way upwards in the block) */
+            u = (long int) VECTOR(*roots)[u];
+            /* Place the block of u recursively */
+            IGRAPH_CHECK(
+                igraph_i_layout_sugiyama_horizontal_compaction_place_block(u,
+                        vertex_to_the_left, roots, align, sinks, shifts, hgap, xs)
+            );
+
+            u_sink = (long int) VECTOR(*sinks)[u];
+            v_sink = (long int) VECTOR(*sinks)[v];
+            /* If v is its own sink yet, set its sink to the sink of u */
+            if (v_sink == v) {
+                VECTOR(*sinks)[v] = v_sink = u_sink;
+            }
+            /* If v and u have different sinks (i.e. they are in different classes),
+             * shift the sink of u so that the two blocks are separated by the
+             * preferred gap
+             */
+            if (v_sink != u_sink) {
+                if (VECTOR(*shifts)[u_sink] > VECTOR(*xs)[v] - VECTOR(*xs)[u] - hgap) {
+                    VECTOR(*shifts)[u_sink] = VECTOR(*xs)[v] - VECTOR(*xs)[u] - hgap;
+                }
+            } else {
+                /* v and u have the same sink, i.e. they are in the same class. Make sure
+                 * that v is separated from u by at least hgap.
+                 */
+                if (VECTOR(*xs)[v] < VECTOR(*xs)[u] + hgap) {
+                    VECTOR(*xs)[v] = VECTOR(*xs)[u] + hgap;
+                }
+            }
+        }
+
+        /* Follow the alignment */
+        w = (long int) VECTOR(*align)[w];
+    } while (w != v);
+
+    return IGRAPH_SUCCESS;
+}
+
+#undef IS_INNER_SEGMENT
+#undef IS_DUMMY
+#undef X_POS
+
+#ifdef SUGIYAMA_DEBUG
+    #undef SUGIYAMA_DEBUG
+#endif
+
+
diff --git a/igraph/src/system_.c b/igraph/src/system_.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/system_.c
@@ -0,0 +1,42 @@
+/* f77 interface to system routine */
+
+#include "f2c.h"
+
+#ifdef KR_headers
+extern char *F77_aloc();
+
+ integer
+system_(s, n) register char *s; ftnlen n;
+#else
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern char *F77_aloc(ftnlen, const char*);
+
+ integer
+system_(register char *s, ftnlen n)
+#endif
+{
+	char buff0[256], *buff;
+	register char *bp, *blast;
+	integer rv;
+
+	buff = bp = n < sizeof(buff0)
+			? buff0 : F77_aloc(n+1, "system_");
+	blast = bp + n;
+
+	while(bp < blast && *s)
+		*bp++ = *s++;
+	*bp = 0;
+	rv = system(buff);
+	if (buff != buff0)
+		free(buff);
+	return rv;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/topology.c b/igraph/src/topology.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/topology.c
@@ -0,0 +1,3129 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_topology.h"
+#include "igraph_memory.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_constructors.h"
+#include "igraph_conversion.h"
+#include "igraph_stack.h"
+#include "igraph_attributes.h"
+#include "igraph_structural.h"
+#include "config.h"
+
+const unsigned int igraph_i_isoclass_3[] = {  0, 1, 1, 3, 1, 5, 6, 7,
+                                              1, 6, 10, 11, 3, 7, 11, 15,
+                                              1, 6, 5, 7, 10, 21, 21, 23,
+                                              6, 25, 21, 27, 11, 27, 30, 31,
+                                              1, 10, 6, 11, 6, 21, 25, 27,
+                                              5, 21, 21, 30, 7, 23, 27, 31,
+                                              3, 11, 7, 15, 11, 30, 27, 31,
+                                              7, 27, 23, 31, 15, 31, 31, 63
+                                           };
+
+const unsigned int igraph_i_isoclass_3_idx[] = { 0, 4, 16, 1, 0, 32, 2, 8, 0 };
+
+const unsigned int igraph_i_isoclass_4[] = {
+    0,   1,   1,   3,   1,   3,   3,   7,   1,   9,  10,  11,  10,
+    11,  14,  15,   1,  10,  18,  19,  20,  21,  22,  23,   3,  11,
+    19,  27,  21,  29,  30,  31,   1,  10,  20,  21,  18,  19,  22,
+    23,   3,  11,  21,  29,  19,  27,  30,  31,   3,  14,  22,  30,
+    22,  30,  54,  55,   7,  15,  23,  31,  23,  31,  55,  63,   1,
+    10,   9,  11,  10,  14,  11,  15,  18,  73,  73,  75,  76,  77,
+    77,  79,  10,  81,  73,  83,  84,  85,  86,  87,  19,  83,  90,
+    91,  92,  93,  94,  95,  20,  84,  98,  99, 100, 101, 102, 103,
+    22,  86, 106, 107, 108, 109, 110, 111,  21,  85, 106, 115, 116,
+    117, 118, 119,  23,  87, 122, 123, 124, 125, 126, 127,   1,  18,
+    10,  19,  20,  22,  21,  23,  10,  73,  81,  83,  84,  86,  85,
+    87,   9,  73,  73,  90,  98, 106, 106, 122,  11,  75,  83,  91,
+    99, 107, 115, 123,  10,  76,  84,  92, 100, 108, 116, 124,  14,
+    77,  85,  93, 101, 109, 117, 125,  11,  77,  86,  94, 102, 110,
+    118, 126,  15,  79,  87,  95, 103, 111, 119, 127,   3,  19,  11,
+    27,  21,  30,  29,  31,  19,  90,  83,  91,  92,  94,  93,  95,
+    11,  83,  75,  91,  99, 115, 107, 123,  27,  91,  91, 219, 220,
+    221, 221, 223,  21,  92,  99, 220, 228, 229, 230, 231,  30,  94,
+    115, 221, 229, 237, 238, 239,  29,  93, 107, 221, 230, 238, 246,
+    247,  31,  95, 123, 223, 231, 239, 247, 255,   1,  20,  10,  21,
+    18,  22,  19,  23,  20,  98,  84,  99, 100, 102, 101, 103,  10,
+    84,  76,  92, 100, 116, 108, 124,  21,  99,  92, 220, 228, 230,
+    229, 231,  18, 100, 100, 228, 292, 293, 293, 295,  22, 102, 116,
+    230, 293, 301, 302, 303,  19, 101, 108, 229, 293, 302, 310, 311,
+    23, 103, 124, 231, 295, 303, 311, 319,   3,  21,  11,  29,  19,
+    30,  27,  31,  22, 106,  86, 107, 108, 110, 109, 111,  14,  85,
+    77,  93, 101, 117, 109, 125,  30, 115,  94, 221, 229, 238, 237,
+    239,  22, 116, 102, 230, 293, 302, 301, 303,  54, 118, 118, 246,
+    310, 365, 365, 367,  30, 117, 110, 238, 302, 373, 365, 375,  55,
+    119, 126, 247, 311, 375, 382, 383,   3,  22,  14,  30,  22,  54,
+    30,  55,  21, 106,  85, 115, 116, 118, 117, 119,  11,  86,  77,
+    94, 102, 118, 110, 126,  29, 107,  93, 221, 230, 246, 238, 247,
+    19, 108, 101, 229, 293, 310, 302, 311,  30, 110, 117, 238, 302,
+    365, 373, 375,  27, 109, 109, 237, 301, 365, 365, 382,  31, 111,
+    125, 239, 303, 367, 375, 383,   7,  23,  15,  31,  23,  55,  31,
+    63,  23, 122,  87, 123, 124, 126, 125, 127,  15,  87,  79,  95,
+    103, 119, 111, 127,  31, 123,  95, 223, 231, 247, 239, 255,  23,
+    124, 103, 231, 295, 311, 303, 319,  55, 126, 119, 247, 311, 382,
+    375, 383,  31, 125, 111, 239, 303, 375, 367, 383,  63, 127, 127,
+    255, 319, 383, 383, 511,   1,  10,  10,  14,   9,  11,  11,  15,
+    18,  73,  76,  77,  73,  75,  77,  79,  20,  84, 100, 101,  98,
+    99, 102, 103,  22,  86, 108, 109, 106, 107, 110, 111,  10,  81,
+    84,  85,  73,  83,  86,  87,  19,  83,  92,  93,  90,  91,  94,
+    95,  21,  85, 116, 117, 106, 115, 118, 119,  23,  87, 124, 125,
+    122, 123, 126, 127,  18,  76,  73,  77,  73,  77,  75,  79, 292,
+    585, 585, 587, 585, 587, 587, 591, 100, 593, 594, 595, 596, 597,
+    598, 599, 293, 601, 602, 603, 604, 605, 606, 607, 100, 593, 596,
+    597, 594, 595, 598, 599, 293, 601, 604, 605, 602, 603, 606, 607,
+    228, 625, 626, 627, 626, 627, 630, 631, 295, 633, 634, 635, 634,
+    635, 638, 639,  20, 100,  84, 101,  98, 102,  99, 103, 100, 594,
+    593, 595, 596, 598, 597, 599,  98, 596, 596, 659, 660, 661, 661,
+    663, 102, 598, 666, 667, 661, 669, 670, 671,  84, 593, 674, 675,
+    596, 666, 678, 679, 101, 595, 675, 683, 659, 667, 686, 687,  99,
+    597, 678, 686, 661, 670, 694, 695, 103, 599, 679, 687, 663, 671,
+    695, 703,  22, 108,  86, 109, 106, 110, 107, 111, 293, 602, 601,
+    603, 604, 606, 605, 607, 102, 666, 598, 667, 661, 670, 669, 671,
+    301, 729, 729, 731, 732, 733, 733, 735, 116, 737, 678, 739, 626,
+    741, 742, 743, 302, 745, 746, 747, 748, 749, 750, 751, 230, 753,
+    742, 755, 756, 757, 758, 759, 303, 761, 762, 763, 764, 765, 766,
+    767,  10,  84,  81,  85,  73,  86,  83,  87, 100, 596, 593, 597,
+    594, 598, 595, 599,  84, 674, 593, 675, 596, 678, 666, 679, 116,
+    678, 737, 739, 626, 742, 741, 743,  76, 593, 593, 625, 585, 601,
+    601, 633, 108, 666, 737, 753, 602, 729, 745, 761,  92, 675, 737,
+    819, 604, 746, 822, 823, 124, 679, 826, 827, 634, 762, 830, 831,
+    19,  92,  83,  93,  90,  94,  91,  95, 293, 604, 601, 605, 602,
+    606, 603, 607, 101, 675, 595, 683, 659, 686, 667, 687, 302, 746,
+    745, 747, 748, 750, 749, 751, 108, 737, 666, 753, 602, 745, 729,
+    761, 310, 822, 822, 875, 876, 877, 877, 879, 229, 819, 741, 883,
+    748, 885, 886, 887, 311, 823, 830, 891, 892, 893, 894, 895,  21,
+    116,  85, 117, 106, 118, 115, 119, 228, 626, 625, 627, 626, 630,
+    627, 631,  99, 678, 597, 686, 661, 694, 670, 695, 230, 742, 753,
+    755, 756, 758, 757, 759,  92, 737, 675, 819, 604, 822, 746, 823,
+    229, 741, 819, 883, 748, 886, 885, 887, 220, 739, 739, 947, 732,
+    949, 949, 951, 231, 743, 827, 955, 764, 957, 958, 959,  23, 124,
+    87, 125, 122, 126, 123, 127, 295, 634, 633, 635, 634, 638, 635,
+    639, 103, 679, 599, 687, 663, 695, 671, 703, 303, 762, 761, 763,
+    764, 766, 765, 767, 124, 826, 679, 827, 634, 830, 762, 831, 311,
+    830, 823, 891, 892, 894, 893, 895, 231, 827, 743, 955, 764, 958,
+    957, 959, 319, 831, 831, 1019, 1020, 1021, 1021, 1023,   1,  18,  20,
+    22,  10,  19,  21,  23,  10,  73,  84,  86,  81,  83,  85,  87,
+    10,  76, 100, 108,  84,  92, 116, 124,  14,  77, 101, 109,  85,
+    93, 117, 125,   9,  73,  98, 106,  73,  90, 106, 122,  11,  75,
+    99, 107,  83,  91, 115, 123,  11,  77, 102, 110,  86,  94, 118,
+    126,  15,  79, 103, 111,  87,  95, 119, 127,  20, 100,  98, 102,
+    84, 101,  99, 103, 100, 594, 596, 598, 593, 595, 597, 599,  84,
+    593, 596, 666, 674, 675, 678, 679, 101, 595, 659, 667, 675, 683,
+    686, 687,  98, 596, 660, 661, 596, 659, 661, 663, 102, 598, 661,
+    669, 666, 667, 670, 671,  99, 597, 661, 670, 678, 686, 694, 695,
+    103, 599, 663, 671, 679, 687, 695, 703,  18, 292, 100, 293, 100,
+    293, 228, 295,  76, 585, 593, 601, 593, 601, 625, 633,  73, 585,
+    594, 602, 596, 604, 626, 634,  77, 587, 595, 603, 597, 605, 627,
+    635,  73, 585, 596, 604, 594, 602, 626, 634,  77, 587, 597, 605,
+    595, 603, 627, 635,  75, 587, 598, 606, 598, 606, 630, 638,  79,
+    591, 599, 607, 599, 607, 631, 639,  22, 293, 102, 301, 116, 302,
+    230, 303, 108, 602, 666, 729, 737, 745, 753, 761,  86, 601, 598,
+    729, 678, 746, 742, 762, 109, 603, 667, 731, 739, 747, 755, 763,
+    106, 604, 661, 732, 626, 748, 756, 764, 110, 606, 670, 733, 741,
+    749, 757, 765, 107, 605, 669, 733, 742, 750, 758, 766, 111, 607,
+    671, 735, 743, 751, 759, 767,  10, 100,  84, 116,  76, 108,  92,
+    124,  84, 596, 674, 678, 593, 666, 675, 679,  81, 593, 593, 737,
+    593, 737, 737, 826,  85, 597, 675, 739, 625, 753, 819, 827,  73,
+    594, 596, 626, 585, 602, 604, 634,  86, 598, 678, 742, 601, 729,
+    746, 762,  83, 595, 666, 741, 601, 745, 822, 830,  87, 599, 679,
+    743, 633, 761, 823, 831,  21, 228,  99, 230,  92, 229, 220, 231,
+    116, 626, 678, 742, 737, 741, 739, 743,  85, 625, 597, 753, 675,
+    819, 739, 827, 117, 627, 686, 755, 819, 883, 947, 955, 106, 626,
+    661, 756, 604, 748, 732, 764, 118, 630, 694, 758, 822, 886, 949,
+    957, 115, 627, 670, 757, 746, 885, 949, 958, 119, 631, 695, 759,
+    823, 887, 951, 959,  19, 293, 101, 302, 108, 310, 229, 311,  92,
+    604, 675, 746, 737, 822, 819, 823,  83, 601, 595, 745, 666, 822,
+    741, 830,  93, 605, 683, 747, 753, 875, 883, 891,  90, 602, 659,
+    748, 602, 876, 748, 892,  94, 606, 686, 750, 745, 877, 885, 893,
+    91, 603, 667, 749, 729, 877, 886, 894,  95, 607, 687, 751, 761,
+    879, 887, 895,  23, 295, 103, 303, 124, 311, 231, 319, 124, 634,
+    679, 762, 826, 830, 827, 831,  87, 633, 599, 761, 679, 823, 743,
+    831, 125, 635, 687, 763, 827, 891, 955, 1019, 122, 634, 663, 764,
+    634, 892, 764, 1020, 126, 638, 695, 766, 830, 894, 958, 1021, 123,
+    635, 671, 765, 762, 893, 957, 1021, 127, 639, 703, 767, 831, 895,
+    959, 1023,   3,  19,  21,  30,  11,  27,  29,  31,  19,  90,  92,
+    94,  83,  91,  93,  95,  21,  92, 228, 229,  99, 220, 230, 231,
+    30,  94, 229, 237, 115, 221, 238, 239,  11,  83,  99, 115,  75,
+    91, 107, 123,  27,  91, 220, 221,  91, 219, 221, 223,  29,  93,
+    230, 238, 107, 221, 246, 247,  31,  95, 231, 239, 123, 223, 247,
+    255,  22, 108, 106, 110,  86, 109, 107, 111, 293, 602, 604, 606,
+    601, 603, 605, 607, 116, 737, 626, 741, 678, 739, 742, 743, 302,
+    745, 748, 749, 746, 747, 750, 751, 102, 666, 661, 670, 598, 667,
+    669, 671, 301, 729, 732, 733, 729, 731, 733, 735, 230, 753, 756,
+    757, 742, 755, 758, 759, 303, 761, 764, 765, 762, 763, 766, 767,
+    22, 293, 116, 302, 102, 301, 230, 303, 108, 602, 737, 745, 666,
+    729, 753, 761, 106, 604, 626, 748, 661, 732, 756, 764, 110, 606,
+    741, 749, 670, 733, 757, 765,  86, 601, 678, 746, 598, 729, 742,
+    762, 109, 603, 739, 747, 667, 731, 755, 763, 107, 605, 742, 750,
+    669, 733, 758, 766, 111, 607, 743, 751, 671, 735, 759, 767,  54,
+    310, 118, 365, 118, 365, 246, 367, 310, 876, 822, 877, 822, 877,
+    875, 879, 118, 822, 630, 886, 694, 949, 758, 957, 365, 877, 886,
+    1755, 949, 1757, 1758, 1759, 118, 822, 694, 949, 630, 886, 758, 957,
+    365, 877, 949, 1757, 886, 1755, 1758, 1759, 246, 875, 758, 1758, 758,
+    1758, 1782, 1783, 367, 879, 957, 1759, 957, 1759, 1783, 1791,  14, 101,
+    85, 117,  77, 109,  93, 125, 101, 659, 675, 686, 595, 667, 683,
+    687,  85, 675, 625, 819, 597, 739, 753, 827, 117, 686, 819, 947,
+    627, 755, 883, 955,  77, 595, 597, 627, 587, 603, 605, 635, 109,
+    667, 739, 755, 603, 731, 747, 763,  93, 683, 753, 883, 605, 747,
+    875, 891, 125, 687, 827, 955, 635, 763, 891, 1019,  30, 229, 115,
+    238,  94, 237, 221, 239, 302, 748, 746, 750, 745, 749, 747, 751,
+    117, 819, 627, 883, 686, 947, 755, 955, 373, 885, 885, 1883, 885,
+    1883, 1883, 1887, 110, 741, 670, 757, 606, 749, 733, 765, 365, 886,
+    949, 1758, 877, 1755, 1757, 1759, 238, 883, 757, 1907, 750, 1883, 1758,
+    1911, 375, 887, 958, 1911, 893, 1917, 1918, 1919,  30, 302, 117, 373,
+    110, 365, 238, 375, 229, 748, 819, 885, 741, 886, 883, 887, 115,
+    746, 627, 885, 670, 949, 757, 958, 238, 750, 883, 1883, 757, 1758,
+    1907, 1911,  94, 745, 686, 885, 606, 877, 750, 893, 237, 749, 947,
+    1883, 749, 1755, 1883, 1917, 221, 747, 755, 1883, 733, 1757, 1758, 1918,
+    239, 751, 955, 1887, 765, 1759, 1911, 1919,  55, 311, 119, 375, 126,
+    382, 247, 383, 311, 892, 823, 893, 830, 894, 891, 895, 119, 823,
+    631, 887, 695, 951, 759, 959, 375, 893, 887, 1917, 958, 1918, 1911,
+    1919, 126, 830, 695, 958, 638, 894, 766, 1021, 382, 894, 951, 1918,
+    894, 2029, 1918, 2031, 247, 891, 759, 1911, 766, 1918, 1783, 2039, 383,
+    895, 959, 1919, 1021, 2031, 2039, 2047,   1,  20,  18,  22,  10,  21,
+    19,  23,  20,  98, 100, 102,  84,  99, 101, 103,  18, 100, 292,
+    293, 100, 228, 293, 295,  22, 102, 293, 301, 116, 230, 302, 303,
+    10,  84, 100, 116,  76,  92, 108, 124,  21,  99, 228, 230,  92,
+    220, 229, 231,  19, 101, 293, 302, 108, 229, 310, 311,  23, 103,
+    295, 303, 124, 231, 311, 319,  10,  84,  73,  86,  81,  85,  83,
+    87, 100, 596, 594, 598, 593, 597, 595, 599,  76, 593, 585, 601,
+    593, 625, 601, 633, 108, 666, 602, 729, 737, 753, 745, 761,  84,
+    674, 596, 678, 593, 675, 666, 679, 116, 678, 626, 742, 737, 739,
+    741, 743,  92, 675, 604, 746, 737, 819, 822, 823, 124, 679, 634,
+    762, 826, 827, 830, 831,  10, 100,  76, 108,  84, 116,  92, 124,
+    84, 596, 593, 666, 674, 678, 675, 679,  73, 594, 585, 602, 596,
+    626, 604, 634,  86, 598, 601, 729, 678, 742, 746, 762,  81, 593,
+    593, 737, 593, 737, 737, 826,  85, 597, 625, 753, 675, 739, 819,
+    827,  83, 595, 601, 745, 666, 741, 822, 830,  87, 599, 633, 761,
+    679, 743, 823, 831,  14, 101,  77, 109,  85, 117,  93, 125, 101,
+    659, 595, 667, 675, 686, 683, 687,  77, 595, 587, 603, 597, 627,
+    605, 635, 109, 667, 603, 731, 739, 755, 747, 763,  85, 675, 597,
+    739, 625, 819, 753, 827, 117, 686, 627, 755, 819, 947, 883, 955,
+    93, 683, 605, 747, 753, 883, 875, 891, 125, 687, 635, 763, 827,
+    955, 891, 1019,   9,  98,  73, 106,  73, 106,  90, 122,  98, 660,
+    596, 661, 596, 661, 659, 663,  73, 596, 585, 604, 594, 626, 602,
+    634, 106, 661, 604, 732, 626, 756, 748, 764,  73, 596, 594, 626,
+    585, 604, 602, 634, 106, 661, 626, 756, 604, 732, 748, 764,  90,
+    659, 602, 748, 602, 748, 876, 892, 122, 663, 634, 764, 634, 764,
+    892, 1020,  11,  99,  75, 107,  83, 115,  91, 123, 102, 661, 598,
+    669, 666, 670, 667, 671,  77, 597, 587, 605, 595, 627, 603, 635,
+    110, 670, 606, 733, 741, 757, 749, 765,  86, 678, 598, 742, 601,
+    746, 729, 762, 118, 694, 630, 758, 822, 949, 886, 957,  94, 686,
+    606, 750, 745, 885, 877, 893, 126, 695, 638, 766, 830, 958, 894,
+    1021,  11, 102,  77, 110,  86, 118,  94, 126,  99, 661, 597, 670,
+    678, 694, 686, 695,  75, 598, 587, 606, 598, 630, 606, 638, 107,
+    669, 605, 733, 742, 758, 750, 766,  83, 666, 595, 741, 601, 822,
+    745, 830, 115, 670, 627, 757, 746, 949, 885, 958,  91, 667, 603,
+    749, 729, 886, 877, 894, 123, 671, 635, 765, 762, 957, 893, 1021,
+    15, 103,  79, 111,  87, 119,  95, 127, 103, 663, 599, 671, 679,
+    695, 687, 703,  79, 599, 591, 607, 599, 631, 607, 639, 111, 671,
+    607, 735, 743, 759, 751, 767,  87, 679, 599, 743, 633, 823, 761,
+    831, 119, 695, 631, 759, 823, 951, 887, 959,  95, 687, 607, 751,
+    761, 887, 879, 895, 127, 703, 639, 767, 831, 959, 895, 1023,   3,
+    21,  19,  30,  11,  29,  27,  31,  22, 106, 108, 110,  86, 107,
+    109, 111,  22, 116, 293, 302, 102, 230, 301, 303,  54, 118, 310,
+    365, 118, 246, 365, 367,  14,  85, 101, 117,  77,  93, 109, 125,
+    30, 115, 229, 238,  94, 221, 237, 239,  30, 117, 302, 373, 110,
+    238, 365, 375,  55, 119, 311, 375, 126, 247, 382, 383,  19,  92,
+    90,  94,  83,  93,  91,  95, 293, 604, 602, 606, 601, 605, 603,
+    607, 108, 737, 602, 745, 666, 753, 729, 761, 310, 822, 876, 877,
+    822, 875, 877, 879, 101, 675, 659, 686, 595, 683, 667, 687, 302,
+    746, 748, 750, 745, 747, 749, 751, 229, 819, 748, 885, 741, 883,
+    886, 887, 311, 823, 892, 893, 830, 891, 894, 895,  21, 228,  92,
+    229,  99, 230, 220, 231, 116, 626, 737, 741, 678, 742, 739, 743,
+    106, 626, 604, 748, 661, 756, 732, 764, 118, 630, 822, 886, 694,
+    758, 949, 957,  85, 625, 675, 819, 597, 753, 739, 827, 117, 627,
+    819, 883, 686, 755, 947, 955, 115, 627, 746, 885, 670, 757, 949,
+    958, 119, 631, 823, 887, 695, 759, 951, 959,  30, 229,  94, 237,
+    115, 238, 221, 239, 302, 748, 745, 749, 746, 750, 747, 751, 110,
+    741, 606, 749, 670, 757, 733, 765, 365, 886, 877, 1755, 949, 1758,
+    1757, 1759, 117, 819, 686, 947, 627, 883, 755, 955, 373, 885, 885,
+    1883, 885, 1883, 1883, 1887, 238, 883, 750, 1883, 757, 1907, 1758, 1911,
+    375, 887, 893, 1917, 958, 1911, 1918, 1919,  11,  99,  83, 115,  75,
+    107,  91, 123, 102, 661, 666, 670, 598, 669, 667, 671,  86, 678,
+    601, 746, 598, 742, 729, 762, 118, 694, 822, 949, 630, 758, 886,
+    957,  77, 597, 595, 627, 587, 605, 603, 635, 110, 670, 741, 757,
+    606, 733, 749, 765,  94, 686, 745, 885, 606, 750, 877, 893, 126,
+    695, 830, 958, 638, 766, 894, 1021,  27, 220,  91, 221,  91, 221,
+    219, 223, 301, 732, 729, 733, 729, 733, 731, 735, 109, 739, 603,
+    747, 667, 755, 731, 763, 365, 949, 877, 1757, 886, 1758, 1755, 1759,
+    109, 739, 667, 755, 603, 747, 731, 763, 365, 949, 886, 1758, 877,
+    1757, 1755, 1759, 237, 947, 749, 1883, 749, 1883, 1755, 1917, 382, 951,
+    894, 1918, 894, 1918, 2029, 2031,  29, 230,  93, 238, 107, 246, 221,
+    247, 230, 756, 753, 757, 742, 758, 755, 759, 107, 742, 605, 750,
+    669, 758, 733, 766, 246, 758, 875, 1758, 758, 1782, 1758, 1783,  93,
+    753, 683, 883, 605, 875, 747, 891, 238, 757, 883, 1907, 750, 1758,
+    1883, 1911, 221, 755, 747, 1883, 733, 1758, 1757, 1918, 247, 759, 891,
+    1911, 766, 1783, 1918, 2039,  31, 231,  95, 239, 123, 247, 223, 255,
+    303, 764, 761, 765, 762, 766, 763, 767, 111, 743, 607, 751, 671,
+    759, 735, 767, 367, 957, 879, 1759, 957, 1783, 1759, 1791, 125, 827,
+    687, 955, 635, 891, 763, 1019, 375, 958, 887, 1911, 893, 1918, 1917,
+    1919, 239, 955, 751, 1887, 765, 1911, 1759, 1919, 383, 959, 895, 1919,
+    1021, 2039, 2031, 2047,   3,  22,  22,  54,  14,  30,  30,  55,  21,
+    106, 116, 118,  85, 115, 117, 119,  19, 108, 293, 310, 101, 229,
+    302, 311,  30, 110, 302, 365, 117, 238, 373, 375,  11,  86, 102,
+    118,  77,  94, 110, 126,  29, 107, 230, 246,  93, 221, 238, 247,
+    27, 109, 301, 365, 109, 237, 365, 382,  31, 111, 303, 367, 125,
+    239, 375, 383,  21, 116, 106, 118,  85, 117, 115, 119, 228, 626,
+    626, 630, 625, 627, 627, 631,  92, 737, 604, 822, 675, 819, 746,
+    823, 229, 741, 748, 886, 819, 883, 885, 887,  99, 678, 661, 694,
+    597, 686, 670, 695, 230, 742, 756, 758, 753, 755, 757, 759, 220,
+    739, 732, 949, 739, 947, 949, 951, 231, 743, 764, 957, 827, 955,
+    958, 959,  19, 293, 108, 310, 101, 302, 229, 311,  92, 604, 737,
+    822, 675, 746, 819, 823,  90, 602, 602, 876, 659, 748, 748, 892,
+    94, 606, 745, 877, 686, 750, 885, 893,  83, 601, 666, 822, 595,
+    745, 741, 830,  93, 605, 753, 875, 683, 747, 883, 891,  91, 603,
+    729, 877, 667, 749, 886, 894,  95, 607, 761, 879, 687, 751, 887,
+    895,  30, 302, 110, 365, 117, 373, 238, 375, 229, 748, 741, 886,
+    819, 885, 883, 887,  94, 745, 606, 877, 686, 885, 750, 893, 237,
+    749, 749, 1755, 947, 1883, 1883, 1917, 115, 746, 670, 949, 627, 885,
+    757, 958, 238, 750, 757, 1758, 883, 1883, 1907, 1911, 221, 747, 733,
+    1757, 755, 1883, 1758, 1918, 239, 751, 765, 1759, 955, 1887, 1911, 1919,
+    11, 102,  86, 118,  77, 110,  94, 126,  99, 661, 678, 694, 597,
+    670, 686, 695,  83, 666, 601, 822, 595, 741, 745, 830, 115, 670,
+    746, 949, 627, 757, 885, 958,  75, 598, 598, 630, 587, 606, 606,
+    638, 107, 669, 742, 758, 605, 733, 750, 766,  91, 667, 729, 886,
+    603, 749, 877, 894, 123, 671, 762, 957, 635, 765, 893, 1021,  29,
+    230, 107, 246,  93, 238, 221, 247, 230, 756, 742, 758, 753, 757,
+    755, 759,  93, 753, 605, 875, 683, 883, 747, 891, 238, 757, 750,
+    1758, 883, 1907, 1883, 1911, 107, 742, 669, 758, 605, 750, 733, 766,
+    246, 758, 758, 1782, 875, 1758, 1758, 1783, 221, 755, 733, 1758, 747,
+    1883, 1757, 1918, 247, 759, 766, 1783, 891, 1911, 1918, 2039,  27, 301,
+    109, 365, 109, 365, 237, 382, 220, 732, 739, 949, 739, 949, 947,
+    951,  91, 729, 603, 877, 667, 886, 749, 894, 221, 733, 747, 1757,
+    755, 1758, 1883, 1918,  91, 729, 667, 886, 603, 877, 749, 894, 221,
+    733, 755, 1758, 747, 1757, 1883, 1918, 219, 731, 731, 1755, 731, 1755,
+    1755, 2029, 223, 735, 763, 1759, 763, 1759, 1917, 2031,  31, 303, 111,
+    367, 125, 375, 239, 383, 231, 764, 743, 957, 827, 958, 955, 959,
+    95, 761, 607, 879, 687, 887, 751, 895, 239, 765, 751, 1759, 955,
+    1911, 1887, 1919, 123, 762, 671, 957, 635, 893, 765, 1021, 247, 766,
+    759, 1783, 891, 1918, 1911, 2039, 223, 763, 735, 1759, 763, 1917, 1759,
+    2031, 255, 767, 767, 1791, 1019, 1919, 1919, 2047,   7,  23,  23,  55,
+    15,  31,  31,  63,  23, 122, 124, 126,  87, 123, 125, 127,  23,
+    124, 295, 311, 103, 231, 303, 319,  55, 126, 311, 382, 119, 247,
+    375, 383,  15,  87, 103, 119,  79,  95, 111, 127,  31, 123, 231,
+    247,  95, 223, 239, 255,  31, 125, 303, 375, 111, 239, 367, 383,
+    63, 127, 319, 383, 127, 255, 383, 511,  23, 124, 122, 126,  87,
+    125, 123, 127, 295, 634, 634, 638, 633, 635, 635, 639, 124, 826,
+    634, 830, 679, 827, 762, 831, 311, 830, 892, 894, 823, 891, 893,
+    895, 103, 679, 663, 695, 599, 687, 671, 703, 303, 762, 764, 766,
+    761, 763, 765, 767, 231, 827, 764, 958, 743, 955, 957, 959, 319,
+    831, 1020, 1021, 831, 1019, 1021, 1023,  23, 295, 124, 311, 103, 303,
+    231, 319, 124, 634, 826, 830, 679, 762, 827, 831, 122, 634, 634,
+    892, 663, 764, 764, 1020, 126, 638, 830, 894, 695, 766, 958, 1021,
+    87, 633, 679, 823, 599, 761, 743, 831, 125, 635, 827, 891, 687,
+    763, 955, 1019, 123, 635, 762, 893, 671, 765, 957, 1021, 127, 639,
+    831, 895, 703, 767, 959, 1023,  55, 311, 126, 382, 119, 375, 247,
+    383, 311, 892, 830, 894, 823, 893, 891, 895, 126, 830, 638, 894,
+    695, 958, 766, 1021, 382, 894, 894, 2029, 951, 1918, 1918, 2031, 119,
+    823, 695, 951, 631, 887, 759, 959, 375, 893, 958, 1918, 887, 1917,
+    1911, 1919, 247, 891, 766, 1918, 759, 1911, 1783, 2039, 383, 895, 1021,
+    2031, 959, 1919, 2039, 2047,  15, 103,  87, 119,  79, 111,  95, 127,
+    103, 663, 679, 695, 599, 671, 687, 703,  87, 679, 633, 823, 599,
+    743, 761, 831, 119, 695, 823, 951, 631, 759, 887, 959,  79, 599,
+    599, 631, 591, 607, 607, 639, 111, 671, 743, 759, 607, 735, 751,
+    767,  95, 687, 761, 887, 607, 751, 879, 895, 127, 703, 831, 959,
+    639, 767, 895, 1023,  31, 231, 123, 247,  95, 239, 223, 255, 303,
+    764, 762, 766, 761, 765, 763, 767, 125, 827, 635, 891, 687, 955,
+    763, 1019, 375, 958, 893, 1918, 887, 1911, 1917, 1919, 111, 743, 671,
+    759, 607, 751, 735, 767, 367, 957, 957, 1783, 879, 1759, 1759, 1791,
+    239, 955, 765, 1911, 751, 1887, 1759, 1919, 383, 959, 1021, 2039, 895,
+    1919, 2031, 2047,  31, 303, 125, 375, 111, 367, 239, 383, 231, 764,
+    827, 958, 743, 957, 955, 959, 123, 762, 635, 893, 671, 957, 765,
+    1021, 247, 766, 891, 1918, 759, 1783, 1911, 2039,  95, 761, 687, 887,
+    607, 879, 751, 895, 239, 765, 955, 1911, 751, 1759, 1887, 1919, 223,
+    763, 763, 1917, 735, 1759, 1759, 2031, 255, 767, 1019, 1919, 767, 1791,
+    1919, 2047,  63, 319, 127, 383, 127, 383, 255, 511, 319, 1020, 831,
+    1021, 831, 1021, 1019, 1023, 127, 831, 639, 895, 703, 959, 767, 1023,
+    383, 1021, 895, 2031, 959, 2039, 1919, 2047, 127, 831, 703, 959, 639,
+    895, 767, 1023, 383, 1021, 959, 2039, 895, 2031, 1919, 2047, 255, 1019,
+    767, 1919, 767, 1919, 1791, 2047, 511, 1023, 1023, 2047, 1023, 2047, 2047,
+    4095
+};
+
+const unsigned int igraph_i_isoclass_4_idx[] = {
+    0, 8, 64, 512, 1, 0, 128, 1024, 2, 16, 0, 2048, 4, 32, 256, 0
+};
+
+const unsigned int igraph_i_isoclass_3u[] = { 0, 1, 1, 3, 1, 3, 3, 7 };
+
+const unsigned int igraph_i_isoclass_3u_idx[] = { 0, 1, 2, 1, 0, 4, 2, 4, 0 };
+
+const unsigned int igraph_i_isoclass_4u[] = {
+    0, 1, 1, 3, 1, 3, 3, 7, 1, 3, 3, 11, 12, 13, 13, 15, 1, 3, 12, 13, 3, 11, 13, 15, 3, 7,
+    13, 15, 13, 15, 30, 31, 1, 12, 3, 13, 3, 13, 11, 15, 3, 13, 7, 15, 13, 30, 15, 31, 3, 13, 13, 30,
+    7, 15, 15, 31, 11, 15, 15, 31, 15, 31, 31, 63
+};
+
+const unsigned int igraph_i_isoclass_4u_idx[] = {
+    0, 1, 2, 8, 1, 0, 4, 16, 2, 4, 0, 32, 8, 16, 32, 0
+};
+
+const unsigned int igraph_i_isoclass2_3[] = {
+    0, 1, 1, 2, 1, 3, 4, 5, 1, 4, 6, 7, 2, 5, 7, 8, 1, 4, 3, 5, 6, 9, 9, 10, 4, 11,
+    9, 12, 7, 12, 13, 14, 1, 6, 4, 7, 4, 9, 11, 12, 3, 9, 9, 13, 5, 10, 12, 14, 2, 7, 5, 8,
+    7, 13, 12, 14, 5, 12, 10, 14, 8, 14, 14, 15
+};
+
+const unsigned int igraph_i_isoclass2_3u[] = {
+    0, 1, 1, 2, 1, 2, 2, 3
+};
+
+const unsigned int igraph_i_isoclass2_4u[] = {
+    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 4, 5, 6, 6, 7, 1, 2, 5, 6, 2, 4, 6, 7, 2, 3,
+    6, 7, 6, 7, 8, 9, 1, 5, 2, 6, 2, 6, 4, 7, 2, 6, 3, 7, 6, 8, 7, 9, 2, 6, 6, 8,
+    3, 7, 7, 9, 4, 7, 7, 9, 7, 9, 9, 10
+};
+
+const unsigned int igraph_i_isoclass2_4[] = {
+    0,  1,  1,  2,  1,  2,  2,  3,  1,  4,  5,  6,  5,  6,  7,  8,  1,  5,  9, 10,
+    11, 12, 13, 14,  2,  6, 10, 15, 12, 16, 17, 18,  1,  5, 11, 12,  9, 10, 13, 14,
+    2,  6, 12, 16, 10, 15, 17, 18,  2,  7, 13, 17, 13, 17, 19, 20,  3,  8, 14, 18,
+    14, 18, 20, 21,  1,  5,  4,  6,  5,  7,  6,  8,  9, 22, 22, 23, 24, 25, 25, 26,
+    5, 27, 22, 28, 29, 30, 31, 32, 10, 28, 33, 34, 35, 36, 37, 38, 11, 29, 39, 40,
+    41, 42, 43, 44, 13, 31, 45, 46, 47, 48, 49, 50, 12, 30, 45, 51, 52, 53, 54, 55,
+    14, 32, 56, 57, 58, 59, 60, 61,  1,  9,  5, 10, 11, 13, 12, 14,  5, 22, 27, 28,
+    29, 31, 30, 32,  4, 22, 22, 33, 39, 45, 45, 56,  6, 23, 28, 34, 40, 46, 51, 57,
+    5, 24, 29, 35, 41, 47, 52, 58,  7, 25, 30, 36, 42, 48, 53, 59,  6, 25, 31, 37,
+    43, 49, 54, 60,  8, 26, 32, 38, 44, 50, 55, 61,  2, 10,  6, 15, 12, 17, 16, 18,
+    10, 33, 28, 34, 35, 37, 36, 38,  6, 28, 23, 34, 40, 51, 46, 57, 15, 34, 34, 62,
+    63, 64, 64, 65, 12, 35, 40, 63, 66, 67, 68, 69, 17, 37, 51, 64, 67, 70, 71, 72,
+    16, 36, 46, 64, 68, 71, 73, 74, 18, 38, 57, 65, 69, 72, 74, 75,  1, 11,  5, 12,
+    9, 13, 10, 14, 11, 39, 29, 40, 41, 43, 42, 44,  5, 29, 24, 35, 41, 52, 47, 58,
+    12, 40, 35, 63, 66, 68, 67, 69,  9, 41, 41, 66, 76, 77, 77, 78, 13, 43, 52, 68,
+    77, 79, 80, 81, 10, 42, 47, 67, 77, 80, 82, 83, 14, 44, 58, 69, 78, 81, 83, 84,
+    2, 12,  6, 16, 10, 17, 15, 18, 13, 45, 31, 46, 47, 49, 48, 50,  7, 30, 25, 36,
+    42, 53, 48, 59, 17, 51, 37, 64, 67, 71, 70, 72, 13, 52, 43, 68, 77, 80, 79, 81,
+    19, 54, 54, 73, 82, 85, 85, 86, 17, 53, 49, 71, 80, 87, 85, 88, 20, 55, 60, 74,
+    83, 88, 89, 90,  2, 13,  7, 17, 13, 19, 17, 20, 12, 45, 30, 51, 52, 54, 53, 55,
+    6, 31, 25, 37, 43, 54, 49, 60, 16, 46, 36, 64, 68, 73, 71, 74, 10, 47, 42, 67,
+    77, 82, 80, 83, 17, 49, 53, 71, 80, 85, 87, 88, 15, 48, 48, 70, 79, 85, 85, 89,
+    18, 50, 59, 72, 81, 86, 88, 90,  3, 14,  8, 18, 14, 20, 18, 21, 14, 56, 32, 57,
+    58, 60, 59, 61,  8, 32, 26, 38, 44, 55, 50, 61, 18, 57, 38, 65, 69, 74, 72, 75,
+    14, 58, 44, 69, 78, 83, 81, 84, 20, 60, 55, 74, 83, 89, 88, 90, 18, 59, 50, 72,
+    81, 88, 86, 90, 21, 61, 61, 75, 84, 90, 90, 91,  1,  5,  5,  7,  4,  6,  6,  8,
+    9, 22, 24, 25, 22, 23, 25, 26, 11, 29, 41, 42, 39, 40, 43, 44, 13, 31, 47, 48,
+    45, 46, 49, 50,  5, 27, 29, 30, 22, 28, 31, 32, 10, 28, 35, 36, 33, 34, 37, 38,
+    12, 30, 52, 53, 45, 51, 54, 55, 14, 32, 58, 59, 56, 57, 60, 61,  9, 24, 22, 25,
+    22, 25, 23, 26, 76, 92, 92, 93, 92, 93, 93, 94, 41, 95, 96, 97, 98, 99, 100, 101,
+    77, 102, 103, 104, 105, 106, 107, 108, 41, 95, 98, 99, 96, 97, 100, 101, 77, 102, 105, 106,
+    103, 104, 107, 108, 66, 109, 110, 111, 110, 111, 112, 113, 78, 114, 115, 116, 115, 116, 117, 118,
+    11, 41, 29, 42, 39, 43, 40, 44, 41, 96, 95, 97, 98, 100, 99, 101, 39, 98, 98, 119,
+    120, 121, 121, 122, 43, 100, 123, 124, 121, 125, 126, 127, 29, 95, 128, 129, 98, 123, 130, 131,
+    42, 97, 129, 132, 119, 124, 133, 134, 40, 99, 130, 133, 121, 126, 135, 136, 44, 101, 131, 134,
+    122, 127, 136, 137, 13, 47, 31, 48, 45, 49, 46, 50, 77, 103, 102, 104, 105, 107, 106, 108,
+    43, 123, 100, 124, 121, 126, 125, 127, 79, 138, 138, 139, 140, 141, 141, 142, 52, 143, 130, 144,
+    110, 145, 146, 147, 80, 148, 149, 150, 151, 152, 153, 154, 68, 155, 146, 156, 157, 158, 159, 160,
+    81, 161, 162, 163, 164, 165, 166, 167,  5, 29, 27, 30, 22, 31, 28, 32, 41, 98, 95, 99,
+    96, 100, 97, 101, 29, 128, 95, 129, 98, 130, 123, 131, 52, 130, 143, 144, 110, 146, 145, 147,
+    24, 95, 95, 109, 92, 102, 102, 114, 47, 123, 143, 155, 103, 138, 148, 161, 35, 129, 143, 168,
+    105, 149, 169, 170, 58, 131, 171, 172, 115, 162, 173, 174, 10, 35, 28, 36, 33, 37, 34, 38,
+    77, 105, 102, 106, 103, 107, 104, 108, 42, 129, 97, 132, 119, 133, 124, 134, 80, 149, 148, 150,
+    151, 153, 152, 154, 47, 143, 123, 155, 103, 148, 138, 161, 82, 169, 169, 175, 176, 177, 177, 178,
+    67, 168, 145, 179, 151, 180, 181, 182, 83, 170, 173, 183, 184, 185, 186, 187, 12, 52, 30, 53,
+    45, 54, 51, 55, 66, 110, 109, 111, 110, 112, 111, 113, 40, 130, 99, 133, 121, 135, 126, 136,
+    68, 146, 155, 156, 157, 159, 158, 160, 35, 143, 129, 168, 105, 169, 149, 170, 67, 145, 168, 179,
+    151, 181, 180, 182, 63, 144, 144, 188, 140, 189, 189, 190, 69, 147, 172, 191, 164, 192, 193, 194,
+    14, 58, 32, 59, 56, 60, 57, 61, 78, 115, 114, 116, 115, 117, 116, 118, 44, 131, 101, 134,
+    122, 136, 127, 137, 81, 162, 161, 163, 164, 166, 165, 167, 58, 171, 131, 172, 115, 173, 162, 174,
+    83, 173, 170, 183, 184, 186, 185, 187, 69, 172, 147, 191, 164, 193, 192, 194, 84, 174, 174, 195,
+    196, 197, 197, 198,  1,  9, 11, 13,  5, 10, 12, 14,  5, 22, 29, 31, 27, 28, 30, 32,
+    5, 24, 41, 47, 29, 35, 52, 58,  7, 25, 42, 48, 30, 36, 53, 59,  4, 22, 39, 45,
+    22, 33, 45, 56,  6, 23, 40, 46, 28, 34, 51, 57,  6, 25, 43, 49, 31, 37, 54, 60,
+    8, 26, 44, 50, 32, 38, 55, 61, 11, 41, 39, 43, 29, 42, 40, 44, 41, 96, 98, 100,
+    95, 97, 99, 101, 29, 95, 98, 123, 128, 129, 130, 131, 42, 97, 119, 124, 129, 132, 133, 134,
+    39, 98, 120, 121, 98, 119, 121, 122, 43, 100, 121, 125, 123, 124, 126, 127, 40, 99, 121, 126,
+    130, 133, 135, 136, 44, 101, 122, 127, 131, 134, 136, 137,  9, 76, 41, 77, 41, 77, 66, 78,
+    24, 92, 95, 102, 95, 102, 109, 114, 22, 92, 96, 103, 98, 105, 110, 115, 25, 93, 97, 104,
+    99, 106, 111, 116, 22, 92, 98, 105, 96, 103, 110, 115, 25, 93, 99, 106, 97, 104, 111, 116,
+    23, 93, 100, 107, 100, 107, 112, 117, 26, 94, 101, 108, 101, 108, 113, 118, 13, 77, 43, 79,
+    52, 80, 68, 81, 47, 103, 123, 138, 143, 148, 155, 161, 31, 102, 100, 138, 130, 149, 146, 162,
+    48, 104, 124, 139, 144, 150, 156, 163, 45, 105, 121, 140, 110, 151, 157, 164, 49, 107, 126, 141,
+    145, 152, 158, 165, 46, 106, 125, 141, 146, 153, 159, 166, 50, 108, 127, 142, 147, 154, 160, 167,
+    5, 41, 29, 52, 24, 47, 35, 58, 29, 98, 128, 130, 95, 123, 129, 131, 27, 95, 95, 143,
+    95, 143, 143, 171, 30, 99, 129, 144, 109, 155, 168, 172, 22, 96, 98, 110, 92, 103, 105, 115,
+    31, 100, 130, 146, 102, 138, 149, 162, 28, 97, 123, 145, 102, 148, 169, 173, 32, 101, 131, 147,
+    114, 161, 170, 174, 12, 66, 40, 68, 35, 67, 63, 69, 52, 110, 130, 146, 143, 145, 144, 147,
+    30, 109, 99, 155, 129, 168, 144, 172, 53, 111, 133, 156, 168, 179, 188, 191, 45, 110, 121, 157,
+    105, 151, 140, 164, 54, 112, 135, 159, 169, 181, 189, 192, 51, 111, 126, 158, 149, 180, 189, 193,
+    55, 113, 136, 160, 170, 182, 190, 194, 10, 77, 42, 80, 47, 82, 67, 83, 35, 105, 129, 149,
+    143, 169, 168, 170, 28, 102, 97, 148, 123, 169, 145, 173, 36, 106, 132, 150, 155, 175, 179, 183,
+    33, 103, 119, 151, 103, 176, 151, 184, 37, 107, 133, 153, 148, 177, 180, 185, 34, 104, 124, 152,
+    138, 177, 181, 186, 38, 108, 134, 154, 161, 178, 182, 187, 14, 78, 44, 81, 58, 83, 69, 84,
+    58, 115, 131, 162, 171, 173, 172, 174, 32, 114, 101, 161, 131, 170, 147, 174, 59, 116, 134, 163,
+    172, 183, 191, 195, 56, 115, 122, 164, 115, 184, 164, 196, 60, 117, 136, 166, 173, 186, 193, 197,
+    57, 116, 127, 165, 162, 185, 192, 197, 61, 118, 137, 167, 174, 187, 194, 198,  2, 10, 12, 17,
+    6, 15, 16, 18, 10, 33, 35, 37, 28, 34, 36, 38, 12, 35, 66, 67, 40, 63, 68, 69,
+    17, 37, 67, 70, 51, 64, 71, 72,  6, 28, 40, 51, 23, 34, 46, 57, 15, 34, 63, 64,
+    34, 62, 64, 65, 16, 36, 68, 71, 46, 64, 73, 74, 18, 38, 69, 72, 57, 65, 74, 75,
+    13, 47, 45, 49, 31, 48, 46, 50, 77, 103, 105, 107, 102, 104, 106, 108, 52, 143, 110, 145,
+    130, 144, 146, 147, 80, 148, 151, 152, 149, 150, 153, 154, 43, 123, 121, 126, 100, 124, 125, 127,
+    79, 138, 140, 141, 138, 139, 141, 142, 68, 155, 157, 158, 146, 156, 159, 160, 81, 161, 164, 165,
+    162, 163, 166, 167, 13, 77, 52, 80, 43, 79, 68, 81, 47, 103, 143, 148, 123, 138, 155, 161,
+    45, 105, 110, 151, 121, 140, 157, 164, 49, 107, 145, 152, 126, 141, 158, 165, 31, 102, 130, 149,
+    100, 138, 146, 162, 48, 104, 144, 150, 124, 139, 156, 163, 46, 106, 146, 153, 125, 141, 159, 166,
+    50, 108, 147, 154, 127, 142, 160, 167, 19, 82, 54, 85, 54, 85, 73, 86, 82, 176, 169, 177,
+    169, 177, 175, 178, 54, 169, 112, 181, 135, 189, 159, 192, 85, 177, 181, 199, 189, 200, 201, 202,
+    54, 169, 135, 189, 112, 181, 159, 192, 85, 177, 189, 200, 181, 199, 201, 202, 73, 175, 159, 201,
+    159, 201, 203, 204, 86, 178, 192, 202, 192, 202, 204, 205,  7, 42, 30, 53, 25, 48, 36, 59,
+    42, 119, 129, 133, 97, 124, 132, 134, 30, 129, 109, 168, 99, 144, 155, 172, 53, 133, 168, 188,
+    111, 156, 179, 191, 25, 97, 99, 111, 93, 104, 106, 116, 48, 124, 144, 156, 104, 139, 150, 163,
+    36, 132, 155, 179, 106, 150, 175, 183, 59, 134, 172, 191, 116, 163, 183, 195, 17, 67, 51, 71,
+    37, 70, 64, 72, 80, 151, 149, 153, 148, 152, 150, 154, 53, 168, 111, 179, 133, 188, 156, 191,
+    87, 180, 180, 206, 180, 206, 206, 207, 49, 145, 126, 158, 107, 152, 141, 165, 85, 181, 189, 201,
+    177, 199, 200, 202, 71, 179, 158, 208, 153, 206, 201, 209, 88, 182, 193, 209, 185, 210, 211, 212,
+    17, 80, 53, 87, 49, 85, 71, 88, 67, 151, 168, 180, 145, 181, 179, 182, 51, 149, 111, 180,
+    126, 189, 158, 193, 71, 153, 179, 206, 158, 201, 208, 209, 37, 148, 133, 180, 107, 177, 153, 185,
+    70, 152, 188, 206, 152, 199, 206, 210, 64, 150, 156, 206, 141, 200, 201, 211, 72, 154, 191, 207,
+    165, 202, 209, 212, 20, 83, 55, 88, 60, 89, 74, 90, 83, 184, 170, 185, 173, 186, 183, 187,
+    55, 170, 113, 182, 136, 190, 160, 194, 88, 185, 182, 210, 193, 211, 209, 212, 60, 173, 136, 193,
+    117, 186, 166, 197, 89, 186, 190, 211, 186, 213, 211, 214, 74, 183, 160, 209, 166, 211, 204, 215,
+    90, 187, 194, 212, 197, 214, 215, 216,  1, 11,  9, 13,  5, 12, 10, 14, 11, 39, 41, 43,
+    29, 40, 42, 44,  9, 41, 76, 77, 41, 66, 77, 78, 13, 43, 77, 79, 52, 68, 80, 81,
+    5, 29, 41, 52, 24, 35, 47, 58, 12, 40, 66, 68, 35, 63, 67, 69, 10, 42, 77, 80,
+    47, 67, 82, 83, 14, 44, 78, 81, 58, 69, 83, 84,  5, 29, 22, 31, 27, 30, 28, 32,
+    41, 98, 96, 100, 95, 99, 97, 101, 24, 95, 92, 102, 95, 109, 102, 114, 47, 123, 103, 138,
+    143, 155, 148, 161, 29, 128, 98, 130, 95, 129, 123, 131, 52, 130, 110, 146, 143, 144, 145, 147,
+    35, 129, 105, 149, 143, 168, 169, 170, 58, 131, 115, 162, 171, 172, 173, 174,  5, 41, 24, 47,
+    29, 52, 35, 58, 29, 98, 95, 123, 128, 130, 129, 131, 22, 96, 92, 103, 98, 110, 105, 115,
+    31, 100, 102, 138, 130, 146, 149, 162, 27, 95, 95, 143, 95, 143, 143, 171, 30, 99, 109, 155,
+    129, 144, 168, 172, 28, 97, 102, 148, 123, 145, 169, 173, 32, 101, 114, 161, 131, 147, 170, 174,
+    7, 42, 25, 48, 30, 53, 36, 59, 42, 119, 97, 124, 129, 133, 132, 134, 25, 97, 93, 104,
+    99, 111, 106, 116, 48, 124, 104, 139, 144, 156, 150, 163, 30, 129, 99, 144, 109, 168, 155, 172,
+    53, 133, 111, 156, 168, 188, 179, 191, 36, 132, 106, 150, 155, 179, 175, 183, 59, 134, 116, 163,
+    172, 191, 183, 195,  4, 39, 22, 45, 22, 45, 33, 56, 39, 120, 98, 121, 98, 121, 119, 122,
+    22, 98, 92, 105, 96, 110, 103, 115, 45, 121, 105, 140, 110, 157, 151, 164, 22, 98, 96, 110,
+    92, 105, 103, 115, 45, 121, 110, 157, 105, 140, 151, 164, 33, 119, 103, 151, 103, 151, 176, 184,
+    56, 122, 115, 164, 115, 164, 184, 196,  6, 40, 23, 46, 28, 51, 34, 57, 43, 121, 100, 125,
+    123, 126, 124, 127, 25, 99, 93, 106, 97, 111, 104, 116, 49, 126, 107, 141, 145, 158, 152, 165,
+    31, 130, 100, 146, 102, 149, 138, 162, 54, 135, 112, 159, 169, 189, 181, 192, 37, 133, 107, 153,
+    148, 180, 177, 185, 60, 136, 117, 166, 173, 193, 186, 197,  6, 43, 25, 49, 31, 54, 37, 60,
+    40, 121, 99, 126, 130, 135, 133, 136, 23, 100, 93, 107, 100, 112, 107, 117, 46, 125, 106, 141,
+    146, 159, 153, 166, 28, 123, 97, 145, 102, 169, 148, 173, 51, 126, 111, 158, 149, 189, 180, 193,
+    34, 124, 104, 152, 138, 181, 177, 186, 57, 127, 116, 165, 162, 192, 185, 197,  8, 44, 26, 50,
+    32, 55, 38, 61, 44, 122, 101, 127, 131, 136, 134, 137, 26, 101, 94, 108, 101, 113, 108, 118,
+    50, 127, 108, 142, 147, 160, 154, 167, 32, 131, 101, 147, 114, 170, 161, 174, 55, 136, 113, 160,
+    170, 190, 182, 194, 38, 134, 108, 154, 161, 182, 178, 187, 61, 137, 118, 167, 174, 194, 187, 198,
+    2, 12, 10, 17,  6, 16, 15, 18, 13, 45, 47, 49, 31, 46, 48, 50, 13, 52, 77, 80,
+    43, 68, 79, 81, 19, 54, 82, 85, 54, 73, 85, 86,  7, 30, 42, 53, 25, 36, 48, 59,
+    17, 51, 67, 71, 37, 64, 70, 72, 17, 53, 80, 87, 49, 71, 85, 88, 20, 55, 83, 88,
+    60, 74, 89, 90, 10, 35, 33, 37, 28, 36, 34, 38, 77, 105, 103, 107, 102, 106, 104, 108,
+    47, 143, 103, 148, 123, 155, 138, 161, 82, 169, 176, 177, 169, 175, 177, 178, 42, 129, 119, 133,
+    97, 132, 124, 134, 80, 149, 151, 153, 148, 150, 152, 154, 67, 168, 151, 180, 145, 179, 181, 182,
+    83, 170, 184, 185, 173, 183, 186, 187, 12, 66, 35, 67, 40, 68, 63, 69, 52, 110, 143, 145,
+    130, 146, 144, 147, 45, 110, 105, 151, 121, 157, 140, 164, 54, 112, 169, 181, 135, 159, 189, 192,
+    30, 109, 129, 168, 99, 155, 144, 172, 53, 111, 168, 179, 133, 156, 188, 191, 51, 111, 149, 180,
+    126, 158, 189, 193, 55, 113, 170, 182, 136, 160, 190, 194, 17, 67, 37, 70, 51, 71, 64, 72,
+    80, 151, 148, 152, 149, 153, 150, 154, 49, 145, 107, 152, 126, 158, 141, 165, 85, 181, 177, 199,
+    189, 201, 200, 202, 53, 168, 133, 188, 111, 179, 156, 191, 87, 180, 180, 206, 180, 206, 206, 207,
+    71, 179, 153, 206, 158, 208, 201, 209, 88, 182, 185, 210, 193, 209, 211, 212,  6, 40, 28, 51,
+    23, 46, 34, 57, 43, 121, 123, 126, 100, 125, 124, 127, 31, 130, 102, 149, 100, 146, 138, 162,
+    54, 135, 169, 189, 112, 159, 181, 192, 25, 99, 97, 111, 93, 106, 104, 116, 49, 126, 145, 158,
+    107, 141, 152, 165, 37, 133, 148, 180, 107, 153, 177, 185, 60, 136, 173, 193, 117, 166, 186, 197,
+    15, 63, 34, 64, 34, 64, 62, 65, 79, 140, 138, 141, 138, 141, 139, 142, 48, 144, 104, 150,
+    124, 156, 139, 163, 85, 189, 177, 200, 181, 201, 199, 202, 48, 144, 124, 156, 104, 150, 139, 163,
+    85, 189, 181, 201, 177, 200, 199, 202, 70, 188, 152, 206, 152, 206, 199, 210, 89, 190, 186, 211,
+    186, 211, 213, 214, 16, 68, 36, 71, 46, 73, 64, 74, 68, 157, 155, 158, 146, 159, 156, 160,
+    46, 146, 106, 153, 125, 159, 141, 166, 73, 159, 175, 201, 159, 203, 201, 204, 36, 155, 132, 179,
+    106, 175, 150, 183, 71, 158, 179, 208, 153, 201, 206, 209, 64, 156, 150, 206, 141, 201, 200, 211,
+    74, 160, 183, 209, 166, 204, 211, 215, 18, 69, 38, 72, 57, 74, 65, 75, 81, 164, 161, 165,
+    162, 166, 163, 167, 50, 147, 108, 154, 127, 160, 142, 167, 86, 192, 178, 202, 192, 204, 202, 205,
+    59, 172, 134, 191, 116, 183, 163, 195, 88, 193, 182, 209, 185, 211, 210, 212, 72, 191, 154, 207,
+    165, 209, 202, 212, 90, 194, 187, 212, 197, 215, 214, 216,  2, 13, 13, 19,  7, 17, 17, 20,
+    12, 45, 52, 54, 30, 51, 53, 55, 10, 47, 77, 82, 42, 67, 80, 83, 17, 49, 80, 85,
+    53, 71, 87, 88,  6, 31, 43, 54, 25, 37, 49, 60, 16, 46, 68, 73, 36, 64, 71, 74,
+    15, 48, 79, 85, 48, 70, 85, 89, 18, 50, 81, 86, 59, 72, 88, 90, 12, 52, 45, 54,
+    30, 53, 51, 55, 66, 110, 110, 112, 109, 111, 111, 113, 35, 143, 105, 169, 129, 168, 149, 170,
+    67, 145, 151, 181, 168, 179, 180, 182, 40, 130, 121, 135, 99, 133, 126, 136, 68, 146, 157, 159,
+    155, 156, 158, 160, 63, 144, 140, 189, 144, 188, 189, 190, 69, 147, 164, 192, 172, 191, 193, 194,
+    10, 77, 47, 82, 42, 80, 67, 83, 35, 105, 143, 169, 129, 149, 168, 170, 33, 103, 103, 176,
+    119, 151, 151, 184, 37, 107, 148, 177, 133, 153, 180, 185, 28, 102, 123, 169, 97, 148, 145, 173,
+    36, 106, 155, 175, 132, 150, 179, 183, 34, 104, 138, 177, 124, 152, 181, 186, 38, 108, 161, 178,
+    134, 154, 182, 187, 17, 80, 49, 85, 53, 87, 71, 88, 67, 151, 145, 181, 168, 180, 179, 182,
+    37, 148, 107, 177, 133, 180, 153, 185, 70, 152, 152, 199, 188, 206, 206, 210, 51, 149, 126, 189,
+    111, 180, 158, 193, 71, 153, 158, 201, 179, 206, 208, 209, 64, 150, 141, 200, 156, 206, 201, 211,
+    72, 154, 165, 202, 191, 207, 209, 212,  6, 43, 31, 54, 25, 49, 37, 60, 40, 121, 130, 135,
+    99, 126, 133, 136, 28, 123, 102, 169, 97, 145, 148, 173, 51, 126, 149, 189, 111, 158, 180, 193,
+    23, 100, 100, 112, 93, 107, 107, 117, 46, 125, 146, 159, 106, 141, 153, 166, 34, 124, 138, 181,
+    104, 152, 177, 186, 57, 127, 162, 192, 116, 165, 185, 197, 16, 68, 46, 73, 36, 71, 64, 74,
+    68, 157, 146, 159, 155, 158, 156, 160, 36, 155, 106, 175, 132, 179, 150, 183, 71, 158, 153, 201,
+    179, 208, 206, 209, 46, 146, 125, 159, 106, 153, 141, 166, 73, 159, 159, 203, 175, 201, 201, 204,
+    64, 156, 141, 201, 150, 206, 200, 211, 74, 160, 166, 204, 183, 209, 211, 215, 15, 79, 48, 85,
+    48, 85, 70, 89, 63, 140, 144, 189, 144, 189, 188, 190, 34, 138, 104, 177, 124, 181, 152, 186,
+    64, 141, 150, 200, 156, 201, 206, 211, 34, 138, 124, 181, 104, 177, 152, 186, 64, 141, 156, 201,
+    150, 200, 206, 211, 62, 139, 139, 199, 139, 199, 199, 213, 65, 142, 163, 202, 163, 202, 210, 214,
+    18, 81, 50, 86, 59, 88, 72, 90, 69, 164, 147, 192, 172, 193, 191, 194, 38, 161, 108, 178,
+    134, 182, 154, 187, 72, 165, 154, 202, 191, 209, 207, 212, 57, 162, 127, 192, 116, 185, 165, 197,
+    74, 166, 160, 204, 183, 211, 209, 215, 65, 163, 142, 202, 163, 210, 202, 214, 75, 167, 167, 205,
+    195, 212, 212, 216,  3, 14, 14, 20,  8, 18, 18, 21, 14, 56, 58, 60, 32, 57, 59, 61,
+    14, 58, 78, 83, 44, 69, 81, 84, 20, 60, 83, 89, 55, 74, 88, 90,  8, 32, 44, 55,
+    26, 38, 50, 61, 18, 57, 69, 74, 38, 65, 72, 75, 18, 59, 81, 88, 50, 72, 86, 90,
+    21, 61, 84, 90, 61, 75, 90, 91, 14, 58, 56, 60, 32, 59, 57, 61, 78, 115, 115, 117,
+    114, 116, 116, 118, 58, 171, 115, 173, 131, 172, 162, 174, 83, 173, 184, 186, 170, 183, 185, 187,
+    44, 131, 122, 136, 101, 134, 127, 137, 81, 162, 164, 166, 161, 163, 165, 167, 69, 172, 164, 193,
+    147, 191, 192, 194, 84, 174, 196, 197, 174, 195, 197, 198, 14, 78, 58, 83, 44, 81, 69, 84,
+    58, 115, 171, 173, 131, 162, 172, 174, 56, 115, 115, 184, 122, 164, 164, 196, 60, 117, 173, 186,
+    136, 166, 193, 197, 32, 114, 131, 170, 101, 161, 147, 174, 59, 116, 172, 183, 134, 163, 191, 195,
+    57, 116, 162, 185, 127, 165, 192, 197, 61, 118, 174, 187, 137, 167, 194, 198, 20, 83, 60, 89,
+    55, 88, 74, 90, 83, 184, 173, 186, 170, 185, 183, 187, 60, 173, 117, 186, 136, 193, 166, 197,
+    89, 186, 186, 213, 190, 211, 211, 214, 55, 170, 136, 190, 113, 182, 160, 194, 88, 185, 193, 211,
+    182, 210, 209, 212, 74, 183, 166, 211, 160, 209, 204, 215, 90, 187, 197, 214, 194, 212, 215, 216,
+    8, 44, 32, 55, 26, 50, 38, 61, 44, 122, 131, 136, 101, 127, 134, 137, 32, 131, 114, 170,
+    101, 147, 161, 174, 55, 136, 170, 190, 113, 160, 182, 194, 26, 101, 101, 113, 94, 108, 108, 118,
+    50, 127, 147, 160, 108, 142, 154, 167, 38, 134, 161, 182, 108, 154, 178, 187, 61, 137, 174, 194,
+    118, 167, 187, 198, 18, 69, 57, 74, 38, 72, 65, 75, 81, 164, 162, 166, 161, 165, 163, 167,
+    59, 172, 116, 183, 134, 191, 163, 195, 88, 193, 185, 211, 182, 209, 210, 212, 50, 147, 127, 160,
+    108, 154, 142, 167, 86, 192, 192, 204, 178, 202, 202, 205, 72, 191, 165, 209, 154, 207, 202, 212,
+    90, 194, 197, 215, 187, 212, 214, 216, 18, 81, 59, 88, 50, 86, 72, 90, 69, 164, 172, 193,
+    147, 192, 191, 194, 57, 162, 116, 185, 127, 192, 165, 197, 74, 166, 183, 211, 160, 204, 209, 215,
+    38, 161, 134, 182, 108, 178, 154, 187, 72, 165, 191, 209, 154, 202, 207, 212, 65, 163, 163, 210,
+    142, 202, 202, 214, 75, 167, 195, 212, 167, 205, 212, 216, 21, 84, 61, 90, 61, 90, 75, 91,
+    84, 196, 174, 197, 174, 197, 195, 198, 61, 174, 118, 187, 137, 194, 167, 198, 90, 197, 187, 214,
+    194, 215, 212, 216, 61, 174, 137, 194, 118, 187, 167, 198, 90, 197, 194, 215, 187, 214, 212, 216,
+    75, 195, 167, 212, 167, 212, 205, 216, 91, 198, 198, 216, 198, 216, 216, 217
+};
+
+const unsigned int igraph_i_isographs_3[] =  { 0, 1, 3, 5, 6, 7, 10, 11, 15, 21,
+                                               23, 25, 27, 30, 31, 63
+                                             };
+const unsigned int igraph_i_isographs_3u[] = { 0, 1, 3, 7 };
+const unsigned int igraph_i_isographs_4[] = {
+    0,    1,    3,    7,    9,   10,   11,   14,   15,   18,   19,   20,   21,
+    22,   23,   27,   29,   30,   31,   54,   55,   63,   73,   75,   76,   77,
+    79,   81,   83,   84,   85,   86,   87,   90,   91,   92,   93,   94,   95,
+    98,   99,  100,  101,  102,  103,  106,  107,  108,  109,  110,  111,  115,
+    116,  117,  118,  119,  122,  123,  124,  125,  126,  127,  219,  220,  221,
+    223,  228,  229,  230,  231,  237,  238,  239,  246,  247,  255,  292,  293,
+    295,  301,  302,  303,  310,  311,  319,  365,  367,  373,  375,  382,  383,
+    511,  585,  587,  591,  593,  594,  595,  596,  597,  598,  599,  601,  602,
+    603,  604,  605,  606,  607,  625,  626,  627,  630,  631,  633,  634,  635,
+    638,  639,  659,  660,  661,  663,  666,  667,  669,  670,  671,  674,  675,
+    678,  679,  683,  686,  687,  694,  695,  703,  729,  731,  732,  733,  735,
+    737,  739,  741,  742,  743,  745,  746,  747,  748,  749,  750,  751,  753,
+    755,  756,  757,  758,  759,  761,  762,  763,  764,  765,  766,  767,  819,
+    822,  823,  826,  827,  830,  831,  875,  876,  877,  879,  883,  885,  886,
+    887,  891,  892,  893,  894,  895,  947,  949,  951,  955,  957,  958,  959,
+    1019, 1020, 1021, 1023, 1755, 1757, 1758, 1759, 1782, 1783, 1791, 1883, 1887,
+    1907, 1911, 1917, 1918, 1919, 2029, 2031, 2039, 2047, 4095
+};
+const unsigned int igraph_i_isographs_4u[] = { 0, 1, 3, 7, 11, 12, 13,
+                                               15, 30, 31, 63
+                                             };
+
+const unsigned int igraph_i_classedges_3[] = { 1, 2, 0, 2, 2, 1, 0, 1, 2, 0, 1, 0 };
+const unsigned int igraph_i_classedges_3u[] = { 1, 2, 0, 2, 0, 1 };
+const unsigned int igraph_i_classedges_4[] = { 2, 3, 1, 3, 0, 3, 3, 2, 1, 2, 0, 2,
+                                               3, 1, 2, 1, 0, 1, 3, 0, 2, 0, 1, 0
+                                             };
+const unsigned int igraph_i_classedges_4u[] = { 2, 3, 1, 3, 0, 3, 1, 2, 0, 2, 0, 1 };
+
+/**
+ * \section about_graph_isomorphism
+ *
+ * <para>igraph provides four set of functions to deal with graph
+ * isomorphism problems.</para>
+ *
+ * <para>The \ref igraph_isomorphic() and \ref igraph_subisomorphic()
+ * functions make up the first set (in addition with the \ref
+ * igraph_permute_vertices() function). These functions choose the
+ * algorithm which is best for the supplied input graph. (The choice is
+ * not very sophisticated though, see their documentation for
+ * details.)</para>
+ *
+ * <para>The VF2 graph (and subgraph) isomorphism algorithm is implemented in
+ * igraph, these functions are the second set. See \ref
+ * igraph_isomorphic_vf2() and \ref igraph_subisomorphic_vf2() for
+ * starters.</para>
+ *
+ * <para>Functions for the BLISS algorithm constitute the third set,
+ * see \ref igraph_isomorphic_bliss().</para>
+ *
+ * <para>Finally, the isomorphism classes of all graphs with three and
+ * four vertices are precomputed and stored in igraph, so for these
+ * small graphs there is a very simple fast way to decide isomorphism.
+ * See \ref igraph_isomorphic_34().
+ * </para>
+ */
+
+/**
+ * \function igraph_isoclass
+ * \brief Determine the isomorphism class of a graph with 3 or 4 vertices
+ *
+ * </para><para>
+ * All graphs with a given number of vertices belong to a number of
+ * isomorphism classes, with every graph in a given class being
+ * isomorphic to each other.
+ *
+ * </para><para>
+ * This function gives the isomorphism class (a number) of a
+ * graph. Two graphs have the same isomorphism class if and only if
+ * they are isomorphic.
+ *
+ * </para><para>
+ * The first isomorphism class is numbered zero and it is the empty
+ * graph, the last isomorphism class is the full graph. The number of
+ * isomorphism class for directed graphs with three vertices is 16
+ * (between 0 and 15), for undirected graph it is only 4. For graphs
+ * with four vertices it is 218 (directed) and 11 (undirected).
+ *
+ * \param graph The graph object.
+ * \param isoclass Pointer to an integer, the isomorphism class will
+ *        be stored here.
+ * \return Error code.
+ * \sa \ref igraph_isomorphic(), \ref igraph_isoclass_subgraph(),
+ * \ref igraph_isoclass_create(), \ref igraph_motifs_randesu().
+ *
+ * Because of some limitations this function works only for graphs
+ * with three of four vertices.
+ *
+ * </para><para>
+ * Time complexity: O(|E|), the number of edges in the graph.
+ */
+
+int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) {
+    long int e;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_integer_t from, to;
+    unsigned char idx, mul;
+    const unsigned int *arr_idx, *arr_code;
+    int code = 0;
+
+    if (no_of_nodes < 3 || no_of_nodes > 4) {
+        IGRAPH_ERROR("Only implemented for graphs with 3 or 4 vertices",
+                     IGRAPH_UNIMPLEMENTED);
+    }
+
+    if (igraph_is_directed(graph)) {
+        if (no_of_nodes == 3) {
+            arr_idx = igraph_i_isoclass_3_idx;
+            arr_code = igraph_i_isoclass2_3;
+            mul = 3;
+        } else {
+            arr_idx = igraph_i_isoclass_4_idx;
+            arr_code = igraph_i_isoclass2_4;
+            mul = 4;
+        }
+    } else {
+        if (no_of_nodes == 3) {
+            arr_idx = igraph_i_isoclass_3u_idx;
+            arr_code = igraph_i_isoclass2_3u;
+            mul = 3;
+        } else {
+            arr_idx = igraph_i_isoclass_4u_idx;
+            arr_code = igraph_i_isoclass2_4u;
+            mul = 4;
+        }
+    }
+
+    for (e = 0; e < no_of_edges; e++) {
+        igraph_edge(graph, (igraph_integer_t) e, &from, &to);
+        idx = (unsigned char) (mul * from + to);
+        code |= arr_idx[idx];
+    }
+
+    *isoclass = (igraph_integer_t) arr_code[code];
+    return 0;
+}
+
+/**
+ * \function igraph_isomorphic
+ * \brief Decides whether two graphs are isomorphic
+ *
+ * </para><para>
+ * From Wikipedia: The graph isomorphism problem or GI problem is the
+ * graph theory problem of determining whether, given two graphs G1
+ * and G2, it is possible to permute (or relabel) the vertices of one
+ * graph so that it is equal to the other. Such a permutation is
+ * called a graph isomorphism.</para>
+ *
+ * <para>This function decides which graph isomorphism algorithm to be
+ * used based on the input graphs. Right now it does the following:
+ * \olist
+ * \oli If one graph is directed and the other undirected then an
+ *    error is triggered.
+ * \oli If the two graphs does not have the same number of vertices
+ *    and edges it returns with \c FALSE.
+ * \oli Otherwise, if the graphs have three or four vertices then an O(1)
+ *    algorithm is used with precomputed data.
+ * \oli Otherwise BLISS is used, see \ref igraph_isomorphic_bliss().
+ * \endolist
+ * </para>
+ *
+ * <para> Please call the VF2 and BLISS functions directly if you need
+ * something more sophisticated, e.g. you need the isomorphic mapping.
+ *
+ * \param graph1 The first graph.
+ * \param graph2 The second graph.
+ * \param iso Pointer to a logical variable, will be set to TRUE (1)
+ *        if the two graphs are isomorphic, and FALSE (0) otherwise.
+ * \return Error code.
+ * \sa \ref igraph_isoclass(), \ref igraph_isoclass_subgraph(),
+ * \ref igraph_isoclass_create().
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2,
+                      igraph_bool_t *iso) {
+
+    long int nodes1 = igraph_vcount(graph1), nodes2 = igraph_vcount(graph2);
+    long int edges1 = igraph_ecount(graph1), edges2 = igraph_ecount(graph2);
+    igraph_bool_t dir1 = igraph_is_directed(graph1), dir2 = igraph_is_directed(graph2);
+    igraph_bool_t loop1, loop2;
+
+    if (dir1 != dir2) {
+        IGRAPH_ERROR("Cannot compare directed and undirected graphs", IGRAPH_EINVAL);
+    } else if (nodes1 != nodes2 || edges1 != edges2) {
+        *iso = 0;
+    } else if (nodes1 == 3 || nodes1 == 4) {
+        IGRAPH_CHECK(igraph_has_loop(graph1, &loop1));
+        IGRAPH_CHECK(igraph_has_loop(graph2, &loop2));
+        if (!loop1 && !loop2) {
+            IGRAPH_CHECK(igraph_isomorphic_34(graph1, graph2, iso));
+        } else {
+            IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso,
+                                                 0, 0, /*sh=*/ IGRAPH_BLISS_F, 0, 0));
+        }
+    } else {
+        IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso,
+                                             0, 0, /*sh=*/ IGRAPH_BLISS_F, 0, 0));
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_isomorphic_34
+ * Graph isomorphism for 3-4 vertices
+ *
+ * This function uses precomputed indices to decide isomorphism
+ * problems for graphs with only 3 or 4 vertices.
+ * \param graph1 The first input graph.
+ * \param graph2 The second input graph. Must have the same
+ *   directedness as \p graph1.
+ * \param iso Pointer to a boolean, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2,
+                         igraph_bool_t *iso) {
+
+    igraph_integer_t class1, class2;
+    IGRAPH_CHECK(igraph_isoclass(graph1, &class1));
+    IGRAPH_CHECK(igraph_isoclass(graph2, &class2));
+    *iso = (class1 == class2);
+    return 0;
+}
+
+/**
+ * \function igraph_isoclass_subgraph
+ * \brief The isomorphism class of a subgraph of a graph.
+ *
+ * </para><para>
+ * This function is only implemented for subgraphs with three or four
+ * vertices.
+ * \param graph The graph object.
+ * \param vids A vector containing the vertex ids to be considered as
+ *        a subgraph. Each vertex id should be included at most once.
+ * \param isoclass Pointer to an integer, this will be set to the
+ *        isomorphism class.
+ * \return Error code.
+ * \sa \ref igraph_isoclass(), \ref igraph_isomorphic(),
+ * \ref igraph_isoclass_create().
+ *
+ * Time complexity: O((d+n)*n), d is the average degree in the network,
+ * and n is the number of vertices in \c vids.
+ */
+
+int igraph_isoclass_subgraph(const igraph_t *graph, igraph_vector_t *vids,
+                             igraph_integer_t *isoclass) {
+    int nodes = (int) igraph_vector_size(vids);
+    igraph_bool_t directed = igraph_is_directed(graph);
+    igraph_vector_t neis;
+
+    unsigned char mul, idx;
+    const unsigned int *arr_idx, *arr_code;
+    int code = 0;
+
+    long int i, j, s;
+
+    if (nodes < 3 || nodes > 4) {
+        IGRAPH_ERROR("Only for three- or four-vertex subgraphs",
+                     IGRAPH_UNIMPLEMENTED);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+
+    if (directed) {
+        if (nodes == 3) {
+            arr_idx = igraph_i_isoclass_3_idx;
+            arr_code = igraph_i_isoclass2_3;
+            mul = 3;
+        } else {
+            arr_idx = igraph_i_isoclass_4_idx;
+            arr_code = igraph_i_isoclass2_4;
+            mul = 4;
+        }
+    } else {
+        if (nodes == 3) {
+            arr_idx = igraph_i_isoclass_3u_idx;
+            arr_code = igraph_i_isoclass2_3u;
+            mul = 3;
+        } else {
+            arr_idx = igraph_i_isoclass_4u_idx;
+            arr_code = igraph_i_isoclass2_4u;
+            mul = 4;
+        }
+    }
+
+    for (i = 0; i < nodes; i++) {
+        long int from = (long int) VECTOR(*vids)[i];
+        igraph_neighbors(graph, &neis, (igraph_integer_t) from, IGRAPH_OUT);
+        s = igraph_vector_size(&neis);
+        for (j = 0; j < s; j++) {
+            long int nei = (long int) VECTOR(neis)[j], to;
+            if (igraph_vector_search(vids, 0, nei, &to)) {
+                idx = (unsigned char) (mul * i + to);
+                code |= arr_idx[idx];
+            }
+        }
+    }
+
+    *isoclass = (igraph_integer_t) arr_code[code];
+    igraph_vector_destroy(&neis);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_isoclass_create
+ * \brief Creates a graph from the given isomorphism class.
+ *
+ * </para><para>
+ * This function is implemented only for graphs with three or four
+ * vertices.
+ * \param graph Pointer to an uninitialized graph object.
+ * \param size The number of vertices to add to the graph.
+ * \param number The isomorphism class.
+ * \param directed Logical constant, whether to create a directed
+ *        graph.
+ * \return Error code.
+ * \sa \ref igraph_isoclass(),
+ * \ref igraph_isoclass_subgraph(),
+ * \ref igraph_isomorphic().
+ *
+ * Time complexity: O(|V|+|E|), the number of vertices plus the number
+ * of edges in the graph to create.
+ */
+
+int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size,
+                           igraph_integer_t number, igraph_bool_t directed) {
+    igraph_vector_t edges;
+    const unsigned int *classedges;
+    long int power;
+    long int code;
+    long int pos;
+
+    if (size < 3 || size > 4) {
+        IGRAPH_ERROR("Only for graphs with three of four vertices",
+                     IGRAPH_UNIMPLEMENTED);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+
+    if (directed) {
+        if (size == 3) {
+            classedges = igraph_i_classedges_3;
+
+            if (number < 0 ||
+                number >= (int)(sizeof(igraph_i_isographs_3) / sizeof(unsigned int))) {
+                IGRAPH_ERROR("`number' invalid, cannot create graph", IGRAPH_EINVAL);
+            }
+
+            code = igraph_i_isographs_3[ (long int) number];
+            power = 32;
+        } else {
+            classedges = igraph_i_classedges_4;
+
+            if (number < 0 ||
+                number >= (int)(sizeof(igraph_i_isographs_4) / sizeof(unsigned int))) {
+                IGRAPH_ERROR("`number' invalid, cannot create graph", IGRAPH_EINVAL);
+            }
+
+            code = igraph_i_isographs_4[ (long int) number];
+            power = 2048;
+        }
+    } else {
+        if (size == 3) {
+            classedges = igraph_i_classedges_3u;
+
+            if (number < 0 ||
+                number >= (int)(sizeof(igraph_i_isographs_3u) /
+                                sizeof(unsigned int))) {
+                IGRAPH_ERROR("`number' invalid, cannot create graph", IGRAPH_EINVAL);
+            }
+
+            code = igraph_i_isographs_3u[ (long int) number];
+            power = 4;
+        } else {
+            classedges = igraph_i_classedges_4u;
+
+            if (number < 0 ||
+                number >= (int)(sizeof(igraph_i_isographs_4u) /
+                                sizeof(unsigned int))) {
+                IGRAPH_ERROR("`number' invalid, cannot create graph", IGRAPH_EINVAL);
+            }
+
+            code = igraph_i_isographs_4u[ (long int) number];
+            power = 32;
+        }
+    }
+
+    pos = 0;
+    while (code > 0) {
+        if (code >= power) {
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, classedges[2 * pos]));
+            IGRAPH_CHECK(igraph_vector_push_back(&edges, classedges[2 * pos + 1]));
+            code -= power;
+        }
+        power /= 2;
+        pos++;
+    }
+
+    IGRAPH_CHECK(igraph_create(graph, &edges, size, directed));
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \section about_vf2
+ *
+ * <para>
+ * The VF2 algorithm can search for a subgraph in a larger graph, or check if two
+ * graphs are isomorphic. See P. Foggia, C. Sansone, M. Vento, An Improved algorithm for
+ * matching large graphs, Proc. of the 3rd IAPR-TC-15 International
+ * Workshop on Graph-based Representations, Italy, 2001.
+ * </para>
+ *
+ * <para>
+ * VF2 supports both vertex and edge-colored graphs, as well as custom vertex or edge
+ * compatibility functions.
+ * </para>
+ *
+ * <para>
+ * VF2 works with both directed and undirected graphs. Only simple graphs are supported.
+ * Self-loops or multi-edges must not be present in the graphs. Currently, the VF2
+ * functions do not check that the input graph is simple: it is the responsibility
+ * of the user to pass in valid input.
+ * </para>
+ */
+
+/**
+ * \function igraph_isomorphic_function_vf2
+ * The generic VF2 interface
+ *
+ * </para><para>
+ * This function is an implementation of the VF2 isomorphism algorithm,
+ * see P. Foggia, C. Sansone, M. Vento, An Improved algorithm for
+ * matching large graphs, Proc. of the 3rd IAPR-TC-15 International
+ * Workshop on Graph-based Representations, Italy, 2001.</para>
+ *
+ * <para>For using it you need to define a callback function of type
+ * \ref igraph_isohandler_t. This function will be called whenever VF2
+ * finds an isomorphism between the two graphs. The mapping between
+ * the two graphs will be also provided to this function. If the
+ * callback returns a nonzero value then the search is continued,
+ * otherwise it stops. The callback function must not destroy the
+ * mapping vectors that are passed to it.
+ * \param graph1 The first input graph.
+ * \param graph2 The second input graph.
+ * \param vertex_color1 An optional color vector for the first graph. If
+ *   color vectors are given for both graphs, then the isomorphism is
+ *   calculated on the colored graphs; i.e. two vertices can match
+ *   only if their color also matches. Supply a null pointer here if
+ *   your graphs are not colored.
+ * \param vertex_color2 An optional color vector for the second graph. See
+ *   the previous argument for explanation.
+ * \param edge_color1 An optional edge color vector for the first
+ *   graph. The matching edges in the two graphs must have matching
+ *   colors as well. Supply a null pointer here if your graphs are not
+ *   edge-colored.
+ * \param edge_color2 The edge color vector for the second graph.
+ * \param map12 Pointer to an initialized vector or \c NULL. If not \c
+ *   NULL and the supplied graphs are isomorphic then the permutation
+ *   taking \p graph1 to \p graph is stored here. If not \c NULL and the
+ *   graphs are not isomorphic then a zero-length vector is returned.
+ * \param map21 This is the same as \p map12, but for the permutation
+ *   taking \p graph2 to \p graph1.
+ * \param isohandler_fn The callback function to be called if an
+ *   isomorphism is found. See also \ref igraph_isohandler_t.
+ * \param node_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two nodes are compatible.
+ * \param edge_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two edges are compatible.
+ * \param arg Extra argument to supply to functions \p isohandler_fn, \p
+ *   node_compat_fn and \p edge_compat_fn.
+ * \return Error code.
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph2,
+                                   const igraph_vector_int_t *vertex_color1,
+                                   const igraph_vector_int_t *vertex_color2,
+                                   const igraph_vector_int_t *edge_color1,
+                                   const igraph_vector_int_t *edge_color2,
+                                   igraph_vector_t *map12,
+                                   igraph_vector_t *map21,
+                                   igraph_isohandler_t *isohandler_fn,
+                                   igraph_isocompat_t *node_compat_fn,
+                                   igraph_isocompat_t *edge_compat_fn,
+                                   void *arg) {
+
+    long int no_of_nodes = igraph_vcount(graph1);
+    long int no_of_edges = igraph_ecount(graph1);
+    igraph_vector_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2;
+    igraph_vector_t in_1, in_2, out_1, out_2;
+    long int in_1_size = 0, in_2_size = 0, out_1_size = 0, out_2_size = 0;
+    igraph_vector_t *inneis_1, *inneis_2, *outneis_1, *outneis_2;
+    long int matched_nodes = 0;
+    long int depth;
+    long int cand1, cand2;
+    long int last1, last2;
+    igraph_stack_t path;
+    igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2;
+    igraph_vector_t indeg1, indeg2, outdeg1, outdeg2;
+
+    if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) {
+        IGRAPH_ERROR("Cannot compare directed and undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) {
+        IGRAPH_WARNING("Only one graph is vertex-colored, vertex colors will be ignored");
+        vertex_color1 = vertex_color2 = 0;
+    }
+
+    if ( (edge_color1 && !edge_color2) || (!edge_color1 && edge_color2)) {
+        IGRAPH_WARNING("Only one graph is edge-colored, edge colors will be ignored");
+        edge_color1 = edge_color2 = 0;
+    }
+
+    if (no_of_nodes != igraph_vcount(graph2) ||
+        no_of_edges != igraph_ecount(graph2)) {
+        return 0;
+    }
+
+    if (vertex_color1) {
+        if (igraph_vector_int_size(vertex_color1) != no_of_nodes ||
+            igraph_vector_int_size(vertex_color2) != no_of_nodes) {
+            IGRAPH_ERROR("Invalid vertex color vector length", IGRAPH_EINVAL);
+        }
+    }
+
+    if (edge_color1) {
+        if (igraph_vector_int_size(edge_color1) != no_of_edges ||
+            igraph_vector_int_size(edge_color2) != no_of_edges) {
+            IGRAPH_ERROR("Invalid edge color vector length", IGRAPH_EINVAL);
+        }
+    }
+
+    /* Check color distribution */
+    if (vertex_color1) {
+        int ret = 0;
+        igraph_vector_int_t tmp1, tmp2;
+        IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, vertex_color1));
+        IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1);
+        IGRAPH_CHECK(igraph_vector_int_copy(&tmp2, vertex_color2));
+        IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp2);
+        igraph_vector_int_sort(&tmp1);
+        igraph_vector_int_sort(&tmp2);
+        ret = !igraph_vector_int_all_e(&tmp1, &tmp2);
+        igraph_vector_int_destroy(&tmp1);
+        igraph_vector_int_destroy(&tmp2);
+        IGRAPH_FINALLY_CLEAN(2);
+        if (ret) {
+            return 0;
+        }
+    }
+
+    /* Check edge color distribution */
+    if (edge_color1) {
+        int ret = 0;
+        igraph_vector_int_t tmp1, tmp2;
+        IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, edge_color1));
+        IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1);
+        IGRAPH_CHECK(igraph_vector_int_copy(&tmp2, edge_color2));
+        IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp2);
+        igraph_vector_int_sort(&tmp1);
+        igraph_vector_int_sort(&tmp2);
+        ret = !igraph_vector_int_all_e(&tmp1, &tmp2);
+        igraph_vector_int_destroy(&tmp1);
+        igraph_vector_int_destroy(&tmp2);
+        IGRAPH_FINALLY_CLEAN(2);
+        if (ret) {
+            return 0;
+        }
+    }
+
+    if (map12) {
+        core_1 = map12;
+        IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes));
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes);
+    }
+    igraph_vector_fill(core_1, -1);
+    if (map21) {
+        core_2 = map21;
+        IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes));
+        igraph_vector_null(core_2);
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes);
+    }
+    igraph_vector_fill(core_2, -1);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&in_1, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&in_2, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&out_1, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&out_2, no_of_nodes);
+    IGRAPH_CHECK(igraph_stack_init(&path, 0));
+    IGRAPH_FINALLY(igraph_stack_destroy, &path);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &inadj1, IGRAPH_IN,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj1);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &outadj1, IGRAPH_OUT,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj1);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &inadj2, IGRAPH_IN,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj2);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &outadj2, IGRAPH_OUT,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj2);
+    IGRAPH_VECTOR_INIT_FINALLY(&indeg1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&indeg2, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&outdeg1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&outdeg2, 0);
+
+    IGRAPH_CHECK(igraph_stack_reserve(&path, no_of_nodes * 2));
+    IGRAPH_CHECK(igraph_degree(graph1, &indeg1, igraph_vss_all(),
+                               IGRAPH_IN, IGRAPH_LOOPS));
+    IGRAPH_CHECK(igraph_degree(graph2, &indeg2, igraph_vss_all(),
+                               IGRAPH_IN, IGRAPH_LOOPS));
+    IGRAPH_CHECK(igraph_degree(graph1, &outdeg1, igraph_vss_all(),
+                               IGRAPH_OUT, IGRAPH_LOOPS));
+    IGRAPH_CHECK(igraph_degree(graph2, &outdeg2, igraph_vss_all(),
+                               IGRAPH_OUT, IGRAPH_LOOPS));
+
+    depth = 0; last1 = -1; last2 = -1;
+    while (depth >= 0) {
+        long int i;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        cand1 = -1; cand2 = -1;
+        /* Search for the next pair to try */
+        if ((in_1_size != in_2_size) ||
+            (out_1_size != out_2_size)) {
+            /* step back, nothing to do */
+        } else if (out_1_size > 0 && out_2_size > 0) {
+            /**************************************************************/
+            /* cand2, search not always needed */
+            if (last2 >= 0) {
+                cand2 = last2;
+            } else {
+                i = 0;
+                while (cand2 < 0 && i < no_of_nodes) {
+                    if (VECTOR(out_2)[i] > 0 && VECTOR(*core_2)[i] < 0) {
+                        cand2 = i;
+                    }
+                    i++;
+                }
+            }
+            /* search for cand1 now, it should be bigger than last1 */
+            i = last1 + 1;
+            while (cand1 < 0 && i < no_of_nodes) {
+                if (VECTOR(out_1)[i] > 0 && VECTOR(*core_1)[i] < 0) {
+                    cand1 = i;
+                }
+                i++;
+            }
+        } else if (in_1_size > 0 && in_2_size > 0) {
+            /**************************************************************/
+            /* cand2, search not always needed */
+            if (last2 >= 0) {
+                cand2 = last2;
+            } else {
+                i = 0;
+                while (cand2 < 0 && i < no_of_nodes) {
+                    if (VECTOR(in_2)[i] > 0 && VECTOR(*core_2)[i] < 0) {
+                        cand2 = i;
+                    }
+                    i++;
+                }
+            }
+            /* search for cand1 now, should be bigger than last1 */
+            i = last1 + 1;
+            while (cand1 < 0 && i < no_of_nodes) {
+                if (VECTOR(in_1)[i] > 0 && VECTOR(*core_1)[i] < 0) {
+                    cand1 = i;
+                }
+                i++;
+            }
+        } else {
+            /**************************************************************/
+            /* cand2, search not always needed */
+            if (last2 >= 0) {
+                cand2 = last2;
+            } else {
+                i = 0;
+                while (cand2 < 0 && i < no_of_nodes) {
+                    if (VECTOR(*core_2)[i] < 0) {
+                        cand2 = i;
+                    }
+                    i++;
+                }
+            }
+            /* search for cand1, should be bigger than last1 */
+            i = last1 + 1;
+            while (cand1 < 0 && i < no_of_nodes) {
+                if (VECTOR(*core_1)[i] < 0) {
+                    cand1 = i;
+                }
+                i++;
+            }
+        }
+
+        /* Ok, we have cand1, cand2 as candidates. Or not? */
+        if (cand1 < 0 || cand2 < 0) {
+            /**************************************************************/
+            /* dead end, step back, if possible. Otherwise we'll terminate */
+            if (depth >= 1) {
+                last2 = (long int) igraph_stack_pop(&path);
+                last1 = (long int) igraph_stack_pop(&path);
+                matched_nodes -= 1;
+                VECTOR(*core_1)[last1] = -1;
+                VECTOR(*core_2)[last2] = -1;
+
+                if (VECTOR(in_1)[last1] != 0) {
+                    in_1_size += 1;
+                }
+                if (VECTOR(out_1)[last1] != 0) {
+                    out_1_size += 1;
+                }
+                if (VECTOR(in_2)[last2] != 0) {
+                    in_2_size += 1;
+                }
+                if (VECTOR(out_2)[last2] != 0) {
+                    out_2_size += 1;
+                }
+
+                inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1);
+                for (i = 0; i < igraph_vector_size(inneis_1); i++) {
+                    long int node = (long int) VECTOR(*inneis_1)[i];
+                    if (VECTOR(in_1)[node] == depth) {
+                        VECTOR(in_1)[node] = 0;
+                        in_1_size -= 1;
+                    }
+                }
+                outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) last1);
+                for (i = 0; i < igraph_vector_size(outneis_1); i++) {
+                    long int node = (long int) VECTOR(*outneis_1)[i];
+                    if (VECTOR(out_1)[node] == depth) {
+                        VECTOR(out_1)[node] = 0;
+                        out_1_size -= 1;
+                    }
+                }
+                inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) last2);
+                for (i = 0; i < igraph_vector_size(inneis_2); i++) {
+                    long int node = (long int) VECTOR(*inneis_2)[i];
+                    if (VECTOR(in_2)[node] == depth) {
+                        VECTOR(in_2)[node] = 0;
+                        in_2_size -= 1;
+                    }
+                }
+                outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) last2);
+                for (i = 0; i < igraph_vector_size(outneis_2); i++) {
+                    long int node = (long int) VECTOR(*outneis_2)[i];
+                    if (VECTOR(out_2)[node] == depth) {
+                        VECTOR(out_2)[node] = 0;
+                        out_2_size -= 1;
+                    }
+                }
+
+            } /* end of stepping back */
+
+            depth -= 1;
+
+        } else {
+            /**************************************************************/
+            /* step forward if worth, check if worth first */
+            long int xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0;
+            igraph_bool_t end = 0;
+            inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1);
+            outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1);
+            inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2);
+            outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2);
+            if (VECTOR(indeg1)[cand1] != VECTOR(indeg2)[cand2] ||
+                VECTOR(outdeg1)[cand1] != VECTOR(outdeg2)[cand2]) {
+                end = 1;
+            }
+            if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) {
+                end = 1;
+            }
+            if (node_compat_fn && !node_compat_fn(graph1, graph2,
+                                                  (igraph_integer_t) cand1,
+                                                  (igraph_integer_t) cand2, arg)) {
+                end = 1;
+            }
+
+            for (i = 0; !end && i < igraph_vector_size(inneis_1); i++) {
+                long int node = (long int) VECTOR(*inneis_1)[i];
+                if (VECTOR(*core_1)[node] >= 0) {
+                    long int node2 = (long int) VECTOR(*core_1)[node];
+                    /* check if there is a node2->cand2 edge */
+                    if (!igraph_vector_binsearch2(inneis_2, node2)) {
+                        end = 1;
+                    } else if (edge_color1 || edge_compat_fn) {
+                        igraph_integer_t eid1, eid2;
+                        igraph_get_eid(graph1, &eid1, (igraph_integer_t) node,
+                                       (igraph_integer_t) cand1, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        igraph_get_eid(graph2, &eid2, (igraph_integer_t) node2,
+                                       (igraph_integer_t) cand2, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] !=
+                            VECTOR(*edge_color2)[(long int)eid2]) {
+                            end = 1;
+                        }
+                        if (edge_compat_fn && !edge_compat_fn(graph1, graph2,
+                                                              eid1, eid2, arg)) {
+                            end = 1;
+                        }
+                    }
+                } else {
+                    if (VECTOR(in_1)[node] != 0) {
+                        xin1++;
+                    }
+                    if (VECTOR(out_1)[node] != 0) {
+                        xout1++;
+                    }
+                }
+            }
+            for (i = 0; !end && i < igraph_vector_size(outneis_1); i++) {
+                long int node = (long int) VECTOR(*outneis_1)[i];
+                if (VECTOR(*core_1)[node] >= 0) {
+                    long int node2 = (long int) VECTOR(*core_1)[node];
+                    /* check if there is a cand2->node2 edge */
+                    if (!igraph_vector_binsearch2(outneis_2, node2)) {
+                        end = 1;
+                    } else if (edge_color1 || edge_compat_fn) {
+                        igraph_integer_t eid1, eid2;
+                        igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1,
+                                       (igraph_integer_t) node, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2,
+                                       (igraph_integer_t) node2, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] !=
+                            VECTOR(*edge_color2)[(long int)eid2]) {
+                            end = 1;
+                        }
+                        if (edge_compat_fn && !edge_compat_fn(graph1, graph2,
+                                                              eid1, eid2, arg)) {
+                            end = 1;
+                        }
+                    }
+                } else {
+                    if (VECTOR(in_1)[node] != 0) {
+                        xin1++;
+                    }
+                    if (VECTOR(out_1)[node] != 0) {
+                        xout1++;
+                    }
+                }
+            }
+            for (i = 0; !end && i < igraph_vector_size(inneis_2); i++) {
+                long int node = (long int) VECTOR(*inneis_2)[i];
+                if (VECTOR(*core_2)[node] >= 0) {
+                    long int node2 = (long int) VECTOR(*core_2)[node];
+                    /* check if there is a node2->cand1 edge */
+                    if (!igraph_vector_binsearch2(inneis_1, node2)) {
+                        end = 1;
+                    } else if (edge_color1 || edge_compat_fn) {
+                        igraph_integer_t eid1, eid2;
+                        igraph_get_eid(graph1, &eid1, (igraph_integer_t) node2,
+                                       (igraph_integer_t) cand1, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        igraph_get_eid(graph2, &eid2, (igraph_integer_t) node,
+                                       (igraph_integer_t) cand2, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] !=
+                            VECTOR(*edge_color2)[(long int)eid2]) {
+                            end = 1;
+                        }
+                        if (edge_compat_fn && !edge_compat_fn(graph1, graph2,
+                                                              eid1, eid2, arg)) {
+                            end = 1;
+                        }
+                    }
+                } else {
+                    if (VECTOR(in_2)[node] != 0) {
+                        xin2++;
+                    }
+                    if (VECTOR(out_2)[node] != 0) {
+                        xout2++;
+                    }
+                }
+            }
+            for (i = 0; !end && i < igraph_vector_size(outneis_2); i++) {
+                long int node = (long int) VECTOR(*outneis_2)[i];
+                if (VECTOR(*core_2)[node] >= 0) {
+                    long int node2 = (long int) VECTOR(*core_2)[node];
+                    /* check if there is a cand1->node2 edge */
+                    if (!igraph_vector_binsearch2(outneis_1, node2)) {
+                        end = 1;
+                    } else if (edge_color1 || edge_compat_fn) {
+                        igraph_integer_t eid1, eid2;
+                        igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1,
+                                       (igraph_integer_t) node2, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2,
+                                       (igraph_integer_t) node, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] !=
+                            VECTOR(*edge_color2)[(long int)eid2]) {
+                            end = 1;
+                        }
+                        if (edge_compat_fn && !edge_compat_fn(graph1, graph2,
+                                                              eid1, eid2, arg)) {
+                            end = 1;
+                        }
+                    }
+                } else {
+                    if (VECTOR(in_2)[node] != 0) {
+                        xin2++;
+                    }
+                    if (VECTOR(out_2)[node] != 0) {
+                        xout2++;
+                    }
+                }
+            }
+
+            if (!end && (xin1 == xin2 && xout1 == xout2)) {
+                /* Ok, we add the (cand1, cand2) pair to the mapping */
+                depth += 1;
+                IGRAPH_CHECK(igraph_stack_push(&path, cand1));
+                IGRAPH_CHECK(igraph_stack_push(&path, cand2));
+                matched_nodes += 1;
+                VECTOR(*core_1)[cand1] = cand2;
+                VECTOR(*core_2)[cand2] = cand1;
+
+                /* update in_*, out_* */
+                if (VECTOR(in_1)[cand1] != 0) {
+                    in_1_size -= 1;
+                }
+                if (VECTOR(out_1)[cand1] != 0) {
+                    out_1_size -= 1;
+                }
+                if (VECTOR(in_2)[cand2] != 0) {
+                    in_2_size -= 1;
+                }
+                if (VECTOR(out_2)[cand2] != 0) {
+                    out_2_size -= 1;
+                }
+
+                inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1);
+                for (i = 0; i < igraph_vector_size(inneis_1); i++) {
+                    long int node = (long int) VECTOR(*inneis_1)[i];
+                    if (VECTOR(in_1)[node] == 0 && VECTOR(*core_1)[node] < 0) {
+                        VECTOR(in_1)[node] = depth;
+                        in_1_size += 1;
+                    }
+                }
+                outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1);
+                for (i = 0; i < igraph_vector_size(outneis_1); i++) {
+                    long int node = (long int) VECTOR(*outneis_1)[i];
+                    if (VECTOR(out_1)[node] == 0 && VECTOR(*core_1)[node] < 0) {
+                        VECTOR(out_1)[node] = depth;
+                        out_1_size += 1;
+                    }
+                }
+                inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2);
+                for (i = 0; i < igraph_vector_size(inneis_2); i++) {
+                    long int node = (long int) VECTOR(*inneis_2)[i];
+                    if (VECTOR(in_2)[node] == 0 && VECTOR(*core_2)[node] < 0) {
+                        VECTOR(in_2)[node] = depth;
+                        in_2_size += 1;
+                    }
+                }
+                outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2);
+                for (i = 0; i < igraph_vector_size(outneis_2); i++) {
+                    long int node = (long int) VECTOR(*outneis_2)[i];
+                    if (VECTOR(out_2)[node] == 0 && VECTOR(*core_2)[node] < 0) {
+                        VECTOR(out_2)[node] = depth;
+                        out_2_size += 1;
+                    }
+                }
+                last1 = -1; last2 = -1;       /* this the first time here */
+            } else {
+                last1 = cand1;
+                last2 = cand2;
+            }
+
+        }
+
+        if (matched_nodes == no_of_nodes && isohandler_fn) {
+            if (!isohandler_fn(core_1, core_2, arg)) {
+                break;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&outdeg2);
+    igraph_vector_destroy(&outdeg1);
+    igraph_vector_destroy(&indeg2);
+    igraph_vector_destroy(&indeg1);
+    igraph_lazy_adjlist_destroy(&outadj2);
+    igraph_lazy_adjlist_destroy(&inadj2);
+    igraph_lazy_adjlist_destroy(&outadj1);
+    igraph_lazy_adjlist_destroy(&inadj1);
+    igraph_stack_destroy(&path);
+    igraph_vector_destroy(&out_2);
+    igraph_vector_destroy(&out_1);
+    igraph_vector_destroy(&in_2);
+    igraph_vector_destroy(&in_1);
+    IGRAPH_FINALLY_CLEAN(13);
+    if (!map21) {
+        igraph_vector_destroy(core_2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (!map12) {
+        igraph_vector_destroy(core_1);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+typedef struct {
+    igraph_isocompat_t *node_compat_fn, *edge_compat_fn;
+    void *arg, *carg;
+} igraph_i_iso_cb_data_t;
+
+igraph_bool_t igraph_i_isocompat_node_cb(const igraph_t *graph1,
+        const igraph_t *graph2,
+        const igraph_integer_t g1_num,
+        const igraph_integer_t g2_num,
+        void *arg) {
+    igraph_i_iso_cb_data_t *data = arg;
+    return data->node_compat_fn(graph1, graph2, g1_num, g2_num, data->carg);
+}
+
+igraph_bool_t igraph_i_isocompat_edge_cb(const igraph_t *graph1,
+        const igraph_t *graph2,
+        const igraph_integer_t g1_num,
+        const igraph_integer_t g2_num,
+        void *arg) {
+    igraph_i_iso_cb_data_t *data = arg;
+    return data->edge_compat_fn(graph1, graph2, g1_num, g2_num, data->carg);
+}
+
+igraph_bool_t igraph_i_isomorphic_vf2(igraph_vector_t *map12,
+                                      igraph_vector_t *map21,
+                                      void *arg) {
+    igraph_i_iso_cb_data_t *data = arg;
+    igraph_bool_t *iso = data->arg;
+    IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21);
+    *iso = 1;
+    return 0;         /* don't need to continue */
+}
+
+/**
+ * \function igraph_isomorphic_vf2
+ * \brief Isomorphism via VF2
+ *
+ * </para><para>
+ * This function performs the VF2 algorithm via calling \ref
+ * igraph_isomorphic_function_vf2().
+ *
+ * </para><para> Note that this function cannot be used for
+ * deciding subgraph isomorphism, use \ref igraph_subisomorphic_vf2()
+ * for that.
+ * \param graph1 The first graph, may be directed or undirected.
+ * \param graph2 The second graph. It must have the same directedness
+ *    as \p graph1, otherwise an error is reported.
+ * \param vertex_color1 An optional color vector for the first graph. If
+ *   color vectors are given for both graphs, then the isomorphism is
+ *   calculated on the colored graphs; i.e. two vertices can match
+ *   only if their color also matches. Supply a null pointer here if
+ *   your graphs are not colored.
+ * \param vertex_color2 An optional color vector for the second graph. See
+ *   the previous argument for explanation.
+ * \param edge_color1 An optional edge color vector for the first
+ *   graph. The matching edges in the two graphs must have matching
+ *   colors as well. Supply a null pointer here if your graphs are not
+ *   edge-colored.
+ * \param edge_color2 The edge color vector for the second graph.
+ * \param iso Pointer to a logical constant, the result of the
+ *    algorithm will be placed here.
+ * \param map12 Pointer to an initialized vector or a NULL pointer. If not
+ *    a NULL pointer then the mapping from \p graph1 to \p graph2 is
+ *    stored here. If the graphs are not isomorphic then the vector is
+ *    cleared (ie. has zero elements).
+ * \param map21 Pointer to an initialized vector or a NULL pointer. If not
+ *    a NULL pointer then the mapping from \p graph2 to \p graph1 is
+ *    stored here. If the graphs are not isomorphic then the vector is
+ *    cleared (ie. has zero elements).
+ * \param node_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two nodes are compatible.
+ * \param edge_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two edges are compatible.
+ * \param arg Extra argument to supply to functions \p node_compat_fn
+ *   and \p edge_compat_fn.
+ * \return Error code.
+ *
+ * \sa \ref igraph_subisomorphic_vf2(),
+ * \ref igraph_count_isomorphisms_vf2(),
+ * \ref igraph_get_isomorphisms_vf2(),
+ *
+ * Time complexity: exponential, what did you expect?
+ *
+ * \example examples/simple/igraph_isomorphic_vf2.c
+ */
+
+int igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2,
+                          const igraph_vector_int_t *vertex_color1,
+                          const igraph_vector_int_t *vertex_color2,
+                          const igraph_vector_int_t *edge_color1,
+                          const igraph_vector_int_t *edge_color2,
+                          igraph_bool_t *iso, igraph_vector_t *map12,
+                          igraph_vector_t *map21,
+                          igraph_isocompat_t *node_compat_fn,
+                          igraph_isocompat_t *edge_compat_fn,
+                          void *arg) {
+
+    igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, iso, arg };
+    igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0;
+    igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0;
+    *iso = 0;
+    IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2,
+                 vertex_color1, vertex_color2,
+                 edge_color1, edge_color2,
+                 map12, map21,
+                 (igraph_isohandler_t*)
+                 igraph_i_isomorphic_vf2,
+                 ncb, ecb, &data));
+    if (! *iso) {
+        if (map12) {
+            igraph_vector_clear(map12);
+        }
+        if (map21) {
+            igraph_vector_clear(map21);
+        }
+    }
+    return 0;
+}
+
+igraph_bool_t igraph_i_count_isomorphisms_vf2(const igraph_vector_t *map12,
+        const igraph_vector_t *map21,
+        void *arg) {
+    igraph_i_iso_cb_data_t *data = arg;
+    igraph_integer_t *count = data->arg;
+    IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21);
+    *count += 1;
+    return 1;         /* always continue */
+}
+
+/**
+ * \function igraph_count_isomorphisms_vf2
+ * Number of isomorphisms via VF2
+ *
+ * This function counts the number of isomorphic mappings between two
+ * graphs. It uses the generic \ref igraph_isomorphic_function_vf2()
+ * function.
+ * \param graph1 The first input graph, may be directed or undirected.
+ * \param graph2 The second input graph, it must have the same
+ *   directedness as \p graph1, or an error will be reported.
+ * \param vertex_color1 An optional color vector for the first graph. If
+ *   color vectors are given for both graphs, then the isomorphism is
+ *   calculated on the colored graphs; i.e. two vertices can match
+ *   only if their color also matches. Supply a null pointer here if
+ *   your graphs are not colored.
+ * \param vertex_color2 An optional color vector for the second graph. See
+ *   the previous argument for explanation.
+ * \param edge_color1 An optional edge color vector for the first
+ *   graph. The matching edges in the two graphs must have matching
+ *   colors as well. Supply a null pointer here if your graphs are not
+ *   edge-colored.
+ * \param edge_color2 The edge color vector for the second graph.
+ * \param count Point to an integer, the result will be stored here.
+ * \param node_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two nodes are compatible.
+ * \param edge_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two edges are compatible.
+ * \param arg Extra argument to supply to functions \p node_compat_fn and
+ *   \p edge_compat_fn.
+ * \return Error code.
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2,
+                                  const igraph_vector_int_t *vertex_color1,
+                                  const igraph_vector_int_t *vertex_color2,
+                                  const igraph_vector_int_t *edge_color1,
+                                  const igraph_vector_int_t *edge_color2,
+                                  igraph_integer_t *count,
+                                  igraph_isocompat_t *node_compat_fn,
+                                  igraph_isocompat_t *edge_compat_fn,
+                                  void *arg) {
+
+    igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn,
+                                    count, arg
+                                  };
+    igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0;
+    igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0;
+    *count = 0;
+    IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2,
+                 vertex_color1, vertex_color2,
+                 edge_color1, edge_color2,
+                 0, 0,
+                 (igraph_isohandler_t*)
+                 igraph_i_count_isomorphisms_vf2,
+                 ncb, ecb, &data));
+    return 0;
+}
+
+void igraph_i_get_isomorphisms_free(igraph_vector_ptr_t *data) {
+    long int i, n = igraph_vector_ptr_size(data);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *vec = VECTOR(*data)[i];
+        igraph_vector_destroy(vec);
+        igraph_free(vec);
+    }
+}
+
+igraph_bool_t igraph_i_get_isomorphisms_vf2(const igraph_vector_t *map12,
+        const igraph_vector_t *map21,
+        void *arg) {
+
+    igraph_i_iso_cb_data_t *data = arg;
+    igraph_vector_ptr_t *ptrvector = data->arg;
+    igraph_vector_t *newvector = igraph_Calloc(1, igraph_vector_t);
+    IGRAPH_UNUSED(map12);
+    if (!newvector) {
+        igraph_error("Out of memory", __FILE__, __LINE__, IGRAPH_ENOMEM);
+        return 0;           /* stop right here */
+    }
+    IGRAPH_FINALLY(igraph_free, newvector);
+    IGRAPH_CHECK(igraph_vector_copy(newvector, map21));
+    IGRAPH_FINALLY(igraph_vector_destroy, newvector);
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(ptrvector, newvector));
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 1;         /* continue finding subisomorphisms */
+}
+
+/**
+ * \function igraph_get_isomorphisms_vf2
+ * Collect the isomorphic mappings
+ *
+ * This function finds all the isomorphic mappings between two
+ * graphs. It uses the \ref igraph_isomorphic_function_vf2()
+ * function. Call the function with the same graph as \p graph1 and \p
+ * graph2 to get automorphisms.
+ * \param graph1 The first input graph, may be directed or undirected.
+ * \param graph2 The second input graph, it must have the same
+ *   directedness as \p graph1, or an error will be reported.
+ * \param vertex_color1 An optional color vector for the first graph. If
+ *   color vectors are given for both graphs, then the isomorphism is
+ *   calculated on the colored graphs; i.e. two vertices can match
+ *   only if their color also matches. Supply a null pointer here if
+ *   your graphs are not colored.
+ * \param vertex_color2 An optional color vector for the second graph. See
+ *   the previous argument for explanation.
+ * \param edge_color1 An optional edge color vector for the first
+ *   graph. The matching edges in the two graphs must have matching
+ *   colors as well. Supply a null pointer here if your graphs are not
+ *   edge-colored.
+ * \param edge_color2 The edge color vector for the second graph.
+ * \param maps Pointer vector. On return it is empty if the input graphs
+ *   are no isomorphic. Otherwise it contains pointers to
+ *   <type>igraph_vector_t</type> objects, each vector is an
+ *   isomorphic mapping of \p graph2 to \p graph1. Please note that
+ *   you need to 1) Destroy the vectors via \ref
+ *   igraph_vector_destroy(), 2) free them via
+ *   <function>free()</function> and then 3) call \ref
+ *   igraph_vector_ptr_destroy() on the pointer vector to deallocate all
+ *   memory when \p maps is no longer needed.
+ * \param node_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two nodes are compatible.
+ * \param edge_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two edges are compatible.
+ * \param arg Extra argument to supply to functions \p node_compat_fn
+ *   and \p edge_compat_fn.
+ * \return Error code.
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_get_isomorphisms_vf2(const igraph_t *graph1,
+                                const igraph_t *graph2,
+                                const igraph_vector_int_t *vertex_color1,
+                                const igraph_vector_int_t *vertex_color2,
+                                const igraph_vector_int_t *edge_color1,
+                                const igraph_vector_int_t *edge_color2,
+                                igraph_vector_ptr_t *maps,
+                                igraph_isocompat_t *node_compat_fn,
+                                igraph_isocompat_t *edge_compat_fn,
+                                void *arg) {
+
+    igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, maps, arg };
+    igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0;
+    igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0;
+
+    igraph_vector_ptr_clear(maps);
+    IGRAPH_FINALLY(igraph_i_get_isomorphisms_free, maps);
+    IGRAPH_CHECK(igraph_isomorphic_function_vf2(graph1, graph2,
+                 vertex_color1, vertex_color2,
+                 edge_color1, edge_color2,
+                 0, 0,
+                 (igraph_isohandler_t*)
+                 igraph_i_get_isomorphisms_vf2,
+                 ncb, ecb, &data));
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+
+/**
+ * \function igraph_subisomorphic
+ * Decide subgraph isomorphism
+ *
+ * Check whether \p graph2 is isomorphic to a subgraph of \p graph1.
+ * Currently this function just calls \ref igraph_subisomorphic_vf2()
+ * for all graphs.
+ * \param graph1 The first input graph, may be directed or
+ *   undirected. This is supposed to be the bigger graph.
+ * \param graph2 The second input graph, it must have the same
+ *   directedness as \p graph2, or an error is triggered. This is
+ *   supposed to be the smaller graph.
+ * \param iso Pointer to a boolean, the result is stored here.
+ * \return Error code.
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2,
+                         igraph_bool_t *iso) {
+
+    return igraph_subisomorphic_vf2(graph1, graph2, 0, 0, 0, 0, iso, 0, 0, 0, 0, 0);
+}
+
+/**
+ * \function igraph_subisomorphic_function_vf2
+ * Generic VF2 function for subgraph isomorphism problems
+ *
+ * This function is the pair of \ref igraph_isomorphic_function_vf2(),
+ * for subgraph isomorphism problems. It searches for subgraphs of \p
+ * graph1 which are isomorphic to \p graph2. When it founds an
+ * isomorphic mapping it calls the supplied callback \p isohandler_fn.
+ * The mapping (and its inverse) and the additional \p arg argument
+ * are supplied to the callback.
+ * \param graph1 The first input graph, may be directed or
+ *    undirected. This is supposed to be the larger graph.
+ * \param graph2 The second input graph, it must have the same
+ *    directedness as \p graph1. This is supposed to be the smaller
+ *    graph.
+ * \param vertex_color1 An optional color vector for the first graph. If
+ *   color vectors are given for both graphs, then the subgraph isomorphism is
+ *   calculated on the colored graphs; i.e. two vertices can match
+ *   only if their color also matches. Supply a null pointer here if
+ *   your graphs are not colored.
+ * \param vertex_color2 An optional color vector for the second graph. See
+ *   the previous argument for explanation.
+ * \param edge_color1 An optional edge color vector for the first
+ *   graph. The matching edges in the two graphs must have matching
+ *   colors as well. Supply a null pointer here if your graphs are not
+ *   edge-colored.
+ * \param edge_color2 The edge color vector for the second graph.
+ * \param map12 Pointer to a vector or \c NULL. If not \c NULL, then an
+ *    isomorphic mapping from \p graph1 to \p graph2 is stored here.
+ * \param map21 Pointer to a vector ot \c NULL. If not \c NULL, then
+ *    an isomorphic mapping from \p graph2 to \p graph1 is stored
+ *    here.
+ * \param isohandler_fn A pointer to a function of type \ref
+ *   igraph_isohandler_t. This will be called whenever a subgraph
+ *   isomorphism is found. If the function returns with a non-zero value
+ *   then the search is continued, otherwise it stops and the function
+ *   returns.
+ * \param node_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two nodes are compatible.
+ * \param edge_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two edges are compatible.
+ * \param arg Extra argument to supply to functions \p isohandler_fn, \p
+ *   node_compat_fn and \p edge_compat_fn.
+ * \return Error code.
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_subisomorphic_function_vf2(const igraph_t *graph1,
+                                      const igraph_t *graph2,
+                                      const igraph_vector_int_t *vertex_color1,
+                                      const igraph_vector_int_t *vertex_color2,
+                                      const igraph_vector_int_t *edge_color1,
+                                      const igraph_vector_int_t *edge_color2,
+                                      igraph_vector_t *map12,
+                                      igraph_vector_t *map21,
+                                      igraph_isohandler_t *isohandler_fn,
+                                      igraph_isocompat_t *node_compat_fn,
+                                      igraph_isocompat_t *edge_compat_fn,
+                                      void *arg) {
+
+    long int no_of_nodes1 = igraph_vcount(graph1),
+             no_of_nodes2 = igraph_vcount(graph2);
+    long int no_of_edges1 = igraph_ecount(graph1),
+             no_of_edges2 = igraph_ecount(graph2);
+    igraph_vector_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2;
+    igraph_vector_t in_1, in_2, out_1, out_2;
+    long int in_1_size = 0, in_2_size = 0, out_1_size = 0, out_2_size = 0;
+    igraph_vector_t *inneis_1, *inneis_2, *outneis_1, *outneis_2;
+    long int matched_nodes = 0;
+    long int depth;
+    long int cand1, cand2;
+    long int last1, last2;
+    igraph_stack_t path;
+    igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2;
+    igraph_vector_t indeg1, indeg2, outdeg1, outdeg2;
+
+    if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) {
+        IGRAPH_ERROR("Cannot compare directed and undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+
+    if (no_of_nodes1 < no_of_nodes2 ||
+        no_of_edges1 < no_of_edges2) {
+        return 0;
+    }
+
+    if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) {
+        IGRAPH_WARNING("Only one graph is vertex colored, colors will be ignored");
+        vertex_color1 = vertex_color2 = 0;
+    }
+
+    if ( (edge_color1 && !edge_color2) || (!edge_color1 && edge_color2) ) {
+        IGRAPH_WARNING("Only one graph is edge colored, colors will be ignored");
+        edge_color1 = edge_color2 = 0;
+    }
+
+    if (vertex_color1) {
+        if (igraph_vector_int_size(vertex_color1) != no_of_nodes1 ||
+            igraph_vector_int_size(vertex_color2) != no_of_nodes2) {
+            IGRAPH_ERROR("Invalid vertex color vector length", IGRAPH_EINVAL);
+        }
+    }
+
+    if (edge_color1) {
+        if (igraph_vector_int_size(edge_color1) != no_of_edges1 ||
+            igraph_vector_int_size(edge_color2) != no_of_edges2) {
+            IGRAPH_ERROR("Invalid edge color vector length", IGRAPH_EINVAL);
+        }
+    }
+
+    /* Check color distribution */
+    if (vertex_color1) {
+        /* TODO */
+    }
+
+    /* Check edge color distribution */
+    if (edge_color1) {
+        /* TODO */
+    }
+
+    if (map12) {
+        core_1 = map12;
+        IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes1));
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes1);
+    }
+    igraph_vector_fill(core_1, -1);
+    if (map21) {
+        core_2 = map21;
+        IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes2));
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes2);
+    }
+    igraph_vector_fill(core_2, -1);
+    IGRAPH_VECTOR_INIT_FINALLY(&in_1, no_of_nodes1);
+    IGRAPH_VECTOR_INIT_FINALLY(&in_2, no_of_nodes2);
+    IGRAPH_VECTOR_INIT_FINALLY(&out_1, no_of_nodes1);
+    IGRAPH_VECTOR_INIT_FINALLY(&out_2, no_of_nodes2);
+    IGRAPH_CHECK(igraph_stack_init(&path, 0));
+    IGRAPH_FINALLY(igraph_stack_destroy, &path);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &inadj1, IGRAPH_IN,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj1);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph1, &outadj1, IGRAPH_OUT,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj1);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &inadj2, IGRAPH_IN,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inadj2);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph2, &outadj2, IGRAPH_OUT,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &outadj2);
+    IGRAPH_VECTOR_INIT_FINALLY(&indeg1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&indeg2, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&outdeg1, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&outdeg2, 0);
+
+    IGRAPH_CHECK(igraph_stack_reserve(&path, no_of_nodes2 * 2));
+    IGRAPH_CHECK(igraph_degree(graph1, &indeg1, igraph_vss_all(),
+                               IGRAPH_IN, IGRAPH_LOOPS));
+    IGRAPH_CHECK(igraph_degree(graph2, &indeg2, igraph_vss_all(),
+                               IGRAPH_IN, IGRAPH_LOOPS));
+    IGRAPH_CHECK(igraph_degree(graph1, &outdeg1, igraph_vss_all(),
+                               IGRAPH_OUT, IGRAPH_LOOPS));
+    IGRAPH_CHECK(igraph_degree(graph2, &outdeg2, igraph_vss_all(),
+                               IGRAPH_OUT, IGRAPH_LOOPS));
+
+    depth = 0; last1 = -1; last2 = -1;
+    while (depth >= 0) {
+        long int i;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        cand1 = -1; cand2 = -1;
+        /* Search for the next pair to try */
+        if ((in_1_size < in_2_size) ||
+            (out_1_size < out_2_size)) {
+            /* step back, nothing to do */
+        } else if (out_1_size > 0 && out_2_size > 0) {
+            /**************************************************************/
+            /* cand2, search not always needed */
+            if (last2 >= 0) {
+                cand2 = last2;
+            } else {
+                i = 0;
+                while (cand2 < 0 && i < no_of_nodes2) {
+                    if (VECTOR(out_2)[i] > 0 && VECTOR(*core_2)[i] < 0) {
+                        cand2 = i;
+                    }
+                    i++;
+                }
+            }
+            /* search for cand1 now, it should be bigger than last1 */
+            i = last1 + 1;
+            while (cand1 < 0 && i < no_of_nodes1) {
+                if (VECTOR(out_1)[i] > 0 && VECTOR(*core_1)[i] < 0) {
+                    cand1 = i;
+                }
+                i++;
+            }
+        } else if (in_1_size > 0 && in_2_size > 0) {
+            /**************************************************************/
+            /* cand2, search not always needed */
+            if (last2 >= 0) {
+                cand2 = last2;
+            } else {
+                i = 0;
+                while (cand2 < 0 && i < no_of_nodes2) {
+                    if (VECTOR(in_2)[i] > 0 && VECTOR(*core_2)[i] < 0) {
+                        cand2 = i;
+                    }
+                    i++;
+                }
+            }
+            /* search for cand1 now, should be bigger than last1 */
+            i = last1 + 1;
+            while (cand1 < 0 && i < no_of_nodes1) {
+                if (VECTOR(in_1)[i] > 0 && VECTOR(*core_1)[i] < 0) {
+                    cand1 = i;
+                }
+                i++;
+            }
+        } else {
+            /**************************************************************/
+            /* cand2, search not always needed */
+            if (last2 >= 0) {
+                cand2 = last2;
+            } else {
+                i = 0;
+                while (cand2 < 0 && i < no_of_nodes2) {
+                    if (VECTOR(*core_2)[i] < 0) {
+                        cand2 = i;
+                    }
+                    i++;
+                }
+            }
+            /* search for cand1, should be bigger than last1 */
+            i = last1 + 1;
+            while (cand1 < 0 && i < no_of_nodes1) {
+                if (VECTOR(*core_1)[i] < 0) {
+                    cand1 = i;
+                }
+                i++;
+            }
+        }
+
+        /* Ok, we have cand1, cand2 as candidates. Or not? */
+        if (cand1 < 0 || cand2 < 0) {
+            /**************************************************************/
+            /* dead end, step back, if possible. Otherwise we'll terminate */
+            if (depth >= 1) {
+                last2 = (long int) igraph_stack_pop(&path);
+                last1 = (long int) igraph_stack_pop(&path);
+                matched_nodes -= 1;
+                VECTOR(*core_1)[last1] = -1;
+                VECTOR(*core_2)[last2] = -1;
+
+                if (VECTOR(in_1)[last1] != 0) {
+                    in_1_size += 1;
+                }
+                if (VECTOR(out_1)[last1] != 0) {
+                    out_1_size += 1;
+                }
+                if (VECTOR(in_2)[last2] != 0) {
+                    in_2_size += 1;
+                }
+                if (VECTOR(out_2)[last2] != 0) {
+                    out_2_size += 1;
+                }
+
+                inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1);
+                for (i = 0; i < igraph_vector_size(inneis_1); i++) {
+                    long int node = (long int) VECTOR(*inneis_1)[i];
+                    if (VECTOR(in_1)[node] == depth) {
+                        VECTOR(in_1)[node] = 0;
+                        in_1_size -= 1;
+                    }
+                }
+                outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) last1);
+                for (i = 0; i < igraph_vector_size(outneis_1); i++) {
+                    long int node = (long int) VECTOR(*outneis_1)[i];
+                    if (VECTOR(out_1)[node] == depth) {
+                        VECTOR(out_1)[node] = 0;
+                        out_1_size -= 1;
+                    }
+                }
+                inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) last2);
+                for (i = 0; i < igraph_vector_size(inneis_2); i++) {
+                    long int node = (long int) VECTOR(*inneis_2)[i];
+                    if (VECTOR(in_2)[node] == depth) {
+                        VECTOR(in_2)[node] = 0;
+                        in_2_size -= 1;
+                    }
+                }
+                outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) last2);
+                for (i = 0; i < igraph_vector_size(outneis_2); i++) {
+                    long int node = (long int) VECTOR(*outneis_2)[i];
+                    if (VECTOR(out_2)[node] == depth) {
+                        VECTOR(out_2)[node] = 0;
+                        out_2_size -= 1;
+                    }
+                }
+
+            } /* end of stepping back */
+
+            depth -= 1;
+
+        } else {
+            /**************************************************************/
+            /* step forward if worth, check if worth first */
+            long int xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0;
+            igraph_bool_t end = 0;
+            inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1);
+            outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1);
+            inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2);
+            outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2);
+            if (VECTOR(indeg1)[cand1] < VECTOR(indeg2)[cand2] ||
+                VECTOR(outdeg1)[cand1] < VECTOR(outdeg2)[cand2]) {
+                end = 1;
+            }
+            if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) {
+                end = 1;
+            }
+            if (node_compat_fn && !node_compat_fn(graph1, graph2,
+                                                  (igraph_integer_t) cand1,
+                                                  (igraph_integer_t) cand2, arg)) {
+                end = 1;
+            }
+
+            for (i = 0; !end && i < igraph_vector_size(inneis_1); i++) {
+                long int node = (long int) VECTOR(*inneis_1)[i];
+                if (VECTOR(*core_1)[node] < 0) {
+                    if (VECTOR(in_1)[node] != 0) {
+                        xin1++;
+                    }
+                    if (VECTOR(out_1)[node] != 0) {
+                        xout1++;
+                    }
+                }
+            }
+            for (i = 0; !end && i < igraph_vector_size(outneis_1); i++) {
+                long int node = (long int) VECTOR(*outneis_1)[i];
+                if (VECTOR(*core_1)[node] < 0) {
+                    if (VECTOR(in_1)[node] != 0) {
+                        xin1++;
+                    }
+                    if (VECTOR(out_1)[node] != 0) {
+                        xout1++;
+                    }
+                }
+            }
+            for (i = 0; !end && i < igraph_vector_size(inneis_2); i++) {
+                long int node = (long int) VECTOR(*inneis_2)[i];
+                if (VECTOR(*core_2)[node] >= 0) {
+                    long int node2 = (long int) VECTOR(*core_2)[node];
+                    /* check if there is a node2->cand1 edge */
+                    if (!igraph_vector_binsearch2(inneis_1, node2)) {
+                        end = 1;
+                    } else if (edge_color1 || edge_compat_fn) {
+                        igraph_integer_t eid1, eid2;
+                        igraph_get_eid(graph1, &eid1, (igraph_integer_t) node2,
+                                       (igraph_integer_t) cand1, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        igraph_get_eid(graph2, &eid2, (igraph_integer_t) node,
+                                       (igraph_integer_t) cand2, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] !=
+                            VECTOR(*edge_color2)[(long int)eid2]) {
+                            end = 1;
+                        }
+                        if (edge_compat_fn && !edge_compat_fn(graph1, graph2,
+                                                              eid1, eid2, arg)) {
+                            end = 1;
+                        }
+                    }
+                } else {
+                    if (VECTOR(in_2)[node] != 0) {
+                        xin2++;
+                    }
+                    if (VECTOR(out_2)[node] != 0) {
+                        xout2++;
+                    }
+                }
+            }
+            for (i = 0; !end && i < igraph_vector_size(outneis_2); i++) {
+                long int node = (long int) VECTOR(*outneis_2)[i];
+                if (VECTOR(*core_2)[node] >= 0) {
+                    long int node2 = (long int) VECTOR(*core_2)[node];
+                    /* check if there is a cand1->node2 edge */
+                    if (!igraph_vector_binsearch2(outneis_1, node2)) {
+                        end = 1;
+                    } else if (edge_color1 || edge_compat_fn) {
+                        igraph_integer_t eid1, eid2;
+                        igraph_get_eid(graph1, &eid1, (igraph_integer_t) cand1,
+                                       (igraph_integer_t) node2, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        igraph_get_eid(graph2, &eid2, (igraph_integer_t) cand2,
+                                       (igraph_integer_t) node, /*directed=*/ 1,
+                                       /*error=*/ 1);
+                        if (edge_color1 && VECTOR(*edge_color1)[(long int)eid1] !=
+                            VECTOR(*edge_color2)[(long int)eid2]) {
+                            end = 1;
+                        }
+                        if (edge_compat_fn && !edge_compat_fn(graph1, graph2,
+                                                              eid1, eid2, arg)) {
+                            end = 1;
+                        }
+                    }
+                } else {
+                    if (VECTOR(in_2)[node] != 0) {
+                        xin2++;
+                    }
+                    if (VECTOR(out_2)[node] != 0) {
+                        xout2++;
+                    }
+                }
+            }
+
+            if (!end && (xin1 >= xin2 && xout1 >= xout2)) {
+                /* Ok, we add the (cand1, cand2) pair to the mapping */
+                depth += 1;
+                IGRAPH_CHECK(igraph_stack_push(&path, cand1));
+                IGRAPH_CHECK(igraph_stack_push(&path, cand2));
+                matched_nodes += 1;
+                VECTOR(*core_1)[cand1] = cand2;
+                VECTOR(*core_2)[cand2] = cand1;
+
+                /* update in_*, out_* */
+                if (VECTOR(in_1)[cand1] != 0) {
+                    in_1_size -= 1;
+                }
+                if (VECTOR(out_1)[cand1] != 0) {
+                    out_1_size -= 1;
+                }
+                if (VECTOR(in_2)[cand2] != 0) {
+                    in_2_size -= 1;
+                }
+                if (VECTOR(out_2)[cand2] != 0) {
+                    out_2_size -= 1;
+                }
+
+                inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1);
+                for (i = 0; i < igraph_vector_size(inneis_1); i++) {
+                    long int node = (long int) VECTOR(*inneis_1)[i];
+                    if (VECTOR(in_1)[node] == 0 && VECTOR(*core_1)[node] < 0) {
+                        VECTOR(in_1)[node] = depth;
+                        in_1_size += 1;
+                    }
+                }
+                outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1);
+                for (i = 0; i < igraph_vector_size(outneis_1); i++) {
+                    long int node = (long int) VECTOR(*outneis_1)[i];
+                    if (VECTOR(out_1)[node] == 0 && VECTOR(*core_1)[node] < 0) {
+                        VECTOR(out_1)[node] = depth;
+                        out_1_size += 1;
+                    }
+                }
+                inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2);
+                for (i = 0; i < igraph_vector_size(inneis_2); i++) {
+                    long int node = (long int) VECTOR(*inneis_2)[i];
+                    if (VECTOR(in_2)[node] == 0 && VECTOR(*core_2)[node] < 0) {
+                        VECTOR(in_2)[node] = depth;
+                        in_2_size += 1;
+                    }
+                }
+                outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2);
+                for (i = 0; i < igraph_vector_size(outneis_2); i++) {
+                    long int node = (long int) VECTOR(*outneis_2)[i];
+                    if (VECTOR(out_2)[node] == 0 && VECTOR(*core_2)[node] < 0) {
+                        VECTOR(out_2)[node] = depth;
+                        out_2_size += 1;
+                    }
+                }
+                last1 = -1; last2 = -1;       /* this the first time here */
+            } else {
+                last1 = cand1;
+                last2 = cand2;
+            }
+
+        }
+
+        if (matched_nodes == no_of_nodes2 && isohandler_fn) {
+            if (!isohandler_fn(core_1, core_2, arg)) {
+                break;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&outdeg2);
+    igraph_vector_destroy(&outdeg1);
+    igraph_vector_destroy(&indeg2);
+    igraph_vector_destroy(&indeg1);
+    igraph_lazy_adjlist_destroy(&outadj2);
+    igraph_lazy_adjlist_destroy(&inadj2);
+    igraph_lazy_adjlist_destroy(&outadj1);
+    igraph_lazy_adjlist_destroy(&inadj1);
+    igraph_stack_destroy(&path);
+    igraph_vector_destroy(&out_2);
+    igraph_vector_destroy(&out_1);
+    igraph_vector_destroy(&in_2);
+    igraph_vector_destroy(&in_1);
+    IGRAPH_FINALLY_CLEAN(13);
+    if (!map21) {
+        igraph_vector_destroy(core_2);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+    if (!map12) {
+        igraph_vector_destroy(core_1);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+igraph_bool_t igraph_i_subisomorphic_vf2(const igraph_vector_t *map12,
+        const igraph_vector_t *map21,
+        void *arg) {
+    igraph_i_iso_cb_data_t *data = arg;
+    igraph_bool_t *iso = data->arg;
+    IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21);
+    *iso = 1;
+    return 0; /* stop */
+}
+
+/**
+ * \function igraph_subisomorphic_vf2
+ * Decide subgraph isomorphism using VF2
+ *
+ * Decides whether a subgraph of \p graph1 is isomorphic to \p
+ * graph2. It uses \ref igraph_subisomorphic_function_vf2().
+ * \param graph1 The first input graph, may be directed or
+ *    undirected. This is supposed to be the larger graph.
+ * \param graph2 The second input graph, it must have the same
+ *    directedness as \p graph1. This is supposed to be the smaller
+ *    graph.
+ * \param vertex_color1 An optional color vector for the first graph. If
+ *   color vectors are given for both graphs, then the subgraph isomorphism is
+ *   calculated on the colored graphs; i.e. two vertices can match
+ *   only if their color also matches. Supply a null pointer here if
+ *   your graphs are not colored.
+ * \param vertex_color2 An optional color vector for the second graph. See
+ *   the previous argument for explanation.
+ * \param edge_color1 An optional edge color vector for the first
+ *   graph. The matching edges in the two graphs must have matching
+ *   colors as well. Supply a null pointer here if your graphs are not
+ *   edge-colored.
+ * \param edge_color2 The edge color vector for the second graph.
+ * \param iso Pointer to a boolean. The result of the decision problem
+ *    is stored here.
+ * \param map12 Pointer to a vector or \c NULL. If not \c NULL, then an
+ *    isomorphic mapping from \p graph1 to \p graph2 is stored here.
+ * \param map21 Pointer to a vector ot \c NULL. If not \c NULL, then
+ *    an isomorphic mapping from \p graph2 to \p graph1 is stored
+ *    here.
+ * \param node_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two nodes are compatible.
+ * \param edge_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two edges are compatible.
+ * \param arg Extra argument to supply to functions \p node_compat_fn
+ *   and \p edge_compat_fn.
+ * \return Error code.
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2,
+                             const igraph_vector_int_t *vertex_color1,
+                             const igraph_vector_int_t *vertex_color2,
+                             const igraph_vector_int_t *edge_color1,
+                             const igraph_vector_int_t *edge_color2,
+                             igraph_bool_t *iso, igraph_vector_t *map12,
+                             igraph_vector_t *map21,
+                             igraph_isocompat_t *node_compat_fn,
+                             igraph_isocompat_t *edge_compat_fn,
+                             void *arg) {
+
+    igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, iso, arg };
+    igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0;
+    igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0;
+
+    *iso = 0;
+    IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2,
+                 vertex_color1, vertex_color2,
+                 edge_color1, edge_color2,
+                 map12, map21,
+                 (igraph_isohandler_t *)
+                 igraph_i_subisomorphic_vf2,
+                 ncb, ecb, &data));
+    if (! *iso) {
+        if (map12) {
+            igraph_vector_clear(map12);
+        }
+        if (map21) {
+            igraph_vector_clear(map21);
+        }
+    }
+    return 0;
+}
+
+igraph_bool_t igraph_i_count_subisomorphisms_vf2(const igraph_vector_t *map12,
+        const igraph_vector_t *map21,
+        void *arg) {
+    igraph_i_iso_cb_data_t *data = arg;
+    igraph_integer_t *count = data->arg;
+    IGRAPH_UNUSED(map12); IGRAPH_UNUSED(map21);
+    *count += 1;
+    return 1;         /* always continue */
+}
+
+/**
+ * \function igraph_count_subisomorphisms_vf2
+ * Number of subgraph isomorphisms using VF2
+ *
+ * Count the number of isomorphisms between subgraphs of \p graph1 and
+ * \p graph2. This function uses \ref
+ * igraph_subisomorphic_function_vf2().
+ * \param graph1 The first input graph, may be directed or
+ *    undirected. This is supposed to be the larger graph.
+ * \param graph2 The second input graph, it must have the same
+ *    directedness as \p graph1. This is supposed to be the smaller
+ *    graph.
+ * \param vertex_color1 An optional color vector for the first graph. If
+ *   color vectors are given for both graphs, then the subgraph isomorphism is
+ *   calculated on the colored graphs; i.e. two vertices can match
+ *   only if their color also matches. Supply a null pointer here if
+ *   your graphs are not colored.
+ * \param vertex_color2 An optional color vector for the second graph. See
+ *   the previous argument for explanation.
+ * \param edge_color1 An optional edge color vector for the first
+ *   graph. The matching edges in the two graphs must have matching
+ *   colors as well. Supply a null pointer here if your graphs are not
+ *   edge-colored.
+ * \param edge_color2 The edge color vector for the second graph.
+ * \param count Pointer to an integer. The number of subgraph
+ *    isomorphisms is stored here.
+ * \param node_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two nodes are compatible.
+ * \param edge_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two edges are compatible.
+ * \param arg Extra argument to supply to functions \p node_compat_fn and
+ *   \p edge_compat_fn.
+ * \return Error code.
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2,
+                                     const igraph_vector_int_t *vertex_color1,
+                                     const igraph_vector_int_t *vertex_color2,
+                                     const igraph_vector_int_t *edge_color1,
+                                     const igraph_vector_int_t *edge_color2,
+                                     igraph_integer_t *count,
+                                     igraph_isocompat_t *node_compat_fn,
+                                     igraph_isocompat_t *edge_compat_fn,
+                                     void *arg) {
+
+    igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn,
+                                    count, arg
+                                  };
+    igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0;
+    igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0;
+    *count = 0;
+    IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2,
+                 vertex_color1, vertex_color2,
+                 edge_color1, edge_color2,
+                 0, 0,
+                 (igraph_isohandler_t*)
+                 igraph_i_count_subisomorphisms_vf2,
+                 ncb, ecb, &data));
+    return 0;
+}
+
+void igraph_i_get_subisomorphisms_free(igraph_vector_ptr_t *data) {
+    long int i, n = igraph_vector_ptr_size(data);
+    for (i = 0; i < n; i++) {
+        igraph_vector_t *vec = VECTOR(*data)[i];
+        igraph_vector_destroy(vec);
+        igraph_free(vec);
+    }
+}
+
+igraph_bool_t igraph_i_get_subisomorphisms_vf2(const igraph_vector_t *map12,
+        const igraph_vector_t *map21,
+        void *arg) {
+
+    igraph_i_iso_cb_data_t *data = arg;
+    igraph_vector_ptr_t *vector = data->arg;
+    igraph_vector_t *newvector = igraph_Calloc(1, igraph_vector_t);
+    IGRAPH_UNUSED(map12);
+    if (!newvector) {
+        igraph_error("Out of memory", __FILE__, __LINE__, IGRAPH_ENOMEM);
+        return 0;           /* stop right here */
+    }
+    IGRAPH_FINALLY(igraph_free, newvector);
+    IGRAPH_CHECK(igraph_vector_copy(newvector, map21));
+    IGRAPH_FINALLY(igraph_vector_destroy, newvector);
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(vector, newvector));
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 1;         /* continue finding subisomorphisms */
+}
+
+/**
+ * \function igraph_get_subisomorphisms_vf2
+ * Return all subgraph isomorphic mappings
+ *
+ * This function collects all isomorphic mappings of \p graph2 to a
+ * subgraph of \p graph1. It uses the \ref
+ * igraph_subisomorphic_function_vf2() function.
+ * \param graph1 The first input graph, may be directed or
+ *    undirected. This is supposed to be the larger graph.
+ * \param graph2 The second input graph, it must have the same
+ *    directedness as \p graph1. This is supposed to be the smaller
+ *    graph.
+ * \param vertex_color1 An optional color vector for the first graph. If
+ *   color vectors are given for both graphs, then the subgraph isomorphism is
+ *   calculated on the colored graphs; i.e. two vertices can match
+ *   only if their color also matches. Supply a null pointer here if
+ *   your graphs are not colored.
+ * \param vertex_color2 An optional color vector for the second graph. See
+ *   the previous argument for explanation.
+ * \param edge_color1 An optional edge color vector for the first
+ *   graph. The matching edges in the two graphs must have matching
+ *   colors as well. Supply a null pointer here if your graphs are not
+ *   edge-colored.
+ * \param edge_color2 The edge color vector for the second graph.
+ * \param maps Pointer vector. On return it contains pointers to
+ *   <type>igraph_vector_t</type> objects, each vector is an
+ *   isomorphic mapping of \p graph2 to a subgraph of \p graph1. Please note that
+ *   you need to 1) Destroy the vectors via \ref
+ *   igraph_vector_destroy(), 2) free them via
+ *   <function>free()</function> and then 3) call \ref
+ *   igraph_vector_ptr_destroy() on the pointer vector to deallocate all
+ *   memory when \p maps is no longer needed.
+ * \param node_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two nodes are compatible.
+ * \param edge_compat_fn A pointer to a function of type \ref
+ *   igraph_isocompat_t. This function will be called by the algorithm to
+ *   determine whether two edges are compatible.
+ * \param arg Extra argument to supply to functions \p node_compat_fn
+ *   and \p edge_compat_fn.
+ * \return Error code.
+ *
+ * Time complexity: exponential.
+ */
+
+int igraph_get_subisomorphisms_vf2(const igraph_t *graph1,
+                                   const igraph_t *graph2,
+                                   const igraph_vector_int_t *vertex_color1,
+                                   const igraph_vector_int_t *vertex_color2,
+                                   const igraph_vector_int_t *edge_color1,
+                                   const igraph_vector_int_t *edge_color2,
+                                   igraph_vector_ptr_t *maps,
+                                   igraph_isocompat_t *node_compat_fn,
+                                   igraph_isocompat_t *edge_compat_fn,
+                                   void *arg) {
+
+    igraph_i_iso_cb_data_t data = { node_compat_fn, edge_compat_fn, maps, arg };
+    igraph_isocompat_t *ncb = node_compat_fn ? igraph_i_isocompat_node_cb : 0;
+    igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0;
+
+    igraph_vector_ptr_clear(maps);
+    IGRAPH_FINALLY(igraph_i_get_subisomorphisms_free, maps);
+    IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2,
+                 vertex_color1, vertex_color2,
+                 edge_color1, edge_color2,
+                 0, 0,
+                 (igraph_isohandler_t*)
+                 igraph_i_get_subisomorphisms_vf2,
+                 ncb, ecb, &data));
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \function igraph_permute_vertices
+ * Permute the vertices
+ *
+ * This function creates a new graph from the input graph by permuting
+ * its vertices according to the specified mapping. Call this function
+ * with the output of \ref igraph_canonical_permutation() to create
+ * the canonical form of a graph.
+ * \param graph The input graph.
+ * \param res Pointer to an uninitialized graph object. The new graph
+ *    is created here.
+ * \param permutation The permutation to apply. Vertex 0 is mapped to
+ *    the first element of the vector, vertex 1 to the second,
+ * etc. Note that it is not checked that the vector contains every
+ *    element only once, and no range checking is performed either.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in terms of the number of
+ * vertices and edges.
+ */
+
+int igraph_permute_vertices(const igraph_t *graph, igraph_t *res,
+                            const igraph_vector_t *permutation) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vector_t edges;
+    long int i, p = 0;
+
+    if (igraph_vector_size(permutation) != no_of_nodes) {
+        IGRAPH_ERROR("Permute vertices: invalid permutation vector size", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
+
+    for (i = 0; i < no_of_edges; i++) {
+        VECTOR(edges)[p++] = VECTOR(*permutation)[ (long int) IGRAPH_FROM(graph, i) ];
+        VECTOR(edges)[p++] = VECTOR(*permutation)[ (long int) IGRAPH_TO(graph, i) ];
+    }
+
+    IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes,
+                               igraph_is_directed(graph)));
+
+    /* Attributes */
+    if (graph->attr) {
+        igraph_vector_t index;
+        igraph_vector_t vtypes;
+        IGRAPH_I_ATTRIBUTE_DESTROY(res);
+        IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/1, /*vertex=*/0, /*edge=*/1);
+        IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0);
+        IGRAPH_CHECK(igraph_i_attribute_get_info(graph, 0, 0, 0, &vtypes, 0, 0));
+        if (igraph_vector_size(&vtypes) != 0) {
+            IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_nodes);
+            for (i = 0; i < no_of_nodes; i++) {
+                VECTOR(index)[ (long int) VECTOR(*permutation)[i] ] = i;
+            }
+            IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, &index));
+            igraph_vector_destroy(&index);
+            IGRAPH_FINALLY_CLEAN(1);
+        }
+        igraph_vector_destroy(&vtypes);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \section about_bliss
+ *
+ * <para>
+ * BLISS is a successor of the famous NAUTY algorithm and
+ * implementation. While using the same ideas in general, with better
+ * heuristics and data structures BLISS outperforms NAUTY on most
+ * graphs.
+ * </para>
+ *
+ * <para>
+ * BLISS was developed and implemented by Tommi Junttila and Petteri Kaski at
+ * Helsinki University of Technology, Finland. For more information,
+ * see the BLISS homepage at http://www.tcs.hut.fi/Software/bliss/ and the publication
+ * Tommi Junttila, Petteri Kaski: "Engineering an Efficient Canonical Labeling
+ * Tool for Large and Sparse Graphs" at https://doi.org/10.1137/1.9781611972870.13
+ * </para>
+ *
+ * <para>
+ * BLISS works with both directed graphs and undirected graphs. It supports graphs with
+ * self-loops, but not graphs with multi-edges.
+ * </para>
+ *
+ * <para>
+ * BLISS version 0.73 is included in igraph.
+ * </para>
+ */
+
+/**
+ * \function igraph_isomorphic_bliss
+ * Graph isomorphism via BLISS
+ *
+ * This function uses the BLISS graph isomorphism algorithm, a
+ * successor of the famous NAUTY algorithm and implementation. BLISS
+ * is open source and licensed according to the GNU GPL. See
+ * http://www.tcs.hut.fi/Software/bliss/index.html for
+ * details. Currently the 0.73 version of BLISS is included in igraph.
+ *
+ * </para><para>
+ *
+ * \param graph1 The first input graph. Multiple edges between the same nodes
+ *   are not supported and will cause an incorrect result to be returned.
+ * \param graph2 The second input graph. Multiple edges between the same nodes
+ *   are not supported and will cause an incorrect result to be returned.
+ * \param colors1 An optional vertex color vector for the first graph. Supply a
+ *   null pointer if your graph is not colored.
+ * \param colors2 An optional vertex color vector for the second graph. Supply a
+ *   null pointer if your graph is not colored.
+ * \param iso Pointer to a boolean, the result is stored here.
+ * \param map12 A vector or \c NULL pointer. If not \c NULL then an
+ *   isomorphic mapping from \p graph1 to \p graph2 is stored here.
+ *   If the input graphs are not isomorphic then this vector is
+ *   cleared, i.e. it will have length zero.
+ * \param map21 Similar to \p map12, but for the mapping from \p
+ *   graph2 to \p graph1.
+ * \param sh Splitting heuristics to be used for the graphs. See
+ *   \ref igraph_bliss_sh_t.
+ * \param info1 If not \c NULL, information about the canonization of
+ *    the first input graph is stored here. See \ref igraph_bliss_info_t
+ *    for details. Note that if the two graphs have different number
+ *    of vertices or edges, then this is not filled.
+ * \param info2 Same as \p info1, but for the second graph.
+ * \return Error code.
+ *
+ * Time complexity: exponential, but in practice it is quite fast.
+ */
+
+int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2,
+                            const igraph_vector_int_t *colors1, const igraph_vector_int_t *colors2,
+                            igraph_bool_t *iso, igraph_vector_t *map12,
+                            igraph_vector_t *map21, igraph_bliss_sh_t sh,
+                            igraph_bliss_info_t *info1, igraph_bliss_info_t *info2) {
+
+    long int no_of_nodes = igraph_vcount(graph1);
+    long int no_of_edges = igraph_ecount(graph1);
+    igraph_vector_t perm1, perm2;
+    igraph_vector_t vmap12, *mymap12 = &vmap12;
+    igraph_vector_t from, to, index;
+    igraph_vector_t from2, to2, index2;
+    igraph_bool_t directed;
+    long int i, j;
+
+    *iso = 0;
+    if (info1) {
+        info1->nof_nodes = info1->nof_leaf_nodes = info1->nof_bad_nodes =
+                               info1->nof_canupdates = info1->max_level = info1->nof_generators = -1;
+        info1->group_size = 0;
+    }
+    if (info2) {
+        info2->nof_nodes = info2->nof_leaf_nodes = info2->nof_bad_nodes =
+                               info2->nof_canupdates = info2->max_level = info2->nof_generators = -1;
+        info2->group_size = 0;
+    }
+
+    directed = igraph_is_directed(graph1);
+    if (igraph_is_directed(graph2) != directed) {
+        IGRAPH_ERROR("Cannot compare directed and undirected graphs",
+                     IGRAPH_EINVAL);
+    }
+    if ((colors1 == NULL || colors2 == NULL) && colors1 != colors2) {
+        IGRAPH_WARNING("Only one of the graphs is vertex colored, colors will be ignored");
+        colors1 = NULL; colors2 = NULL;
+    }
+
+    if (no_of_nodes != igraph_vcount(graph2) ||
+        no_of_edges != igraph_ecount(graph2)) {
+        if (map12) {
+            igraph_vector_clear(map12);
+        }
+        if (map21) {
+            igraph_vector_clear(map21);
+        }
+        return 0;
+    }
+
+    if (map12) {
+        mymap12 = map12;
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(mymap12, 0);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&perm1, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&perm2, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_canonical_permutation(graph1, colors1, &perm1, sh, info1));
+    IGRAPH_CHECK(igraph_canonical_permutation(graph2, colors2, &perm2, sh, info2));
+
+    IGRAPH_CHECK(igraph_vector_resize(mymap12, no_of_nodes));
+
+    /* The inverse of perm2 is produced in mymap12 */
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(*mymap12)[ (long int)VECTOR(perm2)[i] ] = i;
+    }
+    /* Now we produce perm2^{-1} o perm1 in perm2 */
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(perm2)[i] = VECTOR(*mymap12)[ (long int) VECTOR(perm1)[i] ];
+    }
+    /* Copy it to mymap12 */
+    igraph_vector_update(mymap12, &perm2);
+
+    igraph_vector_destroy(&perm1);
+    igraph_vector_destroy(&perm2);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    /* Check isomorphism, we apply the permutation in mymap12 to graph1
+       and should get graph2 */
+
+    IGRAPH_VECTOR_INIT_FINALLY(&from, no_of_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&to, no_of_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&from2, no_of_edges * 2);
+    IGRAPH_VECTOR_INIT_FINALLY(&to2, no_of_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&index2, no_of_edges);
+
+    for (i = 0; i < no_of_edges; i++) {
+        VECTOR(from)[i] = VECTOR(*mymap12)[ (long int) IGRAPH_FROM(graph1, i) ];
+        VECTOR(to)[i]   = VECTOR(*mymap12)[ (long int) IGRAPH_TO  (graph1, i) ];
+        if (! directed && VECTOR(from)[i] < VECTOR(to)[i]) {
+            igraph_real_t tmp = VECTOR(from)[i];
+            VECTOR(from)[i] = VECTOR(to)[i];
+            VECTOR(to)[i] = tmp;
+        }
+    }
+    igraph_vector_order(&from, &to, &index, no_of_nodes);
+
+    igraph_get_edgelist(graph2, &from2, /*bycol=*/ 1);
+    for (i = 0, j = no_of_edges; i < no_of_edges; i++, j++) {
+        VECTOR(to2)[i] = VECTOR(from2)[j];
+        if (! directed && VECTOR(from2)[i] < VECTOR(to2)[i]) {
+            igraph_real_t tmp = VECTOR(from2)[i];
+            VECTOR(from2)[i] = VECTOR(to2)[i];
+            VECTOR(to2)[i] = tmp;
+        }
+    }
+    igraph_vector_resize(&from2, no_of_edges);
+    igraph_vector_order(&from2, &to2, &index2, no_of_nodes);
+
+    *iso = 1;
+    for (i = 0; i < no_of_edges; i++) {
+        long int i1 = (long int) VECTOR(index)[i];
+        long int i2 = (long int) VECTOR(index2)[i];
+        if (VECTOR(from)[i1] != VECTOR(from2)[i2] ||
+            VECTOR(to)[i1] != VECTOR(to2)[i2]) {
+            *iso = 0;
+            break;
+        }
+    }
+
+    /* If the graphs are coloured, we also need to check that applying the
+       permutation mymap12 to colors1 gives colors2. */
+
+    if (*iso && colors1 != NULL) {
+        for (i = 0; i < no_of_nodes; i++) {
+            if (VECTOR(*colors1)[i] != VECTOR(*colors2)[(long int) VECTOR(*mymap12)[i] ]) {
+                *iso = 0;
+                break;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&index2);
+    igraph_vector_destroy(&to2);
+    igraph_vector_destroy(&from2);
+    igraph_vector_destroy(&index);
+    igraph_vector_destroy(&to);
+    igraph_vector_destroy(&from);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    if (*iso) {
+        /* The inverse of mymap12 */
+        if (map21) {
+            IGRAPH_CHECK(igraph_vector_resize(map21, no_of_nodes));
+            for (i = 0; i < no_of_nodes; i++) {
+                VECTOR(*map21)[ (long int) VECTOR(*mymap12)[i] ] = i;
+            }
+        }
+    } else {
+        if (map12) {
+            igraph_vector_clear(map12);
+        }
+        if (map21) {
+            igraph_vector_clear(map21);
+        }
+    }
+
+    if (!map12) {
+        igraph_vector_destroy(mymap12);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+
+/**
+ * \function igraph_simplify_and_colorize
+ * \brief Simplify the graph and compute self-loop and edge multiplicities.
+ *
+ * </para><para>
+ * This function creates a vertex and edge colored simple graph from the input
+ * graph. The vertex colors are computed as the number of incident self-loops
+ * to each vertex in the input graph. The edge colors are computed as the number of
+ * parallel edges in the input graph that were merged to create each edge
+ * in the simple graph.
+ *
+ * </para><para>
+ * The resulting colored simple graph is suitable for use by isomorphism checking
+ * algorithms such as VF2, which only support simple graphs, but can consider
+ * vertex and edge colors.
+ *
+ * \param graph The graph object, typically having self-loops or multi-edges.
+ * \param res An uninitialized graph object. The result will be stored here
+ * \param vertex_color Computed vertex colors corresponding to self-loop multiplicities.
+ * \param edge_color Computed edge colors corresponding to edge multiplicities
+ * \return Error code.
+ *
+ * \sa \ref igraph_simplify(), \ref igraph_isomorphic_vf2(), \ref igraph_subisomorphic_vf2()
+ *
+ */
+int igraph_simplify_and_colorize(
+    const igraph_t *graph, igraph_t *res,
+    igraph_vector_int_t *vertex_color, igraph_vector_int_t *edge_color) {
+    igraph_es_t es;
+    igraph_eit_t eit;
+    igraph_vector_t edges;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    long int pto = -1, pfrom = -1;
+    long int i;
+
+    IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_FROM));
+    IGRAPH_FINALLY(igraph_es_destroy, &es);
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2));
+
+    IGRAPH_CHECK(igraph_vector_int_resize(vertex_color, no_of_nodes));
+    igraph_vector_int_null(vertex_color);
+
+    IGRAPH_CHECK(igraph_vector_int_resize(edge_color, no_of_edges));
+    igraph_vector_int_null(edge_color);
+
+    i = -1;
+    for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
+        long int edge = IGRAPH_EIT_GET(eit);
+        long int from = IGRAPH_FROM(graph, edge);
+        long int to   = IGRAPH_TO(graph, edge);
+
+        if (to == from) {
+            VECTOR(*vertex_color)[to]++;
+            continue;
+        }
+
+        if (to == pto && from == pfrom) {
+            VECTOR(*edge_color)[i]++;
+        } else {
+            igraph_vector_push_back(&edges, from);
+            igraph_vector_push_back(&edges, to);
+            i++;
+            VECTOR(*edge_color)[i] = 1;
+        }
+
+        pfrom = from; pto = to;
+    }
+
+    igraph_vector_int_resize(edge_color, i + 1);
+
+    igraph_eit_destroy(&eit);
+    igraph_es_destroy(&es);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph)));
+
+    igraph_vector_destroy(&edges);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return IGRAPH_SUCCESS;
+}
diff --git a/igraph/src/triangles.c b/igraph/src/triangles.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/triangles.c
@@ -0,0 +1,978 @@
+/* -*- mode: C -*-  */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_transitivity.h"
+#include "igraph_interface.h"
+#include "igraph_adjlist.h"
+#include "igraph_memory.h"
+#include "igraph_interrupt_internal.h"
+#include "igraph_centrality.h"
+#include "igraph_motifs.h"
+#include "igraph_structural.h"
+
+/**
+ * \function igraph_transitivity_avglocal_undirected
+ * \brief Average local transitivity (clustering coefficient).
+ *
+ * The transitivity measures the probability that two neighbors of a
+ * vertex are connected. In case of the average local transitivity,
+ * this probability is calculated for each vertex and then the average
+ * is taken. Vertices with less than two neighbors require special treatment,
+ * they will either be left out from the calculation or they will be considered
+ * as having zero transitivity, depending on the \c mode argument.
+ *
+ * </para><para>
+ * Note that this measure is different from the global transitivity measure
+ * (see \ref igraph_transitivity_undirected() ) as it simply takes the
+ * average local transitivity across the whole network. See the following
+ * reference for more details:
+ *
+ * </para><para>
+ * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks.
+ * Nature 393(6684):440-442 (1998).
+ *
+ * </para><para>
+ * Clustering coefficient is an alternative name for transitivity.
+ *
+ * \param graph The input graph, directed graphs are considered as
+ *    undirected ones.
+ * \param res Pointer to a real variable, the result will be stored here.
+ * \param mode Defines how to treat vertices with degree less than two.
+ *    \c IGRAPH_TRANSITIVITY_NAN leaves them out from averaging,
+ *    \c IGRAPH_TRANSITIVITY_ZERO includes them with zero transitivity.
+ *    The result will be \c NaN if the mode is \c IGRAPH_TRANSITIVITY_NAN
+ *    and there are no vertices with more than one neighbor.
+ *
+ * \return Error code.
+ *
+ * \sa \ref igraph_transitivity_undirected(), \ref
+ * igraph_transitivity_local_undirected().
+ *
+ * Time complexity: O(|V|*d^2), |V| is the number of vertices in the
+ * graph and d is the average degree.
+ */
+
+int igraph_transitivity_avglocal_undirected(const igraph_t *graph,
+        igraph_real_t *res,
+        igraph_transitivity_mode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_real_t sum = 0.0;
+    igraph_integer_t count = 0;
+    long int node, i, j, nn;
+    igraph_adjlist_t allneis;
+    igraph_vector_int_t *neis1, *neis2;
+    long int neilen1, neilen2;
+    long int *neis;
+    long int maxdegree;
+
+    igraph_vector_t order;
+    igraph_vector_t rank;
+    igraph_vector_t degree;
+    igraph_vector_t triangles;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
+                               IGRAPH_LOOPS));
+    maxdegree = (long int) igraph_vector_max(&degree) + 1;
+    igraph_vector_order1(&degree, &order, maxdegree);
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(rank)[ (long int) VECTOR(order)[i] ] = no_of_nodes - i - 1;
+    }
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+    IGRAPH_CHECK(igraph_adjlist_simplify(&allneis));
+
+    neis = igraph_Calloc(no_of_nodes, long int);
+    if (neis == 0) {
+        IGRAPH_ERROR("undirected average local transitivity failed",
+                     IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, neis);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&triangles, no_of_nodes);
+
+    for (nn = no_of_nodes - 1; nn >= 0; nn--) {
+        node = (long int) VECTOR(order)[nn];
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        neis1 = igraph_adjlist_get(&allneis, node);
+        neilen1 = igraph_vector_int_size(neis1);
+        /* Mark the neighbors of 'node' */
+        for (i = 0; i < neilen1; i++) {
+            neis[ (long int)VECTOR(*neis1)[i] ] = node + 1;
+        }
+
+        for (i = 0; i < neilen1; i++) {
+            long int nei = (long int) VECTOR(*neis1)[i];
+            if (VECTOR(rank)[nei] > VECTOR(rank)[node]) {
+                neis2 = igraph_adjlist_get(&allneis, nei);
+                neilen2 = igraph_vector_int_size(neis2);
+                for (j = 0; j < neilen2; j++) {
+                    long int nei2 = (long int) VECTOR(*neis2)[j];
+                    if (VECTOR(rank)[nei2] < VECTOR(rank)[nei]) {
+                        continue;
+                    }
+                    if (neis[nei2] == node + 1) {
+                        VECTOR(triangles)[nei2] += 1;
+                        VECTOR(triangles)[nei] += 1;
+                        VECTOR(triangles)[node] += 1;
+                    }
+                }
+            }
+        }
+
+        if (neilen1 >= 2) {
+            sum += VECTOR(triangles)[node] / neilen1 / (neilen1 - 1) * 2.0;
+            count++;
+        } else if (mode == IGRAPH_TRANSITIVITY_ZERO) {
+            count++;
+        }
+    }
+
+    *res = sum / count;
+
+    igraph_vector_destroy(&triangles);
+    igraph_Free(neis);
+    igraph_adjlist_destroy(&allneis);
+    igraph_vector_destroy(&rank);
+    igraph_vector_destroy(&order);
+    IGRAPH_FINALLY_CLEAN(5);
+    return 0;
+}
+
+int igraph_transitivity_local_undirected1(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_transitivity_mode_t mode) {
+
+#define TRANSIT
+#include "triangles_template1.h"
+#undef TRANSIT
+
+    return 0;
+}
+
+int igraph_transitivity_local_undirected2(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_transitivity_mode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vit_t vit;
+    long int nodes_to_calc, affected_nodes;
+    long int maxdegree = 0;
+    long int i, j, k, nn;
+    igraph_lazy_adjlist_t adjlist;
+    igraph_vector_t indexv, avids, rank, order, triangles, degree;
+    long int *neis;
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL,
+                                          IGRAPH_SIMPLIFY));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&indexv, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&avids, 0);
+    IGRAPH_CHECK(igraph_vector_reserve(&avids, nodes_to_calc));
+    k = 0;
+    for (i = 0; i < nodes_to_calc; IGRAPH_VIT_NEXT(vit), i++) {
+        long int v = IGRAPH_VIT_GET(vit);
+        igraph_vector_t *neis2;
+        long int neilen;
+        if (VECTOR(indexv)[v] == 0) {
+            VECTOR(indexv)[v] = k + 1; k++;
+            IGRAPH_CHECK(igraph_vector_push_back(&avids, v));
+        }
+
+        neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v);
+        neilen = igraph_vector_size(neis2);
+        for (j = 0; j < neilen; j++) {
+            long int nei = (long int) VECTOR(*neis2)[j];
+            if (VECTOR(indexv)[nei] == 0) {
+                VECTOR(indexv)[nei] = k + 1; k++;
+                IGRAPH_CHECK(igraph_vector_push_back(&avids, nei));
+            }
+        }
+    }
+
+    /* Degree, ordering, ranking */
+    affected_nodes = igraph_vector_size(&avids);
+    IGRAPH_VECTOR_INIT_FINALLY(&order, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, affected_nodes);
+    for (i = 0; i < affected_nodes; i++) {
+        long int v = (long int) VECTOR(avids)[i];
+        igraph_vector_t *neis2;
+        long int deg;
+        neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v);
+        VECTOR(degree)[i] = deg = igraph_vector_size(neis2);
+        if (deg > maxdegree) {
+            maxdegree = deg;
+        }
+    }
+    igraph_vector_order1(&degree, &order, maxdegree + 1);
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_VECTOR_INIT_FINALLY(&rank, affected_nodes);
+    for (i = 0; i < affected_nodes; i++) {
+        VECTOR(rank)[ (long int) VECTOR(order)[i] ] = affected_nodes - i - 1;
+    }
+
+    neis = igraph_Calloc(no_of_nodes, long int);
+    if (neis == 0) {
+        IGRAPH_ERROR("local transitivity calculation failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, neis);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&triangles, affected_nodes);
+    for (nn = affected_nodes - 1; nn >= 0; nn--) {
+        long int node = (long int) VECTOR(avids) [ (long int) VECTOR(order)[nn] ];
+        igraph_vector_t *neis1, *neis2;
+        long int neilen1, neilen2;
+        long int nodeindex = (long int) VECTOR(indexv)[node];
+        long int noderank = (long int) VECTOR(rank) [nodeindex - 1];
+
+        /*     fprintf(stderr, "node %li (indexv %li, rank %li)\n", node, */
+        /*      (long int)VECTOR(indexv)[node]-1, noderank); */
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        neis1 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node);
+        neilen1 = igraph_vector_size(neis1);
+        for (i = 0; i < neilen1; i++) {
+            long int nei = (long int) VECTOR(*neis1)[i];
+            neis[nei] = node + 1;
+        }
+        for (i = 0; i < neilen1; i++) {
+            long int nei = (long int) VECTOR(*neis1)[i];
+            long int neiindex = (long int) VECTOR(indexv)[nei];
+            long int neirank = (long int) VECTOR(rank)[neiindex - 1];
+
+            /*       fprintf(stderr, "  nei %li (indexv %li, rank %li)\n", nei, */
+            /*        neiindex, neirank); */
+            if (neirank > noderank) {
+                neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) nei);
+                neilen2 = igraph_vector_size(neis2);
+                for (j = 0; j < neilen2; j++) {
+                    long int nei2 = (long int) VECTOR(*neis2)[j];
+                    long int nei2index = (long int) VECTOR(indexv)[nei2];
+                    long int nei2rank = (long int) VECTOR(rank)[nei2index - 1];
+                    /*    fprintf(stderr, "    triple %li %li %li\n", node, nei, nei2); */
+                    if (nei2rank < neirank) {
+                        continue;
+                    }
+                    if (neis[nei2] == node + 1) {
+                        /*      fprintf(stderr, "    triangle\n"); */
+                        VECTOR(triangles) [ nei2index - 1 ] += 1;
+                        VECTOR(triangles) [ neiindex - 1 ] += 1;
+                        VECTOR(triangles) [ nodeindex - 1 ] += 1;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Ok, for all affected vertices the number of triangles were counted */
+
+    IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
+    IGRAPH_VIT_RESET(vit);
+    for (i = 0; i < nodes_to_calc; i++, IGRAPH_VIT_NEXT(vit)) {
+        long int node = IGRAPH_VIT_GET(vit);
+        long int idx = (long int) VECTOR(indexv)[node] - 1;
+        igraph_vector_t *neis2 = igraph_lazy_adjlist_get(&adjlist,
+                                 (igraph_integer_t) node);
+        long int deg = igraph_vector_size(neis2);
+        if (mode == IGRAPH_TRANSITIVITY_ZERO && deg < 2) {
+            VECTOR(*res)[i] = 0.0;
+        } else {
+            VECTOR(*res)[i] = VECTOR(triangles)[idx] / deg / (deg - 1) * 2.0;
+        }
+        /*     fprintf(stderr, "%f %f\n", VECTOR(triangles)[idx], triples); */
+    }
+
+    igraph_vector_destroy(&triangles);
+    igraph_free(neis);
+    igraph_vector_destroy(&rank);
+    igraph_vector_destroy(&order);
+    igraph_vector_destroy(&avids);
+    igraph_vector_destroy(&indexv);
+    igraph_lazy_adjlist_destroy(&adjlist);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(8);
+
+    return 0;
+}
+
+/* We don't use this, it is theoretically good, but practically not.
+ */
+
+/* int igraph_transitivity_local_undirected3(const igraph_t *graph, */
+/*                    igraph_vector_t *res, */
+/*                    const igraph_vs_t vids) { */
+
+/*   igraph_vit_t vit; */
+/*   long int nodes_to_calc; */
+/*   igraph_lazy_adjlist_t adjlist; */
+/*   long int i, j; */
+
+/*   IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); */
+/*   IGRAPH_FINALLY(igraph_vit_destroy, &vit); */
+/*   nodes_to_calc=IGRAPH_VIT_SIZE(vit); */
+
+/*   IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, */
+/*                    IGRAPH_SIMPLIFY)); */
+/*   IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); */
+
+/*   IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); */
+/*   for (i=0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit);  */
+/*        i++, IGRAPH_VIT_NEXT(vit)) { */
+/*     long int node=IGRAPH_VIT_GET(vit); */
+/*     igraph_vector_t *neis=igraph_lazy_adjlist_get(&adjlist, node); */
+/*     long int n1=igraph_vector_size(neis); */
+/*     igraph_real_t triangles=0; */
+/*     igraph_real_t triples=(double)n1*(n1-1); */
+/*     IGRAPH_ALLOW_INTERRUPTION(); */
+/*     for (j=0; j<n1; j++) { */
+/*       long int node2=VECTOR(*neis)[j]; */
+/*       igraph_vector_t *neis2=igraph_lazy_adjlist_get(&adjlist, node2); */
+/*       long int n2=igraph_vector_size(neis2); */
+/*       long int l1=0, l2=0; */
+/*       while (l1 < n1 && l2 < n2) { */
+/*  long int nei1=VECTOR(*neis)[l1]; */
+/*  long int nei2=VECTOR(*neis2)[l2]; */
+/*  if (nei1 < nei2) {  */
+/*    l1++; */
+/*  } else if (nei1 > nei2) { */
+/*    l2++; */
+/*  } else { */
+/*    triangles+=1; */
+/*    l1++; l2++; */
+/*  } */
+/*       } */
+/*     } */
+/*     /\* We're done with 'node' *\/ */
+/*     VECTOR(*res)[i] = triangles / triples;   */
+/*   } */
+
+/*   igraph_lazy_adjlist_destroy(&adjlist); */
+/*   igraph_vit_destroy(&vit); */
+/*   IGRAPH_FINALLY_CLEAN(2); */
+
+/*   return 0; */
+/* } */
+
+/* This removes loop, multiple edges and edges that point
+     "backwards" according to the rank vector. */
+
+int igraph_i_trans4_al_simplify(igraph_adjlist_t *al,
+                                const igraph_vector_int_t *rank) {
+    long int i;
+    long int n = al->length;
+    igraph_vector_int_t mark;
+    igraph_vector_int_init(&mark, n);
+    IGRAPH_FINALLY(igraph_vector_int_destroy, &mark);
+    for (i = 0; i < n; i++) {
+        igraph_vector_int_t *v = &al->adjs[i];
+        int j, l = igraph_vector_int_size(v);
+        int irank = VECTOR(*rank)[i];
+        VECTOR(mark)[i] = i + 1;
+        for (j = 0; j < l; /* nothing */) {
+            long int e = (long int) VECTOR(*v)[j];
+            if (VECTOR(*rank)[e] > irank && VECTOR(mark)[e] != i + 1) {
+                VECTOR(mark)[e] = i + 1;
+                j++;
+            } else {
+                VECTOR(*v)[j] = igraph_vector_int_tail(v);
+                igraph_vector_int_pop_back(v);
+                l--;
+            }
+        }
+    }
+
+    igraph_vector_int_destroy(&mark);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+
+}
+
+int igraph_transitivity_local_undirected4(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_transitivity_mode_t mode) {
+
+#define TRANSIT 1
+#include "triangles_template.h"
+#undef TRANSIT
+
+    return 0;
+}
+
+/**
+ * \function igraph_transitivity_local_undirected
+ * \brief Calculates the local transitivity (clustering coefficient) of a graph.
+ *
+ * The transitivity measures the probability that two neighbors of a
+ * vertex are connected. In case of the local transitivity, this
+ * probability is calculated separately for each vertex.
+ *
+ * </para><para>
+ * Note that this measure is different from the global transitivity measure
+ * (see \ref igraph_transitivity_undirected() ) as it calculates a transitivity
+ * value for each vertex individually. See the following reference for more
+ * details:
+ *
+ * </para><para>
+ * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks.
+ * Nature 393(6684):440-442 (1998).
+ *
+ * </para><para>
+ * Clustering coefficient is an alternative name for transitivity.
+ *
+ * \param graph The input graph, which should be undirected and simple.
+ * \param res Pointer to an initialized vector, the result will be
+ *   stored here. It will be resized as needed.
+ * \param vids Vertex set, the vertices for which the local
+ *   transitivity will be calculated.
+ * \param mode Defines how to treat vertices with degree less than two.
+ *    \c IGRAPH_TRANSITIVITY_NAN returns \c NaN for these vertices,
+ *    \c IGRAPH_TRANSITIVITY_ZERO returns zero.
+ * \return Error code.
+ *
+ * \sa \ref igraph_transitivity_undirected(), \ref
+ * igraph_transitivity_avglocal_undirected().
+ *
+ * Time complexity: O(n*d^2), n is the number of vertices for which
+ * the transitivity is calculated, d is the average vertex degree.
+ */
+
+int igraph_transitivity_local_undirected(const igraph_t *graph,
+        igraph_vector_t *res,
+        const igraph_vs_t vids,
+        igraph_transitivity_mode_t mode) {
+
+    igraph_bool_t simple;
+
+    if (igraph_is_directed(graph)) {
+        IGRAPH_ERROR("Transitivity works on undirected graphs only", IGRAPH_EINVAL);
+    }
+
+    igraph_is_simple(graph, &simple);
+    if (!simple) {
+        IGRAPH_ERROR("Transitivity works on simple graphs only", IGRAPH_EINVAL);
+    }
+
+    if (igraph_vs_is_all(&vids)) {
+        return igraph_transitivity_local_undirected4(graph, res, vids, mode);
+    } else {
+        igraph_vit_t vit;
+        long int size;
+        IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+        IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+        size = IGRAPH_VIT_SIZE(vit);
+        igraph_vit_destroy(&vit);
+        IGRAPH_FINALLY_CLEAN(1);
+        if (size < 100) {
+            return igraph_transitivity_local_undirected1(graph, res, vids, mode);
+        } else {
+            return igraph_transitivity_local_undirected2(graph, res, vids, mode);
+        }
+    }
+
+    return 0;
+}
+
+int igraph_adjacent_triangles1(const igraph_t *graph,
+                               igraph_vector_t *res,
+                               const igraph_vs_t vids) {
+# include "triangles_template1.h"
+    return 0;
+}
+
+int igraph_adjacent_triangles4(const igraph_t *graph,
+                               igraph_vector_t *res) {
+# include "triangles_template.h"
+    return 0;
+}
+
+/**
+ * \function igraph_adjacent_triangles
+ * Count the number of triangles a vertex is part of
+ *
+ * \param graph The input graph. Edge directions are ignored.
+ * \param res Initiliazed vector, the results are stored here.
+ * \param vids The vertices to perform the calculation for.
+ * \return Error mode.
+ *
+ * \sa \ref igraph_list_triangles() to list them.
+ *
+ * Time complexity: O(d^2 n), d is the average vertex degree of the
+ * queried vertices, n is their number.
+ */
+
+int igraph_adjacent_triangles(const igraph_t *graph,
+                              igraph_vector_t *res,
+                              const igraph_vs_t vids) {
+    if (igraph_vs_is_all(&vids)) {
+        return igraph_adjacent_triangles4(graph, res);
+    } else {
+        return igraph_adjacent_triangles1(graph, res, vids);
+    }
+
+    return 0;
+
+}
+
+/**
+ * \function igraph_list_triangles
+ * Find all triangles in a graph
+ *
+ * \param graph The input graph, edge directions are ignored.
+ * \param res Pointer to an initialized integer vector, the result
+ *        is stored here, in a long list of triples of vertex ids.
+ *        Each triple is a triangle in the graph. Each triangle is
+ *        listed exactly once.
+ * \return Error code.
+ *
+ * \sa \ref igraph_transitivity_undirected() to count the triangles,
+ * \ref igraph_adjacent_triangles() to count the triangles a vertex
+ * participates in.
+ *
+ * Time complexity: O(d^2 n), d is the average degree, n is the number
+ * of vertices.
+ */
+
+int igraph_list_triangles(const igraph_t *graph,
+                          igraph_vector_int_t *res) {
+# define TRIANGLES
+# include "triangles_template.h"
+# undef TRIANGLES
+    return 0;
+}
+
+/**
+ * \ingroup structural
+ * \function igraph_transitivity_undirected
+ * \brief Calculates the transitivity (clustering coefficient) of a graph.
+ *
+ * </para><para>
+ * The transitivity measures the probability that two neighbors of a
+ * vertex are connected. More precisely, this is the ratio of the
+ * triangles and connected triples in the graph, the result is a
+ * single real number. Directed graphs are considered as undirected ones.
+ *
+ * </para><para>
+ * Note that this measure is different from the local transitivity measure
+ * (see \ref igraph_transitivity_local_undirected() ) as it calculates a single
+ * value for the whole graph. See the following reference for more details:
+ *
+ * </para><para>
+ * S. Wasserman and K. Faust: Social Network Analysis: Methods and
+ * Applications. Cambridge: Cambridge University Press, 1994.
+ *
+ * </para><para>
+ * Clustering coefficient is an alternative name for transitivity.
+ *
+ * \param graph The graph object.
+ * \param res Pointer to a real variable, the result will be stored here.
+ * \param mode Defines how to treat graphs with no connected triples.
+ *   \c IGRAPH_TRANSITIVITY_NAN returns \c NaN in this case,
+ *   \c IGRAPH_TRANSITIVITY_ZERO returns zero.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM: not enough memory for
+ *         temporary data.
+ *
+ * \sa \ref igraph_transitivity_local_undirected(),
+ * \ref igraph_transitivity_avglocal_undirected().
+ *
+ * Time complexity: O(|V|*d^2), |V| is the number of vertices in
+ * the graph, d is the average node degree.
+ *
+ * \example examples/simple/igraph_transitivity.c
+ */
+
+
+int igraph_transitivity_undirected(const igraph_t *graph,
+                                   igraph_real_t *res,
+                                   igraph_transitivity_mode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_real_t triples = 0, triangles = 0;
+    long int node, nn;
+    long int maxdegree;
+    long int *neis;
+    igraph_vector_t order;
+    igraph_vector_t rank;
+    igraph_vector_t degree;
+
+    igraph_adjlist_t allneis;
+    igraph_vector_int_t *neis1, *neis2;
+    long int i, j, neilen1, neilen2;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
+                               IGRAPH_LOOPS));
+    maxdegree = (long int) igraph_vector_max(&degree) + 1;
+    igraph_vector_order1(&degree, &order, maxdegree);
+    igraph_vector_destroy(&degree);
+    IGRAPH_FINALLY_CLEAN(1);
+    IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(rank)[ (long int) VECTOR(order)[i] ] = no_of_nodes - i - 1;
+    }
+
+    IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
+    IGRAPH_CHECK(igraph_adjlist_simplify(&allneis));
+
+    neis = igraph_Calloc(no_of_nodes, long int);
+    if (neis == 0) {
+        IGRAPH_ERROR("undirected transitivity failed", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, neis);
+
+    for (nn = no_of_nodes - 1; nn >= 0; nn--) {
+        node = (long int) VECTOR(order)[nn];
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        neis1 = igraph_adjlist_get(&allneis, node);
+        neilen1 = igraph_vector_int_size(neis1);
+        triples += (double)neilen1 * (neilen1 - 1);
+        /* Mark the neighbors of 'node' */
+        for (i = 0; i < neilen1; i++) {
+            long int nei = (long int) VECTOR(*neis1)[i];
+            neis[nei] = node + 1;
+        }
+        for (i = 0; i < neilen1; i++) {
+            long int nei = (long int) VECTOR(*neis1)[i];
+            /* If 'nei' is not ready yet */
+            if (VECTOR(rank)[nei] > VECTOR(rank)[node]) {
+                neis2 = igraph_adjlist_get(&allneis, nei);
+                neilen2 = igraph_vector_int_size(neis2);
+                for (j = 0; j < neilen2; j++) {
+                    long int nei2 = (long int) VECTOR(*neis2)[j];
+                    if (neis[nei2] == node + 1) {
+                        triangles += 1.0;
+                    }
+                }
+            }
+        }
+    }
+
+    igraph_Free(neis);
+    igraph_adjlist_destroy(&allneis);
+    igraph_vector_destroy(&rank);
+    igraph_vector_destroy(&order);
+    IGRAPH_FINALLY_CLEAN(4);
+
+    if (triples == 0 && mode == IGRAPH_TRANSITIVITY_ZERO) {
+        *res = 0;
+    } else {
+        *res = triangles / triples * 2.0;
+    }
+
+    return 0;
+}
+
+int igraph_transitivity_barrat1(const igraph_t *graph,
+                                igraph_vector_t *res,
+                                const igraph_vs_t vids,
+                                const igraph_vector_t *weights,
+                                igraph_transitivity_mode_t mode);
+
+int igraph_transitivity_barrat4(const igraph_t *graph,
+                                igraph_vector_t *res,
+                                const igraph_vs_t vids,
+                                const igraph_vector_t *weights,
+                                igraph_transitivity_mode_t mode);
+
+int igraph_transitivity_barrat1(const igraph_t *graph,
+                                igraph_vector_t *res,
+                                const igraph_vs_t vids,
+                                const igraph_vector_t *weights,
+                                igraph_transitivity_mode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vit_t vit;
+    long int nodes_to_calc;
+    igraph_vector_t *adj1, *adj2;
+    igraph_vector_long_t neis;
+    igraph_vector_t actw;
+    igraph_lazy_inclist_t incident;
+    long int i;
+    igraph_vector_t strength;
+
+    if (!weights) {
+        IGRAPH_WARNING("No weights given for Barrat's transitivity, unweighted version is used");
+        return igraph_transitivity_local_undirected(graph, res, vids, mode);
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid edge weight vector length", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+    nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+
+    IGRAPH_CHECK(igraph_vector_long_init(&neis, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &neis);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&actw, no_of_nodes);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&strength, 0);
+    IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(), IGRAPH_ALL,
+                                 IGRAPH_LOOPS, weights));
+
+    igraph_lazy_inclist_init(graph, &incident, IGRAPH_ALL);
+    IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &incident);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
+
+    for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
+        long int node = IGRAPH_VIT_GET(vit);
+        long int adjlen1, adjlen2, j, k;
+        igraph_real_t triples, triangles;
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        adj1 = igraph_lazy_inclist_get(&incident, (igraph_integer_t) node);
+        adjlen1 = igraph_vector_size(adj1);
+        /* Mark the neighbors of the node */
+        for (j = 0; j < adjlen1; j++) {
+            long int edge = (long int) VECTOR(*adj1)[j];
+            long int nei = IGRAPH_OTHER(graph, edge, node);
+            VECTOR(neis)[nei] = i + 1;
+            VECTOR(actw)[nei] = VECTOR(*weights)[edge];
+        }
+        triples = VECTOR(strength)[node] * (adjlen1 - 1);
+        triangles = 0.0;
+
+        for (j = 0; j < adjlen1; j++) {
+            long int edge1 = (long int) VECTOR(*adj1)[j];
+            igraph_real_t weight1 = VECTOR(*weights)[edge1];
+            long int v = IGRAPH_OTHER(graph, edge1, node);
+            adj2 = igraph_lazy_inclist_get(&incident, (igraph_integer_t) v);
+            adjlen2 = igraph_vector_size(adj2);
+            for (k = 0; k < adjlen2; k++) {
+                long int edge2 = (long int) VECTOR(*adj2)[k];
+                long int v2 = IGRAPH_OTHER(graph, edge2, v);
+                if (VECTOR(neis)[v2] == i + 1) {
+                    triangles += (VECTOR(actw)[v2] + weight1) / 2.0;
+                }
+            }
+        }
+        if (mode == IGRAPH_TRANSITIVITY_ZERO && triples == 0) {
+            VECTOR(*res)[i] = 0.0;
+        } else {
+            VECTOR(*res)[i] = triangles / triples;
+        }
+    }
+
+    igraph_lazy_inclist_destroy(&incident);
+    igraph_vector_destroy(&strength);
+    igraph_vector_destroy(&actw);
+    igraph_vector_long_destroy(&neis);
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(5);
+
+    return 0;
+}
+
+int igraph_transitivity_barrat4(const igraph_t *graph,
+                                igraph_vector_t *res,
+                                const igraph_vs_t vids,
+                                const igraph_vector_t *weights,
+                                igraph_transitivity_mode_t mode) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_vector_t order, degree, rank;
+    long int maxdegree;
+    igraph_inclist_t incident;
+    igraph_vector_long_t neis;
+    igraph_vector_int_t *adj1, *adj2;
+    igraph_vector_t actw;
+    long int i, nn;
+
+    if (!weights) {
+        IGRAPH_WARNING("No weights given for Barrat's transitivity, unweighted version is used");
+        return igraph_transitivity_local_undirected(graph, res, vids, mode);
+    }
+
+    if (igraph_vector_size(weights) != no_of_edges) {
+        IGRAPH_ERROR("Invalid edge weight vector length", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
+                               IGRAPH_LOOPS));
+    maxdegree = (long int) igraph_vector_max(&degree) + 1;
+    IGRAPH_CHECK(igraph_vector_order1(&degree, &order, maxdegree));
+
+    IGRAPH_CHECK(igraph_strength(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
+                                 IGRAPH_LOOPS, weights));
+
+    IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes);
+    for (i = 0; i < no_of_nodes; i++) {
+        VECTOR(rank)[ (long int)VECTOR(order)[i] ] = no_of_nodes - i - 1;
+    }
+
+    IGRAPH_CHECK(igraph_inclist_init(graph, &incident, IGRAPH_ALL));
+    IGRAPH_FINALLY(igraph_inclist_destroy, &incident);
+
+    IGRAPH_CHECK(igraph_vector_long_init(&neis, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &neis);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&actw, no_of_nodes);
+
+    IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
+    igraph_vector_null(res);
+
+    for (nn = no_of_nodes - 1; nn >= 0; nn--) {
+        long int adjlen1, adjlen2;
+        igraph_real_t triples;
+        long int node = (long int) VECTOR(order)[nn];
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        adj1 = igraph_inclist_get(&incident, node);
+        adjlen1 = igraph_vector_int_size(adj1);
+        triples = VECTOR(degree)[node] * (adjlen1 - 1) / 2.0;
+        /* Mark the neighbors of the node */
+        for (i = 0; i < adjlen1; i++) {
+            long int edge = (long int) VECTOR(*adj1)[i];
+            long int nei = IGRAPH_OTHER(graph, edge, node);
+            VECTOR(neis)[nei] = node + 1;
+            VECTOR(actw)[nei] = VECTOR(*weights)[edge];
+        }
+
+        for (i = 0; i < adjlen1; i++) {
+            long int edge1 = (long int) VECTOR(*adj1)[i];
+            igraph_real_t weight1 = VECTOR(*weights)[edge1];
+            long int nei = IGRAPH_OTHER(graph, edge1, node);
+            long int j;
+            if (VECTOR(rank)[nei] > VECTOR(rank)[node]) {
+                adj2 = igraph_inclist_get(&incident, nei);
+                adjlen2 = igraph_vector_int_size(adj2);
+                for (j = 0; j < adjlen2; j++) {
+                    long int edge2 = (long int) VECTOR(*adj2)[j];
+                    igraph_real_t weight2 = VECTOR(*weights)[edge2];
+                    long int nei2 = IGRAPH_OTHER(graph, edge2, nei);
+                    if (VECTOR(rank)[nei2] < VECTOR(rank)[nei]) {
+                        continue;
+                    }
+                    if (VECTOR(neis)[nei2] == node + 1) {
+                        VECTOR(*res)[nei2] += (VECTOR(actw)[nei2] + weight2) / 2.0;
+                        VECTOR(*res)[nei] += (weight1 + weight2) / 2.0;
+                        VECTOR(*res)[node] += (VECTOR(actw)[nei2] + weight1) / 2.0;
+                    }
+                }
+            }
+        }
+
+        if (mode == IGRAPH_TRANSITIVITY_ZERO && triples == 0) {
+            VECTOR(*res)[node] = 0.0;
+        } else {
+            VECTOR(*res)[node] /= triples;
+        }
+    }
+
+    igraph_vector_destroy(&actw);
+    igraph_vector_long_destroy(&neis);
+    igraph_inclist_destroy(&incident);
+    igraph_vector_destroy(&rank);
+    igraph_vector_destroy(&degree);
+    igraph_vector_destroy(&order);
+    IGRAPH_FINALLY_CLEAN(6);
+
+    return 0;
+}
+
+/**
+ * \function igraph_transitivity_barrat
+ * Weighted transitivity, as defined by A. Barrat.
+ *
+ * This is a local transitivity, i.e. a vertex-level index. For a
+ * given vertex \c i, from all triangles in which it participates we
+ * consider the weight of the edges incident on \c i. The transitivity
+ * is the sum of these weights divided by twice the strength of the
+ * vertex (see \ref igraph_strength()) and the degree of the vertex
+ * minus one. See   Alain Barrat, Marc Barthelemy, Romualdo
+ * Pastor-Satorras, Alessandro Vespignani: The architecture of complex
+ * weighted networks, Proc. Natl. Acad. Sci. USA 101, 3747 (2004) at
+ * http://arxiv.org/abs/cond-mat/0311416 for the exact formula.
+ *
+ * \param graph The input graph, edge directions are ignored for
+ *   directed graphs. Note that the function does NOT work for
+ *   non-simple graphs.
+ * \param res Pointer to an initialized vector, the result will be
+ *   stored here. It will be resized as needed.
+ * \param vids The vertices for which the calculation is performed.
+ * \param weights Edge weights. If this is a null pointer, then a
+ *   warning is given and \ref igraph_transitivity_local_undirected()
+ *   is called.
+ * \param mode Defines how to treat vertices with zero strength.
+ *   \c IGRAPH_TRANSITIVITY_NAN says that the transitivity of these
+ *   vertices is \c NaN, \c IGRAPH_TRANSITIVITY_ZERO says it is zero.
+ *
+ * \return Error code.
+ *
+ * Time complexity: O(|V|*d^2), |V| is the number of vertices in
+ * the graph, d is the average node degree.
+ *
+ * \sa \ref igraph_transitivity_undirected(), \ref
+ * igraph_transitivity_local_undirected() and \ref
+ * igraph_transitivity_avglocal_undirected() for other kinds of
+ * (non-weighted) transitivity.
+ */
+
+int igraph_transitivity_barrat(const igraph_t *graph,
+                               igraph_vector_t *res,
+                               const igraph_vs_t vids,
+                               const igraph_vector_t *weights,
+                               igraph_transitivity_mode_t mode) {
+    if (igraph_vs_is_all(&vids)) {
+        return igraph_transitivity_barrat4(graph, res, vids, weights, mode);
+    } else {
+        return igraph_transitivity_barrat1(graph, res, vids, weights, mode);
+    }
+
+    return 0;
+}
diff --git a/igraph/src/type_indexededgelist.c b/igraph/src/type_indexededgelist.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/type_indexededgelist.c
@@ -0,0 +1,1706 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_datatype.h"
+#include "igraph_interface.h"
+#include "igraph_attributes.h"
+#include "igraph_memory.h"
+#include <string.h>     /* memset & co. */
+#include "config.h"
+
+/* Internal functions */
+
+int igraph_i_create_start(igraph_vector_t *res, igraph_vector_t *el, igraph_vector_t *index,
+                          igraph_integer_t nodes);
+
+/**
+ * \section about_basic_interface
+ *
+ * <para>This is the very minimal API in \a igraph. All the other
+ * functions use this minimal set for creating and manipulating
+ * graphs.</para>
+ *
+ * <para>This is a very important principle since it makes possible to
+ * implement other data representations by implementing only this
+ * minimal set.</para>
+ */
+
+/**
+ * \ingroup interface
+ * \function igraph_empty
+ * \brief Creates an empty graph with some vertices and no edges.
+ *
+ * </para><para>
+ * The most basic constructor, all the other constructors should call
+ * this to create a minimal graph object. Our use of the term "empty graph"
+ * in the above description should be distinguished from the mathematical
+ * definition of the empty or null graph. Strictly speaking, the empty or null
+ * graph in graph theory is the graph with no vertices and no edges. However
+ * by "empty graph" as used in \c igraph we mean a graph having zero or more
+ * vertices, but no edges.
+ * \param graph Pointer to a not-yet initialized graph object.
+ * \param n The number of vertices in the graph, a non-negative
+ *          integer number is expected.
+ * \param directed Boolean; whether the graph is directed or not. Supported
+ *        values are:
+ *        \clist
+ *        \cli IGRAPH_DIRECTED
+ *          The graph will be \em directed.
+ *        \cli IGRAPH_UNDIRECTED
+ *          The graph will be \em undirected.
+ *        \endclist
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid number of vertices.
+ *
+ * Time complexity: O(|V|) for a graph with
+ * |V| vertices (and no edges).
+ *
+ * \example examples/simple/igraph_empty.c
+ */
+int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) {
+    return igraph_empty_attrs(graph, n, directed, 0);
+}
+
+
+/**
+ * \ingroup interface
+ * \function igraph_empty_attrs
+ * \brief Creates an empty graph with some vertices, no edges and some graph attributes.
+ *
+ * </para><para>
+ * Use this instead of \ref igraph_empty() if you wish to add some graph
+ * attributes right after initialization. This function is currently
+ * not very interesting for the ordinary user. Just supply 0 here or
+ * use \ref igraph_empty().
+ * \param graph Pointer to a not-yet initialized graph object.
+ * \param n The number of vertices in the graph; a non-negative
+ *          integer number is expected.
+ * \param directed Boolean; whether the graph is directed or not. Supported
+ *        values are:
+ *        \clist
+ *        \cli IGRAPH_DIRECTED
+ *          Create a \em directed graph.
+ *        \cli IGRAPH_UNDIRECTED
+ *          Create an \em undirected graph.
+ *        \endclist
+ * \param attr The attributes.
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid number of vertices.
+ *
+ * Time complexity: O(|V|) for a graph with
+ * |V| vertices (and no edges).
+ */
+int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void* attr) {
+
+    if (n < 0) {
+        IGRAPH_ERROR("cannot create empty graph with negative number of vertices",
+                     IGRAPH_EINVAL);
+    }
+
+    if (!IGRAPH_FINITE(n)) {
+        IGRAPH_ERROR("number of vertices is not finite (NA, NaN or Inf)", IGRAPH_EINVAL);
+    }
+
+    graph->n = 0;
+    graph->directed = directed;
+    IGRAPH_VECTOR_INIT_FINALLY(&graph->from, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&graph->to, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&graph->oi, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&graph->ii, 0);
+    IGRAPH_VECTOR_INIT_FINALLY(&graph->os, 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&graph->is, 1);
+
+    VECTOR(graph->os)[0] = 0;
+    VECTOR(graph->is)[0] = 0;
+
+    /* init attributes */
+    graph->attr = 0;
+    IGRAPH_CHECK(igraph_i_attribute_init(graph, attr));
+
+    /* add the vertices */
+    IGRAPH_CHECK(igraph_add_vertices(graph, n, 0));
+
+    IGRAPH_FINALLY_CLEAN(6);
+    return 0;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_destroy
+ * \brief Frees the memory allocated for a graph object.
+ *
+ * </para><para>
+ * This function should be called for every graph object exactly once.
+ *
+ * </para><para>
+ * This function invalidates all iterators (of course), but the
+ * iterators of a graph should be destroyed before the graph itself
+ * anyway.
+ * \param graph Pointer to the graph to free.
+ *
+ * Time complexity: operating system specific.
+ */
+void igraph_destroy(igraph_t *graph) {
+
+    IGRAPH_I_ATTRIBUTE_DESTROY(graph);
+
+    igraph_vector_destroy(&graph->from);
+    igraph_vector_destroy(&graph->to);
+    igraph_vector_destroy(&graph->oi);
+    igraph_vector_destroy(&graph->ii);
+    igraph_vector_destroy(&graph->os);
+    igraph_vector_destroy(&graph->is);
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_copy
+ * \brief Creates an exact (deep) copy of a graph.
+ *
+ * </para><para>
+ * This function deeply copies a graph object to create an exact
+ * replica of it. The new replica should be destroyed by calling
+ * \ref igraph_destroy() on it when not needed any more.
+ *
+ * </para><para>
+ * You can also create a shallow copy of a graph by simply using the
+ * standard assignment operator, but be careful and do \em not
+ * destroy a shallow replica. To avoid this mistake, creating shallow
+ * copies is not recommended.
+ * \param to Pointer to an uninitialized graph object.
+ * \param from Pointer to the graph object to copy.
+ * \return Error code.
+ *
+ * Time complexity:  O(|V|+|E|) for a
+ * graph with |V| vertices and
+ * |E| edges.
+ *
+ * \example examples/simple/igraph_copy.c
+ */
+
+int igraph_copy(igraph_t *to, const igraph_t *from) {
+    to->n = from->n;
+    to->directed = from->directed;
+    IGRAPH_CHECK(igraph_vector_copy(&to->from, &from->from));
+    IGRAPH_FINALLY(igraph_vector_destroy, &to->from);
+    IGRAPH_CHECK(igraph_vector_copy(&to->to, &from->to));
+    IGRAPH_FINALLY(igraph_vector_destroy, &to->to);
+    IGRAPH_CHECK(igraph_vector_copy(&to->oi, &from->oi));
+    IGRAPH_FINALLY(igraph_vector_destroy, &to->oi);
+    IGRAPH_CHECK(igraph_vector_copy(&to->ii, &from->ii));
+    IGRAPH_FINALLY(igraph_vector_destroy, &to->ii);
+    IGRAPH_CHECK(igraph_vector_copy(&to->os, &from->os));
+    IGRAPH_FINALLY(igraph_vector_destroy, &to->os);
+    IGRAPH_CHECK(igraph_vector_copy(&to->is, &from->is));
+    IGRAPH_FINALLY(igraph_vector_destroy, &to->is);
+
+    IGRAPH_I_ATTRIBUTE_COPY(to, from, 1, 1, 1); /* does IGRAPH_CHECK */
+
+    IGRAPH_FINALLY_CLEAN(6);
+    return 0;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_add_edges
+ * \brief Adds edges to a graph object.
+ *
+ * </para><para>
+ * The edges are given in a vector, the
+ * first two elements define the first edge (the order is
+ * <code>from</code>, <code>to</code> 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.
+ * \param graph The graph to which the edges will be added.
+ * \param edges The edges themselves.
+ * \param attr The attributes of the new edges, only used by high level
+ *        interfaces currently, you can supply 0 here.
+ * \return Error code:
+ *    \c IGRAPH_EINVEVECTOR: invalid (odd)
+ *    edges vector length, \c IGRAPH_EINVVID:
+ *    invalid vertex id in edges vector.
+ *
+ * This function invalidates all iterators.
+ *
+ * </para><para>
+ * Time complexity: O(|V|+|E|) where
+ * |V| is the number of vertices and
+ * |E| is the number of
+ * edges in the \em new, extended graph.
+ *
+ * \example examples/simple/igraph_add_edges.c
+ */
+int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges,
+                     void *attr) {
+    long int no_of_edges = igraph_vector_size(&graph->from);
+    long int edges_to_add = igraph_vector_size(edges) / 2;
+    long int i = 0;
+    igraph_error_handler_t *oldhandler;
+    int ret1, ret2;
+    igraph_vector_t newoi, newii;
+    igraph_bool_t directed = igraph_is_directed(graph);
+
+    if (igraph_vector_size(edges) % 2 != 0) {
+        IGRAPH_ERROR("invalid (odd) length of edges vector", IGRAPH_EINVEVECTOR);
+    }
+    if (!igraph_vector_isininterval(edges, 0, igraph_vcount(graph) - 1)) {
+        IGRAPH_ERROR("cannot add edges", IGRAPH_EINVVID);
+    }
+
+    /* from & to */
+    IGRAPH_CHECK(igraph_vector_reserve(&graph->from, no_of_edges + edges_to_add));
+    IGRAPH_CHECK(igraph_vector_reserve(&graph->to, no_of_edges + edges_to_add));
+
+    while (i < edges_to_add * 2) {
+        if (directed || VECTOR(*edges)[i] > VECTOR(*edges)[i + 1]) {
+            igraph_vector_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */
+            igraph_vector_push_back(&graph->to,   VECTOR(*edges)[i++]); /* reserved */
+        } else {
+            igraph_vector_push_back(&graph->to,   VECTOR(*edges)[i++]); /* reserved */
+            igraph_vector_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */
+        }
+    }
+
+    /* disable the error handler temporarily */
+    oldhandler = igraph_set_error_handler(igraph_error_handler_ignore);
+
+    /* oi & ii */
+    ret1 = igraph_vector_init(&newoi, no_of_edges);
+    ret2 = igraph_vector_init(&newii, no_of_edges);
+    if (ret1 != 0 || ret2 != 0) {
+        igraph_vector_resize(&graph->from, no_of_edges); /* gets smaller */
+        igraph_vector_resize(&graph->to, no_of_edges);   /* gets smaller */
+        igraph_set_error_handler(oldhandler);
+        IGRAPH_ERROR("cannot add edges", IGRAPH_ERROR_SELECT_2(ret1, ret2));
+    }
+    ret1 = igraph_vector_order(&graph->from, &graph->to, &newoi, graph->n);
+    ret2 = igraph_vector_order(&graph->to, &graph->from, &newii, graph->n);
+    if (ret1 != 0 || ret2 != 0) {
+        igraph_vector_resize(&graph->from, no_of_edges);
+        igraph_vector_resize(&graph->to, no_of_edges);
+        igraph_vector_destroy(&newoi);
+        igraph_vector_destroy(&newii);
+        igraph_set_error_handler(oldhandler);
+        IGRAPH_ERROR("cannot add edges", IGRAPH_ERROR_SELECT_2(ret1, ret2));
+    }
+
+    /* Attributes */
+    if (graph->attr) {
+        igraph_set_error_handler(oldhandler);
+        ret1 = igraph_i_attribute_add_edges(graph, edges, attr);
+        igraph_set_error_handler(igraph_error_handler_ignore);
+        if (ret1 != 0) {
+            igraph_vector_resize(&graph->from, no_of_edges);
+            igraph_vector_resize(&graph->to, no_of_edges);
+            igraph_vector_destroy(&newoi);
+            igraph_vector_destroy(&newii);
+            igraph_set_error_handler(oldhandler);
+            IGRAPH_ERROR("cannot add edges", ret1);
+        }
+    }
+
+    /* os & is, its length does not change, error safe */
+    igraph_i_create_start(&graph->os, &graph->from, &newoi, graph->n);
+    igraph_i_create_start(&graph->is, &graph->to, &newii, graph->n);
+
+    /* everything went fine  */
+    igraph_vector_destroy(&graph->oi);
+    igraph_vector_destroy(&graph->ii);
+    graph->oi = newoi;
+    graph->ii = newii;
+    igraph_set_error_handler(oldhandler);
+
+    return 0;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_add_vertices
+ * \brief Adds vertices to a graph.
+ *
+ * </para><para>
+ * This function invalidates all iterators.
+ *
+ * \param graph The graph object to extend.
+ * \param nv Non-negative integer giving the number of
+ *           vertices to add.
+ * \param attr The attributes of the new vertices, only used by
+ *           high level interfaces, you can supply 0 here.
+ * \return Error code:
+ *         \c IGRAPH_EINVAL: invalid number of new
+ *         vertices.
+ *
+ * Time complexity: O(|V|) where
+ * |V| is
+ * the number of vertices in the \em new, extended graph.
+ *
+ * \example examples/simple/igraph_add_vertices.c
+ */
+int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) {
+    long int ec = igraph_ecount(graph);
+    long int i;
+
+    if (nv < 0) {
+        IGRAPH_ERROR("cannot add negative number of vertices", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_reserve(&graph->os, graph->n + nv + 1));
+    IGRAPH_CHECK(igraph_vector_reserve(&graph->is, graph->n + nv + 1));
+
+    igraph_vector_resize(&graph->os, graph->n + nv + 1); /* reserved */
+    igraph_vector_resize(&graph->is, graph->n + nv + 1); /* reserved */
+    for (i = graph->n + 1; i < graph->n + nv + 1; i++) {
+        VECTOR(graph->os)[i] = ec;
+        VECTOR(graph->is)[i] = ec;
+    }
+
+    graph->n += nv;
+
+    if (graph->attr) {
+        IGRAPH_CHECK(igraph_i_attribute_add_vertices(graph, nv, attr));
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_delete_edges
+ * \brief Removes edges from a graph.
+ *
+ * </para><para>
+ * The edges to remove are given as an edge selector.
+ *
+ * </para><para>
+ * This function cannot remove vertices, they will be kept, even if
+ * they lose all their edges.
+ *
+ * </para><para>
+ * This function invalidates all iterators.
+ * \param graph The graph to work on.
+ * \param edges The edges to remove.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|) where
+ * |V|
+ * and |E| are the number of vertices
+ * and edges in the \em original graph, respectively.
+ *
+ * \example examples/simple/igraph_delete_edges.c
+ */
+int igraph_delete_edges(igraph_t *graph, igraph_es_t edges) {
+    long int no_of_edges = igraph_ecount(graph);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int edges_to_remove = 0;
+    long int remaining_edges;
+    igraph_eit_t eit;
+
+    igraph_vector_t newfrom, newto, newoi;
+
+    int *mark;
+    long int i, j;
+
+    mark = igraph_Calloc(no_of_edges, int);
+    if (mark == 0) {
+        IGRAPH_ERROR("Cannot delete edges", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, mark);
+
+    IGRAPH_CHECK(igraph_eit_create(graph, edges, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+
+    for (IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
+        long int e = IGRAPH_EIT_GET(eit);
+        if (mark[e] == 0) {
+            edges_to_remove++;
+            mark[e]++;
+        }
+    }
+    remaining_edges = no_of_edges - edges_to_remove;
+
+    /* We don't need the iterator any more */
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&newfrom, remaining_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&newto, remaining_edges);
+
+    /* Actually remove the edges, move from pos i to pos j in newfrom/newto */
+    for (i = 0, j = 0; j < remaining_edges; i++) {
+        if (mark[i] == 0) {
+            VECTOR(newfrom)[j] = VECTOR(graph->from)[i];
+            VECTOR(newto)[j] = VECTOR(graph->to)[i];
+            j++;
+        }
+    }
+
+    /* Create index, this might require additional memory */
+    IGRAPH_VECTOR_INIT_FINALLY(&newoi, remaining_edges);
+    IGRAPH_CHECK(igraph_vector_order(&newfrom, &newto, &newoi, no_of_nodes));
+    IGRAPH_CHECK(igraph_vector_order(&newto, &newfrom, &graph->ii, no_of_nodes));
+
+    /* Edge attributes, we need an index that gives the ids of the
+       original edges for every new edge.
+    */
+    if (graph->attr) {
+        igraph_vector_t idx;
+        IGRAPH_VECTOR_INIT_FINALLY(&idx, remaining_edges);
+        for (i = 0, j = 0; i < no_of_edges; i++) {
+            if (mark[i] == 0) {
+                VECTOR(idx)[j++] = i;
+            }
+        }
+        IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, graph, &idx));
+        igraph_vector_destroy(&idx);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    /* Ok, we've all memory needed, free the old structure  */
+    igraph_vector_destroy(&graph->from);
+    igraph_vector_destroy(&graph->to);
+    igraph_vector_destroy(&graph->oi);
+    graph->from = newfrom;
+    graph->to = newto;
+    graph->oi = newoi;
+    IGRAPH_FINALLY_CLEAN(3);
+
+    igraph_Free(mark);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    /* Create start vectors, no memory is needed for this */
+    igraph_i_create_start(&graph->os, &graph->from, &graph->oi,
+                          (igraph_integer_t) no_of_nodes);
+    igraph_i_create_start(&graph->is, &graph->to,   &graph->ii,
+                          (igraph_integer_t) no_of_nodes);
+
+    /* Nothing to deallocate... */
+    return 0;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_delete_vertices
+ * \brief Removes vertices (with all their edges) from the graph.
+ *
+ * </para><para>
+ * This function changes the ids of the vertices (except in some very
+ * special cases, but these should not be relied on anyway).
+ *
+ * </para><para>
+ * This function invalidates all iterators.
+ *
+ * \param graph The graph to work on.
+ * \param vertices The ids of the vertices to remove in a
+ *                 vector. The vector may contain the same id more
+ *                 than once.
+ * \return Error code:
+ *         \c IGRAPH_EINVVID: invalid vertex id.
+ *
+ * Time complexity: O(|V|+|E|),
+ * |V| and
+ * |E| are the number of vertices and
+ * edges in the original graph.
+ *
+ * \example examples/simple/igraph_delete_vertices.c
+ */
+int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices) {
+    return igraph_delete_vertices_idx(graph, vertices, /* idx= */ 0,
+                                      /* invidx= */ 0);
+}
+
+int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices,
+                               igraph_vector_t *idx,
+                               igraph_vector_t *invidx) {
+
+    long int no_of_edges = igraph_ecount(graph);
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_vector_t edge_recoding, vertex_recoding;
+    igraph_vector_t *my_vertex_recoding = &vertex_recoding;
+    igraph_vit_t vit;
+    igraph_t newgraph;
+    long int i, j;
+    long int remaining_vertices, remaining_edges;
+
+    if (idx) {
+        my_vertex_recoding = idx;
+        IGRAPH_CHECK(igraph_vector_resize(idx, no_of_nodes));
+        igraph_vector_null(idx);
+    } else {
+        IGRAPH_VECTOR_INIT_FINALLY(&vertex_recoding, no_of_nodes);
+    }
+
+    IGRAPH_VECTOR_INIT_FINALLY(&edge_recoding, no_of_edges);
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vertices, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    /* mark the vertices to delete */
+    for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit) ) {
+        long int vertex = IGRAPH_VIT_GET(vit);
+        if (vertex < 0 || vertex >= no_of_nodes) {
+            IGRAPH_ERROR("Cannot delete vertices", IGRAPH_EINVVID);
+        }
+        VECTOR(*my_vertex_recoding)[vertex] = 1;
+    }
+    /* create vertex recoding vector */
+    for (remaining_vertices = 0, i = 0; i < no_of_nodes; i++) {
+        if (VECTOR(*my_vertex_recoding)[i] == 0) {
+            VECTOR(*my_vertex_recoding)[i] = remaining_vertices + 1;
+            remaining_vertices++;
+        } else {
+            VECTOR(*my_vertex_recoding)[i] = 0;
+        }
+    }
+    /* create edge recoding vector */
+    for (remaining_edges = 0, i = 0; i < no_of_edges; i++) {
+        long int from = (long int) VECTOR(graph->from)[i];
+        long int to = (long int) VECTOR(graph->to)[i];
+        if (VECTOR(*my_vertex_recoding)[from] != 0 &&
+            VECTOR(*my_vertex_recoding)[to  ] != 0) {
+            VECTOR(edge_recoding)[i] = remaining_edges + 1;
+            remaining_edges++;
+        }
+    }
+
+    /* start creating the graph */
+    newgraph.n = (igraph_integer_t) remaining_vertices;
+    newgraph.directed = graph->directed;
+
+    /* allocate vectors */
+    IGRAPH_VECTOR_INIT_FINALLY(&newgraph.from, remaining_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&newgraph.to, remaining_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&newgraph.oi, remaining_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&newgraph.ii, remaining_edges);
+    IGRAPH_VECTOR_INIT_FINALLY(&newgraph.os, remaining_vertices + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&newgraph.is, remaining_vertices + 1);
+
+    /* Add the edges */
+    for (i = 0, j = 0; j < remaining_edges; i++) {
+        if (VECTOR(edge_recoding)[i] > 0) {
+            long int from = (long int) VECTOR(graph->from)[i];
+            long int to = (long int) VECTOR(graph->to  )[i];
+            VECTOR(newgraph.from)[j] = VECTOR(*my_vertex_recoding)[from] - 1;
+            VECTOR(newgraph.to  )[j] = VECTOR(*my_vertex_recoding)[to] - 1;
+            j++;
+        }
+    }
+    /* update oi & ii */
+    IGRAPH_CHECK(igraph_vector_order(&newgraph.from, &newgraph.to, &newgraph.oi,
+                                     remaining_vertices));
+    IGRAPH_CHECK(igraph_vector_order(&newgraph.to, &newgraph.from, &newgraph.ii,
+                                     remaining_vertices));
+
+    IGRAPH_CHECK(igraph_i_create_start(&newgraph.os, &newgraph.from,
+                                       &newgraph.oi, (igraph_integer_t)
+                                       remaining_vertices));
+    IGRAPH_CHECK(igraph_i_create_start(&newgraph.is, &newgraph.to,
+                                       &newgraph.ii, (igraph_integer_t)
+                                       remaining_vertices));
+
+    /* attributes */
+    IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph,
+                            /*graph=*/ 1, /*vertex=*/0, /*edge=*/0);
+    IGRAPH_FINALLY_CLEAN(6);
+    IGRAPH_FINALLY(igraph_destroy, &newgraph);
+
+    if (newgraph.attr) {
+        igraph_vector_t iidx;
+        IGRAPH_VECTOR_INIT_FINALLY(&iidx, remaining_vertices);
+        for (i = 0; i < no_of_nodes; i++) {
+            long int jj = (long int) VECTOR(*my_vertex_recoding)[i];
+            if (jj != 0) {
+                VECTOR(iidx)[ jj - 1 ] = i;
+            }
+        }
+        IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph,
+                     &newgraph,
+                     &iidx));
+        IGRAPH_CHECK(igraph_vector_resize(&iidx, remaining_edges));
+        for (i = 0; i < no_of_edges; i++) {
+            long int jj = (long int) VECTOR(edge_recoding)[i];
+            if (jj != 0) {
+                VECTOR(iidx)[ jj - 1 ] = i;
+            }
+        }
+        IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &iidx));
+        igraph_vector_destroy(&iidx);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    igraph_vit_destroy(&vit);
+    igraph_vector_destroy(&edge_recoding);
+    igraph_destroy(graph);
+    *graph = newgraph;
+
+    IGRAPH_FINALLY_CLEAN(3);
+
+    /* TODO: this is duplicate */
+    if (invidx) {
+        IGRAPH_CHECK(igraph_vector_resize(invidx, remaining_vertices));
+        for (i = 0; i < no_of_nodes; i++) {
+            long int newid = (long int) VECTOR(*my_vertex_recoding)[i];
+            if (newid != 0) {
+                VECTOR(*invidx)[newid - 1] = i;
+            }
+        }
+    }
+
+    if (!idx) {
+        igraph_vector_destroy(my_vertex_recoding);
+        IGRAPH_FINALLY_CLEAN(1);
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_vcount
+ * \brief The number of vertices in a graph.
+ *
+ * \param graph The graph.
+ * \return Number of vertices.
+ *
+ * Time complexity: O(1)
+ */
+igraph_integer_t igraph_vcount(const igraph_t *graph) {
+    return graph->n;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_ecount
+ * \brief The number of edges in a graph.
+ *
+ * \param graph The graph.
+ * \return Number of edges.
+ *
+ * Time complexity: O(1)
+ */
+igraph_integer_t igraph_ecount(const igraph_t *graph) {
+    return (igraph_integer_t) igraph_vector_size(&graph->from);
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_neighbors
+ * \brief Adjacent vertices to a vertex.
+ *
+ * \param graph The graph to work on.
+ * \param neis This vector will contain the result. The vector should
+ *        be initialized beforehand and will be resized. Starting from igraph
+ *        version 0.4 this vector is always sorted, the vertex ids are
+ *        in increasing order.
+ * \param pnode The id of the node for which the adjacent vertices are
+ *        to be searched.
+ * \param mode Defines the way adjacent vertices are searched in
+ *        directed graphs. It can have the following values:
+ *        \c IGRAPH_OUT, vertices reachable by an
+ *        edge from the specified vertex are searched;
+ *        \c IGRAPH_IN, vertices from which the
+ *        specified vertex is reachable are searched;
+ *        \c IGRAPH_ALL, both kinds of vertices are
+ *        searched.
+ *        This parameter is ignored for undirected graphs.
+ * \return Error code:
+ *         \c IGRAPH_EINVVID: invalid vertex id.
+ *         \c IGRAPH_EINVMODE: invalid mode argument.
+ *         \c IGRAPH_ENOMEM: not enough memory.
+ *
+ * Time complexity: O(d),
+ * d is the number
+ * of adjacent vertices to the queried vertex.
+ *
+ * \example examples/simple/igraph_neighbors.c
+ */
+int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t pnode,
+                     igraph_neimode_t mode) {
+
+    long int length = 0, idx = 0;
+    long int i, j;
+
+    long int node = pnode;
+
+    if (node < 0 || node > igraph_vcount(graph) - 1) {
+        IGRAPH_ERROR("cannot get neighbors", IGRAPH_EINVVID);
+    }
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("cannot get neighbors", IGRAPH_EINVMODE);
+    }
+
+    if (! graph->directed) {
+        mode = IGRAPH_ALL;
+    }
+
+    /* Calculate needed space first & allocate it*/
+
+    if (mode & IGRAPH_OUT) {
+        length += (VECTOR(graph->os)[node + 1] - VECTOR(graph->os)[node]);
+    }
+    if (mode & IGRAPH_IN) {
+        length += (VECTOR(graph->is)[node + 1] - VECTOR(graph->is)[node]);
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(neis, length));
+
+    if (!igraph_is_directed(graph) || mode != IGRAPH_ALL) {
+
+        if (mode & IGRAPH_OUT) {
+            j = (long int) VECTOR(graph->os)[node + 1];
+            for (i = (long int) VECTOR(graph->os)[node]; i < j; i++) {
+                VECTOR(*neis)[idx++] =
+                    VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[i] ];
+            }
+        }
+        if (mode & IGRAPH_IN) {
+            j = (long int) VECTOR(graph->is)[node + 1];
+            for (i = (long int) VECTOR(graph->is)[node]; i < j; i++) {
+                VECTOR(*neis)[idx++] =
+                    VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[i] ];
+            }
+        }
+    } else {
+        /* both in- and out- neighbors in a directed graph,
+           we need to merge the two 'vectors' */
+        long int jj1 = (long int) VECTOR(graph->os)[node + 1];
+        long int j2 = (long int) VECTOR(graph->is)[node + 1];
+        long int i1 = (long int) VECTOR(graph->os)[node];
+        long int i2 = (long int) VECTOR(graph->is)[node];
+        while (i1 < jj1 && i2 < j2) {
+            long int n1 = (long int) VECTOR(graph->to)[
+                   (long int)VECTOR(graph->oi)[i1] ];
+            long int n2 = (long int) VECTOR(graph->from)[
+                   (long int)VECTOR(graph->ii)[i2] ];
+            if (n1 < n2) {
+                VECTOR(*neis)[idx++] = n1;
+                i1++;
+            } else if (n1 > n2) {
+                VECTOR(*neis)[idx++] = n2;
+                i2++;
+            } else {
+                VECTOR(*neis)[idx++] = n1;
+                VECTOR(*neis)[idx++] = n2;
+                i1++;
+                i2++;
+            }
+        }
+        while (i1 < jj1) {
+            long int n1 = (long int) VECTOR(graph->to)[
+                   (long int)VECTOR(graph->oi)[i1] ];
+            VECTOR(*neis)[idx++] = n1;
+            i1++;
+        }
+        while (i2 < j2) {
+            long int n2 = (long int) VECTOR(graph->from)[
+                   (long int)VECTOR(graph->ii)[i2] ];
+            VECTOR(*neis)[idx++] = n2;
+            i2++;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \ingroup internal
+ *
+ */
+
+int igraph_i_create_start(igraph_vector_t *res, igraph_vector_t *el, igraph_vector_t *iindex,
+                          igraph_integer_t nodes) {
+
+# define EDGE(i) (VECTOR(*el)[ (long int) VECTOR(*iindex)[(i)] ])
+
+    long int no_of_nodes;
+    long int no_of_edges;
+    long int i, j, idx;
+
+    no_of_nodes = nodes;
+    no_of_edges = igraph_vector_size(el);
+
+    /* result */
+
+    IGRAPH_CHECK(igraph_vector_resize(res, nodes + 1));
+
+    /* create the index */
+
+    if (igraph_vector_size(el) == 0) {
+        /* empty graph */
+        igraph_vector_null(res);
+    } else {
+        idx = -1;
+        for (i = 0; i <= EDGE(0); i++) {
+            idx++; VECTOR(*res)[idx] = 0;
+        }
+        for (i = 1; i < no_of_edges; i++) {
+            long int n = (long int) (EDGE(i) - EDGE((long int)VECTOR(*res)[idx]));
+            for (j = 0; j < n; j++) {
+                idx++; VECTOR(*res)[idx] = i;
+            }
+        }
+        j = (long int) EDGE((long int)VECTOR(*res)[idx]);
+        for (i = 0; i < no_of_nodes - j; i++) {
+            idx++; VECTOR(*res)[idx] = no_of_edges;
+        }
+    }
+
+    /* clean */
+
+# undef EDGE
+    return 0;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_is_directed
+ * \brief Is this a directed graph?
+ *
+ * \param graph The graph.
+ * \return Logical value, <code>TRUE</code> if the graph is directed,
+ * <code>FALSE</code> otherwise.
+ *
+ * Time complexity: O(1)
+ *
+ * \example examples/simple/igraph_is_directed.c
+ */
+
+igraph_bool_t igraph_is_directed(const igraph_t *graph) {
+    return graph->directed;
+}
+
+/**
+ * \ingroup interface
+ * \function igraph_degree
+ * \brief The degree of some vertices in a graph.
+ *
+ * </para><para>
+ * This function calculates the in-, out- or total degree of the
+ * specified vertices.
+ * \param graph The graph.
+ * \param res Vector, this will contain the result. It should be
+ *        initialized and will be resized to be the appropriate size.
+ * \param vids Vector, giving the vertex ids of which the degree will
+ *        be calculated.
+ * \param mode Defines the type of the degree. Valid modes are:
+ *        \c IGRAPH_OUT, out-degree;
+ *        \c IGRAPH_IN, in-degree;
+ *        \c IGRAPH_ALL, total degree (sum of the
+ *        in- and out-degree).
+ *        This parameter is ignored for undirected graphs.
+ * \param loops Boolean, gives whether the self-loops should be
+ *        counted.
+ * \return Error code:
+ *         \c IGRAPH_EINVVID: invalid vertex id.
+ *         \c IGRAPH_EINVMODE: invalid mode argument.
+ *
+ * Time complexity: O(v) if
+ * loops is
+ * TRUE, and
+ * O(v*d)
+ * otherwise. v is the number of
+ * vertices for which the degree will be calculated, and
+ * d is their (average) degree.
+ *
+ * \sa \ref igraph_strength() for the version that takes into account
+ * edge weights.
+ *
+ * \example examples/simple/igraph_degree.c
+ */
+int igraph_degree(const igraph_t *graph, igraph_vector_t *res,
+                  const igraph_vs_t vids,
+                  igraph_neimode_t mode, igraph_bool_t loops) {
+
+    long int nodes_to_calc;
+    long int i, j;
+    igraph_vit_t vit;
+
+    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
+    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
+
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("degree calculation failed", IGRAPH_EINVMODE);
+    }
+
+    nodes_to_calc = IGRAPH_VIT_SIZE(vit);
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
+    igraph_vector_null(res);
+
+    if (loops) {
+        if (mode & IGRAPH_OUT) {
+            for (IGRAPH_VIT_RESET(vit), i = 0;
+                 !IGRAPH_VIT_END(vit);
+                 IGRAPH_VIT_NEXT(vit), i++) {
+                long int vid = IGRAPH_VIT_GET(vit);
+                VECTOR(*res)[i] += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]);
+            }
+        }
+        if (mode & IGRAPH_IN) {
+            for (IGRAPH_VIT_RESET(vit), i = 0;
+                 !IGRAPH_VIT_END(vit);
+                 IGRAPH_VIT_NEXT(vit), i++) {
+                long int vid = IGRAPH_VIT_GET(vit);
+                VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]);
+            }
+        }
+    } else { /* no loops */
+        if (mode & IGRAPH_OUT) {
+            for (IGRAPH_VIT_RESET(vit), i = 0;
+                 !IGRAPH_VIT_END(vit);
+                 IGRAPH_VIT_NEXT(vit), i++) {
+                long int vid = IGRAPH_VIT_GET(vit);
+                VECTOR(*res)[i] += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]);
+                for (j = (long int) VECTOR(graph->os)[vid];
+                     j < VECTOR(graph->os)[vid + 1]; j++) {
+                    if (VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[j] ] == vid) {
+                        VECTOR(*res)[i] -= 1;
+                    }
+                }
+            }
+        }
+        if (mode & IGRAPH_IN) {
+            for (IGRAPH_VIT_RESET(vit), i = 0;
+                 !IGRAPH_VIT_END(vit);
+                 IGRAPH_VIT_NEXT(vit), i++) {
+                long int vid = IGRAPH_VIT_GET(vit);
+                VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]);
+                for (j = (long int) VECTOR(graph->is)[vid];
+                     j < VECTOR(graph->is)[vid + 1]; j++) {
+                    if (VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[j] ] == vid) {
+                        VECTOR(*res)[i] -= 1;
+                    }
+                }
+            }
+        }
+    }  /* loops */
+
+    igraph_vit_destroy(&vit);
+    IGRAPH_FINALLY_CLEAN(1);
+
+    return 0;
+}
+
+/**
+ * \function igraph_edge
+ * \brief Gives the head and tail vertices of an edge.
+ *
+ * \param graph The graph object.
+ * \param eid The edge id.
+ * \param from Pointer to an \type igraph_integer_t. The tail of the edge
+ * will be placed here.
+ * \param to Pointer to an \type igraph_integer_t. The head of the edge
+ * will be placed here.
+ * \return Error code. The current implementation always returns with
+ * success.
+ * \sa \ref igraph_get_eid() for the opposite operation.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(1).
+ */
+
+int igraph_edge(const igraph_t *graph, igraph_integer_t eid,
+                igraph_integer_t *from, igraph_integer_t *to) {
+
+    if (igraph_is_directed(graph)) {
+        *from = (igraph_integer_t) VECTOR(graph->from)[(long int)eid];
+        *to   = (igraph_integer_t) VECTOR(graph->to  )[(long int)eid];
+    } else {
+        *from = (igraph_integer_t) VECTOR(graph->to  )[(long int)eid];
+        *to   = (igraph_integer_t) VECTOR(graph->from)[(long int)eid];
+    }
+
+    return 0;
+}
+
+int igraph_edges(const igraph_t *graph, igraph_es_t eids,
+                 igraph_vector_t *edges) {
+
+    igraph_eit_t eit;
+    long int n, ptr = 0;
+
+    IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit));
+    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
+    n = IGRAPH_EIT_SIZE(eit);
+    IGRAPH_CHECK(igraph_vector_resize(edges, n * 2));
+    if (igraph_is_directed(graph)) {
+        for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
+            long int e = IGRAPH_EIT_GET(eit);
+            VECTOR(*edges)[ptr++] = IGRAPH_FROM(graph, e);
+            VECTOR(*edges)[ptr++] = IGRAPH_TO(graph, e);
+        }
+    } else {
+        for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
+            long int e = IGRAPH_EIT_GET(eit);
+            VECTOR(*edges)[ptr++] = IGRAPH_TO(graph, e);
+            VECTOR(*edges)[ptr++] = IGRAPH_FROM(graph, e);
+        }
+    }
+
+    igraph_eit_destroy(&eit);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/* This is an unsafe macro. Only supply variable names, i.e. no
+   expressions as parameters, otherwise nasty things can happen */
+
+#define BINSEARCH(start,end,value,iindex,edgelist,N,pos)     \
+    do {                                                      \
+        while ((start) < (end)) {                                 \
+            long int mid=(start)+((end)-(start))/2;                 \
+            long int e=(long int) VECTOR((iindex))[mid];            \
+            if (VECTOR((edgelist))[e] < (value)) {                  \
+                (start)=mid+1;                                        \
+            } else {                                                \
+                (end)=mid;                                            \
+            }                                                       \
+        }                                                         \
+        if ((start)<(N)) {                                        \
+            long int e=(long int) VECTOR((iindex))[(start)];        \
+            if (VECTOR((edgelist))[e] == (value)) {                 \
+                *(pos)=(igraph_integer_t) e;              \
+            }                                                       \
+        } } while(0)
+
+#define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid)                     \
+    do {                                                              \
+        long int start=(long int) VECTOR(graph->os)[xfrom];         \
+        long int end=(long int) VECTOR(graph->os)[xfrom+1];         \
+        long int N=end;                                                 \
+        long int start2=(long int) VECTOR(graph->is)[xto];          \
+        long int end2=(long int) VECTOR(graph->is)[xto+1];          \
+        long int N2=end2;                                               \
+        if (end-start<end2-start2) {                                    \
+            BINSEARCH(start,end,xto,graph->oi,graph->to,N,eid);           \
+        } else {                                                        \
+            BINSEARCH(start2,end2,xfrom,graph->ii,graph->from,N2,eid);    \
+        }                                                               \
+    } while (0)
+
+#define FIND_UNDIRECTED_EDGE(graph,from,to,eid)                     \
+    do {                                                              \
+        long int xfrom1= from > to ? from : to;                         \
+        long int xto1= from > to ? to : from;                           \
+        FIND_DIRECTED_EDGE(graph,xfrom1,xto1,eid);                      \
+    } while (0)
+
+/**
+ * \function igraph_get_eid
+ * \brief Get the edge id from the end points of an edge.
+ *
+ * For undirected graphs \c pfrom and \c pto are exchangeable.
+ *
+ * \param graph The graph object.
+ * \param eid Pointer to an integer, the edge id will be stored here.
+ * \param pfrom The starting point of the edge.
+ * \param pto The end point of the edge.
+ * \param directed Logical constant, whether to search for directed
+ *        edges in a directed graph. Ignored for undirected graphs.
+ * \param error Logical scalar, whether to report an error if the edge
+ *        was not found. If it is false, then -1 will be assigned to \p eid.
+ * \return Error code.
+ * \sa \ref igraph_edge() for the opposite operation.
+ *
+ * Time complexity: O(log (d)), where d is smaller of the out-degree
+ * of \c pfrom and in-degree of \c pto if \p directed is true. If \p directed
+ * is false, then it is O(log(d)+log(d2)), where d is the same as before and
+ * d2 is the minimum of the out-degree of \c pto and the in-degree of \c pfrom.
+ *
+ * \example examples/simple/igraph_get_eid.c
+ *
+ * Added in version 0.2.</para><para>
+ */
+
+int igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid,
+                   igraph_integer_t pfrom, igraph_integer_t pto,
+                   igraph_bool_t directed, igraph_bool_t error) {
+
+    long int from = pfrom, to = pto;
+    long int nov = igraph_vcount(graph);
+
+    if (from < 0 || to < 0 || from > nov - 1 || to > nov - 1) {
+        IGRAPH_ERROR("cannot get edge id", IGRAPH_EINVVID);
+    }
+
+    *eid = -1;
+    if (igraph_is_directed(graph)) {
+
+        /* Directed graph */
+        FIND_DIRECTED_EDGE(graph, from, to, eid);
+        if (!directed && *eid < 0) {
+            FIND_DIRECTED_EDGE(graph, to, from, eid);
+        }
+
+    } else {
+
+        /* Undirected graph, they only have one mode */
+        FIND_UNDIRECTED_EDGE(graph, from, to, eid);
+
+    }
+
+    if (*eid < 0) {
+        if (error) {
+            IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+        }
+    }
+
+    return IGRAPH_SUCCESS;
+}
+
+int igraph_get_eids_pairs(const igraph_t *graph, igraph_vector_t *eids,
+                          const igraph_vector_t *pairs,
+                          igraph_bool_t directed, igraph_bool_t error);
+
+int igraph_get_eids_path(const igraph_t *graph, igraph_vector_t *eids,
+                         const igraph_vector_t *path,
+                         igraph_bool_t directed, igraph_bool_t error);
+
+int igraph_get_eids_pairs(const igraph_t *graph, igraph_vector_t *eids,
+                          const igraph_vector_t *pairs,
+                          igraph_bool_t directed, igraph_bool_t error) {
+    long int n = igraph_vector_size(pairs);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+    igraph_integer_t eid = -1;
+
+    if (n % 2 != 0) {
+        IGRAPH_ERROR("Cannot get edge ids, invalid length of edge ids",
+                     IGRAPH_EINVAL);
+    }
+    if (!igraph_vector_isininterval(pairs, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID);
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(eids, n / 2));
+
+    if (igraph_is_directed(graph)) {
+        for (i = 0; i < n / 2; i++) {
+            long int from = (long int) VECTOR(*pairs)[2 * i];
+            long int to = (long int) VECTOR(*pairs)[2 * i + 1];
+
+            eid = -1;
+            FIND_DIRECTED_EDGE(graph, from, to, &eid);
+            if (!directed && eid < 0) {
+                FIND_DIRECTED_EDGE(graph, to, from, &eid);
+            }
+
+            VECTOR(*eids)[i] = eid;
+            if (eid < 0 && error) {
+                IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+            }
+        }
+    } else {
+        for (i = 0; i < n / 2; i++) {
+            long int from = (long int) VECTOR(*pairs)[2 * i];
+            long int to = (long int) VECTOR(*pairs)[2 * i + 1];
+
+            eid = -1;
+            FIND_UNDIRECTED_EDGE(graph, from, to, &eid);
+            VECTOR(*eids)[i] = eid;
+            if (eid < 0 && error) {
+                IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+            }
+        }
+    }
+
+    return 0;
+}
+
+int igraph_get_eids_path(const igraph_t *graph, igraph_vector_t *eids,
+                         const igraph_vector_t *path,
+                         igraph_bool_t directed, igraph_bool_t error) {
+
+    long int n = igraph_vector_size(path);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+    igraph_integer_t eid = -1;
+
+    if (!igraph_vector_isininterval(path, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID);
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(eids, n == 0 ? 0 : n - 1));
+
+    if (igraph_is_directed(graph)) {
+        for (i = 0; i < n - 1; i++) {
+            long int from = (long int) VECTOR(*path)[i];
+            long int to = (long int) VECTOR(*path)[i + 1];
+
+            eid = -1;
+            FIND_DIRECTED_EDGE(graph, from, to, &eid);
+            if (!directed && eid < 0) {
+                FIND_DIRECTED_EDGE(graph, to, from, &eid);
+            }
+
+            VECTOR(*eids)[i] = eid;
+            if (eid < 0 && error) {
+                IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+            }
+        }
+    } else {
+        for (i = 0; i < n - 1; i++) {
+            long int from = (long int) VECTOR(*path)[i];
+            long int to = (long int) VECTOR(*path)[i + 1];
+
+            eid = -1;
+            FIND_UNDIRECTED_EDGE(graph, from, to, &eid);
+            VECTOR(*eids)[i] = eid;
+            if (eid < 0 && error) {
+                IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+            }
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * \function igraph_get_eids
+ * Return edge ids based on the adjacent vertices.
+ *
+ * This function operates in two modes. If the \c pairs argument is
+ * not a null pointer, but the \c path argument is, then it searches
+ * for the edge ids of all pairs of vertices given in \c pairs. The
+ * pairs of vertex ids are taken consecutively from the vector,
+ * i.e. <code>VECTOR(pairs)[0]</code> and
+ * <code>VECTOR(pairs)[1]</code> give the first
+ * pair, <code>VECTOR(pairs)[2]</code> and
+ * <code>VECTOR(pairs)[3]</code> the second pair, etc.
+ *
+ * </para><para>
+ * If the \c pairs argument is a null pointer, and \c path is not a
+ * null pointer, then the \c path is interpreted as a path given by
+ * vertex ids and the edges along the path are returned.
+ *
+ * </para><para>
+ * If neither \c pairs nor \c path are null pointers, then both are
+ * considered (first \c pairs and then \c path), and the results are
+ * concatenated.
+ *
+ * </para><para>
+ * If the \c error argument is true, then it is an error to give pairs
+ * of vertices that are not connected. Otherwise -1 is
+ * reported for not connected vertices.
+ *
+ * </para><para>
+ * If there are multiple edges in the graph, then these are ignored;
+ * i.e. for a given pair of vertex ids, always the same edge id is
+ * returned, even if the pair is given multiple time in \c pairs or in
+ * \c path. See \ref igraph_get_eids_multi() for a similar function
+ * that works differently in case of multiple edges.
+ *
+ * \param graph The input graph.
+ * \param eids Pointer to an initialized vector, the result is stored
+ *        here. It will be resized as needed.
+ * \param pairs Vector giving pairs of vertices, or a null pointer.
+ * \param path Vector giving vertex ids along a path, or a null
+ *        pointer.
+ * \param directed Logical scalar, whether to consider edge directions
+ *        in directed graphs. This is ignored for undirected graphs.
+ * \param error Logical scalar, whether it is an error to supply
+ *        non-connected vertices. If false, then -1 is
+ *        returned for non-connected pairs.
+ * \return Error code.
+ *
+ * Time complexity: O(n log(d)), where n is the number of queried
+ * edges and d is the average degree of the vertices.
+ *
+ * \sa \ref igraph_get_eid() for a single edge, \ref
+ * igraph_get_eids_multi() for a version that handles multiple edges
+ * better (at a cost).
+ *
+ * \example examples/simple/igraph_get_eids.c
+ */
+
+int igraph_get_eids(const igraph_t *graph, igraph_vector_t *eids,
+                    const igraph_vector_t *pairs,
+                    const igraph_vector_t *path,
+                    igraph_bool_t directed, igraph_bool_t error) {
+
+    if (!pairs && !path) {
+        igraph_vector_clear(eids);
+        return 0;
+    } else if (pairs && !path) {
+        return igraph_get_eids_pairs(graph, eids, pairs, directed, error);
+    } else if (!pairs && path) {
+        return igraph_get_eids_path(graph, eids, path, directed, error);
+    } else {
+        /* both */
+        igraph_vector_t tmp;
+        IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
+        IGRAPH_CHECK(igraph_get_eids_pairs(graph, eids, pairs, directed, error));
+        IGRAPH_CHECK(igraph_get_eids_path(graph, &tmp, path, directed, error));
+        IGRAPH_CHECK(igraph_vector_append(eids, &tmp));
+        igraph_vector_destroy(&tmp);
+        IGRAPH_FINALLY_CLEAN(1);
+        return 0;
+    }
+}
+
+#undef BINSEARCH
+#undef FIND_DIRECTED_EDGE
+#undef FIND_UNDIRECTED_EDGE
+
+#define BINSEARCH(start,end,value,iindex,edgelist,N,pos,seen)    \
+    do {                                                      \
+        while ((start) < (end)) {                                 \
+            long int mid=(start)+((end)-(start))/2;                 \
+            long int e=(long int) VECTOR((iindex))[mid];        \
+            if (VECTOR((edgelist))[e] < (value)) {                  \
+                (start)=mid+1;                                        \
+            } else {                                                \
+                (end)=mid;                                            \
+            }                                                       \
+        }                                                         \
+        if ((start)<(N)) {                                        \
+            long int e=(long int) VECTOR((iindex))[(start)];        \
+            while ((start)<(N) && seen[e] && VECTOR(edgelist)[e] == (value)) {  \
+                (start)++;                        \
+                e=(long int) VECTOR(iindex)[(start)];         \
+            }                                           \
+            if ((start)<(N) && !(seen[e]) && VECTOR(edgelist)[e] == (value)) {  \
+                *(pos)=(igraph_integer_t) e;                  \
+            }                                                       \
+        } } while(0)
+
+#define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid,seen)            \
+    do {                                                              \
+        long int start=(long int) VECTOR(graph->os)[xfrom];         \
+        long int end=(long int) VECTOR(graph->os)[xfrom+1];         \
+        long int N=end;                                                 \
+        long int start2=(long int) VECTOR(graph->is)[xto];          \
+        long int end2=(long int) VECTOR(graph->is)[xto+1];          \
+        long int N2=end2;                                               \
+        if (end-start<end2-start2) {                                    \
+            BINSEARCH(start,end,xto,graph->oi,graph->to,N,eid,seen);      \
+        } else {                                                        \
+            BINSEARCH(start2,end2,xfrom,graph->ii,graph->from,N2,eid,seen);   \
+        }                                                               \
+    } while (0)
+
+#define FIND_UNDIRECTED_EDGE(graph,from,to,eid,seen)            \
+    do {                                                              \
+        long int xfrom1= from > to ? from : to;                         \
+        long int xto1= from > to ? to : from;                           \
+        FIND_DIRECTED_EDGE(graph,xfrom1,xto1,eid,seen);         \
+    } while (0)
+
+
+int igraph_get_eids_multipairs(const igraph_t *graph, igraph_vector_t *eids,
+                               const igraph_vector_t *pairs,
+                               igraph_bool_t directed, igraph_bool_t error);
+
+int igraph_get_eids_multipath(const igraph_t *graph, igraph_vector_t *eids,
+                              const igraph_vector_t *path,
+                              igraph_bool_t directed, igraph_bool_t error);
+
+int igraph_get_eids_multipairs(const igraph_t *graph, igraph_vector_t *eids,
+                               const igraph_vector_t *pairs,
+                               igraph_bool_t directed, igraph_bool_t error) {
+
+    long int n = igraph_vector_size(pairs);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_bool_t *seen;
+    long int i;
+    igraph_integer_t eid = -1;
+
+    if (n % 2 != 0) {
+        IGRAPH_ERROR("Cannot get edge ids, invalid length of edge ids",
+                     IGRAPH_EINVAL);
+    }
+    if (!igraph_vector_isininterval(pairs, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID);
+    }
+
+    seen = igraph_Calloc(no_of_edges, igraph_bool_t);
+    if (seen == 0) {
+        IGRAPH_ERROR("Cannot get edge ids", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, seen);
+    IGRAPH_CHECK(igraph_vector_resize(eids, n / 2));
+
+    if (igraph_is_directed(graph)) {
+        for (i = 0; i < n / 2; i++) {
+            long int from = (long int) VECTOR(*pairs)[2 * i];
+            long int to = (long int) VECTOR(*pairs)[2 * i + 1];
+
+            eid = -1;
+            FIND_DIRECTED_EDGE(graph, from, to, &eid, seen);
+            if (!directed && eid < 0) {
+                FIND_DIRECTED_EDGE(graph, to, from, &eid, seen);
+            }
+
+            VECTOR(*eids)[i] = eid;
+            if (eid >= 0) {
+                seen[(long int)(eid)] = 1;
+            } else if (error) {
+                IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+            }
+        }
+    } else {
+        for (i = 0; i < n / 2; i++) {
+            long int from = (long int) VECTOR(*pairs)[2 * i];
+            long int to = (long int) VECTOR(*pairs)[2 * i + 1];
+
+            eid = -1;
+            FIND_UNDIRECTED_EDGE(graph, from, to, &eid, seen);
+            VECTOR(*eids)[i] = eid;
+            if (eid >= 0) {
+                seen[(long int)(eid)] = 1;
+            } else if (error) {
+                IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+            }
+        }
+    }
+
+    igraph_Free(seen);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+int igraph_get_eids_multipath(const igraph_t *graph, igraph_vector_t *eids,
+                              const igraph_vector_t *path,
+                              igraph_bool_t directed, igraph_bool_t error) {
+
+    long int n = igraph_vector_size(path);
+    long int no_of_nodes = igraph_vcount(graph);
+    long int no_of_edges = igraph_ecount(graph);
+    igraph_bool_t *seen;
+    long int i;
+    igraph_integer_t eid = -1;
+
+    if (!igraph_vector_isininterval(path, 0, no_of_nodes - 1)) {
+        IGRAPH_ERROR("Cannot get edge ids, invalid vertex id", IGRAPH_EINVVID);
+    }
+
+    seen = igraph_Calloc(no_of_edges, igraph_bool_t);
+    if (!seen) {
+        IGRAPH_ERROR("Cannot get edge ids", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, seen);
+    IGRAPH_CHECK(igraph_vector_resize(eids, n == 0 ? 0 : n - 1));
+
+    if (igraph_is_directed(graph)) {
+        for (i = 0; i < n - 1; i++) {
+            long int from = (long int) VECTOR(*path)[i];
+            long int to = (long int) VECTOR(*path)[i + 1];
+
+            eid = -1;
+            FIND_DIRECTED_EDGE(graph, from, to, &eid, seen);
+            if (!directed && eid < 0) {
+                FIND_DIRECTED_EDGE(graph, to, from, &eid, seen);
+            }
+
+            VECTOR(*eids)[i] = eid;
+            if (eid >= 0) {
+                seen[(long int)(eid)] = 1;
+            } else if (error) {
+                IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+            }
+        }
+    } else {
+        for (i = 0; i < n - 1; i++) {
+            long int from = (long int) VECTOR(*path)[i];
+            long int to = (long int) VECTOR(*path)[i + 1];
+
+            eid = -1;
+            FIND_UNDIRECTED_EDGE(graph, from, to, &eid, seen);
+            VECTOR(*eids)[i] = eid;
+            if (eid >= 0) {
+                seen[(long int)(eid)] = 1;
+            } else if (error) {
+                IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL);
+            }
+        }
+    }
+
+    igraph_Free(seen);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+#undef BINSEARCH
+#undef FIND_DIRECTED_EDGE
+#undef FIND_UNDIRECTED_EDGE
+
+/**
+ * \function igraph_get_eids_multi
+ * \brief Query edge ids based on their adjacent vertices, handle multiple edges.
+ *
+ * This function operates in two modes. If the \c pairs argument is
+ * not a null pointer, but the \c path argument is, then it searches
+ * for the edge ids of all pairs of vertices given in \c pairs. The
+ * pairs of vertex ids are taken consecutively from the vector,
+ * i.e. <code>VECTOR(pairs)[0]</code> and
+ * <code>VECTOR(pairs)[1]</code> give the first pair,
+ * <code>VECTOR(pairs)[2]</code> and <code>VECTOR(pairs)[3]</code> the
+ * second pair, etc.
+ *
+ * </para><para>
+ * If the \c pairs argument is a null pointer, and \c path is not a
+ * null pointer, then the \c path is interpreted as a path given by
+ * vertex ids and the edges along the path are returned.
+ *
+ * </para><para>
+ * If the \c error argument is true, then it is an error to give pairs of
+ * vertices that are not connected. Otherwise -1 is
+ * returned for not connected vertex pairs.
+ *
+ * </para><para>
+ * An error is triggered if both \c pairs and \c path are non-null
+ * pointers.
+ *
+ * </para><para>
+ * This function handles multiple edges properly, i.e. if the same
+ * pair is given multiple times and they are indeed connected by
+ * multiple edges, then each time a different edge id is reported.
+ *
+ * \param graph The input graph.
+ * \param eids Pointer to an initialized vector, the result is stored
+ *        here. It will be resized as needed.
+ * \param pairs Vector giving pairs of vertices, or a null pointer.
+ * \param path Vector giving vertex ids along a path, or a null
+ *        pointer.
+ * \param directed Logical scalar, whether to consider edge directions
+ *        in directed graphs. This is ignored for undirected graphs.
+ * \param error Logical scalar, whether to report an error if
+ *        non-connected vertices are specified. If false, then -1
+ *        is returned for non-connected vertex pairs.
+ * \return Error code.
+ *
+ * Time complexity: O(|E|+n log(d)), where |E| is the number of edges
+ * in the graph, n is the number of queried edges and d is the average
+ * degree of the vertices.
+ *
+ * \sa \ref igraph_get_eid() for a single edge, \ref
+ * igraph_get_eids() for a faster version that does not handle
+ * multiple edges.
+ */
+
+int igraph_get_eids_multi(const igraph_t *graph, igraph_vector_t *eids,
+                          const igraph_vector_t *pairs,
+                          const igraph_vector_t *path,
+                          igraph_bool_t directed, igraph_bool_t error) {
+
+    if (!pairs && !path) {
+        igraph_vector_clear(eids);
+        return 0;
+    } else if (pairs && !path) {
+        return igraph_get_eids_multipairs(graph, eids, pairs, directed, error);
+    } else if (!pairs && path) {
+        return igraph_get_eids_multipath(graph, eids, path, directed, error);
+    } else { /* both */
+        IGRAPH_ERROR("Give `pairs' or `path' but not both", IGRAPH_EINVAL);
+    }
+}
+
+/**
+ * \function igraph_adjacent
+ * \brief Gives the incident edges of a vertex.
+ *
+ * This function was superseded by \ref igraph_incident() in igraph 0.6.
+ * Please use \ref igraph_incident() instead of this function.
+ *
+ * </para><para>
+ * Added in version 0.2, deprecated in version 0.6.
+ */
+int igraph_adjacent(const igraph_t *graph, igraph_vector_t *eids,
+                    igraph_integer_t pnode, igraph_neimode_t mode) {
+    IGRAPH_WARNING("igraph_adjacent is deprecated, use igraph_incident");
+    return igraph_incident(graph, eids, pnode, mode);
+}
+
+/**
+ * \function igraph_incident
+ * \brief Gives the incident edges of a vertex.
+ *
+ * \param graph The graph object.
+ * \param eids An initialized \type vector_t object. It will be resized
+ * to hold the result.
+ * \param pnode A vertex id.
+ * \param mode Specifies what kind of edges to include for directed
+ * graphs. \c IGRAPH_OUT means only outgoing edges, \c IGRAPH_IN only
+ * incoming edges, \c IGRAPH_ALL both. This parameter is ignored for
+ * undirected graphs.
+ * \return Error code. \c IGRAPH_EINVVID: invalid \p pnode argument,
+ *   \c IGRAPH_EINVMODE: invalid \p mode argument.
+ *
+ * Added in version 0.2.</para><para>
+ *
+ * Time complexity: O(d), the number of incident edges to \p pnode.
+ */
+
+int igraph_incident(const igraph_t *graph, igraph_vector_t *eids,
+                    igraph_integer_t pnode, igraph_neimode_t mode) {
+
+    long int length = 0, idx = 0;
+    long int i, j;
+
+    long int node = pnode;
+
+    if (node < 0 || node > igraph_vcount(graph) - 1) {
+        IGRAPH_ERROR("cannot get neighbors", IGRAPH_EINVVID);
+    }
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("cannot get neighbors", IGRAPH_EINVMODE);
+    }
+
+    if (! graph->directed) {
+        mode = IGRAPH_ALL;
+    }
+
+    /* Calculate needed space first & allocate it*/
+
+    if (mode & IGRAPH_OUT) {
+        length += (VECTOR(graph->os)[node + 1] - VECTOR(graph->os)[node]);
+    }
+    if (mode & IGRAPH_IN) {
+        length += (VECTOR(graph->is)[node + 1] - VECTOR(graph->is)[node]);
+    }
+
+    IGRAPH_CHECK(igraph_vector_resize(eids, length));
+
+    if (mode & IGRAPH_OUT) {
+        j = (long int) VECTOR(graph->os)[node + 1];
+        for (i = (long int) VECTOR(graph->os)[node]; i < j; i++) {
+            VECTOR(*eids)[idx++] = VECTOR(graph->oi)[i];
+        }
+    }
+    if (mode & IGRAPH_IN) {
+        j = (long int) VECTOR(graph->is)[node + 1];
+        for (i = (long int) VECTOR(graph->is)[node]; i < j; i++) {
+            VECTOR(*eids)[idx++] = VECTOR(graph->ii)[i];
+        }
+    }
+
+    return 0;
+}
diff --git a/igraph/src/types.c b/igraph/src/types.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/types.c
@@ -0,0 +1,146 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include <float.h>
+
+#ifdef _MSC_VER
+    #define snprintf _snprintf
+#endif
+
+#ifdef DBL_DIG
+    /* Use DBL_DIG to determine the maximum precision used for %g */
+    #define STRINGIFY_HELPER(x) #x
+    #define STRINGIFY(x) STRINGIFY_HELPER(x)
+    #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%." STRINGIFY(DBL_DIG) "g"
+#else
+    /* Assume a precision of 10 digits for %g */
+    #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%.10g"
+#endif
+
+#ifndef USING_R
+int igraph_real_printf(igraph_real_t val) {
+    if (igraph_finite(val)) {
+        return printf("%g", val);
+    } else if (igraph_is_nan(val)) {
+        return printf("NaN");
+    } else if (igraph_is_inf(val)) {
+        if (val < 0) {
+            return printf("-Inf");
+        } else {
+            return printf("Inf");
+        }
+    } else {
+        /* fallback */
+        return printf("%g", val);
+    }
+}
+#endif
+
+int igraph_real_fprintf(FILE *file, igraph_real_t val) {
+    if (igraph_finite(val)) {
+        return fprintf(file, "%g", val);
+    } else if (igraph_is_nan(val)) {
+        return fprintf(file, "NaN");
+    } else if (igraph_is_inf(val)) {
+        if (val < 0) {
+            return fprintf(file, "-Inf");
+        } else {
+            return fprintf(file, "Inf");
+        }
+    } else {
+        /* fallback */
+        return fprintf(file, "%g", val);
+    }
+}
+
+int igraph_real_snprintf(char* str, size_t size, igraph_real_t val) {
+    if (igraph_finite(val)) {
+        return snprintf(str, size, "%g", val);
+    } else if (igraph_is_nan(val)) {
+        return snprintf(str, size, "NaN");
+    } else if (igraph_is_inf(val)) {
+        if (val < 0) {
+            return snprintf(str, size, "-Inf");
+        } else {
+            return snprintf(str, size, "Inf");
+        }
+    } else {
+        /* fallback */
+        return snprintf(str, size, "%g", val);
+    }
+}
+
+#ifndef USING_R
+int igraph_real_printf_precise(igraph_real_t val) {
+    if (igraph_finite(val)) {
+        return printf(IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val);
+    } else if (igraph_is_nan(val)) {
+        return printf("NaN");
+    } else if (igraph_is_inf(val)) {
+        if (val < 0) {
+            return printf("-Inf");
+        } else {
+            return printf("Inf");
+        }
+    } else {
+        /* fallback */
+        return printf(IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val);
+    }
+}
+#endif
+
+int igraph_real_fprintf_precise(FILE *file, igraph_real_t val) {
+    if (igraph_finite(val)) {
+        return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val);
+    } else if (igraph_is_nan(val)) {
+        return fprintf(file, "NaN");
+    } else if (igraph_is_inf(val)) {
+        if (val < 0) {
+            return fprintf(file, "-Inf");
+        } else {
+            return fprintf(file, "Inf");
+        }
+    } else {
+        /* fallback */
+        return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val);
+    }
+}
+
+int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val) {
+    if (igraph_finite(val)) {
+        return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val);
+    } else if (igraph_is_nan(val)) {
+        return snprintf(str, size, "NaN");
+    } else if (igraph_is_inf(val)) {
+        if (val < 0) {
+            return snprintf(str, size, "-Inf");
+        } else {
+            return snprintf(str, size, "Inf");
+        }
+    } else {
+        /* fallback */
+        return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val);
+    }
+}
+
diff --git a/igraph/src/typesize.c b/igraph/src/typesize.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/typesize.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ftnlen f__typesize[] = { 0, 0, sizeof(shortint), sizeof(integer),
+			sizeof(real), sizeof(doublereal),
+			sizeof(f2c_complex), sizeof(doublecomplex),
+			sizeof(logical), sizeof(char),
+			0, sizeof(integer1),
+			sizeof(logical1), sizeof(shortlogical),
+#ifdef Allow_TYQUAD
+			sizeof(longint),
+#endif
+			0};
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/uintseqhash.cc b/igraph/src/uintseqhash.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/uintseqhash.cc
@@ -0,0 +1,117 @@
+#include "uintseqhash.hh"
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+/*
+ * Random bits generated by
+ * http://www.fourmilab.ch/hotbits/
+ */
+static unsigned int rtab[256] = {
+  0xAEAA35B8, 0x65632E16, 0x155EDBA9, 0x01349B39,
+  0x8EB8BD97, 0x8E4C5367, 0x8EA78B35, 0x2B1B4072,
+  0xC1163893, 0x269A8642, 0xC79D7F6D, 0x6A32DEA0,
+  0xD4D2DA56, 0xD96D4F47, 0x47B5F48A, 0x2587C6BF,
+  0x642B71D8, 0x5DBBAF58, 0x5C178169, 0xA16D9279,
+  0x75CDA063, 0x291BC48B, 0x01AC2F47, 0x5416DF7C,
+  0x45307514, 0xB3E1317B, 0xE1C7A8DE, 0x3ACDAC96,
+  0x11B96831, 0x32DE22DD, 0x6A1DA93B, 0x58B62381,
+  0x283810E2, 0xBC30E6A6, 0x8EE51705, 0xB06E8DFB,
+  0x729AB12A, 0xA9634922, 0x1A6E8525, 0x49DD4E19,
+  0xE5DB3D44, 0x8C5B3A02, 0xEBDE2864, 0xA9146D9F,
+  0x736D2CB4, 0xF5229F42, 0x712BA846, 0x20631593,
+  0x89C02603, 0xD5A5BF6A, 0x823F4E18, 0x5BE5DEFF,
+  0x1C4EBBFA, 0x5FAB8490, 0x6E559B0C, 0x1FE528D6,
+  0xB3198066, 0x4A965EB5, 0xFE8BB3D5, 0x4D2F6234,
+  0x5F125AA4, 0xBCC640FA, 0x4F8BC191, 0xA447E537,
+  0xAC474D3C, 0x703BFA2C, 0x617DC0E7, 0xF26299D7,
+  0xC90FD835, 0x33B71C7B, 0x6D83E138, 0xCBB1BB14,
+  0x029CF5FF, 0x7CBD093D, 0x4C9825EF, 0x845C4D6D,
+  0x124349A5, 0x53942D21, 0x800E60DA, 0x2BA6EB7F,
+  0xCEBF30D3, 0xEB18D449, 0xE281F724, 0x58B1CB09,
+  0xD469A13D, 0x9C7495C3, 0xE53A7810, 0xA866C08E,
+  0x832A038B, 0xDDDCA484, 0xD5FE0DDE, 0x0756002B,
+  0x2FF51342, 0x60FEC9C8, 0x061A53E3, 0x47B1884E,
+  0xDC17E461, 0xA17A6A37, 0x3158E7E2, 0xA40D873B,
+  0x45AE2140, 0xC8F36149, 0x63A4EE2D, 0xD7107447,
+  0x6F90994F, 0x5006770F, 0xC1F3CA9A, 0x91B317B2,
+  0xF61B4406, 0xA8C9EE8F, 0xC6939B75, 0xB28BBC3B,
+  0x36BF4AEF, 0x3B12118D, 0x4D536ECF, 0x9CF4B46B,
+  0xE8AB1E03, 0x8225A360, 0x7AE4A130, 0xC4EE8B50,
+  0x50651797, 0x5BB4C59F, 0xD120EE47, 0x24F3A386,
+  0xBE579B45, 0x3A378EFC, 0xC5AB007B, 0x3668942B,
+  0x2DBDCC3A, 0x6F37F64C, 0xC24F862A, 0xB6F97FCF,
+  0x9E4FA23D, 0x551AE769, 0x46A8A5A6, 0xDC1BCFDD,
+  0x8F684CF9, 0x501D811B, 0x84279F80, 0x2614E0AC,
+  0x86445276, 0xAEA0CE71, 0x0812250F, 0xB586D18A,
+  0xC68D721B, 0x44514E1D, 0x37CDB99A, 0x24731F89,
+  0xFA72E589, 0x81E6EBA2, 0x15452965, 0x55523D9D,
+  0x2DC47E14, 0x2E7FA107, 0xA7790F23, 0x40EBFDBB,
+  0x77E7906B, 0x6C1DB960, 0x1A8B9898, 0x65FA0D90,
+  0xED28B4D8, 0x34C3ED75, 0x768FD2EC, 0xFAB60BCB,
+  0x962C75F4, 0x304F0498, 0x0A41A36B, 0xF7DE2A4A,
+  0xF4770FE2, 0x73C93BBB, 0xD21C82C5, 0x6C387447,
+  0x8CDB4CB9, 0x2CC243E8, 0x41859E3D, 0xB667B9CB,
+  0x89681E8A, 0x61A0526C, 0x883EDDDC, 0x539DE9A4,
+  0xC29E1DEC, 0x97C71EC5, 0x4A560A66, 0xBD7ECACF,
+  0x576AE998, 0x31CE5616, 0x97172A6C, 0x83D047C4,
+  0x274EA9A8, 0xEB31A9DA, 0x327209B5, 0x14D1F2CB,
+  0x00FE1D96, 0x817DBE08, 0xD3E55AED, 0xF2D30AFC,
+  0xFB072660, 0x866687D6, 0x92552EB9, 0xEA8219CD,
+  0xF7927269, 0xF1948483, 0x694C1DF5, 0xB7D8B7BF,
+  0xFFBC5D2F, 0x2E88B849, 0x883FD32B, 0xA0331192,
+  0x8CB244DF, 0x41FAF895, 0x16902220, 0x97FB512A,
+  0x2BEA3CC4, 0xAF9CAE61, 0x41ACD0D5, 0xFD2F28FF,
+  0xE780ADFA, 0xB3A3A76E, 0x7112AD87, 0x7C3D6058,
+  0x69E64FFF, 0xE5F8617C, 0x8580727C, 0x41F54F04,
+  0xD72BE498, 0x653D1795, 0x1275A327, 0x14B499D4,
+  0x4E34D553, 0x4687AA39, 0x68B64292, 0x5C18ABC3,
+  0x41EABFCC, 0x92A85616, 0x82684CF8, 0x5B9F8A4E,
+  0x35382FFE, 0xFB936318, 0x52C08E15, 0x80918B2E,
+  0x199EDEE0, 0xA9470163, 0xEC44ACDD, 0x612D6735,
+  0x8F88EA7D, 0x759F5EA4, 0xE5CC7240, 0x68CFEB8B,
+  0x04725601, 0x0C22C23E, 0x5BC97174, 0x89965841,
+  0x5D939479, 0x690F338A, 0x3C2D4380, 0xDAE97F2B
+};
+
+
+void UintSeqHash::update(unsigned int i)
+{
+  i++;
+  while(i > 0)
+    {
+      h ^= rtab[i & 0xff];
+#if 1
+      const unsigned int b = (h & 0x80000000) >> 31;
+      i = i >> 8;
+      h = (h << 1) | b;
+#else
+      const unsigned int b = h & 0x80000000;
+      h = h << 1;
+      if(b != 0)
+	h++;
+      i = i >> 8;
+#endif
+    }
+}
+
+
+} // namespace bliss
diff --git a/igraph/src/uio.c b/igraph/src/uio.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/uio.c
@@ -0,0 +1,75 @@
+#include "f2c.h"
+#include "fio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+uiolen f__reclen;
+
+ int
+#ifdef KR_headers
+do_us(number,ptr,len) ftnint *number; char *ptr; ftnlen len;
+#else
+do_us(ftnint *number, char *ptr, ftnlen len)
+#endif
+{
+	if(f__reading)
+	{
+		f__recpos += (int)(*number * len);
+		if(f__recpos>f__reclen)
+			err(f__elist->cierr, 110, "do_us");
+		if (fread(ptr,(int)len,(int)(*number),f__cf) != *number)
+			err(f__elist->ciend, EOF, "do_us");
+		return(0);
+	}
+	else
+	{
+		f__reclen += *number * len;
+		(void) fwrite(ptr,(int)len,(int)(*number),f__cf);
+		return(0);
+	}
+}
+#ifdef KR_headers
+integer do_ud(number,ptr,len) ftnint *number; char *ptr; ftnlen len;
+#else
+integer do_ud(ftnint *number, char *ptr, ftnlen len)
+#endif
+{
+	f__recpos += (int)(*number * len);
+	if(f__recpos > f__curunit->url && f__curunit->url!=1)
+		err(f__elist->cierr,110,"do_ud");
+	if(f__reading)
+	{
+#ifdef Pad_UDread
+#ifdef KR_headers
+	int i;
+#else
+	size_t i;
+#endif
+		if (!(i = fread(ptr,(int)len,(int)(*number),f__cf))
+		 && !(f__recpos - *number*len))
+			err(f__elist->cierr,EOF,"do_ud")
+		if (i < *number)
+			memset(ptr + i*len, 0, (*number - i)*len);
+		return 0;
+#else
+		if(fread(ptr,(int)len,(int)(*number),f__cf) != *number)
+			err(f__elist->cierr,EOF,"do_ud")
+		else return(0);
+#endif
+	}
+	(void) fwrite(ptr,(int)len,(int)(*number),f__cf);
+	return(0);
+}
+#ifdef KR_headers
+integer do_uio(number,ptr,len) ftnint *number; char *ptr; ftnlen len;
+#else
+integer do_uio(ftnint *number, char *ptr, ftnlen len)
+#endif
+{
+	if(f__sequential)
+		return(do_us(number,ptr,len));
+	else	return(do_ud(number,ptr,len));
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/uninit.c b/igraph/src/uninit.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/uninit.c
@@ -0,0 +1,463 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "arith.h"
+
+#define TYSHORT 2
+#define TYLONG 3
+#define TYREAL 4
+#define TYDREAL 5
+#define TYCOMPLEX 6
+#define TYDCOMPLEX 7
+#define TYINT1 11
+#define TYQUAD 14
+#ifndef Long
+#define Long long
+#endif
+
+#ifdef __mips
+#define RNAN	0xffc00000 /* Quiet NaN */
+#define DNAN0	0xfff80000 /* Signalling NaN double Big endian */
+#define DNAN1	0
+#endif
+
+#ifdef _PA_RISC1_1
+#define RNAN	0xffc00000 /* Quiet Nan -- big endian */
+#define DNAN0	0xfff80000
+#define DNAN1	0
+#endif
+
+#ifndef RNAN
+#define RNAN	0xff800001
+#ifdef IEEE_MC68k /* set on PPC*/
+#define DNAN0	0xfff00000 /* Quiet NaN big endian */
+#define DNAN1	1
+#else
+#define DNAN0	1   /* LSB, MSB for little endian machines */
+#define DNAN1	0xfff00000
+#endif
+#endif /*RNAN*/
+
+#ifdef KR_headers
+#define Void /*void*/
+#define FA7UL (unsigned Long) 0xfa7a7a7aL
+#else
+#define Void void
+#define FA7UL 0xfa7a7a7aUL
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static void ieee0(Void);
+
+static unsigned Long rnan = RNAN,
+	dnan0 = DNAN0,
+	dnan1 = DNAN1;
+
+double _0 = 0.;
+
+void unsupported_error()
+{
+  fprintf(stderr,"Runtime Error: Your Architecture is not supported by the"
+                       " -trapuv option of f2c\n");
+  exit(-1);
+}
+
+
+
+ void
+#ifdef KR_headers
+_uninit_f2c(x, type, len) void *x; int type; long len;
+#else
+_uninit_f2c(void *x, int type, long len)
+#endif
+{
+	static int first = 1;
+
+	unsigned Long *lx, *lxe;
+
+	if (first) {
+		first = 0;
+		ieee0();
+		}
+	if (len == 1)
+	 switch(type) {
+	  case TYINT1:
+		*(char*)x = 'Z';
+		return;
+	  case TYSHORT:
+		*(short*)x = 0xfa7a;
+		break;
+	  case TYLONG:
+		*(unsigned Long*)x = FA7UL;
+		return;
+	  case TYQUAD:
+	  case TYCOMPLEX:
+	  case TYDCOMPLEX:
+		break;
+	  case TYREAL:
+		*(unsigned Long*)x = rnan;
+		return;
+	  case TYDREAL:
+		lx = (unsigned Long*)x;
+		lx[0] = dnan0;
+		lx[1] = dnan1;
+		return;
+	  default:
+		printf("Surprise type %d in _uninit_f2c\n", type);
+	  }
+	switch(type) {
+	  case TYINT1:
+		memset(x, 'Z', len);
+		break;
+	  case TYSHORT:
+		*(short*)x = 0xfa7a;
+		break;
+	  case TYQUAD:
+		len *= 2;
+		/* no break */
+	  case TYLONG:
+		lx = (unsigned Long*)x;
+		lxe = lx + len;
+		while(lx < lxe)
+			*lx++ = FA7UL;
+		break;
+	  case TYCOMPLEX:
+		len *= 2;
+		/* no break */
+	  case TYREAL:
+		lx = (unsigned Long*)x;
+		lxe = lx + len;
+		while(lx < lxe)
+			*lx++ = rnan;
+		break;
+	  case TYDCOMPLEX:
+		len *= 2;
+		/* no break */
+	  case TYDREAL:
+		lx = (unsigned Long*)x;
+		for(lxe = lx + 2*len; lx < lxe; lx += 2) {
+			lx[0] = dnan0;
+			lx[1] = dnan1;
+			}
+	  }
+	}
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef MSpc
+#ifdef MSDOS
+#define MSpc
+#else
+#ifdef _WIN32
+#define MSpc
+#endif
+#endif
+#endif
+
+#ifdef MSpc
+#define IEEE0_done
+#include "float.h"
+#include "signal.h"
+
+ static void
+ieee0(Void)
+{
+#ifndef __alpha
+#ifndef EM_DENORMAL
+#define EM_DENORMAL _EM_DENORMAL
+#endif
+#ifndef EM_UNDERFLOW
+#define EM_UNDERFLOW _EM_UNDERFLOW
+#endif
+#ifndef EM_INEXACT
+#define EM_INEXACT _EM_INEXACT
+#endif
+#ifndef MCW_EM
+#define MCW_EM _MCW_EM
+#endif
+	_control87(EM_DENORMAL | EM_UNDERFLOW | EM_INEXACT, MCW_EM);
+#endif
+	/* With MS VC++, compiling and linking with -Zi will permit */
+	/* clicking to invoke the MS C++ debugger, which will show */
+	/* the point of error -- provided SIGFPE is SIG_DFL. */
+	signal(SIGFPE, SIG_DFL);
+	}
+#endif /* MSpc */
+
+/* What follows is for SGI IRIX only */
+#if defined(__mips) && defined(__sgi)   /* must link with -lfpe */
+#define IEEE0_done
+/* code from Eric Grosse */
+#include <stdlib.h>
+#include <stdio.h>
+#include "/usr/include/sigfpe.h"	/* full pathname for lcc -N */
+#include "/usr/include/sys/fpu.h"
+
+ static void
+#ifdef KR_headers
+ieeeuserhand(exception, val) unsigned exception[5]; int val[2];
+#else
+ieeeuserhand(unsigned exception[5], int val[2])
+#endif
+{
+	fflush(stdout);
+	fprintf(stderr,"ieee0() aborting because of ");
+	if(exception[0]==_OVERFL) fprintf(stderr,"overflow\n");
+	else if(exception[0]==_UNDERFL) fprintf(stderr,"underflow\n");
+	else if(exception[0]==_DIVZERO) fprintf(stderr,"divide by 0\n");
+	else if(exception[0]==_INVALID) fprintf(stderr,"invalid operation\n");
+	else fprintf(stderr,"\tunknown reason\n");
+	fflush(stderr);
+	abort();
+}
+
+ static void
+#ifdef KR_headers
+ieeeuserhand2(j) unsigned int **j;
+#else
+ieeeuserhand2(unsigned int **j)
+#endif
+{
+	fprintf(stderr,"ieee0() aborting because of confusion\n");
+	abort();
+}
+
+ static void
+ieee0(Void)
+{
+	int i;
+	for(i=1; i<=4; i++){
+		sigfpe_[i].count = 1000;
+		sigfpe_[i].trace = 1;
+		sigfpe_[i].repls = _USER_DETERMINED;
+		}
+	sigfpe_[1].repls = _ZERO;	/* underflow */
+	handle_sigfpes( _ON,
+		_EN_UNDERFL|_EN_OVERFL|_EN_DIVZERO|_EN_INVALID,
+		ieeeuserhand,_ABORT_ON_ERROR,ieeeuserhand2);
+	}
+#endif /* IRIX mips */
+
+/*
+ * The following is the preferred method but depends upon a GLIBC extension only
+ * to be found in GLIBC 2.2 or later.  It is a GNU extension, not included in the
+ * C99 extensions which allow the FP status register to be examined in a platform
+ * independent way.  It should be used if at all possible  -- AFRB
+ */
+
+
+#ifdef __GLIBC__
+#define IEEE0_done
+
+#if ((__GLIBC__>=2) && (__GLIBC_MINOR__>=2))
+#define _GNU_SOURCE 1
+#include <fenv.h>
+ static void
+  ieee0(Void)
+        
+{
+    /* Clear all exception flags */
+    if (fedisableexcept(FE_ALL_EXCEPT)==-1)
+         unsupported_error();
+    if (feenableexcept(FE_DIVBYZERO|FE_INVALID|FE_OVERFLOW)==-1)
+         unsupported_error();
+}
+
+/* Many linux cases will be treated through GLIBC.  Note that modern
+ * linux runs on many non-i86 plaforms and as a result the following code
+ * must be processor dependent rather than simply OS specific */
+
+#else /* __GLIBC__<2.2 */
+#include <fpu_control.h>
+
+
+#ifdef __alpha__
+#ifndef USE_setfpucw
+#define __setfpucw(x) __fpu_control = (x)
+#endif
+#endif
+
+/* Not all versions of libc define _FPU_SETCW;
+ *  * some only provide the __setfpucw() function.
+ *   */
+#ifndef _FPU_SETCW
+#define _FPU_SETCW(cw) __setfpucw(cw)
+#endif
+
+/* The exact set of flags we want to set in the FPU control word
+ * depends on the architecture.
+ * Note also that whether an exception is enabled or disabled when
+ * the _FPU_MASK_nn bit is set is architecture dependent!
+ * Enabled-when-set: M68k, ARM, MIPS, PowerPC
+ * Disabled-when-set: x86, Alpha
+ * The state we are after is:
+ * exceptions on division by zero, overflow and invalid operation.
+ */
+
+
+#ifdef __alpha__
+#ifndef USE_setfpucw
+#define __setfpucw(x) __fpu_control = (x)
+#endif
+#endif
+
+
+#ifndef _FPU_SETCW
+#undef  Can_use__setfpucw
+#define Can_use__setfpucw
+#endif
+
+#undef RQD_FPU_MASK
+#undef RQD_FPU_CLEAR_MASK
+
+#if (defined(__mc68000__) || defined(__mc68020__) || defined(mc68020) || defined (__mc68k__))
+/* Reported 20010705 by Alan Bain <alanb@chiark.greenend.org.uk> */
+/* Note that IEEE 754 IOP (illegal operation) */
+/* = Signaling NAN (SNAN) + operation error (OPERR). */
+#define RQD_FPU_STATE (_FPU_IEEE + _FPU_DOUBLE + _FPU_MASK_OPERR + \
+                 _FPU_MASK_DZ + _FPU_MASK_SNAN+_FPU_MASK_OVFL)
+#define RQD_FPU_MASK (_FPU_MASK_OPERR+_FPU_MASK_DZ+_FPU_MASK_SNAN+_FPU_MASK_OVFL)
+
+#elif (defined(__powerpc__)||defined(_ARCH_PPC)||defined(_ARCH_PWR)) /* !__mc68k__ */
+    /* The following is NOT a mistake -- the author of the fpu_control.h
+     * for the PPC has erroneously defined IEEE mode to turn on exceptions
+     * other than Inexact! Start from default then and turn on only the ones
+     * which we want*/
+
+    /* I have changed _FPU_MASK_UM here to _FPU_MASK_ZM, because that is
+     * in line with all the other architectures specified here. -- AFRB
+     */
+#define RQD_FPU_STATE (_FPU_DEFAULT +_FPU_MASK_OM+_FPU_MASK_IM+_FPU_MASK_ZM)
+#define RQD_FPU_MASK (_FPU_MASK_OM+_FPU_MASK_IM+_FPU_MASK_ZM)
+
+#elif (defined(__arm__))
+    /* On ARM too, IEEE implies all exceptions enabled.
+     * -- Peter Maydell <pmaydell@chiark.greenend.org.uk>
+     * Unfortunately some version of ARMlinux don't include any
+     * flags in the fpu_control.h file
+     */
+#define RQD_FPU_STATE (_FPU_DEFAULT +_FPU_MASK_OM+_FPU_MASK_IM+_FPU_MASK_ZM)
+#define RQD_FPU_MASK (_FPU_MASK_OM+_FPU_MASK_IM+_FPU_MASK_ZM)
+
+#elif (defined(__mips__))
+    /* And same again for MIPS; _FPU_IEEE => exceptions seems a common meme.
+     *  * MIPS uses different MASK constant names, no idea why -- PMM
+     *   */
+#define RQD_FPU_STATE (_FPU_DEFAULT +_FPU_MASK_O+_FPU_MASK_V+_FPU_MASK_Z)
+#define RQD_FPU_MASK (_FPU_MASK_O+_FPU_MASK_V+_FPU_MASK_Z)
+
+#elif (defined(__sparc__))
+#define RQD_FPU_STATE (_FPU_DEFAULT +_FPU_DOUBLE+_FPU_MASK_OM+_FPU_MASK_IM+_FPU_MASK_ZM)
+#define RQD_FPU_MASK (_FPU_MASK_OM+_FPU_MASK_IM+_FPU_MASK_ZM)
+
+#elif (defined(__i386__) || defined(__alpha__))
+    /* This case is for Intel, and also Alpha, because the Alpha header 
+     * purposely emulates x86 flags and meanings for compatibility with
+     * stupid programs.
+     * We used to try this case for anything defining _FPU_IEEE, but I think
+     * that that's a bad idea because it isn't really likely to work.
+     * Instead for unknown architectures we just won't allow -trapuv to work.
+     * Trying this case was just getting us 
+     *  (a) compile errors on archs which didn't know all these constants
+     *  (b) silent wrong behaviour on archs (like SPARC) which do know all
+     *      constants but have different semantics for them
+     */
+#define RQD_FPU_STATE (_FPU_IEEE - _FPU_EXTENDED + _FPU_DOUBLE - _FPU_MASK_IM - _FPU_MASK_ZM - _FPU_MASK_OM)
+#define RQD_FPU_CLEAR_MASK (_FPU_MASK_IM + _FPU_MASK_ZM + _FPU_MASK_OM)
+#endif
+
+static void ieee0(Void)
+{
+#ifdef RQD_FPU_STATE
+        
+#ifndef UNINIT_F2C_PRECISION_53 /* 20051004 */
+        __fpu_control = RQD_FPU_STATE;
+        _FPU_SETCW(__fpu_control);
+#else 
+	/* unmask invalid, etc., and keep current rounding precision */
+	fpu_control_t cw;
+	_FPU_GETCW(cw);
+#ifdef RQD_FPU_CLEAR_MASK
+	cw &= ~ RQD_FPU_CLEAR_MASK;
+#else
+        cw |= RQD_FPU_MASK;
+#endif
+	_FPU_SETCW(cw);
+#endif
+
+#else /* !_FPU_IEEE */
+
+	fprintf(stderr, "\n%s\n%s\n%s\n%s\n",
+		"WARNING:  _uninit_f2c in libf2c does not know how",
+		"to enable trapping on this system, so f2c's -trapuv",
+		"option will not detect uninitialized variables unless",
+		"you can enable trapping manually.");
+	fflush(stderr);
+
+#endif /* _FPU_IEEE */
+	}
+#endif /* __GLIBC__>2.2 */
+#endif /* __GLIBC__ */
+
+/* Specific to OSF/1 */
+#if (defined(__alpha)&&defined(__osf__))
+#ifndef IEEE0_done
+#define IEEE0_done
+#include <machine/fpu.h>
+ static void
+ieee0(Void)
+{
+	ieee_set_fp_control(IEEE_TRAP_ENABLE_INV);
+	}
+#endif /*IEEE0_done*/
+#endif /*__alpha OSF/1*/
+
+#ifdef __hpux
+#define IEEE0_done
+#define _INCLUDE_HPUX_SOURCE
+#include <math.h>
+
+#ifndef FP_X_INV
+#include <fenv.h>
+#define fpsetmask fesettrapenable
+#define FP_X_INV FE_INVALID
+#endif
+
+ static void
+ieee0(Void)
+{
+	fpsetmask(FP_X_INV);
+	}
+#endif /*__hpux*/
+
+#ifdef _AIX
+#define IEEE0_done
+#include <fptrap.h>
+
+ static void
+ieee0(Void)
+{
+	fp_enable(TRP_INVALID);
+	fp_trap(FP_TRAP_SYNC);
+	}
+#endif /*_AIX*/
+
+#ifdef __sun
+#define IEEE0_done
+#include <ieeefp.h>
+
+ static void
+ieee0(Void)
+{
+	fpsetmask(FP_X_INV);
+	}
+#endif /*__sparc*/
+
+#ifndef IEEE0_done
+ static void
+ieee0(Void) {}
+#endif
diff --git a/igraph/src/util.c b/igraph/src/util.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/util.c
@@ -0,0 +1,57 @@
+#include "sysdep1.h"	/* here to get stat64 on some badly designed Linux systems */
+#include "f2c.h"
+#include "fio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ VOID
+#ifdef KR_headers
+#define Const /*nothing*/
+g_char(a,alen,b) char *a,*b; ftnlen alen;
+#else
+#define Const const
+g_char(const char *a, ftnlen alen, char *b)
+#endif
+{
+	Const char *x = a + alen;
+	char *y = b + alen;
+
+	for(;; y--) {
+		if (x <= a) {
+			*b = 0;
+			return;
+			}
+		if (*--x != ' ')
+			break;
+		}
+	*y-- = 0;
+	do *y-- = *x;
+		while(x-- > a);
+	}
+
+ VOID
+#ifdef KR_headers
+b_char(a,b,blen) char *a,*b; ftnlen blen;
+#else
+b_char(const char *a, char *b, ftnlen blen)
+#endif
+{	int i;
+	for(i=0;i<blen && *a!=0;i++) *b++= *a++;
+	for(;i<blen;i++) *b++=' ';
+}
+#ifndef NON_UNIX_STDIO
+#ifdef KR_headers
+long f__inode(a, dev) char *a; int *dev;
+#else
+long f__inode(char *a, int *dev)
+#endif
+{	struct STAT_ST x;
+	if(STAT(a,&x)<0) return(-1);
+	*dev = x.st_dev;
+	return(x.st_ino);
+}
+#endif
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/utils.cc b/igraph/src/utils.cc
new file mode 100644
--- /dev/null
+++ b/igraph/src/utils.cc
@@ -0,0 +1,122 @@
+#include <cassert>
+#include <vector>
+#include "utils.hh"
+
+/*
+  Copyright (c) 2003-2015 Tommi Junttila
+  Released under the GNU Lesser General Public License version 3.
+  
+  This file is part of bliss.
+  
+  bliss is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  bliss is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with bliss.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace bliss {
+
+void
+print_permutation(FILE* const fp,
+		  const unsigned int N,
+		  const unsigned int* perm,
+		  const unsigned int offset)
+{
+  assert(N > 0);
+  assert(perm);
+  for(unsigned int i = 0; i < N; i++) {
+    unsigned int j = perm[i];
+    if(j == i)
+      continue;
+    bool is_first = true;
+    while(j != i) {
+      if(j < i) {
+        is_first = false;
+        break;
+      }
+      j = perm[j];
+    }
+    if(!is_first)
+      continue;
+    fprintf(fp, "(%u,", i+offset);
+    j = perm[i];
+    while(j != i) {
+      fprintf(fp, "%u", j+offset);
+      j = perm[j];
+      if(j != i)
+        fprintf(fp, ",");
+    }
+    fprintf(fp, ")");
+  }
+}
+
+void
+print_permutation(FILE* const fp,
+		  const std::vector<unsigned int>& perm,
+		  const unsigned int offset)
+{
+  const unsigned int N = perm.size();
+  for(unsigned int i = 0; i < N; i++) {
+    unsigned int j = perm[i];
+    if(j == i)
+      continue;
+    bool is_first = true;
+    while(j != i) {
+      if(j < i) {
+        is_first = false;
+        break;
+      }
+      j = perm[j];
+    }
+    if(!is_first)
+      continue;
+    fprintf(fp, "(%u,", i+offset);
+    j = perm[i];
+    while(j != i) {
+      fprintf(fp, "%u", j+offset);
+      j = perm[j];
+      if(j != i)
+        fprintf(fp, ",");
+    }
+    fprintf(fp, ")");
+  }
+}
+
+bool
+is_permutation(const unsigned int N, const unsigned int* perm)
+{
+  if(N == 0)
+    return true;
+  std::vector<bool> m(N, false);
+  for(unsigned int i = 0; i < N; i++) {
+    if(perm[i] >= N) return false;
+    if(m[perm[i]]) return false;
+    m[perm[i]] = true;
+  }
+  return true;
+}
+
+bool
+is_permutation(const std::vector<unsigned int>& perm)
+{
+  const unsigned int N = perm.size();
+  if(N == 0)
+    return true;
+  std::vector<bool> m(N, false);
+  for(unsigned int i = 0; i < N; i++) {
+    if(perm[i] >= N) return false;
+    if(m[perm[i]]) return false;
+    m[perm[i]] = true;
+  }
+  return true;
+}
+
+
+} // namespace bliss
diff --git a/igraph/src/vector.c b/igraph/src/vector.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/vector.c
@@ -0,0 +1,466 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_types_internal.h"
+#include "igraph_complex.h"
+#include "bigint.h"
+#include "config.h"
+#include <float.h>
+
+#define BASE_IGRAPH_REAL
+#include "igraph_pmt.h"
+#include "vector.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_IGRAPH_REAL
+
+#define BASE_FLOAT
+#include "igraph_pmt.h"
+#include "vector.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_FLOAT
+
+#define BASE_LONG
+#include "igraph_pmt.h"
+#include "vector.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_LONG
+
+#define BASE_CHAR
+#include "igraph_pmt.h"
+#include "vector.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_CHAR
+
+#define BASE_BOOL
+#include "igraph_pmt.h"
+#include "vector.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_BOOL
+
+#define BASE_INT
+#include "igraph_pmt.h"
+#include "vector.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_INT
+
+#define BASE_COMPLEX
+#include "igraph_pmt.h"
+#include "vector.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_COMPLEX
+
+#define BASE_LIMB
+#include "igraph_pmt.h"
+#include "vector.pmt"
+#include "igraph_pmt_off.h"
+#undef BASE_LIMB
+
+#include "igraph_math.h"
+
+int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to) {
+    long int i, n = igraph_vector_size(from);
+
+    IGRAPH_CHECK(igraph_vector_long_resize(to, n));
+    for (i = 0; i < n; i++) {
+        VECTOR(*to)[i] = (long int) floor(VECTOR(*from)[i]);
+    }
+    return 0;
+}
+
+int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to) {
+    long int i, n = igraph_vector_size(from);
+
+    IGRAPH_CHECK(igraph_vector_long_resize(to, n));
+    for (i = 0; i < n; i++) {
+        VECTOR(*to)[i] = (long int) round(VECTOR(*from)[i]);
+    }
+    return 0;
+}
+
+int igraph_vector_order2(igraph_vector_t *v) {
+
+    igraph_indheap_t heap;
+
+    igraph_indheap_init_array(&heap, VECTOR(*v), igraph_vector_size(v));
+    IGRAPH_FINALLY(igraph_indheap_destroy, &heap);
+
+    igraph_vector_clear(v);
+    while (!igraph_indheap_empty(&heap)) {
+        IGRAPH_CHECK(igraph_vector_push_back(v, igraph_indheap_max_index(&heap) - 1));
+        igraph_indheap_delete_max(&heap);
+    }
+
+    igraph_indheap_destroy(&heap);
+    IGRAPH_FINALLY_CLEAN(1);
+    return 0;
+}
+
+/**
+ * \ingroup vector
+ * \function igraph_vector_order
+ * \brief Calculate the order of the elements in a vector.
+ *
+ * </para><para>
+ * The smallest element will have order zero, the second smallest
+ * order one, etc.
+ * \param v The original \type igraph_vector_t object.
+ * \param v2 A secondary key, another \type igraph_vector_t object.
+ * \param res An initialized \type igraph_vector_t object, it will be
+ *    resized to match the size of \p v. The
+ *    result of the computation will be stored here.
+ * \param nodes Hint, the largest element in \p v.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM: out of memory
+ *
+ * Time complexity: O()
+ */
+
+int igraph_vector_order(const igraph_vector_t* v,
+                        const igraph_vector_t *v2,
+                        igraph_vector_t* res, igraph_real_t nodes) {
+    long int edges = igraph_vector_size(v);
+    igraph_vector_t ptr;
+    igraph_vector_t rad;
+    long int i, j;
+
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&rad, edges);
+    IGRAPH_CHECK(igraph_vector_resize(res, edges));
+
+    for (i = 0; i < edges; i++) {
+        long int radix = (long int) v2->stor_begin[i];
+        if (VECTOR(ptr)[radix] != 0) {
+            VECTOR(rad)[i] = VECTOR(ptr)[radix];
+        }
+        VECTOR(ptr)[radix] = i + 1;
+    }
+
+    j = 0;
+    for (i = 0; i < nodes + 1; i++) {
+        if (VECTOR(ptr)[i] != 0) {
+            long int next = (long int) VECTOR(ptr)[i] - 1;
+            res->stor_begin[j++] = next;
+            while (VECTOR(rad)[next] != 0) {
+                next = (long int) VECTOR(rad)[next] - 1;
+                res->stor_begin[j++] = next;
+            }
+        }
+    }
+
+    igraph_vector_null(&ptr);
+    igraph_vector_null(&rad);
+
+    for (i = 0; i < edges; i++) {
+        long int edge = (long int) VECTOR(*res)[edges - i - 1];
+        long int radix = (long int) VECTOR(*v)[edge];
+        if (VECTOR(ptr)[radix] != 0) {
+            VECTOR(rad)[edge] = VECTOR(ptr)[radix];
+        }
+        VECTOR(ptr)[radix] = edge + 1;
+    }
+
+    j = 0;
+    for (i = 0; i < nodes + 1; i++) {
+        if (VECTOR(ptr)[i] != 0) {
+            long int next = (long int) VECTOR(ptr)[i] - 1;
+            res->stor_begin[j++] = next;
+            while (VECTOR(rad)[next] != 0) {
+                next = (long int) VECTOR(rad)[next] - 1;
+                res->stor_begin[j++] = next;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&ptr);
+    igraph_vector_destroy(&rad);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_vector_order1(const igraph_vector_t* v,
+                         igraph_vector_t* res, igraph_real_t nodes) {
+    long int edges = igraph_vector_size(v);
+    igraph_vector_t ptr;
+    igraph_vector_t rad;
+    long int i, j;
+
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&rad, edges);
+    IGRAPH_CHECK(igraph_vector_resize(res, edges));
+
+    for (i = 0; i < edges; i++) {
+        long int radix = (long int) v->stor_begin[i];
+        if (VECTOR(ptr)[radix] != 0) {
+            VECTOR(rad)[i] = VECTOR(ptr)[radix];
+        }
+        VECTOR(ptr)[radix] = i + 1;
+    }
+
+    j = 0;
+    for (i = 0; i < nodes + 1; i++) {
+        if (VECTOR(ptr)[i] != 0) {
+            long int next = (long int) VECTOR(ptr)[i] - 1;
+            res->stor_begin[j++] = next;
+            while (VECTOR(rad)[next] != 0) {
+                next = (long int) VECTOR(rad)[next] - 1;
+                res->stor_begin[j++] = next;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&ptr);
+    igraph_vector_destroy(&rad);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_vector_order1_int(const igraph_vector_t* v,
+                             igraph_vector_int_t* res,
+                             igraph_real_t nodes) {
+    long int edges = igraph_vector_size(v);
+    igraph_vector_t ptr;
+    igraph_vector_t rad;
+    long int i, j;
+
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+
+    IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1);
+    IGRAPH_VECTOR_INIT_FINALLY(&rad, edges);
+    IGRAPH_CHECK(igraph_vector_int_resize(res, edges));
+
+    for (i = 0; i < edges; i++) {
+        long int radix = (long int) v->stor_begin[i];
+        if (VECTOR(ptr)[radix] != 0) {
+            VECTOR(rad)[i] = VECTOR(ptr)[radix];
+        }
+        VECTOR(ptr)[radix] = i + 1;
+    }
+
+    j = 0;
+    for (i = 0; i < nodes + 1; i++) {
+        if (VECTOR(ptr)[i] != 0) {
+            long int next = (long int) VECTOR(ptr)[i] - 1;
+            res->stor_begin[j++] = next;
+            while (VECTOR(rad)[next] != 0) {
+                next = (long int) VECTOR(rad)[next] - 1;
+                res->stor_begin[j++] = next;
+            }
+        }
+    }
+
+    igraph_vector_destroy(&ptr);
+    igraph_vector_destroy(&rad);
+    IGRAPH_FINALLY_CLEAN(2);
+
+    return 0;
+}
+
+int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res,
+                       long int nodes) {
+
+    igraph_vector_t rad;
+    igraph_vector_t ptr;
+    long int edges = igraph_vector_size(v);
+    long int i, c = 0;
+
+    IGRAPH_VECTOR_INIT_FINALLY(&rad, nodes);
+    IGRAPH_VECTOR_INIT_FINALLY(&ptr, edges);
+    IGRAPH_CHECK(igraph_vector_resize(res, edges));
+
+    for (i = 0; i < edges; i++) {
+        long int elem = (long int) VECTOR(*v)[i];
+        VECTOR(ptr)[i] = VECTOR(rad)[elem];
+        VECTOR(rad)[elem] = i + 1;
+    }
+
+    for (i = 0; i < nodes; i++) {
+        long int p = (long int) VECTOR(rad)[i];
+        while (p != 0) {
+            VECTOR(*res)[p - 1] = c++;
+            p = (long int) VECTOR(ptr)[p - 1];
+        }
+    }
+
+    igraph_vector_destroy(&ptr);
+    igraph_vector_destroy(&rad);
+    IGRAPH_FINALLY_CLEAN(2);
+    return 0;
+}
+
+#ifndef USING_R
+int igraph_vector_complex_print(const igraph_vector_complex_t *v) {
+    long int i, n = igraph_vector_complex_size(v);
+    if (n != 0) {
+        igraph_complex_t z = VECTOR(*v)[0];
+        printf("%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z));
+    }
+    for (i = 1; i < n; i++) {
+        igraph_complex_t z = VECTOR(*v)[i];
+        printf(" %g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z));
+    }
+    printf("\n");
+    return 0;
+}
+#endif
+
+int igraph_vector_complex_fprint(const igraph_vector_complex_t *v,
+                                 FILE *file) {
+    long int i, n = igraph_vector_complex_size(v);
+    if (n != 0) {
+        igraph_complex_t z = VECTOR(*v)[0];
+        fprintf(file, "%g%+g", IGRAPH_REAL(z), IGRAPH_IMAG(z));
+    }
+    for (i = 1; i < n; i++) {
+        igraph_complex_t z = VECTOR(*v)[i];
+        fprintf(file, " %g%+g", IGRAPH_REAL(z), IGRAPH_IMAG(z));
+    }
+    fprintf(file, "\n");
+    return 0;
+}
+
+int igraph_vector_complex_real(const igraph_vector_complex_t *v,
+                               igraph_vector_t *real) {
+    int i, n = (int) igraph_vector_complex_size(v);
+    IGRAPH_CHECK(igraph_vector_resize(real, n));
+    for (i = 0; i < n; i++) {
+        VECTOR(*real)[i] = IGRAPH_REAL(VECTOR(*v)[i]);
+    }
+
+    return 0;
+}
+
+int igraph_vector_complex_imag(const igraph_vector_complex_t *v,
+                               igraph_vector_t *imag) {
+    int i, n = (int) igraph_vector_complex_size(v);
+    IGRAPH_CHECK(igraph_vector_resize(imag, n));
+    for (i = 0; i < n; i++) {
+        VECTOR(*imag)[i] = IGRAPH_IMAG(VECTOR(*v)[i]);
+    }
+
+    return 0;
+}
+
+int igraph_vector_complex_realimag(const igraph_vector_complex_t *v,
+                                   igraph_vector_t *real,
+                                   igraph_vector_t *imag) {
+    int i, n = (int) igraph_vector_complex_size(v);
+    IGRAPH_CHECK(igraph_vector_resize(real, n));
+    IGRAPH_CHECK(igraph_vector_resize(imag, n));
+    for (i = 0; i < n; i++) {
+        igraph_complex_t z = VECTOR(*v)[i];
+        VECTOR(*real)[i] = IGRAPH_REAL(z);
+        VECTOR(*imag)[i] = IGRAPH_IMAG(z);
+    }
+
+    return 0;
+}
+
+int igraph_vector_complex_create(igraph_vector_complex_t *v,
+                                 const igraph_vector_t *real,
+                                 const igraph_vector_t *imag) {
+    int i, n = (int) igraph_vector_size(real);
+    if (n != igraph_vector_size(imag)) {
+        IGRAPH_ERROR("Real and imag vector sizes don't match", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_complex_init(v, n));
+    /* FINALLY not needed */
+
+    for (i = 0; i < n; i++) {
+        VECTOR(*v)[i] = igraph_complex(VECTOR(*real)[i], VECTOR(*imag)[i]);
+    }
+
+    return 0;
+}
+
+int igraph_vector_complex_create_polar(igraph_vector_complex_t *v,
+                                       const igraph_vector_t *r,
+                                       const igraph_vector_t *theta) {
+    int i, n = (int) igraph_vector_size(r);
+    if (n != igraph_vector_size(theta)) {
+        IGRAPH_ERROR("'r' and 'theta' vector sizes don't match", IGRAPH_EINVAL);
+    }
+
+    IGRAPH_CHECK(igraph_vector_complex_init(v, n));
+    /* FINALLY not needed */
+
+    for (i = 0; i < n; i++) {
+        VECTOR(*v)[i] = igraph_complex_polar(VECTOR(*r)[i], VECTOR(*theta)[i]);
+    }
+
+    return 0;
+}
+
+igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs,
+                                  const igraph_vector_t *rhs,
+                                  igraph_real_t tol) {
+    long int i, s;
+    assert(lhs != 0);
+    assert(rhs != 0);
+    assert(lhs->stor_begin != 0);
+    assert(rhs->stor_begin != 0);
+
+    s = igraph_vector_size(lhs);
+    if (s != igraph_vector_size(rhs)) {
+        return 0;
+    } else {
+        if (tol == 0) {
+            tol = DBL_EPSILON;
+        }
+        for (i = 0; i < s; i++) {
+            igraph_real_t l = VECTOR(*lhs)[i];
+            igraph_real_t r = VECTOR(*rhs)[i];
+            if (l < r - tol || l > r + tol) {
+                return 0;
+            }
+        }
+        return 1;
+    }
+}
+
+int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) {
+    int i, n = igraph_vector_size(v);
+    if (tol < 0.0) {
+        IGRAPH_ERROR("`tol' tolerance must be non-negative", IGRAPH_EINVAL);
+    }
+    if (tol == 0.0) {
+        tol = sqrt(DBL_EPSILON);
+    }
+    for (i = 0; i < n; i++) {
+        igraph_real_t val = VECTOR(*v)[i];
+        if (val < tol && val > -tol) {
+            VECTOR(*v)[i] = 0.0;
+        }
+    }
+    return 0;
+}
diff --git a/igraph/src/vector_ptr.c b/igraph/src/vector_ptr.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/vector_ptr.c
@@ -0,0 +1,628 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_types.h"
+#include "igraph_vector_ptr.h"
+#include "igraph_memory.h"
+#include "igraph_random.h"
+#include "igraph_error.h"
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>         /* memcpy & co. */
+#include <stdlib.h>
+
+/**
+ * \section about_igraph_vector_ptr_objects Pointer vectors
+ * (<type>igraph_vector_ptr_t</type>)
+ *
+ * <para>The \type igraph_vector_ptr_t data type is very similar to
+ * the \type igraph_vector_t type, but it stores generic pointers instead of
+ * real numbers.</para>
+ *
+ * <para>This type has the same space complexity as \type
+ * igraph_vector_t, and most implemented operations work the same way
+ * as for \type igraph_vector_t. </para>
+ *
+ * <para>This type is mostly used to pass to or receive from a set of
+ * graphs to some \a igraph functions, such as \ref
+ * igraph_decompose(), which decomposes a graph to connected
+ * components.</para>
+ *
+ * <para>The same \ref VECTOR macro used for ordinary vectors can be
+ * used for pointer vectors as well, please note that a typeless
+ * generic pointer will be provided by this macro and you may need to
+ * cast it to a specific pointer before starting to work with it.</para>
+ *
+ * <para>Pointer vectors may have an associated item destructor function
+ * which takes a pointer and returns nothing. The item destructor will
+ * be called on each item in the pointer vector when it is destroyed by
+ * \ref igraph_vector_ptr_destroy() or \ref igraph_vector_ptr_destroy_all(),
+ * or when its elements are freed by \ref igraph_vector_ptr_free_all().
+ * Note that the semantics of an item destructor does not coincide with
+ * C++ destructors; for instance, when a pointer vector is resized to a
+ * smaller size, the extra items will \em not be destroyed automatically!
+ * Nevertheless, item destructors may become handy in many cases; for
+ * instance, a vector of graphs generated by \ref igraph_decompose() can
+ * be destroyed with a single call to \ref igraph_vector_ptr_destroy_all()
+ * if the item destructor is set to \ref igraph_destroy().</para>
+ */
+
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_init
+ * \brief Initialize a pointer vector (constructor).
+ *
+ * </para><para>
+ * This is the constructor of the pointer vector data type. All
+ * pointer vectors constructed this way should be destroyed via
+ * calling \ref igraph_vector_ptr_destroy().
+ * \param v Pointer to an uninitialized
+ *        <type>igraph_vector_ptr_t</type> object, to be created.
+ * \param size Integer, the size of the pointer vector.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM if out of memory
+ *
+ * Time complexity: operating system dependent, the amount of \quote
+ * time \endquote required to allocate \p size elements.
+ */
+
+int igraph_vector_ptr_init      (igraph_vector_ptr_t* v, int long size) {
+    long int alloc_size = size > 0 ? size : 1;
+    assert(v != NULL);
+    if (size < 0) {
+        size = 0;
+    }
+    v->stor_begin = igraph_Calloc(alloc_size, void*);
+    if (v->stor_begin == 0) {
+        IGRAPH_ERROR("vector ptr init failed", IGRAPH_ENOMEM);
+    }
+    v->stor_end = v->stor_begin + alloc_size;
+    v->end = v->stor_begin + size;
+    v->item_destructor = 0;
+
+    return 0;
+}
+
+/**
+ */
+
+const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v, void *const *data,
+        long int length) {
+    igraph_vector_ptr_t *v2 = (igraph_vector_ptr_t*) v;
+    v2->stor_begin = (void **)data;
+    v2->stor_end = (void**)data + length;
+    v2->end = v2->stor_end;
+    v2->item_destructor = 0;
+    return v;
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_destroy
+ * \brief Destroys a pointer vector.
+ *
+ * </para><para>
+ * The destructor for pointer vectors.
+ * \param v Pointer to the pointer vector to destroy.
+ *
+ * Time complexity: operating system dependent, the \quote time
+ * \endquote required to deallocate O(n) bytes, n is the number of
+ * elements allocated for the pointer vector (not necessarily the
+ * number of elements in the vector).
+ */
+
+void igraph_vector_ptr_destroy   (igraph_vector_ptr_t* v) {
+    assert(v != 0);
+    if (v->stor_begin != 0) {
+        igraph_Free(v->stor_begin);
+        v->stor_begin = NULL;
+    }
+}
+
+void igraph_i_vector_ptr_call_item_destructor_all(igraph_vector_ptr_t* v) {
+    void **ptr;
+
+    if (v->item_destructor != 0) {
+        for (ptr = v->stor_begin; ptr < v->end; ptr++) {
+            if (*ptr != 0) {
+                v->item_destructor(*ptr);
+            }
+        }
+    }
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_free_all
+ * \brief Frees all the elements of a pointer vector.
+ *
+ * If an item destructor is set for this pointer vector, this function will
+ * first call the destructor on all elements of the vector and then
+ * free all the elements using free(). If an item destructor is not set,
+ * the elements will simply be freed.
+ *
+ * \param v Pointer to the pointer vector whose elements will be freed.
+ *
+ * Time complexity: operating system dependent, the \quote time
+ * \endquote required to call the destructor n times and then
+ * deallocate O(n) pointers, each pointing to a memory area of
+ * arbitrary size. n is the number of elements in the pointer vector.
+ */
+
+void igraph_vector_ptr_free_all   (igraph_vector_ptr_t* v) {
+    void **ptr;
+    assert(v != 0);
+    assert(v->stor_begin != 0);
+
+    igraph_i_vector_ptr_call_item_destructor_all(v);
+    for (ptr = v->stor_begin; ptr < v->end; ptr++) {
+        igraph_Free(*ptr);
+    }
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_destroy_all
+ * \brief Frees all the elements and destroys the pointer vector.
+ *
+ * This function is equivalent to \ref igraph_vector_ptr_free_all()
+ * followed by \ref igraph_vector_ptr_destroy().
+ *
+ * \param v Pointer to the pointer vector to destroy.
+ *
+ * Time complexity: operating system dependent, the \quote time
+ * \endquote required to deallocate O(n) pointers, each pointing to
+ * a memory area of arbitrary size, plus the \quote time \endquote
+ * required to deallocate O(n) bytes, n being the number of elements
+ * allocated for the pointer vector (not necessarily the number of
+ * elements in the vector).
+ */
+
+void igraph_vector_ptr_destroy_all   (igraph_vector_ptr_t* v) {
+    assert(v != 0);
+    assert(v->stor_begin != 0);
+    igraph_vector_ptr_free_all(v);
+    igraph_vector_ptr_set_item_destructor(v, 0);
+    igraph_vector_ptr_destroy(v);
+}
+
+/**
+ * \ingroup vectorptr
+ * \brief Reserves memory for a pointer vector for later use.
+ *
+ * @return Error code:
+ *         - <b>IGRAPH_ENOMEM</b>: out of memory
+ */
+
+int igraph_vector_ptr_reserve   (igraph_vector_ptr_t* v, long int size) {
+    long int actual_size = igraph_vector_ptr_size(v);
+    void **tmp;
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+
+    if (size <= igraph_vector_ptr_size(v)) {
+        return 0;
+    }
+
+    tmp = igraph_Realloc(v->stor_begin, (size_t) size, void*);
+    if (tmp == 0) {
+        IGRAPH_ERROR("vector ptr reserve failed", IGRAPH_ENOMEM);
+    }
+    v->stor_begin = tmp;
+    v->stor_end = v->stor_begin + size;
+    v->end = v->stor_begin + actual_size;
+
+    return 0;
+}
+
+/**
+ * \ingroup vectorptr
+ * \brief Decides whether the pointer vector is empty.
+ */
+
+igraph_bool_t igraph_vector_ptr_empty     (const igraph_vector_ptr_t* v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    return v->stor_begin == v->end;
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_size
+ * \brief Gives the number of elements in the pointer vector.
+ *
+ * \param v The pointer vector object.
+ * \return The size of the object, ie. the number of pointers stored.
+ *
+ * Time complexity: O(1).
+ */
+
+long int igraph_vector_ptr_size      (const igraph_vector_ptr_t* v) {
+    assert(v != NULL);
+    /*  assert(v->stor_begin != NULL);       */ /* TODO */
+    return v->end - v->stor_begin;
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_clear
+ * \brief Removes all elements from a pointer vector.
+ *
+ * </para><para>
+ * This function resizes a pointer to vector to zero length. Note that
+ * the pointed objects are \em not deallocated, you should call
+ * free() on them, or make sure that their allocated memory is freed
+ * in some other way, you'll get memory leaks otherwise. If you have
+ * set up an item destructor earlier, the destructor will be called
+ * on every element.
+ *
+ * </para><para>
+ * Note that the current implementation of this function does
+ * \em not deallocate the memory required for storing the
+ * pointers, so making a pointer vector smaller this way does not give
+ * back any memory. This behavior might change in the future.
+ * \param v The pointer vector to clear.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_vector_ptr_clear     (igraph_vector_ptr_t* v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    igraph_i_vector_ptr_call_item_destructor_all(v);
+    v->end = v->stor_begin;
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_push_back
+ * \brief Appends an element to the back of a pointer vector.
+ *
+ * \param v The pointer vector.
+ * \param e The new element to include in the pointer vector.
+ * \return Error code.
+ * \sa igraph_vector_push_back() for the corresponding operation of
+ * the ordinary vector type.
+ *
+ * Time complexity: O(1) or O(n), n is the number of elements in the
+ * vector. The pointer vector implementation ensures that n subsequent
+ * push_back operations need O(n) time to complete.
+ */
+
+int igraph_vector_ptr_push_back (igraph_vector_ptr_t* v, void* e) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+
+    /* full, allocate more storage */
+    if (v->stor_end == v->end) {
+        long int new_size = igraph_vector_ptr_size(v) * 2;
+        if (new_size == 0) {
+            new_size = 1;
+        }
+        IGRAPH_CHECK(igraph_vector_ptr_reserve(v, new_size));
+    }
+
+    *(v->end) = e;
+    v->end += 1;
+
+    return 0;
+}
+
+void *igraph_vector_ptr_pop_back (igraph_vector_ptr_t *v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    assert(v->stor_begin != v->end);
+    v->end -= 1;
+    return *(v->end);
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_insert
+ * \brief Inserts a single element into a pointer vector.
+ *
+ * Note that this function does not do range checking. Insertion will shift the
+ * elements from the position given to the end of the vector one position to the
+ * right, and the new element will be inserted in the empty space created at
+ * the given position. The size of the vector will increase by one.
+ *
+ * \param v The pointer vector object.
+ * \param pos The position where the new element is inserted.
+ * \param e The inserted element
+ */
+int igraph_vector_ptr_insert(igraph_vector_ptr_t* v, long int pos, void* e) {
+    long int size = igraph_vector_ptr_size(v);
+    IGRAPH_CHECK(igraph_vector_ptr_resize(v, size + 1));
+    if (pos < size) {
+        memmove(v->stor_begin + pos + 1, v->stor_begin + pos,
+                sizeof(void*) * (size_t) (size - pos));
+    }
+    v->stor_begin[pos] = e;
+    return 0;
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_e
+ * \brief Access an element of a pointer vector.
+ *
+ * \param v Pointer to a pointer vector.
+ * \param pos The index of the pointer to return.
+ * \return The pointer at \p pos position.
+ *
+ * Time complexity: O(1).
+ */
+
+void* igraph_vector_ptr_e         (const igraph_vector_ptr_t* v, long int pos) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    return * (v->stor_begin + pos);
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_set
+ * \brief Assign to an element of a pointer vector.
+ *
+ * \param v Pointer to a pointer vector.
+ * \param pos The index of the pointer to update.
+ * \param value The new pointer to set in the vector.
+ *
+ * Time complexity: O(1).
+ */
+
+void igraph_vector_ptr_set       (igraph_vector_ptr_t* v, long int pos, void* value) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    *(v->stor_begin + pos) = value;
+}
+
+/**
+ * \ingroup vectorptr
+ * \brief Set all elements of a pointer vector to the NULL pointer.
+ */
+
+void igraph_vector_ptr_null      (igraph_vector_ptr_t* v) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    if (igraph_vector_ptr_size(v) > 0) {
+        memset(v->stor_begin, 0, sizeof(void*) *
+               (size_t) igraph_vector_ptr_size(v));
+    }
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_resize
+ * \brief Resizes a pointer vector.
+ *
+ * </para><para>
+ * Note that if a vector is made smaller the pointed object are not
+ * deallocated by this function and the item destructor is not called
+ * on the extra elements.
+ *
+ * \param v A pointer vector.
+ * \param newsize The new size of the pointer vector.
+ * \return Error code.
+ *
+ * Time complexity: O(1) if the vector if made smaller. Operating
+ * system dependent otherwise, the amount of \quote time \endquote
+ * needed to allocate the memory for the vector elements.
+ */
+
+int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize) {
+    IGRAPH_CHECK(igraph_vector_ptr_reserve(v, newsize));
+    v->end = v->stor_begin + newsize;
+    return 0;
+}
+
+/**
+ * \ingroup vectorptr
+ * \brief Initializes a pointer vector from an array (constructor).
+ *
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM if out of memory
+ */
+
+int igraph_vector_ptr_init_copy(igraph_vector_ptr_t *v, void * *data, long int length) {
+    v->stor_begin = igraph_Calloc(length, void*);
+    if (v->stor_begin == 0) {
+        IGRAPH_ERROR("cannot init ptr vector from array", IGRAPH_ENOMEM);
+    }
+    v->stor_end = v->stor_begin + length;
+    v->end = v->stor_end;
+    v->item_destructor = 0;
+    memcpy(v->stor_begin, data, (size_t) length * sizeof(void*));
+
+    return 0;
+}
+
+/**
+ * \ingroup vectorptr
+ * \brief Copy the contents of a pointer vector to a regular C array.
+ */
+
+void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    if (v->end != v->stor_begin) {
+        memcpy(to, v->stor_begin, sizeof(void*) *
+               (size_t) (v->end - v->stor_begin));
+    }
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_copy
+ * \brief Copy a pointer vector (constructor).
+ *
+ * </para><para>
+ * This function creates a pointer vector by copying another one. This
+ * is shallow copy, only the pointers in the vector will be copied.
+ *
+ * </para><para>
+ * It is potentially dangerous to copy a pointer vector with an associated
+ * item destructor. The copied vector will inherit the item destructor,
+ * which may cause problems when both vectors are destroyed as the items
+ * might get destroyed twice. Make sure you know what you are doing when
+ * copying a pointer vector with an item destructor, or unset the item
+ * destructor on one of the vectors later.
+ *
+ * \param to Pointer to an uninitialized pointer vector object.
+ * \param from A pointer vector object.
+ * \return Error code:
+ *         \c IGRAPH_ENOMEM if out of memory
+ *
+ * Time complexity: O(n) if allocating memory for n elements can be
+ * done in O(n) time.
+ */
+
+int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) {
+    assert(from != NULL);
+    /*   assert(from->stor_begin != NULL); */ /* TODO */
+    to->stor_begin = igraph_Calloc(igraph_vector_ptr_size(from), void*);
+    if (to->stor_begin == 0) {
+        IGRAPH_ERROR("cannot copy ptr vector", IGRAPH_ENOMEM);
+    }
+    to->stor_end = to->stor_begin + igraph_vector_ptr_size(from);
+    to->end = to->stor_end;
+    to->item_destructor = from->item_destructor;
+    memcpy(to->stor_begin, from->stor_begin,
+           (size_t) igraph_vector_ptr_size(from)*sizeof(void*));
+
+    return 0;
+}
+
+/**
+ * \ingroup vectorptr
+ * \brief Remove an element from a pointer vector.
+ */
+
+void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos) {
+    assert(v != NULL);
+    assert(v->stor_begin != NULL);
+    if (pos + 1 < igraph_vector_ptr_size(v)) { /* TOOD: why is this needed */
+        memmove(v->stor_begin + pos, v->stor_begin + pos + 1,
+                sizeof(void*) * (size_t) (igraph_vector_ptr_size(v) - pos - 1));
+    }
+    v->end--;
+}
+
+/**
+ * \ingroup vectorptr
+ * \brief Sort the pointer vector based on an external comparison function
+ *
+ * Sometimes it is necessary to sort the pointers in the vector based on
+ * the property of the element being referenced by the pointer. This
+ * function allows us to sort the vector based on an arbitrary external
+ * comparison function which accepts two \c void* pointers \c p1 and \c p2
+ * and returns an integer less than, equal to or greater than zero if the
+ * first argument is considered to be respectively less than, equal to, or
+ * greater than the second. \c p1 and \c p2 will point to the pointer in the
+ * vector, so they have to be double-dereferenced if one wants to get access
+ * to the underlying object the address of which is stored in \c v .
+ */
+void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int (*compar)(const void*, const void*)) {
+    qsort(v->stor_begin, (size_t) igraph_vector_ptr_size(v), sizeof(void*),
+          compar);
+}
+
+int igraph_vector_ptr_index_int(igraph_vector_ptr_t *v,
+                                const igraph_vector_int_t *idx) {
+    void **tmp;
+    int i, n = igraph_vector_int_size(idx);
+
+    tmp = igraph_Calloc(n, void*);
+    if (!tmp) {
+        IGRAPH_ERROR("Cannot index pointer vector", IGRAPH_ENOMEM);
+    }
+
+    for (i = 0; i < n; i++) {
+        tmp[i] = VECTOR(*v)[ VECTOR(*idx)[i] ];
+    }
+
+    igraph_Free(v->stor_begin);
+    v->stor_begin = tmp;
+    v->stor_end = v->end = tmp + n;
+
+    return 0;
+}
+
+int igraph_vector_ptr_append    (igraph_vector_ptr_t *to,
+                                 const igraph_vector_ptr_t *from) {
+    long int origsize = igraph_vector_ptr_size(to);
+    long int othersize = igraph_vector_ptr_size(from);
+    long int i;
+
+    IGRAPH_CHECK(igraph_vector_ptr_resize(to, origsize + othersize));
+    for (i = 0; i < othersize; i++, origsize++) {
+        to->stor_begin[origsize] = from->stor_begin[i];
+    }
+
+    return 0;
+}
+
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_set_item_destructor
+ * \brief Sets the item destructor for this pointer vector.
+ *
+ * The item destructor is a function which will be called on every non-null
+ * pointer stored in this vector when \ref igraph_vector_ptr_destroy(),
+ * igraph_vector_ptr_destroy_all() or \ref igraph_vector_ptr_free_all()
+ * is called.
+ *
+ * \return The old item destructor.
+ *
+ * Time complexity: O(1).
+ */
+igraph_finally_func_t* igraph_vector_ptr_set_item_destructor(
+    igraph_vector_ptr_t *v, igraph_finally_func_t *func) {
+    igraph_finally_func_t* result = v->item_destructor;
+
+    v->item_destructor = func;
+
+    return result;
+}
+
+/**
+ * \ingroup vectorptr
+ * \function igraph_vector_ptr_get_item_destructor
+ * \brief Gets the current item destructor for this pointer vector.
+ *
+ * The item destructor is a function which will be called on every non-null
+ * pointer stored in this vector when \ref igraph_vector_ptr_destroy(),
+ * igraph_vector_ptr_destroy_all() or \ref igraph_vector_ptr_free_all()
+ * is called.
+ *
+ * \return The current item destructor.
+ *
+ * Time complexity: O(1).
+ */
+igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v) {
+    assert(v != 0);
+    return v->item_destructor;
+}
diff --git a/igraph/src/version.c b/igraph/src/version.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/version.c
@@ -0,0 +1,67 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2008-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_version.h"
+
+#include <stdio.h>
+
+static const char *igraph_version_string = IGRAPH_VERSION;
+
+/**
+ * \function igraph_version
+ * Return the version of the igraph C library
+ *
+ * \param version_string Pointer to a string pointer. If not null, it
+ *    is set to the igraph version string, e.g. "0.6" or "0.5.3". This
+ *    string should not be modified or deallocated.
+ * \param major If not a null pointer, then it is set to the major
+ *    igraph version. E.g. for version "0.5.3" this is 0.
+ * \param minor If not a null pointer, then it is set to the minor
+ *    igraph version. E.g. for version "0.5.3" this is 5.
+ * \param subminor If not a null pointer, then it is set to the
+ *    subminor igraph version. E.g. for version "0.5.3" this is 3.
+ * \return Error code.
+ *
+ * Time complexity: O(1).
+ *
+ * \example examples/simple/igraph_version.c
+ */
+
+int igraph_version(const char **version_string,
+                   int *major,
+                   int *minor,
+                   int *subminor) {
+    int i1, i2, i3;
+    int *p1 = major ? major : &i1,
+         *p2 = minor ? minor : &i2,
+          *p3 = subminor ? subminor : &i3;
+
+    if (version_string) {
+        *version_string = igraph_version_string;
+    }
+
+    *p1 = *p2 = *p3 = 0;
+    sscanf(IGRAPH_VERSION, "%i.%i.%i", p1, p2, p3);
+
+    return 0;
+}
diff --git a/igraph/src/visitors.c b/igraph/src/visitors.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/visitors.c
@@ -0,0 +1,593 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph R package.
+   Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+#include "igraph_visitor.h"
+#include "igraph_memory.h"
+#include "igraph_adjlist.h"
+#include "igraph_interface.h"
+#include "igraph_dqueue.h"
+#include "igraph_stack.h"
+#include "config.h"
+
+/**
+ * \function igraph_bfs
+ * Breadth-first search
+ *
+ * A simple breadth-first search, with a lot of different results and
+ * the possibility to call a callback whenever a vertex is visited.
+ * It is allowed to supply null pointers as the output arguments the
+ * user is not interested in, in this case they will be ignored.
+ *
+ * </para><para>
+ * If not all vertices can be reached from the supplied root vertex,
+ * then additional root vertices will be used, in the order of their
+ * vertex ids.
+ * \param graph The input graph.
+ * \param root The id of the root vertex. It is ignored if the \c
+ *        roots argument is not a null pointer.
+ * \param roots Pointer to an initialized vector, or a null
+ *        pointer. If not a null pointer, then it is a vector
+ *        containing root vertices to start the BFS from. The vertices
+ *        are considered in the order they appear. If a root vertex
+ *        was already found while searching from another one, then no
+ *        search is conducted from it.
+ * \param mode For directed graphs, it defines which edges to follow.
+ *        \c IGRAPH_OUT means following the direction of the edges,
+ *        \c IGRAPH_IN means the opposite, and
+ *        \c IGRAPH_ALL ignores the direction of the edges.
+ *        This parameter is ignored for undirected graphs.
+ * \param unreachable Logical scalar, whether the search should visit
+ *        the vertices that are unreachable from the given root
+ *        node(s). If true, then additional searches are performed
+ *        until all vertices are visited.
+ * \param restricted If not a null pointer, then it must be a pointer
+ *        to a vector containing vertex ids. The BFS is carried out
+ *        only on these vertices.
+ * \param order If not null pointer, then the vertex ids of the graph are
+ *        stored here, in the same order as they were visited.
+ * \param rank If not a null pointer, then the rank of each vertex is
+ *        stored here.
+ * \param father If not a null pointer, then the id of the father of
+ *        each vertex is stored here.
+ * \param pred If not a null pointer, then the id of vertex that was
+ *        visited before the current one is stored here. If there is
+ *        no such vertex (the current vertex is the root of a search
+ *        tree), then -1 is stored.
+ * \param succ If not a null pointer, then the id of the vertex that
+ *        was visited after the current one is stored here. If there
+ *        is no such vertex (the current one is the last in a search
+ *        tree), then -1 is stored.
+ * \param dist If not a null pointer, then the distance from the root of
+ *        the current search tree is stored here.
+ * \param callback If not null, then it should be a pointer to a
+ *        function of type \ref igraph_bfshandler_t. This function
+ *        will be called, whenever a new vertex is visited.
+ * \param extra Extra argument to pass to the callback function.
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ *
+ * \example examples/simple/igraph_bfs.c
+ * \example examples/simple/igraph_bfs2.c
+ */
+
+int igraph_bfs(const igraph_t *graph,
+               igraph_integer_t root, const igraph_vector_t *roots,
+               igraph_neimode_t mode, igraph_bool_t unreachable,
+               const igraph_vector_t *restricted,
+               igraph_vector_t *order, igraph_vector_t *rank,
+               igraph_vector_t *father,
+               igraph_vector_t *pred, igraph_vector_t *succ,
+               igraph_vector_t *dist, igraph_bfshandler_t *callback,
+               void *extra) {
+
+    igraph_dqueue_t Q;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int actroot = 0;
+    igraph_vector_char_t added;
+
+    igraph_lazy_adjlist_t adjlist;
+
+    long int act_rank = 0;
+    long int pred_vec = -1;
+
+    long int rootpos = 0;
+    long int noroots = roots ? igraph_vector_size(roots) : 1;
+
+    if (!roots && (root < 0 || root >= no_of_nodes)) {
+        IGRAPH_ERROR("Invalid root vertex in BFS", IGRAPH_EINVAL);
+    }
+
+    if (roots) {
+        igraph_real_t min, max;
+        igraph_vector_minmax(roots, &min, &max);
+        if (min < 0 || max >= no_of_nodes) {
+            IGRAPH_ERROR("Invalid root vertex in BFS", IGRAPH_EINVAL);
+        }
+    }
+
+    if (restricted) {
+        igraph_real_t min, max;
+        igraph_vector_minmax(restricted, &min, &max);
+        if (min < 0 || max >= no_of_nodes) {
+            IGRAPH_ERROR("Invalid vertex id in restricted set", IGRAPH_EINVAL);
+        }
+    }
+
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    IGRAPH_CHECK(igraph_vector_char_init(&added, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &added);
+    IGRAPH_CHECK(igraph_dqueue_init(&Q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &Q);
+
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, /*simplify=*/ 0));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
+
+    /* Mark the vertices that are not in the restricted set, as already
+       found. Special care must be taken for vertices that are not in
+       the restricted set, but are to be used as 'root' vertices. */
+    if (restricted) {
+        long int i, n = igraph_vector_size(restricted);
+        igraph_vector_char_fill(&added, 1);
+        for (i = 0; i < n; i++) {
+            long int v = (long int) VECTOR(*restricted)[i];
+            VECTOR(added)[v] = 0;
+        }
+    }
+
+    /* Resize result vectors, and fill them with IGRAPH_NAN */
+
+# define VINIT(v) if (v) {                      \
+        igraph_vector_resize((v), no_of_nodes);   \
+        igraph_vector_fill((v), IGRAPH_NAN); }
+
+    VINIT(order);
+    VINIT(rank);
+    VINIT(father);
+    VINIT(pred);
+    VINIT(succ);
+    VINIT(dist);
+# undef VINIT
+
+    while (1) {
+
+        /* Get the next root vertex, if any */
+
+        if (roots && rootpos < noroots) {
+            /* We are still going through the 'roots' vector */
+            actroot = (long int) VECTOR(*roots)[rootpos++];
+        } else if (!roots && rootpos == 0) {
+            /* We have a single root vertex given, and start now */
+            actroot = root;
+            rootpos++;
+        } else if (rootpos == noroots && unreachable) {
+            /* We finished the given root(s), but other vertices are also
+            tried as root */
+            actroot = 0;
+            rootpos++;
+        } else if (unreachable && actroot + 1 < no_of_nodes) {
+            /* We are already doing the other vertices, take the next one */
+            actroot++;
+        } else {
+            /* No more root nodes to do */
+            break;
+        }
+
+        /* OK, we have a new root, start BFS */
+        if (VECTOR(added)[actroot]) {
+            continue;
+        }
+        IGRAPH_CHECK(igraph_dqueue_push(&Q, actroot));
+        IGRAPH_CHECK(igraph_dqueue_push(&Q, 0));
+        VECTOR(added)[actroot] = 1;
+        if (father) {
+            VECTOR(*father)[actroot] = -1;
+        }
+
+        pred_vec = -1;
+
+        while (!igraph_dqueue_empty(&Q)) {
+            long int actvect = (long int) igraph_dqueue_pop(&Q);
+            long int actdist = (long int) igraph_dqueue_pop(&Q);
+            long int succ_vec;
+            igraph_vector_t *neis = igraph_lazy_adjlist_get(&adjlist,
+                                    (igraph_integer_t) actvect);
+            long int i, n = igraph_vector_size(neis);
+
+            if (pred) {
+                VECTOR(*pred)[actvect] = pred_vec;
+            }
+            if (rank) {
+                VECTOR(*rank) [actvect] = act_rank;
+            }
+            if (order) {
+                VECTOR(*order)[act_rank++] = actvect;
+            }
+            if (dist) {
+                VECTOR(*dist)[actvect] = actdist;
+            }
+
+            for (i = 0; i < n; i++) {
+                long int nei = (long int) VECTOR(*neis)[i];
+                if (! VECTOR(added)[nei]) {
+                    VECTOR(added)[nei] = 1;
+                    IGRAPH_CHECK(igraph_dqueue_push(&Q, nei));
+                    IGRAPH_CHECK(igraph_dqueue_push(&Q, actdist + 1));
+                    if (father) {
+                        VECTOR(*father)[nei] = actvect;
+                    }
+                }
+            }
+
+            succ_vec = igraph_dqueue_empty(&Q) ? -1L :
+                       (long int) igraph_dqueue_head(&Q);
+            if (callback) {
+                igraph_bool_t terminate =
+                    callback(graph, (igraph_integer_t) actvect, (igraph_integer_t)
+                             pred_vec, (igraph_integer_t) succ_vec,
+                             (igraph_integer_t) act_rank - 1, (igraph_integer_t) actdist,
+                             extra);
+                if (terminate) {
+                    igraph_lazy_adjlist_destroy(&adjlist);
+                    igraph_dqueue_destroy(&Q);
+                    igraph_vector_char_destroy(&added);
+                    IGRAPH_FINALLY_CLEAN(3);
+                    return 0;
+                }
+            }
+
+            if (succ) {
+                VECTOR(*succ)[actvect] = succ_vec;
+            }
+            pred_vec = actvect;
+
+        } /* while Q !empty */
+
+    } /* for actroot < no_of_nodes */
+
+    igraph_lazy_adjlist_destroy(&adjlist);
+    igraph_dqueue_destroy(&Q);
+    igraph_vector_char_destroy(&added);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_i_bfs
+ * \ingroup internal
+ *
+ * Added in version 0.2.
+ *
+ * TODO
+ */
+
+int igraph_i_bfs(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mode,
+                 igraph_vector_t *vids, igraph_vector_t *layers,
+                 igraph_vector_t *parents) {
+
+    igraph_dqueue_t q;
+    long int vidspos = 0;
+    igraph_vector_t neis;
+    long int no_of_nodes = igraph_vcount(graph);
+    long int i;
+    char *added;
+    long int lastlayer = -1;
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    /* temporary storage */
+    added = igraph_Calloc(no_of_nodes, char);
+    if (added == 0) {
+        IGRAPH_ERROR("Cannot calculate BFS", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, added);
+    IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
+    IGRAPH_CHECK(igraph_dqueue_init(&q, 100));
+    IGRAPH_FINALLY(igraph_dqueue_destroy, &q);
+
+    /* results */
+    IGRAPH_CHECK(igraph_vector_resize(vids, no_of_nodes));
+    igraph_vector_clear(layers);
+    IGRAPH_CHECK(igraph_vector_resize(parents, no_of_nodes));
+
+    /* ok start with vid */
+    IGRAPH_CHECK(igraph_dqueue_push(&q, vid));
+    IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
+    IGRAPH_CHECK(igraph_vector_push_back(layers, vidspos));
+    VECTOR(*vids)[vidspos++] = vid;
+    VECTOR(*parents)[(long int)vid] = vid;
+    added[(long int)vid] = 1;
+
+    while (!igraph_dqueue_empty(&q)) {
+        long int actvect = (long int) igraph_dqueue_pop(&q);
+        long int actdist = (long int) igraph_dqueue_pop(&q);
+        IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actvect,
+                                      mode));
+        for (i = 0; i < igraph_vector_size(&neis); i++) {
+            long int neighbor = (long int) VECTOR(neis)[i];
+            if (added[neighbor] == 0) {
+                added[neighbor] = 1;
+                VECTOR(*parents)[neighbor] = actvect;
+                IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
+                IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
+                if (lastlayer != actdist + 1) {
+                    IGRAPH_CHECK(igraph_vector_push_back(layers, vidspos));
+                }
+                VECTOR(*vids)[vidspos++] = neighbor;
+                lastlayer = actdist + 1;
+            }
+        } /* for i in neis */
+    } /* while ! dqueue_empty */
+    IGRAPH_CHECK(igraph_vector_push_back(layers, vidspos));
+
+    igraph_vector_destroy(&neis);
+    igraph_dqueue_destroy(&q);
+    igraph_Free(added);
+    IGRAPH_FINALLY_CLEAN(3);
+
+    return 0;
+}
+
+/**
+ * \function igraph_dfs
+ * Depth-first search
+ *
+ * A simple depth-first search, with
+ * the possibility to call a callback whenever a vertex is discovered
+ * and/or whenever a subtree is finished.
+ * It is allowed to supply null pointers as the output arguments the
+ * user is not interested in, in this case they will be ignored.
+ *
+ * </para><para>
+ * If not all vertices can be reached from the supplied root vertex,
+ * then additional root vertices will be used, in the order of their
+ * vertex ids.
+ * \param graph The input graph.
+ * \param root The id of the root vertex.
+ * \param mode For directed graphs, it defines which edges to follow.
+ *        \c IGRAPH_OUT means following the direction of the edges,
+ *        \c IGRAPH_IN means the opposite, and
+ *        \c IGRAPH_ALL ignores the direction of the edges.
+ *        This parameter is ignored for undirected graphs.
+ * \param unreachable Logical scalar, whether the search should visit
+ *        the vertices that are unreachable from the given root
+ *        node(s). If true, then additional searches are performed
+ *        until all vertices are visited.
+ * \param order If not null pointer, then the vertex ids of the graph are
+ *        stored here, in the same order as they were discovered.
+ * \param order_out If not a null pointer, then the vertex ids of the
+ *        graphs are stored here, in the order of the completion of
+ *        their subtree.
+ * \param father If not a null pointer, then the id of the father of
+ *        each vertex is stored here.
+ * \param dist If not a null pointer, then the distance from the root of
+ *        the current search tree is stored here.
+ * \param in_callback If not null, then it should be a pointer to a
+ *        function of type \ref igraph_dfshandler_t. This function
+ *        will be called, whenever a new vertex is discovered.
+ * \param out_callback If not null, then it should be a pointer to a
+ *        function of type \ref igraph_dfshandler_t. This function
+ *        will be called, whenever the subtree of a vertex is completed.
+ * \param extra Extra argument to pass to the callback function(s).
+ * \return Error code.
+ *
+ * Time complexity: O(|V|+|E|), linear in the number of vertices and
+ * edges.
+ */
+
+int igraph_dfs(const igraph_t *graph, igraph_integer_t root,
+               igraph_neimode_t mode, igraph_bool_t unreachable,
+               igraph_vector_t *order,
+               igraph_vector_t *order_out, igraph_vector_t *father,
+               igraph_vector_t *dist, igraph_dfshandler_t *in_callback,
+               igraph_dfshandler_t *out_callback,
+               void *extra) {
+
+    long int no_of_nodes = igraph_vcount(graph);
+    igraph_lazy_adjlist_t adjlist;
+    igraph_stack_t stack;
+    igraph_vector_char_t added;
+    igraph_vector_long_t nptr;
+    long int actroot;
+    long int act_rank = 0;
+    long int rank_out = 0;
+    long int act_dist = 0;
+
+    if (root < 0 || root >= no_of_nodes) {
+        IGRAPH_ERROR("Invalid root vertex for DFS", IGRAPH_EINVAL);
+    }
+
+    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
+        mode != IGRAPH_ALL) {
+        IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
+    }
+
+    if (!igraph_is_directed(graph)) {
+        mode = IGRAPH_ALL;
+    }
+
+    IGRAPH_CHECK(igraph_vector_char_init(&added, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_char_destroy, &added);
+    IGRAPH_CHECK(igraph_stack_init(&stack, 100));
+    IGRAPH_FINALLY(igraph_stack_destroy, &stack);
+    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, /*simplify=*/ 0));
+    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
+    IGRAPH_CHECK(igraph_vector_long_init(&nptr, no_of_nodes));
+    IGRAPH_FINALLY(igraph_vector_long_destroy, &nptr);
+
+# define FREE_ALL() do {            \
+        igraph_vector_long_destroy(&nptr);            \
+        igraph_lazy_adjlist_destroy(&adjlist);        \
+        igraph_stack_destroy(&stack);                 \
+        igraph_vector_char_destroy(&added);           \
+        IGRAPH_FINALLY_CLEAN(4); } while (0)
+
+    /* Resize result vectors and fill them with IGRAPH_NAN */
+
+# define VINIT(v) if (v) {                      \
+        igraph_vector_resize(v, no_of_nodes);       \
+        igraph_vector_fill(v, IGRAPH_NAN); }
+
+    VINIT(order);
+    VINIT(order_out);
+    VINIT(father);
+    VINIT(dist);
+
+# undef VINIT
+
+    IGRAPH_CHECK(igraph_stack_push(&stack, root));
+    VECTOR(added)[(long int)root] = 1;
+    if (father) {
+        VECTOR(*father)[(long int)root] = -1;
+    }
+    if (order) {
+        VECTOR(*order)[act_rank++] = root;
+    }
+    if (dist) {
+        VECTOR(*dist)[(long int)root] = 0;
+    }
+    if (in_callback) {
+        igraph_bool_t terminate = in_callback(graph, root, 0, extra);
+        if (terminate) {
+            FREE_ALL();
+            return 0;
+        }
+    }
+
+    for (actroot = 0; actroot < no_of_nodes; ) {
+
+        /* 'root' first, then all other vertices */
+        if (igraph_stack_empty(&stack)) {
+            if (!unreachable) {
+                break;
+            }
+            if (VECTOR(added)[actroot]) {
+                actroot++;
+                continue;
+            }
+            IGRAPH_CHECK(igraph_stack_push(&stack, actroot));
+            VECTOR(added)[actroot] = 1;
+            if (father) {
+                VECTOR(*father)[actroot] = -1;
+            }
+            if (order) {
+                VECTOR(*order)[act_rank++] = actroot;
+            }
+            if (dist) {
+                VECTOR(*dist)[actroot] = 0;
+            }
+
+            if (in_callback) {
+                igraph_bool_t terminate = in_callback(graph, (igraph_integer_t) actroot,
+                                                      0, extra);
+                if (terminate) {
+                    FREE_ALL();
+                    return 0;
+                }
+            }
+            actroot++;
+        }
+
+        while (!igraph_stack_empty(&stack)) {
+            long int actvect = (long int) igraph_stack_top(&stack);
+            igraph_vector_t *neis = igraph_lazy_adjlist_get(&adjlist,
+                                    (igraph_integer_t) actvect);
+            long int n = igraph_vector_size(neis);
+            long int *ptr = igraph_vector_long_e_ptr(&nptr, actvect);
+
+            /* Search for a neighbor that was not yet visited */
+            igraph_bool_t any = 0;
+            long int nei;
+            while (!any && (*ptr) < n) {
+                nei = (long int) VECTOR(*neis)[(*ptr)];
+                any = !VECTOR(added)[nei];
+                (*ptr) ++;
+            }
+            if (any) {
+                /* There is such a neighbor, add it */
+                IGRAPH_CHECK(igraph_stack_push(&stack, nei));
+                VECTOR(added)[nei] = 1;
+                if (father) {
+                    VECTOR(*father)[ nei ] = actvect;
+                }
+                if (order) {
+                    VECTOR(*order)[act_rank++] = nei;
+                }
+                act_dist++;
+                if (dist) {
+                    VECTOR(*dist)[nei] = act_dist;
+                }
+
+                if (in_callback) {
+                    igraph_bool_t terminate = in_callback(graph, (igraph_integer_t) nei,
+                                                          (igraph_integer_t) act_dist,
+                                                          extra);
+                    if (terminate) {
+                        FREE_ALL();
+                        return 0;
+                    }
+                }
+
+            } else {
+                /* There is no such neighbor, finished with the subtree */
+                igraph_stack_pop(&stack);
+                if (order_out) {
+                    VECTOR(*order_out)[rank_out++] = actvect;
+                }
+                act_dist--;
+
+                if (out_callback) {
+                    igraph_bool_t terminate = out_callback(graph, (igraph_integer_t)
+                                                           actvect, (igraph_integer_t)
+                                                           act_dist, extra);
+                    if (terminate) {
+                        FREE_ALL();
+                        return 0;
+                    }
+                }
+            }
+        }
+    }
+
+    FREE_ALL();
+# undef FREE_ALL
+
+    return 0;
+}
diff --git a/igraph/src/walktrap.cpp b/igraph/src/walktrap.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/walktrap.cpp
@@ -0,0 +1,174 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Pascal Pons
+   The original copyright notice follows here. The FSF address was
+   fixed by Tamas Nepusz */
+
+// File: walktrap.cpp
+//-----------------------------------------------------------------------------
+// Walktrap v0.2 -- Finds community structure of networks using random walks
+// Copyright (C) 2004-2005 Pascal Pons
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+//-----------------------------------------------------------------------------
+// Author   : Pascal Pons
+// Email    : pascal.pons@gmail.com
+// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html
+// Location : Paris, France
+// Time     : June 2005
+//-----------------------------------------------------------------------------
+// see readme.txt for more details
+
+#include "walktrap_graph.h"
+#include "walktrap_communities.h"
+#include <ctime>
+#include <set>
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+
+#include "igraph_community.h"
+#include "igraph_components.h"
+#include "igraph_interface.h"
+#include "igraph_interrupt_internal.h"
+
+using namespace std;
+using namespace igraph::walktrap;
+
+/**
+ * \function igraph_community_walktrap
+ *
+ * This function is the implementation of the Walktrap community
+ * finding algorithm, see Pascal Pons, Matthieu Latapy: Computing
+ * communities in large networks using random walks,
+ * http://arxiv.org/abs/physics/0512106
+ *
+ * </para><para>
+ * Currently the original C++ implementation is used in igraph,
+ * see http://www-rp.lip6.fr/~latapy/PP/walktrap.html
+ * I'm grateful to Matthieu Latapy and Pascal Pons for providing this
+ * source code.
+ *
+ * </para><para>
+ * In contrast to the original implementation, isolated vertices are allowed
+ * in the graph and they are assumed to have a single incident loop edge with
+ * weight 1.
+ *
+ * \param graph The input graph, edge directions are ignored.
+ * \param weights Numeric vector giving the weights of the edges.
+ *     If it is a NULL pointer then all edges will have equal
+ *     weights. The weights are expected to be positive.
+ * \param steps Integer constant, the length of the random walks.
+ * \param merges Pointer to a matrix, the merges performed by the
+ *     algorithm will be stored here (if not NULL). Each merge is a
+ *     row in a two-column matrix and contains the ids of the merged
+ *     clusters. Clusters are numbered from zero and cluster numbers
+ *     smaller than the number of nodes in the network belong to the
+ *     individual vertices as singleton clusters. In each step a new
+ *     cluster is created from two other clusters and its id will be
+ *     one larger than the largest cluster id so far. This means that
+ *     before the first merge we have \c n clusters (the number of
+ *     vertices in the graph) numbered from zero to \c n-1. The first
+ *     merge creates cluster \c n, the second cluster \c n+1, etc.
+ * \param modularity Pointer to a vector. If not NULL then the
+ *     modularity score of the current clustering is stored here after
+ *     each merge operation.
+ * \param membership Pointer to a vector. If not a NULL pointer, then
+ *     the membership vector corresponding to the maximal modularity
+ *     score is stored here. If it is not a NULL pointer, then neither
+ *     \p modularity nor \p merges may be NULL.
+ * \return Error code.
+ *
+ * \sa \ref igraph_community_spinglass(), \ref
+ * igraph_community_edge_betweenness().
+ *
+ * Time complexity: O(|E||V|^2) in the worst case, O(|V|^2 log|V|) typically,
+ * |V| is the number of vertices, |E| is the number of edges.
+ *
+ * \example examples/simple/walktrap.c
+ */
+
+int igraph_community_walktrap(const igraph_t *graph,
+                              const igraph_vector_t *weights,
+                              int steps,
+                              igraph_matrix_t *merges,
+                              igraph_vector_t *modularity,
+                              igraph_vector_t *membership) {
+
+    long int no_of_nodes = (long int)igraph_vcount(graph);
+    int length = steps;
+    long max_memory = -1;
+
+    if (membership && !(modularity && merges)) {
+        IGRAPH_ERROR("Cannot calculate membership without modularity or merges",
+                     IGRAPH_EINVAL);
+    }
+
+    Graph* G = new Graph;
+    if (G->convert_from_igraph(graph, weights)) {
+        IGRAPH_ERROR("Cannot convert igraph graph into walktrap format", IGRAPH_EINVAL);
+    }
+
+    if (merges) {
+        igraph_integer_t no;
+        IGRAPH_CHECK(igraph_clusters(graph, /*membership=*/ 0, /*csize=*/ 0,
+                                     &no, IGRAPH_WEAK));
+        IGRAPH_CHECK(igraph_matrix_resize(merges, no_of_nodes - no, 2));
+    }
+    if (modularity) {
+        IGRAPH_CHECK(igraph_vector_resize(modularity, no_of_nodes));
+        igraph_vector_null(modularity);
+    }
+    Communities C(G, length, max_memory, merges, modularity);
+
+    while (!C.H->is_empty()) {
+        IGRAPH_ALLOW_INTERRUPTION();
+        C.merge_nearest_communities();
+    }
+
+    delete G;
+
+    if (membership) {
+        long int m = igraph_vector_which_max(modularity);
+        IGRAPH_CHECK(igraph_community_to_membership(merges, no_of_nodes,
+                     /*steps=*/ m,
+                     membership,
+                     /*csize=*/ 0));
+    }
+
+    return 0;
+}
diff --git a/igraph/src/walktrap_communities.cpp b/igraph/src/walktrap_communities.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/walktrap_communities.cpp
@@ -0,0 +1,937 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Pascal Pons
+   The original copyright notice follows here. The FSF address was
+   fixed by Tamas Nepusz */
+
+// File: communities.cpp
+//-----------------------------------------------------------------------------
+// Walktrap v0.2 -- Finds community structure of networks using random walks
+// Copyright (C) 2004-2005 Pascal Pons
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+//-----------------------------------------------------------------------------
+// Author   : Pascal Pons
+// Email    : pascal.pons@gmail.com
+// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html
+// Location : Paris, France
+// Time     : June 2005
+//-----------------------------------------------------------------------------
+// see readme.txt for more details
+
+#include "walktrap_communities.h"
+#include <cstdlib>
+#include <iostream>
+#include <cmath>
+#include <algorithm>
+
+#include "config.h"
+
+namespace igraph {
+
+namespace walktrap {
+
+IGRAPH_THREAD_LOCAL int Probabilities::length = 0;
+IGRAPH_THREAD_LOCAL Communities* Probabilities::C = 0;
+IGRAPH_THREAD_LOCAL float* Probabilities::tmp_vector1 = 0;
+IGRAPH_THREAD_LOCAL float* Probabilities::tmp_vector2 = 0;
+IGRAPH_THREAD_LOCAL int* Probabilities::id = 0;
+IGRAPH_THREAD_LOCAL int* Probabilities::vertices1 = 0;
+IGRAPH_THREAD_LOCAL int* Probabilities::vertices2 = 0;
+IGRAPH_THREAD_LOCAL int Probabilities::current_id = 0;
+
+
+Neighbor::Neighbor() {
+    next_community1 = 0;
+    previous_community1 = 0;
+    next_community2 = 0;
+    previous_community2 = 0;
+    heap_index = -1;
+}
+
+Probabilities::~Probabilities() {
+    C->memory_used -= memory();
+    if (P) {
+        delete[] P;
+    }
+    if (vertices) {
+        delete[] vertices;
+    }
+}
+
+Probabilities::Probabilities(int community) {
+    Graph* G = C->G;
+    int nb_vertices1 = 0;
+    int nb_vertices2 = 0;
+
+    float initial_proba = 1. / float(C->communities[community].size);
+    int last =  C->members[C->communities[community].last_member];
+    for (int m = C->communities[community].first_member; m != last; m = C->members[m]) {
+        tmp_vector1[m] = initial_proba;
+        vertices1[nb_vertices1++] = m;
+    }
+
+    for (int t = 0; t < length; t++) {
+        current_id++;
+        if (nb_vertices1 > (G->nb_vertices / 2)) {
+            nb_vertices2 = G->nb_vertices;
+            for (int i = 0; i < G->nb_vertices; i++) {
+                tmp_vector2[i] = 0.;
+            }
+            if (nb_vertices1 == G->nb_vertices) {
+                for (int i = 0; i < G->nb_vertices; i++) {
+                    float proba = tmp_vector1[i] / G->vertices[i].total_weight;
+                    for (int j = 0; j < G->vertices[i].degree; j++) {
+                        tmp_vector2[G->vertices[i].edges[j].neighbor] += proba * G->vertices[i].edges[j].weight;
+                    }
+                }
+            } else {
+                for (int i = 0; i < nb_vertices1; i++) {
+                    int v1 = vertices1[i];
+                    float proba = tmp_vector1[v1] / G->vertices[v1].total_weight;
+                    for (int j = 0; j < G->vertices[v1].degree; j++) {
+                        tmp_vector2[G->vertices[v1].edges[j].neighbor] += proba * G->vertices[v1].edges[j].weight;
+                    }
+                }
+            }
+        } else {
+            nb_vertices2 = 0;
+            for (int i = 0; i < nb_vertices1; i++) {
+                int v1 = vertices1[i];
+                float proba = tmp_vector1[v1] / G->vertices[v1].total_weight;
+                for (int j = 0; j < G->vertices[v1].degree; j++) {
+                    int v2 = G->vertices[v1].edges[j].neighbor;
+                    if (id[v2] == current_id) {
+                        tmp_vector2[v2] += proba * G->vertices[v1].edges[j].weight;
+                    } else {
+                        tmp_vector2[v2] = proba * G->vertices[v1].edges[j].weight;
+                        id[v2] = current_id;
+                        vertices2[nb_vertices2++] = v2;
+                    }
+                }
+            }
+        }
+        float* tmp = tmp_vector2;
+        tmp_vector2 = tmp_vector1;
+        tmp_vector1 = tmp;
+
+        int* tmp2 = vertices2;
+        vertices2 = vertices1;
+        vertices1 = tmp2;
+
+        nb_vertices1 = nb_vertices2;
+    }
+
+    if (nb_vertices1 > (G->nb_vertices / 2)) {
+        P = new float[G->nb_vertices];
+        size = G->nb_vertices;
+        vertices = 0;
+        if (nb_vertices1 == G->nb_vertices) {
+            for (int i = 0; i < G->nb_vertices; i++) {
+                P[i] = tmp_vector1[i] / sqrt(G->vertices[i].total_weight);
+            }
+        } else {
+            for (int i = 0; i < G->nb_vertices; i++) {
+                P[i] = 0.;
+            }
+            for (int i = 0; i < nb_vertices1; i++) {
+                P[vertices1[i]] = tmp_vector1[vertices1[i]] / sqrt(G->vertices[vertices1[i]].total_weight);
+            }
+        }
+    } else {
+        P = new float[nb_vertices1];
+        size = nb_vertices1;
+        vertices = new int[nb_vertices1];
+        int j = 0;
+        for (int i = 0; i < G->nb_vertices; i++) {
+            if (id[i] == current_id) {
+                P[j] = tmp_vector1[i] / sqrt(G->vertices[i].total_weight);
+                vertices[j] = i;
+                j++;
+            }
+        }
+    }
+    C->memory_used += memory();
+}
+
+Probabilities::Probabilities(int community1, int community2) {
+    // The two following probability vectors must exist.
+    // Do not call this function if it is not the case.
+    Probabilities* P1 = C->communities[community1].P;
+    Probabilities* P2 = C->communities[community2].P;
+
+    float w1 = float(C->communities[community1].size) / float(C->communities[community1].size + C->communities[community2].size);
+    float w2 = float(C->communities[community2].size) / float(C->communities[community1].size + C->communities[community2].size);
+
+
+    if (P1->size == C->G->nb_vertices) {
+        P = new float[C->G->nb_vertices];
+        size = C->G->nb_vertices;
+        vertices = 0;
+
+        if (P2->size == C->G->nb_vertices) { // two full vectors
+            for (int i = 0; i < C->G->nb_vertices; i++) {
+                P[i] = P1->P[i] * w1 + P2->P[i] * w2;
+            }
+        } else { // P1 full vector, P2 partial vector
+            int j = 0;
+            for (int i = 0; i < P2->size; i++) {
+                for (; j < P2->vertices[i]; j++) {
+                    P[j] = P1->P[j] * w1;
+                }
+                P[j] = P1->P[j] * w1 + P2->P[i] * w2;
+                j++;
+            }
+            for (; j < C->G->nb_vertices; j++) {
+                P[j] = P1->P[j] * w1;
+            }
+        }
+    } else {
+        if (P2->size == C->G->nb_vertices) { // P1 partial vector, P2 full vector
+            P = new float[C->G->nb_vertices];
+            size = C->G->nb_vertices;
+            vertices = 0;
+
+            int j = 0;
+            for (int i = 0; i < P1->size; i++) {
+                for (; j < P1->vertices[i]; j++) {
+                    P[j] = P2->P[j] * w2;
+                }
+                P[j] = P1->P[i] * w1 + P2->P[j] * w2;
+                j++;
+            }
+            for (; j < C->G->nb_vertices; j++) {
+                P[j] = P2->P[j] * w2;
+            }
+        } else { // two partial vectors
+            int i = 0;
+            int j = 0;
+            int nb_vertices1 = 0;
+            while ((i < P1->size) && (j < P2->size)) {
+                if (P1->vertices[i] < P2->vertices[j]) {
+                    tmp_vector1[P1->vertices[i]] = P1->P[i] * w1;
+                    vertices1[nb_vertices1++] = P1->vertices[i];
+                    i++;
+                    continue;
+                }
+                if (P1->vertices[i] > P2->vertices[j]) {
+                    tmp_vector1[P2->vertices[j]] = P2->P[j] * w2;
+                    vertices1[nb_vertices1++] = P2->vertices[j];
+                    j++;
+                    continue;
+                }
+                tmp_vector1[P1->vertices[i]] = P1->P[i] * w1 + P2->P[j] * w2;
+                vertices1[nb_vertices1++] = P1->vertices[i];
+                i++;
+                j++;
+            }
+            if (i == P1->size) {
+                for (; j < P2->size; j++) {
+                    tmp_vector1[P2->vertices[j]] = P2->P[j] * w2;
+                    vertices1[nb_vertices1++] = P2->vertices[j];
+                }
+            } else {
+                for (; i < P1->size; i++) {
+                    tmp_vector1[P1->vertices[i]] = P1->P[i] * w1;
+                    vertices1[nb_vertices1++] = P1->vertices[i];
+                }
+            }
+
+            if (nb_vertices1 > (C->G->nb_vertices / 2)) {
+                P = new float[C->G->nb_vertices];
+                size = C->G->nb_vertices;
+                vertices = 0;
+                for (int i = 0; i < C->G->nb_vertices; i++) {
+                    P[i] = 0.;
+                }
+                for (int i = 0; i < nb_vertices1; i++) {
+                    P[vertices1[i]] = tmp_vector1[vertices1[i]];
+                }
+            } else {
+                P = new float[nb_vertices1];
+                size = nb_vertices1;
+                vertices = new int[nb_vertices1];
+                for (int i = 0; i < nb_vertices1; i++) {
+                    vertices[i] = vertices1[i];
+                    P[i] = tmp_vector1[vertices1[i]];
+                }
+            }
+        }
+    }
+
+    C->memory_used += memory();
+}
+
+double Probabilities::compute_distance(const Probabilities* P2) const {
+    double r = 0.;
+    if (vertices) {
+        if (P2->vertices) { // two partial vectors
+            int i = 0;
+            int j = 0;
+            while ((i < size) && (j < P2->size)) {
+                if (vertices[i] < P2->vertices[j]) {
+                    r += P[i] * P[i];
+                    i++;
+                    continue;
+                }
+                if (vertices[i] > P2->vertices[j]) {
+                    r += P2->P[j] * P2->P[j];
+                    j++;
+                    continue;
+                }
+                r += (P[i] - P2->P[j]) * (P[i] - P2->P[j]);
+                i++;
+                j++;
+            }
+            if (i == size) {
+                for (; j < P2->size; j++) {
+                    r += P2->P[j] * P2->P[j];
+                }
+            } else {
+                for (; i < size; i++) {
+                    r += P[i] * P[i];
+                }
+            }
+        } else { // P1 partial vector, P2 full vector
+
+            int i = 0;
+            for (int j = 0; j < size; j++) {
+                for (; i < vertices[j]; i++) {
+                    r += P2->P[i] * P2->P[i];
+                }
+                r += (P[j] - P2->P[i]) * (P[j] - P2->P[i]);
+                i++;
+            }
+            for (; i < P2->size; i++) {
+                r += P2->P[i] * P2->P[i];
+            }
+        }
+    } else {
+        if (P2->vertices) { // P1 full vector, P2 partial vector
+            int i = 0;
+            for (int j = 0; j < P2->size; j++) {
+                for (; i < P2->vertices[j]; i++) {
+                    r += P[i] * P[i];
+                }
+                r += (P[i] - P2->P[j]) * (P[i] - P2->P[j]);
+                i++;
+            }
+            for (; i < size; i++) {
+                r += P[i] * P[i];
+            }
+        } else { // two full vectors
+            for (int i = 0; i < size; i++) {
+                r += (P[i] - P2->P[i]) * (P[i] - P2->P[i]);
+            }
+        }
+    }
+    return r;
+}
+
+long Probabilities::memory() {
+    if (vertices) {
+        return (sizeof(Probabilities) + long(size) * (sizeof(float) + sizeof(int)));
+    } else {
+        return (sizeof(Probabilities) + long(size) * sizeof(float));
+    }
+}
+
+Community::Community() {
+    P = 0;
+    first_neighbor = 0;
+    last_neighbor = 0;
+    sub_community_of = -1;
+    sub_communities[0] = -1;
+    sub_communities[1] = -1;
+    sigma = 0.;
+    internal_weight = 0.;
+    total_weight = 0.;
+}
+
+Community::~Community() {
+    if (P) {
+        delete P;
+    }
+}
+
+
+Communities::Communities(Graph* graph, int random_walks_length,
+                         long m, igraph_matrix_t *pmerges,
+                         igraph_vector_t *pmodularity) {
+    max_memory = m;
+    memory_used = 0;
+    G = graph;
+    merges = pmerges;
+    mergeidx = 0;
+    modularity = pmodularity;
+
+    Probabilities::C = this;
+    Probabilities::length = random_walks_length;
+    Probabilities::tmp_vector1 = new float[G->nb_vertices];
+    Probabilities::tmp_vector2 = new float[G->nb_vertices];
+    Probabilities::id = new int[G->nb_vertices];
+    for (int i = 0; i < G->nb_vertices; i++) {
+        Probabilities::id[i] = 0;
+    }
+    Probabilities::vertices1 = new int[G->nb_vertices];
+    Probabilities::vertices2 = new int[G->nb_vertices];
+    Probabilities::current_id = 0;
+
+
+    members = new int[G->nb_vertices];
+    for (int i = 0; i < G->nb_vertices; i++) {
+        members[i] = -1;
+    }
+
+    H = new Neighbor_heap(G->nb_edges);
+    communities = new Community[2 * G->nb_vertices];
+
+// init the n single vertex communities
+
+    if (max_memory != -1) {
+        min_delta_sigma = new Min_delta_sigma_heap(G->nb_vertices * 2);
+    } else {
+        min_delta_sigma = 0;
+    }
+
+    for (int i = 0; i < G->nb_vertices; i++) {
+        communities[i].this_community = i;
+        communities[i].first_member = i;
+        communities[i].last_member = i;
+        communities[i].size = 1;
+        communities[i].sub_community_of = 0;
+    }
+
+    nb_communities = G->nb_vertices;
+    nb_active_communities = G->nb_vertices;
+
+    for (int i = 0; i < G->nb_vertices; i++)
+        for (int j = 0; j < G->vertices[i].degree; j++)
+            if (i < G->vertices[i].edges[j].neighbor) {
+                communities[i].total_weight += G->vertices[i].edges[j].weight / 2.;
+                communities[G->vertices[i].edges[j].neighbor].total_weight += G->vertices[i].edges[j].weight / 2.;
+                Neighbor* N = new Neighbor;
+                N->community1 = i;
+                N->community2 = G->vertices[i].edges[j].neighbor;
+                N->delta_sigma = -1. / double(min(G->vertices[i].degree,  G->vertices[G->vertices[i].edges[j].neighbor].degree));
+                N->weight = G->vertices[i].edges[j].weight;
+                N->exact = false;
+                add_neighbor(N);
+            }
+
+    if (max_memory != -1) {
+        memory_used += min_delta_sigma->memory();
+        memory_used += 2 * long(G->nb_vertices) * sizeof(Community);
+        memory_used += long(G->nb_vertices) * (2 * sizeof(float) + 3 * sizeof(int)); // the static data of Probabilities class
+        memory_used += H->memory() + long(G->nb_edges) * sizeof(Neighbor);
+        memory_used += G->memory();
+    }
+
+    /*   int c = 0; */
+    Neighbor* N = H->get_first();
+    if (N == 0) {
+        return;    /* this can happen if there are no edges */
+    }
+    while (!N->exact) {
+        update_neighbor(N, compute_delta_sigma(N->community1, N->community2));
+        N->exact = true;
+        N = H->get_first();
+        if (max_memory != -1) {
+            manage_memory();
+        }
+        /* TODO: this could use igraph_progress */
+        /*     if(!silent) { */
+        /*       c++; */
+        /*       for(int k = (500*(c-1))/G->nb_edges + 1; k <= (500*c)/G->nb_edges; k++) { */
+        /*  if(k % 50 == 1) {cerr.width(2); cerr << endl << k/ 5 << "% ";} */
+        /*  cerr << "."; */
+        /*       } */
+        /*     } */
+    }
+
+}
+
+Communities::~Communities() {
+    delete[] members;
+    delete[] communities;
+    delete H;
+    if (min_delta_sigma) {
+        delete min_delta_sigma;
+    }
+
+    delete[] Probabilities::tmp_vector1;
+    delete[] Probabilities::tmp_vector2;
+    delete[] Probabilities::id;
+    delete[] Probabilities::vertices1;
+    delete[] Probabilities::vertices2;
+}
+
+float Community::min_delta_sigma() {
+    float r = 1.;
+    for (Neighbor* N = first_neighbor; N != 0;) {
+        if (N->delta_sigma < r) {
+            r = N->delta_sigma;
+        }
+        if (N->community1 == this_community) {
+            N = N->next_community1;
+        } else {
+            N = N->next_community2;
+        }
+    }
+    return r;
+}
+
+
+void Community::add_neighbor(Neighbor* N) { // add a new neighbor at the end of the list
+    if (last_neighbor) {
+        if (last_neighbor->community1 == this_community) {
+            last_neighbor->next_community1 = N;
+        } else {
+            last_neighbor->next_community2 = N;
+        }
+
+        if (N->community1 == this_community) {
+            N->previous_community1 = last_neighbor;
+        } else {
+            N->previous_community2 = last_neighbor;
+        }
+    } else {
+        first_neighbor = N;
+        if (N->community1 == this_community) {
+            N->previous_community1 = 0;
+        } else {
+            N->previous_community2 = 0;
+        }
+    }
+    last_neighbor = N;
+}
+
+void Community::remove_neighbor(Neighbor* N) {  // remove a neighbor from the list
+    if (N->community1 == this_community) {
+        if (N->next_community1) {
+//      if (N->next_community1->community1 == this_community)
+            N->next_community1->previous_community1 = N->previous_community1;
+//      else
+//  N->next_community1->previous_community2 = N->previous_community1;
+        } else {
+            last_neighbor = N->previous_community1;
+        }
+        if (N->previous_community1) {
+            if (N->previous_community1->community1 == this_community) {
+                N->previous_community1->next_community1 = N->next_community1;
+            } else {
+                N->previous_community1->next_community2 = N->next_community1;
+            }
+        } else {
+            first_neighbor = N->next_community1;
+        }
+    } else {
+        if (N->next_community2) {
+            if (N->next_community2->community1 == this_community) {
+                N->next_community2->previous_community1 = N->previous_community2;
+            } else {
+                N->next_community2->previous_community2 = N->previous_community2;
+            }
+        } else {
+            last_neighbor = N->previous_community2;
+        }
+        if (N->previous_community2) {
+//      if (N->previous_community2->community1 == this_community)
+//  N->previous_community2->next_community1 = N->next_community2;
+//      else
+            N->previous_community2->next_community2 = N->next_community2;
+        } else {
+            first_neighbor = N->next_community2;
+        }
+    }
+}
+
+void Communities::remove_neighbor(Neighbor* N) {
+    communities[N->community1].remove_neighbor(N);
+    communities[N->community2].remove_neighbor(N);
+    H->remove(N);
+
+    if (max_memory != -1) {
+        if (N->delta_sigma == min_delta_sigma->delta_sigma[N->community1]) {
+            min_delta_sigma->delta_sigma[N->community1] = communities[N->community1].min_delta_sigma();
+            if (communities[N->community1].P) {
+                min_delta_sigma->update(N->community1);
+            }
+        }
+
+        if (N->delta_sigma == min_delta_sigma->delta_sigma[N->community2]) {
+            min_delta_sigma->delta_sigma[N->community2] = communities[N->community2].min_delta_sigma();
+            if (communities[N->community2].P) {
+                min_delta_sigma->update(N->community2);
+            }
+        }
+    }
+}
+
+void Communities::add_neighbor(Neighbor* N) {
+    communities[N->community1].add_neighbor(N);
+    communities[N->community2].add_neighbor(N);
+    H->add(N);
+
+    if (max_memory != -1) {
+        if (N->delta_sigma < min_delta_sigma->delta_sigma[N->community1]) {
+            min_delta_sigma->delta_sigma[N->community1] = N->delta_sigma;
+            if (communities[N->community1].P) {
+                min_delta_sigma->update(N->community1);
+            }
+        }
+
+        if (N->delta_sigma < min_delta_sigma->delta_sigma[N->community2]) {
+            min_delta_sigma->delta_sigma[N->community2] = N->delta_sigma;
+            if (communities[N->community2].P) {
+                min_delta_sigma->update(N->community2);
+            }
+        }
+    }
+}
+
+void Communities::update_neighbor(Neighbor* N, float new_delta_sigma) {
+    if (max_memory != -1) {
+        if (new_delta_sigma < min_delta_sigma->delta_sigma[N->community1]) {
+            min_delta_sigma->delta_sigma[N->community1] = new_delta_sigma;
+            if (communities[N->community1].P) {
+                min_delta_sigma->update(N->community1);
+            }
+        }
+
+        if (new_delta_sigma < min_delta_sigma->delta_sigma[N->community2]) {
+            min_delta_sigma->delta_sigma[N->community2] = new_delta_sigma;
+            if (communities[N->community2].P) {
+                min_delta_sigma->update(N->community2);
+            }
+        }
+
+        float old_delta_sigma = N->delta_sigma;
+        N->delta_sigma = new_delta_sigma;
+        H->update(N);
+
+        if (old_delta_sigma == min_delta_sigma->delta_sigma[N->community1]) {
+            min_delta_sigma->delta_sigma[N->community1] = communities[N->community1].min_delta_sigma();
+            if (communities[N->community1].P) {
+                min_delta_sigma->update(N->community1);
+            }
+        }
+
+        if (old_delta_sigma == min_delta_sigma->delta_sigma[N->community2]) {
+            min_delta_sigma->delta_sigma[N->community2] = communities[N->community2].min_delta_sigma();
+            if (communities[N->community2].P) {
+                min_delta_sigma->update(N->community2);
+            }
+        }
+    } else {
+        N->delta_sigma = new_delta_sigma;
+        H->update(N);
+    }
+}
+
+void Communities::manage_memory() {
+    while ((memory_used > max_memory) && !min_delta_sigma->is_empty()) {
+        int c = min_delta_sigma->get_max_community();
+        delete communities[c].P;
+        communities[c].P = 0;
+        min_delta_sigma->remove_community(c);
+    }
+}
+
+
+
+void Communities::merge_communities(Neighbor* merge_N) {
+    int c1 = merge_N->community1;
+    int c2 = merge_N->community2;
+
+    communities[nb_communities].first_member = communities[c1].first_member;  // merge the
+    communities[nb_communities].last_member = communities[c2].last_member;    // two lists
+    members[communities[c1].last_member] = communities[c2].first_member;      // of members
+
+    communities[nb_communities].size = communities[c1].size + communities[c2].size;
+    communities[nb_communities].this_community = nb_communities;
+    communities[nb_communities].sub_community_of = 0;
+    communities[nb_communities].sub_communities[0] = c1;
+    communities[nb_communities].sub_communities[1] = c2;
+    communities[nb_communities].total_weight = communities[c1].total_weight + communities[c2].total_weight;
+    communities[nb_communities].internal_weight = communities[c1].internal_weight + communities[c2].internal_weight + merge_N->weight;
+    communities[nb_communities].sigma = communities[c1].sigma + communities[c2].sigma + merge_N->delta_sigma;
+
+    communities[c1].sub_community_of = nb_communities;
+    communities[c2].sub_community_of = nb_communities;
+
+// update the new probability vector...
+
+    if (communities[c1].P && communities[c2].P) {
+        communities[nb_communities].P = new Probabilities(c1, c2);
+    }
+
+    if (communities[c1].P) {
+        delete communities[c1].P;
+        communities[c1].P = 0;
+        if (max_memory != -1) {
+            min_delta_sigma->remove_community(c1);
+        }
+    }
+    if (communities[c2].P) {
+        delete communities[c2].P;
+        communities[c2].P = 0;
+        if (max_memory != -1) {
+            min_delta_sigma->remove_community(c2);
+        }
+    }
+
+    if (max_memory != -1) {
+        min_delta_sigma->delta_sigma[c1] = -1.;         // to avoid to update the min_delta_sigma for these communities
+        min_delta_sigma->delta_sigma[c2] = -1.;         //
+        min_delta_sigma->delta_sigma[nb_communities] = -1.;
+    }
+
+// update the new neighbors
+// by enumerating all the neighbors of c1 and c2
+
+    Neighbor* N1 = communities[c1].first_neighbor;
+    Neighbor* N2 = communities[c2].first_neighbor;
+
+    while (N1 && N2) {
+        int neighbor_community1;
+        int neighbor_community2;
+
+        if (N1->community1 == c1) {
+            neighbor_community1 = N1->community2;
+        } else {
+            neighbor_community1 = N1->community1;
+        }
+        if (N2->community1 == c2) {
+            neighbor_community2 = N2->community2;
+        } else {
+            neighbor_community2 = N2->community1;
+        }
+
+        if (neighbor_community1 < neighbor_community2) {
+            Neighbor* tmp = N1;
+            if (N1->community1 == c1) {
+                N1 = N1->next_community1;
+            } else {
+                N1 = N1->next_community2;
+            }
+            remove_neighbor(tmp);
+            Neighbor* N = new Neighbor;
+            N->weight = tmp->weight;
+            N->community1 = neighbor_community1;
+            N->community2 = nb_communities;
+            N->delta_sigma = (double(communities[c1].size + communities[neighbor_community1].size) * tmp->delta_sigma + double(communities[c2].size) * merge_N->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community1].size)); //compute_delta_sigma(neighbor_community1, nb_communities);
+            N->exact = false;
+            delete tmp;
+            add_neighbor(N);
+        }
+
+        if (neighbor_community2 < neighbor_community1) {
+            Neighbor* tmp = N2;
+            if (N2->community1 == c2) {
+                N2 = N2->next_community1;
+            } else {
+                N2 = N2->next_community2;
+            }
+            remove_neighbor(tmp);
+            Neighbor* N = new Neighbor;
+            N->weight = tmp->weight;
+            N->community1 = neighbor_community2;
+            N->community2 = nb_communities;
+            N->delta_sigma = (double(communities[c1].size) * merge_N->delta_sigma + double(communities[c2].size + communities[neighbor_community2].size) * tmp->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community2].size)); //compute_delta_sigma(neighbor_community2, nb_communities);
+            N->exact = false;
+            delete tmp;
+            add_neighbor(N);
+        }
+
+        if (neighbor_community1 == neighbor_community2) {
+            Neighbor* tmp1 = N1;
+            Neighbor* tmp2 = N2;
+            bool exact = N1->exact && N2->exact;
+            if (N1->community1 == c1) {
+                N1 = N1->next_community1;
+            } else {
+                N1 = N1->next_community2;
+            }
+            if (N2->community1 == c2) {
+                N2 = N2->next_community1;
+            } else {
+                N2 = N2->next_community2;
+            }
+            remove_neighbor(tmp1);
+            remove_neighbor(tmp2);
+            Neighbor* N = new Neighbor;
+            N->weight = tmp1->weight + tmp2->weight;
+            N->community1 = neighbor_community1;
+            N->community2 = nb_communities;
+            N->delta_sigma = (double(communities[c1].size + communities[neighbor_community1].size) * tmp1->delta_sigma + double(communities[c2].size + communities[neighbor_community1].size) * tmp2->delta_sigma - double(communities[neighbor_community1].size) * merge_N->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community1].size));
+            N->exact = exact;
+            delete tmp1;
+            delete tmp2;
+            add_neighbor(N);
+        }
+    }
+
+
+    if (!N1) {
+        while (N2) {
+//      double delta_sigma2 = N2->delta_sigma;
+            int neighbor_community;
+            if (N2->community1 == c2) {
+                neighbor_community = N2->community2;
+            } else {
+                neighbor_community = N2->community1;
+            }
+            Neighbor* tmp = N2;
+            if (N2->community1 == c2) {
+                N2 = N2->next_community1;
+            } else {
+                N2 = N2->next_community2;
+            }
+            remove_neighbor(tmp);
+            Neighbor* N = new Neighbor;
+            N->weight = tmp->weight;
+            N->community1 = neighbor_community;
+            N->community2 = nb_communities;
+            N->delta_sigma = (double(communities[c1].size) * merge_N->delta_sigma + double(communities[c2].size + communities[neighbor_community].size) * tmp->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community].size)); //compute_delta_sigma(neighbor_community, nb_communities);
+            N->exact = false;
+            delete tmp;
+            add_neighbor(N);
+        }
+    }
+    if (!N2) {
+        while (N1) {
+//      double delta_sigma1 = N1->delta_sigma;
+            int neighbor_community;
+            if (N1->community1 == c1) {
+                neighbor_community = N1->community2;
+            } else {
+                neighbor_community = N1->community1;
+            }
+            Neighbor* tmp = N1;
+            if (N1->community1 == c1) {
+                N1 = N1->next_community1;
+            } else {
+                N1 = N1->next_community2;
+            }
+            remove_neighbor(tmp);
+            Neighbor* N = new Neighbor;
+            N->weight = tmp->weight;
+            N->community1 = neighbor_community;
+            N->community2 = nb_communities;
+            N->delta_sigma = (double(communities[c1].size + communities[neighbor_community].size) * tmp->delta_sigma + double(communities[c2].size) * merge_N->delta_sigma) / (double(communities[c1].size + communities[c2].size + communities[neighbor_community].size)); //compute_delta_sigma(neighbor_community, nb_communities);
+            N->exact = false;
+            delete tmp;
+            add_neighbor(N);
+        }
+    }
+
+    if (max_memory != -1) {
+        min_delta_sigma->delta_sigma[nb_communities] = communities[nb_communities].min_delta_sigma();
+        min_delta_sigma->update(nb_communities);
+    }
+
+    nb_communities++;
+    nb_active_communities--;
+}
+
+double Communities::merge_nearest_communities() {
+    Neighbor* N = H->get_first();
+    while (!N->exact) {
+        update_neighbor(N, compute_delta_sigma(N->community1, N->community2));
+        N->exact = true;
+        N = H->get_first();
+        if (max_memory != -1) {
+            manage_memory();
+        }
+    }
+
+    double d = N->delta_sigma;
+    remove_neighbor(N);
+
+    merge_communities(N);
+    if (max_memory != -1) {
+        manage_memory();
+    }
+
+    if (merges) {
+        MATRIX(*merges, mergeidx, 0) = N->community1;
+        MATRIX(*merges, mergeidx, 1) = N->community2;
+        mergeidx++;
+    }
+
+    if (modularity) {
+        float Q = 0.;
+        for (int i = 0; i < nb_communities; i++) {
+            if (communities[i].sub_community_of == 0) {
+                Q += (communities[i].internal_weight - communities[i].total_weight * communities[i].total_weight / G->total_weight) / G->total_weight;
+            }
+        }
+        VECTOR(*modularity)[mergeidx] = Q;
+    }
+
+    delete N;
+
+    /* This could use igraph_progress */
+    /*   if(!silent) { */
+    /*     for(int k = (500*(G->nb_vertices - nb_active_communities - 1))/(G->nb_vertices-1) + 1; k <= (500*(G->nb_vertices - nb_active_communities))/(G->nb_vertices-1); k++) { */
+    /*       if(k % 50 == 1) {cerr.width(2); cerr << endl << k/ 5 << "% ";} */
+    /*       cerr << "."; */
+    /*     } */
+    /*   } */
+    return d;
+}
+
+double Communities::compute_delta_sigma(int community1, int community2) {
+    if (!communities[community1].P) {
+        communities[community1].P = new Probabilities(community1);
+        if (max_memory != -1) {
+            min_delta_sigma->update(community1);
+        }
+    }
+    if (!communities[community2].P) {
+        communities[community2].P = new Probabilities(community2);
+        if (max_memory != -1) {
+            min_delta_sigma->update(community2);
+        }
+    }
+
+    return communities[community1].P->compute_distance(communities[community2].P) * double(communities[community1].size) * double(communities[community2].size) / double(communities[community1].size + communities[community2].size);
+}
+
+}
+}    /* end of namespaces */
diff --git a/igraph/src/walktrap_graph.cpp b/igraph/src/walktrap_graph.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/walktrap_graph.cpp
@@ -0,0 +1,254 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Pascal Pons
+   The original copyright notice follows here. The FSF address was
+   fixed by Tamas Nepusz */
+
+// File: graph.cpp
+//-----------------------------------------------------------------------------
+// Walktrap v0.2 -- Finds community structure of networks using random walks
+// Copyright (C) 2004-2005 Pascal Pons
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+//-----------------------------------------------------------------------------
+// Author   : Pascal Pons
+// Email    : pascal.pons@gmail.com
+// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html
+// Location : Paris, France
+// Time     : June 2005
+//-----------------------------------------------------------------------------
+// see readme.txt for more details
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <algorithm>
+#include <cstring>      // strlen
+#include "walktrap_graph.h"
+
+#include "igraph_interface.h"
+
+using namespace std;
+
+namespace igraph {
+
+namespace walktrap {
+
+bool operator<(const Edge& E1, const Edge& E2) {
+    return (E1.neighbor < E2.neighbor);
+}
+
+
+Vertex::Vertex() {
+    degree = 0;
+    edges = 0;
+    total_weight = 0.;
+}
+
+Vertex::~Vertex() {
+    if (edges) {
+        delete[] edges;
+    }
+}
+
+Graph::Graph() {
+    nb_vertices = 0;
+    nb_edges = 0;
+    vertices = 0;
+    index = 0;
+    total_weight = 0.;
+}
+
+Graph::~Graph () {
+    if (vertices) {
+        delete[] vertices;
+    }
+}
+
+class Edge_list {
+public:
+    int* V1;
+    int* V2;
+    float* W;
+
+    int size;
+    int size_max;
+
+    void add(int v1, int v2, float w);
+    Edge_list() {
+        size = 0;
+        size_max = 1024;
+        V1 = new int[1024];
+        V2 = new int[1024];
+        W = new float[1024];
+    }
+    ~Edge_list() {
+        if (V1) {
+            delete[] V1;
+        }
+        if (V2) {
+            delete[] V2;
+        }
+        if (W) {
+            delete[] W;
+        }
+    }
+};
+
+void Edge_list::add(int v1, int v2, float w) {
+    if (size == size_max) {
+        int* tmp1 = new int[2 * size_max];
+        int* tmp2 = new int[2 * size_max];
+        float* tmp3 = new float[2 * size_max];
+        for (int i = 0; i < size_max; i++) {
+            tmp1[i] = V1[i];
+            tmp2[i] = V2[i];
+            tmp3[i] = W[i];
+        }
+        delete[] V1;
+        delete[] V2;
+        delete[] W;
+        V1 = tmp1;
+        V2 = tmp2;
+        W = tmp3;
+        size_max *= 2;
+    }
+    V1[size] = v1;
+    V2[size] = v2;
+    W[size] = w;
+    size++;
+}
+
+int Graph::convert_from_igraph(const igraph_t *graph,
+                               const igraph_vector_t *weights) {
+    Graph &G = *this;
+
+    int max_vertex = (int)igraph_vcount(graph) - 1;
+    long int no_of_edges = (long int)igraph_ecount(graph);
+    long int i;
+    long int deg;
+    double w;
+
+    Edge_list EL;
+
+    for (i = 0; i < no_of_edges; i++) {
+        igraph_integer_t from, to;
+        int v1, v2;
+        w = weights ? VECTOR(*weights)[i] : 1.0;
+        igraph_edge(graph, i, &from, &to);
+        v1 = (int)from; v2 = (int)to;
+        EL.add(v1, v2, w);
+    }
+
+    G.nb_vertices = max_vertex + 1;
+    G.vertices = new Vertex[G.nb_vertices];
+    G.nb_edges = 0;
+    G.total_weight = 0.0;
+
+    for (int i = 0; i < EL.size; i++) {
+        G.vertices[EL.V1[i]].degree++;
+        G.vertices[EL.V2[i]].degree++;
+        G.vertices[EL.V1[i]].total_weight += EL.W[i];
+        G.vertices[EL.V2[i]].total_weight += EL.W[i];
+        G.nb_edges++;
+        G.total_weight += EL.W[i];
+    }
+
+    for (int i = 0; i < G.nb_vertices; i++) {
+        deg = G.vertices[i].degree;
+        w = (deg == 0) ? 1.0 : (G.vertices[i].total_weight / double(deg));
+        G.vertices[i].edges = new Edge[deg + 1];
+        G.vertices[i].edges[0].neighbor = i;
+        G.vertices[i].edges[0].weight = w;
+        G.vertices[i].total_weight += w;
+        G.vertices[i].degree = 1;
+    }
+
+    for (int i = 0; i < EL.size; i++) {
+        G.vertices[EL.V1[i]].edges[G.vertices[EL.V1[i]].degree].neighbor = EL.V2[i];
+        G.vertices[EL.V1[i]].edges[G.vertices[EL.V1[i]].degree].weight = EL.W[i];
+        G.vertices[EL.V1[i]].degree++;
+        G.vertices[EL.V2[i]].edges[G.vertices[EL.V2[i]].degree].neighbor = EL.V1[i];
+        G.vertices[EL.V2[i]].edges[G.vertices[EL.V2[i]].degree].weight = EL.W[i];
+        G.vertices[EL.V2[i]].degree++;
+    }
+
+    for (int i = 0; i < G.nb_vertices; i++) {
+        sort(G.vertices[i].edges, G.vertices[i].edges + G.vertices[i].degree);
+    }
+
+    for (int i = 0; i < G.nb_vertices; i++) { // merge multi edges
+        int a = 0;
+        for (int b = 1; b < G.vertices[i].degree; b++) {
+            if (G.vertices[i].edges[b].neighbor == G.vertices[i].edges[a].neighbor) {
+                G.vertices[i].edges[a].weight += G.vertices[i].edges[b].weight;
+            } else {
+                G.vertices[i].edges[++a] = G.vertices[i].edges[b];
+            }
+        }
+        G.vertices[i].degree = a + 1;
+    }
+
+    return 0;
+}
+
+long Graph::memory() {
+    size_t m = 0;
+    m += size_t(nb_vertices) * sizeof(Vertex);
+    m += 2 * size_t(nb_edges) * sizeof(Edge);
+    m += sizeof(Graph);
+    if (index != 0) {
+        m += size_t(nb_vertices) * sizeof(char*);
+        for (int i = 0; i < nb_vertices; i++) {
+            m += strlen(index[i]) + 1;
+        }
+    }
+    return m;
+}
+
+}
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/igraph/src/walktrap_heap.cpp b/igraph/src/walktrap_heap.cpp
new file mode 100644
--- /dev/null
+++ b/igraph/src/walktrap_heap.cpp
@@ -0,0 +1,245 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* The original version of this file was written by Pascal Pons
+   The original copyright notice follows here. The FSF address was
+   fixed by Tamas Nepusz */
+
+// File: heap.cpp
+//-----------------------------------------------------------------------------
+// Walktrap v0.2 -- Finds community structure of networks using random walks
+// Copyright (C) 2004-2005 Pascal Pons
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+//-----------------------------------------------------------------------------
+// Author   : Pascal Pons
+// Email    : pascal.pons@gmail.com
+// Web page : http://www-rp.lip6.fr/~latapy/PP/walktrap.html
+// Location : Paris, France
+// Time     : June 2005
+//-----------------------------------------------------------------------------
+// see readme.txt for more details
+
+#include "walktrap_heap.h"
+#include <cstdlib>
+#include <iostream>
+
+
+using namespace std;
+using namespace igraph::walktrap;
+
+void Neighbor_heap::move_up(int index) {
+    while (H[index / 2]->delta_sigma > H[index]->delta_sigma) {
+        Neighbor* tmp = H[index / 2];
+        H[index]->heap_index = index / 2;
+        H[index / 2] = H[index];
+        tmp->heap_index = index;
+        H[index] = tmp;
+        index = index / 2;
+    }
+}
+
+void Neighbor_heap::move_down(int index) {
+    while (true) {
+        int min = index;
+        if ((2 * index < size) && (H[2 * index]->delta_sigma < H[min]->delta_sigma)) {
+            min = 2 * index;
+        }
+        if (2 * index + 1 < size && H[2 * index + 1]->delta_sigma < H[min]->delta_sigma) {
+            min = 2 * index + 1;
+        }
+        if (min != index) {
+            Neighbor* tmp = H[min];
+            H[index]->heap_index = min;
+            H[min] = H[index];
+            tmp->heap_index = index;
+            H[index] = tmp;
+            index = min;
+        } else {
+            break;
+        }
+    }
+}
+
+Neighbor* Neighbor_heap::get_first() {
+    if (size == 0) {
+        return 0;
+    } else {
+        return H[0];
+    }
+}
+
+void Neighbor_heap::remove(Neighbor* N) {
+    if (N->heap_index == -1 || size == 0) {
+        return;
+    }
+    Neighbor* last_N = H[--size];
+    H[N->heap_index] = last_N;
+    last_N->heap_index = N->heap_index;
+    move_up(last_N->heap_index);
+    move_down(last_N->heap_index);
+    N->heap_index = -1;
+}
+
+void Neighbor_heap::add(Neighbor* N) {
+    if (size >= max_size) {
+        return;
+    }
+    N->heap_index = size++;
+    H[N->heap_index] = N;
+    move_up(N->heap_index);
+}
+
+void Neighbor_heap::update(Neighbor* N) {
+    if (N->heap_index == -1) {
+        return;
+    }
+    move_up(N->heap_index);
+    move_down(N->heap_index);
+}
+
+long Neighbor_heap::memory() {
+    return (sizeof(Neighbor_heap) + long(max_size) * sizeof(Neighbor*));
+}
+
+Neighbor_heap::Neighbor_heap(int max_s) {
+    max_size = max_s;
+    size = 0;
+    H = new Neighbor*[max_s];
+}
+
+Neighbor_heap::~Neighbor_heap() {
+    delete[] H;
+}
+
+bool Neighbor_heap::is_empty() {
+    return (size == 0);
+}
+
+
+
+//#################################################################
+
+void Min_delta_sigma_heap::move_up(int index) {
+    while (delta_sigma[H[index / 2]] < delta_sigma[H[index]]) {
+        int tmp = H[index / 2];
+        I[H[index]] = index / 2;
+        H[index / 2] = H[index];
+        I[tmp] = index;
+        H[index] = tmp;
+        index = index / 2;
+    }
+}
+
+void Min_delta_sigma_heap::move_down(int index) {
+    while (true) {
+        int max = index;
+        if (2 * index < size && delta_sigma[H[2 * index]] > delta_sigma[H[max]]) {
+            max = 2 * index;
+        }
+        if (2 * index + 1 < size && delta_sigma[H[2 * index + 1]] > delta_sigma[H[max]]) {
+            max = 2 * index + 1;
+        }
+        if (max != index) {
+            int tmp = H[max];
+            I[H[index]] = max;
+            H[max] = H[index];
+            I[tmp] = index;
+            H[index] = tmp;
+            index = max;
+        } else {
+            break;
+        }
+    }
+}
+
+int Min_delta_sigma_heap::get_max_community() {
+    if (size == 0) {
+        return -1;
+    } else {
+        return H[0];
+    }
+}
+
+void Min_delta_sigma_heap::remove_community(int community) {
+    if (I[community] == -1 || size == 0) {
+        return;
+    }
+    int last_community = H[--size];
+    H[I[community]] = last_community;
+    I[last_community] = I[community];
+    move_up(I[last_community]);
+    move_down(I[last_community]);
+    I[community] = -1;
+}
+
+void Min_delta_sigma_heap::update(int community) {
+    if (community < 0 || community >= max_size) {
+        return;
+    }
+    if (I[community] == -1) {
+        I[community] = size++;
+        H[I[community]] = community;
+    }
+    move_up(I[community]);
+    move_down(I[community]);
+}
+
+long Min_delta_sigma_heap::memory() {
+    return (sizeof(Min_delta_sigma_heap) + long(max_size) * (2 * sizeof(int) + sizeof(float)));
+}
+
+Min_delta_sigma_heap::Min_delta_sigma_heap(int max_s) {
+    max_size = max_s;
+    size = 0;
+    H = new int[max_s];
+    I = new int[max_s];
+    delta_sigma = new float[max_s];
+    for (int i = 0; i < max_size; i++) {
+        I[i] = -1;
+        delta_sigma[i] = 1.;
+    }
+}
+
+Min_delta_sigma_heap::~Min_delta_sigma_heap() {
+    delete[] H;
+    delete[] I;
+    delete[] delta_sigma;
+}
+
+bool Min_delta_sigma_heap::is_empty() {
+    return (size == 0);
+}
diff --git a/igraph/src/wref.c b/igraph/src/wref.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/wref.c
@@ -0,0 +1,294 @@
+#include "f2c.h"
+#include "fio.h"
+
+#ifndef KR_headers
+#undef abs
+#undef min
+#undef max
+#include "stdlib.h"
+#include "string.h"
+#endif
+
+#include "fmt.h"
+#include "fp.h"
+#ifndef VAX
+#include "ctype.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#endif
+
+ int
+#ifdef KR_headers
+wrt_E(p,w,d,e,len) ufloat *p; ftnlen len;
+#else
+wrt_E(ufloat *p, int w, int d, int e, ftnlen len)
+#endif
+{
+	char buf[FMAX+EXPMAXDIGS+4], *s, *se;
+	int d1, delta, e1, i, sign, signspace;
+	double dd;
+#ifdef WANT_LEAD_0
+	int insert0 = 0;
+#endif
+#ifndef VAX
+	int e0 = e;
+#endif
+
+	if(e <= 0)
+		e = 2;
+	if(f__scale) {
+		if(f__scale >= d + 2 || f__scale <= -d)
+			goto nogood;
+		}
+	if(f__scale <= 0)
+		--d;
+	if (len == sizeof(real))
+		dd = p->pf;
+	else
+		dd = p->pd;
+	if (dd < 0.) {
+		signspace = sign = 1;
+		dd = -dd;
+		}
+	else {
+		sign = 0;
+		signspace = (int)f__cplus;
+#ifndef VAX
+		if (!dd) {
+#ifdef SIGNED_ZEROS
+			if (signbit_f2c(&dd))
+				signspace = sign = 1;
+#endif
+			dd = 0.;	/* avoid -0 */
+			}
+#endif
+		}
+	delta = w - (2 /* for the . and the d adjustment above */
+			+ 2 /* for the E+ */ + signspace + d + e);
+#ifdef WANT_LEAD_0
+	if (f__scale <= 0 && delta > 0) {
+		delta--;
+		insert0 = 1;
+		}
+	else
+#endif
+	if (delta < 0) {
+nogood:
+		while(--w >= 0)
+			PUT('*');
+		return(0);
+		}
+	if (f__scale < 0)
+		d += f__scale;
+	if (d > FMAX) {
+		d1 = d - FMAX;
+		d = FMAX;
+		}
+	else
+		d1 = 0;
+	sprintf(buf,"%#.*E", d, dd);
+#ifndef VAX
+	/* check for NaN, Infinity */
+	if (!isdigit(buf[0])) {
+		switch(buf[0]) {
+			case 'n':
+			case 'N':
+				signspace = 0;	/* no sign for NaNs */
+			}
+		delta = w - strlen(buf) - signspace;
+		if (delta < 0)
+			goto nogood;
+		while(--delta >= 0)
+			PUT(' ');
+		if (signspace)
+			PUT(sign ? '-' : '+');
+		for(s = buf; *s; s++)
+			PUT(*s);
+		return 0;
+		}
+#endif
+	se = buf + d + 3;
+#ifdef GOOD_SPRINTF_EXPONENT /* When possible, exponent has 2 digits. */
+	if (f__scale != 1 && dd)
+		sprintf(se, "%+.2d", atoi(se) + 1 - f__scale);
+#else
+	if (dd)
+		sprintf(se, "%+.2d", atoi(se) + 1 - f__scale);
+	else
+		strcpy(se, "+00");
+#endif
+	s = ++se;
+	if (e < 2) {
+		if (*s != '0')
+			goto nogood;
+		}
+#ifndef VAX
+	/* accommodate 3 significant digits in exponent */
+	if (s[2]) {
+#ifdef Pedantic
+		if (!e0 && !s[3])
+			for(s -= 2, e1 = 2; s[0] = s[1]; s++);
+
+	/* Pedantic gives the behavior that Fortran 77 specifies,	*/
+	/* i.e., requires that E be specified for exponent fields	*/
+	/* of more than 3 digits.  With Pedantic undefined, we get	*/
+	/* the behavior that Cray displays -- you get a bigger		*/
+	/* exponent field if it fits.	*/
+#else
+		if (!e0) {
+			for(s -= 2, e1 = 2; s[0] = s[1]; s++)
+#ifdef CRAY
+				delta--;
+			if ((delta += 4) < 0)
+				goto nogood
+#endif
+				;
+			}
+#endif
+		else if (e0 >= 0)
+			goto shift;
+		else
+			e1 = e;
+		}
+	else
+ shift:
+#endif
+		for(s += 2, e1 = 2; *s; ++e1, ++s)
+			if (e1 >= e)
+				goto nogood;
+	while(--delta >= 0)
+		PUT(' ');
+	if (signspace)
+		PUT(sign ? '-' : '+');
+	s = buf;
+	i = f__scale;
+	if (f__scale <= 0) {
+#ifdef WANT_LEAD_0
+		if (insert0)
+			PUT('0');
+#endif
+		PUT('.');
+		for(; i < 0; ++i)
+			PUT('0');
+		PUT(*s);
+		s += 2;
+		}
+	else if (f__scale > 1) {
+		PUT(*s);
+		s += 2;
+		while(--i > 0)
+			PUT(*s++);
+		PUT('.');
+		}
+	if (d1) {
+		se -= 2;
+		while(s < se) PUT(*s++);
+		se += 2;
+		do PUT('0'); while(--d1 > 0);
+		}
+	while(s < se)
+		PUT(*s++);
+	if (e < 2)
+		PUT(s[1]);
+	else {
+		while(++e1 <= e)
+			PUT('0');
+		while(*s)
+			PUT(*s++);
+		}
+	return 0;
+	}
+
+ int
+#ifdef KR_headers
+wrt_F(p,w,d,len) ufloat *p; ftnlen len;
+#else
+wrt_F(ufloat *p, int w, int d, ftnlen len)
+#endif
+{
+	int d1, sign, n;
+	double x;
+	char *b, buf[MAXINTDIGS+MAXFRACDIGS+4], *s;
+
+	x= (len==sizeof(real)?p->pf:p->pd);
+	if (d < MAXFRACDIGS)
+		d1 = 0;
+	else {
+		d1 = d - MAXFRACDIGS;
+		d = MAXFRACDIGS;
+		}
+	if (x < 0.)
+		{ x = -x; sign = 1; }
+	else {
+		sign = 0;
+#ifndef VAX
+		if (!x) {
+#ifdef SIGNED_ZEROS
+			if (signbit_f2c(&x))
+				sign = 2;
+#endif
+			x = 0.;
+			}
+#endif
+		}
+
+	if (n = f__scale)
+		if (n > 0)
+			do x *= 10.; while(--n > 0);
+		else
+			do x *= 0.1; while(++n < 0);
+
+#ifdef USE_STRLEN
+	sprintf(b = buf, "%#.*f", d, x);
+	n = strlen(b) + d1;
+#else
+	n = sprintf(b = buf, "%#.*f", d, x) + d1;
+#endif
+
+#ifndef WANT_LEAD_0
+	if (buf[0] == '0' && d)
+		{ ++b; --n; }
+#endif
+	if (sign == 1) {
+		/* check for all zeros */
+		for(s = b;;) {
+			while(*s == '0') s++;
+			switch(*s) {
+				case '.':
+					s++; continue;
+				case 0:
+					sign = 0;
+				}
+			break;
+			}
+		}
+	if (sign || f__cplus)
+		++n;
+	if (n > w) {
+#ifdef WANT_LEAD_0
+		if (buf[0] == '0' && --n == w)
+			++b;
+		else
+#endif
+		{
+			while(--w >= 0)
+				PUT('*');
+			return 0;
+			}
+		}
+	for(w -= n; --w >= 0; )
+		PUT(' ');
+	if (sign)
+		PUT('-');
+	else if (f__cplus)
+		PUT('+');
+	while(n = *b++)
+		PUT(n);
+	while(--d1 >= 0)
+		PUT('0');
+	return 0;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/wrtfmt.c b/igraph/src/wrtfmt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/wrtfmt.c
@@ -0,0 +1,377 @@
+#include "f2c.h"
+#include "fio.h"
+#include "fmt.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern icilist *f__svic;
+extern char *f__icptr;
+
+ static int
+mv_cur(Void)	/* shouldn't use fseek because it insists on calling fflush */
+		/* instead we know too much about stdio */
+{
+	int cursor = f__cursor;
+	f__cursor = 0;
+	if(f__external == 0) {
+		if(cursor < 0) {
+			if(f__hiwater < f__recpos)
+				f__hiwater = f__recpos;
+			f__recpos += cursor;
+			f__icptr += cursor;
+			if(f__recpos < 0)
+				err(f__elist->cierr, 110, "left off");
+		}
+		else if(cursor > 0) {
+			if(f__recpos + cursor >= f__svic->icirlen)
+				err(f__elist->cierr, 110, "recend");
+			if(f__hiwater <= f__recpos)
+				for(; cursor > 0; cursor--)
+					(*f__putn)(' ');
+			else if(f__hiwater <= f__recpos + cursor) {
+				cursor -= f__hiwater - f__recpos;
+				f__icptr += f__hiwater - f__recpos;
+				f__recpos = f__hiwater;
+				for(; cursor > 0; cursor--)
+					(*f__putn)(' ');
+			}
+			else {
+				f__icptr += cursor;
+				f__recpos += cursor;
+			}
+		}
+		return(0);
+	}
+	if (cursor > 0) {
+		if(f__hiwater <= f__recpos)
+			for(;cursor>0;cursor--) (*f__putn)(' ');
+		else if(f__hiwater <= f__recpos + cursor) {
+			cursor -= f__hiwater - f__recpos;
+			f__recpos = f__hiwater;
+			for(; cursor > 0; cursor--)
+				(*f__putn)(' ');
+		}
+		else {
+			f__recpos += cursor;
+		}
+	}
+	else if (cursor < 0)
+	{
+		if(cursor + f__recpos < 0)
+			err(f__elist->cierr,110,"left off");
+		if(f__hiwater < f__recpos)
+			f__hiwater = f__recpos;
+		f__recpos += cursor;
+	}
+	return(0);
+}
+
+ static int
+#ifdef KR_headers
+wrt_Z(n,w,minlen,len) Uint *n; int w, minlen; ftnlen len;
+#else
+wrt_Z(Uint *n, int w, int minlen, ftnlen len)
+#endif
+{
+	register char *s, *se;
+	register int i, w1;
+	static int one = 1;
+	static char hex[] = "0123456789ABCDEF";
+	s = (char *)n;
+	--len;
+	if (*(char *)&one) {
+		/* little endian */
+		se = s;
+		s += len;
+		i = -1;
+		}
+	else {
+		se = s + len;
+		i = 1;
+		}
+	for(;; s += i)
+		if (s == se || *s)
+			break;
+	w1 = (i*(se-s) << 1) + 1;
+	if (*s & 0xf0)
+		w1++;
+	if (w1 > w)
+		for(i = 0; i < w; i++)
+			(*f__putn)('*');
+	else {
+		if ((minlen -= w1) > 0)
+			w1 += minlen;
+		while(--w >= w1)
+			(*f__putn)(' ');
+		while(--minlen >= 0)
+			(*f__putn)('0');
+		if (!(*s & 0xf0)) {
+			(*f__putn)(hex[*s & 0xf]);
+			if (s == se)
+				return 0;
+			s += i;
+			}
+		for(;; s += i) {
+			(*f__putn)(hex[*s >> 4 & 0xf]);
+			(*f__putn)(hex[*s & 0xf]);
+			if (s == se)
+				break;
+			}
+		}
+	return 0;
+	}
+
+ static int
+#ifdef KR_headers
+wrt_I(n,w,len, base) Uint *n; ftnlen len; register int base;
+#else
+wrt_I(Uint *n, int w, ftnlen len, register int base)
+#endif
+{	int ndigit,sign,spare,i;
+	longint x;
+	char *ans;
+	if(len==sizeof(integer)) x=n->il;
+	else if(len == sizeof(char)) x = n->ic;
+#ifdef Allow_TYQUAD
+	else if (len == sizeof(longint)) x = n->ili;
+#endif
+	else x=n->is;
+	ans=f__icvt(x,&ndigit,&sign, base);
+	spare=w-ndigit;
+	if(sign || f__cplus) spare--;
+	if(spare<0)
+		for(i=0;i<w;i++) (*f__putn)('*');
+	else
+	{	for(i=0;i<spare;i++) (*f__putn)(' ');
+		if(sign) (*f__putn)('-');
+		else if(f__cplus) (*f__putn)('+');
+		for(i=0;i<ndigit;i++) (*f__putn)(*ans++);
+	}
+	return(0);
+}
+ static int
+#ifdef KR_headers
+wrt_IM(n,w,m,len,base) Uint *n; ftnlen len; int base;
+#else
+wrt_IM(Uint *n, int w, int m, ftnlen len, int base)
+#endif
+{	int ndigit,sign,spare,i,xsign;
+	longint x;
+	char *ans;
+	if(sizeof(integer)==len) x=n->il;
+	else if(len == sizeof(char)) x = n->ic;
+#ifdef Allow_TYQUAD
+	else if (len == sizeof(longint)) x = n->ili;
+#endif
+	else x=n->is;
+	ans=f__icvt(x,&ndigit,&sign, base);
+	if(sign || f__cplus) xsign=1;
+	else xsign=0;
+	if(ndigit+xsign>w || m+xsign>w)
+	{	for(i=0;i<w;i++) (*f__putn)('*');
+		return(0);
+	}
+	if(x==0 && m==0)
+	{	for(i=0;i<w;i++) (*f__putn)(' ');
+		return(0);
+	}
+	if(ndigit>=m)
+		spare=w-ndigit-xsign;
+	else
+		spare=w-m-xsign;
+	for(i=0;i<spare;i++) (*f__putn)(' ');
+	if(sign) (*f__putn)('-');
+	else if(f__cplus) (*f__putn)('+');
+	for(i=0;i<m-ndigit;i++) (*f__putn)('0');
+	for(i=0;i<ndigit;i++) (*f__putn)(*ans++);
+	return(0);
+}
+ static int
+#ifdef KR_headers
+wrt_AP(s) char *s;
+#else
+wrt_AP(char *s)
+#endif
+{	char quote;
+	int i;
+
+	if(f__cursor && (i = mv_cur()))
+		return i;
+	quote = *s++;
+	for(;*s;s++)
+	{	if(*s!=quote) (*f__putn)(*s);
+		else if(*++s==quote) (*f__putn)(*s);
+		else return(1);
+	}
+	return(1);
+}
+ static int
+#ifdef KR_headers
+wrt_H(a,s) char *s;
+#else
+wrt_H(int a, char *s)
+#endif
+{
+	int i;
+
+	if(f__cursor && (i = mv_cur()))
+		return i;
+	while(a--) (*f__putn)(*s++);
+	return(1);
+}
+
+ int
+#ifdef KR_headers
+wrt_L(n,len, sz) Uint *n; ftnlen sz;
+#else
+wrt_L(Uint *n, int len, ftnlen sz)
+#endif
+{	int i;
+	long x;
+	if(sizeof(long)==sz) x=n->il;
+	else if(sz == sizeof(char)) x = n->ic;
+	else x=n->is;
+	for(i=0;i<len-1;i++)
+		(*f__putn)(' ');
+	if(x) (*f__putn)('T');
+	else (*f__putn)('F');
+	return(0);
+}
+ static int
+#ifdef KR_headers
+wrt_A(p,len) char *p; ftnlen len;
+#else
+wrt_A(char *p, ftnlen len)
+#endif
+{
+	while(len-- > 0) (*f__putn)(*p++);
+	return(0);
+}
+ static int
+#ifdef KR_headers
+wrt_AW(p,w,len) char * p; ftnlen len;
+#else
+wrt_AW(char * p, int w, ftnlen len)
+#endif
+{
+	while(w>len)
+	{	w--;
+		(*f__putn)(' ');
+	}
+	while(w-- > 0)
+		(*f__putn)(*p++);
+	return(0);
+}
+
+ static int
+#ifdef KR_headers
+wrt_G(p,w,d,e,len) ufloat *p; ftnlen len;
+#else
+wrt_G(ufloat *p, int w, int d, int e, ftnlen len)
+#endif
+{	double up = 1,x;
+	int i=0,oldscale,n,j;
+	x = len==sizeof(real)?p->pf:p->pd;
+	if(x < 0 ) x = -x;
+	if(x<.1) {
+		if (x != 0.)
+			return(wrt_E(p,w,d,e,len));
+		i = 1;
+		goto have_i;
+		}
+	for(;i<=d;i++,up*=10)
+	{	if(x>=up) continue;
+ have_i:
+		oldscale = f__scale;
+		f__scale = 0;
+		if(e==0) n=4;
+		else	n=e+2;
+		i=wrt_F(p,w-n,d-i,len);
+		for(j=0;j<n;j++) (*f__putn)(' ');
+		f__scale=oldscale;
+		return(i);
+	}
+	return(wrt_E(p,w,d,e,len));
+}
+
+ int
+#ifdef KR_headers
+w_ed(p,ptr,len) struct syl *p; char *ptr; ftnlen len;
+#else
+w_ed(struct syl *p, char *ptr, ftnlen len)
+#endif
+{
+	int i;
+
+	if(f__cursor && (i = mv_cur()))
+		return i;
+	switch(p->op)
+	{
+	default:
+		fprintf(stderr,"w_ed, unexpected code: %d\n", p->op);
+		sig_die(f__fmtbuf, 1);
+	case I:	return(wrt_I((Uint *)ptr,p->p1,len, 10));
+	case IM:
+		return(wrt_IM((Uint *)ptr,p->p1,p->p2.i[0],len,10));
+
+		/* O and OM don't work right for character, double, complex, */
+		/* or doublecomplex, and they differ from Fortran 90 in */
+		/* showing a minus sign for negative values. */
+
+	case O:	return(wrt_I((Uint *)ptr, p->p1, len, 8));
+	case OM:
+		return(wrt_IM((Uint *)ptr,p->p1,p->p2.i[0],len,8));
+	case L:	return(wrt_L((Uint *)ptr,p->p1, len));
+	case A: return(wrt_A(ptr,len));
+	case AW:
+		return(wrt_AW(ptr,p->p1,len));
+	case D:
+	case E:
+	case EE:
+		return(wrt_E((ufloat *)ptr,p->p1,p->p2.i[0],p->p2.i[1],len));
+	case G:
+	case GE:
+		return(wrt_G((ufloat *)ptr,p->p1,p->p2.i[0],p->p2.i[1],len));
+	case F:	return(wrt_F((ufloat *)ptr,p->p1,p->p2.i[0],len));
+
+		/* Z and ZM assume 8-bit bytes. */
+
+	case Z: return(wrt_Z((Uint *)ptr,p->p1,0,len));
+	case ZM:
+		return(wrt_Z((Uint *)ptr,p->p1,p->p2.i[0],len));
+	}
+}
+
+ int
+#ifdef KR_headers
+w_ned(p) struct syl *p;
+#else
+w_ned(struct syl *p)
+#endif
+{
+	switch(p->op)
+	{
+	default: fprintf(stderr,"w_ned, unexpected code: %d\n", p->op);
+		sig_die(f__fmtbuf, 1);
+	case SLASH:
+		return((*f__donewrec)());
+	case T: f__cursor = p->p1-f__recpos - 1;
+		return(1);
+	case TL: f__cursor -= p->p1;
+		if(f__cursor < -f__recpos)	/* TL1000, 1X */
+			f__cursor = -f__recpos;
+		return(1);
+	case TR:
+	case X:
+		f__cursor += p->p1;
+		return(1);
+	case APOS:
+		return(wrt_AP(p->p2.s));
+	case H:
+		return(wrt_H(p->p1,p->p2.s));
+	}
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/wsfe.c b/igraph/src/wsfe.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/wsfe.c
@@ -0,0 +1,78 @@
+/*write sequential formatted external*/
+#include "f2c.h"
+#include "fio.h"
+#include "fmt.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ int
+x_wSL(Void)
+{
+	int n = f__putbuf('\n');
+	f__hiwater = f__recpos = f__cursor = 0;
+	return(n == 0);
+}
+
+ static int
+xw_end(Void)
+{
+	int n;
+
+	if(f__nonl) {
+		f__putbuf(n = 0);
+		fflush(f__cf);
+		}
+	else
+		n = f__putbuf('\n');
+	f__hiwater = f__recpos = f__cursor = 0;
+	return n;
+}
+
+ static int
+xw_rev(Void)
+{
+	int n = 0;
+	if(f__workdone) {
+		n = f__putbuf('\n');
+		f__workdone = 0;
+		}
+	f__hiwater = f__recpos = f__cursor = 0;
+	return n;
+}
+
+#ifdef KR_headers
+integer s_wsfe(a) cilist *a;	/*start*/
+#else
+integer s_wsfe(cilist *a)	/*start*/
+#endif
+{	int n;
+	if(!f__init) f_init();
+	f__reading=0;
+	f__sequential=1;
+	f__formatted=1;
+	f__external=1;
+	if(n=c_sfe(a)) return(n);
+	f__elist=a;
+	f__hiwater = f__cursor=f__recpos=0;
+	f__nonl = 0;
+	f__scale=0;
+	f__fmtbuf=a->cifmt;
+	f__cf=f__curunit->ufd;
+	if(pars_f(f__fmtbuf)<0) err(a->cierr,100,"startio");
+	f__putn= x_putc;
+	f__doed= w_ed;
+	f__doned= w_ned;
+	f__doend=xw_end;
+	f__dorevert=xw_rev;
+	f__donewrec=x_wSL;
+	fmt_bg();
+	f__cplus=0;
+	f__cblank=f__curunit->ublnk;
+	if(f__curunit->uwrt != 1 && f__nowwriting(f__curunit))
+		err(a->cierr,errno,"write start");
+	return(0);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/wsle.c b/igraph/src/wsle.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/wsle.c
@@ -0,0 +1,42 @@
+#include "f2c.h"
+#include "fio.h"
+#include "fmt.h"
+#include "lio.h"
+#include "string.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+integer s_wsle(a) cilist *a;
+#else
+integer s_wsle(cilist *a)
+#endif
+{
+	int n;
+	if(n=c_le(a)) return(n);
+	f__reading=0;
+	f__external=1;
+	f__formatted=1;
+	f__putn = x_putc;
+	f__lioproc = l_write;
+	L_len = LINE;
+	f__donewrec = x_wSL;
+	if(f__curunit->uwrt != 1 && f__nowwriting(f__curunit))
+		err(a->cierr, errno, "list output start");
+	return(0);
+	}
+
+integer e_wsle(Void)
+{
+	int n = f__putbuf('\n');
+	f__recpos=0;
+#ifdef ALWAYS_FLUSH
+	if (!n && fflush(f__cf))
+		err(f__elist->cierr, errno, "write end");
+#endif
+	return(n);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/wsne.c b/igraph/src/wsne.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/wsne.c
@@ -0,0 +1,32 @@
+#include "f2c.h"
+#include "fio.h"
+#include "lio.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ integer
+#ifdef KR_headers
+s_wsne(a) cilist *a;
+#else
+s_wsne(cilist *a)
+#endif
+{
+	int n;
+
+	if(n=c_le(a))
+		return(n);
+	f__reading=0;
+	f__external=1;
+	f__formatted=1;
+	f__putn = x_putc;
+	L_len = LINE;
+	f__donewrec = x_wSL;
+	if(f__curunit->uwrt != 1 && f__nowwriting(f__curunit))
+		err(a->cierr, errno, "namelist output start");
+	x_wsne(a);
+	return e_wsle();
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/xerbla.c b/igraph/src/xerbla.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/xerbla.c
@@ -0,0 +1,129 @@
+/*  -- translated by f2c (version 20100827).
+   You must link the resulting object file with libf2c:
+	on Microsoft Windows system, link with libf2c.lib;
+	on Linux or Unix systems, link with .../path/to/libf2c.a -lm
+	or, if you install libf2c.a in a standard place, with -lf2c -lm
+	-- in that order, at the end of the command line, as in
+		cc *.o -lf2c -lm
+	Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,
+
+		http://www.netlib.org/f2c/libf2c.zip
+*/
+
+#include "f2c.h"
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* > \brief \b XERBLA   
+
+    =========== DOCUMENTATION ===========   
+
+   Online html documentation available at   
+              http://www.netlib.org/lapack/explore-html/   
+
+   > \htmlonly   
+   > Download XERBLA + dependencies   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/xerbla.
+f">   
+   > [TGZ]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/xerbla.
+f">   
+   > [ZIP]</a>   
+   > <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/xerbla.
+f">   
+   > [TXT]</a>   
+   > \endhtmlonly   
+
+    Definition:   
+    ===========   
+
+         SUBROUTINE XERBLA( SRNAME, INFO )   
+
+         CHARACTER*(*)      SRNAME   
+         INTEGER            INFO   
+
+
+   > \par Purpose:   
+    =============   
+   >   
+   > \verbatim   
+   >   
+   > XERBLA  is an error handler for the LAPACK routines.   
+   > It is called by an LAPACK routine if an input parameter has an   
+   > invalid value.  A message is printed and execution stops.   
+   >   
+   > Installers may consider modifying the STOP statement in order to   
+   > call system-specific exception-handling facilities.   
+   > \endverbatim   
+
+    Arguments:   
+    ==========   
+
+   > \param[in] SRNAME   
+   > \verbatim   
+   >          SRNAME is CHARACTER*(*)   
+   >          The name of the routine which called XERBLA.   
+   > \endverbatim   
+   >   
+   > \param[in] INFO   
+   > \verbatim   
+   >          INFO is INTEGER   
+   >          The position of the invalid parameter in the parameter list   
+   >          of the calling routine.   
+   > \endverbatim   
+
+    Authors:   
+    ========   
+
+   > \author Univ. of Tennessee   
+   > \author Univ. of California Berkeley   
+   > \author Univ. of Colorado Denver   
+   > \author NAG Ltd.   
+
+   > \date November 2011   
+
+   > \ingroup auxOTHERauxiliary   
+
+    =====================================================================   
+   Subroutine */ int igraphxerbla_(char *srname, integer *info, ftnlen srname_len)
+{
+    /* Format strings */
+    static char fmt_9999[] = "(\002 ** On entry to \002,a,\002 parameter num"
+	    "ber \002,i2,\002 had \002,\002an illegal value\002)";
+
+    /* Builtin functions */
+    integer s_wsfe(cilist *), do_fio(integer *, char *, ftnlen), e_wsfe(void);
+    /* Subroutine */ int s_stop(char *, ftnlen);
+
+    /* Local variables */
+    extern integer igraphlen_trim__(char *, ftnlen);
+
+    /* Fortran I/O blocks */
+    static cilist io___1 = { 0, 6, 0, fmt_9999, 0 };
+
+
+
+/*  -- LAPACK auxiliary routine (version 3.4.0) --   
+    -- LAPACK is a software package provided by Univ. of Tennessee,    --   
+    -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--   
+       November 2011   
+
+
+   ===================================================================== */
+
+
+    s_wsfe(&io___1);
+    do_fio(&c__1, srname, igraphlen_trim__(srname, srname_len));
+    do_fio(&c__1, (char *)&(*info), (ftnlen)sizeof(integer));
+    e_wsfe();
+
+    s_stop("", (ftnlen)0);
+
+
+/*     End of XERBLA */
+
+    return 0;
+} /* igraphxerbla_ */
+
diff --git a/igraph/src/xwsne.c b/igraph/src/xwsne.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/xwsne.c
@@ -0,0 +1,77 @@
+#include "f2c.h"
+#include "fio.h"
+#include "lio.h"
+#include "fmt.h"
+
+extern int f__Aquote;
+
+ static VOID
+nl_donewrec(Void)
+{
+	(*f__donewrec)();
+	PUT(' ');
+	}
+
+#ifdef KR_headers
+x_wsne(a) cilist *a;
+#else
+#include "string.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ VOID
+x_wsne(cilist *a)
+#endif
+{
+	Namelist *nl;
+	char *s;
+	Vardesc *v, **vd, **vde;
+	ftnint number, type;
+	ftnlen *dims;
+	ftnlen size;
+	extern ftnlen f__typesize[];
+
+	nl = (Namelist *)a->cifmt;
+	PUT('&');
+	for(s = nl->name; *s; s++)
+		PUT(*s);
+	PUT(' ');
+	f__Aquote = 1;
+	vd = nl->vars;
+	vde = vd + nl->nvars;
+	while(vd < vde) {
+		v = *vd++;
+		s = v->name;
+#ifdef No_Extra_Namelist_Newlines
+		if (f__recpos+strlen(s)+2 >= L_len)
+#endif
+			nl_donewrec();
+		while(*s)
+			PUT(*s++);
+		PUT(' ');
+		PUT('=');
+		number = (dims = v->dims) ? dims[1] : 1;
+		type = v->type;
+		if (type < 0) {
+			size = -type;
+			type = TYCHAR;
+			}
+		else
+			size = f__typesize[type];
+		l_write(&number, v->addr, size, type);
+		if (vd < vde) {
+			if (f__recpos+2 >= L_len)
+				nl_donewrec();
+			PUT(',');
+			PUT(' ');
+			}
+		else if (f__recpos+1 >= L_len)
+			nl_donewrec();
+		}
+	f__Aquote = 0;
+	PUT('/');
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/z_abs.c b/igraph/src/z_abs.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/z_abs.c
@@ -0,0 +1,18 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+double f__cabs();
+double z_abs(z) doublecomplex *z;
+#else
+double f__cabs(double, double);
+double z_abs(doublecomplex *z)
+#endif
+{
+return( f__cabs( z->r, z->i ) );
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/z_cos.c b/igraph/src/z_cos.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/z_cos.c
@@ -0,0 +1,21 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sin(), cos(), sinh(), cosh();
+VOID z_cos(r, z) doublecomplex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+void z_cos(doublecomplex *r, doublecomplex *z)
+#endif
+{
+	double zi = z->i, zr = z->r;
+	r->r =   cos(zr) * cosh(zi);
+	r->i = - sin(zr) * sinh(zi);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/z_div.c b/igraph/src/z_div.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/z_div.c
@@ -0,0 +1,50 @@
+#include "f2c.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef KR_headers
+extern VOID sig_die();
+VOID z_div(c, a, b) doublecomplex *a, *b, *c;
+#else
+extern void sig_die(const char*, int);
+void z_div(doublecomplex *c, doublecomplex *a, doublecomplex *b)
+#endif
+{
+	double ratio, den;
+	double abr, abi, cr;
+
+	if( (abr = b->r) < 0.)
+		abr = - abr;
+	if( (abi = b->i) < 0.)
+		abi = - abi;
+	if( abr <= abi )
+		{
+		if(abi == 0) {
+#ifdef IEEE_COMPLEX_DIVIDE
+			if (a->i != 0 || a->r != 0)
+				abi = 1.;
+			c->i = c->r = abi / abr;
+			return;
+#else
+			sig_die("complex division by zero", 1);
+#endif
+			}
+		ratio = b->r / b->i ;
+		den = b->i * (1 + ratio*ratio);
+		cr = (a->r*ratio + a->i) / den;
+		c->i = (a->i*ratio - a->r) / den;
+		}
+
+	else
+		{
+		ratio = b->i / b->r ;
+		den = b->r * (1 + ratio*ratio);
+		cr = (a->r + a->i*ratio) / den;
+		c->i = (a->i - a->r*ratio) / den;
+		}
+	c->r = cr;
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/z_exp.c b/igraph/src/z_exp.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/z_exp.c
@@ -0,0 +1,23 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double exp(), cos(), sin();
+VOID z_exp(r, z) doublecomplex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+void z_exp(doublecomplex *r, doublecomplex *z)
+#endif
+{
+	double expx, zi = z->i;
+
+	expx = exp(z->r);
+	r->r = expx * cos(zi);
+	r->i = expx * sin(zi);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/z_log.c b/igraph/src/z_log.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/z_log.c
@@ -0,0 +1,121 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double log(), f__cabs(), atan2();
+#define ANSI(x) ()
+#else
+#define ANSI(x) x
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern double f__cabs(double, double);
+#endif
+
+#ifndef NO_DOUBLE_EXTENDED
+#ifndef GCC_COMPARE_BUG_FIXED
+#ifndef Pre20000310
+#ifdef Comment
+Some versions of gcc, such as 2.95.3 and 3.0.4, are buggy under -O2 or -O3:
+on IA32 (Intel 80x87) systems, they may do comparisons on values computed
+in extended-precision registers.  This can lead to the test "s > s0" that
+was used below being carried out incorrectly.  The fix below cannot be
+spoiled by overzealous optimization, since the compiler cannot know
+whether gcc_bug_bypass_diff_F2C will be nonzero.  (We expect it always
+to be zero.  The weird name is unlikely to collide with anything.)
+
+An example (provided by Ulrich Jakobus) where the bug fix matters is
+
+	double complex a, b
+	a = (.1099557428756427618354862829619, .9857360542953131909982289471372)
+	b = log(a)
+
+An alternative to the fix below would be to use 53-bit rounding precision,
+but the means of specifying this 80x87 feature are highly unportable.
+#endif /*Comment*/
+#define BYPASS_GCC_COMPARE_BUG
+double (*gcc_bug_bypass_diff_F2C) ANSI((double*,double*));
+ static double
+#ifdef KR_headers
+diff1(a,b) double *a, *b;
+#else
+diff1(double *a, double *b)
+#endif
+{ return *a - *b; }
+#endif /*Pre20000310*/
+#endif /*GCC_COMPARE_BUG_FIXED*/
+#endif /*NO_DOUBLE_EXTENDED*/
+
+#ifdef KR_headers
+VOID z_log(r, z) doublecomplex *r, *z;
+#else
+void z_log(doublecomplex *r, doublecomplex *z)
+#endif
+{
+	double s, s0, t, t2, u, v;
+	double zi = z->i, zr = z->r;
+#ifdef BYPASS_GCC_COMPARE_BUG
+	double (*diff) ANSI((double*,double*));
+#endif
+
+	r->i = atan2(zi, zr);
+#ifdef Pre20000310
+	r->r = log( f__cabs( zr, zi ) );
+#else
+	if (zi < 0)
+		zi = -zi;
+	if (zr < 0)
+		zr = -zr;
+	if (zr < zi) {
+		t = zi;
+		zi = zr;
+		zr = t;
+		}
+	t = zi/zr;
+	s = zr * sqrt(1 + t*t);
+	/* now s = f__cabs(zi,zr), and zr = |zr| >= |zi| = zi */
+	if ((t = s - 1) < 0)
+		t = -t;
+	if (t > .01)
+		r->r = log(s);
+	else {
+
+#ifdef Comment
+
+	log(1+x) = x - x^2/2 + x^3/3 - x^4/4 + - ...
+
+		 = x(1 - x/2 + x^2/3 -+...)
+
+	[sqrt(y^2 + z^2) - 1] * [sqrt(y^2 + z^2) + 1] = y^2 + z^2 - 1, so
+
+	sqrt(y^2 + z^2) - 1 = (y^2 + z^2 - 1) / [sqrt(y^2 + z^2) + 1]
+
+#endif /*Comment*/
+
+#ifdef BYPASS_GCC_COMPARE_BUG
+		if (!(diff = gcc_bug_bypass_diff_F2C))
+			diff = diff1;
+#endif
+		t = ((zr*zr - 1.) + zi*zi) / (s + 1);
+		t2 = t*t;
+		s = 1. - 0.5*t;
+		u = v = 1;
+		do {
+			s0 = s;
+			u *= t2;
+			v += 2;
+			s += u/v - t*u/(v+1);
+			}
+#ifdef BYPASS_GCC_COMPARE_BUG
+			while(s - s0 > 1e-18 || (*diff)(&s,&s0) > 0.);
+#else
+			while(s > s0);
+#endif
+		r->r = s*t;
+		}
+#endif
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/z_sin.c b/igraph/src/z_sin.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/z_sin.c
@@ -0,0 +1,21 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sin(), cos(), sinh(), cosh();
+VOID z_sin(r, z) doublecomplex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+void z_sin(doublecomplex *r, doublecomplex *z)
+#endif
+{
+	double zi = z->i, zr = z->r;
+	r->r = sin(zr) * cosh(zi);
+	r->i = cos(zr) * sinh(zi);
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/z_sqrt.c b/igraph/src/z_sqrt.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/z_sqrt.c
@@ -0,0 +1,35 @@
+#include "f2c.h"
+
+#ifdef KR_headers
+double sqrt(), f__cabs();
+VOID z_sqrt(r, z) doublecomplex *r, *z;
+#else
+#undef abs
+#include "math.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern double f__cabs(double, double);
+void z_sqrt(doublecomplex *r, doublecomplex *z)
+#endif
+{
+	double mag, zi = z->i, zr = z->r;
+
+	if( (mag = f__cabs(zr, zi)) == 0.)
+		r->r = r->i = 0.;
+	else if(zr > 0)
+		{
+		r->r = sqrt(0.5 * (mag + zr) );
+		r->i = zi / r->r / 2;
+		}
+	else
+		{
+		r->i = sqrt(0.5 * (mag - zr) );
+		if(zi < 0)
+			r->i = - r->i;
+		r->r = zi / r->i / 2;
+		}
+	}
+#ifdef __cplusplus
+}
+#endif
diff --git a/igraph/src/zeroin.c b/igraph/src/zeroin.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/zeroin.c
@@ -0,0 +1,203 @@
+/* -*- mode: C -*-  */
+/*
+   IGraph library.
+   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
+   334 Harvard street, Cambridge, MA 02139 USA
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+
+*/
+
+/* from GNU R's zeroin.c, minor modifications by Gabor Csardi */
+
+/* from NETLIB c/brent.shar with max.iter, add'l info and convergence
+   details hacked in by Peter Dalgaard */
+
+/*************************************************************************
+ *              C math library
+ * function ZEROIN - obtain a function zero within the given range
+ *
+ * Input
+ *  double zeroin(ax,bx,f,info,Tol,Maxit)
+ *  double ax;          Root will be seeked for within
+ *  double bx;          a range [ax,bx]
+ *  double (*f)(double x, void *info); Name of the function whose zero
+ *                  will be seeked for
+ *  void *info;         Add'l info passed to f
+ *  double *Tol;            Acceptable tolerance for the root
+ *                  value.
+ *                  May be specified as 0.0 to cause
+ *                  the program to find the root as
+ *                  accurate as possible
+ *
+ *  int *Maxit;         Max. iterations
+ *
+ *
+ * Output
+ *  Zeroin returns an estimate for the root with accuracy
+ *  4*EPSILON*abs(x) + tol
+ *  *Tol returns estimated precision
+ *  *Maxit returns actual # of iterations, or -1 if maxit was
+ *      reached without convergence.
+ *
+ * Algorithm
+ *  G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical
+ *  computations. M., Mir, 1980, p.180 of the Russian edition
+ *
+ *  The function makes use of the bisection procedure combined with
+ *  the linear or quadric inverse interpolation.
+ *  At every step program operates on three abscissae - a, b, and c.
+ *  b - the last and the best approximation to the root
+ *  a - the last but one approximation
+ *  c - the last but one or even earlier approximation than a that
+ *      1) |f(b)| <= |f(c)|
+ *      2) f(b) and f(c) have opposite signs, i.e. b and c confine
+ *         the root
+ *  At every step Zeroin selects one of the two new approximations, the
+ *  former being obtained by the bisection procedure and the latter
+ *  resulting in the interpolation (if a,b, and c are all different
+ *  the quadric interpolation is utilized, otherwise the linear one).
+ *  If the latter (i.e. obtained by the interpolation) point is
+ *  reasonable (i.e. lies within the current interval [b,c] not being
+ *  too close to the boundaries) it is accepted. The bisection result
+ *  is used in the other case. Therefore, the range of uncertainty is
+ *  ensured to be reduced at least by the factor 1.6
+ *
+ ************************************************************************
+ */
+
+#include "igraph_types.h"
+#include "igraph_interrupt_internal.h"
+
+#include <float.h>
+#include <math.h>
+
+#define EPSILON DBL_EPSILON
+
+int igraph_zeroin(              /* An estimate of the root */
+    igraph_real_t *ax,          /* Left border | of the range   */
+    igraph_real_t *bx,          /* Right border| the root is seeked*/
+    igraph_real_t (*f)(igraph_real_t x, void *info),    /* Function under investigation */
+    void *info,             /* Add'l info passed on to f    */
+    igraph_real_t *Tol,         /* Acceptable tolerance     */
+    int *Maxit,             /* Max # of iterations */
+    igraph_real_t *res) {               /* Result is stored here */
+    igraph_real_t a, b, c,      /* Abscissae, descr. see above  */
+                  fa, fb, fc;         /* f(a), f(b), f(c) */
+    igraph_real_t tol;
+    int maxit;
+
+    a = *ax;  b = *bx;  fa = (*f)(a, info);  fb = (*f)(b, info);
+    c = a;   fc = fa;
+    maxit = *Maxit + 1; tol = * Tol;
+
+    /* First test if we have found a root at an endpoint */
+    if (fa == 0.0) {
+        *Tol = 0.0;
+        *Maxit = 0;
+        *res = a;
+        return 0;
+    }
+    if (fb ==  0.0) {
+        *Tol = 0.0;
+        *Maxit = 0;
+        *res = b;
+        return 0;
+    }
+
+    while (maxit--) {   /* Main iteration loop  */
+        igraph_real_t prev_step = b - a;  /* Distance from the last but one
+                       to the last approximation    */
+        igraph_real_t tol_act;      /* Actual tolerance     */
+        igraph_real_t p;        /* Interpolation step is calcu- */
+        igraph_real_t q;        /* lated in the form p/q; divi-
+                     * sion operations is delayed
+                     * until the last moment    */
+        igraph_real_t new_step;     /* Step at this iteration   */
+
+        IGRAPH_ALLOW_INTERRUPTION();
+
+        if ( fabs(fc) < fabs(fb) ) {
+            /* Swap data for b to be the    */
+            a = b;  b = c;  c = a;  /* best approximation       */
+            fa = fb;  fb = fc;  fc = fa;
+        }
+        tol_act = 2 * EPSILON * fabs(b) + tol / 2;
+        new_step = (c - b) / 2;
+
+        if ( fabs(new_step) <= tol_act || fb == (igraph_real_t)0 ) {
+            *Maxit -= maxit;
+            *Tol = fabs(c - b);
+            *res = b;
+            return 0;           /* Acceptable approx. is found  */
+        }
+
+        /* Decide if the interpolation can be tried */
+        if ( fabs(prev_step) >= tol_act /* If prev_step was large enough*/
+             && fabs(fa) > fabs(fb) ) {
+            /* and was in true direction,
+                         * Interpolation may be tried   */
+            register igraph_real_t t1, cb, t2;
+            cb = c - b;
+            if ( a == c ) {     /* If we have only two distinct */
+                /* points linear interpolation  */
+                t1 = fb / fa;   /* can only be applied      */
+                p = cb * t1;
+                q = 1.0 - t1;
+            } else {        /* Quadric inverse interpolation*/
+
+                q = fa / fc;  t1 = fb / fc;  t2 = fb / fa;
+                p = t2 * ( cb * q * (q - t1) - (b - a) * (t1 - 1.0) );
+                q = (q - 1.0) * (t1 - 1.0) * (t2 - 1.0);
+            }
+            if ( p > (igraph_real_t)0 ) { /* p was calculated with the */
+                q = -q;    /* opposite sign; make p positive */
+            } else {        /* and assign possible minus to */
+                p = -p;    /* q              */
+            }
+
+            if ( p < (0.75 * cb * q - fabs(tol_act * q) / 2) /* If b+p/q falls in [b,c]*/
+                 && p < fabs(prev_step * q / 2) ) { /* and isn't too large  */
+                new_step = p / q;
+            }         /* it is accepted
+                         * If p/q is too large then the
+                         * bisection procedure can
+                         * reduce [b,c] range to more
+                         * extent */
+        }
+
+        if ( fabs(new_step) < tol_act) { /* Adjust the step to be not less*/
+            if ( new_step > (igraph_real_t)0 ) { /* than tolerance       */
+                new_step = tol_act;
+            } else {
+                new_step = -tol_act;
+            }
+        }
+        a = b;  fa = fb;            /* Save the previous approx. */
+        b += new_step;  fb = (*f)(b, info); /* Do step to a new approxim. */
+        if ( (fb > 0 && fc > 0) || (fb < 0 && fc < 0) ) {
+            /* Adjust c for it to have a sign opposite to that of b */
+            c = a;  fc = fa;
+        }
+
+    }
+    /* failed! */
+    *Tol = fabs(c - b);
+    *Maxit = -1;
+    *res = b;
+    return IGRAPH_DIVERGED;
+}
+
diff --git a/igraph/src/zeta.c b/igraph/src/zeta.c
new file mode 100644
--- /dev/null
+++ b/igraph/src/zeta.c
@@ -0,0 +1,154 @@
+/* specfunc/zeta.c
+ * 
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004 Gerard Jungman
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* Author:  G. Jungman */
+
+/* This file was taken from the GNU Scientific Library. Some modifications
+ * were done in order to make it independent from the rest of GSL
+ */
+
+/*
+#include <config.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_sf_elementary.h>
+#include <gsl/gsl_sf_exp.h>
+#include <gsl/gsl_sf_gamma.h>
+#include <gsl/gsl_sf_pow_int.h>
+#include <gsl/gsl_sf_zeta.h>
+
+#include "error.h"
+
+#include "chebyshev.h"
+#include "cheb_eval.c"
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include "error.h"
+
+/*-*-*-*-*-*-*-*-*-*- From gsl_machine.h -*-*-*-*-*-*-*-*-*-*-*-*-*/
+
+#define GSL_LOG_DBL_MIN   (-7.0839641853226408e+02)
+#define GSL_LOG_DBL_MAX    7.0978271289338397e+02
+#define GSL_DBL_EPSILON        2.2204460492503131e-16
+
+/*-*-*-*-*-*-*-*-*-* From gsl_sf_result.h *-*-*-*-*-*-*-*-*-*-*-*/
+
+struct gsl_sf_result_struct {
+  double val;
+  double err;
+};
+typedef struct gsl_sf_result_struct gsl_sf_result;
+
+/*-*-*-*-*-*-*-*-*-*-*-* Private Section *-*-*-*-*-*-*-*-*-*-*-*/
+
+/* coefficients for Maclaurin summation in hzeta()
+ * B_{2j}/(2j)!
+ */
+static double hzeta_c[15] = {
+  1.00000000000000000000000000000,
+  0.083333333333333333333333333333,
+ -0.00138888888888888888888888888889,
+  0.000033068783068783068783068783069,
+ -8.2671957671957671957671957672e-07,
+  2.0876756987868098979210090321e-08,
+ -5.2841901386874931848476822022e-10,
+  1.3382536530684678832826980975e-11,
+ -3.3896802963225828668301953912e-13,
+  8.5860620562778445641359054504e-15,
+ -2.1748686985580618730415164239e-16,
+  5.5090028283602295152026526089e-18,
+ -1.3954464685812523340707686264e-19,
+  3.5347070396294674716932299778e-21,
+ -8.9535174270375468504026113181e-23
+};
+
+/*-*-*-*-*-*-*-*-*-*-*-* Functions with Error Codes *-*-*-*-*-*-*-*-*-*-*-*/
+
+static int gsl_sf_hzeta_e(const double s, const double q, gsl_sf_result * result)
+{
+  /* CHECK_POINTER(result) */
+
+  if(s <= 1.0 || q <= 0.0) {
+	PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
+  }
+  else {
+    const double max_bits = 54.0;
+    const double ln_term0 = -s * log(q);  
+
+    if(ln_term0 < GSL_LOG_DBL_MIN + 1.0) {
+	  PLFIT_ERROR("underflow", PLFIT_UNDRFLOW);
+    }
+    else if(ln_term0 > GSL_LOG_DBL_MAX - 1.0) {
+	  PLFIT_ERROR("overflow", PLFIT_OVERFLOW);
+    }
+    else if((s > max_bits && q < 1.0) || (s > 0.5*max_bits && q < 0.25)) {
+      result->val = pow(q, -s);
+      result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val);
+      return PLFIT_SUCCESS;
+    }
+    else if(s > 0.5*max_bits && q < 1.0) {
+      const double p1 = pow(q, -s);
+      const double p2 = pow(q/(1.0+q), s);
+      const double p3 = pow(q/(2.0+q), s);
+      result->val = p1 * (1.0 + p2 + p3);
+      result->err = GSL_DBL_EPSILON * (0.5*s + 2.0) * fabs(result->val);
+      return PLFIT_SUCCESS;
+    }
+    else {
+      /* Euler-Maclaurin summation formula 
+       * [Moshier, p. 400, with several typo corrections]
+       */
+      const int jmax = 12;
+      const int kmax = 10;
+      int j, k;
+      const double pmax  = pow(kmax + q, -s);
+      double scp = s;
+      double pcp = pmax / (kmax + q);
+      double ans = pmax*((kmax+q)/(s-1.0) + 0.5);
+
+      for(k=0; k<kmax; k++) {
+        ans += pow(k + q, -s);
+      }
+
+      for(j=0; j<=jmax; j++) {
+        double delta = hzeta_c[j+1] * scp * pcp;
+        ans += delta;
+        if(fabs(delta/ans) < 0.5*GSL_DBL_EPSILON) break;
+        scp *= (s+2*j+1)*(s+2*j+2);
+        pcp /= (kmax + q)*(kmax + q);
+      }
+
+      result->val = ans;
+      result->err = 2.0 * (jmax + 1.0) * GSL_DBL_EPSILON * fabs(ans);
+      return PLFIT_SUCCESS;
+    }
+  }
+}
+
+/*-*-*-*-*-*-*-*-*-* Functions w/ Natural Prototypes *-*-*-*-*-*-*-*-*-*-*/
+
+double gsl_sf_hzeta(const double s, const double a)
+{
+  gsl_sf_result result;
+  gsl_sf_hzeta_e(s, a, &result);
+  return result.val;
+}
+
diff --git a/include/bytestring.h b/include/bytestring.h
--- a/include/bytestring.h
+++ b/include/bytestring.h
@@ -1,7 +1,7 @@
 #ifndef HASKELL_IGRAPH_BYTESTRING
 #define HASKELL_IGRAPH_BYTESTRING
 
-#include <igraph/igraph.h>
+#include "igraph.h"
 
 typedef struct bytestring_t {
   unsigned long int len;
diff --git a/include/haskell_attributes.h b/include/haskell_attributes.h
--- a/include/haskell_attributes.h
+++ b/include/haskell_attributes.h
@@ -1,7 +1,7 @@
 #ifndef HASKELL_IGRAPH_ATTRIBUTE
 #define HASKELL_IGRAPH_ATTRIBUTE
 
-#include "igraph/igraph.h"
+#include "igraph.h"
 #include "bytestring.h"
 
 #include <string.h>
diff --git a/include/haskell_igraph.h b/include/haskell_igraph.h
--- a/include/haskell_igraph.h
+++ b/include/haskell_igraph.h
@@ -1,7 +1,7 @@
 #ifndef HASKELL_IGRAPH
 #define HASKELL_IGRAPH
 
-#include <igraph/igraph.h>
+#include "igraph.h"
 
 void haskelligraph_init();
 
diff --git a/src/IGraph/Algorithms.hs b/src/IGraph/Algorithms.hs
--- a/src/IGraph/Algorithms.hs
+++ b/src/IGraph/Algorithms.hs
@@ -2,7 +2,7 @@
     ( module IGraph.Algorithms.Structure
     , module IGraph.Algorithms.Community
     , module IGraph.Algorithms.Clique
-    , module IGraph.Algorithms.Layout
+--    , module IGraph.Algorithms.Layout
     , module IGraph.Algorithms.Motif
     , module IGraph.Algorithms.Generators
     , module IGraph.Algorithms.Isomorphism
@@ -12,7 +12,7 @@
 import IGraph.Algorithms.Structure
 import IGraph.Algorithms.Community
 import IGraph.Algorithms.Clique
-import IGraph.Algorithms.Layout
+--import IGraph.Algorithms.Layout
 import IGraph.Algorithms.Motif
 import IGraph.Algorithms.Generators
 import IGraph.Algorithms.Isomorphism
diff --git a/src/IGraph/Algorithms/Generators.chs b/src/IGraph/Algorithms/Generators.chs
--- a/src/IGraph/Algorithms/Generators.chs
+++ b/src/IGraph/Algorithms/Generators.chs
@@ -22,6 +22,7 @@
 import Foreign
 
 import           IGraph
+import           IGraph.Random (Gen)
 import           IGraph.Mutable (MGraph(..))
 {#import IGraph.Internal #}
 {#import IGraph.Internal.Constants #}
@@ -86,8 +87,9 @@
 erdosRenyiGame :: forall d. SingI d
                => ErdosRenyiModel
                -> Bool  -- ^ self-loop
+               -> Gen
                -> IO (Graph d () ())
-erdosRenyiGame model self = do
+erdosRenyiGame model self _ = do
     igraphInit
     gr <- case model of
         GNP n p -> igraphErdosRenyiGame IgraphErdosRenyiGnp n p directed self
@@ -107,8 +109,9 @@
 -- | Generates a random graph with a given degree sequence.
 degreeSequenceGame :: [Int]   -- ^ Out degree
                    -> [Int]   -- ^ In degree
+                   -> Gen
                    -> IO (Graph 'D () ())
-degreeSequenceGame out_deg in_deg = do
+degreeSequenceGame out_deg in_deg _ = do
     igraphInit
     withList out_deg $ \out_deg' ->
         withList in_deg $ \in_deg' -> do
@@ -127,8 +130,9 @@
                         -- one (inclusive).
             -> Bool     -- ^ whether loop edges are allowed in the new graph, or not.
             -> Bool     -- ^ whether multiple edges are allowed in the new graph.
+            -> Gen
             -> IO ()
-rewireEdges gr p loop multi = igraphRewireEdges (_mgraph gr) p loop multi
+rewireEdges gr p loop multi _ = igraphRewireEdges (_mgraph gr) p loop multi
 {#fun igraph_rewire_edges as ^ 
     { `IGraph'
     , `Double'
@@ -140,8 +144,9 @@
 rewire :: (Serialize v, Ord v, Serialize e)
        => Int    -- ^ Number of rewiring trials to perform.
        -> Graph d v e
+       -> Gen
        -> IO (Graph d v e)
-rewire n gr = do
+rewire n gr _ = do
     gr' <- thaw gr
     igraphRewire (_mgraph gr') n IgraphRewiringSimple
     unsafeFreeze gr'
diff --git a/src/IGraph/Algorithms/Layout.chs b/src/IGraph/Algorithms/Layout.chs
deleted file mode 100644
--- a/src/IGraph/Algorithms/Layout.chs
+++ /dev/null
@@ -1,113 +0,0 @@
-{-# LANGUAGE ForeignFunctionInterface #-}
-module IGraph.Algorithms.Layout
-    ( getLayout
-    , LayoutMethod(..)
-    , defaultKamadaKawai
-    , defaultLGL
-    ) where
-
-import           Data.Maybe             (isJust)
-import           Foreign                (nullPtr)
-
-import Foreign
-
-import           IGraph
-{#import IGraph.Internal #}
-
-#include "igraph/igraph.h"
-
-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 { 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 = 10
-    , 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 = 100
-    , 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 v e -> LayoutMethod -> IO [(Double, Double)]
-getLayout gr method = case method of
-    KamadaKawai seed niter sigma initemp coolexp kkconst -> case seed of
-        Nothing -> allocaMatrix $ \mat -> do
-            igraphLayoutKamadaKawai gptr mat niter (sigma n) initemp coolexp
-                (kkconst n) (isJust seed) nullPtr nullPtr nullPtr nullPtr
-            [x, y] <- toColumnLists mat
-            return $ zip x y
-        Just xs -> if length xs /= nNodes gr
-            then error "Seed error: incorrect size"
-            else withRowLists ((\(x,y) -> [x,y]) (unzip xs)) $ \mat -> do
-                igraphLayoutKamadaKawai gptr mat niter (sigma n) initemp coolexp
-                    (kkconst n) (isJust seed) nullPtr nullPtr nullPtr nullPtr
-                [x, y] <- toColumnLists mat
-                return $ zip x y
-
-    LGL niter delta area coolexp repulserad cellsize -> allocaMatrix $ \mat -> do
-        igraphLayoutLgl gptr mat niter (delta n) (area n) coolexp
-            (repulserad n) (cellsize n) (-1)
-        [x, y] <- toColumnLists mat
-        return $ zip x y
-  where
-    n = nNodes gr
-    gptr = _graph gr
-
-{#fun igraph_layout_kamada_kawai as ^
-    { `IGraph'
-    , castPtr `Ptr Matrix'
-    , `Int'
-    , `Double'
-    , `Double'
-    , `Double'
-    , `Double'
-    , `Bool'
-    , castPtr `Ptr Vector'
-    , castPtr `Ptr Vector'
-    , castPtr `Ptr Vector'
-    , castPtr `Ptr Vector'
-    } -> `CInt' void- #}
-
-{# fun igraph_layout_lgl as ^
-    { `IGraph'
-    , castPtr `Ptr Matrix'
-    , `Int'
-    , `Double'
-    , `Double'
-    , `Double'
-    , `Double'
-    , `Double'
-    , `Int'
-    } -> `CInt' void- #}
diff --git a/src/IGraph/Exporter/GEXF.hs b/src/IGraph/Exporter/GEXF.hs
deleted file mode 100644
--- a/src/IGraph/Exporter/GEXF.hs
+++ /dev/null
@@ -1,129 +0,0 @@
-{-# LANGUAGE DeriveGeneric     #-}
-{-# LANGUAGE FlexibleInstances #-}
-module IGraph.Exporter.GEXF
-    ( NodeAttr(..)
-    , defaultNodeAttributes
-    , EdgeAttr(..)
-    , defaultEdgeAttributes
-    , genXMLTree
-    , writeGEXF
-    ) where
-
-import           Data.Colour       (AlphaColour, alphaChannel, black, opaque,
-                                    over)
-import           Data.Colour.SRGB  (channelBlue, channelGreen, channelRed,
-                                    toSRGB24)
-import           Data.Serialize
-import Data.Function (on)
-import           Data.Singletons   (SingI)
-import           GHC.Generics
-import           IGraph
-import           Text.XML.HXT.Core
-
-instance Serialize (AlphaColour Double) where
-    get = do
-        x <- get
-        return $ read x
-    put x = put $ show x
-
-data NodeAttr = NodeAttr
-    { _size       :: Double
-    , _nodeColour :: AlphaColour Double
-    , _nodeLabel  :: String
-    , _positionX  :: Double
-    , _positionY  :: Double
-    , _nodeZindex :: Int
-    } deriving (Show, Read, Eq, Generic)
-
-instance Ord NodeAttr where
-    compare = compare `on` _nodeLabel
-instance Serialize NodeAttr
-
-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, Generic)
-
-instance Ord EdgeAttr where
-    compare = compare `on` _edgeLabel
-instance Serialize EdgeAttr
-
-defaultEdgeAttributes :: EdgeAttr
-defaultEdgeAttributes = EdgeAttr
-    { _edgeLabel = ""
-    , _edgeColour = opaque black
-    , _edgeWeight = 1.0
-    , _edgeArrowLength = 10
-    , _edgeZindex = 2
-    }
-
-genXMLTree :: (SingI d, ArrowXml a) => Graph 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 :: SingI d => FilePath -> Graph 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
deleted file mode 100644
--- a/src/IGraph/Exporter/Graphics.hs
+++ /dev/null
@@ -1,49 +0,0 @@
-{-# 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
-               & headStyle %~ fcA (_edgeColour eattr)
-               & headLength .~ output (_edgeArrowLength eattr)
-        ) start end # lwO (_edgeWeight eattr) # lcA (_edgeColour eattr), _edgeZindex eattr )
-      where
-        eattr = edgeLab gr (from, to)
-        start = x1 ^& y1
-        end = (alpha * x1 + (1 - alpha) * x2) ^& (alpha * y1 + (1 - alpha) * y2)
-        x1 = _positionX nattr1
-        y1 = _positionY nattr1
-        x2 = _positionX nattr2
-        y2 = _positionY nattr2
-        alpha = r / sqrt ((x1 - x2)**2 + (y1 - y2)**2)
-        r = _size nattr2
-        nattr1 = nodeLab gr from
-        nattr2 = nodeLab gr to
-        arrowH | isDirected gr = dart
-               | otherwise = noHead
-{-# INLINE graphToDiagram #-}
diff --git a/src/IGraph/Internal.chs b/src/IGraph/Internal.chs
--- a/src/IGraph/Internal.chs
+++ b/src/IGraph/Internal.chs
@@ -115,6 +115,13 @@
       -- * Igraph arpack options type
     , ArpackOpt
     , allocaArpackOpt
+
+      -- * Random numbers
+    , RNG
+    , igraphRngDefault
+    , igraphRngSetDefault
+    , allocaRng
+    , igraphRngSeed
     ) where
 
 import Control.Monad
@@ -125,7 +132,6 @@
 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_)
@@ -341,6 +347,9 @@
         igraphMatrixCopyTo mptr ptr
         peekArray (r*c) ptr
     return $ chunksOf r $ map realToFrac xs
+  where
+    chunksOf _ [] = []
+    chunksOf i ls = take i ls : chunksOf i (drop i ls)
 
 {#fun igraph_matrix_null as ^ { castPtr `Ptr Matrix' } -> `()' #}
 
@@ -749,3 +758,36 @@
     igraphArpackOptionsInit opt >> fun opt
 {-# INLINE allocaArpackOpt #-}
 {#fun igraph_arpack_options_init as ^ { castPtr `Ptr ArpackOpt' } -> `CInt' void- #}
+
+
+--------------------------------------------------------------------------------
+-- Random numbers
+--------------------------------------------------------------------------------
+
+data RNG
+
+-- | Query the default random number generator.
+{#fun igraph_rng_default as ^ {} -> `Ptr RNG' castPtr #}
+
+-- | Set the default igraph random number generator.
+{#fun igraph_rng_set_default as ^ { castPtr `Ptr RNG' } -> `()' #}
+
+-- | Allocate and initialize a RNG.
+allocaRng :: (Ptr RNG -> IO a) -> IO a
+allocaRng fun = allocaBytes {# sizeof igraph_rng_t #} $ \rng ->
+    bracket_ (igraphRngInit_ rng) (igraphRngDestroy rng) (fun rng)
+{-# INLINE allocaRng #-}
+
+{#fun igraph_rng_init_ as igraphRngInit_
+    { castPtr `Ptr RNG' } -> `CInt' void- #}
+{#fun igraph_rng_destroy as ^ { castPtr `Ptr RNG' } -> `()' #}
+
+-- | Set the seed of a random number generator
+{#fun igraph_rng_seed as ^
+    { castPtr `Ptr RNG', `Int' } -> `CInt' void- #}
+
+#c
+int igraph_rng_init_(igraph_rng_t *rng) {
+    return(igraph_rng_init(rng, &igraph_rngtype_mt19937));
+}
+#endc
diff --git a/src/IGraph/Internal/Constants.chs b/src/IGraph/Internal/Constants.chs
--- a/src/IGraph/Internal/Constants.chs
+++ b/src/IGraph/Internal/Constants.chs
@@ -1,7 +1,7 @@
 {-# LANGUAGE ForeignFunctionInterface #-}
 module IGraph.Internal.Constants where
 
-#include "igraph/igraph.h"
+#include "haskell_igraph.h"
 
 {#enum igraph_neimode_t as Neimode {underscoreToCase}
     deriving (Show, Eq) #}
diff --git a/src/IGraph/Random.hs b/src/IGraph/Random.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Random.hs
@@ -0,0 +1,22 @@
+module IGraph.Random
+    ( Gen
+    , withSeed
+    ) where
+
+import IGraph.Internal
+
+-- | Random number generator
+data Gen = Gen
+
+{-
+withSystemRandom :: (Gen -> IO a) -> IO a
+withSystemRandom fun = fun Gen
+{-# INLINE withSystemRandom #-}
+-}
+
+withSeed :: Int -> (Gen -> IO a) -> IO a
+withSeed seed fun = do
+    rng <- igraphRngDefault
+    igraphRngSeed rng seed
+    fun Gen
+{-# INLINE withSeed #-}
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,4 @@
+packages:
+    - '.'
+
+resolver: lts-15.0
diff --git a/tests/Test/Algorithms.hs b/tests/Test/Algorithms.hs
--- a/tests/Test/Algorithms.hs
+++ b/tests/Test/Algorithms.hs
@@ -11,6 +11,7 @@
 import           Test.Tasty.HUnit
 
 import           IGraph
+import           IGraph.Random
 import           IGraph.Algorithms
 import qualified IGraph.Mutable      as GM
 
@@ -72,7 +73,7 @@
     [ testCase "ring" $ edges (head $ decompose $ ring 10) @?=
         [(0,1), (1,2), (2,3), (3,4), (4,5), (5,6), (6,7), (7,8), (8,9), (0,9)]
     , testCase "1 component" $ do
-        gr <- erdosRenyiGame (GNP 100 (40/100)) False :: IO (Graph 'U () ())
+        gr <- (withSeed 1244 $ erdosRenyiGame (GNP 100 (40/100)) False) :: IO (Graph 'U () ())
         1 @?= length (decompose gr)
     , testCase "toy example" $ map (sort . edges) (decompose gr) @?=
         [ [(0,1), (0,2), (1,2)]
diff --git a/tests/Test/Attributes.hs b/tests/Test/Attributes.hs
--- a/tests/Test/Attributes.hs
+++ b/tests/Test/Attributes.hs
@@ -11,7 +11,6 @@
 import           Test.Utils
 
 import           IGraph
-import           IGraph.Exporter.GEXF
 import           IGraph.Internal
 import           IGraph.Mutable
 
@@ -19,7 +18,6 @@
 tests = testGroup "Attribute tests"
     [ nodeLabelTest
     , labelTest
-    , serializeTest
     ]
 
 nodeLabelTest :: TestTree
@@ -36,6 +34,7 @@
         es' = sort $ map (\(a,b) -> ((nodeLab gr a, nodeLab gr b), edgeLab gr (a,b))) $ edges gr
     assertBool "" $ es == es'
 
+{-
 serializeTest :: TestTree
 serializeTest = testCase "serialize test" $ do
     dat <- randEdges 1000 10000
@@ -49,3 +48,4 @@
             Right r  -> r
         es' = map (\(a,b) -> ((nodeLab gr' a, nodeLab gr' b), edgeLab gr' (a,b))) $ edges gr'
     sort (map show es) @=? sort (map show es')
+    -}
diff --git a/tests/Test/Basic.hs b/tests/Test/Basic.hs
--- a/tests/Test/Basic.hs
+++ b/tests/Test/Basic.hs
@@ -13,7 +13,9 @@
 import           Test.Utils
 
 import           IGraph
+import           IGraph.Random
 import qualified IGraph.Mutable    as GM
+import IGraph.Algorithms.Generators
 
 tests :: TestTree
 tests = testGroup "Basic tests"
@@ -21,6 +23,7 @@
     , graphCreationLabeled
     , graphEdit
     , nonSimpleGraphTest
+    , randomGeneratorTest
     ]
 
 graphCreation :: TestTree
@@ -82,3 +85,18 @@
          , ((0,2), 'd') ]
     gr :: Graph 'U Int Char
     gr = mkGraph [0,1,2] es
+
+randomGeneratorTest :: TestTree
+randomGeneratorTest = testGroup "random generator"
+    [t1, t2]
+  where
+    t1 = testCase "random graph" $ do
+        gr1 <- sort . edges <$> genGr 1244
+        gr2 <- sort . edges <$> genGr 1244
+        gr1 @=? gr2
+    t2 = testCase "random graph" $ do
+        gr1 <- sort . edges <$> genGr 145
+        gr2 <- sort . edges <$> genGr 24
+        assertBool "" $ gr1 /= gr2
+    genGr :: Int -> IO (Graph 'D () ())
+    genGr seed = withSeed seed $ erdosRenyiGame (GNP 500 0.5) False
